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)
15 /* PRIVATE VARIABLES **********************************************************/
17 CALLBACK16 BiosContext
;
21 Bochs BIOS, see rombios.h
22 =========================
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
30 #ifndef BIOS_BUILD_DATE
31 # define BIOS_BUILD_DATE "06/23/99"
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)
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
79 * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
80 * for more information.
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!
87 static const BIOS_CONFIG_TABLE BiosConfigTable
=
89 sizeof(BIOS_CONFIG_TABLE
) - sizeof(((BIOS_CONFIG_TABLE
*)0)->Length
), // Length: Number of bytes following
91 BIOS_MODEL
, // BIOS Model
92 BIOS_SUBMODEL
, // BIOS Sub-Model
93 BIOS_REVISION
, // BIOS Revision
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
107 * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
108 * Some programs otherwise look for "COPR. IBM" at F000:E008 .
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";
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);
118 /* 16-bit bootstrap code at F000:FFF0 */
119 static const BYTE Bootstrap
[] =
122 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
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.
129 static const BYTE PostCode
[] =
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
138 /* PRIVATE FUNCTIONS **********************************************************/
140 static VOID
Bios32CharPrint(CHAR Character
)
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.
153 setBL(DEFAULT_ATTRIBUTE
);
154 setBH(Bda
->VideoPage
);
156 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
158 Int32Call(&BiosContext
, BIOS_VIDEO_INTERRUPT
);
160 /* Restore AX and BX */
165 static VOID WINAPI
BiosException(LPWORD Stack
)
167 /* Get the exception number and call the emulator API */
168 BYTE ExceptionNumber
= LOBYTE(Stack
[STACK_INT_NUM
]);
169 EmulatorException(ExceptionNumber
, Stack
);
172 VOID WINAPI
BiosEquipmentService(LPWORD Stack
)
174 /* Return the equipment list */
175 setAX(Bda
->EquipmentList
);
178 VOID WINAPI
BiosGetMemorySize(LPWORD Stack
)
180 /* Return the conventional memory size in kB, typically 640 kB */
181 setAX(Bda
->MemorySize
);
184 static VOID WINAPI
BiosMiscService(LPWORD Stack
)
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
195 /* Return success by default */
197 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
201 /* Wait on External Event */
206 static DWORD StartingCount
;
208 /* Check if this is the first time this BOP occurred */
211 /* Set the starting count */
212 StartingCount
= Bda
->TickCounter
;
215 if (getBL() != 0 && (Bda
->TickCounter
- StartingCount
) >= getBL())
217 /* Timeout expired */
222 if (getAL() & (1 << 4))
224 /* Read from the I/O port */
225 Value
= IOReadB(getDX());
229 /* Read from the memory */
230 Value
= *(LPBYTE
)SEG_OFF_TO_PTR(getES(), getDI());
235 /* Any external event */
238 /* Return if this is not the first time the BOP occurred */
243 /* Compare and return if equal */
246 Return
= Value
== getBH();
250 /* Compare and return if not equal */
253 Return
= Value
!= getBH();
257 /* Test and return if not zero */
260 Return
= (Value
& getBH()) != 0;
264 /* Test and return if zero */
267 Return
= (Value
& getBH()) == 0;
273 DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
279 /* Repeat the BOP if we shouldn't return */
284 /* Keyboard intercept */
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 */
290 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
298 * Interval in microseconds in CX:DX
299 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
300 * for more information.
302 LARGE_INTEGER TimeOut
;
303 TimeOut
.QuadPart
= MAKELONG(getDX(), getCX()) * -10LL;
305 // HACK: For now, use the NT API (time in hundreds of nanoseconds).
306 NtDelayExecution(FALSE
, &TimeOut
);
309 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
313 /* Copy Extended Memory */
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);
323 /* Check for flags */
324 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
325 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
327 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
330 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
335 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
336 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
339 setAX(ERROR_SUCCESS
);
340 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
344 /* Get Extended Memory Size */
350 * Return the (usable) extended memory (after 1 MB)
351 * size in kB from CMOS.
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
));
360 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
364 /* Switch to Protected Mode */
367 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
369 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
373 /* Get Configuration */
376 /* Return the BIOS ROM Configuration Table address in ES:BX */
377 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
381 /* Call successful; clear CF */
383 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
387 /* Return Extended-Bios Data-Area Segment Address (PS) */
390 /* We do not support EBDA yet */
392 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
396 /* Pointing Device BIOS Interface (PS) */
399 // FIXME: Reenable this call when we understand why
400 // our included mouse driver doesn't correctly reeanble
402 // BiosMousePs2Interface(Stack);
407 /* Get CPU Type and Mask Revision */
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.
417 * Fast486 is a 486DX with FPU included,
418 * but old enough to not support CPUID.
422 /* Call successful; clear CF */
424 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
428 /* Get System Memory Map */
433 /* The amount of memory between 1M and 16M, in kilobytes */
434 ULONG Above1M
= (min(MAX_ADDRESS
, 0x01000000) - 0x00100000) >> 10;
436 /* The amount of memory above 16M, in 64K blocks */
437 ULONG Above16M
= (MAX_ADDRESS
> 0x01000000) ? ((MAX_ADDRESS
- 0x01000000) >> 16) : 0;
444 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
446 else if (getAL() == 0x20 && getEDX() == 'SMAP')
448 ULONG Offset
= getEBX();
450 ULONG BytesWritten
= 0;
452 PBIOS_MEMORY_MAP Map
= (PBIOS_MEMORY_MAP
)SEG_OFF_TO_PTR(getES(), getDI());
454 /* Assume the buffer won't be large enough */
455 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
457 while (BytesWritten
< getECX() && (ULONG_PTR
)Map
< (MAX_ADDRESS
- sizeof(BIOS_MEMORY_MAP
)))
459 /* Let's ask our memory controller */
460 if (!MemQueryMemoryZone(Offset
, &Length
, &Hooked
))
462 /* No more memory blocks */
463 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
467 Map
->BaseAddress
= (ULONGLONG
)Offset
;
468 Map
->Length
= (ULONGLONG
)Length
;
469 Map
->Type
= Hooked
? BIOS_MEMORY_RESERVED
: BIOS_MEMORY_AVAILABLE
;
471 /* Go to the next record */
474 BytesWritten
+= sizeof(BIOS_MEMORY_MAP
);
479 setECX(BytesWritten
);
483 DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
492 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
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.
502 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
507 static VOID WINAPI
BiosRomBasic(LPWORD Stack
)
509 PrintMessageAnsi(Bios32CharPrint
, "FATAL: INT18: BOOT FAILURE.");
511 /* ROM Basic is unsupported, display a message to the user */
512 DisplayMessage(L
"NTVDM doesn't support ROM Basic. The VDM is closing.");
519 extern VOID
DosBootsectorInitialize(VOID
);
520 extern VOID WINAPI
BiosDiskService(LPWORD Stack
);
522 static VOID WINAPI
BiosBootstrapLoader(LPWORD Stack
)
526 USHORT AX
, BX
, CX
, DX
, ES
;
534 * Read the boot sequence order from the CMOS, old behaviour AMI-style.
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
543 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SYSOP
);
544 BootOrder
= (IOReadB(CMOS_DATA_PORT
) & 0x20) >> 5;
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.
554 DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder
);
557 * Format of the BootOrder command:
558 * 2 bytes. Each half-byte contains the ID of the drive to boot.
560 * 0x0: 1st Floppy disk
562 * Other, or 0xF: Stop boot sequence.
564 BootOrder
= 0xFF00 | ((1 << (4 * BootOrder
)) & 0xFF);
567 switch (BootOrder
& 0x0F)
569 /* Boot from 1st floppy drive */
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
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());
587 /* Boot from 1st HDD drive */
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
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());
609 /* Go to next drive and invalidate the last half-byte. */
610 BootOrder
= (BootOrder
>> 4) | 0xF000;
614 /* Clear everything, we are going to load DOS32 */
620 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
623 DosBootsectorInitialize();
627 * Jump to 0000:7C00 to boot the OS.
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.
635 Stack
[STACK_CS
] = 0x0000;
636 Stack
[STACK_IP
] = 0x7C00;
638 DPRINT("<-- BiosBootstrapLoader\n");
641 static VOID WINAPI
BiosTimeService(LPWORD Stack
)
645 /* Get System Time */
648 /* Set AL to 1 if midnight had passed, 0 otherwise */
649 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
651 /* Return the tick count in CX:DX */
652 setCX(HIWORD(Bda
->TickCounter
));
653 setDX(LOWORD(Bda
->TickCounter
));
655 /* Reset the midnight flag */
656 Bda
->MidnightPassed
= FALSE
;
661 /* Set System Time */
664 /* Set the tick count to CX:DX */
665 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
667 /* Reset the midnight flag */
668 Bda
->MidnightPassed
= FALSE
;
673 /* Get Real-Time Clock Time */
678 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_HOURS
);
679 setCH(IOReadB(CMOS_DATA_PORT
));
681 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_MINUTES
);
682 setCL(IOReadB(CMOS_DATA_PORT
));
684 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SECONDS
);
685 setDH(IOReadB(CMOS_DATA_PORT
));
687 /* Daylight Savings Time */
688 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_STATUS_B
);
689 StatusB
= IOReadB(CMOS_DATA_PORT
);
690 setDL(StatusB
& 0x01);
693 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
697 // /* Set Real-Time Clock Time */
703 /* Get Real-Time Clock Date */
706 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_CENTURY
);
707 setCH(IOReadB(CMOS_DATA_PORT
));
709 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_YEAR
);
710 setCL(IOReadB(CMOS_DATA_PORT
));
712 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_MONTH
);
713 setDH(IOReadB(CMOS_DATA_PORT
));
715 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_DAY
);
716 setDL(IOReadB(CMOS_DATA_PORT
));
719 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
723 // /* Set Real-Time Clock Date */
731 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
737 static VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
739 /* Increase the system tick count */
745 static VOID
PicSetIRQMask(USHORT off
, USHORT on
)
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
);
753 VOID
EnableHwIRQ(UCHAR hwirq
, EMULATOR_INT32_PROC func
)
757 PicSetIRQMask(1 << hwirq
, 0);
759 vector
= BIOS_PIC_MASTER_INT
+ hwirq
;
761 vector
= BIOS_PIC_SLAVE_INT
+ hwirq
- 8;
763 RegisterBiosInt32(vector
, func
);
767 VOID
PicIRQComplete(BYTE IntNum
)
770 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
772 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
774 /* It was an IRQ from the master PIC */
775 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
777 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
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
);
785 static VOID WINAPI
BiosHandleMasterPicIRQ(LPWORD Stack
)
789 IOWriteB(PIC_MASTER_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
790 IrqNumber
= IOReadB(PIC_MASTER_CMD
);
792 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber
);
794 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
797 static VOID WINAPI
BiosHandleSlavePicIRQ(LPWORD Stack
)
801 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
802 IrqNumber
= IOReadB(PIC_SLAVE_CMD
);
804 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber
);
806 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
810 static VOID WINAPI
BiosTimerIrq(LPWORD Stack
)
813 * Perform the system timer interrupt.
815 * Do not call directly BiosSystemTimerInterrupt(Stack);
816 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
817 * for their purpose...
830 Int32Call(&BiosContext
, BIOS_SYS_TIMER_INTERRUPT
);
843 // BiosSystemTimerInterrupt(Stack);
844 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
848 static VOID
BiosHwSetup(VOID
)
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
);
855 * Set the interrupt vector offsets for each PIC
856 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
858 IOWriteB(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
859 IOWriteB(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
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);
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
);
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);
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);
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);
890 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
891 IOWriteB(PIT_COMMAND_PORT
, 0xB6);
893 IOWriteB(PIT_DATA_PORT(2), 0x97);
894 IOWriteB(PIT_DATA_PORT(2), 0x0A);
897 /* Initialize PS/2 keyboard 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);
908 EnableHwIRQ(0, BiosTimerIrq
);
911 static VOID
InitializeBiosInt32(VOID
)
915 /* Initialize the callback context */
916 InitializeContext(&BiosContext
, BIOS_SEGMENT
, 0x0000);
918 /* Register the default BIOS interrupt vectors */
921 * Zero out all of the IVT (0x00 -- 0xFF). Some applications
922 * indeed expect to have free vectors at the end of the IVT.
924 RtlZeroMemory(BaseAddress
, 0x0100 * sizeof(ULONG
));
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
);
932 /* Initialize the exception interrupt vectors to a default Exception handler */
933 for (i
= 0x00; i
<= 0x07; i
++)
934 RegisterBiosInt32(i
, BiosException
);
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
);
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
);
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
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
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
971 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
972 // They don't have any default handler at the moment.
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)
987 static VOID
InitializeBiosData(VOID
)
991 /* Initialize the BDA contents */
992 RtlZeroMemory(Bda
, sizeof(*Bda
));
995 * Retrieve the basic equipment list from the CMOS
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...
1003 * Retrieve the conventional memory size
1004 * in kB from the CMOS, typically 640 kB.
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
);
1015 * The BIOS POST (Power On-Self Test)
1019 Bios32Post(LPWORD Stack
)
1021 static BOOLEAN FirstBoot
= TRUE
;
1022 BYTE ShutdownStatus
;
1025 * Initialize BIOS/Keyboard/Video RAM dynamic data
1028 DPRINT("Bios32Post\n");
1030 /* Disable interrupts */
1033 /* Set the data segment */
1036 /* Initialize the stack */
1037 // Temporary stack for POST (to be used only before initializing the INT vectors)
1041 // Stack to be used after the initialization of the INT vectors
1042 setSS(0x0000); // Stack at 00:8000, going downwards
1046 * Perform early CMOS shutdown status checks
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);
1055 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
1056 Bda
->SoftReset
, ShutdownStatus
);
1058 switch (ShutdownStatus
)
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) */
1067 DisplayMessage(L
"Unsupported CMOS Shutdown Status value 0x%02X. The VDM will shut down.", ShutdownStatus
);
1068 EmulatorTerminate();
1072 /* Shutdown to Boot Loader */
1075 DPRINT1("Fast restart to Bootstrap Loader...\n");
1076 goto Quit
; // Reenable interrupts and exit.
1079 /* Flush keyboard, issue an EOI... */
1082 IOReadB(PS2_DATA_PORT
);
1085 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1086 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
1092 * ... and far JMP to user-specified location at 0040:0067
1093 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
1097 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
1098 HIWORD(Bda
->ResumeEntryPoint
),
1099 LOWORD(Bda
->ResumeEntryPoint
));
1101 /* Position execution pointers and return with interrupts disabled */
1102 setCS(HIWORD(Bda
->ResumeEntryPoint
));
1103 setIP(LOWORD(Bda
->ResumeEntryPoint
));
1107 /* Soft reset or unexpected shutdown... */
1109 /* ... or other possible shutdown codes: just continue the POST */
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.
1121 switch (Bda
->SoftReset
)
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();
1136 DisplayMessage(L
"NTVDM is performing a WARM reboot! This is not supported at the moment. The VDM will shut down...");
1137 EmulatorTerminate();
1147 /* Initialize the BDA */
1148 InitializeBiosData();
1150 /* Initialize the User Data Area at 0050:XXXX */
1151 RtlZeroMemory(SEG_OFF_TO_PTR(0x50, 0x0000), sizeof(USER_DATA_AREA
));
1155 //////// NOTE: This is more or less bios-specific ////////
1158 * Initialize IVT and hardware
1161 // WriteUnProtectRom(...);
1163 /* Register the BIOS 32-bit Interrupts */
1164 InitializeBiosInt32();
1166 /* Initialize platform hardware (PIC/PIT chips, ...) */
1169 /* Initialize the Keyboard, Video and Mouse BIOS */
1175 // WriteProtectRom(...);
1177 //////// End of more or less bios-specific section ///////
1181 SearchAndInitRoms(&BiosContext
);
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.
1190 /* Enable interrupts */
1195 /* PUBLIC FUNCTIONS ***********************************************************/
1197 BOOLEAN
Bios32Initialize(VOID
)
1200 * Initialize BIOS/Keyboard/Video ROM static data
1203 /* System BIOS Copyright */
1204 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE000), BiosCopyright
, sizeof(BiosCopyright
)-1);
1206 /* System BIOS Version */
1207 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE070), BiosVersion
, sizeof(BiosVersion
)-1);
1209 /* System BIOS Date */
1210 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFF5), BiosDate
, sizeof(BiosDate
)-1);
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
));
1216 /* BIOS ROM Information */
1217 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE6F5), &BiosConfigTable
, sizeof(BiosConfigTable
));
1219 /* System BIOS Model (same as Bct->Model) */
1220 *(PBYTE
)(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFFE)) = BIOS_MODEL
;
1222 /* Initialize the Keyboard and Video BIOS */
1223 if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
1226 EmulatorTerminate();
1230 /* Redefine our POST function */
1231 RegisterBop(BOP_RESET
, Bios32Post
);
1233 WriteProtectRom((PVOID
)TO_LINEAR(BIOS_SEGMENT
, 0x0000),
1234 ROM_AREA_END
- TO_LINEAR(BIOS_SEGMENT
, 0x0000) + 1);
1240 VOID
Bios32Cleanup(VOID
)
1242 DiskBios32Cleanup();
1243 MouseBios32Cleanup();