user32/listbox: Make SetCount fail if LBS_NODATA is not set.
[wine.git] / dlls / krnl386.exe16 / ioports.c
blob2bff8a26e636c67e4a3113c5905af11404e0baed
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 "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
64 #else
65 # undef DIRECT_IO_ACCESS
66 #endif
68 static struct {
69 WORD countmax;
70 WORD latch;
71 BYTE ctrlbyte_ch;
72 BYTE flags;
73 LONG64 start_time;
74 } tmr_8253[3] = {
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)
167 WORD sum = 0;
168 int i;
170 for (i=0x10; i < 0x2d; i++)
171 sum += cmosimage[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];
183 #define IO_READ 1
184 #define IO_WRITE 2
186 #endif /* DIRECT_IO_ACCESS */
188 #ifdef HAVE_PPDEV
189 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
190 0: available);*/
191 #endif
193 #define BCD2BIN(a) \
194 ((a)%10 + ((a)>>4)%10*10 + ((a)>>8)%10*100 + ((a)>>12)%10*1000)
195 #define BIN2BCD(a) \
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)
204 val = BCD2BIN(val);
206 tmr_8253[timer].flags &= ~TMR_UPDATE;
207 if (!QueryPerformanceCounter((LARGE_INTEGER*)&tmr_8253[timer].start_time))
208 WARN("QueryPerformanceCounter should not fail!\n");
210 switch (timer) {
211 case 0: /* System timer counter divisor */
212 break;
213 case 1: /* RAM refresh */
214 FIXME("RAM refresh counter handling not implemented !\n");
215 break;
216 case 2: /* cassette & speaker */
217 /* speaker on ? */
218 if ((parport_8255[1] & 3) == 3)
220 TRACE("Beep (freq: %d) !\n", 1193180 / val);
221 Beep(1193180 / val, 20);
223 break;
228 static WORD get_timer_val(unsigned timer)
230 LARGE_INTEGER time;
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)
240 return 0;
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)
247 val = BCD2BIN(val);
249 switch ( mode )
251 case 0:
252 case 1:
253 case 4:
254 case 5:
255 maxval = tmr_8253[timer].ctrlbyte_ch & 0x01 ? 9999 : 0xFFFF;
256 break;
257 case 2:
258 case 3:
259 maxval = val;
260 break;
261 default:
262 ERR("Invalid PIT mode: %d\n", mode);
263 return 0;
266 val = (val - time.QuadPart) % (maxval + 1);
267 if (tmr_8253[timer].ctrlbyte_ch & 0x01)
268 val = BIN2BCD(val);
270 return val;
274 /**********************************************************************
275 * IO_port_init
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)
284 int j;
285 if (val1 != -1) {
286 if (val == -1) val = 0x3ff;
287 for (j = val1; j <= val; j++)
288 port_permissions[j] |= rw;
290 do_direct_port_access = 1;
292 val1 = -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)
307 int val, val1;
308 unsigned int i;
309 WCHAR *end;
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;
317 else
319 val = -1;
320 val1 = -1;
321 while (*str)
323 switch(*str)
325 case ',':
326 case ' ':
327 case '\t':
328 set_IO_permissions(val1, val, rw);
329 val1 = -1;
330 val = -1;
331 str++;
332 break;
333 case '-':
334 val1 = val;
335 if (val1 == -1) val1 = 0;
336 str++;
337 break;
338 default:
339 if (isdigitW(*str))
341 val = strtoulW( str, &end, 0 );
342 if (end == str)
344 val = -1;
345 str++;
347 else str = end;
349 break;
352 set_IO_permissions(val1, val, rw);
356 static inline BYTE inb( WORD port )
358 BYTE b;
359 __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
360 return b;
363 static inline WORD inw( WORD port )
365 WORD w;
366 __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
367 return w;
370 static inline DWORD inl( WORD port )
372 DWORD dw;
373 __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
374 return dw;
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)
394 char tmp[1024];
395 HANDLE root, hkey;
396 DWORD dummy;
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? */
407 if (!iopl(3))
409 iopl(0);
411 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
412 attr.Length = sizeof(attr);
413 attr.RootDirectory = root;
414 attr.ObjectName = &nameW;
415 attr.Attributes = 0;
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);
435 NtClose( hkey );
437 NtClose( root );
441 #endif /* DIRECT_IO_ACCESS */
444 #ifdef HAVE_PPDEV
446 typedef struct _PPDEVICESTRUCT{
447 int fd; /* NULL if device not available */
448 char *devicename;
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 */
452 } PPDeviceStruct;
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;
462 /* IO_pp_init
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)
470 char name[80];
471 char buffer[256];
472 HANDLE root, hkey;
473 int i,idx=0,fd,res,userbase,nports=0;
474 char * timeout;
475 char ret=1;
476 int lasterror;
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};
483 TRACE("\n");
485 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
486 attr.Length = sizeof(attr);
487 attr.RootDirectory = root;
488 attr.ObjectName = &nameW;
489 attr.Attributes = 0;
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;
496 NtClose( root );
497 if (!hkey) return 1;
499 for (;;)
501 DWORD total_size, len;
502 char temp[256];
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 );
510 name[len] = 0;
511 RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len,
512 (WCHAR *)(temp + info->DataOffset), total_size-info->DataOffset );
513 buffer[len] = 0;
515 idx++;
516 if(nports >4)
518 FIXME("Make the PPDeviceList larger than 5 elements\n");
519 break;
521 TRACE("Device '%s' at virtual userbase '%s'\n", buffer,name);
522 timeout = strchr(buffer,',');
523 if (timeout)
524 *timeout++=0;
525 fd=open(buffer,O_RDWR);
526 lasterror=errno;
527 if (fd == -1)
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");
533 continue;
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");
540 continue;
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");
546 continue;
548 if (nports > 0)
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");
557 userbase = 0;
558 break;
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);
569 continue;
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");
575 continue;
577 PPDeviceList[nports].devicename = HeapAlloc(GetProcessHeap(), 0, sizeof(buffer)+1);
578 if (!PPDeviceList[nports].devicename)
580 ERR("No (more) space for devicename\n");
581 break;
583 strcpy(PPDeviceList[nports].devicename,buffer);
584 PPDeviceList[nports].fd = fd;
585 PPDeviceList[nports].userbase = userbase;
586 PPDeviceList[nports].lastaccess=GetTickCount();
587 if (timeout)
589 PPDeviceList[nports].timeout = strtol(timeout, NULL, 10);
590 if (errno == ERANGE)
592 WARN("Configuration: Invalid timeout %s in configuration for %s, Setting to 0\n",
593 timeout,buffer);
594 PPDeviceList[nports].timeout = 0;
597 else
598 PPDeviceList[nports].timeout = 0;
599 nports++;
601 TRACE("found %d ports\n",nports);
602 NtClose( hkey );
604 PPDeviceNum= nports;
605 if (nports > 1)
606 /* sort in ascending order for userbase for faster access */
607 qsort (PPDeviceList,PPDeviceNum,sizeof(PPDeviceStruct),IO_pp_sort);
609 if (nports)
610 ret=0;
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);
615 /* FIXME:
616 register a timer callback perhaps every 30 seconds to release unused ports
617 Set lastaccess = 0 as indicator when port was released
619 return ret;
622 /* IO_pp_do_access
624 * Do the actual IOCTL
625 * Return NULL on success
627 static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
629 int ret;
630 if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
632 ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
633 PPDeviceList[idx].devicename);
634 return 1;
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);
641 return 1;
643 return ret;
647 /* IO_pp_inp
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)
654 int idx,j=0;
656 for (idx=0;idx<PPDeviceNum ;idx++)
658 j = port - PPDeviceList[idx].userbase;
659 if (j <0) return 1;
660 switch (j)
662 case 0:
663 return IO_pp_do_access(idx,PPRDATA,res);
664 case 1:
665 return IO_pp_do_access(idx,PPRSTATUS,res);
666 case 2:
667 return IO_pp_do_access(idx,PPRCONTROL,res);
668 case 0x400:
669 case 0x402:
670 case 3:
671 case 4:
672 case 0x401:
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");
675 return 1;
676 default:
677 break;
680 return 1;
683 /* IO_pp_outp
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)
690 int idx,j=0;
692 for (idx=0;idx<PPDeviceNum ;idx++)
694 j = port - PPDeviceList[idx].userbase;
695 if (j <0) return 1;
696 switch (j)
698 case 0:
699 return IO_pp_do_access(idx,PPWDATA,res);
700 case 2:
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);
711 case 1:
712 case 0x400:
713 case 0x402:
714 case 3:
715 case 4:
716 case 0x401:
717 FIXME("Port %d not accessible for writing with ppdev\n",port);
718 FIXME("If this is causing problems, try direct port access\n");
719 return 1;
720 default:
721 break;
724 return TRUE;
727 #endif /* HAVE_PPDEV */
730 /**********************************************************************
731 * DOSVM_inport
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 )
738 DWORD res = ~0U;
740 TRACE("%d-byte value from port 0x%04x\n", size, port );
742 DOSMEM_InitDosMemory();
744 #ifdef HAVE_PPDEV
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;
750 #endif
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))
758 iopl(3);
759 switch(size)
761 case 1: res = inb( port ); break;
762 case 2: res = inw( port ); break;
763 case 4: res = inl( port ); break;
765 iopl(0);
766 return res;
768 #endif
770 switch (port)
772 case 0x40:
773 case 0x41:
774 case 0x42:
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);
791 break;
793 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
795 case 0:
796 res = 0; /* shouldn't happen? */
797 break;
798 case 1: /* read lo byte */
799 res = (BYTE)tempval;
800 tmr_8253[chan].flags &= ~TMR_LATCHED;
801 break;
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)
806 res = (BYTE)tempval;
807 break;
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;
813 break;
816 break;
817 case 0x60:
818 break;
819 case 0x61:
820 res = (DWORD)parport_8255[1];
821 break;
822 case 0x62:
823 res = (DWORD)parport_8255[2];
824 break;
825 case 0x70:
826 res = (DWORD)cmosaddress;
827 break;
828 case 0x71:
829 if (!cmos_image_initialized)
831 IO_FixCMOSCheckSum();
832 cmos_image_initialized = TRUE;
834 res = (DWORD)cmosimage[cmosaddress & 0x3f];
835 break;
836 case 0x200:
837 case 0x201:
838 res = ~0U; /* no joystick */
839 break;
840 case 0x22a:
841 case 0x22c:
842 case 0x22e:
843 res = (DWORD)SB_ioport_in( port );
844 break;
845 case 0x00:
846 case 0x01:
847 case 0x02:
848 case 0x03:
849 case 0x04:
850 case 0x05:
851 case 0x06:
852 case 0x07:
853 case 0xC0:
854 case 0xC2:
855 case 0xC4:
856 case 0xC6:
857 case 0xC8:
858 case 0xCA:
859 case 0xCC:
860 case 0xCE:
861 case 0x87:
862 case 0x83:
863 case 0x81:
864 case 0x82:
865 case 0x8B:
866 case 0x89:
867 case 0x8A:
868 case 0x487:
869 case 0x483:
870 case 0x481:
871 case 0x482:
872 case 0x48B:
873 case 0x489:
874 case 0x48A:
875 case 0x08:
876 case 0xD0:
877 case 0x0D:
878 case 0xDA:
879 res = (DWORD)DMA_ioport_in( port );
880 break;
881 default:
882 WARN("Direct I/O read attempted from port %x\n", port);
883 break;
885 return res;
889 /**********************************************************************
890 * DOSVM_outport
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();
898 #ifdef HAVE_PPDEV
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;
904 #endif
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))
913 iopl(3);
914 switch(size)
916 case 1: outb( LOBYTE(value), port ); break;
917 case 2: outw( LOWORD(value), port ); break;
918 case 4: outl( value, port ); break;
920 iopl(0);
921 return;
923 #endif
925 switch (port)
927 case 0x20:
928 break;
929 case 0x40:
930 case 0x41:
931 case 0x42:
933 BYTE chan = port & 3;
935 tmr_8253[chan].flags |= TMR_UPDATE;
936 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
938 case 0:
939 break; /* shouldn't happen? */
940 case 1: /* write lo byte */
941 tmr_8253[chan].countmax =
942 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
943 break;
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;
950 break;
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);
956 break;
958 /* if programming is finished, update to new value */
959 if ((tmr_8253[chan].ctrlbyte_ch & 0x30) &&
960 !(tmr_8253[chan].flags & TMR_WTOGGLE))
961 set_timer(chan);
963 break;
964 case 0x43:
966 BYTE chan = ((BYTE)value & 0xc0) >> 6;
967 /* ctrl byte for specific timer channel */
968 if (chan == 3)
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) )
991 if (value & 0x02)
992 tmr_8253[0].flags |= TMR_STATUS;
993 if (value & 0x04)
994 tmr_8253[1].flags |= TMR_STATUS;
995 if (value & 0x08)
996 tmr_8253[2].flags |= TMR_STATUS;
998 break;
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);
1008 break;
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;
1015 break;
1018 break;
1019 case 0x61:
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);
1026 break;
1027 case 0x70:
1028 cmosaddress = (BYTE)value & 0x7f;
1029 break;
1030 case 0x71:
1031 if (!cmos_image_initialized)
1033 IO_FixCMOSCheckSum();
1034 cmos_image_initialized = TRUE;
1036 cmosimage[cmosaddress & 0x3f] = (BYTE)value;
1037 break;
1038 case 0x226:
1039 case 0x22c:
1040 SB_ioport_out( port, (BYTE)value );
1041 break;
1042 case 0x00:
1043 case 0x01:
1044 case 0x02:
1045 case 0x03:
1046 case 0x04:
1047 case 0x05:
1048 case 0x06:
1049 case 0x07:
1050 case 0xC0:
1051 case 0xC2:
1052 case 0xC4:
1053 case 0xC6:
1054 case 0xC8:
1055 case 0xCA:
1056 case 0xCC:
1057 case 0xCE:
1058 case 0x87:
1059 case 0x83:
1060 case 0x81:
1061 case 0x82:
1062 case 0x8B:
1063 case 0x89:
1064 case 0x8A:
1065 case 0x487:
1066 case 0x483:
1067 case 0x481:
1068 case 0x482:
1069 case 0x48B:
1070 case 0x489:
1071 case 0x48A:
1072 case 0x08:
1073 case 0xD0:
1074 case 0x0B:
1075 case 0xD6:
1076 case 0x0A:
1077 case 0xD4:
1078 case 0x0F:
1079 case 0xDE:
1080 case 0x09:
1081 case 0xD2:
1082 case 0x0C:
1083 case 0xD8:
1084 case 0x0D:
1085 case 0xDA:
1086 case 0x0E:
1087 case 0xDC:
1088 DMA_ioport_out( port, (BYTE)value );
1089 break;
1090 default:
1091 WARN("Direct I/O write attempted to port %x\n", port );
1092 break;