* Sync up to trunk head (r64829).
[reactos.git] / subsystems / ntvdm / io.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: io.c
5 * PURPOSE: I/O Port Handlers
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "emulator.h"
15 #include "io.h"
16
17 /* PRIVATE VARIABLES **********************************************************/
18
19 typedef struct _EMULATOR_IO_HANDLERS
20 {
21 EMULATOR_INB_PROC InB;
22 EMULATOR_INW_PROC InW;
23 EMULATOR_IND_PROC InD;
24
25 EMULATOR_INSB_PROC InsB;
26 EMULATOR_INSW_PROC InsW;
27 EMULATOR_INSD_PROC InsD;
28
29 EMULATOR_OUTB_PROC OutB;
30 EMULATOR_OUTW_PROC OutW;
31 EMULATOR_OUTD_PROC OutD;
32
33 EMULATOR_OUTSB_PROC OutsB;
34 EMULATOR_OUTSW_PROC OutsW;
35 EMULATOR_OUTSD_PROC OutsD;
36 } EMULATOR_IO_HANDLERS, *PEMULATOR_IO_HANDLERS;
37
38 typedef struct _EMULATOR_IOPORT_HANDLERS
39 {
40 HANDLE hVdd; // == 0 if unused,
41 // INVALID_HANDLE_VALUE if handled internally,
42 // a valid VDD handle if handled externally.
43 union
44 {
45 /* For Windows compatibility only, not used internally... */
46 VDD_IO_HANDLERS VddIoHandlers;
47
48 /* ... we use these members internally */
49 EMULATOR_IO_HANDLERS IoHandlers;
50 };
51 } EMULATOR_IOPORT_HANDLERS, *PEMULATOR_IOPORT_HANDLERS;
52
53 /*
54 * This is the list of registered I/O Port handlers.
55 */
56 EMULATOR_IOPORT_HANDLERS IoPortProc[EMULATOR_MAX_IOPORTS_NUM] = {{NULL}};
57
58 /* PUBLIC FUNCTIONS ***********************************************************/
59
60 UCHAR
61 IOReadB(ULONG Port)
62 {
63 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
64 IoPortProc[Port].IoHandlers.InB)
65 {
66 return IoPortProc[Port].IoHandlers.InB(Port);
67 }
68 else if (IoPortProc[Port].hVdd > 0 &&
69 IoPortProc[Port].VddIoHandlers.inb_handler)
70 {
71 UCHAR Data;
72 ASSERT(Port <= MAXWORD);
73 IoPortProc[Port].VddIoHandlers.inb_handler((WORD)Port, &Data);
74 return Data;
75 }
76 else
77 {
78 /* Return an empty port byte value */
79 DPRINT("Read from unknown port: 0x%X\n", Port);
80 return 0xFF;
81 }
82 }
83
84 VOID
85 IOReadStrB(ULONG Port,
86 PUCHAR Buffer,
87 ULONG Count)
88 {
89 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
90 IoPortProc[Port].IoHandlers.InsB)
91 {
92 IoPortProc[Port].IoHandlers.InsB(Port, Buffer, Count);
93 }
94 else if (IoPortProc[Port].hVdd > 0 &&
95 IoPortProc[Port].VddIoHandlers.insb_handler)
96 {
97 ASSERT(Port <= MAXWORD);
98 ASSERT(Count <= MAXWORD);
99 IoPortProc[Port].VddIoHandlers.insb_handler((WORD)Port, Buffer, (WORD)Count);
100 }
101 else
102 {
103 while (Count--) *Buffer++ = IOReadB(Port);
104 }
105 }
106
107 VOID
108 IOWriteB(ULONG Port,
109 UCHAR Buffer)
110 {
111 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
112 IoPortProc[Port].IoHandlers.OutB)
113 {
114 IoPortProc[Port].IoHandlers.OutB(Port, Buffer);
115 }
116 else if (IoPortProc[Port].hVdd > 0 &&
117 IoPortProc[Port].VddIoHandlers.outb_handler)
118 {
119 ASSERT(Port <= MAXWORD);
120 IoPortProc[Port].VddIoHandlers.outb_handler((WORD)Port, Buffer);
121 }
122 else
123 {
124 /* Do nothing */
125 DPRINT("Write to unknown port: 0x%X\n", Port);
126 }
127 }
128
129 VOID
130 IOWriteStrB(ULONG Port,
131 PUCHAR Buffer,
132 ULONG Count)
133 {
134 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
135 IoPortProc[Port].IoHandlers.OutsB)
136 {
137 IoPortProc[Port].IoHandlers.OutsB(Port, Buffer, Count);
138 }
139 else if (IoPortProc[Port].hVdd > 0 &&
140 IoPortProc[Port].VddIoHandlers.outsb_handler)
141 {
142 ASSERT(Port <= MAXWORD);
143 ASSERT(Count <= MAXWORD);
144 IoPortProc[Port].VddIoHandlers.outsb_handler((WORD)Port, Buffer, (WORD)Count);
145 }
146 else
147 {
148 while (Count--) IOWriteB(Port, *Buffer++);
149 }
150 }
151
152 USHORT
153 IOReadW(ULONG Port)
154 {
155 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
156 IoPortProc[Port].IoHandlers.InW)
157 {
158 return IoPortProc[Port].IoHandlers.InW(Port);
159 }
160 else if (IoPortProc[Port].hVdd > 0 &&
161 IoPortProc[Port].VddIoHandlers.inw_handler)
162 {
163 USHORT Data;
164 ASSERT(Port <= MAXWORD);
165 IoPortProc[Port].VddIoHandlers.inw_handler((WORD)Port, &Data);
166 return Data;
167 }
168 else
169 {
170 UCHAR Low, High;
171
172 // FIXME: Is it ok on Little endian and Big endian ??
173 Low = IOReadB(Port);
174 High = IOReadB(Port + sizeof(UCHAR));
175 return MAKEWORD(Low, High);
176 }
177 }
178
179 VOID
180 IOReadStrW(ULONG Port,
181 PUSHORT Buffer,
182 ULONG Count)
183 {
184 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
185 IoPortProc[Port].IoHandlers.InsW)
186 {
187 IoPortProc[Port].IoHandlers.InsW(Port, Buffer, Count);
188 }
189 else if (IoPortProc[Port].hVdd > 0 &&
190 IoPortProc[Port].VddIoHandlers.insw_handler)
191 {
192 ASSERT(Port <= MAXWORD);
193 ASSERT(Count <= MAXWORD);
194 IoPortProc[Port].VddIoHandlers.insw_handler((WORD)Port, Buffer, (WORD)Count);
195 }
196 else
197 {
198 while (Count--) *Buffer++ = IOReadW(Port);
199 }
200 }
201
202 VOID
203 IOWriteW(ULONG Port,
204 USHORT Buffer)
205 {
206 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
207 IoPortProc[Port].IoHandlers.OutW)
208 {
209 IoPortProc[Port].IoHandlers.OutW(Port, Buffer);
210 }
211 else if (IoPortProc[Port].hVdd > 0 &&
212 IoPortProc[Port].VddIoHandlers.outw_handler)
213 {
214 ASSERT(Port <= MAXWORD);
215 IoPortProc[Port].VddIoHandlers.outw_handler((WORD)Port, Buffer);
216 }
217 else
218 {
219 // FIXME: Is it ok on Little endian and Big endian ??
220 IOWriteB(Port, LOBYTE(Buffer));
221 IOWriteB(Port + sizeof(UCHAR), HIBYTE(Buffer));
222 }
223 }
224
225 VOID
226 IOWriteStrW(ULONG Port,
227 PUSHORT Buffer,
228 ULONG Count)
229 {
230 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
231 IoPortProc[Port].IoHandlers.OutsW)
232 {
233 IoPortProc[Port].IoHandlers.OutsW(Port, Buffer, Count);
234 }
235 else if (IoPortProc[Port].hVdd > 0 &&
236 IoPortProc[Port].VddIoHandlers.outsw_handler)
237 {
238 ASSERT(Port <= MAXWORD);
239 ASSERT(Count <= MAXWORD);
240 IoPortProc[Port].VddIoHandlers.outsw_handler((WORD)Port, Buffer, (WORD)Count);
241 }
242 else
243 {
244 while (Count--) IOWriteW(Port, *Buffer++);
245 }
246 }
247
248 ULONG
249 IOReadD(ULONG Port)
250 {
251 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
252 IoPortProc[Port].IoHandlers.InD)
253 {
254 return IoPortProc[Port].IoHandlers.InD(Port);
255 }
256 else
257 {
258 USHORT Low, High;
259
260 // FIXME: Is it ok on Little endian and Big endian ??
261 Low = IOReadW(Port);
262 High = IOReadW(Port + sizeof(USHORT));
263 return MAKELONG(Low, High);
264 }
265 }
266
267 VOID
268 IOReadStrD(ULONG Port,
269 PULONG Buffer,
270 ULONG Count)
271 {
272 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
273 IoPortProc[Port].IoHandlers.InsD)
274 {
275 IoPortProc[Port].IoHandlers.InsD(Port, Buffer, Count);
276 }
277 else
278 {
279 while (Count--) *Buffer++ = IOReadD(Port);
280 }
281 }
282
283 VOID
284 IOWriteD(ULONG Port,
285 ULONG Buffer)
286 {
287 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
288 IoPortProc[Port].IoHandlers.OutD)
289 {
290 IoPortProc[Port].IoHandlers.OutD(Port, Buffer);
291 }
292 else
293 {
294 // FIXME: Is it ok on Little endian and Big endian ??
295 IOWriteW(Port, LOWORD(Buffer));
296 IOWriteW(Port + sizeof(USHORT), HIWORD(Buffer));
297 }
298 }
299
300 VOID
301 IOWriteStrD(ULONG Port,
302 PULONG Buffer,
303 ULONG Count)
304 {
305 if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
306 IoPortProc[Port].IoHandlers.OutsD)
307 {
308 IoPortProc[Port].IoHandlers.OutsD(Port, Buffer, Count);
309 }
310 else
311 {
312 while (Count--) IOWriteD(Port, *Buffer++);
313 }
314 }
315
316
317 VOID RegisterIoPort(ULONG Port,
318 EMULATOR_INB_PROC InHandler,
319 EMULATOR_OUTB_PROC OutHandler)
320 {
321 if (IoPortProc[Port].IoHandlers.InB == NULL)
322 IoPortProc[Port].IoHandlers.InB = InHandler;
323 else
324 DPRINT1("IoPortProc[0x%X].IoHandlers.InB already registered\n", Port);
325
326 if (IoPortProc[Port].IoHandlers.OutB == NULL)
327 IoPortProc[Port].IoHandlers.OutB = OutHandler;
328 else
329 DPRINT1("IoPortProc[0x%X].IoHandlers.OutB already registered\n", Port);
330
331 /* We hold the I/O port internally */
332 IoPortProc[Port].hVdd = INVALID_HANDLE_VALUE;
333 }
334
335 VOID UnregisterIoPort(ULONG Port)
336 {
337 /*
338 * Put automagically all the fields to zero:
339 * the hVdd gets unregistered as well as all the handlers.
340 */
341 // IoPortProc[Port] = {NULL};
342 RtlZeroMemory(&IoPortProc[Port], sizeof(IoPortProc[Port]));
343 }
344
345 VOID WINAPI
346 EmulatorReadIo(PFAST486_STATE State,
347 ULONG Port,
348 PVOID Buffer,
349 ULONG DataCount,
350 UCHAR DataSize)
351 {
352 UNREFERENCED_PARAMETER(State);
353
354 if (DataSize == 0 || DataCount == 0) return;
355
356 if (DataSize == sizeof(UCHAR))
357 {
358 if (DataCount == 1)
359 *(PUCHAR)Buffer = IOReadB(Port);
360 else
361 IOReadStrB(Port, Buffer, DataCount);
362 }
363 else if (DataSize == sizeof(USHORT))
364 {
365 if (DataCount == 1)
366 *(PUSHORT)Buffer = IOReadW(Port);
367 else
368 IOReadStrW(Port, Buffer, DataCount);
369 }
370 else if (DataSize == sizeof(ULONG))
371 {
372 if (DataCount == 1)
373 *(PULONG)Buffer = IOReadD(Port);
374 else
375 IOReadStrD(Port, Buffer, DataCount);
376 }
377 else
378 {
379 PUCHAR Address = (PUCHAR)Buffer;
380
381 while (DataCount--)
382 {
383 ULONG CurrentPort = Port;
384 ULONG Count;
385 UCHAR NewDataSize = DataSize;
386
387 /* Read dword */
388 Count = NewDataSize >> 2; // NewDataSize / sizeof(ULONG);
389 NewDataSize = NewDataSize & 3; // NewDataSize % sizeof(ULONG);
390 while (Count--)
391 {
392 *(PULONG)Address = IOReadD(CurrentPort);
393 CurrentPort += sizeof(ULONG);
394 Address += sizeof(ULONG);
395 }
396
397 /* Read word */
398 Count = NewDataSize >> 1; // NewDataSize / sizeof(USHORT);
399 NewDataSize = NewDataSize & 1; // NewDataSize % sizeof(USHORT);
400 while (Count--)
401 {
402 *(PUSHORT)Address = IOReadW(CurrentPort);
403 CurrentPort += sizeof(USHORT);
404 Address += sizeof(USHORT);
405 }
406
407 /* Read byte */
408 Count = NewDataSize; // NewDataSize / sizeof(UCHAR);
409 // NewDataSize = NewDataSize % sizeof(UCHAR);
410 while (Count--)
411 {
412 *(PUCHAR)Address = IOReadB(CurrentPort);
413 CurrentPort += sizeof(UCHAR);
414 Address += sizeof(UCHAR);
415 }
416 }
417 }
418 }
419
420 VOID WINAPI
421 EmulatorWriteIo(PFAST486_STATE State,
422 ULONG Port,
423 PVOID Buffer,
424 ULONG DataCount,
425 UCHAR DataSize)
426 {
427 UNREFERENCED_PARAMETER(State);
428
429 if (DataSize == 0 || DataCount == 0) return;
430
431 if (DataSize == sizeof(UCHAR))
432 {
433 if (DataCount == 1)
434 IOWriteB(Port, *(PUCHAR)Buffer);
435 else
436 IOWriteStrB(Port, Buffer, DataCount);
437 }
438 else if (DataSize == sizeof(USHORT))
439 {
440 if (DataCount == 1)
441 IOWriteW(Port, *(PUSHORT)Buffer);
442 else
443 IOWriteStrW(Port, Buffer, DataCount);
444 }
445 else if (DataSize == sizeof(ULONG))
446 {
447 if (DataCount == 1)
448 IOWriteD(Port, *(PULONG)Buffer);
449 else
450 IOWriteStrD(Port, Buffer, DataCount);
451 }
452 else
453 {
454 PUCHAR Address = (PUCHAR)Buffer;
455
456 while (DataCount--)
457 {
458 ULONG CurrentPort = Port;
459 ULONG Count;
460 UCHAR NewDataSize = DataSize;
461
462 /* Write dword */
463 Count = NewDataSize >> 2; // NewDataSize / sizeof(ULONG);
464 NewDataSize = NewDataSize & 3; // NewDataSize % sizeof(ULONG);
465 while (Count--)
466 {
467 IOWriteD(CurrentPort, *(PULONG)Address);
468 CurrentPort += sizeof(ULONG);
469 Address += sizeof(ULONG);
470 }
471
472 /* Write word */
473 Count = NewDataSize >> 1; // NewDataSize / sizeof(USHORT);
474 NewDataSize = NewDataSize & 1; // NewDataSize % sizeof(USHORT);
475 while (Count--)
476 {
477 IOWriteW(CurrentPort, *(PUSHORT)Address);
478 CurrentPort += sizeof(USHORT);
479 Address += sizeof(USHORT);
480 }
481
482 /* Write byte */
483 Count = NewDataSize; // NewDataSize / sizeof(UCHAR);
484 // NewDataSize = NewDataSize % sizeof(UCHAR);
485 while (Count--)
486 {
487 IOWriteB(CurrentPort, *(PUCHAR)Address);
488 CurrentPort += sizeof(UCHAR);
489 Address += sizeof(UCHAR);
490 }
491 }
492 }
493 }
494
495
496
497 BOOL
498 WINAPI
499 VDDInstallIOHook(HANDLE hVdd,
500 WORD cPortRange,
501 PVDD_IO_PORTRANGE pPortRange,
502 PVDD_IO_HANDLERS IOhandler)
503 {
504 /* Check possible validity of the VDD handle */
505 if (hVdd == 0 || hVdd == INVALID_HANDLE_VALUE) return FALSE;
506
507 /* Loop for each range of I/O ports */
508 while (cPortRange--)
509 {
510 WORD i;
511
512 /* Register the range of I/O ports */
513 for (i = pPortRange->First; i <= pPortRange->Last; ++i)
514 {
515 /*
516 * Don't do anything if the I/O port is already
517 * handled internally or externally.
518 */
519 if (IoPortProc[i].hVdd != 0)
520 {
521 DPRINT1("IoPortProc[0x%X] already registered\n", i);
522 continue;
523 }
524
525 /* Register wrt. the VDD */
526 IoPortProc[i].hVdd = hVdd;
527
528 /* Disable the internal handlers */
529 IoPortProc[i].IoHandlers.InB = NULL;
530 IoPortProc[i].IoHandlers.InW = NULL;
531 IoPortProc[i].IoHandlers.InD = NULL;
532
533 IoPortProc[i].IoHandlers.InsB = NULL;
534 IoPortProc[i].IoHandlers.InsW = NULL;
535 IoPortProc[i].IoHandlers.InsD = NULL;
536
537 IoPortProc[i].IoHandlers.OutB = NULL;
538 IoPortProc[i].IoHandlers.OutW = NULL;
539 IoPortProc[i].IoHandlers.OutD = NULL;
540
541 IoPortProc[i].IoHandlers.OutsB = NULL;
542 IoPortProc[i].IoHandlers.OutsW = NULL;
543 IoPortProc[i].IoHandlers.OutsD = NULL;
544
545 /* Save our handlers */
546 IoPortProc[i].VddIoHandlers = *IOhandler;
547 }
548
549 /* Go to the next range */
550 ++pPortRange;
551 ++IOhandler;
552 }
553
554 return TRUE;
555 }
556
557 VOID
558 WINAPI
559 VDDDeInstallIOHook(HANDLE hVdd,
560 WORD cPortRange,
561 PVDD_IO_PORTRANGE pPortRange)
562 {
563 /* Check possible validity of the VDD handle */
564 if (hVdd == 0 || hVdd == INVALID_HANDLE_VALUE) return;
565
566 /* Loop for each range of I/O ports */
567 while (cPortRange--)
568 {
569 WORD i;
570
571 /* Unregister the range of I/O ports */
572 for (i = pPortRange->First; i <= pPortRange->Last; ++i)
573 {
574 /*
575 * Don't do anything if we don't own the I/O port.
576 */
577 if (IoPortProc[i].hVdd != hVdd)
578 {
579 DPRINT1("IoPortProc[0x%X] owned by somebody else\n", i);
580 continue;
581 }
582
583 /*
584 * Put automagically all the fields to zero:
585 * the hVdd gets unregistered as well as all the handlers.
586 */
587 // IoPortProc[i] = {NULL};
588 RtlZeroMemory(&IoPortProc[i], sizeof(IoPortProc[i]));
589 }
590
591 /* Go to the next range */
592 ++pPortRange;
593 }
594 }
595
596 /* EOF */