2 * Emulation of processor ioports.
4 * Copyright 1995 Morten Welinder
5 * Copyright 1998 Andreas Mohr, Ove Kaaven
6 * Copyright 2001 Uwe Bonnes
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 - only a few ports are emulated.
25 - real-time clock in "cmos" is bogus. A nifty alarm() setup could
33 #include <sys/types.h>
34 #ifdef HAVE_SYS_STAT_H
35 # include <sys/stat.h>
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
44 #ifdef HAVE_LINUX_IOCTL_H
45 # include <linux/ioctl.h>
47 #include <linux/ppdev.h>
55 #include "kernel16_private.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(int);
64 # define DIRECT_IO_ACCESS
66 # undef DIRECT_IO_ACCESS
76 {0xFFFF, 0, 0x36, 0, 0},
77 {0x0012, 0, 0x74, 0, 0},
78 {0x0001, 0, 0xB6, 0, 0},
80 /* two byte read in progress */
81 #define TMR_RTOGGLE 0x01
82 /* two byte write in progress */
83 #define TMR_WTOGGLE 0x02
84 /* latch contains data */
85 #define TMR_LATCHED 0x04
86 /* counter is in update phase */
87 #define TMR_UPDATE 0x08
88 /* readback status request */
89 #define TMR_STATUS 0x10
92 static BYTE parport_8255[4] = {0x4f, 0x20, 0xff, 0xff};
94 static BYTE cmosaddress;
96 static int cmos_image_initialized = 0;
98 static BYTE cmosimage[64] =
100 0x27, /* 0x00: seconds */
101 0x34, /* 0X01: seconds alarm */
102 0x31, /* 0x02: minutes */
103 0x47, /* 0x03: minutes alarm */
104 0x16, /* 0x04: hour */
105 0x15, /* 0x05: hour alarm */
106 0x00, /* 0x06: week day */
107 0x01, /* 0x07: month day */
108 0x04, /* 0x08: month */
109 0x94, /* 0x09: year */
110 0x26, /* 0x0a: state A */
111 0x02, /* 0x0b: state B */
112 0x50, /* 0x0c: state C */
113 0x80, /* 0x0d: state D */
114 0x00, /* 0x0e: state diagnostic */
115 0x00, /* 0x0f: state state shutdown */
116 0x40, /* 0x10: floppy type */
117 0xb1, /* 0x11: reserved */
118 0x00, /* 0x12: HD type */
119 0x9c, /* 0x13: reserved */
120 0x01, /* 0x14: equipment */
121 0x80, /* 0x15: low base memory */
122 0x02, /* 0x16: high base memory (0x280 => 640KB) */
123 0x00, /* 0x17: low extended memory */
124 0x3b, /* 0x18: high extended memory (0x3b00 => 15MB) */
125 0x00, /* 0x19: HD 1 extended type byte */
126 0x00, /* 0x1a: HD 2 extended type byte */
127 0xad, /* 0x1b: reserved */
128 0x02, /* 0x1c: reserved */
129 0x10, /* 0x1d: reserved */
130 0x00, /* 0x1e: reserved */
131 0x00, /* 0x1f: installed features */
132 0x08, /* 0x20: HD 1 low cylinder number */
133 0x00, /* 0x21: HD 1 high cylinder number */
134 0x00, /* 0x22: HD 1 heads */
135 0x26, /* 0x23: HD 1 low pre-compensation start */
136 0x00, /* 0x24: HD 1 high pre-compensation start */
137 0x00, /* 0x25: HD 1 low landing zone */
138 0x00, /* 0x26: HD 1 high landing zone */
139 0x00, /* 0x27: HD 1 sectors */
140 0x00, /* 0x28: options 1 */
141 0x00, /* 0x29: reserved */
142 0x00, /* 0x2a: reserved */
143 0x00, /* 0x2b: options 2 */
144 0x00, /* 0x2c: options 3 */
145 0x3f, /* 0x2d: reserved */
146 0xcc, /* 0x2e: low CMOS ram checksum (computed automatically) */
147 0xcc, /* 0x2f: high CMOS ram checksum (computed automatically) */
148 0x00, /* 0x30: low extended memory byte */
149 0x1c, /* 0x31: high extended memory byte */
150 0x19, /* 0x32: century byte */
151 0x81, /* 0x33: setup information */
152 0x00, /* 0x34: CPU speed */
153 0x0e, /* 0x35: HD 2 low cylinder number */
154 0x00, /* 0x36: HD 2 high cylinder number */
155 0x80, /* 0x37: HD 2 heads */
156 0x1b, /* 0x38: HD 2 low pre-compensation start */
157 0x7b, /* 0x39: HD 2 high pre-compensation start */
158 0x21, /* 0x3a: HD 2 low landing zone */
159 0x00, /* 0x3b: HD 2 high landing zone */
160 0x00, /* 0x3c: HD 2 sectors */
161 0x00, /* 0x3d: reserved */
162 0x05, /* 0x3e: reserved */
163 0x5f /* 0x3f: reserved */
166 static void IO_FixCMOSCheckSum(void)
171 for (i=0x10; i < 0x2d; i++)
173 cmosimage[0x2e] = sum >> 8; /* yes, this IS hi byte !! */
174 cmosimage[0x2f] = sum & 0xff;
175 TRACE("calculated hi %02x, lo %02x\n", cmosimage[0x2e], cmosimage[0x2f]);
178 #ifdef DIRECT_IO_ACCESS
180 extern int iopl(int level);
181 static char do_direct_port_access = -1;
182 static char port_permissions[0x10000];
187 #endif /* DIRECT_IO_ACCESS */
190 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
195 ((a)%10 + ((a)>>4)%10*10 + ((a)>>8)%10*100 + ((a)>>12)%10*1000)
197 ((a)%10 | (a)/10%10<<4 | (a)/100%10<<8 | (a)/1000%10<<12)
200 static void set_timer(unsigned timer)
202 DWORD val = tmr_8253[timer].countmax;
204 if (tmr_8253[timer].ctrlbyte_ch & 0x01)
207 tmr_8253[timer].flags &= ~TMR_UPDATE;
208 if (!QueryPerformanceCounter((LARGE_INTEGER*)&tmr_8253[timer].start_time))
209 WARN("QueryPerformanceCounter should not fail!\n");
212 case 0: /* System timer counter divisor */
215 case 1: /* RAM refresh */
216 FIXME("RAM refresh counter handling not implemented !\n");
218 case 2: /* cassette & speaker */
220 if ((parport_8255[1] & 3) == 3)
222 TRACE("Beep (freq: %d) !\n", 1193180 / val);
223 Beep(1193180 / val, 20);
230 static WORD get_timer_val(unsigned timer)
233 WORD maxval, val = tmr_8253[timer].countmax;
234 BYTE mode = tmr_8253[timer].ctrlbyte_ch >> 1 & 0x07;
236 /* This is not strictly correct. In most cases the old countdown should
237 * finish normally (by counting down to 0) or halt and not jump to 0.
238 * But we are calculating and not counting, so this seems to be a good
239 * solution and should work well with most (all?) programs
241 if (tmr_8253[timer].flags & TMR_UPDATE)
244 if (!QueryPerformanceCounter(&time))
245 WARN("QueryPerformanceCounter should not fail!\n");
247 time.QuadPart -= tmr_8253[timer].start_time;
248 if (tmr_8253[timer].ctrlbyte_ch & 0x01)
257 maxval = tmr_8253[timer].ctrlbyte_ch & 0x01 ? 9999 : 0xFFFF;
264 ERR("Invalid PIT mode: %d\n", mode);
268 val = (val - time.QuadPart) % (maxval + 1);
269 if (tmr_8253[timer].ctrlbyte_ch & 0x01)
276 /**********************************************************************
280 /* set_IO_permissions(int val1, int val)
281 * Helper function for IO_port_init
283 #ifdef DIRECT_IO_ACCESS
284 static void set_IO_permissions(int val1, int val, char rw)
288 if (val == -1) val = 0x3ff;
289 for (j = val1; j <= val; j++)
290 port_permissions[j] |= rw;
292 do_direct_port_access = 1;
295 } else if (val != -1) {
296 do_direct_port_access = 1;
298 port_permissions[val] |= rw;
303 /* do_IO_port_init_read_or_write(char* temp, char rw)
304 * Helper function for IO_port_init
307 static void do_IO_port_init_read_or_write(const WCHAR *str, char rw)
312 static const WCHAR allW[] = {'a','l','l',0};
314 if (!strcmpiW(str, allW))
316 for (i=0; i < sizeof(port_permissions); i++)
317 port_permissions[i] |= rw;
330 set_IO_permissions(val1, val, rw);
337 if (val1 == -1) val1 = 0;
343 val = strtoulW( str, &end, 0 );
354 set_IO_permissions(val1, val, rw);
358 static inline BYTE inb( WORD port )
361 __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
365 static inline WORD inw( WORD port )
368 __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
372 static inline DWORD inl( WORD port )
375 __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
379 static inline void outb( BYTE value, WORD port )
381 __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
384 static inline void outw( WORD value, WORD port )
386 __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
389 static inline void outl( DWORD value, WORD port )
391 __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
394 static void IO_port_init(void)
399 OBJECT_ATTRIBUTES attr;
400 UNICODE_STRING nameW;
402 static const WCHAR portsW[] = {'S','o','f','t','w','a','r','e','\\',
403 'W','i','n','e','\\','V','D','M','\\','P','o','r','t','s',0};
404 static const WCHAR readW[] = {'r','e','a','d',0};
405 static const WCHAR writeW[] = {'w','r','i','t','e',0};
407 do_direct_port_access = 0;
408 /* Can we do that? */
413 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
414 attr.Length = sizeof(attr);
415 attr.RootDirectory = root;
416 attr.ObjectName = &nameW;
418 attr.SecurityDescriptor = NULL;
419 attr.SecurityQualityOfService = NULL;
420 RtlInitUnicodeString( &nameW, portsW );
422 /* @@ Wine registry key: HKCU\Software\Wine\VDM\Ports */
423 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
425 RtlInitUnicodeString( &nameW, readW );
426 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
428 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
429 do_IO_port_init_read_or_write(str, IO_READ);
431 RtlInitUnicodeString( &nameW, writeW );
432 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
434 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
435 do_IO_port_init_read_or_write(str, IO_WRITE);
443 #endif /* DIRECT_IO_ACCESS */
448 typedef struct _PPDEVICESTRUCT{
449 int fd; /* NULL if device not available */
451 int userbase; /* where wine thinks the ports are */
452 DWORD lastaccess; /* or NULL if release */
453 int timeout; /* time in second of inactivity to release the port */
456 static PPDeviceStruct PPDeviceList[5];
457 static int PPDeviceNum=0;
459 static int IO_pp_sort(const void *p1,const void *p2)
461 return ((const PPDeviceStruct*)p1)->userbase - ((const PPDeviceStruct*)p2)->userbase;
466 * Read the ppdev entries from registry, open the device and check
467 * for necessary IOCTRL
468 * Report verbose about possible errors
470 static char IO_pp_init(void)
475 int i,idx=0,fd,res,userbase,nports=0;
479 OBJECT_ATTRIBUTES attr;
480 UNICODE_STRING nameW;
482 static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
483 'W','i','n','e','\\','V','D','M','\\','p','p','d','e','v',0};
487 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
488 attr.Length = sizeof(attr);
489 attr.RootDirectory = root;
490 attr.ObjectName = &nameW;
492 attr.SecurityDescriptor = NULL;
493 attr.SecurityQualityOfService = NULL;
494 RtlInitUnicodeString( &nameW, configW );
496 /* @@ Wine registry key: HKCU\Software\Wine\VDM\ppdev */
497 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
503 DWORD total_size, len;
505 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)temp;
507 if (NtEnumerateValueKey( hkey, idx, KeyValueFullInformation,
508 temp, sizeof(temp), &total_size )) break;
509 if (info->Type != REG_SZ) break;
511 RtlUnicodeToMultiByteN( name, sizeof(name)-1, &len, info->Name, info->NameLength );
513 RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len,
514 (WCHAR *)(temp + info->DataOffset), total_size-info->DataOffset );
520 FIXME("Make the PPDeviceList larger than 5 elements\n");
523 TRACE("Device '%s' at virtual userbase '%s'\n", buffer,name);
524 timeout = strchr(buffer,',');
527 fd=open(buffer,O_RDWR);
531 WARN("Configuration: No access to %s Cause: %s\n",buffer,strerror(lasterror));
532 WARN("Rejecting configuration item\n");
533 if (lasterror == ENODEV)
534 ERR("Is the ppdev module loaded?\n");
537 userbase = strtol(name, NULL, 16);
538 if ( errno == ERANGE)
540 WARN("Configuration: Invalid base %s for %s\n",name,buffer);
541 WARN("Rejecting configuration item\n");
544 if (ioctl (fd,PPCLAIM,0))
546 ERR("PPCLAIM rejected %s\n",buffer);
547 ERR("Perhaps the device is already in use or nonexistent\n");
552 for (i=0; i<= nports; i++)
554 if (PPDeviceList[i].userbase == userbase)
556 WARN("Configuration: %s uses the same virtual ports as %s\n",
557 buffer,PPDeviceList[0].devicename);
558 WARN("Configuration: Rejecting configuration item\n");
563 if (!userbase) continue;
565 /* Check for the minimum required IOCTLS */
566 if ((ioctl(fd,PPRDATA,&res))||
567 (ioctl(fd,PPRSTATUS,&res))||
568 (ioctl(fd,PPRCONTROL,&res)))
570 ERR("PPUSER IOCTL not available for parport device %s\n",buffer);
573 if (ioctl (fd,PPRELEASE,0))
575 ERR("PPRELEASE rejected %s\n",buffer);
576 ERR("Perhaps the device is already in use or nonexistent\n");
579 PPDeviceList[nports].devicename = HeapAlloc(GetProcessHeap(), 0, sizeof(buffer)+1);
580 if (!PPDeviceList[nports].devicename)
582 ERR("No (more) space for devicename\n");
585 strcpy(PPDeviceList[nports].devicename,buffer);
586 PPDeviceList[nports].fd = fd;
587 PPDeviceList[nports].userbase = userbase;
588 PPDeviceList[nports].lastaccess=GetTickCount();
591 PPDeviceList[nports].timeout = strtol(timeout, NULL, 10);
594 WARN("Configuration: Invalid timeout %s in configuration for %s, Setting to 0\n",
596 PPDeviceList[nports].timeout = 0;
600 PPDeviceList[nports].timeout = 0;
603 TRACE("found %d ports\n",nports);
608 /* sort in ascending order for userbase for faster access */
609 qsort (PPDeviceList,PPDeviceNum,sizeof(PPDeviceStruct),IO_pp_sort);
613 for (idx= 0;idx<PPDeviceNum; idx++)
614 TRACE("found device %s userbase %x fd %x timeout %d\n",
615 PPDeviceList[idx].devicename, PPDeviceList[idx].userbase,
616 PPDeviceList[idx].fd,PPDeviceList[idx].timeout);
618 register a timer callback perhaps every 30 seconds to release unused ports
619 Set lastaccess = 0 as indicator when port was released
626 * Do the actual IOCTL
627 * Return NULL on success
629 static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
632 if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
634 ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
635 PPDeviceList[idx].devicename);
638 ret = ioctl(PPDeviceList[idx].fd,ppctl,res);
639 if (ioctl(PPDeviceList[idx].fd,PPRELEASE,0))
641 ERR("Can't release device %s, PPUSER/PPDEV handling confused\n",
642 PPDeviceList[idx].devicename);
651 * Check if we can satisfy the INP command with some of the configured PPDEV device
652 * Return NULL on success
654 static int IO_pp_inp(int port, DWORD* res)
658 for (idx=0;idx<PPDeviceNum ;idx++)
660 j = port - PPDeviceList[idx].userbase;
665 return IO_pp_do_access(idx,PPRDATA,res);
667 return IO_pp_do_access(idx,PPRSTATUS,res);
669 return IO_pp_do_access(idx,PPRCONTROL,res);
675 FIXME("Port 0x%x not accessible for reading with ppdev\n",port);
676 FIXME("If this is causing problems, try direct port access\n");
687 * Check if we can satisfy the OUTP command with some of the configured PPDEV device
688 * Return NULL on success
690 static BOOL IO_pp_outp(int port, DWORD* res)
694 for (idx=0;idx<PPDeviceNum ;idx++)
696 j = port - PPDeviceList[idx].userbase;
701 return IO_pp_do_access(idx,PPWDATA,res);
704 /* We can't switch port direction via PPWCONTROL,
705 so do it via PPDATADIR
707 DWORD mode = *res & 0x20;
708 IO_pp_do_access(idx,PPDATADIR,&mode);
709 mode = (*res & ~0x20);
710 return IO_pp_do_access(idx,PPWCONTROL,&mode);
719 FIXME("Port %d not accessible for writing with ppdev\n",port);
720 FIXME("If this is causing problems, try direct port access\n");
729 #endif /* HAVE_PPDEV */
732 /**********************************************************************
735 * Note: The size argument has to be handled correctly _externally_
736 * (as we always return a DWORD)
738 DWORD DOSVM_inport( int port, int size )
742 TRACE("%d-byte value from port 0x%04x\n", size, port );
744 DOSMEM_InitDosMemory();
747 if (do_pp_port_access == -1) do_pp_port_access =IO_pp_init();
748 if ((do_pp_port_access == 0 ) && (size == 1))
750 if (!IO_pp_inp(port,&res)) return res;
754 #ifdef DIRECT_IO_ACCESS
755 if (do_direct_port_access == -1) IO_port_init();
756 if ((do_direct_port_access)
757 /* Make sure we have access to the port */
758 && (port_permissions[port] & IO_READ))
763 case 1: res = inb( port ); break;
764 case 2: res = inw( port ); break;
765 case 4: res = inl( port ); break;
778 BYTE chan = port & 3;
779 WORD tempval = tmr_8253[chan].flags & TMR_LATCHED
780 ? tmr_8253[chan].latch : get_timer_val(chan);
782 if (tmr_8253[chan].flags & TMR_STATUS)
784 WARN("Read-back status\n");
785 /* We differ slightly from the spec:
786 * - TMR_UPDATE is already set with the first write
787 * of a two byte counter update
788 * - 0x80 should be set if OUT signal is 1 (high)
790 tmr_8253[chan].flags &= ~TMR_STATUS;
791 res = (tmr_8253[chan].ctrlbyte_ch & 0x3F) |
792 (tmr_8253[chan].flags & TMR_UPDATE ? 0x40 : 0x00);
795 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
798 res = 0; /* shouldn't happen? */
800 case 1: /* read lo byte */
802 tmr_8253[chan].flags &= ~TMR_LATCHED;
804 case 3: /* read lo byte, then hi byte */
805 tmr_8253[chan].flags ^= TMR_RTOGGLE; /* toggle */
806 if (tmr_8253[chan].flags & TMR_RTOGGLE)
811 /* else [fall through if read hi byte !] */
812 case 2: /* read hi byte */
813 res = (BYTE)(tempval >> 8);
814 tmr_8253[chan].flags &= ~TMR_LATCHED;
820 res = DOSVM_Int09ReadScan(NULL);
823 res = (DWORD)parport_8255[1];
826 res = (DWORD)parport_8255[2];
829 res = (DWORD)cmosaddress;
832 if (!cmos_image_initialized)
834 IO_FixCMOSCheckSum();
835 cmos_image_initialized = 1;
837 res = (DWORD)cmosimage[cmosaddress & 0x3f];
841 res = ~0U; /* no joystick */
846 res = (DWORD)SB_ioport_in( port );
848 /* VGA read registers */
849 case 0x3b4: /* CRT Controller Register - Index (MDA) */
850 case 0x3b5: /* CRT Controller Register - Other (MDA) */
851 case 0x3ba: /* General Register - Input status 1 (MDA) */
852 case 0x3c0: /* Attribute Controller - Address */
853 case 0x3c1: /* Attribute Controller - Other */
854 case 0x3c2: /* General Register - Input status 0 */
855 case 0x3c3: /* General Register - Video subsystem enable */
856 case 0x3c4: /* Sequencer Register - Address */
857 case 0x3c5: /* Sequencer Register - Other */
859 case 0x3c7: /* General Register - DAC State */
862 case 0x3ca: /* General Register - Feature control */
864 case 0x3cc: /* General Register - Misc output */
866 case 0x3ce: /* Graphics Controller Register - Address */
867 case 0x3cf: /* Graphics Controller Register - Other */
872 case 0x3d4: /* CRT Controller Register - Index (CGA) */
873 case 0x3d5: /* CRT Controller Register - Other (CGA) */
885 FIXME("Trying to read more than one byte from VGA!\n");
886 res = (DWORD)VGA_ioport_in( port );
922 res = (DWORD)DMA_ioport_in( port );
925 WARN("Direct I/O read attempted from port %x\n", port);
932 /**********************************************************************
935 void DOSVM_outport( int port, int size, DWORD value )
937 TRACE("IO: 0x%x (%d-byte value) to port 0x%04x\n", value, size, port );
939 DOSMEM_InitDosMemory();
942 if (do_pp_port_access == -1) do_pp_port_access = IO_pp_init();
943 if ((do_pp_port_access == 0) && (size == 1))
945 if (!IO_pp_outp(port,&value)) return;
949 #ifdef DIRECT_IO_ACCESS
951 if (do_direct_port_access == -1) IO_port_init();
952 if ((do_direct_port_access)
953 /* Make sure we have access to the port */
954 && (port_permissions[port] & IO_WRITE))
959 case 1: outb( LOBYTE(value), port ); break;
960 case 2: outw( LOWORD(value), port ); break;
961 case 4: outl( value, port ); break;
971 DOSVM_PIC_ioport_out( port, (BYTE)value );
977 BYTE chan = port & 3;
979 tmr_8253[chan].flags |= TMR_UPDATE;
980 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
983 break; /* shouldn't happen? */
984 case 1: /* write lo byte */
985 tmr_8253[chan].countmax =
986 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
988 case 3: /* write lo byte, then hi byte */
989 tmr_8253[chan].flags ^= TMR_WTOGGLE; /* toggle */
990 if (tmr_8253[chan].flags & TMR_WTOGGLE)
992 tmr_8253[chan].countmax =
993 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
996 /* else [fall through if write hi byte !] */
997 case 2: /* write hi byte */
998 tmr_8253[chan].countmax =
999 (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
1002 /* if programming is finished, update to new value */
1003 if ((tmr_8253[chan].ctrlbyte_ch & 0x30) &&
1004 !(tmr_8253[chan].flags & TMR_WTOGGLE))
1010 BYTE chan = ((BYTE)value & 0xc0) >> 6;
1011 /* ctrl byte for specific timer channel */
1014 if ( !(value & 0x20) )
1016 if ((value & 0x02) && !(tmr_8253[0].flags & TMR_LATCHED))
1018 tmr_8253[0].flags |= TMR_LATCHED;
1019 tmr_8253[0].latch = get_timer_val(0);
1021 if ((value & 0x04) && !(tmr_8253[1].flags & TMR_LATCHED))
1023 tmr_8253[1].flags |= TMR_LATCHED;
1024 tmr_8253[1].latch = get_timer_val(1);
1026 if ((value & 0x08) && !(tmr_8253[2].flags & TMR_LATCHED))
1028 tmr_8253[2].flags |= TMR_LATCHED;
1029 tmr_8253[2].latch = get_timer_val(2);
1033 if ( !(value & 0x10) )
1036 tmr_8253[0].flags |= TMR_STATUS;
1038 tmr_8253[1].flags |= TMR_STATUS;
1040 tmr_8253[2].flags |= TMR_STATUS;
1044 switch (((BYTE)value & 0x30) >> 4)
1046 case 0: /* latch timer */
1047 if ( !(tmr_8253[chan].flags & TMR_LATCHED) )
1049 tmr_8253[chan].flags |= TMR_LATCHED;
1050 tmr_8253[chan].latch = get_timer_val(chan);
1053 case 1: /* write lo byte only */
1054 case 2: /* write hi byte only */
1055 case 3: /* write lo byte, then hi byte */
1056 tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
1057 tmr_8253[chan].countmax = 0;
1058 tmr_8253[chan].flags = TMR_UPDATE;
1064 parport_8255[1] = (BYTE)value;
1065 if (((parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
1067 TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
1068 Beep(1193180 / tmr_8253[2].countmax, 20);
1072 cmosaddress = (BYTE)value & 0x7f;
1075 if (!cmos_image_initialized)
1077 IO_FixCMOSCheckSum();
1078 cmos_image_initialized = 1;
1080 cmosimage[cmosaddress & 0x3f] = (BYTE)value;
1084 SB_ioport_out( port, (BYTE)value );
1086 /* VGA Write registers */
1087 case 0x3b4: /* CRT Controller Register - Index (MDA) */
1088 case 0x3b5: /* CRT Controller Register - Other (MDA) */
1089 case 0x3ba: /* General Register - Feature Control */
1090 case 0x3c0: /* Attribute Controller - Address/Other */
1092 case 0x3c2: /* General Register - Misc output */
1093 case 0x3c3: /* General Register - Video subsystem enable */
1094 case 0x3c4: /* Sequencer Register - Address */
1095 case 0x3c5: /* Sequencer Register - Other */
1104 case 0x3ce: /* Graphics Controller Register - Address */
1105 case 0x3cf: /* Graphics Controller Register - Other */
1110 case 0x3d4: /* CRT Controller Register - Index (CGA) */
1111 case 0x3d5: /* CRT Controller Register - Other (CGA) */
1122 VGA_ioport_out( port, LOBYTE(value) );
1124 VGA_ioport_out( port+1, HIBYTE(value) );
1126 VGA_ioport_out( port+2, LOBYTE(HIWORD(value)) );
1127 VGA_ioport_out( port+3, HIBYTE(HIWORD(value)) );
1177 DMA_ioport_out( port, (BYTE)value );
1180 WARN("Direct I/O write attempted to port %x\n", port );