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