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)
10 /* INCLUDES *******************************************************************/
14 /* BIOS Version number and Copyright */
15 #include <reactos/buildno.h>
16 #include <reactos/version.h>
22 #include "cpu/cpu.h" // for EMULATOR_FLAG_CF
27 #include <bios/bios.h>
31 #include "dskbios32.h"
32 #include "kbdbios32.h"
33 #include "vidbios32.h"
34 #include "moubios32.h"
38 #include "hardware/cmos.h"
39 #include "hardware/pic.h"
40 #include "hardware/pit.h"
41 #include "hardware/ps2.h"
43 /* PRIVATE VARIABLES **********************************************************/
45 CALLBACK16 BiosContext
;
49 Bochs BIOS, see rombios.h
50 =========================
52 // model byte 0xFC = AT
53 #define SYS_MODEL_ID 0xFC
54 #define SYS_SUBMODEL_ID 0x00
55 #define BIOS_REVISION 1
56 #define BIOS_CONFIG_TABLE 0xe6f5
58 #ifndef BIOS_BUILD_DATE
59 # define BIOS_BUILD_DATE "06/23/99"
62 // 1K of base memory used for Extended Bios Data Area (EBDA)
63 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
64 #define EBDA_SEG 0x9FC0
65 #define EBDA_SIZE 1 // In KiB
66 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
72 ROM BIOS compatibility entry points:
73 ===================================
74 $e05b ; POST Entry Point
75 $e2c3 ; NMI Handler Entry Point
76 $e3fe ; INT 13h Fixed Disk Services Entry Point
77 $e401 ; Fixed Disk Parameter Table
78 $e6f2 ; INT 19h Boot Load Service Entry Point
79 $e6f5 ; Configuration Data Table
80 $e729 ; Baud Rate Generator Table
81 $e739 ; INT 14h Serial Communications Service Entry Point
82 $e82e ; INT 16h Keyboard Service Entry Point
83 $e987 ; INT 09h Keyboard Service Entry Point
84 $ec59 ; INT 13h Diskette Service Entry Point
85 $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
86 $efc7 ; Diskette Controller Parameter Table
87 $efd2 ; INT 17h Printer Service Entry Point
88 $f045 ; INT 10 Functions 0-Fh Entry Point
89 $f065 ; INT 10h Video Support Service Entry Point
90 $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
91 $f841 ; INT 12h Memory Size Service Entry Point
92 $f84d ; INT 11h Equipment List Service Entry Point
93 $f859 ; INT 15h System Services Entry Point
94 $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
95 $fe6e ; INT 1Ah Time-of-day Service Entry Point
96 $fea5 ; INT 08h System Timer ISR Entry Point
97 $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
98 $ff53 ; IRET Instruction for Dummy Interrupt Handler
99 $ff54 ; INT 05h Print Screen Service Entry Point
100 $fff0 ; Power-up Entry Point
101 $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
102 $fffe ; System Model ID
107 * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
108 * for more information.
110 #define BIOS_MODEL 0xFC // PC-AT
111 #define BIOS_SUBMODEL 0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
112 #define BIOS_REVISION 0x00
113 // FIXME: Find a nice PS/2 486 + 487 BIOS combination!
115 static const BIOS_CONFIG_TABLE BiosConfigTable
=
117 sizeof(BIOS_CONFIG_TABLE
) - sizeof(((BIOS_CONFIG_TABLE
*)0)->Length
), // Length: Number of bytes following
119 BIOS_MODEL
, // BIOS Model
120 BIOS_SUBMODEL
, // BIOS Sub-Model
121 BIOS_REVISION
, // BIOS Revision
125 0x78, // At the moment we don't have any Extended BIOS Area; see http://www.ctyme.com/intr/rb-1594.htm#Table510
126 0x00, // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
127 0x10, // Bit 4: POST supports ROM-to-RAM enable/disable
135 * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
136 * Some programs otherwise look for "COPR. IBM" at F000:E008 .
138 static const CHAR BiosCopyright
[] = "0000000 NTVDM IBM COMPATIBLE 486 BIOS COPYRIGHT (C) ReactOS Team 1996-"COPYRIGHT_YEAR
;
139 static const CHAR BiosVersion
[] = "ReactOS NTVDM 32-bit BIOS Version "KERNEL_VERSION_STR
"\0"
140 "BIOS32 Version "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")";
141 static const CHAR BiosDate
[] = "06/17/13";
143 C_ASSERT(sizeof(BiosCopyright
)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
144 C_ASSERT(sizeof(BiosDate
)-1 == 0x08);
146 /* 16-bit bootstrap code at F000:FFF0 */
147 static const BYTE Bootstrap
[] =
150 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
154 * POST code at F000:E05B. All the POST is done in 32 bit
155 * and only at the end it calls the bootstrap interrupt.
157 static const BYTE PostCode
[] =
159 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_RESET
, // Call BIOS POST
160 0xCD, BIOS_BOOTSTRAP_LOADER
, // INT 0x19
161 0xCD, BIOS_ROM_BASIC
, // INT 0x18
162 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_UNSIMULATE
166 /* PRIVATE FUNCTIONS **********************************************************/
168 static VOID
BiosCharPrint(CHAR Character
)
175 * Set the parameters:
176 * AL contains the character to print,
177 * BL contains the character attribute,
178 * BH contains the video page to use.
181 setBL(DEFAULT_ATTRIBUTE
);
182 setBH(Bda
->VideoPage
);
184 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
186 Int32Call(&BiosContext
, BIOS_VIDEO_INTERRUPT
);
188 /* Restore AX and BX */
193 static VOID WINAPI
BiosException(LPWORD Stack
)
195 /* Get the exception number and call the emulator API */
196 BYTE ExceptionNumber
= LOBYTE(Stack
[STACK_INT_NUM
]);
197 EmulatorException(ExceptionNumber
, Stack
);
200 VOID WINAPI
BiosEquipmentService(LPWORD Stack
)
202 /* Return the equipment list */
203 setAX(Bda
->EquipmentList
);
206 VOID WINAPI
BiosGetMemorySize(LPWORD Stack
)
208 /* Return the conventional memory size in kB, typically 640 kB */
209 setAX(Bda
->MemorySize
);
212 static VOID WINAPI
BiosMiscService(LPWORD Stack
)
216 /* OS Hooks for Multitasking */
217 case 0x80: // Device Open
218 case 0x81: // Device Close
219 case 0x82: // Program Termination
220 case 0x90: // Device Busy
221 case 0x91: // Device POST
223 /* Return success by default */
225 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
229 /* Wait on External Event */
234 static DWORD StartingCount
;
236 /* Check if this is the first time this BOP occurred */
239 /* Set the starting count */
240 StartingCount
= Bda
->TickCounter
;
243 if (getBL() != 0 && (Bda
->TickCounter
- StartingCount
) >= getBL())
245 /* Timeout expired */
250 if (getAL() & (1 << 4))
252 /* Read from the I/O port */
253 Value
= IOReadB(getDX());
257 /* Read from the memory */
258 Value
= *(LPBYTE
)SEG_OFF_TO_PTR(getES(), getDI());
263 /* Any external event */
266 /* Return if this is not the first time the BOP occurred */
271 /* Compare and return if equal */
274 Return
= Value
== getBH();
278 /* Compare and return if not equal */
281 Return
= Value
!= getBH();
285 /* Test and return if not zero */
288 Return
= (Value
& getBH()) != 0;
292 /* Test and return if zero */
295 Return
= (Value
& getBH()) == 0;
301 DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
307 /* Repeat the BOP if we shouldn't return */
312 /* Keyboard intercept */
315 /* CF should be set but let's just set it again just in case */
316 /* Do not modify AL (the hardware scan code), but set CF to continue processing */
318 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
326 * Interval in microseconds in CX:DX
327 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
328 * for more information.
331 static ULONG CompletionTime
= 0;
333 /* Check if we're already looping */
336 if (GetTickCount() >= CompletionTime
)
341 /* Clear the CF on the stack too */
342 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
347 /* Set the CF on the stack */
348 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
350 /* Set the completion time and start looping */
351 CompletionTime
= GetTickCount() + (MAKELONG(getDX(), getCX()) / 1000);
358 /* Copy Extended Memory */
361 DWORD Count
= (DWORD
)getCX() * 2;
362 PFAST486_GDT_ENTRY Gdt
= (PFAST486_GDT_ENTRY
)SEG_OFF_TO_PTR(getES(), getSI());
363 DWORD SourceBase
= Gdt
[2].Base
+ (Gdt
[2].BaseMid
<< 16) + (Gdt
[2].BaseHigh
<< 24);
364 DWORD SourceLimit
= Gdt
[2].Limit
+ (Gdt
[2].LimitHigh
<< 16);
365 DWORD DestBase
= Gdt
[3].Base
+ (Gdt
[3].BaseMid
<< 16) + (Gdt
[3].BaseHigh
<< 24);
366 DWORD DestLimit
= Gdt
[3].Limit
+ (Gdt
[3].LimitHigh
<< 16);
368 /* Check for flags */
369 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
370 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
372 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
375 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
380 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
381 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
384 setAX(ERROR_SUCCESS
);
385 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
389 /* Get Extended Memory Size */
395 * Return the (usable) extended memory (after 1 MB)
396 * size in kB from CMOS.
398 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_LOW
| CMOS_DISABLE_NMI
);
399 Low
= IOReadB(CMOS_DATA_PORT
);
400 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH
| CMOS_DISABLE_NMI
);
401 High
= IOReadB(CMOS_DATA_PORT
);
402 setAX(MAKEWORD(Low
, High
));
405 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
409 /* Switch to Protected Mode */
412 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
414 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
418 /* Get Configuration */
421 /* Return the BIOS ROM Configuration Table address in ES:BX */
422 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
426 /* Call successful; clear CF */
428 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
432 /* Return Extended-Bios Data-Area Segment Address (PS) */
435 /* We do not support EBDA yet */
437 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
441 /* Pointing Device BIOS Interface (PS) */
444 // FIXME: Reenable this call when we understand why
445 // our included mouse driver doesn't correctly reeanble
447 // BiosMousePs2Interface(Stack);
452 /* Get CPU Type and Mask Revision */
456 * We can see this function as a CPUID replacement.
457 * See Ralf Brown: http://www.ctyme.com/intr/rb-1613.htm
458 * for more information.
462 * Fast486 is a 486DX with FPU included,
463 * but old enough to not support CPUID.
467 /* Call successful; clear CF */
469 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
473 /* Get System Memory Map */
478 /* The amount of memory between 1M and 16M, in kilobytes */
479 ULONG Above1M
= (min(MAX_ADDRESS
, 0x01000000) - 0x00100000) >> 10;
481 /* The amount of memory above 16M, in 64K blocks */
482 ULONG Above16M
= (MAX_ADDRESS
> 0x01000000) ? ((MAX_ADDRESS
- 0x01000000) >> 16) : 0;
489 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
491 else if (getAL() == 0x20 && getEDX() == 'SMAP')
493 ULONG Offset
= getEBX();
495 ULONG BytesWritten
= 0;
497 PBIOS_MEMORY_MAP Map
= (PBIOS_MEMORY_MAP
)SEG_OFF_TO_PTR(getES(), getDI());
499 /* Assume the buffer won't be large enough */
500 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
502 while (BytesWritten
< getECX() && (ULONG_PTR
)Map
< (MAX_ADDRESS
- sizeof(BIOS_MEMORY_MAP
)))
504 /* Let's ask our memory controller */
505 if (!MemQueryMemoryZone(Offset
, &Length
, &Hooked
))
507 /* No more memory blocks */
508 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
512 Map
->BaseAddress
= (ULONGLONG
)Offset
;
513 Map
->Length
= (ULONGLONG
)Length
;
514 Map
->Type
= Hooked
? BIOS_MEMORY_RESERVED
: BIOS_MEMORY_AVAILABLE
;
516 /* Go to the next record */
519 BytesWritten
+= sizeof(BIOS_MEMORY_MAP
);
524 setECX(BytesWritten
);
528 DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
537 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
541 * The original signification of the error code 0x86 is that
542 * no PC Cassette is present. The CF is also set in this case.
543 * To keep backward compatibility, newer BIOSes use this value
544 * to indicate an unimplemented call in INT 15h.
547 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
552 static VOID WINAPI
BiosRomBasic(LPWORD Stack
)
554 PrintMessageAnsi(BiosCharPrint
, "FATAL: INT18: BOOT FAILURE.");
556 /* ROM Basic is unsupported, display a message to the user */
557 DisplayMessage(L
"NTVDM doesn't support ROM Basic. The VDM is closing.");
564 extern VOID
DosBootsectorInitialize(VOID
);
565 extern VOID WINAPI
BiosDiskService(LPWORD Stack
);
567 static VOID WINAPI
BiosBootstrapLoader(LPWORD Stack
)
571 USHORT AX
, BX
, CX
, DX
, ES
;
579 * Read the boot sequence order from the CMOS, old behaviour AMI-style.
581 * For more information, see:
582 * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/orgs.asm
583 * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/boot.c
584 * http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/cmos.cc
585 * https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html
586 * http://www.bioscentral.com/misc/cmosmap.htm
588 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SYSOP
);
589 BootOrder
= (IOReadB(CMOS_DATA_PORT
) & 0x20) >> 5;
593 * 0: Hard Disk, then Floppy Disk
594 * 1: Floppy Disk, then Hard Disk
595 * In all cases, if booting from those devices failed,
596 * ROM DOS-32 is started. If it fails, INT 18h is called.
599 DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder
);
602 * Format of the BootOrder command:
603 * 2 bytes. Each half-byte contains the ID of the drive to boot.
605 * 0x0: 1st Floppy disk
607 * Other, or 0xF: Stop boot sequence.
609 BootOrder
= 0xFF00 | ((1 << (4 * BootOrder
)) & 0xFF);
612 switch (BootOrder
& 0x0F)
614 /* Boot from 1st floppy drive */
617 setAH(0x02); // Read sectors
618 setAL(0x01); // Number of sectors
619 setDH(0x00); // Head 0
620 setCH(0x00); // Cylinder 0
621 setCL(0x01); // Sector 1
622 setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
623 setES(0x0000); // Write data in 0000:7C00
625 BiosDiskService(Stack
);
626 if (!(Stack
[STACK_FLAGS
] & EMULATOR_FLAG_CF
)) goto Quit
;
627 DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
632 /* Boot from 1st HDD drive */
635 setAH(0x02); // Read sectors
636 setAL(0x01); // Number of sectors
637 setDH(0x00); // Head 0
638 setCH(0x00); // Cylinder 0
639 setCL(0x01); // Sector 1
640 setDL(0x80); // First HDD drive (used by loader code, so should not be cleared)
641 setES(0x0000); // Write data in 0000:7C00
643 BiosDiskService(Stack
);
644 if (!(Stack
[STACK_FLAGS
] & EMULATOR_FLAG_CF
)) goto Quit
;
645 DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
654 /* Go to next drive and invalidate the last half-byte. */
655 BootOrder
= (BootOrder
>> 4) | 0xF000;
659 /* Clear everything, we are going to load DOS32 */
665 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
668 DosBootsectorInitialize();
672 * Jump to 0000:7C00 to boot the OS.
674 * Since we are called via the INT32 mechanism, we need to correctly set
675 * CS:IP, not by changing the current one (otherwise the interrupt could
676 * not be clean up and return properly), but by changing the CS:IP in the
677 * stack, so that when the interrupt returns, the modified CS:IP is popped
678 * off the stack and the CPU is correctly repositioned.
680 Stack
[STACK_CS
] = 0x0000;
681 Stack
[STACK_IP
] = 0x7C00;
683 DPRINT("<-- BiosBootstrapLoader\n");
686 static VOID WINAPI
BiosTimeService(LPWORD Stack
)
690 /* Get System Time */
693 /* Set AL to 1 if midnight had passed, 0 otherwise */
694 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
696 /* Return the tick count in CX:DX */
697 setCX(HIWORD(Bda
->TickCounter
));
698 setDX(LOWORD(Bda
->TickCounter
));
700 /* Reset the midnight flag */
701 Bda
->MidnightPassed
= FALSE
;
706 /* Set System Time */
709 /* Set the tick count to CX:DX */
710 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
712 /* Reset the midnight flag */
713 Bda
->MidnightPassed
= FALSE
;
718 /* Get Real-Time Clock Time */
723 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_HOURS
);
724 setCH(IOReadB(CMOS_DATA_PORT
));
726 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_MINUTES
);
727 setCL(IOReadB(CMOS_DATA_PORT
));
729 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SECONDS
);
730 setDH(IOReadB(CMOS_DATA_PORT
));
732 /* Daylight Savings Time */
733 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_STATUS_B
);
734 StatusB
= IOReadB(CMOS_DATA_PORT
);
735 setDL(StatusB
& 0x01);
738 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
742 // /* Set Real-Time Clock Time */
748 /* Get Real-Time Clock Date */
751 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_CENTURY
);
752 setCH(IOReadB(CMOS_DATA_PORT
));
754 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_YEAR
);
755 setCL(IOReadB(CMOS_DATA_PORT
));
757 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_MONTH
);
758 setDH(IOReadB(CMOS_DATA_PORT
));
760 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_DAY
);
761 setDL(IOReadB(CMOS_DATA_PORT
));
764 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
768 // /* Set Real-Time Clock Date */
776 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
782 static VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
784 /* Increase the system tick count */
790 static VOID
PicSetIRQMask(USHORT off
, USHORT on
)
792 UCHAR pic1off
= off
, pic1on
= on
, pic2off
= off
>>8, pic2on
= on
>>8;
793 IOWriteB(PIC_MASTER_DATA
, (IOReadB(PIC_MASTER_DATA
) & ~pic1off
) | pic1on
);
794 IOWriteB(PIC_SLAVE_DATA
, (IOReadB(PIC_SLAVE_DATA
) & ~pic2off
) | pic2on
);
798 VOID
EnableHwIRQ(UCHAR hwirq
, EMULATOR_INT32_PROC func
)
802 PicSetIRQMask(1 << hwirq
, 0);
804 vector
= BIOS_PIC_MASTER_INT
+ hwirq
;
806 vector
= BIOS_PIC_SLAVE_INT
+ hwirq
- 8;
808 RegisterBiosInt32(vector
, func
);
812 VOID
PicIRQComplete(BYTE IntNum
)
815 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
817 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
819 /* It was an IRQ from the master PIC */
820 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
822 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
824 /* It was an IRQ from the slave PIC */
825 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
826 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
830 static VOID WINAPI
BiosHandleMasterPicIRQ(LPWORD Stack
)
834 IOWriteB(PIC_MASTER_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
835 IrqNumber
= IOReadB(PIC_MASTER_CMD
);
837 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber
);
839 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
842 static VOID WINAPI
BiosHandleSlavePicIRQ(LPWORD Stack
)
846 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
847 IrqNumber
= IOReadB(PIC_SLAVE_CMD
);
849 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber
);
851 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
855 static VOID WINAPI
BiosTimerIrq(LPWORD Stack
)
858 * Perform the system timer interrupt.
860 * Do not call directly BiosSystemTimerInterrupt(Stack);
861 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
862 * for their purpose...
875 Int32Call(&BiosContext
, BIOS_SYS_TIMER_INTERRUPT
);
888 // BiosSystemTimerInterrupt(Stack);
889 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
893 static VOID
BiosHwSetup(VOID
)
895 /* Initialize the master and the slave PICs (cascade mode) */
896 IOWriteB(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
897 IOWriteB(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
900 * Set the interrupt vector offsets for each PIC
901 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
903 IOWriteB(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
904 IOWriteB(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
906 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
907 IOWriteB(PIC_MASTER_DATA
, 1 << 2);
908 /* Tell the slave PIC its cascade identity */
909 IOWriteB(PIC_SLAVE_DATA
, 2);
911 /* Make sure both PICs are in 8086 mode */
912 IOWriteB(PIC_MASTER_DATA
, PIC_ICW4_8086
);
913 IOWriteB(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
915 /* Clear the masks for both PICs */
916 // IOWriteB(PIC_MASTER_DATA, 0x00);
917 // IOWriteB(PIC_SLAVE_DATA , 0x00);
918 /* Disable all IRQs */
919 IOWriteB(PIC_MASTER_DATA
, 0xFF);
920 IOWriteB(PIC_SLAVE_DATA
, 0xFF);
923 /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
924 // NOTE: Some BIOSes set it to Mode 3 instead.
925 IOWriteB(PIT_COMMAND_PORT
, 0x34);
926 // 18.2Hz refresh rate
927 IOWriteB(PIT_DATA_PORT(0), 0x00);
928 IOWriteB(PIT_DATA_PORT(0), 0x00);
930 /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
931 IOWriteB(PIT_COMMAND_PORT
, 0x54);
932 // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
933 IOWriteB(PIT_DATA_PORT(1), 18);
935 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
936 IOWriteB(PIT_COMMAND_PORT
, 0xB6);
938 IOWriteB(PIT_DATA_PORT(2), 0x97);
939 IOWriteB(PIT_DATA_PORT(2), 0x0A);
942 /* Initialize PS/2 keyboard port */
944 IOWriteB(PS2_CONTROL_PORT
, 0xAE);
945 // Port interrupts and clock enabled,
946 // enable keyboard scancode translation.
947 // POST passed, force keyboard unlocking.
948 IOWriteB(PS2_CONTROL_PORT
, 0x60);
949 IOWriteB(PS2_DATA_PORT
, 0x6D);
950 // Enable data reporting
951 IOWriteB(PS2_DATA_PORT
, 0xF4);
953 EnableHwIRQ(0, BiosTimerIrq
);
956 static VOID
InitializeBiosInt32(VOID
)
960 /* Initialize the callback context */
961 InitializeContext(&BiosContext
, BIOS_SEGMENT
, 0x0000);
963 /* Register the default BIOS interrupt vectors */
966 * Zero out all of the IVT (0x00 -- 0xFF). Some applications
967 * indeed expect to have free vectors at the end of the IVT.
969 RtlZeroMemory(BaseAddress
, 0x0100 * sizeof(ULONG
));
971 #if defined(ADVANCED_DEBUGGING) && (ADVANCED_DEBUGGING_LEVEL >= 3)
972 // Initialize all the interrupt vectors to the default one.
973 for (i
= 0x00; i
<= 0xFF; i
++)
974 RegisterBiosInt32(i
, NULL
);
977 /* Initialize the exception interrupt vectors to a default Exception handler */
978 for (i
= 0x00; i
<= 0x07; i
++)
979 RegisterBiosInt32(i
, BiosException
);
981 /* Initialize HW interrupt vectors to a default HW handler */
982 for (i
= BIOS_PIC_MASTER_INT
; i
< BIOS_PIC_MASTER_INT
+ 8; i
++) // 0x08 -- 0x0F
983 RegisterBiosInt32(i
, BiosHandleMasterPicIRQ
);
984 for (i
= BIOS_PIC_SLAVE_INT
; i
< BIOS_PIC_SLAVE_INT
+ 8; i
++) // 0x70 -- 0x77
985 RegisterBiosInt32(i
, BiosHandleSlavePicIRQ
);
987 /* Initialize software vector handlers */
988 // BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
989 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
990 RegisterBiosInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
991 // BIOS_DISK_INTERRUPT : 0x13 (dskbios32.c)
992 // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
993 RegisterBiosInt32(BIOS_MISC_INTERRUPT
, BiosMiscService
);
994 // BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
995 // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
996 RegisterBiosInt32(BIOS_ROM_BASIC
, BiosRomBasic
);
997 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER
, BiosBootstrapLoader
);
998 RegisterBiosInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
999 // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
1000 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
1002 /* Vectors that should be implemented (see above) */
1003 RegisterBiosInt32(0x14, NULL
);
1004 RegisterBiosInt32(0x17, NULL
);
1005 RegisterBiosInt32(0x1B, NULL
);
1006 RegisterBiosInt32(0x4A, NULL
); // User Alarm Handler
1008 /* Relocated services by the BIOS (when needed) */
1009 RegisterBiosInt32(0x40, NULL
); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
1010 RegisterBiosInt32(0x42, NULL
); // Relocated Default INT 10h Video Services
1012 /* Miscellaneous unimplemented vector handlers that should better have a default one */
1013 RegisterBiosInt32(0x4B, NULL
); // Virtual DMA Specification Services
1014 RegisterBiosInt32(0x5C, NULL
); // NetBIOS
1016 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
1017 // They don't have any default handler at the moment.
1019 /* Some vectors are in fact addresses to tables */
1020 ((PULONG
)BaseAddress
)[0x1D] = (ULONG
)NULL
; // Video Parameter Tables
1021 ((PULONG
)BaseAddress
)[0x1E] = (ULONG
)NULL
; // Diskette Parameters
1022 ((PULONG
)BaseAddress
)[0x1F] = (ULONG
)NULL
; // 8x8 Graphics Font
1023 ((PULONG
)BaseAddress
)[0x41] = (ULONG
)NULL
; // Hard Disk 0 Parameter Table Address
1024 ((PULONG
)BaseAddress
)[0x43] = (ULONG
)NULL
; // Character Table (EGA, MCGA, VGA)
1025 ((PULONG
)BaseAddress
)[0x46] = (ULONG
)NULL
; // Hard Disk 1 Drive Parameter Table Address
1026 /* Tables that are always uninitialized */
1027 ((PULONG
)BaseAddress
)[0x44] = (ULONG
)NULL
; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
1028 ((PULONG
)BaseAddress
)[0x48] = (ULONG
)NULL
; // Cordless Keyboard Translation (PCjr)
1029 ((PULONG
)BaseAddress
)[0x49] = (ULONG
)NULL
; // Non-Keyboard Scan-code Translation Table (PCJr)
1032 static VOID
InitializeBiosData(VOID
)
1036 /* Initialize the BDA contents */
1037 RtlZeroMemory(Bda
, sizeof(*Bda
));
1040 * Retrieve the basic equipment list from the CMOS
1042 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_EQUIPMENT_LIST
| CMOS_DISABLE_NMI
);
1043 Bda
->EquipmentList
= IOReadB(CMOS_DATA_PORT
);
1044 // TODO: Update it if required.
1045 Bda
->EquipmentList
&= 0x00FF; // High byte cleared for now...
1048 * Retrieve the conventional memory size
1049 * in kB from the CMOS, typically 640 kB.
1051 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_LOW
| CMOS_DISABLE_NMI
);
1052 Low
= IOReadB(CMOS_DATA_PORT
);
1053 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_HIGH
| CMOS_DISABLE_NMI
);
1054 High
= IOReadB(CMOS_DATA_PORT
);
1055 Bda
->MemorySize
= MAKEWORD(Low
, High
);
1060 * The BIOS POST (Power On-Self Test)
1064 Bios32Post(LPWORD Stack
)
1066 static BOOLEAN FirstBoot
= TRUE
;
1067 BYTE ShutdownStatus
;
1070 * Initialize BIOS/Keyboard/Video RAM dynamic data
1073 DPRINT("Bios32Post\n");
1075 /* Disable interrupts */
1078 /* Set the data segment */
1081 /* Initialize the stack */
1082 // Temporary stack for POST (to be used only before initializing the INT vectors)
1086 // Stack to be used after the initialization of the INT vectors
1087 setSS(0x0000); // Stack at 00:8000, going downwards
1091 * Perform early CMOS shutdown status checks
1094 /* Read the CMOS shutdown status byte and reset it */
1095 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SHUTDOWN_STATUS
| CMOS_DISABLE_NMI
);
1096 ShutdownStatus
= IOReadB(CMOS_DATA_PORT
);
1097 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SHUTDOWN_STATUS
| CMOS_DISABLE_NMI
);
1098 IOWriteB(CMOS_DATA_PORT
, 0x00);
1100 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
1101 Bda
->SoftReset
, ShutdownStatus
);
1103 switch (ShutdownStatus
)
1105 /* Shutdown after Memory Tests (unsupported) */
1106 case 0x01: case 0x02: case 0x03:
1107 /* Shutdown after Protected Mode Tests (unsupported) */
1108 case 0x06: case 0x07: case 0x08:
1109 /* Shutdown after Block Move Test (unsupported) */
1112 DisplayMessage(L
"Unsupported CMOS Shutdown Status value 0x%02X. The VDM will shut down.", ShutdownStatus
);
1113 EmulatorTerminate();
1117 /* Shutdown to Boot Loader */
1120 DPRINT1("Fast restart to Bootstrap Loader...\n");
1121 goto Quit
; // Reenable interrupts and exit.
1124 /* Flush keyboard, issue an EOI... */
1127 IOReadB(PS2_DATA_PORT
);
1130 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1131 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
1137 * ... and far JMP to user-specified location at 0040:0067
1138 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
1142 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
1143 HIWORD(Bda
->ResumeEntryPoint
),
1144 LOWORD(Bda
->ResumeEntryPoint
));
1146 /* Position execution pointers and return with interrupts disabled */
1147 setCS(HIWORD(Bda
->ResumeEntryPoint
));
1148 setIP(LOWORD(Bda
->ResumeEntryPoint
));
1152 /* Soft reset or unexpected shutdown... */
1154 /* ... or other possible shutdown codes: just continue the POST */
1160 * FIXME: UNIMPLEMENTED!
1161 * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
1162 * following actions:
1163 * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
1164 * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
1166 switch (Bda
->SoftReset
)
1172 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...");
1173 EmulatorTerminate();
1181 DisplayMessage(L
"NTVDM is performing a WARM reboot! This is not supported at the moment. The VDM will shut down...");
1182 EmulatorTerminate();
1192 /* Initialize the BDA */
1193 InitializeBiosData();
1195 /* Initialize the User Data Area at 0050:XXXX */
1196 RtlZeroMemory(SEG_OFF_TO_PTR(0x50, 0x0000), sizeof(USER_DATA_AREA
));
1200 //////// NOTE: This is more or less bios-specific ////////
1203 * Initialize IVT and hardware
1206 // WriteUnProtectRom(...);
1208 /* Register the BIOS 32-bit Interrupts */
1209 InitializeBiosInt32();
1211 /* Initialize platform hardware (PIC/PIT chips, ...) */
1214 /* Initialize the Keyboard, Video and Mouse BIOS */
1220 // WriteProtectRom(...);
1222 //////// End of more or less bios-specific section ///////
1226 SearchAndInitRoms(&BiosContext
);
1229 * End of the 32-bit POST portion. We then fall back into 16-bit where
1230 * the rest of the POST code is executed, typically calling INT 19h
1231 * to boot up the OS.
1235 /* Enable interrupts */
1240 /* PUBLIC FUNCTIONS ***********************************************************/
1242 BOOLEAN
Bios32Initialize(VOID
)
1245 * Initialize BIOS/Keyboard/Video ROM static data
1248 /* System BIOS Copyright */
1249 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE000), BiosCopyright
, sizeof(BiosCopyright
)-1);
1251 /* System BIOS Version */
1252 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE070), BiosVersion
, sizeof(BiosVersion
)-1);
1254 /* System BIOS Date */
1255 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFF5), BiosDate
, sizeof(BiosDate
)-1);
1257 /* Bootstrap code */
1258 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE05B), PostCode
, sizeof(PostCode
));
1259 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFF0), Bootstrap
, sizeof(Bootstrap
));
1261 /* BIOS ROM Information */
1262 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE6F5), &BiosConfigTable
, sizeof(BiosConfigTable
));
1264 /* System BIOS Model (same as Bct->Model) */
1265 *(PBYTE
)(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFFE)) = BIOS_MODEL
;
1267 /* Initialize the Keyboard and Video BIOS */
1268 if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
1271 EmulatorTerminate();
1275 /* Redefine our POST function */
1276 RegisterBop(BOP_RESET
, Bios32Post
);
1278 WriteProtectRom((PVOID
)TO_LINEAR(BIOS_SEGMENT
, 0x0000),
1279 ROM_AREA_END
- TO_LINEAR(BIOS_SEGMENT
, 0x0000) + 1);
1285 VOID
Bios32Cleanup(VOID
)
1287 DiskBios32Cleanup();
1288 MouseBios32Cleanup();