riched20: Fix ME_SetSelection when cpMax > strlen().
[wine.git] / dlls / krnl386.exe16 / ioports.c
bloba93827c0ed579b8fe192dffbf1ccdc75d73e421e
1 /*
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
23 /* Known problems:
24 - only a few ports are emulated.
25 - real-time clock in "cmos" is bogus. A nifty alarm() setup could
26 fix that, I guess.
29 #include "config.h"
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34 #ifdef HAVE_SYS_STAT_H
35 # include <sys/stat.h>
36 #endif
38 #ifdef HAVE_PPDEV
39 #include <fcntl.h>
40 #include <errno.h>
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
43 #endif
44 #ifdef HAVE_LINUX_IOCTL_H
45 # include <linux/ioctl.h>
46 #endif
47 #include <linux/ppdev.h>
48 #endif
50 #include "windef.h"
51 #include "winbase.h"
52 #include "winnls.h"
53 #include "winreg.h"
54 #include "winternl.h"
55 #include "kernel16_private.h"
56 #include "dosexe.h"
57 #include "vga.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
65 #else
66 # undef DIRECT_IO_ACCESS
67 #endif
69 static struct {
70 WORD countmax;
71 WORD latch;
72 BYTE ctrlbyte_ch;
73 BYTE flags;
74 LONG64 start_time;
75 } tmr_8253[3] = {
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)
168 WORD sum = 0;
169 int i;
171 for (i=0x10; i < 0x2d; i++)
172 sum += cmosimage[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];
184 #define IO_READ 1
185 #define IO_WRITE 2
187 #endif /* DIRECT_IO_ACCESS */
189 #ifdef HAVE_PPDEV
190 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
191 0: available);*/
192 #endif
194 #define BCD2BIN(a) \
195 ((a)%10 + ((a)>>4)%10*10 + ((a)>>8)%10*100 + ((a)>>12)%10*1000)
196 #define BIN2BCD(a) \
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)
205 val = BCD2BIN(val);
207 tmr_8253[timer].flags &= ~TMR_UPDATE;
208 if (!QueryPerformanceCounter((LARGE_INTEGER*)&tmr_8253[timer].start_time))
209 WARN("QueryPerformanceCounter should not fail!\n");
211 switch (timer) {
212 case 0: /* System timer counter divisor */
213 DOSVM_SetTimer(val);
214 break;
215 case 1: /* RAM refresh */
216 FIXME("RAM refresh counter handling not implemented !\n");
217 break;
218 case 2: /* cassette & speaker */
219 /* speaker on ? */
220 if ((parport_8255[1] & 3) == 3)
222 TRACE("Beep (freq: %d) !\n", 1193180 / val);
223 Beep(1193180 / val, 20);
225 break;
230 static WORD get_timer_val(unsigned timer)
232 LARGE_INTEGER time;
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)
242 return 0;
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)
249 val = BCD2BIN(val);
251 switch ( mode )
253 case 0:
254 case 1:
255 case 4:
256 case 5:
257 maxval = tmr_8253[timer].ctrlbyte_ch & 0x01 ? 9999 : 0xFFFF;
258 break;
259 case 2:
260 case 3:
261 maxval = val;
262 break;
263 default:
264 ERR("Invalid PIT mode: %d\n", mode);
265 return 0;
268 val = (val - time.QuadPart) % (maxval + 1);
269 if (tmr_8253[timer].ctrlbyte_ch & 0x01)
270 val = BIN2BCD(val);
272 return val;
276 /**********************************************************************
277 * IO_port_init
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)
286 int j;
287 if (val1 != -1) {
288 if (val == -1) val = 0x3ff;
289 for (j = val1; j <= val; j++)
290 port_permissions[j] |= rw;
292 do_direct_port_access = 1;
294 val1 = -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)
309 int val, val1;
310 unsigned int i;
311 WCHAR *end;
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;
319 else
321 val = -1;
322 val1 = -1;
323 while (*str)
325 switch(*str)
327 case ',':
328 case ' ':
329 case '\t':
330 set_IO_permissions(val1, val, rw);
331 val1 = -1;
332 val = -1;
333 str++;
334 break;
335 case '-':
336 val1 = val;
337 if (val1 == -1) val1 = 0;
338 str++;
339 break;
340 default:
341 if (isdigitW(*str))
343 val = strtoulW( str, &end, 0 );
344 if (end == str)
346 val = -1;
347 str++;
349 else str = end;
351 break;
354 set_IO_permissions(val1, val, rw);
358 static inline BYTE inb( WORD port )
360 BYTE b;
361 __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
362 return b;
365 static inline WORD inw( WORD port )
367 WORD w;
368 __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
369 return w;
372 static inline DWORD inl( WORD port )
374 DWORD dw;
375 __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
376 return dw;
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)
396 char tmp[1024];
397 HANDLE root, hkey;
398 DWORD dummy;
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? */
409 if (!iopl(3))
411 iopl(0);
413 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
414 attr.Length = sizeof(attr);
415 attr.RootDirectory = root;
416 attr.ObjectName = &nameW;
417 attr.Attributes = 0;
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);
437 NtClose( hkey );
439 NtClose( root );
443 #endif /* DIRECT_IO_ACCESS */
446 #ifdef HAVE_PPDEV
448 typedef struct _PPDEVICESTRUCT{
449 int fd; /* NULL if device not available */
450 char *devicename;
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 */
454 } PPDeviceStruct;
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;
464 /* IO_pp_init
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)
472 char name[80];
473 char buffer[256];
474 HANDLE root, hkey;
475 int i,idx=0,fd,res,userbase,nports=0;
476 char * timeout;
477 char ret=1;
478 int lasterror;
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};
485 TRACE("\n");
487 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
488 attr.Length = sizeof(attr);
489 attr.RootDirectory = root;
490 attr.ObjectName = &nameW;
491 attr.Attributes = 0;
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;
498 NtClose( root );
499 if (!hkey) return 1;
501 for (;;)
503 DWORD total_size, len;
504 char temp[256];
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 );
512 name[len] = 0;
513 RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len,
514 (WCHAR *)(temp + info->DataOffset), total_size-info->DataOffset );
515 buffer[len] = 0;
517 idx++;
518 if(nports >4)
520 FIXME("Make the PPDeviceList larger than 5 elements\n");
521 break;
523 TRACE("Device '%s' at virtual userbase '%s'\n", buffer,name);
524 timeout = strchr(buffer,',');
525 if (timeout)
526 *timeout++=0;
527 fd=open(buffer,O_RDWR);
528 lasterror=errno;
529 if (fd == -1)
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");
535 continue;
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");
542 continue;
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");
548 continue;
550 if (nports > 0)
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");
559 userbase = 0;
560 break;
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);
571 continue;
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");
577 continue;
579 PPDeviceList[nports].devicename = HeapAlloc(GetProcessHeap(), 0, sizeof(buffer)+1);
580 if (!PPDeviceList[nports].devicename)
582 ERR("No (more) space for devicename\n");
583 break;
585 strcpy(PPDeviceList[nports].devicename,buffer);
586 PPDeviceList[nports].fd = fd;
587 PPDeviceList[nports].userbase = userbase;
588 PPDeviceList[nports].lastaccess=GetTickCount();
589 if (timeout)
591 PPDeviceList[nports].timeout = strtol(timeout, NULL, 10);
592 if (errno == ERANGE)
594 WARN("Configuration: Invalid timeout %s in configuration for %s, Setting to 0\n",
595 timeout,buffer);
596 PPDeviceList[nports].timeout = 0;
599 else
600 PPDeviceList[nports].timeout = 0;
601 nports++;
603 TRACE("found %d ports\n",nports);
604 NtClose( hkey );
606 PPDeviceNum= nports;
607 if (nports > 1)
608 /* sort in ascending order for userbase for faster access */
609 qsort (PPDeviceList,PPDeviceNum,sizeof(PPDeviceStruct),IO_pp_sort);
611 if (nports)
612 ret=0;
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);
617 /* FIXME:
618 register a timer callback perhaps every 30 seconds to release unused ports
619 Set lastaccess = 0 as indicator when port was released
621 return ret;
624 /* IO_pp_do_access
626 * Do the actual IOCTL
627 * Return NULL on success
629 static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
631 int ret;
632 if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
634 ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
635 PPDeviceList[idx].devicename);
636 return 1;
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);
643 return 1;
645 return ret;
649 /* IO_pp_inp
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)
656 int idx,j=0;
658 for (idx=0;idx<PPDeviceNum ;idx++)
660 j = port - PPDeviceList[idx].userbase;
661 if (j <0) return 1;
662 switch (j)
664 case 0:
665 return IO_pp_do_access(idx,PPRDATA,res);
666 case 1:
667 return IO_pp_do_access(idx,PPRSTATUS,res);
668 case 2:
669 return IO_pp_do_access(idx,PPRCONTROL,res);
670 case 0x400:
671 case 0x402:
672 case 3:
673 case 4:
674 case 0x401:
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");
677 return 1;
678 default:
679 break;
682 return 1;
685 /* IO_pp_outp
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)
692 int idx,j=0;
694 for (idx=0;idx<PPDeviceNum ;idx++)
696 j = port - PPDeviceList[idx].userbase;
697 if (j <0) return 1;
698 switch (j)
700 case 0:
701 return IO_pp_do_access(idx,PPWDATA,res);
702 case 2:
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);
713 case 1:
714 case 0x400:
715 case 0x402:
716 case 3:
717 case 4:
718 case 0x401:
719 FIXME("Port %d not accessible for writing with ppdev\n",port);
720 FIXME("If this is causing problems, try direct port access\n");
721 return 1;
722 default:
723 break;
726 return TRUE;
729 #endif /* HAVE_PPDEV */
732 /**********************************************************************
733 * DOSVM_inport
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 )
740 DWORD res = ~0U;
742 TRACE("%d-byte value from port 0x%04x\n", size, port );
744 DOSMEM_InitDosMemory();
746 #ifdef HAVE_PPDEV
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;
752 #endif
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))
760 iopl(3);
761 switch(size)
763 case 1: res = inb( port ); break;
764 case 2: res = inw( port ); break;
765 case 4: res = inl( port ); break;
767 iopl(0);
768 return res;
770 #endif
772 switch (port)
774 case 0x40:
775 case 0x41:
776 case 0x42:
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);
793 break;
795 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
797 case 0:
798 res = 0; /* shouldn't happen? */
799 break;
800 case 1: /* read lo byte */
801 res = (BYTE)tempval;
802 tmr_8253[chan].flags &= ~TMR_LATCHED;
803 break;
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)
808 res = (BYTE)tempval;
809 break;
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;
815 break;
818 break;
819 case 0x60:
820 res = DOSVM_Int09ReadScan(NULL);
821 break;
822 case 0x61:
823 res = (DWORD)parport_8255[1];
824 break;
825 case 0x62:
826 res = (DWORD)parport_8255[2];
827 break;
828 case 0x70:
829 res = (DWORD)cmosaddress;
830 break;
831 case 0x71:
832 if (!cmos_image_initialized)
834 IO_FixCMOSCheckSum();
835 cmos_image_initialized = TRUE;
837 res = (DWORD)cmosimage[cmosaddress & 0x3f];
838 break;
839 case 0x200:
840 case 0x201:
841 res = ~0U; /* no joystick */
842 break;
843 case 0x22a:
844 case 0x22c:
845 case 0x22e:
846 res = (DWORD)SB_ioport_in( port );
847 break;
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 */
858 case 0x3c6:
859 case 0x3c7: /* General Register - DAC State */
860 case 0x3c8:
861 case 0x3c9:
862 case 0x3ca: /* General Register - Feature control */
863 case 0x3cb:
864 case 0x3cc: /* General Register - Misc output */
865 case 0x3cd:
866 case 0x3ce: /* Graphics Controller Register - Address */
867 case 0x3cf: /* Graphics Controller Register - Other */
868 case 0x3d0:
869 case 0x3d1:
870 case 0x3d2:
871 case 0x3d3:
872 case 0x3d4: /* CRT Controller Register - Index (CGA) */
873 case 0x3d5: /* CRT Controller Register - Other (CGA) */
874 case 0x3d6:
875 case 0x3d7:
876 case 0x3d8:
877 case 0x3d9:
878 case 0x3da:
879 case 0x3db:
880 case 0x3dc:
881 case 0x3dd:
882 case 0x3de:
883 case 0x3df:
884 if (size > 1)
885 FIXME("Trying to read more than one byte from VGA!\n");
886 res = (DWORD)VGA_ioport_in( port );
887 break;
888 case 0x00:
889 case 0x01:
890 case 0x02:
891 case 0x03:
892 case 0x04:
893 case 0x05:
894 case 0x06:
895 case 0x07:
896 case 0xC0:
897 case 0xC2:
898 case 0xC4:
899 case 0xC6:
900 case 0xC8:
901 case 0xCA:
902 case 0xCC:
903 case 0xCE:
904 case 0x87:
905 case 0x83:
906 case 0x81:
907 case 0x82:
908 case 0x8B:
909 case 0x89:
910 case 0x8A:
911 case 0x487:
912 case 0x483:
913 case 0x481:
914 case 0x482:
915 case 0x48B:
916 case 0x489:
917 case 0x48A:
918 case 0x08:
919 case 0xD0:
920 case 0x0D:
921 case 0xDA:
922 res = (DWORD)DMA_ioport_in( port );
923 break;
924 default:
925 WARN("Direct I/O read attempted from port %x\n", port);
926 break;
928 return res;
932 /**********************************************************************
933 * DOSVM_outport
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();
941 #ifdef HAVE_PPDEV
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;
947 #endif
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))
956 iopl(3);
957 switch(size)
959 case 1: outb( LOBYTE(value), port ); break;
960 case 2: outw( LOWORD(value), port ); break;
961 case 4: outl( value, port ); break;
963 iopl(0);
964 return;
966 #endif
968 switch (port)
970 case 0x20:
971 DOSVM_PIC_ioport_out( port, (BYTE)value );
972 break;
973 case 0x40:
974 case 0x41:
975 case 0x42:
977 BYTE chan = port & 3;
979 tmr_8253[chan].flags |= TMR_UPDATE;
980 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
982 case 0:
983 break; /* shouldn't happen? */
984 case 1: /* write lo byte */
985 tmr_8253[chan].countmax =
986 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
987 break;
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;
994 break;
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);
1000 break;
1002 /* if programming is finished, update to new value */
1003 if ((tmr_8253[chan].ctrlbyte_ch & 0x30) &&
1004 !(tmr_8253[chan].flags & TMR_WTOGGLE))
1005 set_timer(chan);
1007 break;
1008 case 0x43:
1010 BYTE chan = ((BYTE)value & 0xc0) >> 6;
1011 /* ctrl byte for specific timer channel */
1012 if (chan == 3)
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) )
1035 if (value & 0x02)
1036 tmr_8253[0].flags |= TMR_STATUS;
1037 if (value & 0x04)
1038 tmr_8253[1].flags |= TMR_STATUS;
1039 if (value & 0x08)
1040 tmr_8253[2].flags |= TMR_STATUS;
1042 break;
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);
1052 break;
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;
1059 break;
1062 break;
1063 case 0x61:
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);
1070 break;
1071 case 0x70:
1072 cmosaddress = (BYTE)value & 0x7f;
1073 break;
1074 case 0x71:
1075 if (!cmos_image_initialized)
1077 IO_FixCMOSCheckSum();
1078 cmos_image_initialized = TRUE;
1080 cmosimage[cmosaddress & 0x3f] = (BYTE)value;
1081 break;
1082 case 0x226:
1083 case 0x22c:
1084 SB_ioport_out( port, (BYTE)value );
1085 break;
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 */
1091 case 0x3c1:
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 */
1096 case 0x3c6:
1097 case 0x3c7:
1098 case 0x3c8:
1099 case 0x3c9:
1100 case 0x3ca:
1101 case 0x3cb:
1102 case 0x3cc:
1103 case 0x3cd:
1104 case 0x3ce: /* Graphics Controller Register - Address */
1105 case 0x3cf: /* Graphics Controller Register - Other */
1106 case 0x3d0:
1107 case 0x3d1:
1108 case 0x3d2:
1109 case 0x3d3:
1110 case 0x3d4: /* CRT Controller Register - Index (CGA) */
1111 case 0x3d5: /* CRT Controller Register - Other (CGA) */
1112 case 0x3d6:
1113 case 0x3d7:
1114 case 0x3d8:
1115 case 0x3d9:
1116 case 0x3da:
1117 case 0x3db:
1118 case 0x3dc:
1119 case 0x3dd:
1120 case 0x3de:
1121 case 0x3df:
1122 VGA_ioport_out( port, LOBYTE(value) );
1123 if(size > 1) {
1124 VGA_ioport_out( port+1, HIBYTE(value) );
1125 if(size > 2) {
1126 VGA_ioport_out( port+2, LOBYTE(HIWORD(value)) );
1127 VGA_ioport_out( port+3, HIBYTE(HIWORD(value)) );
1130 break;
1131 case 0x00:
1132 case 0x01:
1133 case 0x02:
1134 case 0x03:
1135 case 0x04:
1136 case 0x05:
1137 case 0x06:
1138 case 0x07:
1139 case 0xC0:
1140 case 0xC2:
1141 case 0xC4:
1142 case 0xC6:
1143 case 0xC8:
1144 case 0xCA:
1145 case 0xCC:
1146 case 0xCE:
1147 case 0x87:
1148 case 0x83:
1149 case 0x81:
1150 case 0x82:
1151 case 0x8B:
1152 case 0x89:
1153 case 0x8A:
1154 case 0x487:
1155 case 0x483:
1156 case 0x481:
1157 case 0x482:
1158 case 0x48B:
1159 case 0x489:
1160 case 0x48A:
1161 case 0x08:
1162 case 0xD0:
1163 case 0x0B:
1164 case 0xD6:
1165 case 0x0A:
1166 case 0xD4:
1167 case 0x0F:
1168 case 0xDE:
1169 case 0x09:
1170 case 0xD2:
1171 case 0x0C:
1172 case 0xD8:
1173 case 0x0D:
1174 case 0xDA:
1175 case 0x0E:
1176 case 0xDC:
1177 DMA_ioport_out( port, (BYTE)value );
1178 break;
1179 default:
1180 WARN("Direct I/O write attempted to port %x\n", port );
1181 break;