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