[NTVDM] Improve the PCH situation.
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / bios32 / bios32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/bios/bios32/bios32.c
5 * PURPOSE: VDM 32-bit BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 #include "ntvdm.h"
11
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE VARIABLES **********************************************************/
16
17 CALLBACK16 BiosContext;
18
19 /*
20
21 Bochs BIOS, see rombios.h
22 =========================
23
24 // model byte 0xFC = AT
25 #define SYS_MODEL_ID 0xFC
26 #define SYS_SUBMODEL_ID 0x00
27 #define BIOS_REVISION 1
28 #define BIOS_CONFIG_TABLE 0xe6f5
29
30 #ifndef BIOS_BUILD_DATE
31 # define BIOS_BUILD_DATE "06/23/99"
32 #endif
33
34 // 1K of base memory used for Extended Bios Data Area (EBDA)
35 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
36 #define EBDA_SEG 0x9FC0
37 #define EBDA_SIZE 1 // In KiB
38 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
39
40
41 See rombios.c
42 =============
43
44 ROM BIOS compatibility entry points:
45 ===================================
46 $e05b ; POST Entry Point
47 $e2c3 ; NMI Handler Entry Point
48 $e3fe ; INT 13h Fixed Disk Services Entry Point
49 $e401 ; Fixed Disk Parameter Table
50 $e6f2 ; INT 19h Boot Load Service Entry Point
51 $e6f5 ; Configuration Data Table
52 $e729 ; Baud Rate Generator Table
53 $e739 ; INT 14h Serial Communications Service Entry Point
54 $e82e ; INT 16h Keyboard Service Entry Point
55 $e987 ; INT 09h Keyboard Service Entry Point
56 $ec59 ; INT 13h Diskette Service Entry Point
57 $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
58 $efc7 ; Diskette Controller Parameter Table
59 $efd2 ; INT 17h Printer Service Entry Point
60 $f045 ; INT 10 Functions 0-Fh Entry Point
61 $f065 ; INT 10h Video Support Service Entry Point
62 $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
63 $f841 ; INT 12h Memory Size Service Entry Point
64 $f84d ; INT 11h Equipment List Service Entry Point
65 $f859 ; INT 15h System Services Entry Point
66 $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
67 $fe6e ; INT 1Ah Time-of-day Service Entry Point
68 $fea5 ; INT 08h System Timer ISR Entry Point
69 $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
70 $ff53 ; IRET Instruction for Dummy Interrupt Handler
71 $ff54 ; INT 05h Print Screen Service Entry Point
72 $fff0 ; Power-up Entry Point
73 $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
74 $fffe ; System Model ID
75
76 */
77
78 /*
79 * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
80 * for more information.
81 */
82 #define BIOS_MODEL 0xFC // PC-AT
83 #define BIOS_SUBMODEL 0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
84 #define BIOS_REVISION 0x00
85 // FIXME: Find a nice PS/2 486 + 487 BIOS combination!
86
87 static const BIOS_CONFIG_TABLE BiosConfigTable =
88 {
89 sizeof(BIOS_CONFIG_TABLE) - sizeof(((BIOS_CONFIG_TABLE*)0)->Length), // Length: Number of bytes following
90
91 BIOS_MODEL, // BIOS Model
92 BIOS_SUBMODEL, // BIOS Sub-Model
93 BIOS_REVISION, // BIOS Revision
94
95 // Feature bytes
96 {
97 0x78, // At the moment we don't have any Extended BIOS Area; see http://www.ctyme.com/intr/rb-1594.htm#Table510
98 0x00, // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
99 0x10, // Bit 4: POST supports ROM-to-RAM enable/disable
100 0x00,
101 0x00
102 }
103 };
104
105
106 /*
107 * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
108 * Some programs otherwise look for "COPR. IBM" at F000:E008 .
109 */
110 static const CHAR BiosCopyright[] = "0000000 NTVDM IBM COMPATIBLE 486 BIOS COPYRIGHT (C) ReactOS Team 1996-"COPYRIGHT_YEAR;
111 static const CHAR BiosVersion[] = "ReactOS NTVDM 32-bit BIOS Version "KERNEL_VERSION_STR"\0"
112 "BIOS32 Version "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")";
113 static const CHAR BiosDate[] = "06/17/13";
114
115 C_ASSERT(sizeof(BiosCopyright)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
116 C_ASSERT(sizeof(BiosDate)-1 == 0x08);
117
118 /* 16-bit bootstrap code at F000:FFF0 */
119 static const BYTE Bootstrap[] =
120 {
121 0xEA, // jmp far ptr
122 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
123 };
124
125 /*
126 * POST code at F000:E05B. All the POST is done in 32 bit
127 * and only at the end it calls the bootstrap interrupt.
128 */
129 static const BYTE PostCode[] =
130 {
131 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_RESET, // Call BIOS POST
132 0xCD, BIOS_BOOTSTRAP_LOADER, // INT 0x19
133 0xCD, BIOS_ROM_BASIC, // INT 0x18
134 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
135 };
136
137
138 /* PRIVATE FUNCTIONS **********************************************************/
139
140 static VOID Bios32CharPrint(CHAR Character)
141 {
142 /* Save AX and BX */
143 USHORT AX = getAX();
144 USHORT BX = getBX();
145
146 /*
147 * Set the parameters:
148 * AL contains the character to print,
149 * BL contains the character attribute,
150 * BH contains the video page to use.
151 */
152 setAL(Character);
153 setBL(DEFAULT_ATTRIBUTE);
154 setBH(Bda->VideoPage);
155
156 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
157 setAH(0x0E);
158 Int32Call(&BiosContext, BIOS_VIDEO_INTERRUPT);
159
160 /* Restore AX and BX */
161 setBX(BX);
162 setAX(AX);
163 }
164
165 static VOID WINAPI BiosException(LPWORD Stack)
166 {
167 /* Get the exception number and call the emulator API */
168 BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]);
169 EmulatorException(ExceptionNumber, Stack);
170 }
171
172 VOID WINAPI BiosEquipmentService(LPWORD Stack)
173 {
174 /* Return the equipment list */
175 setAX(Bda->EquipmentList);
176 }
177
178 VOID WINAPI BiosGetMemorySize(LPWORD Stack)
179 {
180 /* Return the conventional memory size in kB, typically 640 kB */
181 setAX(Bda->MemorySize);
182 }
183
184 static VOID WINAPI BiosMiscService(LPWORD Stack)
185 {
186 switch (getAH())
187 {
188 /* OS Hooks for Multitasking */
189 case 0x80: // Device Open
190 case 0x81: // Device Close
191 case 0x82: // Program Termination
192 case 0x90: // Device Busy
193 case 0x91: // Device POST
194 {
195 /* Return success by default */
196 setAH(0x00);
197 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
198 break;
199 }
200
201 /* Wait on External Event */
202 case 0x41:
203 {
204 BYTE Value;
205 BOOLEAN Return;
206 static DWORD StartingCount;
207
208 /* Check if this is the first time this BOP occurred */
209 if (!getCF())
210 {
211 /* Set the starting count */
212 StartingCount = Bda->TickCounter;
213 }
214
215 if (getBL() != 0 && (Bda->TickCounter - StartingCount) >= getBL())
216 {
217 /* Timeout expired */
218 setCF(0);
219 break;
220 }
221
222 if (getAL() & (1 << 4))
223 {
224 /* Read from the I/O port */
225 Value = IOReadB(getDX());
226 }
227 else
228 {
229 /* Read from the memory */
230 Value = *(LPBYTE)SEG_OFF_TO_PTR(getES(), getDI());
231 }
232
233 switch (getAL() & 7)
234 {
235 /* Any external event */
236 case 0:
237 {
238 /* Return if this is not the first time the BOP occurred */
239 Return = getCF();
240 break;
241 }
242
243 /* Compare and return if equal */
244 case 1:
245 {
246 Return = Value == getBH();
247 break;
248 }
249
250 /* Compare and return if not equal */
251 case 2:
252 {
253 Return = Value != getBH();
254 break;
255 }
256
257 /* Test and return if not zero */
258 case 3:
259 {
260 Return = (Value & getBH()) != 0;
261 break;
262 }
263
264 /* Test and return if zero */
265 case 4:
266 {
267 Return = (Value & getBH()) == 0;
268 break;
269 }
270
271 default:
272 {
273 DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
274 Return = TRUE;
275 break;
276 }
277 }
278
279 /* Repeat the BOP if we shouldn't return */
280 setCF(!Return);
281 break;
282 }
283
284 /* Keyboard intercept */
285 case 0x4F:
286 {
287 /* CF should be set but let's just set it again just in case */
288 /* Do not modify AL (the hardware scan code), but set CF to continue processing */
289 // setCF(1);
290 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
291 break;
292 }
293
294 /* Wait */
295 case 0x86:
296 {
297 /*
298 * Interval in microseconds in CX:DX
299 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
300 * for more information.
301 */
302 LARGE_INTEGER TimeOut;
303 TimeOut.QuadPart = MAKELONG(getDX(), getCX()) * -10LL;
304
305 // HACK: For now, use the NT API (time in hundreds of nanoseconds).
306 NtDelayExecution(FALSE, &TimeOut);
307
308 /* Clear CF */
309 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
310 break;
311 }
312
313 /* Copy Extended Memory */
314 case 0x87:
315 {
316 DWORD Count = (DWORD)getCX() * 2;
317 PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI());
318 DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24);
319 DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16);
320 DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24);
321 DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16);
322
323 /* Check for flags */
324 if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF;
325 if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF;
326
327 if ((Count > SourceLimit) || (Count > DestLimit))
328 {
329 setAX(0x80);
330 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
331 break;
332 }
333
334 /* Copy */
335 RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase),
336 (PVOID)((ULONG_PTR)BaseAddress + SourceBase),
337 Count);
338
339 setAX(ERROR_SUCCESS);
340 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
341 break;
342 }
343
344 /* Get Extended Memory Size */
345 case 0x88:
346 {
347 UCHAR Low, High;
348
349 /*
350 * Return the (usable) extended memory (after 1 MB)
351 * size in kB from CMOS.
352 */
353 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW | CMOS_DISABLE_NMI);
354 Low = IOReadB(CMOS_DATA_PORT);
355 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH | CMOS_DISABLE_NMI);
356 High = IOReadB(CMOS_DATA_PORT);
357 setAX(MAKEWORD(Low, High));
358
359 /* Clear CF */
360 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
361 break;
362 }
363
364 /* Switch to Protected Mode */
365 case 0x89:
366 {
367 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
368
369 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
370 goto Default;
371 }
372
373 /* Get Configuration */
374 case 0xC0:
375 {
376 /* Return the BIOS ROM Configuration Table address in ES:BX */
377 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
378 setES(BIOS_SEGMENT);
379 setBX(0xE6F5);
380
381 /* Call successful; clear CF */
382 setAH(0x00);
383 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
384 break;
385 }
386
387 /* Return Extended-Bios Data-Area Segment Address (PS) */
388 case 0xC1:
389 {
390 /* We do not support EBDA yet */
391 UNIMPLEMENTED;
392 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
393 goto Default;
394 }
395
396 /* Pointing Device BIOS Interface (PS) */
397 case 0xC2:
398 {
399 // FIXME: Reenable this call when we understand why
400 // our included mouse driver doesn't correctly reeanble
401 // mouse reporting!
402 // BiosMousePs2Interface(Stack);
403 // break;
404 goto Default;
405 }
406
407 /* Get CPU Type and Mask Revision */
408 case 0xC9:
409 {
410 /*
411 * We can see this function as a CPUID replacement.
412 * See Ralf Brown: http://www.ctyme.com/intr/rb-1613.htm
413 * for more information.
414 */
415
416 /*
417 * Fast486 is a 486DX with FPU included,
418 * but old enough to not support CPUID.
419 */
420 setCX(0x0400);
421
422 /* Call successful; clear CF */
423 setAH(0x00);
424 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
425 break;
426 }
427
428 /* Get System Memory Map */
429 case 0xE8:
430 {
431 if (getAL() == 0x01)
432 {
433 /* The amount of memory between 1M and 16M, in kilobytes */
434 ULONG Above1M = (min(MAX_ADDRESS, 0x01000000) - 0x00100000) >> 10;
435
436 /* The amount of memory above 16M, in 64K blocks */
437 ULONG Above16M = (MAX_ADDRESS > 0x01000000) ? ((MAX_ADDRESS - 0x01000000) >> 16) : 0;
438
439 setAX(Above1M);
440 setBX(Above16M);
441 setCX(Above1M);
442 setDX(Above16M);
443
444 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
445 }
446 else if (getAL() == 0x20 && getEDX() == 'SMAP')
447 {
448 ULONG Offset = getEBX();
449 ULONG Length;
450 ULONG BytesWritten = 0;
451 BOOLEAN Hooked;
452 PBIOS_MEMORY_MAP Map = (PBIOS_MEMORY_MAP)SEG_OFF_TO_PTR(getES(), getDI());
453
454 /* Assume the buffer won't be large enough */
455 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
456
457 while (BytesWritten < getECX() && (ULONG_PTR)Map < (MAX_ADDRESS - sizeof(BIOS_MEMORY_MAP)))
458 {
459 /* Let's ask our memory controller */
460 if (!MemQueryMemoryZone(Offset, &Length, &Hooked))
461 {
462 /* No more memory blocks */
463 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
464 break;
465 }
466
467 Map->BaseAddress = (ULONGLONG)Offset;
468 Map->Length = (ULONGLONG)Length;
469 Map->Type = Hooked ? BIOS_MEMORY_RESERVED : BIOS_MEMORY_AVAILABLE;
470
471 /* Go to the next record */
472 Map++;
473 Offset += Length;
474 BytesWritten += sizeof(BIOS_MEMORY_MAP);
475 }
476
477 setEAX('SMAP');
478 setEBX(Offset);
479 setECX(BytesWritten);
480 }
481 else
482 {
483 DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
484 getAL(), getEDX());
485 }
486
487 break;
488 }
489
490 default: Default:
491 {
492 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
493 getAH());
494
495 /*
496 * The original signification of the error code 0x86 is that
497 * no PC Cassette is present. The CF is also set in this case.
498 * To keep backward compatibility, newer BIOSes use this value
499 * to indicate an unimplemented call in INT 15h.
500 */
501 setAH(0x86);
502 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
503 }
504 }
505 }
506
507 static VOID WINAPI BiosRomBasic(LPWORD Stack)
508 {
509 PrintMessageAnsi(Bios32CharPrint, "FATAL: INT18: BOOT FAILURE.");
510
511 /* ROM Basic is unsupported, display a message to the user */
512 DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing.");
513
514 /* Stop the VDM */
515 EmulatorTerminate();
516 }
517
518
519 extern VOID DosBootsectorInitialize(VOID);
520 extern VOID WINAPI BiosDiskService(LPWORD Stack);
521
522 static VOID WINAPI BiosBootstrapLoader(LPWORD Stack)
523 {
524 USHORT BootOrder;
525
526 USHORT AX, BX, CX, DX, ES;
527 AX = getAX();
528 BX = getBX();
529 CX = getCX();
530 DX = getDX();
531 ES = getES();
532
533 /*
534 * Read the boot sequence order from the CMOS, old behaviour AMI-style.
535 *
536 * For more information, see:
537 * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/orgs.asm
538 * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/boot.c
539 * http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/cmos.cc
540 * https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html
541 * http://www.bioscentral.com/misc/cmosmap.htm
542 */
543 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SYSOP);
544 BootOrder = (IOReadB(CMOS_DATA_PORT) & 0x20) >> 5;
545
546 /*
547 * BootOrder =
548 * 0: Hard Disk, then Floppy Disk
549 * 1: Floppy Disk, then Hard Disk
550 * In all cases, if booting from those devices failed,
551 * ROM DOS-32 is started. If it fails, INT 18h is called.
552 */
553
554 DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder);
555
556 /*
557 * Format of the BootOrder command:
558 * 2 bytes. Each half-byte contains the ID of the drive to boot.
559 * Currently defined:
560 * 0x0: 1st Floppy disk
561 * 0x1: 1st Hard disk
562 * Other, or 0xF: Stop boot sequence.
563 */
564 BootOrder = 0xFF00 | ((1 << (4 * BootOrder)) & 0xFF);
565
566 Retry:
567 switch (BootOrder & 0x0F)
568 {
569 /* Boot from 1st floppy drive */
570 case 0:
571 {
572 setAH(0x02); // Read sectors
573 setAL(0x01); // Number of sectors
574 setDH(0x00); // Head 0
575 setCH(0x00); // Cylinder 0
576 setCL(0x01); // Sector 1
577 setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
578 setES(0x0000); // Write data in 0000:7C00
579 setBX(0x7C00);
580 BiosDiskService(Stack);
581 if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
582 DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
583
584 break;
585 }
586
587 /* Boot from 1st HDD drive */
588 case 1:
589 {
590 setAH(0x02); // Read sectors
591 setAL(0x01); // Number of sectors
592 setDH(0x00); // Head 0
593 setCH(0x00); // Cylinder 0
594 setCL(0x01); // Sector 1
595 setDL(0x80); // First HDD drive (used by loader code, so should not be cleared)
596 setES(0x0000); // Write data in 0000:7C00
597 setBX(0x7C00);
598 BiosDiskService(Stack);
599 if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
600 DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
601
602 break;
603 }
604
605 default:
606 goto StartDos;
607 }
608
609 /* Go to next drive and invalidate the last half-byte. */
610 BootOrder = (BootOrder >> 4) | 0xF000;
611 goto Retry;
612
613 StartDos:
614 /* Clear everything, we are going to load DOS32 */
615 setAX(AX);
616 setBX(BX);
617 setCX(CX);
618 setDX(DX);
619 setES(ES);
620 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
621
622 /* Load our DOS */
623 DosBootsectorInitialize();
624
625 Quit:
626 /*
627 * Jump to 0000:7C00 to boot the OS.
628 *
629 * Since we are called via the INT32 mechanism, we need to correctly set
630 * CS:IP, not by changing the current one (otherwise the interrupt could
631 * not be clean up and return properly), but by changing the CS:IP in the
632 * stack, so that when the interrupt returns, the modified CS:IP is popped
633 * off the stack and the CPU is correctly repositioned.
634 */
635 Stack[STACK_CS] = 0x0000;
636 Stack[STACK_IP] = 0x7C00;
637
638 DPRINT("<-- BiosBootstrapLoader\n");
639 }
640
641 static VOID WINAPI BiosTimeService(LPWORD Stack)
642 {
643 switch (getAH())
644 {
645 /* Get System Time */
646 case 0x00:
647 {
648 /* Set AL to 1 if midnight had passed, 0 otherwise */
649 setAL(Bda->MidnightPassed ? 0x01 : 0x00);
650
651 /* Return the tick count in CX:DX */
652 setCX(HIWORD(Bda->TickCounter));
653 setDX(LOWORD(Bda->TickCounter));
654
655 /* Reset the midnight flag */
656 Bda->MidnightPassed = FALSE;
657
658 break;
659 }
660
661 /* Set System Time */
662 case 0x01:
663 {
664 /* Set the tick count to CX:DX */
665 Bda->TickCounter = MAKELONG(getDX(), getCX());
666
667 /* Reset the midnight flag */
668 Bda->MidnightPassed = FALSE;
669
670 break;
671 }
672
673 /* Get Real-Time Clock Time */
674 case 0x02:
675 {
676 UCHAR StatusB;
677
678 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_HOURS);
679 setCH(IOReadB(CMOS_DATA_PORT));
680
681 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_MINUTES);
682 setCL(IOReadB(CMOS_DATA_PORT));
683
684 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SECONDS);
685 setDH(IOReadB(CMOS_DATA_PORT));
686
687 /* Daylight Savings Time */
688 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_STATUS_B);
689 StatusB = IOReadB(CMOS_DATA_PORT);
690 setDL(StatusB & 0x01);
691
692 /* Clear CF */
693 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
694 break;
695 }
696
697 // /* Set Real-Time Clock Time */
698 // case 0x03:
699 // {
700 // break;
701 // }
702
703 /* Get Real-Time Clock Date */
704 case 0x04:
705 {
706 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_CENTURY);
707 setCH(IOReadB(CMOS_DATA_PORT));
708
709 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_YEAR);
710 setCL(IOReadB(CMOS_DATA_PORT));
711
712 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_MONTH);
713 setDH(IOReadB(CMOS_DATA_PORT));
714
715 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_DAY);
716 setDL(IOReadB(CMOS_DATA_PORT));
717
718 /* Clear CF */
719 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
720 break;
721 }
722
723 // /* Set Real-Time Clock Date */
724 // case 0x05:
725 // {
726 // break;
727 // }
728
729 default:
730 {
731 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
732 getAH());
733 }
734 }
735 }
736
737 static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
738 {
739 /* Increase the system tick count */
740 Bda->TickCounter++;
741 }
742
743
744 // From SeaBIOS
745 static VOID PicSetIRQMask(USHORT off, USHORT on)
746 {
747 UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8;
748 IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on);
749 IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on);
750 }
751
752 // From SeaBIOS
753 VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func)
754 {
755 UCHAR vector;
756
757 PicSetIRQMask(1 << hwirq, 0);
758 if (hwirq < 8)
759 vector = BIOS_PIC_MASTER_INT + hwirq;
760 else
761 vector = BIOS_PIC_SLAVE_INT + hwirq - 8;
762
763 RegisterBiosInt32(vector, func);
764 }
765
766
767 VOID PicIRQComplete(BYTE IntNum)
768 {
769 /*
770 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
771 */
772 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
773 {
774 /* It was an IRQ from the master PIC */
775 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
776 }
777 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
778 {
779 /* It was an IRQ from the slave PIC */
780 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
781 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
782 }
783 }
784
785 static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack)
786 {
787 BYTE IrqNumber;
788
789 IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
790 IrqNumber = IOReadB(PIC_MASTER_CMD);
791
792 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber);
793
794 PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
795 }
796
797 static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack)
798 {
799 BYTE IrqNumber;
800
801 IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
802 IrqNumber = IOReadB(PIC_SLAVE_CMD);
803
804 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber);
805
806 PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
807 }
808
809 // Timer IRQ 0
810 static VOID WINAPI BiosTimerIrq(LPWORD Stack)
811 {
812 /*
813 * Perform the system timer interrupt.
814 *
815 * Do not call directly BiosSystemTimerInterrupt(Stack);
816 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
817 * for their purpose...
818 */
819
820 WORD AX = getAX();
821 WORD CX = getCX();
822 WORD DX = getDX();
823 WORD BX = getBX();
824 WORD BP = getBP();
825 WORD SI = getSI();
826 WORD DI = getDI();
827 WORD DS = getDS();
828 WORD ES = getES();
829
830 Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT);
831
832 setAX(AX);
833 setCX(CX);
834 setDX(DX);
835 setBX(BX);
836 setBP(BP);
837 setSI(SI);
838 setDI(DI);
839 setDS(DS);
840 setES(ES);
841 setCF(0);
842
843 // BiosSystemTimerInterrupt(Stack);
844 PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
845 }
846
847
848 static VOID BiosHwSetup(VOID)
849 {
850 /* Initialize the master and the slave PICs (cascade mode) */
851 IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
852 IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
853
854 /*
855 * Set the interrupt vector offsets for each PIC
856 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
857 */
858 IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
859 IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT );
860
861 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
862 IOWriteB(PIC_MASTER_DATA, 1 << 2);
863 /* Tell the slave PIC its cascade identity */
864 IOWriteB(PIC_SLAVE_DATA , 2);
865
866 /* Make sure both PICs are in 8086 mode */
867 IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086);
868 IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086);
869
870 /* Clear the masks for both PICs */
871 // IOWriteB(PIC_MASTER_DATA, 0x00);
872 // IOWriteB(PIC_SLAVE_DATA , 0x00);
873 /* Disable all IRQs */
874 IOWriteB(PIC_MASTER_DATA, 0xFF);
875 IOWriteB(PIC_SLAVE_DATA , 0xFF);
876
877
878 /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
879 // NOTE: Some BIOSes set it to Mode 3 instead.
880 IOWriteB(PIT_COMMAND_PORT, 0x34);
881 // 18.2Hz refresh rate
882 IOWriteB(PIT_DATA_PORT(0), 0x00);
883 IOWriteB(PIT_DATA_PORT(0), 0x00);
884
885 /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
886 IOWriteB(PIT_COMMAND_PORT, 0x54);
887 // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
888 IOWriteB(PIT_DATA_PORT(1), 18);
889
890 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
891 IOWriteB(PIT_COMMAND_PORT, 0xB6);
892 // Count for 440Hz
893 IOWriteB(PIT_DATA_PORT(2), 0x97);
894 IOWriteB(PIT_DATA_PORT(2), 0x0A);
895
896
897 /* Initialize PS/2 keyboard port */
898 // Enable the port
899 IOWriteB(PS2_CONTROL_PORT, 0xAE);
900 // Port interrupts and clock enabled,
901 // enable keyboard scancode translation.
902 // POST passed, force keyboard unlocking.
903 IOWriteB(PS2_CONTROL_PORT, 0x60);
904 IOWriteB(PS2_DATA_PORT , 0x6D);
905 // Enable data reporting
906 IOWriteB(PS2_DATA_PORT , 0xF4);
907
908 EnableHwIRQ(0, BiosTimerIrq);
909 }
910
911 static VOID InitializeBiosInt32(VOID)
912 {
913 USHORT i;
914
915 /* Initialize the callback context */
916 InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000);
917
918 /* Register the default BIOS interrupt vectors */
919
920 /*
921 * Zero out all of the IVT (0x00 -- 0xFF). Some applications
922 * indeed expect to have free vectors at the end of the IVT.
923 */
924 RtlZeroMemory(BaseAddress, 0x0100 * sizeof(ULONG));
925
926 #if defined(ADVANCED_DEBUGGING) && (ADVANCED_DEBUGGING_LEVEL >= 3)
927 // Initialize all the interrupt vectors to the default one.
928 for (i = 0x00; i <= 0xFF; i++)
929 RegisterBiosInt32(i, NULL);
930 #endif
931
932 /* Initialize the exception interrupt vectors to a default Exception handler */
933 for (i = 0x00; i <= 0x07; i++)
934 RegisterBiosInt32(i, BiosException);
935
936 /* Initialize HW interrupt vectors to a default HW handler */
937 for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) // 0x08 -- 0x0F
938 RegisterBiosInt32(i, BiosHandleMasterPicIRQ);
939 for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++) // 0x70 -- 0x77
940 RegisterBiosInt32(i, BiosHandleSlavePicIRQ);
941
942 /* Initialize software vector handlers */
943 // BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
944 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService );
945 RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize );
946 // BIOS_DISK_INTERRUPT : 0x13 (dskbios32.c)
947 // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
948 RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService );
949 // BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
950 // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
951 RegisterBiosInt32(BIOS_ROM_BASIC , BiosRomBasic );
952 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER , BiosBootstrapLoader );
953 RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService );
954 // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
955 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
956
957 /* Vectors that should be implemented (see above) */
958 RegisterBiosInt32(0x14, NULL);
959 RegisterBiosInt32(0x17, NULL);
960 RegisterBiosInt32(0x1B, NULL);
961 RegisterBiosInt32(0x4A, NULL); // User Alarm Handler
962
963 /* Relocated services by the BIOS (when needed) */
964 RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
965 RegisterBiosInt32(0x42, NULL); // Relocated Default INT 10h Video Services
966
967 /* Miscellaneous unimplemented vector handlers that should better have a default one */
968 RegisterBiosInt32(0x4B, NULL); // Virtual DMA Specification Services
969 RegisterBiosInt32(0x5C, NULL); // NetBIOS
970
971 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
972 // They don't have any default handler at the moment.
973
974 /* Some vectors are in fact addresses to tables */
975 ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL; // Video Parameter Tables
976 ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL; // Diskette Parameters
977 ((PULONG)BaseAddress)[0x1F] = (ULONG)NULL; // 8x8 Graphics Font
978 ((PULONG)BaseAddress)[0x41] = (ULONG)NULL; // Hard Disk 0 Parameter Table Address
979 ((PULONG)BaseAddress)[0x43] = (ULONG)NULL; // Character Table (EGA, MCGA, VGA)
980 ((PULONG)BaseAddress)[0x46] = (ULONG)NULL; // Hard Disk 1 Drive Parameter Table Address
981 /* Tables that are always uninitialized */
982 ((PULONG)BaseAddress)[0x44] = (ULONG)NULL; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
983 ((PULONG)BaseAddress)[0x48] = (ULONG)NULL; // Cordless Keyboard Translation (PCjr)
984 ((PULONG)BaseAddress)[0x49] = (ULONG)NULL; // Non-Keyboard Scan-code Translation Table (PCJr)
985 }
986
987 static VOID InitializeBiosData(VOID)
988 {
989 UCHAR Low, High;
990
991 /* Initialize the BDA contents */
992 RtlZeroMemory(Bda, sizeof(*Bda));
993
994 /*
995 * Retrieve the basic equipment list from the CMOS
996 */
997 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_EQUIPMENT_LIST | CMOS_DISABLE_NMI);
998 Bda->EquipmentList = IOReadB(CMOS_DATA_PORT);
999 // TODO: Update it if required.
1000 Bda->EquipmentList &= 0x00FF; // High byte cleared for now...
1001
1002 /*
1003 * Retrieve the conventional memory size
1004 * in kB from the CMOS, typically 640 kB.
1005 */
1006 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW | CMOS_DISABLE_NMI);
1007 Low = IOReadB(CMOS_DATA_PORT);
1008 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH | CMOS_DISABLE_NMI);
1009 High = IOReadB(CMOS_DATA_PORT);
1010 Bda->MemorySize = MAKEWORD(Low, High);
1011 }
1012
1013
1014 /*
1015 * The BIOS POST (Power On-Self Test)
1016 */
1017 /*static*/ VOID
1018 WINAPI
1019 Bios32Post(LPWORD Stack)
1020 {
1021 static BOOLEAN FirstBoot = TRUE;
1022 BYTE ShutdownStatus;
1023
1024 /*
1025 * Initialize BIOS/Keyboard/Video RAM dynamic data
1026 */
1027
1028 DPRINT("Bios32Post\n");
1029
1030 /* Disable interrupts */
1031 setIF(0);
1032
1033 /* Set the data segment */
1034 setDS(BDA_SEGMENT);
1035
1036 /* Initialize the stack */
1037 // Temporary stack for POST (to be used only before initializing the INT vectors)
1038 // setSS(0x0000);
1039 // setSP(0x0400);
1040 //
1041 // Stack to be used after the initialization of the INT vectors
1042 setSS(0x0000); // Stack at 00:8000, going downwards
1043 setSP(0x8000);
1044
1045 /*
1046 * Perform early CMOS shutdown status checks
1047 */
1048
1049 /* Read the CMOS shutdown status byte and reset it */
1050 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
1051 ShutdownStatus = IOReadB(CMOS_DATA_PORT);
1052 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
1053 IOWriteB(CMOS_DATA_PORT, 0x00);
1054
1055 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
1056 Bda->SoftReset, ShutdownStatus);
1057
1058 switch (ShutdownStatus)
1059 {
1060 /* Shutdown after Memory Tests (unsupported) */
1061 case 0x01: case 0x02: case 0x03:
1062 /* Shutdown after Protected Mode Tests (unsupported) */
1063 case 0x06: case 0x07: case 0x08:
1064 /* Shutdown after Block Move Test (unsupported) */
1065 case 0x09:
1066 {
1067 DisplayMessage(L"Unsupported CMOS Shutdown Status value 0x%02X. The VDM will shut down.", ShutdownStatus);
1068 EmulatorTerminate();
1069 return;
1070 }
1071
1072 /* Shutdown to Boot Loader */
1073 case 0x04:
1074 {
1075 DPRINT1("Fast restart to Bootstrap Loader...\n");
1076 goto Quit; // Reenable interrupts and exit.
1077 }
1078
1079 /* Flush keyboard, issue an EOI... */
1080 case 0x05:
1081 {
1082 IOReadB(PS2_DATA_PORT);
1083
1084 /* Send EOI */
1085 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
1086 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
1087
1088 // Fall back
1089 }
1090
1091 /*
1092 * ... and far JMP to user-specified location at 0040:0067
1093 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
1094 */
1095 case 0x0A:
1096 {
1097 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
1098 HIWORD(Bda->ResumeEntryPoint),
1099 LOWORD(Bda->ResumeEntryPoint));
1100
1101 /* Position execution pointers and return with interrupts disabled */
1102 setCS(HIWORD(Bda->ResumeEntryPoint));
1103 setIP(LOWORD(Bda->ResumeEntryPoint));
1104 return;
1105 }
1106
1107 /* Soft reset or unexpected shutdown... */
1108 case 0x00:
1109 /* ... or other possible shutdown codes: just continue the POST */
1110 default:
1111 break;
1112 }
1113
1114 /*
1115 * FIXME: UNIMPLEMENTED!
1116 * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
1117 * following actions:
1118 * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
1119 * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
1120 */
1121 switch (Bda->SoftReset)
1122 {
1123 case 0x0000:
1124 {
1125 if (!FirstBoot)
1126 {
1127 DisplayMessage(L"NTVDM is performing a COLD reboot! The program you are currently testing does not seem to behave correctly! The VDM will shut down...");
1128 EmulatorTerminate();
1129 return;
1130 }
1131 break;
1132 }
1133
1134 case 0x1234:
1135 {
1136 DisplayMessage(L"NTVDM is performing a WARM reboot! This is not supported at the moment. The VDM will shut down...");
1137 EmulatorTerminate();
1138 return;
1139 }
1140
1141 default:
1142 break;
1143 }
1144
1145 FirstBoot = FALSE;
1146
1147 /* Initialize the BDA */
1148 InitializeBiosData();
1149
1150 /* Initialize the User Data Area at 0050:XXXX */
1151 RtlZeroMemory(SEG_OFF_TO_PTR(0x50, 0x0000), sizeof(USER_DATA_AREA));
1152
1153
1154
1155 //////// NOTE: This is more or less bios-specific ////////
1156
1157 /*
1158 * Initialize IVT and hardware
1159 */
1160
1161 // WriteUnProtectRom(...);
1162
1163 /* Register the BIOS 32-bit Interrupts */
1164 InitializeBiosInt32();
1165
1166 /* Initialize platform hardware (PIC/PIT chips, ...) */
1167 BiosHwSetup();
1168
1169 /* Initialize the Keyboard, Video and Mouse BIOS */
1170 KbdBios32Post();
1171 VidBiosPost();
1172 MouseBios32Post();
1173 DiskBios32Post();
1174
1175 // WriteProtectRom(...);
1176
1177 //////// End of more or less bios-specific section ///////
1178
1179
1180
1181 SearchAndInitRoms(&BiosContext);
1182
1183 /*
1184 * End of the 32-bit POST portion. We then fall back into 16-bit where
1185 * the rest of the POST code is executed, typically calling INT 19h
1186 * to boot up the OS.
1187 */
1188
1189 Quit:
1190 /* Enable interrupts */
1191 setIF(1);
1192 }
1193
1194
1195 /* PUBLIC FUNCTIONS ***********************************************************/
1196
1197 BOOLEAN Bios32Initialize(VOID)
1198 {
1199 /*
1200 * Initialize BIOS/Keyboard/Video ROM static data
1201 */
1202
1203 /* System BIOS Copyright */
1204 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE000), BiosCopyright, sizeof(BiosCopyright)-1);
1205
1206 /* System BIOS Version */
1207 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE070), BiosVersion, sizeof(BiosVersion)-1);
1208
1209 /* System BIOS Date */
1210 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFF5), BiosDate, sizeof(BiosDate)-1);
1211
1212 /* Bootstrap code */
1213 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE05B), PostCode , sizeof(PostCode ));
1214 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFF0), Bootstrap, sizeof(Bootstrap));
1215
1216 /* BIOS ROM Information */
1217 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE6F5), &BiosConfigTable, sizeof(BiosConfigTable));
1218
1219 /* System BIOS Model (same as Bct->Model) */
1220 *(PBYTE)(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFFE)) = BIOS_MODEL;
1221
1222 /* Initialize the Keyboard and Video BIOS */
1223 if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
1224 {
1225 /* Stop the VDM */
1226 EmulatorTerminate();
1227 return FALSE;
1228 }
1229
1230 /* Redefine our POST function */
1231 RegisterBop(BOP_RESET, Bios32Post);
1232
1233 WriteProtectRom((PVOID)TO_LINEAR(BIOS_SEGMENT, 0x0000),
1234 ROM_AREA_END - TO_LINEAR(BIOS_SEGMENT, 0x0000) + 1);
1235
1236 /* We are done */
1237 return TRUE;
1238 }
1239
1240 VOID Bios32Cleanup(VOID)
1241 {
1242 DiskBios32Cleanup();
1243 MouseBios32Cleanup();
1244 VidBios32Cleanup();
1245 KbdBiosCleanup();
1246 }
1247
1248 /* EOF */