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.
330 LARGE_INTEGER TimeOut
;
331 TimeOut
.QuadPart
= MAKELONG(getDX(), getCX()) * -10LL;
333 // HACK: For now, use the NT API (time in hundreds of nanoseconds).
334 NtDelayExecution(FALSE
, &TimeOut
);
337 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
341 /* Copy Extended Memory */
344 DWORD Count
= (DWORD
)getCX() * 2;
345 PFAST486_GDT_ENTRY Gdt
= (PFAST486_GDT_ENTRY
)SEG_OFF_TO_PTR(getES(), getSI());
346 DWORD SourceBase
= Gdt
[2].Base
+ (Gdt
[2].BaseMid
<< 16) + (Gdt
[2].BaseHigh
<< 24);
347 DWORD SourceLimit
= Gdt
[2].Limit
+ (Gdt
[2].LimitHigh
<< 16);
348 DWORD DestBase
= Gdt
[3].Base
+ (Gdt
[3].BaseMid
<< 16) + (Gdt
[3].BaseHigh
<< 24);
349 DWORD DestLimit
= Gdt
[3].Limit
+ (Gdt
[3].LimitHigh
<< 16);
351 /* Check for flags */
352 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
353 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
355 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
358 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
363 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
364 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
367 setAX(ERROR_SUCCESS
);
368 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
372 /* Get Extended Memory Size */
378 * Return the (usable) extended memory (after 1 MB)
379 * size in kB from CMOS.
381 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_LOW
| CMOS_DISABLE_NMI
);
382 Low
= IOReadB(CMOS_DATA_PORT
);
383 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH
| CMOS_DISABLE_NMI
);
384 High
= IOReadB(CMOS_DATA_PORT
);
385 setAX(MAKEWORD(Low
, High
));
388 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
392 /* Switch to Protected Mode */
395 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
397 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
401 /* Get Configuration */
404 /* Return the BIOS ROM Configuration Table address in ES:BX */
405 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
409 /* Call successful; clear CF */
411 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
415 /* Return Extended-Bios Data-Area Segment Address (PS) */
418 /* We do not support EBDA yet */
420 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
424 /* Pointing Device BIOS Interface (PS) */
427 // FIXME: Reenable this call when we understand why
428 // our included mouse driver doesn't correctly reeanble
430 // BiosMousePs2Interface(Stack);
435 /* Get CPU Type and Mask Revision */
439 * We can see this function as a CPUID replacement.
440 * See Ralf Brown: http://www.ctyme.com/intr/rb-1613.htm
441 * for more information.
445 * Fast486 is a 486DX with FPU included,
446 * but old enough to not support CPUID.
450 /* Call successful; clear CF */
452 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
456 /* Get System Memory Map */
461 /* The amount of memory between 1M and 16M, in kilobytes */
462 ULONG Above1M
= (min(MAX_ADDRESS
, 0x01000000) - 0x00100000) >> 10;
464 /* The amount of memory above 16M, in 64K blocks */
465 ULONG Above16M
= (MAX_ADDRESS
> 0x01000000) ? ((MAX_ADDRESS
- 0x01000000) >> 16) : 0;
472 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
474 else if (getAL() == 0x20 && getEDX() == 'SMAP')
476 ULONG Offset
= getEBX();
478 ULONG BytesWritten
= 0;
480 PBIOS_MEMORY_MAP Map
= (PBIOS_MEMORY_MAP
)SEG_OFF_TO_PTR(getES(), getDI());
482 /* Assume the buffer won't be large enough */
483 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
485 while (BytesWritten
< getECX() && (ULONG_PTR
)Map
< (MAX_ADDRESS
- sizeof(BIOS_MEMORY_MAP
)))
487 /* Let's ask our memory controller */
488 if (!MemQueryMemoryZone(Offset
, &Length
, &Hooked
))
490 /* No more memory blocks */
491 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
495 Map
->BaseAddress
= (ULONGLONG
)Offset
;
496 Map
->Length
= (ULONGLONG
)Length
;
497 Map
->Type
= Hooked
? BIOS_MEMORY_RESERVED
: BIOS_MEMORY_AVAILABLE
;
499 /* Go to the next record */
502 BytesWritten
+= sizeof(BIOS_MEMORY_MAP
);
507 setECX(BytesWritten
);
511 DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
520 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
524 * The original signification of the error code 0x86 is that
525 * no PC Cassette is present. The CF is also set in this case.
526 * To keep backward compatibility, newer BIOSes use this value
527 * to indicate an unimplemented call in INT 15h.
530 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
535 static VOID WINAPI
BiosRomBasic(LPWORD Stack
)
537 PrintMessageAnsi(BiosCharPrint
, "FATAL: INT18: BOOT FAILURE.");
539 /* ROM Basic is unsupported, display a message to the user */
540 DisplayMessage(L
"NTVDM doesn't support ROM Basic. The VDM is closing.");
547 extern VOID
DosBootsectorInitialize(VOID
);
548 extern VOID WINAPI
BiosDiskService(LPWORD Stack
);
550 static VOID WINAPI
BiosBootstrapLoader(LPWORD Stack
)
554 USHORT AX
, BX
, CX
, DX
, ES
;
562 * Read the boot sequence order from the CMOS, old behaviour AMI-style.
564 * For more information, see:
565 * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/orgs.asm
566 * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/boot.c
567 * http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/cmos.cc
568 * https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html
569 * http://www.bioscentral.com/misc/cmosmap.htm
571 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SYSOP
);
572 BootOrder
= (IOReadB(CMOS_DATA_PORT
) & 0x20) >> 5;
576 * 0: Hard Disk, then Floppy Disk
577 * 1: Floppy Disk, then Hard Disk
578 * In all cases, if booting from those devices failed,
579 * ROM DOS-32 is started. If it fails, INT 18h is called.
582 DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder
);
585 * Format of the BootOrder command:
586 * 2 bytes. Each half-byte contains the ID of the drive to boot.
588 * 0x0: 1st Floppy disk
590 * Other, or 0xF: Stop boot sequence.
592 BootOrder
= 0xFF00 | ((1 << (4 * BootOrder
)) & 0xFF);
595 switch (BootOrder
& 0x0F)
597 /* Boot from 1st floppy drive */
600 setAH(0x02); // Read sectors
601 setAL(0x01); // Number of sectors
602 setDH(0x00); // Head 0
603 setCH(0x00); // Cylinder 0
604 setCL(0x01); // Sector 1
605 setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
606 setES(0x0000); // Write data in 0000:7C00
608 BiosDiskService(Stack
);
609 if (!(Stack
[STACK_FLAGS
] & EMULATOR_FLAG_CF
)) goto Quit
;
610 DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
615 /* Boot from 1st HDD drive */
618 setAH(0x02); // Read sectors
619 setAL(0x01); // Number of sectors
620 setDH(0x00); // Head 0
621 setCH(0x00); // Cylinder 0
622 setCL(0x01); // Sector 1
623 setDL(0x80); // First HDD drive (used by loader code, so should not be cleared)
624 setES(0x0000); // Write data in 0000:7C00
626 BiosDiskService(Stack
);
627 if (!(Stack
[STACK_FLAGS
] & EMULATOR_FLAG_CF
)) goto Quit
;
628 DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
637 /* Go to next drive and invalidate the last half-byte. */
638 BootOrder
= (BootOrder
>> 4) | 0xF000;
642 /* Clear everything, we are going to load DOS32 */
648 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
651 DosBootsectorInitialize();
655 * Jump to 0000:7C00 to boot the OS.
657 * Since we are called via the INT32 mechanism, we need to correctly set
658 * CS:IP, not by changing the current one (otherwise the interrupt could
659 * not be clean up and return properly), but by changing the CS:IP in the
660 * stack, so that when the interrupt returns, the modified CS:IP is popped
661 * off the stack and the CPU is correctly repositioned.
663 Stack
[STACK_CS
] = 0x0000;
664 Stack
[STACK_IP
] = 0x7C00;
666 DPRINT("<-- BiosBootstrapLoader\n");
669 static VOID WINAPI
BiosTimeService(LPWORD Stack
)
673 /* Get System Time */
676 /* Set AL to 1 if midnight had passed, 0 otherwise */
677 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
679 /* Return the tick count in CX:DX */
680 setCX(HIWORD(Bda
->TickCounter
));
681 setDX(LOWORD(Bda
->TickCounter
));
683 /* Reset the midnight flag */
684 Bda
->MidnightPassed
= FALSE
;
689 /* Set System Time */
692 /* Set the tick count to CX:DX */
693 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
695 /* Reset the midnight flag */
696 Bda
->MidnightPassed
= FALSE
;
701 /* Get Real-Time Clock Time */
706 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_HOURS
);
707 setCH(IOReadB(CMOS_DATA_PORT
));
709 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_MINUTES
);
710 setCL(IOReadB(CMOS_DATA_PORT
));
712 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SECONDS
);
713 setDH(IOReadB(CMOS_DATA_PORT
));
715 /* Daylight Savings Time */
716 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_STATUS_B
);
717 StatusB
= IOReadB(CMOS_DATA_PORT
);
718 setDL(StatusB
& 0x01);
721 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
725 // /* Set Real-Time Clock Time */
731 /* Get Real-Time Clock Date */
734 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_CENTURY
);
735 setCH(IOReadB(CMOS_DATA_PORT
));
737 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_YEAR
);
738 setCL(IOReadB(CMOS_DATA_PORT
));
740 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_MONTH
);
741 setDH(IOReadB(CMOS_DATA_PORT
));
743 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_DAY
);
744 setDL(IOReadB(CMOS_DATA_PORT
));
747 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
751 // /* Set Real-Time Clock Date */
759 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
765 static VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
767 /* Increase the system tick count */
773 static VOID
PicSetIRQMask(USHORT off
, USHORT on
)
775 UCHAR pic1off
= off
, pic1on
= on
, pic2off
= off
>>8, pic2on
= on
>>8;
776 IOWriteB(PIC_MASTER_DATA
, (IOReadB(PIC_MASTER_DATA
) & ~pic1off
) | pic1on
);
777 IOWriteB(PIC_SLAVE_DATA
, (IOReadB(PIC_SLAVE_DATA
) & ~pic2off
) | pic2on
);
781 VOID
EnableHwIRQ(UCHAR hwirq
, EMULATOR_INT32_PROC func
)
785 PicSetIRQMask(1 << hwirq
, 0);
787 vector
= BIOS_PIC_MASTER_INT
+ hwirq
;
789 vector
= BIOS_PIC_SLAVE_INT
+ hwirq
- 8;
791 RegisterBiosInt32(vector
, func
);
795 VOID
PicIRQComplete(BYTE IntNum
)
798 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
800 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
802 /* It was an IRQ from the master PIC */
803 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
805 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
807 /* It was an IRQ from the slave PIC */
808 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
809 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
813 static VOID WINAPI
BiosHandleMasterPicIRQ(LPWORD Stack
)
817 IOWriteB(PIC_MASTER_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
818 IrqNumber
= IOReadB(PIC_MASTER_CMD
);
820 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber
);
822 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
825 static VOID WINAPI
BiosHandleSlavePicIRQ(LPWORD Stack
)
829 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
830 IrqNumber
= IOReadB(PIC_SLAVE_CMD
);
832 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber
);
834 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
838 static VOID WINAPI
BiosTimerIrq(LPWORD Stack
)
841 * Perform the system timer interrupt.
843 * Do not call directly BiosSystemTimerInterrupt(Stack);
844 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
845 * for their purpose...
858 Int32Call(&BiosContext
, BIOS_SYS_TIMER_INTERRUPT
);
871 // BiosSystemTimerInterrupt(Stack);
872 PicIRQComplete(LOBYTE(Stack
[STACK_INT_NUM
]));
876 static VOID
BiosHwSetup(VOID
)
878 /* Initialize the master and the slave PICs (cascade mode) */
879 IOWriteB(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
880 IOWriteB(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
883 * Set the interrupt vector offsets for each PIC
884 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
886 IOWriteB(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
887 IOWriteB(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
889 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
890 IOWriteB(PIC_MASTER_DATA
, 1 << 2);
891 /* Tell the slave PIC its cascade identity */
892 IOWriteB(PIC_SLAVE_DATA
, 2);
894 /* Make sure both PICs are in 8086 mode */
895 IOWriteB(PIC_MASTER_DATA
, PIC_ICW4_8086
);
896 IOWriteB(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
898 /* Clear the masks for both PICs */
899 // IOWriteB(PIC_MASTER_DATA, 0x00);
900 // IOWriteB(PIC_SLAVE_DATA , 0x00);
901 /* Disable all IRQs */
902 IOWriteB(PIC_MASTER_DATA
, 0xFF);
903 IOWriteB(PIC_SLAVE_DATA
, 0xFF);
906 /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
907 // NOTE: Some BIOSes set it to Mode 3 instead.
908 IOWriteB(PIT_COMMAND_PORT
, 0x34);
909 // 18.2Hz refresh rate
910 IOWriteB(PIT_DATA_PORT(0), 0x00);
911 IOWriteB(PIT_DATA_PORT(0), 0x00);
913 /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
914 IOWriteB(PIT_COMMAND_PORT
, 0x54);
915 // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
916 IOWriteB(PIT_DATA_PORT(1), 18);
918 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
919 IOWriteB(PIT_COMMAND_PORT
, 0xB6);
921 IOWriteB(PIT_DATA_PORT(2), 0x97);
922 IOWriteB(PIT_DATA_PORT(2), 0x0A);
925 /* Initialize PS/2 keyboard port */
927 IOWriteB(PS2_CONTROL_PORT
, 0xAE);
928 // Port interrupts and clock enabled,
929 // enable keyboard scancode translation.
930 // POST passed, force keyboard unlocking.
931 IOWriteB(PS2_CONTROL_PORT
, 0x60);
932 IOWriteB(PS2_DATA_PORT
, 0x6D);
933 // Enable data reporting
934 IOWriteB(PS2_DATA_PORT
, 0xF4);
936 EnableHwIRQ(0, BiosTimerIrq
);
939 static VOID
InitializeBiosInt32(VOID
)
943 /* Initialize the callback context */
944 InitializeContext(&BiosContext
, BIOS_SEGMENT
, 0x0000);
946 /* Register the default BIOS interrupt vectors */
949 * Zero out all of the IVT (0x00 -- 0xFF). Some applications
950 * indeed expect to have free vectors at the end of the IVT.
952 RtlZeroMemory(BaseAddress
, 0x0100 * sizeof(ULONG
));
954 #if defined(ADVANCED_DEBUGGING) && (ADVANCED_DEBUGGING_LEVEL >= 3)
955 // Initialize all the interrupt vectors to the default one.
956 for (i
= 0x00; i
<= 0xFF; i
++)
957 RegisterBiosInt32(i
, NULL
);
960 /* Initialize the exception interrupt vectors to a default Exception handler */
961 for (i
= 0x00; i
<= 0x07; i
++)
962 RegisterBiosInt32(i
, BiosException
);
964 /* Initialize HW interrupt vectors to a default HW handler */
965 for (i
= BIOS_PIC_MASTER_INT
; i
< BIOS_PIC_MASTER_INT
+ 8; i
++) // 0x08 -- 0x0F
966 RegisterBiosInt32(i
, BiosHandleMasterPicIRQ
);
967 for (i
= BIOS_PIC_SLAVE_INT
; i
< BIOS_PIC_SLAVE_INT
+ 8; i
++) // 0x70 -- 0x77
968 RegisterBiosInt32(i
, BiosHandleSlavePicIRQ
);
970 /* Initialize software vector handlers */
971 // BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
972 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
973 RegisterBiosInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
974 // BIOS_DISK_INTERRUPT : 0x13 (dskbios32.c)
975 // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
976 RegisterBiosInt32(BIOS_MISC_INTERRUPT
, BiosMiscService
);
977 // BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
978 // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
979 RegisterBiosInt32(BIOS_ROM_BASIC
, BiosRomBasic
);
980 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER
, BiosBootstrapLoader
);
981 RegisterBiosInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
982 // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
983 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
985 /* Vectors that should be implemented (see above) */
986 RegisterBiosInt32(0x14, NULL
);
987 RegisterBiosInt32(0x17, NULL
);
988 RegisterBiosInt32(0x1B, NULL
);
989 RegisterBiosInt32(0x4A, NULL
); // User Alarm Handler
991 /* Relocated services by the BIOS (when needed) */
992 RegisterBiosInt32(0x40, NULL
); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
993 RegisterBiosInt32(0x42, NULL
); // Relocated Default INT 10h Video Services
995 /* Miscellaneous unimplemented vector handlers that should better have a default one */
996 RegisterBiosInt32(0x4B, NULL
); // Virtual DMA Specification Services
997 RegisterBiosInt32(0x5C, NULL
); // NetBIOS
999 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
1000 // They don't have any default handler at the moment.
1002 /* Some vectors are in fact addresses to tables */
1003 ((PULONG
)BaseAddress
)[0x1D] = (ULONG
)NULL
; // Video Parameter Tables
1004 ((PULONG
)BaseAddress
)[0x1E] = (ULONG
)NULL
; // Diskette Parameters
1005 ((PULONG
)BaseAddress
)[0x1F] = (ULONG
)NULL
; // 8x8 Graphics Font
1006 ((PULONG
)BaseAddress
)[0x41] = (ULONG
)NULL
; // Hard Disk 0 Parameter Table Address
1007 ((PULONG
)BaseAddress
)[0x43] = (ULONG
)NULL
; // Character Table (EGA, MCGA, VGA)
1008 ((PULONG
)BaseAddress
)[0x46] = (ULONG
)NULL
; // Hard Disk 1 Drive Parameter Table Address
1009 /* Tables that are always uninitialized */
1010 ((PULONG
)BaseAddress
)[0x44] = (ULONG
)NULL
; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
1011 ((PULONG
)BaseAddress
)[0x48] = (ULONG
)NULL
; // Cordless Keyboard Translation (PCjr)
1012 ((PULONG
)BaseAddress
)[0x49] = (ULONG
)NULL
; // Non-Keyboard Scan-code Translation Table (PCJr)
1015 static VOID
InitializeBiosData(VOID
)
1019 /* Initialize the BDA contents */
1020 RtlZeroMemory(Bda
, sizeof(*Bda
));
1023 * Retrieve the basic equipment list from the CMOS
1025 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_EQUIPMENT_LIST
| CMOS_DISABLE_NMI
);
1026 Bda
->EquipmentList
= IOReadB(CMOS_DATA_PORT
);
1027 // TODO: Update it if required.
1028 Bda
->EquipmentList
&= 0x00FF; // High byte cleared for now...
1031 * Retrieve the conventional memory size
1032 * in kB from the CMOS, typically 640 kB.
1034 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_LOW
| CMOS_DISABLE_NMI
);
1035 Low
= IOReadB(CMOS_DATA_PORT
);
1036 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_HIGH
| CMOS_DISABLE_NMI
);
1037 High
= IOReadB(CMOS_DATA_PORT
);
1038 Bda
->MemorySize
= MAKEWORD(Low
, High
);
1043 * The BIOS POST (Power On-Self Test)
1047 Bios32Post(LPWORD Stack
)
1049 static BOOLEAN FirstBoot
= TRUE
;
1050 BYTE ShutdownStatus
;
1053 * Initialize BIOS/Keyboard/Video RAM dynamic data
1056 DPRINT("Bios32Post\n");
1058 /* Disable interrupts */
1061 /* Set the data segment */
1064 /* Initialize the stack */
1065 // Temporary stack for POST (to be used only before initializing the INT vectors)
1069 // Stack to be used after the initialization of the INT vectors
1070 setSS(0x0000); // Stack at 00:8000, going downwards
1074 * Perform early CMOS shutdown status checks
1077 /* Read the CMOS shutdown status byte and reset it */
1078 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SHUTDOWN_STATUS
| CMOS_DISABLE_NMI
);
1079 ShutdownStatus
= IOReadB(CMOS_DATA_PORT
);
1080 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_SHUTDOWN_STATUS
| CMOS_DISABLE_NMI
);
1081 IOWriteB(CMOS_DATA_PORT
, 0x00);
1083 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
1084 Bda
->SoftReset
, ShutdownStatus
);
1086 switch (ShutdownStatus
)
1088 /* Shutdown after Memory Tests (unsupported) */
1089 case 0x01: case 0x02: case 0x03:
1090 /* Shutdown after Protected Mode Tests (unsupported) */
1091 case 0x06: case 0x07: case 0x08:
1092 /* Shutdown after Block Move Test (unsupported) */
1095 DisplayMessage(L
"Unsupported CMOS Shutdown Status value 0x%02X. The VDM will shut down.", ShutdownStatus
);
1096 EmulatorTerminate();
1100 /* Shutdown to Boot Loader */
1103 DPRINT1("Fast restart to Bootstrap Loader...\n");
1104 goto Quit
; // Reenable interrupts and exit.
1107 /* Flush keyboard, issue an EOI... */
1110 IOReadB(PS2_DATA_PORT
);
1113 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1114 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
1120 * ... and far JMP to user-specified location at 0040:0067
1121 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
1125 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
1126 HIWORD(Bda
->ResumeEntryPoint
),
1127 LOWORD(Bda
->ResumeEntryPoint
));
1129 /* Position execution pointers and return with interrupts disabled */
1130 setCS(HIWORD(Bda
->ResumeEntryPoint
));
1131 setIP(LOWORD(Bda
->ResumeEntryPoint
));
1135 /* Soft reset or unexpected shutdown... */
1137 /* ... or other possible shutdown codes: just continue the POST */
1143 * FIXME: UNIMPLEMENTED!
1144 * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
1145 * following actions:
1146 * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
1147 * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
1149 switch (Bda
->SoftReset
)
1155 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...");
1156 EmulatorTerminate();
1164 DisplayMessage(L
"NTVDM is performing a WARM reboot! This is not supported at the moment. The VDM will shut down...");
1165 EmulatorTerminate();
1175 /* Initialize the BDA */
1176 InitializeBiosData();
1178 /* Initialize the User Data Area at 0050:XXXX */
1179 RtlZeroMemory(SEG_OFF_TO_PTR(0x50, 0x0000), sizeof(USER_DATA_AREA
));
1183 //////// NOTE: This is more or less bios-specific ////////
1186 * Initialize IVT and hardware
1189 // WriteUnProtectRom(...);
1191 /* Register the BIOS 32-bit Interrupts */
1192 InitializeBiosInt32();
1194 /* Initialize platform hardware (PIC/PIT chips, ...) */
1197 /* Initialize the Keyboard, Video and Mouse BIOS */
1203 // WriteProtectRom(...);
1205 //////// End of more or less bios-specific section ///////
1209 SearchAndInitRoms(&BiosContext
);
1212 * End of the 32-bit POST portion. We then fall back into 16-bit where
1213 * the rest of the POST code is executed, typically calling INT 19h
1214 * to boot up the OS.
1218 /* Enable interrupts */
1223 /* PUBLIC FUNCTIONS ***********************************************************/
1225 BOOLEAN
Bios32Initialize(VOID
)
1228 * Initialize BIOS/Keyboard/Video ROM static data
1231 /* System BIOS Copyright */
1232 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE000), BiosCopyright
, sizeof(BiosCopyright
)-1);
1234 /* System BIOS Version */
1235 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE070), BiosVersion
, sizeof(BiosVersion
)-1);
1237 /* System BIOS Date */
1238 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFF5), BiosDate
, sizeof(BiosDate
)-1);
1240 /* Bootstrap code */
1241 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE05B), PostCode
, sizeof(PostCode
));
1242 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFF0), Bootstrap
, sizeof(Bootstrap
));
1244 /* BIOS ROM Information */
1245 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xE6F5), &BiosConfigTable
, sizeof(BiosConfigTable
));
1247 /* System BIOS Model (same as Bct->Model) */
1248 *(PBYTE
)(SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0xFFFE)) = BIOS_MODEL
;
1250 /* Initialize the Keyboard and Video BIOS */
1251 if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
1254 EmulatorTerminate();
1258 /* Redefine our POST function */
1259 RegisterBop(BOP_RESET
, Bios32Post
);
1261 WriteProtectRom((PVOID
)TO_LINEAR(BIOS_SEGMENT
, 0x0000),
1262 ROM_AREA_END
- TO_LINEAR(BIOS_SEGMENT
, 0x0000) + 1);
1268 VOID
Bios32Cleanup(VOID
)
1270 DiskBios32Cleanup();
1271 MouseBios32Cleanup();