Added automatic CMOS RAM checksum calculation.
[wine] / msdos / ioports.c
1 /*
2  * Emulation of processor ioports.
3  *
4  * Copyright 1995 Morten Welinder
5  */
6
7 /* Known problems:
8    - only a few ports are emulated.
9    - real-time clock in "cmos" is bogus.  A nifty alarm() setup could
10      fix that, I guess.
11 */
12
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include "windows.h"
19 #include "vga.h"
20 #include "options.h"
21 #include "debug.h"
22
23 static BYTE cmosaddress;
24
25 static BYTE cmosimage[64] =
26 {
27   0x27, 0x34, 0x31, 0x47, 0x16, 0x15, 0x00, 0x01,
28   0x04, 0x94, 0x26, 0x02, 0x50, 0x80, 0x00, 0x00,
29   0x40, 0xb1, 0x00, 0x9c, 0x01, 0x80, 0x02, 0x00,
30   0x1c, 0x00, 0x00, 0xad, 0x02, 0x10, 0x00, 0x00,
31   0x08, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00,
32   0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0x19,
33   0x00, 0x1c, 0x19, 0x81, 0x00, 0x0e, 0x00, 0x80,
34   0x1b, 0x7b, 0x21, 0x00, 0x00, 0x00, 0x05, 0x5f
35 };
36
37 #if defined(linux) && defined(__i386__)
38 # define DIRECT_IO_ACCESS
39 #else
40 # undef DIRECT_IO_ACCESS
41 #endif  /* linux && __i386__ */
42
43 #ifdef DIRECT_IO_ACCESS
44
45 extern int iopl(int level);
46
47 static char do_direct_port_access = 0;
48 static char port_permissions[0x10000];
49
50 #define IO_READ  1
51 #define IO_WRITE 2
52
53 #endif  /* DIRECT_IO_ACCESS */
54
55 static void IO_FixCMOSCheckSum()
56 {
57         WORD sum = 0;
58         int i;
59
60         for (i=0x10; i < 0x2d; i++)
61                 sum += cmosimage[i];
62         cmosimage[0x2e] = sum >> 8; /* yes, this IS hi byte !! */
63         cmosimage[0x2f] = sum & 0xff;
64         TRACE(int, "calculated hi %02x, lo %02x\n", cmosimage[0x2e], cmosimage[0x2f]);
65 }
66
67 /**********************************************************************
68  *          IO_port_init
69  */
70
71 /* set_IO_permissions(int val1, int val)
72  * Helper function for IO_port_init
73  */
74 #ifdef DIRECT_IO_ACCESS
75 static void set_IO_permissions(int val1, int val, char rw)
76 {
77         int j;
78         if (val1 != -1) {
79                 if (val == -1) val = 0x3ff;             
80                 for (j = val1; j <= val; j++)
81                         port_permissions[j] |= rw;              
82
83                 do_direct_port_access = 1;
84
85                 val1 = -1;
86         } else if (val != -1) {         
87                 do_direct_port_access = 1;
88
89                 port_permissions[val] |= rw;
90         }
91
92 }
93
94 /* do_IO_port_init_read_or_write(char* temp, char rw)
95  * Helper function for IO_port_init
96  */
97
98 static void do_IO_port_init_read_or_write(char* temp, char rw)
99 {
100         int val, val1, i, len;
101         if (!strcasecmp(temp, "all")) {
102                 MSG("Warning!!! Granting FULL IO port access to"
103                         " windoze programs!\nWarning!!! "
104                         "*** THIS IS NOT AT ALL "
105                         "RECOMMENDED!!! ***\n");
106                 for (i=0; i < sizeof(port_permissions); i++)
107                         port_permissions[i] |= rw;
108
109         } else if (!(!strcmp(temp, "*") || *temp == '\0')) {
110                 len = strlen(temp);
111                 val = -1;
112                 val1 = -1;              
113                 for (i = 0; i < len; i++) {
114                         switch (temp[i]) {
115                         case '0':
116                                 if (temp[i+1] == 'x' || temp[i+1] == 'X') {
117                                         sscanf(temp+i, "%x", &val);
118                                         i += 2;
119                                 } else {
120                                         sscanf(temp+i, "%d", &val);
121                                 }
122                                 while (isxdigit(temp[i]))
123                                         i++;
124                                 i--;
125                                 break;
126                         case ',':
127                         case ' ':
128                         case '\t':
129                                 set_IO_permissions(val1, val, rw);
130                                 val1 = -1; val = -1;
131                                 break;
132                         case '-':
133                                 val1 = val;
134                                 if (val1 == -1) val1 = 0;
135                                 break;
136                         default:
137                                 if (temp[i] >= '0' && temp[i] <= '9') {
138                                         sscanf(temp+i, "%d", &val);
139                                         while (isdigit(temp[i]))
140                                                 i++;
141                                 }
142                         }
143                 }
144                 set_IO_permissions(val1, val, rw);              
145         }
146 }
147
148 static __inline__ BYTE inb( WORD port )
149 {
150     BYTE b;
151     __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
152     return b;
153 }
154
155 static __inline__ WORD inw( WORD port )
156 {
157     WORD w;
158     __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
159     return w;
160 }
161
162 static __inline__ DWORD inl( WORD port )
163 {
164     DWORD dw;
165     __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
166     return dw;
167 }
168
169 static __inline__ void outb( BYTE value, WORD port )
170 {
171     __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
172 }
173
174 static __inline__ void outw( WORD value, WORD port )
175 {
176     __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
177 }
178
179 static __inline__ void outl( DWORD value, WORD port )
180 {
181     __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
182 }
183
184 #endif  /* DIRECT_IO_ACCESS */
185
186 void IO_port_init()
187 {
188 #ifdef DIRECT_IO_ACCESS
189         char temp[1024];
190
191         /* Can we do that? */
192         if (!iopl(3)) {
193                 iopl(0);
194
195                 PROFILE_GetWineIniString( "ports", "read", "*",
196                                          temp, sizeof(temp) );
197                 do_IO_port_init_read_or_write(temp, IO_READ);
198                 PROFILE_GetWineIniString( "ports", "write", "*",
199                                          temp, sizeof(temp) );
200                 do_IO_port_init_read_or_write(temp, IO_WRITE);
201         }
202 #endif  /* DIRECT_IO_ACCESS */
203     IO_FixCMOSCheckSum();
204 }
205
206
207 /**********************************************************************
208  *          IO_inport
209  */
210 DWORD IO_inport( int port, int count )
211 {
212     DWORD res = 0;
213     BYTE b;    
214
215 #ifdef DIRECT_IO_ACCESS    
216     if (do_direct_port_access)
217     {
218         /* Make sure we have access to the whole range */
219         int i;
220         for (i = 0; i < count; i++)
221             if (!(port_permissions[port+i] & IO_READ)) break;
222         if (i == count)
223         {
224             iopl(3);
225             switch(count)
226             {
227                 case 1: res = inb( port ); break;
228                 case 2: res = inw( port ); break;
229                 case 4: res = inl( port ); break;
230                 default:
231                     ERR(int, "invalid count %d\n", count);
232             }
233             iopl(0);
234             return res;
235         }
236     }
237 #endif
238
239     TRACE(int, "%d bytes from port 0x%02x\n", count, port );
240
241     while (count-- > 0)
242     {
243         switch (port)
244         {
245         case 0x70:
246             b = cmosaddress;
247             break;
248         case 0x71:
249             b = cmosimage[cmosaddress & 0x3f];
250             break;
251         case 0x200:
252         case 0x201:
253             b = 0xff; /* no joystick */
254             break;
255         case 0x3da:
256             b = VGA_ioport_in( port );
257             break;
258         default:
259             WARN( int, "Direct I/O read attempted from port %x\n", port);
260             b = 0xff;
261             break;
262         }
263         port++;
264         res = (res << 8) | b;
265     }
266     TRACE(int, "  returning ( 0x%lx )\n", res );
267     return res;
268 }
269
270
271 /**********************************************************************
272  *          IO_outport
273  */
274 void IO_outport( int port, int count, DWORD value )
275 {
276     BYTE b;
277
278     TRACE(int, "IO: 0x%lx (%d bytes) to port 0x%02x\n",
279                  value, count, port );
280
281 #ifdef DIRECT_IO_ACCESS
282     if (do_direct_port_access)
283     {
284         /* Make sure we have access to the whole range */
285         int i;
286         for (i = 0; i < count; i++)
287             if (!(port_permissions[port+i] & IO_WRITE)) break;
288         if (i == count)
289         {
290             iopl(3);
291             switch(count)
292             {
293                 case 1: outb( LOBYTE(value), port ); break;
294                 case 2: outw( LOWORD(value), port ); break;
295                 case 4: outl( value, port ); break;
296                 default:
297                     WARN(int, "Invalid count %d\n", count);
298             }
299             iopl(0);
300             return;
301         }
302     }
303 #endif
304
305     while (count-- > 0)
306     {
307         b = value & 0xff;
308         value >>= 8;
309         switch (port)
310         {
311         case 0x70:
312             cmosaddress = b & 0x7f;
313             break;
314         case 0x71:
315             cmosimage[cmosaddress & 0x3f] = b;
316             break;
317         case 0x3c8:
318         case 0x3c9:
319             VGA_ioport_out( port, b );
320             break;
321         default:
322             WARN(int, "Direct I/O write attempted to port %x\n", port );
323             break;
324         }
325         port++;
326     }
327 }