2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: VDM 32-bit BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
13 /* For BIOS Version number */
14 #include <reactos/buildno.h>
18 #include "cpu/cpu.h" // for EMULATOR_FLAG_CF
22 #include <bios/bios.h>
26 #include "kbdbios32.h"
27 #include "vidbios32.h"
28 #include "moubios32.h"
32 #include "hardware/cmos.h"
33 #include "hardware/pic.h"
34 #include "hardware/pit.h"
35 #include "hardware/ps2.h"
37 /* PRIVATE VARIABLES **********************************************************/
39 CALLBACK16 BiosContext
;
43 Bochs BIOS, see rombios.h
44 =========================
46 // model byte 0xFC = AT
47 #define SYS_MODEL_ID 0xFC
48 #define SYS_SUBMODEL_ID 0x00
49 #define BIOS_REVISION 1
50 #define BIOS_CONFIG_TABLE 0xe6f5
52 #ifndef BIOS_BUILD_DATE
53 # define BIOS_BUILD_DATE "06/23/99"
56 // 1K of base memory used for Extended Bios Data Area (EBDA)
57 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
58 #define EBDA_SEG 0x9FC0
59 #define EBDA_SIZE 1 // In KiB
60 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
66 ROM BIOS compatibility entry points:
67 ===================================
68 $e05b ; POST Entry Point
69 $e2c3 ; NMI Handler Entry Point
70 $e3fe ; INT 13h Fixed Disk Services Entry Point
71 $e401 ; Fixed Disk Parameter Table
72 $e6f2 ; INT 19h Boot Load Service Entry Point
73 $e6f5 ; Configuration Data Table
74 $e729 ; Baud Rate Generator Table
75 $e739 ; INT 14h Serial Communications Service Entry Point
76 $e82e ; INT 16h Keyboard Service Entry Point
77 $e987 ; INT 09h Keyboard Service Entry Point
78 $ec59 ; INT 13h Diskette Service Entry Point
79 $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
80 $efc7 ; Diskette Controller Parameter Table
81 $efd2 ; INT 17h Printer Service Entry Point
82 $f045 ; INT 10 Functions 0-Fh Entry Point
83 $f065 ; INT 10h Video Support Service Entry Point
84 $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
85 $f841 ; INT 12h Memory Size Service Entry Point
86 $f84d ; INT 11h Equipment List Service Entry Point
87 $f859 ; INT 15h System Services Entry Point
88 $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
89 $fe6e ; INT 1Ah Time-of-day Service Entry Point
90 $fea5 ; INT 08h System Timer ISR Entry Point
91 $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
92 $ff53 ; IRET Instruction for Dummy Interrupt Handler
93 $ff54 ; INT 05h Print Screen Service Entry Point
94 $fff0 ; Power-up Entry Point
95 $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
96 $fffe ; System Model ID
101 * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
102 * for more information.
104 #define BIOS_MODEL 0xFC // PC-AT
105 #define BIOS_SUBMODEL 0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
106 #define BIOS_REVISION 0x00
107 // FIXME: Find a nice PS/2 486 + 487 BIOS combination!
110 * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
111 * Some programs alternatively look at "COPR. IBM" that is at F000:E008 .
113 static const CHAR BiosCopyright
[] = "0000000 NTVDM IBM Compatible 486 32-bit BIOS Copyright (C) ReactOS Team 1996-2014";
114 static const CHAR BiosVersion
[] = "ReactOS NTVDM 32-bit BIOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")";
115 static const CHAR BiosDate
[] = "06/17/13";
117 C_ASSERT(sizeof(BiosCopyright
)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
118 C_ASSERT(sizeof(BiosDate
)-1 == 0x08);
120 /* 16-bit bootstrap code at F000:FFF0 */
121 static BYTE Bootstrap
[] =
124 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
128 * Normally at F000:E05B there is the POST that finally calls the bootstrap
129 * interrupt. It should also check the value of Bda->SoftReset. Since we do
130 * all the POST in 32 bit from the start, we just place there the bootstrap
133 static BYTE PostCode
[] =
135 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_RESET
, // Call BIOS POST
136 0xCD, BIOS_BOOTSTRAP_LOADER
, // INT 0x19
137 // LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
141 /* PRIVATE FUNCTIONS **********************************************************/
143 static VOID WINAPI
BiosException(LPWORD Stack
)
145 /* Get the exception number and call the emulator API */
146 BYTE ExceptionNumber
= LOBYTE(Stack
[STACK_INT_NUM
]);
147 EmulatorException(ExceptionNumber
, Stack
);
150 static VOID WINAPI
BiosMiscService(LPWORD Stack
)
154 /* OS Hooks for Multitasking */
155 case 0x80: // Device Open
156 case 0x81: // Device Close
157 case 0x82: // Program Termination
158 case 0x90: // Device Busy
159 case 0x91: // Device POST
161 /* Return success by default */
163 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
167 /* Wait On External Event */
172 static DWORD StartingCount
;
174 /* Check if this is the first time this BOP occurred */
177 /* Set the starting count */
178 StartingCount
= Bda
->TickCounter
;
181 if (getBL() != 0 && (Bda
->TickCounter
- StartingCount
) >= getBL())
183 /* Timeout expired */
188 if (getAL() & (1 << 4))
190 /* Read from the I/O port */
191 Value
= IOReadB(getDX());
195 /* Read from the memory */
196 Value
= *(LPBYTE
)SEG_OFF_TO_PTR(getES(), getDI());
201 /* Any external event */
204 /* Return if this is not the first time the BOP occurred */
209 /* Compare and return if equal */
212 Return
= Value
== getBH();
216 /* Compare and return if not equal */
219 Return
= Value
!= getBH();
223 /* Test and return if not zero */
226 Return
= (Value
& getBH()) != 0;
230 /* Test and return if zero */
233 Return
= (Value
& getBH()) == 0;
239 DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
245 /* Repeat the BOP if we shouldn't return */
251 /* Keyboard intercept */
254 /* CF should be set but let's just set it again just in case */
255 /* Do not modify AL (the hardware scan code), but set CF to continue processing */
257 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
265 * Interval in microseconds in CX:DX
266 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
267 * for more information.
269 LARGE_INTEGER TimeOut
;
270 TimeOut
.QuadPart
= MAKELONG(getDX(), getCX()) * -10LL;
272 // HACK: For now, use the NT API (time in hundreds of nanoseconds).
273 NtDelayExecution(FALSE
, &TimeOut
);
276 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
281 /* Copy Extended Memory */
284 DWORD Count
= (DWORD
)getCX() * 2;
285 PFAST486_GDT_ENTRY Gdt
= (PFAST486_GDT_ENTRY
)SEG_OFF_TO_PTR(getES(), getSI());
286 DWORD SourceBase
= Gdt
[2].Base
+ (Gdt
[2].BaseMid
<< 16) + (Gdt
[2].BaseHigh
<< 24);
287 DWORD SourceLimit
= Gdt
[2].Limit
+ (Gdt
[2].LimitHigh
<< 16);
288 DWORD DestBase
= Gdt
[3].Base
+ (Gdt
[3].BaseMid
<< 16) + (Gdt
[3].BaseHigh
<< 24);
289 DWORD DestLimit
= Gdt
[3].Limit
+ (Gdt
[3].LimitHigh
<< 16);
291 /* Check for flags */
292 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
293 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
295 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
298 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
304 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
305 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
308 setAX(ERROR_SUCCESS
);
309 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
313 /* Get Extended Memory Size */
319 * Return the (usable) extended memory (after 1 MB)
320 * size in kB from CMOS.
322 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_LOW
| CMOS_DISABLE_NMI
);
323 Low
= IOReadB(CMOS_DATA_PORT
);
324 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH
| CMOS_DISABLE_NMI
);
325 High
= IOReadB(CMOS_DATA_PORT
);
326 setAX(MAKEWORD(Low
, High
));
329 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
334 /* Switch to Protected Mode */
337 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
339 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
343 /* Get Configuration */
346 /* Return the BIOS ROM Configuration Table address in ES:BX */
347 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
351 /* Call successful; clear CF */
353 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
358 /* Return Extended-Bios Data-Area Segment Address (PS) */
361 // Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
366 /* We do not support EBDA yet */
367 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
372 /* Pointing Device BIOS Interface (PS) */
375 BiosMousePs2Interface(Stack
);
379 /* Get System Memory Map */
384 /* The amount of memory between 1M and 16M, in kilobytes */
385 ULONG Above1M
= (min(MAX_ADDRESS
, 0x01000000) - 0x00100000) >> 10;
387 /* The amount of memory above 16M, in 64K blocks */
388 ULONG Above16M
= (MAX_ADDRESS
> 0x01000000) ? (MAX_ADDRESS
- 0x01000000) >> 16: 0;
395 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
397 else if (getAL() == 0x20 && getEDX() == 'SMAP')
399 ULONG Offset
= getEBX();
401 ULONG BytesWritten
= 0;
403 PBIOS_MEMORY_MAP Map
= (PBIOS_MEMORY_MAP
)SEG_OFF_TO_PTR(getES(), getDI());
405 /* Assume the buffer won't be large enough */
406 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
408 while (BytesWritten
< getECX() && (ULONG_PTR
)Map
< (MAX_ADDRESS
- sizeof(BIOS_MEMORY_MAP
)))
410 /* Let's ask our memory controller */
411 if (!MemQueryMemoryZone(Offset
, &Length
, &Hooked
))
413 /* No more memory blocks */
414 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
418 Map
->BaseAddress
= (ULONGLONG
)Offset
;
419 Map
->Length
= (ULONGLONG
)Length
;
420 Map
->Type
= Hooked
? BIOS_MEMORY_RESERVED
: BIOS_MEMORY_AVAILABLE
;
422 /* Go to the next record */
425 BytesWritten
+= sizeof(BIOS_MEMORY_MAP
);
430 setECX(BytesWritten
);
434 DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
444 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
450 static VOID WINAPI
BiosRomBasic(LPWORD Stack
)
452 /* ROM Basic is unsupported, display a message to the user */
453 DisplayMessage(L
"NTVDM doesn't support ROM Basic. The VDM is closing.");
461 VOID
DosBootsectorInitialize(VOID
);
463 static VOID WINAPI
BiosBootstrapLoader(LPWORD Stack
)
466 * In real BIOSes one loads the bootsector read from a diskette
467 * or from a disk, copy it to 0000:7C00 and then boot it.
468 * Since we are 32-bit VM and we hardcode our DOS at the moment,
469 * just call the DOS 32-bit initialization code.
472 DPRINT("BiosBootstrapLoader -->\n");
475 DosBootsectorInitialize();
478 * Position CPU to 0000:7C00 to boot the OS.
480 * Since we are called via the INT32 mechanism, we need to correctly set
481 * CS:IP, not by changing the current one (otherwise the interrupt could
482 * not be clean up and return properly), but by changing the CS:IP in the
483 * stack, so that when the interrupt returns, the modified CS:IP is popped
484 * off the stack and the CPU is correctly repositioned.
486 Stack
[STACK_CS
] = 0x0000;
487 Stack
[STACK_IP
] = 0x7C00;
489 DPRINT("<-- BiosBootstrapLoader\n");
492 static VOID WINAPI
BiosTimeService(LPWORD Stack
)
498 /* Set AL to 1 if midnight had passed, 0 otherwise */
499 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
501 /* Return the tick count in CX:DX */
502 setCX(HIWORD(Bda
->TickCounter
));
503 setDX(LOWORD(Bda
->TickCounter
));
505 /* Reset the midnight flag */
506 Bda
->MidnightPassed
= FALSE
;
513 /* Set the tick count to CX:DX */
514 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
516 /* Reset the midnight flag */
517 Bda
->MidnightPassed
= FALSE
;
524 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
530 static VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
532 /* Increase the system tick count */
538 static VOID
PicSetIRQMask(USHORT off
, USHORT on
)
540 UCHAR pic1off
= off
, pic1on
= on
, pic2off
= off
>>8, pic2on
= on
>>8;
541 IOWriteB(PIC_MASTER_DATA
, (IOReadB(PIC_MASTER_DATA
) & ~pic1off
) | pic1on
);
542 IOWriteB(PIC_SLAVE_DATA
, (IOReadB(PIC_SLAVE_DATA
) & ~pic2off
) | pic2on
);
546 VOID
EnableHwIRQ(UCHAR hwirq
, EMULATOR_INT32_PROC func
)
550 PicSetIRQMask(1 << hwirq
, 0);
552 vector
= BIOS_PIC_MASTER_INT
+ hwirq
;
554 vector
= BIOS_PIC_SLAVE_INT
+ hwirq
- 8;
556 RegisterBiosInt32(vector
, func
);
560 VOID
PicIRQComplete(LPWORD Stack
)
562 /* Get the interrupt number */
563 BYTE IntNum
= LOBYTE(Stack
[STACK_INT_NUM
]);
566 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
569 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
571 /* It was an IRQ from the master PIC */
572 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
574 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
576 /* It was an IRQ from the slave PIC */
577 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
578 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
582 static VOID WINAPI
BiosHandleMasterPicIRQ(LPWORD Stack
)
586 IOWriteB(PIC_MASTER_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
587 IrqNumber
= IOReadB(PIC_MASTER_CMD
);
589 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber
);
591 PicIRQComplete(Stack
);
594 static VOID WINAPI
BiosHandleSlavePicIRQ(LPWORD Stack
)
598 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
599 IrqNumber
= IOReadB(PIC_SLAVE_CMD
);
601 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber
);
603 PicIRQComplete(Stack
);
607 static VOID WINAPI
BiosTimerIrq(LPWORD Stack
)
610 * Perform the system timer interrupt.
612 * Do not call directly BiosSystemTimerInterrupt(Stack);
613 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
614 * for their purpose...
616 Int32Call(&BiosContext
, BIOS_SYS_TIMER_INTERRUPT
);
617 // BiosSystemTimerInterrupt(Stack);
618 PicIRQComplete(Stack
);
622 static VOID
BiosHwSetup(VOID
)
624 /* Initialize the master and the slave PICs (cascade mode) */
625 IOWriteB(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
626 IOWriteB(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
629 * Set the interrupt vector offsets for each PIC
630 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
632 IOWriteB(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
633 IOWriteB(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
635 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
636 IOWriteB(PIC_MASTER_DATA
, 1 << 2);
637 /* Tell the slave PIC its cascade identity */
638 IOWriteB(PIC_SLAVE_DATA
, 2);
640 /* Make sure both PICs are in 8086 mode */
641 IOWriteB(PIC_MASTER_DATA
, PIC_ICW4_8086
);
642 IOWriteB(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
644 /* Clear the masks for both PICs */
645 // IOWriteB(PIC_MASTER_DATA, 0x00);
646 // IOWriteB(PIC_SLAVE_DATA , 0x00);
647 /* Disable all IRQs */
648 IOWriteB(PIC_MASTER_DATA
, 0xFF);
649 IOWriteB(PIC_SLAVE_DATA
, 0xFF);
652 /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
653 // NOTE: Some BIOSes set it to Mode 3 instead.
654 IOWriteB(PIT_COMMAND_PORT
, 0x34);
655 // 18.2Hz refresh rate
656 IOWriteB(PIT_DATA_PORT(0), 0x00);
657 IOWriteB(PIT_DATA_PORT(0), 0x00);
659 /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
660 IOWriteB(PIT_COMMAND_PORT
, 0x54);
661 // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
662 IOWriteB(PIT_DATA_PORT(1), 18);
664 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
665 IOWriteB(PIT_COMMAND_PORT
, 0xB6);
667 IOWriteB(PIT_DATA_PORT(2), 0x97);
668 IOWriteB(PIT_DATA_PORT(2), 0x0A);
670 EnableHwIRQ(0, BiosTimerIrq
);
673 static VOID
InitializeBiosInt32(VOID
)
677 /* Initialize the callback context */
678 InitializeContext(&BiosContext
, BIOS_SEGMENT
, 0x0000);
680 /* Register the default BIOS interrupt vectors */
682 /* Zero out all of the IVT (0x00 -- 0xFF) */
683 RtlZeroMemory(BaseAddress
, 0x0100 * sizeof(ULONG
));
685 #ifdef ADVANCED_DEBUGGING
686 // Initialize all the interrupt vectors to the default one.
687 for (i
= 0x00; i
<= 0xFF; i
++)
688 RegisterBiosInt32(i
, NULL
);
691 /* Initialize the exception interrupt vectors to a default Exception handler */
692 for (i
= 0x00; i
<= 0x07; i
++)
693 RegisterBiosInt32(i
, BiosException
);
695 /* Initialize HW interrupt vectors to a default HW handler */
696 for (i
= BIOS_PIC_MASTER_INT
; i
< BIOS_PIC_MASTER_INT
+ 8; i
++) // 0x08 -- 0x0F
697 RegisterBiosInt32(i
, BiosHandleMasterPicIRQ
);
698 for (i
= BIOS_PIC_SLAVE_INT
; i
< BIOS_PIC_SLAVE_INT
+ 8; i
++) // 0x70 -- 0x77
699 RegisterBiosInt32(i
, BiosHandleSlavePicIRQ
);
701 /* Initialize software vector handlers */
702 // BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
703 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
704 RegisterBiosInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
705 // BIOS_DISK_INTERRUPT : 0x13 -- UNIMPLEMENTED
706 // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
707 RegisterBiosInt32(BIOS_MISC_INTERRUPT
, BiosMiscService
);
708 // BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
709 // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
710 RegisterBiosInt32(BIOS_ROM_BASIC
, BiosRomBasic
);
711 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER
, BiosBootstrapLoader
);
712 RegisterBiosInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
713 // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
714 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
716 /* Vectors that should be implemented (see above) */
717 RegisterBiosInt32(0x13, NULL
);
718 RegisterBiosInt32(0x14, NULL
);
719 RegisterBiosInt32(0x17, NULL
);
720 RegisterBiosInt32(0x1B, NULL
);
721 RegisterBiosInt32(0x4A, NULL
); // User Alarm Handler
723 /* Relocated services by the BIOS (when needed) */
724 RegisterBiosInt32(0x40, NULL
); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
725 RegisterBiosInt32(0x42, NULL
); // Relocated Default INT 10h Video Services
727 /* Miscellaneous unimplemented vector handlers that should better have a default one */
728 RegisterBiosInt32(0x4B, NULL
); // Virtual DMA Specification Services
729 RegisterBiosInt32(0x5C, NULL
); // NetBIOS
731 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
732 // They don't have any default handler at the moment.
734 /* Some vectors are in fact addresses to tables */
735 ((PULONG
)BaseAddress
)[0x1D] = (ULONG
)NULL
; // Video Parameter Tables
736 ((PULONG
)BaseAddress
)[0x1E] = (ULONG
)NULL
; // Diskette Parameters
737 ((PULONG
)BaseAddress
)[0x1F] = (ULONG
)NULL
; // 8x8 Graphics Font
738 ((PULONG
)BaseAddress
)[0x41] = (ULONG
)NULL
; // Hard Disk 0 Parameter Table Address
739 ((PULONG
)BaseAddress
)[0x43] = (ULONG
)NULL
; // Character Table (EGA, MCGA, VGA)
740 ((PULONG
)BaseAddress
)[0x46] = (ULONG
)NULL
; // Hard Disk 1 Drive Parameter Table Address
741 /* Tables that are always uninitialized */
742 ((PULONG
)BaseAddress
)[0x44] = (ULONG
)NULL
; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
743 ((PULONG
)BaseAddress
)[0x48] = (ULONG
)NULL
; // Cordless Keyboard Translation (PCjr)
744 ((PULONG
)BaseAddress
)[0x49] = (ULONG
)NULL
; // Non-Keyboard Scan-code Translation Table (PCJr)
747 static VOID
InitializeBiosData(VOID
)
751 /* Initialize the BDA contents */
752 RtlZeroMemory(Bda
, sizeof(*Bda
));
753 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
756 * Retrieve the conventional memory size
757 * in kB from CMOS, typically 640 kB.
759 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_LOW
| CMOS_DISABLE_NMI
);
760 Low
= IOReadB(CMOS_DATA_PORT
);
761 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_HIGH
| CMOS_DISABLE_NMI
);
762 High
= IOReadB(CMOS_DATA_PORT
);
763 Bda
->MemorySize
= MAKEWORD(Low
, High
);
766 static VOID
InitializeBiosInfo(VOID
)
768 RtlZeroMemory(Bct
, sizeof(*Bct
));
770 Bct
->Length
= sizeof(*Bct
);
771 Bct
->Model
= BIOS_MODEL
;
772 Bct
->SubModel
= BIOS_SUBMODEL
;
773 Bct
->Revision
= BIOS_REVISION
;
774 Bct
->Feature
[0] = 0x70; // At the moment we don't support "wait for external event (INT 15/AH=41h)", we also don't have any "extended BIOS area allocated (usually at top of RAM)"; see http://www.ctyme.com/intr/rb-1594.htm#Table510
775 Bct
->Feature
[1] = 0x00; // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
776 Bct
->Feature
[2] = 0x00;
777 Bct
->Feature
[3] = 0x00;
778 Bct
->Feature
[4] = 0x00;
784 * The BIOS POST (Power On-Self Test)
788 Bios32Post(LPWORD Stack
)
795 DPRINT("Bios32Post\n");
797 /* Disable interrupts */
800 /* Initialize the stack */
801 // That's what says IBM... (stack at 30:00FF going downwards)
804 setSS(0x0050); // Stack at 50:0400, going downwards
807 /* Set data segment */
811 * Perform early CMOS shutdown status checks
814 /* Read the CMOS shutdown status byte and reset it */
815 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SHUTDOWN_STATUS
| CMOS_DISABLE_NMI
);
816 ShutdownStatus
= IOReadB(CMOS_DATA_PORT
);
817 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SHUTDOWN_STATUS
| CMOS_DISABLE_NMI
);
818 IOWriteB(CMOS_DATA_PORT
, 0x00);
820 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
821 Bda
->SoftReset
, ShutdownStatus
);
823 switch (ShutdownStatus
)
825 /* Shutdown after Memory Tests (unsupported) */
826 case 0x01: case 0x02: case 0x03:
827 /* Shutdown after Protected Mode Tests (unsupported) */
828 case 0x06: case 0x07: case 0x08:
829 /* Shutdown after Block Move Test (unsupported) */
832 DisplayMessage(L
"Unsupported CMOS Shutdown Status value 0x%02X. The VDM is stopping...", ShutdownStatus
);
837 /* Shutdown to Boot Loader */
840 DPRINT1("Fast restart to Bootstrap Loader...\n");
841 goto Quit
; // Reenable interrupts and exit.
844 /* Flush keyboard, issue an EOI... */
847 IOReadB(PS2_DATA_PORT
);
850 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
851 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
857 * ... and far JMP to user-specified location at 0040:0067
858 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
862 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
863 HIWORD(Bda
->ResumeEntryPoint
),
864 LOWORD(Bda
->ResumeEntryPoint
));
866 /* Position execution pointers and return with interrupts disabled */
867 setCS(HIWORD(Bda
->ResumeEntryPoint
));
868 setIP(LOWORD(Bda
->ResumeEntryPoint
));
872 /* Soft reset or unexpected shutdown... */
874 /* ... or other possible shutdown codes: just continue the POST */
880 * FIXME: UNIMPLEMENTED!
881 * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
883 * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
884 * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
887 // FIXME: This is a debug temporary check:
888 // Since NTVDM memory is by default initialized with 0xCC, it is also
889 // the case for the BDA. So at first boot we get SoftReset == 0xCCCC.
890 // After we zero out the BDA and put valid values in it.
891 // If for some reason an app calls the BIOS initialization code,
892 // SoftReset is normally zero (unless the app puts a non-null value in SoftReset)
893 // and we can detect that. With the current state of NTVDM, apps calling
894 // by hand the BIOS init code is a sign of a bug, e.g. see MSD.EXE version 2+.
895 if (Bda
->SoftReset
!= 0xCCCC)
897 DisplayMessage(L
"NTVDM is performing a COLD reboot! The program you are currently testing seems to not behave correctly! The VDM is stopping...");
902 /* Initialize the BDA and the BIOS ROM Information */
903 InitializeBiosData();
904 InitializeBiosInfo();
907 * Initialize IVT and hardware
910 /* Register the BIOS 32-bit Interrupts */
911 InitializeBiosInt32();
913 /* Initialize platform hardware (PIC/PIT chips, ...) */
916 /* Initialize the Keyboard, Video and Mouse BIOS */
917 if (!KbdBios32Initialize() || !VidBios32Initialize() || !MouseBios32Initialize())
925 /* Initialize the Keyboard and Video BIOS */
926 if (!KbdBiosInitialize() || !VidBiosInitialize())
934 ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! /////////////////////
938 Success
= LoadRom("boot.bin", (PVOID
)0xE0000, NULL
);
939 DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success
? "succeeded" : "failed", GetLastError());
942 SearchAndInitRoms(&BiosContext
);
945 * End of the 32-bit POST portion. We then fall back into 16-bit where
946 * the rest of the POST code is executed, typically calling INT 19h
951 /* Enable interrupts */
956 /* PUBLIC FUNCTIONS ***********************************************************/
958 BOOLEAN
Bios32Initialize(VOID
)
961 * Initialize BIOS32 static data
965 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE05B), PostCode
, sizeof(PostCode
));
966 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF0), Bootstrap
, sizeof(Bootstrap
));
968 /* System BIOS Copyright */
969 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE000), BiosCopyright
, sizeof(BiosCopyright
)-1);
971 /* System BIOS Version */
972 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE080), BiosVersion
, sizeof(BiosVersion
)-1);
973 // FIXME: or E061, or E100 ??
975 /* System BIOS Date */
976 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF5), BiosDate
, sizeof(BiosDate
)-1);
978 /* System BIOS Model (same as Bct->Model) */
979 *(PBYTE
)(SEG_OFF_TO_PTR(0xF000, 0xFFFE)) = BIOS_MODEL
;
981 /* Redefine our POST function */
982 RegisterBop(BOP_RESET
, Bios32Post
);
988 VOID
Bios32Cleanup(VOID
)
990 MouseBios32Cleanup();