kernel32: Get rid of DOSMEM_AllocSelector.
[wine] / dlls / winedos / 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
30 #include <stdarg.h>
31 #include <stdlib.h>
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winnls.h"
36 #include "winreg.h"
37 #include "winternl.h"
38 #include "dosexe.h"
39 #include "vga.h"
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(int);
44
45 #if defined(linux) && defined(__i386__)
46 # define DIRECT_IO_ACCESS
47 #else
48 # undef DIRECT_IO_ACCESS
49 #endif  /* linux && __i386__ */
50
51 static struct {
52     WORD        countmax;
53     BOOL16      byte_toggle; /* if TRUE, then hi byte has already been written */
54     WORD        latch;
55     BOOL16      latched;
56     BYTE        ctrlbyte_ch;
57     WORD        oldval;
58 } tmr_8253[3] = {
59     {0xFFFF,    FALSE,  0,      FALSE,  0x36,   0},
60     {0x0012,    FALSE,  0,      FALSE,  0x74,   0},
61     {0x0001,    FALSE,  0,      FALSE,  0xB6,   0},
62 };
63
64 static int dummy_ctr = 0;
65
66 static BYTE parport_8255[4] = {0x4f, 0x20, 0xff, 0xff};
67
68 static BYTE cmosaddress;
69
70 /* if you change anything here, use IO_FixCMOSCheckSum below to compute
71  * the checksum and put the right values in.
72  */
73 static BYTE cmosimage[64] =
74 {
75   0x27, 0x34, 0x31, 0x47, 0x16, 0x15, 0x00, 0x01,
76   0x04, 0x94, 0x26, 0x02, 0x50, 0x80, 0x00, 0x00,
77   0x40, 0xb1, 0x00, 0x9c, 0x01, 0x80, 0x02, 0x00,
78   0x1c, 0x00, 0x00, 0xad, 0x02, 0x10, 0x00, 0x00,
79   0x08, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00,
80   0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0x19,  /* last 2 bytes are checksum */
81   0x00, 0x1c, 0x19, 0x81, 0x00, 0x0e, 0x00, 0x80,
82   0x1b, 0x7b, 0x21, 0x00, 0x00, 0x00, 0x05, 0x5f
83 };
84
85 #if 0
86 static void IO_FixCMOSCheckSum(void)
87 {
88         WORD sum = 0;
89         int i;
90
91         for (i=0x10; i < 0x2d; i++)
92                 sum += cmosimage[i];
93         cmosimage[0x2e] = sum >> 8; /* yes, this IS hi byte !! */
94         cmosimage[0x2f] = sum & 0xff;
95         MESSAGE("calculated hi %02x, lo %02x\n", cmosimage[0x2e], cmosimage[0x2f]);
96 }
97 #endif
98
99 #ifdef DIRECT_IO_ACCESS
100
101 extern int iopl(int level);
102 static char do_direct_port_access = -1;
103 static char port_permissions[0x10000];
104
105 #define IO_READ  1
106 #define IO_WRITE 2
107
108 #endif  /* DIRECT_IO_ACCESS */
109
110 #ifdef HAVE_PPDEV
111 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
112                                        0: available);*/
113 #endif
114
115 static void set_timer_maxval(unsigned timer, unsigned maxval)
116 {
117     switch (timer) {
118         case 0: /* System timer counter divisor */
119             DOSVM_SetTimer(maxval);
120             break;
121         case 1: /* RAM refresh */
122             FIXME("RAM refresh counter handling not implemented !\n");
123             break;
124         case 2: /* cassette & speaker */
125             /* speaker on ? */
126             if ((parport_8255[1] & 3) == 3)
127             {
128                 TRACE("Beep (freq: %d) !\n", 1193180 / maxval );
129                 Beep(1193180 / maxval, 20);
130             }
131             break;
132     }
133 }
134
135
136 /**********************************************************************
137  *          IO_port_init
138  */
139
140 /* set_IO_permissions(int val1, int val)
141  * Helper function for IO_port_init
142  */
143 #ifdef DIRECT_IO_ACCESS
144 static void set_IO_permissions(int val1, int val, char rw)
145 {
146         int j;
147         if (val1 != -1) {
148                 if (val == -1) val = 0x3ff;
149                 for (j = val1; j <= val; j++)
150                         port_permissions[j] |= rw;
151
152                 do_direct_port_access = 1;
153
154                 val1 = -1;
155         } else if (val != -1) {
156                 do_direct_port_access = 1;
157
158                 port_permissions[val] |= rw;
159         }
160
161 }
162
163 /* do_IO_port_init_read_or_write(char* temp, char rw)
164  * Helper function for IO_port_init
165  */
166
167 static void do_IO_port_init_read_or_write(const WCHAR *str, char rw)
168 {
169     int val, val1;
170     unsigned int i;
171     WCHAR *end;
172     static const WCHAR allW[] = {'a','l','l',0};
173
174     if (!strcmpiW(str, allW))
175     {
176         for (i=0; i < sizeof(port_permissions); i++)
177             port_permissions[i] |= rw;
178     }
179     else
180     {
181         val = -1;
182         val1 = -1;
183         while (*str)
184         {
185             switch(*str)
186             {
187             case ',':
188             case ' ':
189             case '\t':
190                 set_IO_permissions(val1, val, rw);
191                 val1 = -1;
192                 val = -1;
193                 str++;
194                 break;
195             case '-':
196                 val1 = val;
197                 if (val1 == -1) val1 = 0;
198                 str++;
199                 break;
200             default:
201                 if (isdigitW(*str))
202                 {
203                     val = strtoulW( str, &end, 0 );
204                     if (end == str)
205                     {
206                         val = -1;
207                         str++;
208                     }
209                     else str = end;
210                 }
211                 break;
212             }
213         }
214         set_IO_permissions(val1, val, rw);
215     }
216 }
217
218 static inline BYTE inb( WORD port )
219 {
220     BYTE b;
221     __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
222     return b;
223 }
224
225 static inline WORD inw( WORD port )
226 {
227     WORD w;
228     __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
229     return w;
230 }
231
232 static inline DWORD inl( WORD port )
233 {
234     DWORD dw;
235     __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
236     return dw;
237 }
238
239 static inline void outb( BYTE value, WORD port )
240 {
241     __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
242 }
243
244 static inline void outw( WORD value, WORD port )
245 {
246     __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
247 }
248
249 static inline void outl( DWORD value, WORD port )
250 {
251     __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
252 }
253
254 static void IO_port_init(void)
255 {
256     char tmp[1024];
257     HANDLE root, hkey;
258     DWORD dummy;
259     OBJECT_ATTRIBUTES attr;
260     UNICODE_STRING nameW;
261
262     static const WCHAR portsW[] = {'S','o','f','t','w','a','r','e','\\',
263                                    'W','i','n','e','\\','V','D','M','\\','P','o','r','t','s',0};
264     static const WCHAR readW[] = {'r','e','a','d',0};
265     static const WCHAR writeW[] = {'w','r','i','t','e',0};
266
267     do_direct_port_access = 0;
268     /* Can we do that? */
269     if (!iopl(3))
270     {
271         iopl(0);
272
273         RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
274         attr.Length = sizeof(attr);
275         attr.RootDirectory = root;
276         attr.ObjectName = &nameW;
277         attr.Attributes = 0;
278         attr.SecurityDescriptor = NULL;
279         attr.SecurityQualityOfService = NULL;
280         RtlInitUnicodeString( &nameW, portsW );
281
282         /* @@ Wine registry key: HKCU\Software\Wine\VDM\Ports */
283         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
284         {
285             RtlInitUnicodeString( &nameW, readW );
286             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
287             {
288                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
289                 do_IO_port_init_read_or_write(str, IO_READ);
290             }
291             RtlInitUnicodeString( &nameW, writeW );
292             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
293             {
294                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
295                 do_IO_port_init_read_or_write(str, IO_WRITE);
296             }
297             NtClose( hkey );
298         }
299         NtClose( root );
300     }
301 }
302
303 #endif  /* DIRECT_IO_ACCESS */
304
305
306 /**********************************************************************
307  *          inport   (WINEDOS.@)
308  *
309  * Note: The size argument has to be handled correctly _externally_
310  * (as we always return a DWORD)
311  */
312 DWORD WINAPI DOSVM_inport( int port, int size )
313 {
314     DWORD res = ~0U;
315
316     TRACE("%d-byte value from port 0x%04x\n", size, port );
317
318 #ifdef HAVE_PPDEV
319     if (do_pp_port_access == -1) do_pp_port_access =IO_pp_init();
320     if ((do_pp_port_access == 0 ) && (size == 1))
321     {
322         if (!IO_pp_inp(port,&res)) return res;
323     }
324 #endif
325
326 #ifdef DIRECT_IO_ACCESS
327     if (do_direct_port_access == -1) IO_port_init();
328     if ((do_direct_port_access)
329         /* Make sure we have access to the port */
330         && (port_permissions[port] & IO_READ))
331     {
332         iopl(3);
333         switch(size)
334         {
335         case 1: res = inb( port ); break;
336         case 2: res = inw( port ); break;
337         case 4: res = inl( port ); break;
338         }
339         iopl(0);
340         return res;
341     }
342 #endif
343
344     switch (port)
345     {
346     case 0x40:
347     case 0x41:
348     case 0x42:
349         {
350             BYTE chan = port & 3;
351             WORD tempval = 0;
352             if (tmr_8253[chan].latched)
353                 tempval = tmr_8253[chan].latch;
354             else
355             {
356                 dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
357                 if (chan == 0) /* System timer counter divisor */
358                 {
359                     /* FIXME: DOSVM_GetTimer() returns quite rigid values */
360                     tempval = dummy_ctr + (WORD)DOSVM_GetTimer();
361                 }
362                 else
363                 {
364                     /* FIXME: intelligent hardware timer emulation needed */
365                     tempval = dummy_ctr;
366                 }
367             }
368
369             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
370             {
371             case 0:
372                 res = 0; /* shouldn't happen? */
373                 break;
374             case 1: /* read lo byte */
375                 res = (BYTE)tempval;
376                 tmr_8253[chan].latched = FALSE;
377                 break;
378             case 3: /* read lo byte, then hi byte */
379                 tmr_8253[chan].byte_toggle ^= 1; /* toggle */
380                 if (tmr_8253[chan].byte_toggle)
381                 {
382                     res = (BYTE)tempval;
383                     break;
384                 }
385                 /* else [fall through if read hi byte !] */
386             case 2: /* read hi byte */
387                 res = (BYTE)(tempval >> 8);
388                 tmr_8253[chan].latched = FALSE;
389                 break;
390             }
391         }
392         break;
393     case 0x60:
394         res = DOSVM_Int09ReadScan(NULL);
395         break;
396     case 0x61:
397         res = (DWORD)parport_8255[1];
398         break;
399     case 0x62:
400         res = (DWORD)parport_8255[2];
401         break;
402     case 0x70:
403         res = (DWORD)cmosaddress;
404         break;
405     case 0x71:
406         res = (DWORD)cmosimage[cmosaddress & 0x3f];
407         break;
408     case 0x200:
409     case 0x201:
410         res = ~0U; /* no joystick */
411         break;
412     case 0x22a:
413     case 0x22c:
414     case 0x22e:
415         res = (DWORD)SB_ioport_in( port );
416         break;
417     case 0x3ba:
418     case 0x3c0:
419     case 0x3c1:
420     case 0x3c2:
421     case 0x3c3:
422     case 0x3c4:
423     case 0x3c5:
424     case 0x3c6:
425     case 0x3c7:
426     case 0x3c8:
427     case 0x3c9:
428     case 0x3ca:
429     case 0x3cb:
430     case 0x3cc:
431     case 0x3cd:
432     case 0x3ce:
433     case 0x3cf:
434     case 0x3d0:
435     case 0x3d1:
436     case 0x3d2:
437     case 0x3d3:
438     case 0x3d4:
439     case 0x3d5:
440     case 0x3d6:
441     case 0x3d7:
442     case 0x3d8:
443     case 0x3d9:
444     case 0x3da:
445     case 0x3db:
446     case 0x3dc:
447     case 0x3dd:
448     case 0x3de:
449     case 0x3df:
450         if(size > 1)
451            FIXME("Trying to read more than one byte from VGA!\n");
452         res = (DWORD)VGA_ioport_in( port );
453         break;
454     case 0x00:
455     case 0x01:
456     case 0x02:
457     case 0x03:
458     case 0x04:
459     case 0x05:
460     case 0x06:
461     case 0x07:
462     case 0xC0:
463     case 0xC2:
464     case 0xC4:
465     case 0xC6:
466     case 0xC8:
467     case 0xCA:
468     case 0xCC:
469     case 0xCE:
470     case 0x87:
471     case 0x83:
472     case 0x81:
473     case 0x82:
474     case 0x8B:
475     case 0x89:
476     case 0x8A:
477     case 0x487:
478     case 0x483:
479     case 0x481:
480     case 0x482:
481     case 0x48B:
482     case 0x489:
483     case 0x48A:
484     case 0x08:
485     case 0xD0:
486     case 0x0D:
487     case 0xDA:
488         res = (DWORD)DMA_ioport_in( port );
489         break;
490     default:
491         WARN("Direct I/O read attempted from port %x\n", port);
492         break;
493     }
494     return res;
495 }
496
497
498 /**********************************************************************
499  *          outport  (WINEDOS.@)
500  */
501 void WINAPI DOSVM_outport( int port, int size, DWORD value )
502 {
503     TRACE("IO: 0x%lx (%d-byte value) to port 0x%04x\n", value, size, port );
504
505 #ifdef HAVE_PPDEV
506     if (do_pp_port_access == -1) do_pp_port_access = IO_pp_init();
507     if ((do_pp_port_access == 0) && (size == 1))
508     {
509         if (!IO_pp_outp(port,&value)) return;
510     }
511 #endif
512
513 #ifdef DIRECT_IO_ACCESS
514
515     if (do_direct_port_access == -1) IO_port_init();
516     if ((do_direct_port_access)
517         /* Make sure we have access to the port */
518         && (port_permissions[port] & IO_WRITE))
519     {
520         iopl(3);
521         switch(size)
522         {
523         case 1: outb( LOBYTE(value), port ); break;
524         case 2: outw( LOWORD(value), port ); break;
525         case 4: outl( value, port ); break;
526         }
527         iopl(0);
528         return;
529     }
530 #endif
531
532     switch (port)
533     {
534     case 0x20:
535         DOSVM_PIC_ioport_out( port, (BYTE)value );
536         break;
537     case 0x40:
538     case 0x41:
539     case 0x42:
540         {
541             BYTE chan = port & 3;
542
543             /* we need to get the oldval before any lo/hi byte change has been made */
544             if (((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
545                 !tmr_8253[chan].byte_toggle)
546                 tmr_8253[chan].oldval = tmr_8253[chan].countmax;
547             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
548             {
549             case 0:
550                 break; /* shouldn't happen? */
551             case 1: /* write lo byte */
552                 tmr_8253[chan].countmax =
553                     (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
554                 break;
555             case 3: /* write lo byte, then hi byte */
556                 tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
557                 if (tmr_8253[chan].byte_toggle)
558                 {
559                     tmr_8253[chan].countmax =
560                         (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
561                     break;
562                 }
563                 /* else [fall through if write hi byte !] */
564             case 2: /* write hi byte */
565                 tmr_8253[chan].countmax =
566                     (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
567                 break;
568             }
569             /* if programming is finished and value has changed
570                then update to new value */
571             if ((((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
572                  !tmr_8253[chan].byte_toggle) &&
573                 (tmr_8253[chan].countmax != tmr_8253[chan].oldval))
574                 set_timer_maxval(chan, tmr_8253[chan].countmax);
575         }
576         break;
577     case 0x43:
578        {
579            BYTE chan = ((BYTE)value & 0xc0) >> 6;
580            /* ctrl byte for specific timer channel */
581            if (chan == 3)
582            {
583                FIXME("8254 timer readback not implemented yet\n");
584                break;
585            }
586            switch (((BYTE)value & 0x30) >> 4)
587            {
588            case 0:      /* latch timer */
589                tmr_8253[chan].latched = TRUE;
590                dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
591                if (chan == 0) /* System timer divisor */
592                    tmr_8253[chan].latch = dummy_ctr + (WORD)DOSVM_GetTimer();
593                else
594                {
595                    /* FIXME: intelligent hardware timer emulation needed */
596                    tmr_8253[chan].latch = dummy_ctr;
597                }
598                break;
599            case 3:      /* write lo byte, then hi byte */
600                tmr_8253[chan].byte_toggle = FALSE; /* init */
601                /* fall through */
602            case 1:      /* write lo byte only */
603            case 2:      /* write hi byte only */
604                tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
605                break;
606            }
607        }
608        break;
609     case 0x61:
610         parport_8255[1] = (BYTE)value;
611         if ((((BYTE)parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
612         {
613             TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
614             Beep(1193180 / tmr_8253[2].countmax, 20);
615         }
616         break;
617     case 0x70:
618         cmosaddress = (BYTE)value & 0x7f;
619         break;
620     case 0x71:
621         cmosimage[cmosaddress & 0x3f] = (BYTE)value;
622         break;
623     case 0x226:
624     case 0x22c:
625         SB_ioport_out( port, (BYTE)value );
626         break;
627     case 0x3c0:
628     case 0x3c1:
629     case 0x3c2:
630     case 0x3c3:
631     case 0x3c4:
632     case 0x3c5:
633     case 0x3c6:
634     case 0x3c7:
635     case 0x3c8:
636     case 0x3c9:
637     case 0x3ca:
638     case 0x3cb:
639     case 0x3cc:
640     case 0x3cd:
641     case 0x3ce:
642     case 0x3cf:
643     case 0x3d0:
644     case 0x3d1:
645     case 0x3d2:
646     case 0x3d3:
647     case 0x3d4:
648     case 0x3d5:
649     case 0x3d6:
650     case 0x3d7:
651     case 0x3d8:
652     case 0x3d9:
653     case 0x3da:
654     case 0x3db:
655     case 0x3dc:
656     case 0x3dd:
657     case 0x3de:
658     case 0x3df:
659         VGA_ioport_out( port, LOBYTE(value) );
660         if(size > 1) {
661             VGA_ioport_out( port+1, HIBYTE(value) );
662             if(size > 2) {
663                 VGA_ioport_out( port+2, LOBYTE(HIWORD(value)) );
664                 VGA_ioport_out( port+3, HIBYTE(HIWORD(value)) );
665             }
666         }
667         break;
668     case 0x00:
669     case 0x01:
670     case 0x02:
671     case 0x03:
672     case 0x04:
673     case 0x05:
674     case 0x06:
675     case 0x07:
676     case 0xC0:
677     case 0xC2:
678     case 0xC4:
679     case 0xC6:
680     case 0xC8:
681     case 0xCA:
682     case 0xCC:
683     case 0xCE:
684     case 0x87:
685     case 0x83:
686     case 0x81:
687     case 0x82:
688     case 0x8B:
689     case 0x89:
690     case 0x8A:
691     case 0x487:
692     case 0x483:
693     case 0x481:
694     case 0x482:
695     case 0x48B:
696     case 0x489:
697     case 0x48A:
698     case 0x08:
699     case 0xD0:
700     case 0x0B:
701     case 0xD6:
702     case 0x0A:
703     case 0xD4:
704     case 0x0F:
705     case 0xDE:
706     case 0x09:
707     case 0xD2:
708     case 0x0C:
709     case 0xD8:
710     case 0x0D:
711     case 0xDA:
712     case 0x0E:
713     case 0xDC:
714         DMA_ioport_out( port, (BYTE)value );
715         break;
716     default:
717         WARN("Direct I/O write attempted to port %x\n", port );
718         break;
719     }
720 }