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