Fixed bit 2 value and set bit 1 too.
[wine] / msdos / ioports.c
1 /*
2  * Emulation of processor ioports.
3  *
4  * Copyright 1995 Morten Welinder
5  * Copyright 1998 Andreas Mohr, Ove Kaaven
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 /* Known problems:
23    - only a few ports are emulated.
24    - real-time clock in "cmos" is bogus.  A nifty alarm() setup could
25      fix that, I guess.
26 */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <time.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include "windef.h"
40 #include "winnls.h"
41 #include "callback.h"
42 #include "file.h"
43 #include "miscemu.h"
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(int);
47
48 static struct {
49     WORD        countmax;
50     BOOL16      byte_toggle; /* if TRUE, then hi byte has already been written */
51     WORD        latch;
52     BOOL16      latched;
53     BYTE        ctrlbyte_ch;
54     WORD        oldval;
55 } tmr_8253[3] = {
56     {0xFFFF,    FALSE,  0,      FALSE,  0x36,   0},
57     {0x0012,    FALSE,  0,      FALSE,  0x74,   0},
58     {0x0001,    FALSE,  0,      FALSE,  0xB6,   0},
59 };
60
61 static int dummy_ctr = 0;
62
63 static BYTE parport_8255[4] = {0x4f, 0x20, 0xff, 0xff};
64
65 static BYTE cmosaddress;
66
67 static BYTE cmosimage[64] =
68 {
69   0x27, 0x34, 0x31, 0x47, 0x16, 0x15, 0x00, 0x01,
70   0x04, 0x94, 0x26, 0x02, 0x50, 0x80, 0x00, 0x00,
71   0x40, 0xb1, 0x00, 0x9c, 0x01, 0x80, 0x02, 0x00,
72   0x1c, 0x00, 0x00, 0xad, 0x02, 0x10, 0x00, 0x00,
73   0x08, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00,
74   0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0x19,
75   0x00, 0x1c, 0x19, 0x81, 0x00, 0x0e, 0x00, 0x80,
76   0x1b, 0x7b, 0x21, 0x00, 0x00, 0x00, 0x05, 0x5f
77 };
78
79 #if defined(linux) && defined(__i386__)
80 # define DIRECT_IO_ACCESS
81 #else
82 # undef DIRECT_IO_ACCESS
83 # undef PP_IO_ACCESS
84 #endif  /* linux && __i386__ */
85
86 #ifdef HAVE_PPDEV
87 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
88                                        0: available);*/
89 #endif
90
91 #ifdef DIRECT_IO_ACCESS
92
93 extern int iopl(int level);
94 static char do_direct_port_access = -1;
95 static char port_permissions[0x10000];
96
97 #define IO_READ  1
98 #define IO_WRITE 2
99
100 static void IO_FixCMOSCheckSum(void)
101 {
102         WORD sum = 0;
103         int i;
104
105         for (i=0x10; i < 0x2d; i++)
106                 sum += cmosimage[i];
107         cmosimage[0x2e] = sum >> 8; /* yes, this IS hi byte !! */
108         cmosimage[0x2f] = sum & 0xff;
109         TRACE("calculated hi %02x, lo %02x\n", cmosimage[0x2e], cmosimage[0x2f]);
110 }
111
112 #endif  /* DIRECT_IO_ACCESS */
113
114 static void set_timer_maxval(unsigned timer, unsigned maxval)
115 {
116     switch (timer) {
117         case 0: /* System timer counter divisor */
118             if (Dosvm.SetTimer) Dosvm.SetTimer(maxval);
119             break;
120         case 1: /* RAM refresh */
121             FIXME("RAM refresh counter handling not implemented !\n");
122             break;
123         case 2: /* cassette & speaker */
124             /* speaker on ? */
125             if (((BYTE)parport_8255[1] & 3) == 3)
126             {
127                 TRACE("Beep (freq: %d) !\n", 1193180 / maxval );
128                 Beep(1193180 / maxval, 20);
129             }
130             break;
131     }
132 }
133
134 /**********************************************************************
135  *          IO_port_init
136  */
137
138 /* set_IO_permissions(int val1, int val)
139  * Helper function for IO_port_init
140  */
141 #ifdef DIRECT_IO_ACCESS
142 static void set_IO_permissions(int val1, int val, char rw)
143 {
144         int j;
145         if (val1 != -1) {
146                 if (val == -1) val = 0x3ff;
147                 for (j = val1; j <= val; j++)
148                         port_permissions[j] |= rw;
149
150                 do_direct_port_access = 1;
151
152                 val1 = -1;
153         } else if (val != -1) {
154                 do_direct_port_access = 1;
155
156                 port_permissions[val] |= rw;
157         }
158
159 }
160
161 /* do_IO_port_init_read_or_write(char* temp, char rw)
162  * Helper function for IO_port_init
163  */
164
165 static void do_IO_port_init_read_or_write(char* temp, char rw)
166 {
167         int val, val1, i, len;
168         if (!strcasecmp(temp, "all")) {
169                 MESSAGE("Warning!!! Granting FULL IO port access to"
170                         " windoze programs!\nWarning!!! "
171                         "*** THIS IS NOT AT ALL "
172                         "RECOMMENDED!!! ***\n");
173                 for (i=0; i < sizeof(port_permissions); i++)
174                         port_permissions[i] |= rw;
175
176         } else if (!(!strcmp(temp, "*") || *temp == '\0')) {
177                 len = strlen(temp);
178                 val = -1;
179                 val1 = -1;
180                 for (i = 0; i < len; i++) {
181                         switch (temp[i]) {
182                         case '0':
183                                 if (temp[i+1] == 'x' || temp[i+1] == 'X') {
184                                         sscanf(temp+i, "%x", &val);
185                                         i += 2;
186                                 } else {
187                                         sscanf(temp+i, "%d", &val);
188                                 }
189                                 while (isxdigit(temp[i]))
190                                         i++;
191                                 i--;
192                                 break;
193                         case ',':
194                         case ' ':
195                         case '\t':
196                                 set_IO_permissions(val1, val, rw);
197                                 val1 = -1; val = -1;
198                                 break;
199                         case '-':
200                                 val1 = val;
201                                 if (val1 == -1) val1 = 0;
202                                 break;
203                         default:
204                                 if (temp[i] >= '0' && temp[i] <= '9') {
205                                         sscanf(temp+i, "%d", &val);
206                                         while (isdigit(temp[i]))
207                                                 i++;
208                                 }
209                         }
210                 }
211                 set_IO_permissions(val1, val, rw);
212         }
213 }
214
215 static inline BYTE inb( WORD port )
216 {
217     BYTE b;
218     __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
219     return b;
220 }
221
222 static inline WORD inw( WORD port )
223 {
224     WORD w;
225     __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
226     return w;
227 }
228
229 static inline DWORD inl( WORD port )
230 {
231     DWORD dw;
232     __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
233     return dw;
234 }
235
236 static inline void outb( BYTE value, WORD port )
237 {
238     __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
239 }
240
241 static inline void outw( WORD value, WORD port )
242 {
243     __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
244 }
245
246 static inline void outl( DWORD value, WORD port )
247 {
248     __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
249 }
250
251 static void IO_port_init(void)
252 {
253         char temp[1024];
254         WCHAR tempW[1024];
255         static const WCHAR portsW[] = {'p','o','r','t','s',0};
256         static const WCHAR readW[] = {'r','e','a','d',0};
257         static const WCHAR writeW[] = {'w','r','i','t','e',0};
258         static const WCHAR asteriskW[] = {'*',0};
259
260         do_direct_port_access = 0;
261         /* Can we do that? */
262         if (!iopl(3)) {
263                 iopl(0);
264
265                 PROFILE_GetWineIniString( portsW, readW, asteriskW, tempW, 1024 );
266                 WideCharToMultiByte(CP_ACP, 0, tempW, -1, temp, 1024, NULL, NULL);
267                 do_IO_port_init_read_or_write(temp, IO_READ);
268                 PROFILE_GetWineIniString( portsW, writeW, asteriskW, tempW, 1024 );
269                 WideCharToMultiByte(CP_ACP, 0, tempW, -1, temp, 1024, NULL, NULL);
270                 do_IO_port_init_read_or_write(temp, IO_WRITE);
271         }
272     IO_FixCMOSCheckSum();
273 }
274
275 #endif  /* DIRECT_IO_ACCESS */
276
277 /**********************************************************************
278  *          IO_inport
279  *
280  * Note: The size argument has to be handled correctly _externally_
281  * (as we always return a DWORD)
282  */
283 DWORD IO_inport( int port, int size )
284 {
285     DWORD res = 0;
286
287     TRACE("%d-byte value from port 0x%02x\n", size, port );
288
289 #ifdef HAVE_PPDEV
290     if (do_pp_port_access == -1)
291       do_pp_port_access =IO_pp_init();
292     if ((do_pp_port_access == 0 ) && (size == 1))
293       if (!IO_pp_inp(port,&res))
294          return res;
295 #endif
296 #ifdef DIRECT_IO_ACCESS
297     if (do_direct_port_access == -1) IO_port_init();
298     if ((do_direct_port_access)
299         /* Make sure we have access to the port */
300         && (port_permissions[port] & IO_READ))
301     {
302         iopl(3);
303         switch(size)
304         {
305         case 1: res = inb( port ); break;
306         case 2: res = inw( port ); break;
307         case 4: res = inl( port ); break;
308         default:
309             ERR("invalid data size %d\n", size);
310         }
311         iopl(0);
312         return res;
313     }
314 #endif
315
316     /* first give the DOS VM a chance to handle it */
317     if (Dosvm.inport && Dosvm.inport( port, size, &res )) return res;
318
319     switch (port)
320     {
321     case 0x40:
322     case 0x41:
323     case 0x42:
324     {
325         BYTE chan = port & 3;
326         WORD tempval = 0;
327         if (tmr_8253[chan].latched)
328             tempval = tmr_8253[chan].latch;
329         else
330         {
331             dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
332             if (chan == 0) /* System timer counter divisor */
333             {
334                 /* FIXME: Dosvm.GetTimer() returns quite rigid values */
335                 if (Dosvm.GetTimer)
336                   tempval = dummy_ctr + (WORD)Dosvm.GetTimer();
337                 else
338                   tempval = dummy_ctr;
339             }
340             else
341             {
342                 /* FIXME: intelligent hardware timer emulation needed */
343                 tempval = dummy_ctr;
344             }
345         }
346
347         switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
348         {
349         case 0:
350             res = 0; /* shouldn't happen? */
351             break;
352         case 1: /* read lo byte */
353             res = (BYTE)tempval;
354             tmr_8253[chan].latched = FALSE;
355             break;
356         case 3: /* read lo byte, then hi byte */
357             tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
358             if (tmr_8253[chan].byte_toggle)
359             {
360                 res = (BYTE)tempval;
361                 break;
362             }
363             /* else [fall through if read hi byte !] */
364         case 2: /* read hi byte */
365             res = (BYTE)(tempval >> 8);
366             tmr_8253[chan].latched = FALSE;
367             break;
368         }
369     }
370     break;
371     case 0x60:
372 #if 0 /* what's this port got to do with parport ? */
373         res = (DWORD)parport_8255[0];
374 #endif
375         break;
376     case 0x61:
377         res = (DWORD)parport_8255[1];
378         break;
379     case 0x62:
380         res = (DWORD)parport_8255[2];
381         break;
382     case 0x70:
383         res = (DWORD)cmosaddress;
384         break;
385     case 0x71:
386         res = (DWORD)cmosimage[cmosaddress & 0x3f];
387         break;
388     case 0x200:
389     case 0x201:
390         res = 0xffffffff; /* no joystick */
391         break;
392     default:
393         WARN("Direct I/O read attempted from port %x\n", port);
394         res = 0xffffffff;
395         break;
396     }
397     TRACE("  returning ( 0x%lx )\n", res );
398     return res;
399 }
400
401
402 /**********************************************************************
403  *          IO_outport
404  */
405 void IO_outport( int port, int size, DWORD value )
406 {
407     TRACE("IO: 0x%lx (%d-byte value) to port 0x%02x\n",
408                  value, size, port );
409
410 #ifdef HAVE_PPDEV
411     if (do_pp_port_access == -1)
412       do_pp_port_access = IO_pp_init();
413     if ((do_pp_port_access == 0) && (size == 1))
414       if (!IO_pp_outp(port,&value))
415          return;
416 #endif
417 #ifdef DIRECT_IO_ACCESS
418
419     if (do_direct_port_access == -1) IO_port_init();
420     if ((do_direct_port_access)
421         /* Make sure we have access to the port */
422         && (port_permissions[port] & IO_WRITE))
423     {
424         iopl(3);
425         switch(size)
426         {
427         case 1: outb( LOBYTE(value), port ); break;
428         case 2: outw( LOWORD(value), port ); break;
429         case 4: outl( value, port ); break;
430         default:
431             WARN("Invalid data size %d\n", size);
432         }
433         iopl(0);
434         return;
435     }
436 #endif
437
438     /* first give the DOS VM a chance to handle it */
439     if (Dosvm.outport && Dosvm.outport( port, size, value )) return;
440
441     switch (port)
442     {
443     case 0x40:
444     case 0x41:
445     case 0x42:
446     {
447         BYTE chan = port & 3;
448
449         /* we need to get the oldval before any lo/hi byte change has been made */
450         if (((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
451             !tmr_8253[chan].byte_toggle)
452             tmr_8253[chan].oldval = tmr_8253[chan].countmax;
453         switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
454         {
455         case 0:
456             break; /* shouldn't happen? */
457         case 1: /* write lo byte */
458             tmr_8253[chan].countmax =
459                 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
460             break;
461         case 3: /* write lo byte, then hi byte */
462             tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
463             if (tmr_8253[chan].byte_toggle)
464             {
465                 tmr_8253[chan].countmax =
466                     (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
467                 break;
468             }
469             /* else [fall through if write hi byte !] */
470         case 2: /* write hi byte */
471             tmr_8253[chan].countmax =
472                 (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
473             break;
474         }
475         /* if programming is finished and value has changed
476            then update to new value */
477         if ((((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
478              !tmr_8253[chan].byte_toggle) &&
479             (tmr_8253[chan].countmax != tmr_8253[chan].oldval))
480             set_timer_maxval(chan, tmr_8253[chan].countmax);
481     }
482     break;
483     case 0x43:
484     {
485         BYTE chan = ((BYTE)value & 0xc0) >> 6;
486         /* ctrl byte for specific timer channel */
487         if (chan == 3)
488         {
489             FIXME("8254 timer readback not implemented yet\n");
490             break;
491         }
492         switch (((BYTE)value & 0x30) >> 4)
493         {
494         case 0: /* latch timer */
495             tmr_8253[chan].latched = TRUE;
496             dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
497             if (chan == 0) /* System timer divisor */
498                 if (Dosvm.GetTimer)
499                   tmr_8253[chan].latch = dummy_ctr + (WORD)Dosvm.GetTimer();
500                 else
501                   tmr_8253[chan].latch = dummy_ctr;
502             else
503             {
504                 /* FIXME: intelligent hardware timer emulation needed */
505                 tmr_8253[chan].latch = dummy_ctr;
506             }
507             break;
508         case 3: /* write lo byte, then hi byte */
509             tmr_8253[chan].byte_toggle = FALSE; /* init */
510             /* fall through */
511         case 1: /* write lo byte only */
512         case 2: /* write hi byte only */
513             tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
514             break;
515         }
516     }
517     break;
518     case 0x61:
519         parport_8255[1] = (BYTE)value;
520         if ((((BYTE)parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
521         {
522             TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
523             Beep(1193180 / tmr_8253[2].countmax, 20);
524         }
525         break;
526     case 0x70:
527         cmosaddress = (BYTE)value & 0x7f;
528         break;
529     case 0x71:
530         cmosimage[cmosaddress & 0x3f] = (BYTE)value;
531         break;
532     default:
533         WARN("Direct I/O write attempted to port %x\n", port );
534         break;
535     }
536 }