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);
63 #if defined(linux) && !defined(__ANDROID__)
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 BOOL cmos_image_initialized
= FALSE
;
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
= TRUE
;
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
= TRUE
;
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
);