msxml3: COM cleanup in domdoc.c.
[wine] / dlls / krnl386.exe16 / ioports.c
1 /*
2  * Emulation of processor ioports.
3  *
4  * Copyright 1995 Morten Welinder
5  * Copyright 1998 Andreas Mohr, Ove Kaaven
6  * Copyright 2001 Uwe Bonnes
7  *
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.
12  *
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.
17  *
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
21  */
22
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.
27 */
28
29 #include "config.h"
30
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
37
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
49
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"
60
61 WINE_DEFAULT_DEBUG_CHANNEL(int);
62
63 #ifdef linux
64 # define DIRECT_IO_ACCESS
65 #else
66 # undef DIRECT_IO_ACCESS
67 #endif
68
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},
79 };
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
90
91
92 static BYTE parport_8255[4] = {0x4f, 0x20, 0xff, 0xff};
93
94 static BYTE cmosaddress;
95
96 static int cmos_image_initialized = 0;
97
98 static BYTE cmosimage[64] =
99 {
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 */
164 };
165
166 static void IO_FixCMOSCheckSum(void)
167 {
168         WORD sum = 0;
169         int i;
170
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]);
176 }
177
178 #ifdef DIRECT_IO_ACCESS
179
180 extern int iopl(int level);
181 static char do_direct_port_access = -1;
182 static char port_permissions[0x10000];
183
184 #define IO_READ  1
185 #define IO_WRITE 2
186
187 #endif  /* DIRECT_IO_ACCESS */
188
189 #ifdef HAVE_PPDEV
190 static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
191                                        0: available);*/
192 #endif
193
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)
198
199
200 static void set_timer(unsigned timer)
201 {
202     DWORD val = tmr_8253[timer].countmax;
203
204     if (tmr_8253[timer].ctrlbyte_ch & 0x01)
205         val = BCD2BIN(val);
206
207     tmr_8253[timer].flags &= ~TMR_UPDATE;
208     if (!QueryPerformanceCounter((LARGE_INTEGER*)&tmr_8253[timer].start_time))
209         WARN("QueryPerformanceCounter should not fail!\n");
210
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)
221             {
222                 TRACE("Beep (freq: %d) !\n", 1193180 / val);
223                 Beep(1193180 / val, 20);
224             }
225             break;
226     }
227 }
228
229
230 static WORD get_timer_val(unsigned timer)
231 {
232     LARGE_INTEGER time;
233     WORD maxval, val = tmr_8253[timer].countmax;
234     BYTE mode = tmr_8253[timer].ctrlbyte_ch >> 1 & 0x07;
235
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 countig, so this seems to be a good
239      * solution and should work well with most (all?) programs
240      */
241     if (tmr_8253[timer].flags & TMR_UPDATE)
242         return 0;
243
244     if (!QueryPerformanceCounter(&time))
245         WARN("QueryPerformanceCounter should not fail!\n");
246
247     time.QuadPart -= tmr_8253[timer].start_time;
248     if (tmr_8253[timer].ctrlbyte_ch & 0x01)
249         val = BCD2BIN(val);
250
251     switch ( mode )
252     {
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;
266     }
267
268     val = (val - time.QuadPart) % (maxval + 1);
269     if (tmr_8253[timer].ctrlbyte_ch & 0x01)
270         val = BIN2BCD(val);
271
272     return val;
273 }
274
275
276 /**********************************************************************
277  *          IO_port_init
278  */
279
280 /* set_IO_permissions(int val1, int val)
281  * Helper function for IO_port_init
282  */
283 #ifdef DIRECT_IO_ACCESS
284 static void set_IO_permissions(int val1, int val, char rw)
285 {
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;
291
292                 do_direct_port_access = 1;
293
294                 val1 = -1;
295         } else if (val != -1) {
296                 do_direct_port_access = 1;
297
298                 port_permissions[val] |= rw;
299         }
300
301 }
302
303 /* do_IO_port_init_read_or_write(char* temp, char rw)
304  * Helper function for IO_port_init
305  */
306
307 static void do_IO_port_init_read_or_write(const WCHAR *str, char rw)
308 {
309     int val, val1;
310     unsigned int i;
311     WCHAR *end;
312     static const WCHAR allW[] = {'a','l','l',0};
313
314     if (!strcmpiW(str, allW))
315     {
316         for (i=0; i < sizeof(port_permissions); i++)
317             port_permissions[i] |= rw;
318     }
319     else
320     {
321         val = -1;
322         val1 = -1;
323         while (*str)
324         {
325             switch(*str)
326             {
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))
342                 {
343                     val = strtoulW( str, &end, 0 );
344                     if (end == str)
345                     {
346                         val = -1;
347                         str++;
348                     }
349                     else str = end;
350                 }
351                 break;
352             }
353         }
354         set_IO_permissions(val1, val, rw);
355     }
356 }
357
358 static inline BYTE inb( WORD port )
359 {
360     BYTE b;
361     __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
362     return b;
363 }
364
365 static inline WORD inw( WORD port )
366 {
367     WORD w;
368     __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
369     return w;
370 }
371
372 static inline DWORD inl( WORD port )
373 {
374     DWORD dw;
375     __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
376     return dw;
377 }
378
379 static inline void outb( BYTE value, WORD port )
380 {
381     __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
382 }
383
384 static inline void outw( WORD value, WORD port )
385 {
386     __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
387 }
388
389 static inline void outl( DWORD value, WORD port )
390 {
391     __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
392 }
393
394 static void IO_port_init(void)
395 {
396     char tmp[1024];
397     HANDLE root, hkey;
398     DWORD dummy;
399     OBJECT_ATTRIBUTES attr;
400     UNICODE_STRING nameW;
401
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};
406
407     do_direct_port_access = 0;
408     /* Can we do that? */
409     if (!iopl(3))
410     {
411         iopl(0);
412
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 );
421
422         /* @@ Wine registry key: HKCU\Software\Wine\VDM\Ports */
423         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
424         {
425             RtlInitUnicodeString( &nameW, readW );
426             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
427             {
428                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
429                 do_IO_port_init_read_or_write(str, IO_READ);
430             }
431             RtlInitUnicodeString( &nameW, writeW );
432             if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
433             {
434                 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
435                 do_IO_port_init_read_or_write(str, IO_WRITE);
436             }
437             NtClose( hkey );
438         }
439         NtClose( root );
440     }
441 }
442
443 #endif  /* DIRECT_IO_ACCESS */
444
445
446 #ifdef HAVE_PPDEV
447
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;
455
456 static PPDeviceStruct PPDeviceList[5];
457 static int PPDeviceNum=0;
458
459 static int IO_pp_sort(const void *p1,const  void *p2)
460 {
461     return ((const PPDeviceStruct*)p1)->userbase - ((const PPDeviceStruct*)p2)->userbase;
462 }
463
464 /* IO_pp_init
465  *
466  * Read the ppdev entries from registry, open the device and check
467  * for necessary IOCTRL
468  * Report verbose about possible errors
469  */
470 static char IO_pp_init(void)
471 {
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;
481
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};
484
485     TRACE("\n");
486
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 );
495
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;
500
501     for (;;)
502     {
503         DWORD total_size, len;
504         char temp[256];
505         KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)temp;
506
507         if (NtEnumerateValueKey( hkey, idx, KeyValueFullInformation,
508                                  temp, sizeof(temp), &total_size )) break;
509         if (info->Type != REG_SZ) break;
510
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;
516
517         idx++;
518         if(nports >4)
519           {
520             FIXME("Make the PPDeviceList larger than 5 elements\n");
521             break;
522           }
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)
530           {
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;
536           }
537         userbase = strtol(name, NULL, 16);
538         if ( errno == ERANGE)
539           {
540             WARN("Configuration: Invalid base %s for %s\n",name,buffer);
541             WARN("Rejecting configuration item\n");
542             continue;
543           }
544         if (ioctl (fd,PPCLAIM,0))
545           {
546             ERR("PPCLAIM rejected %s\n",buffer);
547             ERR("Perhaps the device is already in use or nonexistent\n");
548             continue;
549           }
550         if (nports > 0)
551           {
552             for (i=0; i<= nports; i++)
553               {
554                 if (PPDeviceList[i].userbase == userbase)
555                   {
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;
561                   }
562               }
563             if (!userbase) continue;
564           }
565         /* Check for the minimum required IOCTLS */
566         if ((ioctl(fd,PPRDATA,&res))||
567             (ioctl(fd,PPRSTATUS,&res))||
568             (ioctl(fd,PPRCONTROL,&res)))
569           {
570             ERR("PPUSER IOCTL not available for parport device %s\n",buffer);
571             continue;
572           }
573         if (ioctl (fd,PPRELEASE,0))
574           {
575             ERR("PPRELEASE rejected %s\n",buffer);
576             ERR("Perhaps the device is already in use or nonexistent\n");
577             continue;
578           }
579         PPDeviceList[nports].devicename = HeapAlloc(GetProcessHeap(), 0, sizeof(buffer)+1);
580         if (!PPDeviceList[nports].devicename)
581           {
582             ERR("No (more) space for devicename\n");
583             break;
584           }
585         strcpy(PPDeviceList[nports].devicename,buffer);
586         PPDeviceList[nports].fd = fd;
587         PPDeviceList[nports].userbase = userbase;
588         PPDeviceList[nports].lastaccess=GetTickCount();
589         if (timeout)
590           {
591             PPDeviceList[nports].timeout = strtol(timeout, NULL, 10);
592             if (errno == ERANGE)
593               {
594                 WARN("Configuration: Invalid timeout %s in configuration for %s, Setting to 0\n",
595                      timeout,buffer);
596                 PPDeviceList[nports].timeout = 0;
597               }
598           }
599         else
600           PPDeviceList[nports].timeout = 0;
601         nports++;
602     }
603     TRACE("found %d ports\n",nports);
604     NtClose( hkey );
605
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);
610
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
620     */
621     return ret;
622 }
623
624 /* IO_pp_do_access
625  *
626  * Do the actual IOCTL
627  * Return NULL on success
628  */
629 static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
630 {
631   int ret;
632   if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
633     {
634       ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
635           PPDeviceList[idx].devicename);
636       return 1;
637     }
638   ret = ioctl(PPDeviceList[idx].fd,ppctl,res);
639   if (ioctl(PPDeviceList[idx].fd,PPRELEASE,0))
640     {
641       ERR("Can't release device %s, PPUSER/PPDEV handling confused\n",
642           PPDeviceList[idx].devicename);
643       return 1;
644     }
645   return ret;
646
647 }
648
649 /* IO_pp_inp
650  *
651  * Check if we can satisfy the INP command with some of the configured PPDEV deviced
652  * Return NULL on success
653  */
654 static int IO_pp_inp(int port, DWORD* res)
655 {
656     int idx,j=0;
657
658     for (idx=0;idx<PPDeviceNum ;idx++)
659       {
660        j = port - PPDeviceList[idx].userbase;
661        if (j <0) return 1;
662        switch (j)
663          {
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;
680          }
681       }
682     return 1;
683 }
684
685 /* IO_pp_outp
686  *
687  * Check if we can satisfy the OUTP command with some of the configured PPDEV deviced
688  * Return NULL on success
689  */
690 static BOOL IO_pp_outp(int port, DWORD* res)
691 {
692     int idx,j=0;
693
694     for (idx=0;idx<PPDeviceNum ;idx++)
695       {
696        j = port - PPDeviceList[idx].userbase;
697        if (j <0) return 1;
698        switch (j)
699          {
700          case 0:
701            return IO_pp_do_access(idx,PPWDATA,res);
702          case 2:
703            {
704              /* We can't switch port direction via PPWCONTROL,
705                 so do it via PPDATADIR
706              */
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);
711            }
712
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;
724          }
725       }
726     return TRUE;
727 }
728
729 #endif  /* HAVE_PPDEV */
730
731
732 /**********************************************************************
733  *          DOSVM_inport
734  *
735  * Note: The size argument has to be handled correctly _externally_
736  * (as we always return a DWORD)
737  */
738 DWORD DOSVM_inport( int port, int size )
739 {
740     DWORD res = ~0U;
741
742     TRACE("%d-byte value from port 0x%04x\n", size, port );
743
744     DOSMEM_InitDosMemory();
745
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))
749     {
750         if (!IO_pp_inp(port,&res)) return res;
751     }
752 #endif
753
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))
759     {
760         iopl(3);
761         switch(size)
762         {
763         case 1: res = inb( port ); break;
764         case 2: res = inw( port ); break;
765         case 4: res = inl( port ); break;
766         }
767         iopl(0);
768         return res;
769     }
770 #endif
771
772     switch (port)
773     {
774     case 0x40:
775     case 0x41:
776     case 0x42:
777         {
778             BYTE chan = port & 3;
779             WORD tempval = tmr_8253[chan].flags & TMR_LATCHED
780                 ? tmr_8253[chan].latch : get_timer_val(chan);
781
782             if (tmr_8253[chan].flags & TMR_STATUS)
783             {
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)
789                  */
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;
794             }
795             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
796             {
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)
807                 {
808                     res = (BYTE)tempval;
809                     break;
810                 }
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;
816             }
817         }
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)
833         {
834             IO_FixCMOSCheckSum();
835             cmos_image_initialized = 1;
836         }
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;
927     }
928     return res;
929 }
930
931
932 /**********************************************************************
933  *          DOSVM_outport
934  */
935 void DOSVM_outport( int port, int size, DWORD value )
936 {
937     TRACE("IO: 0x%x (%d-byte value) to port 0x%04x\n", value, size, port );
938
939     DOSMEM_InitDosMemory();
940
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))
944     {
945         if (!IO_pp_outp(port,&value)) return;
946     }
947 #endif
948
949 #ifdef DIRECT_IO_ACCESS
950
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))
955     {
956         iopl(3);
957         switch(size)
958         {
959         case 1: outb( LOBYTE(value), port ); break;
960         case 2: outw( LOWORD(value), port ); break;
961         case 4: outl( value, port ); break;
962         }
963         iopl(0);
964         return;
965     }
966 #endif
967
968     switch (port)
969     {
970     case 0x20:
971         DOSVM_PIC_ioport_out( port, (BYTE)value );
972         break;
973     case 0x40:
974     case 0x41:
975     case 0x42:
976         {
977             BYTE chan = port & 3;
978
979             tmr_8253[chan].flags |= TMR_UPDATE;
980             switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
981             {
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)
991                 {
992                     tmr_8253[chan].countmax =
993                         (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
994                     break;
995                 }
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;
1001             }
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);
1006         }
1007         break;
1008     case 0x43:
1009        {
1010            BYTE chan = ((BYTE)value & 0xc0) >> 6;
1011            /* ctrl byte for specific timer channel */
1012            if (chan == 3)
1013            {
1014                if ( !(value & 0x20) )
1015                {
1016                    if (value & 0x02 && !(tmr_8253[0].flags & TMR_LATCHED))
1017                    {
1018                        tmr_8253[0].flags |= TMR_LATCHED;
1019                        tmr_8253[0].latch = get_timer_val(0);
1020                    }
1021                    if (value & 0x04 && !(tmr_8253[1].flags & TMR_LATCHED))
1022                    {
1023                        tmr_8253[1].flags |= TMR_LATCHED;
1024                        tmr_8253[1].latch = get_timer_val(1);
1025                    }
1026                    if (value & 0x08 && !(tmr_8253[2].flags & TMR_LATCHED))
1027                    {
1028                        tmr_8253[2].flags |= TMR_LATCHED;
1029                        tmr_8253[2].latch = get_timer_val(2);
1030                    }
1031                }
1032
1033                if ( !(value & 0x10) )
1034                {
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;
1041                }
1042                break;
1043            }
1044            switch (((BYTE)value & 0x30) >> 4)
1045            {
1046            case 0:      /* latch timer */
1047                if ( !(tmr_8253[chan].flags & TMR_LATCHED) )
1048                {
1049                    tmr_8253[chan].flags |= TMR_LATCHED;
1050                    tmr_8253[chan].latch = get_timer_val(chan);
1051                }
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;
1060            }
1061        }
1062        break;
1063     case 0x61:
1064         parport_8255[1] = (BYTE)value;
1065         if (((parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
1066         {
1067             TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
1068             Beep(1193180 / tmr_8253[2].countmax, 20);
1069         }
1070         break;
1071     case 0x70:
1072         cmosaddress = (BYTE)value & 0x7f;
1073         break;
1074     case 0x71:
1075         if (!cmos_image_initialized)
1076         {
1077             IO_FixCMOSCheckSum();
1078             cmos_image_initialized = 1;
1079         }
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)) );
1128             }
1129         }
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;
1182     }
1183 }