Implemented CoReleaseMarshalData.
[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, i;
170     WCHAR *end;
171     static const WCHAR allW[] = {'a','l','l',0};
172
173     if (!strcmpiW(str, allW))
174     {
175         for (i=0; i < sizeof(port_permissions); i++)
176             port_permissions[i] |= rw;
177     }
178     else
179     {
180         val = -1;
181         val1 = -1;
182         while (*str)
183         {
184             switch(*str)
185             {
186             case ',':
187             case ' ':
188             case '\t':
189                 set_IO_permissions(val1, val, rw);
190                 val1 = -1;
191                 val = -1;
192                 str++;
193                 break;
194             case '-':
195                 val1 = val;
196                 if (val1 == -1) val1 = 0;
197                 str++;
198                 break;
199             default:
200                 if (isdigitW(*str))
201                 {
202                     val = strtoulW( str, &end, 0 );
203                     if (end == str)
204                     {
205                         val = -1;
206                         str++;
207                     }
208                     else str = end;
209                 }
210                 break;
211             }
212         }
213         set_IO_permissions(val1, val, rw);
214     }
215 }
216
217 static inline BYTE inb( WORD port )
218 {
219     BYTE b;
220     __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
221     return b;
222 }
223
224 static inline WORD inw( WORD port )
225 {
226     WORD w;
227     __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
228     return w;
229 }
230
231 static inline DWORD inl( WORD port )
232 {
233     DWORD dw;
234     __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
235     return dw;
236 }
237
238 static inline void outb( BYTE value, WORD port )
239 {
240     __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
241 }
242
243 static inline void outw( WORD value, WORD port )
244 {
245     __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
246 }
247
248 static inline void outl( DWORD value, WORD port )
249 {
250     __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
251 }
252
253 static void IO_port_init(void)
254 {
255     char tmp[1024];
256     HKEY hkey;
257     DWORD dummy;
258     OBJECT_ATTRIBUTES attr;
259     UNICODE_STRING nameW;
260
261     static const WCHAR portsW[] = {'M','a','c','h','i','n','e','\\',
262                                    'S','o','f','t','w','a','r','e','\\',
263                                    'W','i','n','e','\\','W','i','n','e','\\',
264                                    'C','o','n','f','i','g','\\','P','o','r','t','s',0};
265     static const WCHAR readW[] = {'r','e','a','d',0};
266     static const WCHAR writeW[] = {'w','r','i','t','e',0};
267
268     do_direct_port_access = 0;
269     /* Can we do that? */
270     if (!iopl(3))
271     {
272         iopl(0);
273
274         attr.Length = sizeof(attr);
275         attr.RootDirectory = 0;
276         attr.ObjectName = &nameW;
277         attr.Attributes = 0;
278         attr.SecurityDescriptor = NULL;
279         attr.SecurityQualityOfService = NULL;
280         RtlInitUnicodeString( &nameW, portsW );
281
282         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
283         {
284             RtlInitUnicodeString( &nameW, readW );
285             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
286             {
287                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
288                 do_IO_port_init_read_or_write(str, IO_READ);
289             }
290             RtlInitUnicodeString( &nameW, writeW );
291             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
292             {
293                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
294                 do_IO_port_init_read_or_write(str, IO_WRITE);
295             }
296             NtClose( hkey );
297         }
298     }
299 }
300
301 #endif  /* DIRECT_IO_ACCESS */
302
303
304 /**********************************************************************
305  *          inport   (WINEDOS.@)
306  *
307  * Note: The size argument has to be handled correctly _externally_
308  * (as we always return a DWORD)
309  */
310 DWORD WINAPI DOSVM_inport( int port, int size )
311 {
312     DWORD res = ~0U;
313
314     TRACE("%d-byte value from port 0x%04x\n", size, port );
315
316 #ifdef HAVE_PPDEV
317     if (do_pp_port_access == -1) do_pp_port_access =IO_pp_init();
318     if ((do_pp_port_access == 0 ) && (size == 1))
319     {
320         if (!IO_pp_inp(port,&res)) return res;
321     }
322 #endif
323
324 #ifdef DIRECT_IO_ACCESS
325     if (do_direct_port_access == -1) IO_port_init();
326     if ((do_direct_port_access)
327         /* Make sure we have access to the port */
328         && (port_permissions[port] & IO_READ))
329     {
330         iopl(3);
331         switch(size)
332         {
333         case 1: res = inb( port ); break;
334         case 2: res = inw( port ); break;
335         case 4: res = inl( port ); break;
336         }
337         iopl(0);
338         return res;
339     }
340 #endif
341
342     switch (port)
343     {
344     case 0x40:
345     case 0x41:
346     case 0x42:
347         {
348             BYTE chan = port & 3;
349             WORD tempval = 0;
350             if (tmr_8253[chan].latched)
351                 tempval = tmr_8253[chan].latch;
352             else
353             {
354                 dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
355                 if (chan == 0) /* System timer counter divisor */
356                 {
357                     /* FIXME: DOSVM_GetTimer() returns quite rigid values */
358                     tempval = dummy_ctr + (WORD)DOSVM_GetTimer();
359                 }
360                 else
361                 {
362                     /* FIXME: intelligent hardware timer emulation needed */
363                     tempval = dummy_ctr;
364                 }
365             }
366
367             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
368             {
369             case 0:
370                 res = 0; /* shouldn't happen? */
371                 break;
372             case 1: /* read lo byte */
373                 res = (BYTE)tempval;
374                 tmr_8253[chan].latched = FALSE;
375                 break;
376             case 3: /* read lo byte, then hi byte */
377                 tmr_8253[chan].byte_toggle ^= 1; /* toggle */
378                 if (tmr_8253[chan].byte_toggle)
379                 {
380                     res = (BYTE)tempval;
381                     break;
382                 }
383                 /* else [fall through if read hi byte !] */
384             case 2: /* read hi byte */
385                 res = (BYTE)(tempval >> 8);
386                 tmr_8253[chan].latched = FALSE;
387                 break;
388             }
389         }
390         break;
391     case 0x60:
392         res = DOSVM_Int09ReadScan(NULL);
393         break;
394     case 0x61:
395         res = (DWORD)parport_8255[1];
396         break;
397     case 0x62:
398         res = (DWORD)parport_8255[2];
399         break;
400     case 0x70:
401         res = (DWORD)cmosaddress;
402         break;
403     case 0x71:
404         res = (DWORD)cmosimage[cmosaddress & 0x3f];
405         break;
406     case 0x200:
407     case 0x201:
408         res = ~0U; /* no joystick */
409         break;
410     case 0x22a:
411     case 0x22c:
412     case 0x22e:
413         res = (DWORD)SB_ioport_in( port );
414         break;
415     case 0x3ba:
416     case 0x3c0:
417     case 0x3c1:
418     case 0x3c2:
419     case 0x3c3:
420     case 0x3c4:
421     case 0x3c5:
422     case 0x3c6:
423     case 0x3c7:
424     case 0x3c8:
425     case 0x3c9:
426     case 0x3ca:
427     case 0x3cb:
428     case 0x3cc:
429     case 0x3cd:
430     case 0x3ce:
431     case 0x3cf:
432     case 0x3d0:
433     case 0x3d1:
434     case 0x3d2:
435     case 0x3d3:
436     case 0x3d4:
437     case 0x3d5:
438     case 0x3d6:
439     case 0x3d7:
440     case 0x3d8:
441     case 0x3d9:
442     case 0x3da:
443     case 0x3db:
444     case 0x3dc:
445     case 0x3dd:
446     case 0x3de:
447     case 0x3df:
448         if(size > 1)
449            FIXME("Trying to read more than one byte from VGA!\n");
450         res = (DWORD)VGA_ioport_in( port );
451         break;
452     case 0x00:
453     case 0x01:
454     case 0x02:
455     case 0x03:
456     case 0x04:
457     case 0x05:
458     case 0x06:
459     case 0x07:
460     case 0xC0:
461     case 0xC2:
462     case 0xC4:
463     case 0xC6:
464     case 0xC8:
465     case 0xCA:
466     case 0xCC:
467     case 0xCE:
468     case 0x87:
469     case 0x83:
470     case 0x81:
471     case 0x82:
472     case 0x8B:
473     case 0x89:
474     case 0x8A:
475     case 0x487:
476     case 0x483:
477     case 0x481:
478     case 0x482:
479     case 0x48B:
480     case 0x489:
481     case 0x48A:
482     case 0x08:
483     case 0xD0:
484     case 0x0D:
485     case 0xDA:
486         res = (DWORD)DMA_ioport_in( port );
487         break;
488     default:
489         WARN("Direct I/O read attempted from port %x\n", port);
490         break;
491     }
492     return res;
493 }
494
495
496 /**********************************************************************
497  *          outport  (WINEDOS.@)
498  */
499 void WINAPI DOSVM_outport( int port, int size, DWORD value )
500 {
501     TRACE("IO: 0x%lx (%d-byte value) to port 0x%04x\n", value, size, port );
502
503 #ifdef HAVE_PPDEV
504     if (do_pp_port_access == -1) do_pp_port_access = IO_pp_init();
505     if ((do_pp_port_access == 0) && (size == 1))
506     {
507         if (!IO_pp_outp(port,&value)) return;
508     }
509 #endif
510
511 #ifdef DIRECT_IO_ACCESS
512
513     if (do_direct_port_access == -1) IO_port_init();
514     if ((do_direct_port_access)
515         /* Make sure we have access to the port */
516         && (port_permissions[port] & IO_WRITE))
517     {
518         iopl(3);
519         switch(size)
520         {
521         case 1: outb( LOBYTE(value), port ); break;
522         case 2: outw( LOWORD(value), port ); break;
523         case 4: outl( value, port ); break;
524         }
525         iopl(0);
526         return;
527     }
528 #endif
529
530     switch (port)
531     {
532     case 0x20:
533         DOSVM_PIC_ioport_out( port, (BYTE)value );
534         break;
535     case 0x40:
536     case 0x41:
537     case 0x42:
538         {
539             BYTE chan = port & 3;
540
541             /* we need to get the oldval before any lo/hi byte change has been made */
542             if (((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
543                 !tmr_8253[chan].byte_toggle)
544                 tmr_8253[chan].oldval = tmr_8253[chan].countmax;
545             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
546             {
547             case 0:
548                 break; /* shouldn't happen? */
549             case 1: /* write lo byte */
550                 tmr_8253[chan].countmax =
551                     (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
552                 break;
553             case 3: /* write lo byte, then hi byte */
554                 tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
555                 if (tmr_8253[chan].byte_toggle)
556                 {
557                     tmr_8253[chan].countmax =
558                         (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
559                     break;
560                 }
561                 /* else [fall through if write hi byte !] */
562             case 2: /* write hi byte */
563                 tmr_8253[chan].countmax =
564                     (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
565                 break;
566             }
567             /* if programming is finished and value has changed
568                then update to new value */
569             if ((((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
570                  !tmr_8253[chan].byte_toggle) &&
571                 (tmr_8253[chan].countmax != tmr_8253[chan].oldval))
572                 set_timer_maxval(chan, tmr_8253[chan].countmax);
573         }
574         break;
575     case 0x43:
576        {
577            BYTE chan = ((BYTE)value & 0xc0) >> 6;
578            /* ctrl byte for specific timer channel */
579            if (chan == 3)
580            {
581                FIXME("8254 timer readback not implemented yet\n");
582                break;
583            }
584            switch (((BYTE)value & 0x30) >> 4)
585            {
586            case 0:      /* latch timer */
587                tmr_8253[chan].latched = TRUE;
588                dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
589                if (chan == 0) /* System timer divisor */
590                    tmr_8253[chan].latch = dummy_ctr + (WORD)DOSVM_GetTimer();
591                else
592                {
593                    /* FIXME: intelligent hardware timer emulation needed */
594                    tmr_8253[chan].latch = dummy_ctr;
595                }
596                break;
597            case 3:      /* write lo byte, then hi byte */
598                tmr_8253[chan].byte_toggle = FALSE; /* init */
599                /* fall through */
600            case 1:      /* write lo byte only */
601            case 2:      /* write hi byte only */
602                tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
603                break;
604            }
605        }
606        break;
607     case 0x61:
608         parport_8255[1] = (BYTE)value;
609         if ((((BYTE)parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
610         {
611             TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
612             Beep(1193180 / tmr_8253[2].countmax, 20);
613         }
614         break;
615     case 0x70:
616         cmosaddress = (BYTE)value & 0x7f;
617         break;
618     case 0x71:
619         cmosimage[cmosaddress & 0x3f] = (BYTE)value;
620         break;
621     case 0x226:
622     case 0x22c:
623         SB_ioport_out( port, (BYTE)value );
624         break;
625     case 0x3c0:
626     case 0x3c1:
627     case 0x3c2:
628     case 0x3c3:
629     case 0x3c4:
630     case 0x3c5:
631     case 0x3c6:
632     case 0x3c7:
633     case 0x3c8:
634     case 0x3c9:
635     case 0x3ca:
636     case 0x3cb:
637     case 0x3cc:
638     case 0x3cd:
639     case 0x3ce:
640     case 0x3cf:
641     case 0x3d0:
642     case 0x3d1:
643     case 0x3d2:
644     case 0x3d3:
645     case 0x3d4:
646     case 0x3d5:
647     case 0x3d6:
648     case 0x3d7:
649     case 0x3d8:
650     case 0x3d9:
651     case 0x3da:
652     case 0x3db:
653     case 0x3dc:
654     case 0x3dd:
655     case 0x3de:
656     case 0x3df:
657         VGA_ioport_out( port, LOBYTE(value) );
658         if(size > 1) {
659             VGA_ioport_out( port+1, HIBYTE(value) );
660             if(size > 2) {
661                 VGA_ioport_out( port+2, LOBYTE(HIWORD(value)) );
662                 VGA_ioport_out( port+3, HIBYTE(HIWORD(value)) );
663             }
664         }
665         break;
666     case 0x00:
667     case 0x01:
668     case 0x02:
669     case 0x03:
670     case 0x04:
671     case 0x05:
672     case 0x06:
673     case 0x07:
674     case 0xC0:
675     case 0xC2:
676     case 0xC4:
677     case 0xC6:
678     case 0xC8:
679     case 0xCA:
680     case 0xCC:
681     case 0xCE:
682     case 0x87:
683     case 0x83:
684     case 0x81:
685     case 0x82:
686     case 0x8B:
687     case 0x89:
688     case 0x8A:
689     case 0x487:
690     case 0x483:
691     case 0x481:
692     case 0x482:
693     case 0x48B:
694     case 0x489:
695     case 0x48A:
696     case 0x08:
697     case 0xD0:
698     case 0x0B:
699     case 0xD6:
700     case 0x0A:
701     case 0xD4:
702     case 0x0F:
703     case 0xDE:
704     case 0x09:
705     case 0xD2:
706     case 0x0C:
707     case 0xD8:
708     case 0x0D:
709     case 0xDA:
710     case 0x0E:
711     case 0xDC:
712         DMA_ioport_out( port, (BYTE)value );
713         break;
714     default:
715         WARN("Direct I/O write attempted to port %x\n", port );
716         break;
717     }
718 }