ole32: Add a helper function to return the file size and modify EnsureExists to use it.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 static int cmos_image_initialized = 0;
71
72 static BYTE cmosimage[64] =
73 {
74   0x27, /* 0x00: seconds */
75   0x34, /* 0X01: seconds alarm */
76   0x31, /* 0x02: minutes */
77   0x47, /* 0x03: minutes alarm */
78   0x16, /* 0x04: hour */
79   0x15, /* 0x05: hour alarm */
80   0x00, /* 0x06: week day */
81   0x01, /* 0x07: month day */
82   0x04, /* 0x08: month */
83   0x94, /* 0x09: year */
84   0x26, /* 0x0a: state A */
85   0x02, /* 0x0b: state B */
86   0x50, /* 0x0c: state C */
87   0x80, /* 0x0d: state D */
88   0x00, /* 0x0e: state diagnostic */
89   0x00, /* 0x0f: state state shutdown */
90   0x40, /* 0x10: floppy type */
91   0xb1, /* 0x11: reserved */
92   0x00, /* 0x12: HD type */
93   0x9c, /* 0x13: reserved */
94   0x01, /* 0x14: equipment */
95   0x80, /* 0x15: low base memory */
96   0x02, /* 0x16: high base memory (0x280 => 640KB) */
97   0x00, /* 0x17: low extended memory */
98   0x3b, /* 0x18: high extended memory (0x3b00 => 15MB) */
99   0x00, /* 0x19: HD 1 extended type byte */
100   0x00, /* 0x1a: HD 2 extended type byte */
101   0xad, /* 0x1b: reserved */
102   0x02, /* 0x1c: reserved */
103   0x10, /* 0x1d: reserved */
104   0x00, /* 0x1e: reserved */
105   0x00, /* 0x1f: installed features */
106   0x08, /* 0x20: HD 1 low cylinder number */
107   0x00, /* 0x21: HD 1 high cylinder number */
108   0x00, /* 0x22: HD 1 heads */
109   0x26, /* 0x23: HD 1 low pre-compensation start */
110   0x00, /* 0x24: HD 1 high pre-compensation start */
111   0x00, /* 0x25: HD 1 low landing zone */
112   0x00, /* 0x26: HD 1 high landing zone */
113   0x00, /* 0x27: HD 1 sectors */
114   0x00, /* 0x28: options 1 */
115   0x00, /* 0x29: reserved */
116   0x00, /* 0x2a: reserved */
117   0x00, /* 0x2b: options 2 */
118   0x00, /* 0x2c: options 3 */
119   0x3f, /* 0x2d: reserved  */
120   0xcc, /* 0x2e: low CMOS ram checksum (computed automatically) */
121   0xcc, /* 0x2f: high CMOS ram checksum (computed automatically) */
122   0x00, /* 0x30: low extended memory byte */
123   0x1c, /* 0x31: high extended memory byte */
124   0x19, /* 0x32: century byte */
125   0x81, /* 0x33: setup information */
126   0x00, /* 0x34: CPU speed */
127   0x0e, /* 0x35: HD 2 low cylinder number */
128   0x00, /* 0x36: HD 2 high cylinder number */
129   0x80, /* 0x37: HD 2 heads */
130   0x1b, /* 0x38: HD 2 low pre-compensation start */
131   0x7b, /* 0x39: HD 2 high pre-compensation start */
132   0x21, /* 0x3a: HD 2 low landing zone */
133   0x00, /* 0x3b: HD 2 high landing zone */
134   0x00, /* 0x3c: HD 2 sectors */
135   0x00, /* 0x3d: reserved */
136   0x05, /* 0x3e: reserved */
137   0x5f  /* 0x3f: reserved */
138 };
139
140 static void IO_FixCMOSCheckSum(void)
141 {
142         WORD sum = 0;
143         int i;
144
145         for (i=0x10; i < 0x2d; i++)
146                 sum += cmosimage[i];
147         cmosimage[0x2e] = sum >> 8; /* yes, this IS hi byte !! */
148         cmosimage[0x2f] = sum & 0xff;
149         TRACE("calculated hi %02x, lo %02x\n", cmosimage[0x2e], cmosimage[0x2f]);
150 }
151
152 #ifdef DIRECT_IO_ACCESS
153
154 extern int iopl(int level);
155 static char do_direct_port_access = -1;
156 static char port_permissions[0x10000];
157
158 #define IO_READ  1
159 #define IO_WRITE 2
160
161 #endif  /* DIRECT_IO_ACCESS */
162
163 #ifdef HAVE_PPDEV
164 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
165                                        0: available);*/
166 #endif
167
168 static void set_timer_maxval(unsigned timer, unsigned maxval)
169 {
170     switch (timer) {
171         case 0: /* System timer counter divisor */
172             DOSVM_SetTimer(maxval);
173             break;
174         case 1: /* RAM refresh */
175             FIXME("RAM refresh counter handling not implemented !\n");
176             break;
177         case 2: /* cassette & speaker */
178             /* speaker on ? */
179             if ((parport_8255[1] & 3) == 3)
180             {
181                 TRACE("Beep (freq: %d) !\n", 1193180 / maxval );
182                 Beep(1193180 / maxval, 20);
183             }
184             break;
185     }
186 }
187
188
189 /**********************************************************************
190  *          IO_port_init
191  */
192
193 /* set_IO_permissions(int val1, int val)
194  * Helper function for IO_port_init
195  */
196 #ifdef DIRECT_IO_ACCESS
197 static void set_IO_permissions(int val1, int val, char rw)
198 {
199         int j;
200         if (val1 != -1) {
201                 if (val == -1) val = 0x3ff;
202                 for (j = val1; j <= val; j++)
203                         port_permissions[j] |= rw;
204
205                 do_direct_port_access = 1;
206
207                 val1 = -1;
208         } else if (val != -1) {
209                 do_direct_port_access = 1;
210
211                 port_permissions[val] |= rw;
212         }
213
214 }
215
216 /* do_IO_port_init_read_or_write(char* temp, char rw)
217  * Helper function for IO_port_init
218  */
219
220 static void do_IO_port_init_read_or_write(const WCHAR *str, char rw)
221 {
222     int val, val1;
223     unsigned int i;
224     WCHAR *end;
225     static const WCHAR allW[] = {'a','l','l',0};
226
227     if (!strcmpiW(str, allW))
228     {
229         for (i=0; i < sizeof(port_permissions); i++)
230             port_permissions[i] |= rw;
231     }
232     else
233     {
234         val = -1;
235         val1 = -1;
236         while (*str)
237         {
238             switch(*str)
239             {
240             case ',':
241             case ' ':
242             case '\t':
243                 set_IO_permissions(val1, val, rw);
244                 val1 = -1;
245                 val = -1;
246                 str++;
247                 break;
248             case '-':
249                 val1 = val;
250                 if (val1 == -1) val1 = 0;
251                 str++;
252                 break;
253             default:
254                 if (isdigitW(*str))
255                 {
256                     val = strtoulW( str, &end, 0 );
257                     if (end == str)
258                     {
259                         val = -1;
260                         str++;
261                     }
262                     else str = end;
263                 }
264                 break;
265             }
266         }
267         set_IO_permissions(val1, val, rw);
268     }
269 }
270
271 static inline BYTE inb( WORD port )
272 {
273     BYTE b;
274     __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
275     return b;
276 }
277
278 static inline WORD inw( WORD port )
279 {
280     WORD w;
281     __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
282     return w;
283 }
284
285 static inline DWORD inl( WORD port )
286 {
287     DWORD dw;
288     __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
289     return dw;
290 }
291
292 static inline void outb( BYTE value, WORD port )
293 {
294     __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
295 }
296
297 static inline void outw( WORD value, WORD port )
298 {
299     __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
300 }
301
302 static inline void outl( DWORD value, WORD port )
303 {
304     __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
305 }
306
307 static void IO_port_init(void)
308 {
309     char tmp[1024];
310     HANDLE root, hkey;
311     DWORD dummy;
312     OBJECT_ATTRIBUTES attr;
313     UNICODE_STRING nameW;
314
315     static const WCHAR portsW[] = {'S','o','f','t','w','a','r','e','\\',
316                                    'W','i','n','e','\\','V','D','M','\\','P','o','r','t','s',0};
317     static const WCHAR readW[] = {'r','e','a','d',0};
318     static const WCHAR writeW[] = {'w','r','i','t','e',0};
319
320     do_direct_port_access = 0;
321     /* Can we do that? */
322     if (!iopl(3))
323     {
324         iopl(0);
325
326         RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
327         attr.Length = sizeof(attr);
328         attr.RootDirectory = root;
329         attr.ObjectName = &nameW;
330         attr.Attributes = 0;
331         attr.SecurityDescriptor = NULL;
332         attr.SecurityQualityOfService = NULL;
333         RtlInitUnicodeString( &nameW, portsW );
334
335         /* @@ Wine registry key: HKCU\Software\Wine\VDM\Ports */
336         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
337         {
338             RtlInitUnicodeString( &nameW, readW );
339             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
340             {
341                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
342                 do_IO_port_init_read_or_write(str, IO_READ);
343             }
344             RtlInitUnicodeString( &nameW, writeW );
345             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
346             {
347                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
348                 do_IO_port_init_read_or_write(str, IO_WRITE);
349             }
350             NtClose( hkey );
351         }
352         NtClose( root );
353     }
354 }
355
356 #endif  /* DIRECT_IO_ACCESS */
357
358
359 /**********************************************************************
360  *          inport   (WINEDOS.@)
361  *
362  * Note: The size argument has to be handled correctly _externally_
363  * (as we always return a DWORD)
364  */
365 DWORD WINAPI DOSVM_inport( int port, int size )
366 {
367     DWORD res = ~0U;
368
369     TRACE("%d-byte value from port 0x%04x\n", size, port );
370
371 #ifdef HAVE_PPDEV
372     if (do_pp_port_access == -1) do_pp_port_access =IO_pp_init();
373     if ((do_pp_port_access == 0 ) && (size == 1))
374     {
375         if (!IO_pp_inp(port,&res)) return res;
376     }
377 #endif
378
379 #ifdef DIRECT_IO_ACCESS
380     if (do_direct_port_access == -1) IO_port_init();
381     if ((do_direct_port_access)
382         /* Make sure we have access to the port */
383         && (port_permissions[port] & IO_READ))
384     {
385         iopl(3);
386         switch(size)
387         {
388         case 1: res = inb( port ); break;
389         case 2: res = inw( port ); break;
390         case 4: res = inl( port ); break;
391         }
392         iopl(0);
393         return res;
394     }
395 #endif
396
397     switch (port)
398     {
399     case 0x40:
400     case 0x41:
401     case 0x42:
402         {
403             BYTE chan = port & 3;
404             WORD tempval = 0;
405             if (tmr_8253[chan].latched)
406                 tempval = tmr_8253[chan].latch;
407             else
408             {
409                 dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
410                 if (chan == 0) /* System timer counter divisor */
411                 {
412                     /* FIXME: DOSVM_GetTimer() returns quite rigid values */
413                     tempval = dummy_ctr + (WORD)DOSVM_GetTimer();
414                 }
415                 else
416                 {
417                     /* FIXME: intelligent hardware timer emulation needed */
418                     tempval = dummy_ctr;
419                 }
420             }
421
422             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
423             {
424             case 0:
425                 res = 0; /* shouldn't happen? */
426                 break;
427             case 1: /* read lo byte */
428                 res = (BYTE)tempval;
429                 tmr_8253[chan].latched = FALSE;
430                 break;
431             case 3: /* read lo byte, then hi byte */
432                 tmr_8253[chan].byte_toggle ^= 1; /* toggle */
433                 if (tmr_8253[chan].byte_toggle)
434                 {
435                     res = (BYTE)tempval;
436                     break;
437                 }
438                 /* else [fall through if read hi byte !] */
439             case 2: /* read hi byte */
440                 res = (BYTE)(tempval >> 8);
441                 tmr_8253[chan].latched = FALSE;
442                 break;
443             }
444         }
445         break;
446     case 0x60:
447         res = DOSVM_Int09ReadScan(NULL);
448         break;
449     case 0x61:
450         res = (DWORD)parport_8255[1];
451         break;
452     case 0x62:
453         res = (DWORD)parport_8255[2];
454         break;
455     case 0x70:
456         res = (DWORD)cmosaddress;
457         break;
458     case 0x71:
459         if (!cmos_image_initialized)
460         {
461             IO_FixCMOSCheckSum();
462             cmos_image_initialized = 1;
463         }
464         res = (DWORD)cmosimage[cmosaddress & 0x3f];
465         break;
466     case 0x200:
467     case 0x201:
468         res = ~0U; /* no joystick */
469         break;
470     case 0x22a:
471     case 0x22c:
472     case 0x22e:
473         res = (DWORD)SB_ioport_in( port );
474         break;
475     /* VGA read registers */
476     case 0x3b4:  /* CRT Controller Register - Index (MDA) */
477     case 0x3b5:  /* CRT Controller Register - Other (MDA) */
478     case 0x3ba:  /* General Register - Input status 1 (MDA) */
479     case 0x3c0:  /* Attribute Controller - Address */
480     case 0x3c1:  /* Attribute Controller - Other */
481     case 0x3c2:  /* General Register - Input status 0 */
482     case 0x3c3:  /* General Register - Video subsystem enable */
483     case 0x3c4:  /* Sequencer Register - Address */
484     case 0x3c5:  /* Sequencer Register - Other */
485     case 0x3c6:
486     case 0x3c7:  /* General Register -  DAC State */
487     case 0x3c8:
488     case 0x3c9:
489     case 0x3ca:  /* General Register - Feature control */
490     case 0x3cb:
491     case 0x3cc:  /* General Register - Misc output */
492     case 0x3cd:
493     case 0x3ce:  /* Graphics Controller Register - Address */
494     case 0x3cf:  /* Graphics Controller Register - Other */
495     case 0x3d0:
496     case 0x3d1:
497     case 0x3d2:
498     case 0x3d3:
499     case 0x3d4:  /* CRT Controller Register - Index (CGA) */
500     case 0x3d5:  /* CRT Controller Register - Other (CGA) */
501     case 0x3d6:
502     case 0x3d7:
503     case 0x3d8:
504     case 0x3d9:
505     case 0x3da:
506     case 0x3db:
507     case 0x3dc:
508     case 0x3dd:
509     case 0x3de:
510     case 0x3df:
511         if (size > 1)
512             FIXME("Trying to read more than one byte from VGA!\n");
513         res = (DWORD)VGA_ioport_in( port );
514         break;
515     case 0x00:
516     case 0x01:
517     case 0x02:
518     case 0x03:
519     case 0x04:
520     case 0x05:
521     case 0x06:
522     case 0x07:
523     case 0xC0:
524     case 0xC2:
525     case 0xC4:
526     case 0xC6:
527     case 0xC8:
528     case 0xCA:
529     case 0xCC:
530     case 0xCE:
531     case 0x87:
532     case 0x83:
533     case 0x81:
534     case 0x82:
535     case 0x8B:
536     case 0x89:
537     case 0x8A:
538     case 0x487:
539     case 0x483:
540     case 0x481:
541     case 0x482:
542     case 0x48B:
543     case 0x489:
544     case 0x48A:
545     case 0x08:
546     case 0xD0:
547     case 0x0D:
548     case 0xDA:
549         res = (DWORD)DMA_ioport_in( port );
550         break;
551     default:
552         WARN("Direct I/O read attempted from port %x\n", port);
553         break;
554     }
555     return res;
556 }
557
558
559 /**********************************************************************
560  *          outport  (WINEDOS.@)
561  */
562 void WINAPI DOSVM_outport( int port, int size, DWORD value )
563 {
564     TRACE("IO: 0x%x (%d-byte value) to port 0x%04x\n", value, size, port );
565
566 #ifdef HAVE_PPDEV
567     if (do_pp_port_access == -1) do_pp_port_access = IO_pp_init();
568     if ((do_pp_port_access == 0) && (size == 1))
569     {
570         if (!IO_pp_outp(port,&value)) return;
571     }
572 #endif
573
574 #ifdef DIRECT_IO_ACCESS
575
576     if (do_direct_port_access == -1) IO_port_init();
577     if ((do_direct_port_access)
578         /* Make sure we have access to the port */
579         && (port_permissions[port] & IO_WRITE))
580     {
581         iopl(3);
582         switch(size)
583         {
584         case 1: outb( LOBYTE(value), port ); break;
585         case 2: outw( LOWORD(value), port ); break;
586         case 4: outl( value, port ); break;
587         }
588         iopl(0);
589         return;
590     }
591 #endif
592
593     switch (port)
594     {
595     case 0x20:
596         DOSVM_PIC_ioport_out( port, (BYTE)value );
597         break;
598     case 0x40:
599     case 0x41:
600     case 0x42:
601         {
602             BYTE chan = port & 3;
603
604             /* we need to get the oldval before any lo/hi byte change has been made */
605             if (((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
606                 !tmr_8253[chan].byte_toggle)
607                 tmr_8253[chan].oldval = tmr_8253[chan].countmax;
608             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
609             {
610             case 0:
611                 break; /* shouldn't happen? */
612             case 1: /* write lo byte */
613                 tmr_8253[chan].countmax =
614                     (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
615                 break;
616             case 3: /* write lo byte, then hi byte */
617                 tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
618                 if (tmr_8253[chan].byte_toggle)
619                 {
620                     tmr_8253[chan].countmax =
621                         (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
622                     break;
623                 }
624                 /* else [fall through if write hi byte !] */
625             case 2: /* write hi byte */
626                 tmr_8253[chan].countmax =
627                     (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
628                 break;
629             }
630             /* if programming is finished and value has changed
631                then update to new value */
632             if ((((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
633                  !tmr_8253[chan].byte_toggle) &&
634                 (tmr_8253[chan].countmax != tmr_8253[chan].oldval))
635                 set_timer_maxval(chan, tmr_8253[chan].countmax);
636         }
637         break;
638     case 0x43:
639        {
640            BYTE chan = ((BYTE)value & 0xc0) >> 6;
641            /* ctrl byte for specific timer channel */
642            if (chan == 3)
643            {
644                FIXME("8254 timer readback not implemented yet\n");
645                break;
646            }
647            switch (((BYTE)value & 0x30) >> 4)
648            {
649            case 0:      /* latch timer */
650                tmr_8253[chan].latched = TRUE;
651                dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
652                if (chan == 0) /* System timer divisor */
653                    tmr_8253[chan].latch = dummy_ctr + (WORD)DOSVM_GetTimer();
654                else
655                {
656                    /* FIXME: intelligent hardware timer emulation needed */
657                    tmr_8253[chan].latch = dummy_ctr;
658                }
659                break;
660            case 3:      /* write lo byte, then hi byte */
661                tmr_8253[chan].byte_toggle = FALSE; /* init */
662                /* fall through */
663            case 1:      /* write lo byte only */
664            case 2:      /* write hi byte only */
665                tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
666                break;
667            }
668        }
669        break;
670     case 0x61:
671         parport_8255[1] = (BYTE)value;
672         if (((parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
673         {
674             TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
675             Beep(1193180 / tmr_8253[2].countmax, 20);
676         }
677         break;
678     case 0x70:
679         cmosaddress = (BYTE)value & 0x7f;
680         break;
681     case 0x71:
682         if (!cmos_image_initialized)
683         {
684             IO_FixCMOSCheckSum();
685             cmos_image_initialized = 1;
686         }
687         cmosimage[cmosaddress & 0x3f] = (BYTE)value;
688         break;
689     case 0x226:
690     case 0x22c:
691         SB_ioport_out( port, (BYTE)value );
692         break;
693     /* VGA Write registers */
694     case 0x3b4:  /* CRT Controller Register - Index (MDA) */
695     case 0x3b5:  /* CRT Controller Register - Other (MDA) */
696     case 0x3ba:  /* General Register - Feature Control */
697     case 0x3c0:  /* Attribute Controller - Address/Other */
698     case 0x3c1:
699     case 0x3c2:  /* General Register - Misc output */
700     case 0x3c3:  /* General Register - Video subsystem enable */
701     case 0x3c4:  /* Sequencer Register - Address */
702     case 0x3c5:  /* Sequencer Register - Other */
703     case 0x3c6:
704     case 0x3c7:
705     case 0x3c8:
706     case 0x3c9:
707     case 0x3ca:
708     case 0x3cb:
709     case 0x3cc:
710     case 0x3cd:
711     case 0x3ce:  /* Graphics Controller Register - Address */
712     case 0x3cf:  /* Graphics Controller Register - Other */
713     case 0x3d0:
714     case 0x3d1:
715     case 0x3d2:
716     case 0x3d3:
717     case 0x3d4:  /* CRT Controller Register - Index (CGA) */
718     case 0x3d5:  /* CRT Controller Register - Other (CGA) */
719     case 0x3d6:
720     case 0x3d7:
721     case 0x3d8:
722     case 0x3d9:
723     case 0x3da:
724     case 0x3db:
725     case 0x3dc:
726     case 0x3dd:
727     case 0x3de:
728     case 0x3df:
729         VGA_ioport_out( port, LOBYTE(value) );
730         if(size > 1) {
731             VGA_ioport_out( port+1, HIBYTE(value) );
732             if(size > 2) {
733                 VGA_ioport_out( port+2, LOBYTE(HIWORD(value)) );
734                 VGA_ioport_out( port+3, HIBYTE(HIWORD(value)) );
735             }
736         }
737         break;
738     case 0x00:
739     case 0x01:
740     case 0x02:
741     case 0x03:
742     case 0x04:
743     case 0x05:
744     case 0x06:
745     case 0x07:
746     case 0xC0:
747     case 0xC2:
748     case 0xC4:
749     case 0xC6:
750     case 0xC8:
751     case 0xCA:
752     case 0xCC:
753     case 0xCE:
754     case 0x87:
755     case 0x83:
756     case 0x81:
757     case 0x82:
758     case 0x8B:
759     case 0x89:
760     case 0x8A:
761     case 0x487:
762     case 0x483:
763     case 0x481:
764     case 0x482:
765     case 0x48B:
766     case 0x489:
767     case 0x48A:
768     case 0x08:
769     case 0xD0:
770     case 0x0B:
771     case 0xD6:
772     case 0x0A:
773     case 0xD4:
774     case 0x0F:
775     case 0xDE:
776     case 0x09:
777     case 0xD2:
778     case 0x0C:
779     case 0xD8:
780     case 0x0D:
781     case 0xDA:
782     case 0x0E:
783     case 0xDC:
784         DMA_ioport_out( port, (BYTE)value );
785         break;
786     default:
787         WARN("Direct I/O write attempted to port %x\n", port );
788         break;
789     }
790 }