blob: a10474246d40babf3a41b8d24a1911df54ae847b [file] [log] [blame]
Alexandre Julliarde2991ea1995-07-29 13:09:43 +00001/*
2 * Emulation of processor ioports.
3 *
4 * Copyright 1995 Morten Welinder
Alexandre Julliard638f1691999-01-17 16:32:32 +00005 * Copyright 1998 Andreas Mohr, Ove Kaaven
Alexandre Julliard0799c1a2002-03-09 23:29:33 +00006 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Alexandre Julliarde2991ea1995-07-29 13:09:43 +000020 */
21
22/* Known problems:
23 - only a few ports are emulated.
24 - real-time clock in "cmos" is bogus. A nifty alarm() setup could
25 fix that, I guess.
26*/
27
Patrik Stridvall17fd4e32001-06-28 18:04:41 +000028#include "config.h"
Patrik Stridvall9aab47e2002-08-28 23:42:34 +000029#include "wine/port.h"
Patrik Stridvall17fd4e32001-06-28 18:04:41 +000030
Alexandre Julliard23946ad1997-06-16 17:43:53 +000031#include <ctype.h>
Alexandre Julliard58199531994-04-21 01:20:00 +000032#include <stdlib.h>
Alexandre Julliard383da682000-02-10 22:15:21 +000033#include <stdio.h>
Alexandre Julliard641ee761997-08-04 16:34:36 +000034#include <string.h>
Alexandre Julliard8d24ae61994-04-05 21:42:43 +000035#include <time.h>
Patrik Stridvalld016f812002-08-17 00:43:16 +000036#ifdef HAVE_UNISTD_H
37# include <unistd.h>
38#endif
Jim Aston2e1cafa1999-03-14 16:35:05 +000039#include "windef.h"
Alexandre Julliard864ca062003-08-20 18:22:31 +000040#include "winbase.h"
Dmitry Timoshkovd75aed22002-08-27 01:13:58 +000041#include "winnls.h"
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +000042#include "winternl.h"
Ove Kaavene5557b32000-12-26 00:22:45 +000043#include "callback.h"
Michael Veksler3496b431999-04-01 10:01:20 +000044#include "miscemu.h"
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +000045#include "wine/unicode.h"
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000046#include "wine/debug.h"
Alexandre Julliard8d24ae61994-04-05 21:42:43 +000047
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000048WINE_DEFAULT_DEBUG_CHANNEL(int);
Patrik Stridvallb4b9fae1999-04-19 14:56:29 +000049
James Abbatiello9ad9e652000-08-22 20:38:47 +000050static struct {
51 WORD countmax;
52 BOOL16 byte_toggle; /* if TRUE, then hi byte has already been written */
53 WORD latch;
54 BOOL16 latched;
55 BYTE ctrlbyte_ch;
56 WORD oldval;
57} tmr_8253[3] = {
Ove Kaavene2c477b2001-11-08 17:06:40 +000058 {0xFFFF, FALSE, 0, FALSE, 0x36, 0},
59 {0x0012, FALSE, 0, FALSE, 0x74, 0},
60 {0x0001, FALSE, 0, FALSE, 0xB6, 0},
James Abbatiello9ad9e652000-08-22 20:38:47 +000061};
Andreas Mohr935ccab1999-01-30 13:37:54 +000062
Alexandre Julliard638f1691999-01-17 16:32:32 +000063static int dummy_ctr = 0;
64
65static BYTE parport_8255[4] = {0x4f, 0x20, 0xff, 0xff};
66
Alexandre Julliard8d24ae61994-04-05 21:42:43 +000067static BYTE cmosaddress;
68
Alexandre Julliarde2991ea1995-07-29 13:09:43 +000069static BYTE cmosimage[64] =
Alexandre Julliard8d24ae61994-04-05 21:42:43 +000070{
Alexandre Julliarde2991ea1995-07-29 13:09:43 +000071 0x27, 0x34, 0x31, 0x47, 0x16, 0x15, 0x00, 0x01,
72 0x04, 0x94, 0x26, 0x02, 0x50, 0x80, 0x00, 0x00,
73 0x40, 0xb1, 0x00, 0x9c, 0x01, 0x80, 0x02, 0x00,
74 0x1c, 0x00, 0x00, 0xad, 0x02, 0x10, 0x00, 0x00,
75 0x08, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00,
Andreas Mohreeaafcc1999-01-03 12:30:43 +000076 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0x19,
Alexandre Julliarde2991ea1995-07-29 13:09:43 +000077 0x00, 0x1c, 0x19, 0x81, 0x00, 0x0e, 0x00, 0x80,
78 0x1b, 0x7b, 0x21, 0x00, 0x00, 0x00, 0x05, 0x5f
79};
Alexandre Julliard8d24ae61994-04-05 21:42:43 +000080
Alexandre Julliard641ee761997-08-04 16:34:36 +000081#if defined(linux) && defined(__i386__)
82# define DIRECT_IO_ACCESS
83#else
84# undef DIRECT_IO_ACCESS
Uwe Bonnes6509fa92001-06-26 21:06:07 +000085# undef PP_IO_ACCESS
Alexandre Julliard641ee761997-08-04 16:34:36 +000086#endif /* linux && __i386__ */
87
Marcus Meissner03c1f4a2002-08-13 03:18:40 +000088#ifdef HAVE_PPDEV
89static int do_pp_port_access = -1; /* -1: uninitialized, 1: not available
90 0: available);*/
91#endif
92
Alexandre Julliard641ee761997-08-04 16:34:36 +000093#ifdef DIRECT_IO_ACCESS
Alexandre Julliard17216f51997-10-12 16:30:17 +000094
95extern int iopl(int level);
Alexandre Julliardde304902000-06-03 04:50:59 +000096static char do_direct_port_access = -1;
Alexandre Julliard641ee761997-08-04 16:34:36 +000097static char port_permissions[0x10000];
98
99#define IO_READ 1
100#define IO_WRITE 2
Alexandre Julliard17216f51997-10-12 16:30:17 +0000101
Alexandre Julliard638f1691999-01-17 16:32:32 +0000102static void IO_FixCMOSCheckSum(void)
Andreas Mohreeaafcc1999-01-03 12:30:43 +0000103{
104 WORD sum = 0;
105 int i;
106
107 for (i=0x10; i < 0x2d; i++)
108 sum += cmosimage[i];
109 cmosimage[0x2e] = sum >> 8; /* yes, this IS hi byte !! */
110 cmosimage[0x2f] = sum & 0xff;
Alexandre Julliard61fece01999-06-26 19:09:08 +0000111 TRACE("calculated hi %02x, lo %02x\n", cmosimage[0x2e], cmosimage[0x2f]);
Andreas Mohreeaafcc1999-01-03 12:30:43 +0000112}
113
Patrik Stridvallc94e0862000-06-07 02:16:47 +0000114#endif /* DIRECT_IO_ACCESS */
115
Alexandre Julliard638f1691999-01-17 16:32:32 +0000116static void set_timer_maxval(unsigned timer, unsigned maxval)
117{
118 switch (timer) {
119 case 0: /* System timer counter divisor */
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000120 if (Dosvm.SetTimer) Dosvm.SetTimer(maxval);
Alexandre Julliard638f1691999-01-17 16:32:32 +0000121 break;
122 case 1: /* RAM refresh */
Francois Gougete76218d2001-05-09 17:31:31 +0000123 FIXME("RAM refresh counter handling not implemented !\n");
Alexandre Julliard638f1691999-01-17 16:32:32 +0000124 break;
125 case 2: /* cassette & speaker */
126 /* speaker on ? */
127 if (((BYTE)parport_8255[1] & 3) == 3)
128 {
Alexandre Julliard61fece01999-06-26 19:09:08 +0000129 TRACE("Beep (freq: %d) !\n", 1193180 / maxval );
Alexandre Julliard638f1691999-01-17 16:32:32 +0000130 Beep(1193180 / maxval, 20);
131 }
132 break;
133 }
134}
135
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000136/**********************************************************************
137 * IO_port_init
138 */
139
140/* set_IO_permissions(int val1, int val)
141 * Helper function for IO_port_init
142 */
Alexandre Julliard641ee761997-08-04 16:34:36 +0000143#ifdef DIRECT_IO_ACCESS
144static void set_IO_permissions(int val1, int val, char rw)
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000145{
146 int j;
147 if (val1 != -1) {
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000148 if (val == -1) val = 0x3ff;
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000149 for (j = val1; j <= val; j++)
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000150 port_permissions[j] |= rw;
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000151
152 do_direct_port_access = 1;
153
154 val1 = -1;
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000155 } else if (val != -1) {
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000156 do_direct_port_access = 1;
157
158 port_permissions[val] |= rw;
159 }
160
161}
162
163/* do_IO_port_init_read_or_write(char* temp, char rw)
164 * Helper function for IO_port_init
165 */
166
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +0000167static void do_IO_port_init_read_or_write(const WCHAR *str, char rw)
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000168{
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +0000169 int val, val1, i;
170 WCHAR *end;
171 static const WCHAR allW[] = {'a','l','l',0};
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000172
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +0000173 if (!strcmpiW(str, allW))
174 {
175 for (i=0; i < sizeof(port_permissions); i++)
176 port_permissions[i] |= rw;
177 }
178 else
179 {
180 val = -1;
181 val1 = -1;
182 while (*str)
183 {
184 switch(*str)
185 {
186 case ',':
187 case ' ':
188 case '\t':
189 set_IO_permissions(val1, val, rw);
190 val1 = -1;
191 val = -1;
192 str++;
193 break;
194 case '-':
195 val1 = val;
196 if (val1 == -1) val1 = 0;
197 str++;
198 break;
199 default:
200 if (isdigitW(*str))
201 {
202 val = strtoulW( str, &end, 0 );
203 if (end == str)
204 {
205 val = -1;
206 str++;
207 }
208 else str = end;
209 }
210 break;
211 }
212 }
213 set_IO_permissions(val1, val, rw);
214 }
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000215}
216
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000217static inline BYTE inb( WORD port )
Alexandre Julliard641ee761997-08-04 16:34:36 +0000218{
219 BYTE b;
220 __asm__ __volatile__( "inb %w1,%0" : "=a" (b) : "d" (port) );
221 return b;
222}
223
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000224static inline WORD inw( WORD port )
Alexandre Julliard641ee761997-08-04 16:34:36 +0000225{
226 WORD w;
227 __asm__ __volatile__( "inw %w1,%0" : "=a" (w) : "d" (port) );
228 return w;
229}
230
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000231static inline DWORD inl( WORD port )
Alexandre Julliard641ee761997-08-04 16:34:36 +0000232{
233 DWORD dw;
234 __asm__ __volatile__( "inl %w1,%0" : "=a" (dw) : "d" (port) );
235 return dw;
236}
237
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000238static inline void outb( BYTE value, WORD port )
Alexandre Julliard641ee761997-08-04 16:34:36 +0000239{
240 __asm__ __volatile__( "outb %b0,%w1" : : "a" (value), "d" (port) );
241}
242
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000243static inline void outw( WORD value, WORD port )
Alexandre Julliard641ee761997-08-04 16:34:36 +0000244{
245 __asm__ __volatile__( "outw %w0,%w1" : : "a" (value), "d" (port) );
246}
247
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000248static inline void outl( DWORD value, WORD port )
Alexandre Julliard641ee761997-08-04 16:34:36 +0000249{
250 __asm__ __volatile__( "outl %0,%w1" : : "a" (value), "d" (port) );
251}
252
Alexandre Julliardde304902000-06-03 04:50:59 +0000253static void IO_port_init(void)
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000254{
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +0000255 char tmp[1024];
256 HKEY hkey;
257 DWORD dummy;
258 OBJECT_ATTRIBUTES attr;
259 UNICODE_STRING nameW;
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000260
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +0000261 static const WCHAR portsW[] = {'M','a','c','h','i','n','e','\\',
262 'S','o','f','t','w','a','r','e','\\',
263 'W','i','n','e','\\','W','i','n','e','\\',
264 'C','o','n','f','i','g','\\','P','o','r','t','s',0};
265 static const WCHAR readW[] = {'r','e','a','d',0};
266 static const WCHAR writeW[] = {'w','r','i','t','e',0};
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000267
Alexandre Julliarde0deb0c2003-08-19 03:21:04 +0000268 do_direct_port_access = 0;
269 /* Can we do that? */
270 if (!iopl(3))
271 {
272 iopl(0);
273
274 attr.Length = sizeof(attr);
275 attr.RootDirectory = 0;
276 attr.ObjectName = &nameW;
277 attr.Attributes = 0;
278 attr.SecurityDescriptor = NULL;
279 attr.SecurityQualityOfService = NULL;
280 RtlInitUnicodeString( &nameW, portsW );
281
282 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
283 {
284 RtlInitUnicodeString( &nameW, readW );
285 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
286 {
287 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
288 do_IO_port_init_read_or_write(str, IO_READ);
289 }
290 RtlInitUnicodeString( &nameW, writeW );
291 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
292 {
293 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
294 do_IO_port_init_read_or_write(str, IO_WRITE);
295 }
296 NtClose( hkey );
297 }
298 }
Andreas Mohreeaafcc1999-01-03 12:30:43 +0000299 IO_FixCMOSCheckSum();
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000300}
Alexandre Julliard8d24ae61994-04-05 21:42:43 +0000301
Patrik Stridvallc94e0862000-06-07 02:16:47 +0000302#endif /* DIRECT_IO_ACCESS */
Alexandre Julliard641ee761997-08-04 16:34:36 +0000303
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000304/**********************************************************************
305 * IO_inport
Alexandre Julliard638f1691999-01-17 16:32:32 +0000306 *
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000307 * Note: The size argument has to be handled correctly _externally_
Alexandre Julliard638f1691999-01-17 16:32:32 +0000308 * (as we always return a DWORD)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000309 */
Alexandre Julliard638f1691999-01-17 16:32:32 +0000310DWORD IO_inport( int port, int size )
Alexandre Julliard8d24ae61994-04-05 21:42:43 +0000311{
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000312 DWORD res = 0;
Alexandre Julliard8d24ae61994-04-05 21:42:43 +0000313
Alexandre Julliard61fece01999-06-26 19:09:08 +0000314 TRACE("%d-byte value from port 0x%02x\n", size, port );
Andreas Mohr935ccab1999-01-30 13:37:54 +0000315
Uwe Bonnes6509fa92001-06-26 21:06:07 +0000316#ifdef HAVE_PPDEV
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000317 if (do_pp_port_access == -1)
Uwe Bonnes6509fa92001-06-26 21:06:07 +0000318 do_pp_port_access =IO_pp_init();
319 if ((do_pp_port_access == 0 ) && (size == 1))
320 if (!IO_pp_inp(port,&res))
321 return res;
322#endif
Alexandre Julliardde304902000-06-03 04:50:59 +0000323#ifdef DIRECT_IO_ACCESS
324 if (do_direct_port_access == -1) IO_port_init();
Alexandre Julliard638f1691999-01-17 16:32:32 +0000325 if ((do_direct_port_access)
326 /* Make sure we have access to the port */
327 && (port_permissions[port] & IO_READ))
Alexandre Julliard641ee761997-08-04 16:34:36 +0000328 {
Alexandre Julliard638f1691999-01-17 16:32:32 +0000329 iopl(3);
330 switch(size)
Alexandre Julliard641ee761997-08-04 16:34:36 +0000331 {
Alexandre Julliard638f1691999-01-17 16:32:32 +0000332 case 1: res = inb( port ); break;
333 case 2: res = inw( port ); break;
334 case 4: res = inl( port ); break;
335 default:
Alexandre Julliard61fece01999-06-26 19:09:08 +0000336 ERR("invalid data size %d\n", size);
Alexandre Julliard641ee761997-08-04 16:34:36 +0000337 }
Alexandre Julliard638f1691999-01-17 16:32:32 +0000338 iopl(0);
339 return res;
Alexandre Julliard641ee761997-08-04 16:34:36 +0000340 }
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000341#endif
342
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000343 /* first give the DOS VM a chance to handle it */
Jukka Heinonen47592a42003-05-11 03:30:02 +0000344 if (Dosvm.inport || DPMI_LoadDosSystem())
345 if (Dosvm.inport( port, size, &res ))
346 return res;
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000347
Alexandre Julliard638f1691999-01-17 16:32:32 +0000348 switch (port)
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000349 {
Alexandre Julliard638f1691999-01-17 16:32:32 +0000350 case 0x40:
351 case 0x41:
352 case 0x42:
353 {
354 BYTE chan = port & 3;
355 WORD tempval = 0;
James Abbatiello9ad9e652000-08-22 20:38:47 +0000356 if (tmr_8253[chan].latched)
357 tempval = tmr_8253[chan].latch;
358 else
359 {
360 dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
361 if (chan == 0) /* System timer counter divisor */
362 {
Ove Kaavene5557b32000-12-26 00:22:45 +0000363 /* FIXME: Dosvm.GetTimer() returns quite rigid values */
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000364 if (Dosvm.GetTimer)
Uwe Bonnes23fbf4a2001-08-15 23:19:45 +0000365 tempval = dummy_ctr + (WORD)Dosvm.GetTimer();
366 else
367 tempval = dummy_ctr;
James Abbatiello9ad9e652000-08-22 20:38:47 +0000368 }
369 else
370 {
371 /* FIXME: intelligent hardware timer emulation needed */
372 tempval = dummy_ctr;
373 }
374 }
Alexandre Julliard638f1691999-01-17 16:32:32 +0000375
James Abbatiello9ad9e652000-08-22 20:38:47 +0000376 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
Andreas Mohr935ccab1999-01-30 13:37:54 +0000377 {
James Abbatiello9ad9e652000-08-22 20:38:47 +0000378 case 0:
379 res = 0; /* shouldn't happen? */
380 break;
381 case 1: /* read lo byte */
382 res = (BYTE)tempval;
383 tmr_8253[chan].latched = FALSE;
384 break;
385 case 3: /* read lo byte, then hi byte */
386 tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
387 if (tmr_8253[chan].byte_toggle)
Alexandre Julliard638f1691999-01-17 16:32:32 +0000388 {
389 res = (BYTE)tempval;
390 break;
391 }
392 /* else [fall through if read hi byte !] */
James Abbatiello9ad9e652000-08-22 20:38:47 +0000393 case 2: /* read hi byte */
394 res = (BYTE)(tempval >> 8);
395 tmr_8253[chan].latched = FALSE;
396 break;
Alexandre Julliard641ee761997-08-04 16:34:36 +0000397 }
Alexandre Julliard638f1691999-01-17 16:32:32 +0000398 }
James Abbatiello9ad9e652000-08-22 20:38:47 +0000399 break;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000400 case 0x60:
Ove Kaaven866b3371999-03-25 10:51:38 +0000401#if 0 /* what's this port got to do with parport ? */
Alexandre Julliard638f1691999-01-17 16:32:32 +0000402 res = (DWORD)parport_8255[0];
Ove Kaaven866b3371999-03-25 10:51:38 +0000403#endif
Alexandre Julliard638f1691999-01-17 16:32:32 +0000404 break;
405 case 0x61:
406 res = (DWORD)parport_8255[1];
407 break;
408 case 0x62:
409 res = (DWORD)parport_8255[2];
410 break;
411 case 0x70:
412 res = (DWORD)cmosaddress;
413 break;
414 case 0x71:
415 res = (DWORD)cmosimage[cmosaddress & 0x3f];
416 break;
417 case 0x200:
418 case 0x201:
419 res = 0xffffffff; /* no joystick */
420 break;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000421 default:
Alexandre Julliard61fece01999-06-26 19:09:08 +0000422 WARN("Direct I/O read attempted from port %x\n", port);
Alexandre Julliard638f1691999-01-17 16:32:32 +0000423 res = 0xffffffff;
424 break;
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000425 }
Alexandre Julliard61fece01999-06-26 19:09:08 +0000426 TRACE(" returning ( 0x%lx )\n", res );
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000427 return res;
Alexandre Julliard8d24ae61994-04-05 21:42:43 +0000428}
429
Alexandre Julliard234bc241994-12-10 13:02:28 +0000430
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000431/**********************************************************************
432 * IO_outport
433 */
Alexandre Julliard638f1691999-01-17 16:32:32 +0000434void IO_outport( int port, int size, DWORD value )
Alexandre Julliard234bc241994-12-10 13:02:28 +0000435{
Alexandre Julliard61fece01999-06-26 19:09:08 +0000436 TRACE("IO: 0x%lx (%d-byte value) to port 0x%02x\n",
Alexandre Julliard638f1691999-01-17 16:32:32 +0000437 value, size, port );
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000438
Uwe Bonnes6509fa92001-06-26 21:06:07 +0000439#ifdef HAVE_PPDEV
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000440 if (do_pp_port_access == -1)
Uwe Bonnes6509fa92001-06-26 21:06:07 +0000441 do_pp_port_access = IO_pp_init();
442 if ((do_pp_port_access == 0) && (size == 1))
443 if (!IO_pp_outp(port,&value))
444 return;
445#endif
Alexandre Julliard641ee761997-08-04 16:34:36 +0000446#ifdef DIRECT_IO_ACCESS
Uwe Bonnes6509fa92001-06-26 21:06:07 +0000447
Alexandre Julliardde304902000-06-03 04:50:59 +0000448 if (do_direct_port_access == -1) IO_port_init();
Alexandre Julliard638f1691999-01-17 16:32:32 +0000449 if ((do_direct_port_access)
450 /* Make sure we have access to the port */
451 && (port_permissions[port] & IO_WRITE))
Alexandre Julliard641ee761997-08-04 16:34:36 +0000452 {
Alexandre Julliard638f1691999-01-17 16:32:32 +0000453 iopl(3);
454 switch(size)
Alexandre Julliard641ee761997-08-04 16:34:36 +0000455 {
Alexandre Julliard638f1691999-01-17 16:32:32 +0000456 case 1: outb( LOBYTE(value), port ); break;
457 case 2: outw( LOWORD(value), port ); break;
458 case 4: outl( value, port ); break;
459 default:
Alexandre Julliard61fece01999-06-26 19:09:08 +0000460 WARN("Invalid data size %d\n", size);
Alexandre Julliard641ee761997-08-04 16:34:36 +0000461 }
Alexandre Julliard638f1691999-01-17 16:32:32 +0000462 iopl(0);
463 return;
Alexandre Julliard641ee761997-08-04 16:34:36 +0000464 }
Alexandre Julliard23946ad1997-06-16 17:43:53 +0000465#endif
466
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000467 /* first give the DOS VM a chance to handle it */
Jukka Heinonen47592a42003-05-11 03:30:02 +0000468 if (Dosvm.outport || DPMI_LoadDosSystem())
469 if (Dosvm.outport( port, size, value ))
470 return;
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000471
Alexandre Julliard638f1691999-01-17 16:32:32 +0000472 switch (port)
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000473 {
Alexandre Julliard638f1691999-01-17 16:32:32 +0000474 case 0x40:
475 case 0x41:
476 case 0x42:
477 {
478 BYTE chan = port & 3;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000479
James Abbatiello9ad9e652000-08-22 20:38:47 +0000480 /* we need to get the oldval before any lo/hi byte change has been made */
481 if (((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
482 !tmr_8253[chan].byte_toggle)
483 tmr_8253[chan].oldval = tmr_8253[chan].countmax;
484 switch ((tmr_8253[chan].ctrlbyte_ch & 0x30) >> 4)
Alexandre Julliard641ee761997-08-04 16:34:36 +0000485 {
James Abbatiello9ad9e652000-08-22 20:38:47 +0000486 case 0:
487 break; /* shouldn't happen? */
488 case 1: /* write lo byte */
489 tmr_8253[chan].countmax =
490 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
Alexandre Julliard641ee761997-08-04 16:34:36 +0000491 break;
James Abbatiello9ad9e652000-08-22 20:38:47 +0000492 case 3: /* write lo byte, then hi byte */
493 tmr_8253[chan].byte_toggle ^= TRUE; /* toggle */
494 if (tmr_8253[chan].byte_toggle)
Alexandre Julliard638f1691999-01-17 16:32:32 +0000495 {
James Abbatiello9ad9e652000-08-22 20:38:47 +0000496 tmr_8253[chan].countmax =
497 (tmr_8253[chan].countmax & 0xff00) | (BYTE)value;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000498 break;
499 }
500 /* else [fall through if write hi byte !] */
James Abbatiello9ad9e652000-08-22 20:38:47 +0000501 case 2: /* write hi byte */
502 tmr_8253[chan].countmax =
503 (tmr_8253[chan].countmax & 0x00ff) | ((BYTE)value << 8);
Alexandre Julliard641ee761997-08-04 16:34:36 +0000504 break;
505 }
James Abbatiello9ad9e652000-08-22 20:38:47 +0000506 /* if programming is finished and value has changed
507 then update to new value */
508 if ((((tmr_8253[chan].ctrlbyte_ch & 0x30) != 0x30) ||
509 !tmr_8253[chan].byte_toggle) &&
510 (tmr_8253[chan].countmax != tmr_8253[chan].oldval))
511 set_timer_maxval(chan, tmr_8253[chan].countmax);
Alexandre Julliard638f1691999-01-17 16:32:32 +0000512 }
Vincent BĂ©ron9a624912002-05-31 23:06:46 +0000513 break;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000514 case 0x43:
James Abbatiello9ad9e652000-08-22 20:38:47 +0000515 {
516 BYTE chan = ((BYTE)value & 0xc0) >> 6;
517 /* ctrl byte for specific timer channel */
518 if (chan == 3)
519 {
520 FIXME("8254 timer readback not implemented yet\n");
521 break;
522 }
523 switch (((BYTE)value & 0x30) >> 4)
524 {
525 case 0: /* latch timer */
526 tmr_8253[chan].latched = TRUE;
527 dummy_ctr -= 1 + (int)(10.0 * rand() / (RAND_MAX + 1.0));
528 if (chan == 0) /* System timer divisor */
Alexandre Julliard8cd55d02001-12-04 19:54:44 +0000529 if (Dosvm.GetTimer)
Uwe Bonnes23fbf4a2001-08-15 23:19:45 +0000530 tmr_8253[chan].latch = dummy_ctr + (WORD)Dosvm.GetTimer();
531 else
532 tmr_8253[chan].latch = dummy_ctr;
James Abbatiello9ad9e652000-08-22 20:38:47 +0000533 else
534 {
535 /* FIXME: intelligent hardware timer emulation needed */
536 tmr_8253[chan].latch = dummy_ctr;
537 }
538 break;
539 case 3: /* write lo byte, then hi byte */
540 tmr_8253[chan].byte_toggle = FALSE; /* init */
541 /* fall through */
542 case 1: /* write lo byte only */
543 case 2: /* write hi byte only */
544 tmr_8253[chan].ctrlbyte_ch = (BYTE)value;
545 break;
546 }
547 }
548 break;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000549 case 0x61:
550 parport_8255[1] = (BYTE)value;
James Abbatiello9ad9e652000-08-22 20:38:47 +0000551 if ((((BYTE)parport_8255[1] & 3) == 3) && (tmr_8253[2].countmax != 1))
Alexandre Julliard638f1691999-01-17 16:32:32 +0000552 {
James Abbatiello9ad9e652000-08-22 20:38:47 +0000553 TRACE("Beep (freq: %d) !\n", 1193180 / tmr_8253[2].countmax);
554 Beep(1193180 / tmr_8253[2].countmax, 20);
Alexandre Julliard638f1691999-01-17 16:32:32 +0000555 }
556 break;
557 case 0x70:
558 cmosaddress = (BYTE)value & 0x7f;
559 break;
560 case 0x71:
561 cmosimage[cmosaddress & 0x3f] = (BYTE)value;
562 break;
Alexandre Julliard638f1691999-01-17 16:32:32 +0000563 default:
Alexandre Julliard61fece01999-06-26 19:09:08 +0000564 WARN("Direct I/O write attempted to port %x\n", port );
Alexandre Julliard638f1691999-01-17 16:32:32 +0000565 break;
Alexandre Julliarde2991ea1995-07-29 13:09:43 +0000566 }
Alexandre Julliard234bc241994-12-10 13:02:28 +0000567}