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"
57 #include "wine/unicode.h"
58 #include "wine/debug.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(int);
62 #if defined(linux) && !defined(__ANDROID__)
63 # define DIRECT_IO_ACCESS
65 # undef DIRECT_IO_ACCESS
75 {0xFFFF, 0, 0x36, 0, 0},
76 {0x0012, 0, 0x74, 0, 0},
77 {0x0001, 0, 0xB6, 0, 0},
79 /* two byte read in progress */
80 #define TMR_RTOGGLE 0x01
81 /* two byte write in progress */
82 #define TMR_WTOGGLE 0x02
83 /* latch contains data */
84 #define TMR_LATCHED 0x04
85 /* counter is in update phase */
86 #define TMR_UPDATE 0x08
87 /* readback status request */
88 #define TMR_STATUS 0x10
91 static BYTE parport_8255
[4] = {0x4f, 0x20, 0xff, 0xff};
93 static BYTE cmosaddress
;
95 static BOOL cmos_image_initialized
= FALSE
;
97 static BYTE cmosimage
[64] =
99 0x27, /* 0x00: seconds */
100 0x34, /* 0X01: seconds alarm */
101 0x31, /* 0x02: minutes */
102 0x47, /* 0x03: minutes alarm */
103 0x16, /* 0x04: hour */
104 0x15, /* 0x05: hour alarm */
105 0x00, /* 0x06: week day */
106 0x01, /* 0x07: month day */
107 0x04, /* 0x08: month */
108 0x94, /* 0x09: year */
109 0x26, /* 0x0a: state A */
110 0x02, /* 0x0b: state B */
111 0x50, /* 0x0c: state C */
112 0x80, /* 0x0d: state D */
113 0x00, /* 0x0e: state diagnostic */
114 0x00, /* 0x0f: state state shutdown */
115 0x40, /* 0x10: floppy type */
116 0xb1, /* 0x11: reserved */
117 0x00, /* 0x12: HD type */
118 0x9c, /* 0x13: reserved */
119 0x01, /* 0x14: equipment */
120 0x80, /* 0x15: low base memory */
121 0x02, /* 0x16: high base memory (0x280 => 640KB) */
122 0x00, /* 0x17: low extended memory */
123 0x3b, /* 0x18: high extended memory (0x3b00 => 15MB) */
124 0x00, /* 0x19: HD 1 extended type byte */
125 0x00, /* 0x1a: HD 2 extended type byte */
126 0xad, /* 0x1b: reserved */
127 0x02, /* 0x1c: reserved */
128 0x10, /* 0x1d: reserved */
129 0x00, /* 0x1e: reserved */
130 0x00, /* 0x1f: installed features */
131 0x08, /* 0x20: HD 1 low cylinder number */
132 0x00, /* 0x21: HD 1 high cylinder number */
133 0x00, /* 0x22: HD 1 heads */
134 0x26, /* 0x23: HD 1 low pre-compensation start */
135 0x00, /* 0x24: HD 1 high pre-compensation start */
136 0x00, /* 0x25: HD 1 low landing zone */
137 0x00, /* 0x26: HD 1 high landing zone */
138 0x00, /* 0x27: HD 1 sectors */
139 0x00, /* 0x28: options 1 */
140 0x00, /* 0x29: reserved */
141 0x00, /* 0x2a: reserved */
142 0x00, /* 0x2b: options 2 */
143 0x00, /* 0x2c: options 3 */
144 0x3f, /* 0x2d: reserved */
145 0xcc, /* 0x2e: low CMOS ram checksum (computed automatically) */
146 0xcc, /* 0x2f: high CMOS ram checksum (computed automatically) */
147 0x00, /* 0x30: low extended memory byte */
148 0x1c, /* 0x31: high extended memory byte */
149 0x19, /* 0x32: century byte */
150 0x81, /* 0x33: setup information */
151 0x00, /* 0x34: CPU speed */
152 0x0e, /* 0x35: HD 2 low cylinder number */
153 0x00, /* 0x36: HD 2 high cylinder number */
154 0x80, /* 0x37: HD 2 heads */
155 0x1b, /* 0x38: HD 2 low pre-compensation start */
156 0x7b, /* 0x39: HD 2 high pre-compensation start */
157 0x21, /* 0x3a: HD 2 low landing zone */
158 0x00, /* 0x3b: HD 2 high landing zone */
159 0x00, /* 0x3c: HD 2 sectors */
160 0x00, /* 0x3d: reserved */
161 0x05, /* 0x3e: reserved */
162 0x5f /* 0x3f: reserved */
165 static void IO_FixCMOSCheckSum(void)
170 for (i
=0x10; i
< 0x2d; i
++)
172 cmosimage
[0x2e] = sum
>> 8; /* yes, this IS hi byte !! */
173 cmosimage
[0x2f] = sum
& 0xff;
174 TRACE("calculated hi %02x, lo %02x\n", cmosimage
[0x2e], cmosimage
[0x2f]);
177 #ifdef DIRECT_IO_ACCESS
179 extern int iopl(int level
);
180 static char do_direct_port_access
= -1;
181 static char port_permissions
[0x10000];
186 #endif /* DIRECT_IO_ACCESS */
189 static int do_pp_port_access
= -1; /* -1: uninitialized, 1: not available
194 ((a)%10 + ((a)>>4)%10*10 + ((a)>>8)%10*100 + ((a)>>12)%10*1000)
196 ((a)%10 | (a)/10%10<<4 | (a)/100%10<<8 | (a)/1000%10<<12)
199 static void set_timer(unsigned timer
)
201 DWORD val
= tmr_8253
[timer
].countmax
;
203 if (tmr_8253
[timer
].ctrlbyte_ch
& 0x01)
206 tmr_8253
[timer
].flags
&= ~TMR_UPDATE
;
207 if (!QueryPerformanceCounter((LARGE_INTEGER
*)&tmr_8253
[timer
].start_time
))
208 WARN("QueryPerformanceCounter should not fail!\n");
211 case 0: /* System timer counter divisor */
213 case 1: /* RAM refresh */
214 FIXME("RAM refresh counter handling not implemented !\n");
216 case 2: /* cassette & speaker */
218 if ((parport_8255
[1] & 3) == 3)
220 TRACE("Beep (freq: %d) !\n", 1193180 / val
);
221 Beep(1193180 / val
, 20);
228 static WORD
get_timer_val(unsigned timer
)
231 WORD maxval
, val
= tmr_8253
[timer
].countmax
;
232 BYTE mode
= tmr_8253
[timer
].ctrlbyte_ch
>> 1 & 0x07;
234 /* This is not strictly correct. In most cases the old countdown should
235 * finish normally (by counting down to 0) or halt and not jump to 0.
236 * But we are calculating and not counting, so this seems to be a good
237 * solution and should work well with most (all?) programs
239 if (tmr_8253
[timer
].flags
& TMR_UPDATE
)
242 if (!QueryPerformanceCounter(&time
))
243 WARN("QueryPerformanceCounter should not fail!\n");
245 time
.QuadPart
-= tmr_8253
[timer
].start_time
;
246 if (tmr_8253
[timer
].ctrlbyte_ch
& 0x01)
255 maxval
= tmr_8253
[timer
].ctrlbyte_ch
& 0x01 ? 9999 : 0xFFFF;
262 ERR("Invalid PIT mode: %d\n", mode
);
266 val
= (val
- time
.QuadPart
) % (maxval
+ 1);
267 if (tmr_8253
[timer
].ctrlbyte_ch
& 0x01)
274 /**********************************************************************
278 /* set_IO_permissions(int val1, int val)
279 * Helper function for IO_port_init
281 #ifdef DIRECT_IO_ACCESS
282 static void set_IO_permissions(int val1
, int val
, char rw
)
286 if (val
== -1) val
= 0x3ff;
287 for (j
= val1
; j
<= val
; j
++)
288 port_permissions
[j
] |= rw
;
290 do_direct_port_access
= 1;
293 } else if (val
!= -1) {
294 do_direct_port_access
= 1;
296 port_permissions
[val
] |= rw
;
301 /* do_IO_port_init_read_or_write(char* temp, char rw)
302 * Helper function for IO_port_init
305 static void do_IO_port_init_read_or_write(const WCHAR
*str
, char rw
)
310 static const WCHAR allW
[] = {'a','l','l',0};
312 if (!strcmpiW(str
, allW
))
314 for (i
=0; i
< sizeof(port_permissions
); i
++)
315 port_permissions
[i
] |= rw
;
328 set_IO_permissions(val1
, val
, rw
);
335 if (val1
== -1) val1
= 0;
341 val
= strtoulW( str
, &end
, 0 );
352 set_IO_permissions(val1
, val
, rw
);
356 static inline BYTE
inb( WORD port
)
359 __asm__
__volatile__( "inb %w1,%0" : "=a" (b
) : "d" (port
) );
363 static inline WORD
inw( WORD port
)
366 __asm__
__volatile__( "inw %w1,%0" : "=a" (w
) : "d" (port
) );
370 static inline DWORD
inl( WORD port
)
373 __asm__
__volatile__( "inl %w1,%0" : "=a" (dw
) : "d" (port
) );
377 static inline void outb( BYTE value
, WORD port
)
379 __asm__
__volatile__( "outb %b0,%w1" : : "a" (value
), "d" (port
) );
382 static inline void outw( WORD value
, WORD port
)
384 __asm__
__volatile__( "outw %w0,%w1" : : "a" (value
), "d" (port
) );
387 static inline void outl( DWORD value
, WORD port
)
389 __asm__
__volatile__( "outl %0,%w1" : : "a" (value
), "d" (port
) );
392 static void IO_port_init(void)
397 OBJECT_ATTRIBUTES attr
;
398 UNICODE_STRING nameW
;
400 static const WCHAR portsW
[] = {'S','o','f','t','w','a','r','e','\\',
401 'W','i','n','e','\\','V','D','M','\\','P','o','r','t','s',0};
402 static const WCHAR readW
[] = {'r','e','a','d',0};
403 static const WCHAR writeW
[] = {'w','r','i','t','e',0};
405 do_direct_port_access
= 0;
406 /* Can we do that? */
411 RtlOpenCurrentUser( KEY_ALL_ACCESS
, &root
);
412 attr
.Length
= sizeof(attr
);
413 attr
.RootDirectory
= root
;
414 attr
.ObjectName
= &nameW
;
416 attr
.SecurityDescriptor
= NULL
;
417 attr
.SecurityQualityOfService
= NULL
;
418 RtlInitUnicodeString( &nameW
, portsW
);
420 /* @@ Wine registry key: HKCU\Software\Wine\VDM\Ports */
421 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
423 RtlInitUnicodeString( &nameW
, readW
);
424 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
426 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
427 do_IO_port_init_read_or_write(str
, IO_READ
);
429 RtlInitUnicodeString( &nameW
, writeW
);
430 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
432 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
433 do_IO_port_init_read_or_write(str
, IO_WRITE
);
441 #endif /* DIRECT_IO_ACCESS */
446 typedef struct _PPDEVICESTRUCT
{
447 int fd
; /* NULL if device not available */
449 int userbase
; /* where wine thinks the ports are */
450 DWORD lastaccess
; /* or NULL if release */
451 int timeout
; /* time in second of inactivity to release the port */
454 static PPDeviceStruct PPDeviceList
[5];
455 static int PPDeviceNum
=0;
457 static int IO_pp_sort(const void *p1
,const void *p2
)
459 return ((const PPDeviceStruct
*)p1
)->userbase
- ((const PPDeviceStruct
*)p2
)->userbase
;
464 * Read the ppdev entries from registry, open the device and check
465 * for necessary IOCTRL
466 * Report verbose about possible errors
468 static char IO_pp_init(void)
473 int i
,idx
=0,fd
,res
,userbase
,nports
=0;
477 OBJECT_ATTRIBUTES attr
;
478 UNICODE_STRING nameW
;
480 static const WCHAR configW
[] = {'S','o','f','t','w','a','r','e','\\',
481 'W','i','n','e','\\','V','D','M','\\','p','p','d','e','v',0};
485 RtlOpenCurrentUser( KEY_ALL_ACCESS
, &root
);
486 attr
.Length
= sizeof(attr
);
487 attr
.RootDirectory
= root
;
488 attr
.ObjectName
= &nameW
;
490 attr
.SecurityDescriptor
= NULL
;
491 attr
.SecurityQualityOfService
= NULL
;
492 RtlInitUnicodeString( &nameW
, configW
);
494 /* @@ Wine registry key: HKCU\Software\Wine\VDM\ppdev */
495 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)) hkey
= 0;
501 DWORD total_size
, len
;
503 KEY_VALUE_FULL_INFORMATION
*info
= (KEY_VALUE_FULL_INFORMATION
*)temp
;
505 if (NtEnumerateValueKey( hkey
, idx
, KeyValueFullInformation
,
506 temp
, sizeof(temp
), &total_size
)) break;
507 if (info
->Type
!= REG_SZ
) break;
509 RtlUnicodeToMultiByteN( name
, sizeof(name
)-1, &len
, info
->Name
, info
->NameLength
);
511 RtlUnicodeToMultiByteN( buffer
, sizeof(buffer
)-1, &len
,
512 (WCHAR
*)(temp
+ info
->DataOffset
), total_size
-info
->DataOffset
);
518 FIXME("Make the PPDeviceList larger than 5 elements\n");
521 TRACE("Device '%s' at virtual userbase '%s'\n", buffer
,name
);
522 timeout
= strchr(buffer
,',');
525 fd
=open(buffer
,O_RDWR
);
529 WARN("Configuration: No access to %s Cause: %s\n",buffer
,strerror(lasterror
));
530 WARN("Rejecting configuration item\n");
531 if (lasterror
== ENODEV
)
532 ERR("Is the ppdev module loaded?\n");
535 userbase
= strtol(name
, NULL
, 16);
536 if ( errno
== ERANGE
)
538 WARN("Configuration: Invalid base %s for %s\n",name
,buffer
);
539 WARN("Rejecting configuration item\n");
542 if (ioctl (fd
,PPCLAIM
,0))
544 ERR("PPCLAIM rejected %s\n",buffer
);
545 ERR("Perhaps the device is already in use or nonexistent\n");
550 for (i
=0; i
<= nports
; i
++)
552 if (PPDeviceList
[i
].userbase
== userbase
)
554 WARN("Configuration: %s uses the same virtual ports as %s\n",
555 buffer
,PPDeviceList
[0].devicename
);
556 WARN("Configuration: Rejecting configuration item\n");
561 if (!userbase
) continue;
563 /* Check for the minimum required IOCTLS */
564 if ((ioctl(fd
,PPRDATA
,&res
))||
565 (ioctl(fd
,PPRSTATUS
,&res
))||
566 (ioctl(fd
,PPRCONTROL
,&res
)))
568 ERR("PPUSER IOCTL not available for parport device %s\n",buffer
);
571 if (ioctl (fd
,PPRELEASE
,0))
573 ERR("PPRELEASE rejected %s\n",buffer
);
574 ERR("Perhaps the device is already in use or nonexistent\n");
577 PPDeviceList
[nports
].devicename
= HeapAlloc(GetProcessHeap(), 0, sizeof(buffer
)+1);
578 if (!PPDeviceList
[nports
].devicename
)
580 ERR("No (more) space for devicename\n");
583 strcpy(PPDeviceList
[nports
].devicename
,buffer
);
584 PPDeviceList
[nports
].fd
= fd
;
585 PPDeviceList
[nports
].userbase
= userbase
;
586 PPDeviceList
[nports
].lastaccess
=GetTickCount();
589 PPDeviceList
[nports
].timeout
= strtol(timeout
, NULL
, 10);
592 WARN("Configuration: Invalid timeout %s in configuration for %s, Setting to 0\n",
594 PPDeviceList
[nports
].timeout
= 0;
598 PPDeviceList
[nports
].timeout
= 0;
601 TRACE("found %d ports\n",nports
);
606 /* sort in ascending order for userbase for faster access */
607 qsort (PPDeviceList
,PPDeviceNum
,sizeof(PPDeviceStruct
),IO_pp_sort
);
611 for (idx
= 0;idx
<PPDeviceNum
; idx
++)
612 TRACE("found device %s userbase %x fd %x timeout %d\n",
613 PPDeviceList
[idx
].devicename
, PPDeviceList
[idx
].userbase
,
614 PPDeviceList
[idx
].fd
,PPDeviceList
[idx
].timeout
);
616 register a timer callback perhaps every 30 seconds to release unused ports
617 Set lastaccess = 0 as indicator when port was released
624 * Do the actual IOCTL
625 * Return NULL on success
627 static int IO_pp_do_access(int idx
,int ppctl
, DWORD
* res
)
630 if (ioctl(PPDeviceList
[idx
].fd
,PPCLAIM
,0))
632 ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
633 PPDeviceList
[idx
].devicename
);
636 ret
= ioctl(PPDeviceList
[idx
].fd
,ppctl
,res
);
637 if (ioctl(PPDeviceList
[idx
].fd
,PPRELEASE
,0))
639 ERR("Can't release device %s, PPUSER/PPDEV handling confused\n",
640 PPDeviceList
[idx
].devicename
);
649 * Check if we can satisfy the INP command with some of the configured PPDEV device
650 * Return NULL on success
652 static int IO_pp_inp(int port
, DWORD
* res
)
656 for (idx
=0;idx
<PPDeviceNum
;idx
++)
658 j
= port
- PPDeviceList
[idx
].userbase
;
663 return IO_pp_do_access(idx
,PPRDATA
,res
);
665 return IO_pp_do_access(idx
,PPRSTATUS
,res
);
667 return IO_pp_do_access(idx
,PPRCONTROL
,res
);
673 FIXME("Port 0x%x not accessible for reading with ppdev\n",port
);
674 FIXME("If this is causing problems, try direct port access\n");
685 * Check if we can satisfy the OUTP command with some of the configured PPDEV device
686 * Return NULL on success
688 static BOOL
IO_pp_outp(int port
, DWORD
* res
)
692 for (idx
=0;idx
<PPDeviceNum
;idx
++)
694 j
= port
- PPDeviceList
[idx
].userbase
;
699 return IO_pp_do_access(idx
,PPWDATA
,res
);
702 /* We can't switch port direction via PPWCONTROL,
703 so do it via PPDATADIR
705 DWORD mode
= *res
& 0x20;
706 IO_pp_do_access(idx
,PPDATADIR
,&mode
);
707 mode
= (*res
& ~0x20);
708 return IO_pp_do_access(idx
,PPWCONTROL
,&mode
);
717 FIXME("Port %d not accessible for writing with ppdev\n",port
);
718 FIXME("If this is causing problems, try direct port access\n");
727 #endif /* HAVE_PPDEV */
730 /**********************************************************************
733 * Note: The size argument has to be handled correctly _externally_
734 * (as we always return a DWORD)
736 DWORD
DOSVM_inport( int port
, int size
)
740 TRACE("%d-byte value from port 0x%04x\n", size
, port
);
742 DOSMEM_InitDosMemory();
745 if (do_pp_port_access
== -1) do_pp_port_access
=IO_pp_init();
746 if ((do_pp_port_access
== 0 ) && (size
== 1))
748 if (!IO_pp_inp(port
,&res
)) return res
;
752 #ifdef DIRECT_IO_ACCESS
753 if (do_direct_port_access
== -1) IO_port_init();
754 if ((do_direct_port_access
)
755 /* Make sure we have access to the port */
756 && (port_permissions
[port
] & IO_READ
))
761 case 1: res
= inb( port
); break;
762 case 2: res
= inw( port
); break;
763 case 4: res
= inl( port
); break;
776 BYTE chan
= port
& 3;
777 WORD tempval
= tmr_8253
[chan
].flags
& TMR_LATCHED
778 ? tmr_8253
[chan
].latch
: get_timer_val(chan
);
780 if (tmr_8253
[chan
].flags
& TMR_STATUS
)
782 WARN("Read-back status\n");
783 /* We differ slightly from the spec:
784 * - TMR_UPDATE is already set with the first write
785 * of a two byte counter update
786 * - 0x80 should be set if OUT signal is 1 (high)
788 tmr_8253
[chan
].flags
&= ~TMR_STATUS
;
789 res
= (tmr_8253
[chan
].ctrlbyte_ch
& 0x3F) |
790 (tmr_8253
[chan
].flags
& TMR_UPDATE
? 0x40 : 0x00);
793 switch ((tmr_8253
[chan
].ctrlbyte_ch
& 0x30) >> 4)
796 res
= 0; /* shouldn't happen? */
798 case 1: /* read lo byte */
800 tmr_8253
[chan
].flags
&= ~TMR_LATCHED
;
802 case 3: /* read lo byte, then hi byte */
803 tmr_8253
[chan
].flags
^= TMR_RTOGGLE
; /* toggle */
804 if (tmr_8253
[chan
].flags
& TMR_RTOGGLE
)
809 /* else [fall through if read hi byte !] */
810 case 2: /* read hi byte */
811 res
= (BYTE
)(tempval
>> 8);
812 tmr_8253
[chan
].flags
&= ~TMR_LATCHED
;
820 res
= (DWORD
)parport_8255
[1];
823 res
= (DWORD
)parport_8255
[2];
826 res
= (DWORD
)cmosaddress
;
829 if (!cmos_image_initialized
)
831 IO_FixCMOSCheckSum();
832 cmos_image_initialized
= TRUE
;
834 res
= (DWORD
)cmosimage
[cmosaddress
& 0x3f];
838 res
= ~0U; /* no joystick */
843 res
= (DWORD
)SB_ioport_in( port
);
879 res
= (DWORD
)DMA_ioport_in( port
);
882 WARN("Direct I/O read attempted from port %x\n", port
);
889 /**********************************************************************
892 void DOSVM_outport( int port
, int size
, DWORD value
)
894 TRACE("IO: 0x%x (%d-byte value) to port 0x%04x\n", value
, size
, port
);
896 DOSMEM_InitDosMemory();
899 if (do_pp_port_access
== -1) do_pp_port_access
= IO_pp_init();
900 if ((do_pp_port_access
== 0) && (size
== 1))
902 if (!IO_pp_outp(port
,&value
)) return;
906 #ifdef DIRECT_IO_ACCESS
908 if (do_direct_port_access
== -1) IO_port_init();
909 if ((do_direct_port_access
)
910 /* Make sure we have access to the port */
911 && (port_permissions
[port
] & IO_WRITE
))
916 case 1: outb( LOBYTE(value
), port
); break;
917 case 2: outw( LOWORD(value
), port
); break;
918 case 4: outl( value
, port
); break;
933 BYTE chan
= port
& 3;
935 tmr_8253
[chan
].flags
|= TMR_UPDATE
;
936 switch ((tmr_8253
[chan
].ctrlbyte_ch
& 0x30) >> 4)
939 break; /* shouldn't happen? */
940 case 1: /* write lo byte */
941 tmr_8253
[chan
].countmax
=
942 (tmr_8253
[chan
].countmax
& 0xff00) | (BYTE
)value
;
944 case 3: /* write lo byte, then hi byte */
945 tmr_8253
[chan
].flags
^= TMR_WTOGGLE
; /* toggle */
946 if (tmr_8253
[chan
].flags
& TMR_WTOGGLE
)
948 tmr_8253
[chan
].countmax
=
949 (tmr_8253
[chan
].countmax
& 0xff00) | (BYTE
)value
;
952 /* else [fall through if write hi byte !] */
953 case 2: /* write hi byte */
954 tmr_8253
[chan
].countmax
=
955 (tmr_8253
[chan
].countmax
& 0x00ff) | ((BYTE
)value
<< 8);
958 /* if programming is finished, update to new value */
959 if ((tmr_8253
[chan
].ctrlbyte_ch
& 0x30) &&
960 !(tmr_8253
[chan
].flags
& TMR_WTOGGLE
))
966 BYTE chan
= ((BYTE
)value
& 0xc0) >> 6;
967 /* ctrl byte for specific timer channel */
970 if ( !(value
& 0x20) )
972 if ((value
& 0x02) && !(tmr_8253
[0].flags
& TMR_LATCHED
))
974 tmr_8253
[0].flags
|= TMR_LATCHED
;
975 tmr_8253
[0].latch
= get_timer_val(0);
977 if ((value
& 0x04) && !(tmr_8253
[1].flags
& TMR_LATCHED
))
979 tmr_8253
[1].flags
|= TMR_LATCHED
;
980 tmr_8253
[1].latch
= get_timer_val(1);
982 if ((value
& 0x08) && !(tmr_8253
[2].flags
& TMR_LATCHED
))
984 tmr_8253
[2].flags
|= TMR_LATCHED
;
985 tmr_8253
[2].latch
= get_timer_val(2);
989 if ( !(value
& 0x10) )
992 tmr_8253
[0].flags
|= TMR_STATUS
;
994 tmr_8253
[1].flags
|= TMR_STATUS
;
996 tmr_8253
[2].flags
|= TMR_STATUS
;
1000 switch (((BYTE
)value
& 0x30) >> 4)
1002 case 0: /* latch timer */
1003 if ( !(tmr_8253
[chan
].flags
& TMR_LATCHED
) )
1005 tmr_8253
[chan
].flags
|= TMR_LATCHED
;
1006 tmr_8253
[chan
].latch
= get_timer_val(chan
);
1009 case 1: /* write lo byte only */
1010 case 2: /* write hi byte only */
1011 case 3: /* write lo byte, then hi byte */
1012 tmr_8253
[chan
].ctrlbyte_ch
= (BYTE
)value
;
1013 tmr_8253
[chan
].countmax
= 0;
1014 tmr_8253
[chan
].flags
= TMR_UPDATE
;
1020 parport_8255
[1] = (BYTE
)value
;
1021 if (((parport_8255
[1] & 3) == 3) && (tmr_8253
[2].countmax
!= 1))
1023 TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253
[2].countmax
);
1024 Beep(1193180 / tmr_8253
[2].countmax
, 20);
1028 cmosaddress
= (BYTE
)value
& 0x7f;
1031 if (!cmos_image_initialized
)
1033 IO_FixCMOSCheckSum();
1034 cmos_image_initialized
= TRUE
;
1036 cmosimage
[cmosaddress
& 0x3f] = (BYTE
)value
;
1040 SB_ioport_out( port
, (BYTE
)value
);
1088 DMA_ioport_out( port
, (BYTE
)value
);
1091 WARN("Direct I/O write attempted to port %x\n", port
);