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>
24 #include "kbdbios32.h"
25 #include "vidbios32.h"
26 #include "moubios32.h"
29 #include "hardware/cmos.h"
30 #include "hardware/pic.h"
31 #include "hardware/timer.h"
33 /* PRIVATE VARIABLES **********************************************************/
35 CALLBACK16 BiosContext
;
39 Bochs BIOS, see rombios.h
40 =========================
42 // model byte 0xFC = AT
43 #define SYS_MODEL_ID 0xFC
44 #define SYS_SUBMODEL_ID 0x00
45 #define BIOS_REVISION 1
46 #define BIOS_CONFIG_TABLE 0xe6f5
48 #ifndef BIOS_BUILD_DATE
49 # define BIOS_BUILD_DATE "06/23/99"
52 // 1K of base memory used for Extended Bios Data Area (EBDA)
53 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
54 #define EBDA_SEG 0x9FC0
55 #define EBDA_SIZE 1 // In KiB
56 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
62 ROM BIOS compatibility entry points:
63 ===================================
64 $e05b ; POST Entry Point
65 $e2c3 ; NMI Handler Entry Point
66 $e3fe ; INT 13h Fixed Disk Services Entry Point
67 $e401 ; Fixed Disk Parameter Table
68 $e6f2 ; INT 19h Boot Load Service Entry Point
69 $e6f5 ; Configuration Data Table
70 $e729 ; Baud Rate Generator Table
71 $e739 ; INT 14h Serial Communications Service Entry Point
72 $e82e ; INT 16h Keyboard Service Entry Point
73 $e987 ; INT 09h Keyboard Service Entry Point
74 $ec59 ; INT 13h Diskette Service Entry Point
75 $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
76 $efc7 ; Diskette Controller Parameter Table
77 $efd2 ; INT 17h Printer Service Entry Point
78 $f045 ; INT 10 Functions 0-Fh Entry Point
79 $f065 ; INT 10h Video Support Service Entry Point
80 $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
81 $f841 ; INT 12h Memory Size Service Entry Point
82 $f84d ; INT 11h Equipment List Service Entry Point
83 $f859 ; INT 15h System Services Entry Point
84 $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
85 $fe6e ; INT 1Ah Time-of-day Service Entry Point
86 $fea5 ; INT 08h System Timer ISR Entry Point
87 $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
88 $ff53 ; IRET Instruction for Dummy Interrupt Handler
89 $ff54 ; INT 05h Print Screen Service Entry Point
90 $fff0 ; Power-up Entry Point
91 $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
92 $fffe ; System Model ID
97 * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
98 * for more information.
100 #define BIOS_MODEL 0xFC // PC-AT
101 #define BIOS_SUBMODEL 0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
102 #define BIOS_REVISION 0x00
103 // FIXME: Find a nice PS/2 486 + 487 BIOS combination!
106 * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
107 * Some programs alternatively look at "COPR. IBM" that is at F000:E008 .
109 static const CHAR BiosCopyright
[] = "0000000 NTVDM IBM Compatible 486 32-bit BIOS Copyright (C) ReactOS Team 1996-2014";
110 static const CHAR BiosVersion
[] = "ReactOS NTVDM 32-bit BIOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")";
111 static const CHAR BiosDate
[] = "06/17/13";
113 C_ASSERT(sizeof(BiosCopyright
)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
114 C_ASSERT(sizeof(BiosDate
)-1 == 0x08);
116 /* 16-bit bootstrap code at F000:FFF0 */
117 static BYTE Bootstrap
[] =
120 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
124 * Normally at F000:E05B there is the POST that finally calls the bootstrap
125 * interrupt. It should also check the value of Bda->SoftReset. Since we do
126 * all the POST in 32 bit from the start, we just place there the bootstrap
129 static BYTE PostCode
[] =
131 0xCD, 0x19, // int 0x19, the bootstrap loader interrupt
132 // LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
136 /* PRIVATE FUNCTIONS **********************************************************/
138 static VOID WINAPI
BiosException(LPWORD Stack
)
140 /* Get the exception number and call the emulator API */
141 BYTE ExceptionNumber
= LOBYTE(Stack
[STACK_INT_NUM
]);
142 EmulatorException(ExceptionNumber
, Stack
);
145 static VOID WINAPI
BiosMiscService(LPWORD Stack
)
149 /* Keyboard intercept */
152 /* CF should be set but let's just set it again just in case */
153 /* Do not modify AL (the hardware scan code), but set CF to continue processing */
155 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
163 * Interval in microseconds in CX:DX
164 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
165 * for more information.
167 Sleep(MAKELONG(getDX(), getCX()));
170 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
175 /* Copy Extended Memory */
178 DWORD Count
= (DWORD
)getCX() * 2;
179 PFAST486_GDT_ENTRY Gdt
= (PFAST486_GDT_ENTRY
)SEG_OFF_TO_PTR(getES(), getSI());
180 DWORD SourceBase
= Gdt
[2].Base
+ (Gdt
[2].BaseMid
<< 16) + (Gdt
[2].BaseHigh
<< 24);
181 DWORD SourceLimit
= Gdt
[2].Limit
+ (Gdt
[2].LimitHigh
<< 16);
182 DWORD DestBase
= Gdt
[3].Base
+ (Gdt
[3].BaseMid
<< 16) + (Gdt
[3].BaseHigh
<< 24);
183 DWORD DestLimit
= Gdt
[3].Limit
+ (Gdt
[3].LimitHigh
<< 16);
185 /* Check for flags */
186 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
187 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
189 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
192 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
198 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
199 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
202 setAX(ERROR_SUCCESS
);
203 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
207 /* Get Extended Memory Size */
213 * Return the (usable) extended memory (after 1 MB)
214 * size in kB from CMOS.
216 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_LOW
);
217 Low
= IOReadB(CMOS_DATA_PORT
);
218 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH
);
219 High
= IOReadB(CMOS_DATA_PORT
);
220 setAX(MAKEWORD(Low
, High
));
223 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
228 /* Get Configuration */
231 /* Return the BIOS ROM Configuration Table address in ES:BX */
232 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
236 /* Call successful; clear CF */
238 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
243 /* Return Extended-Bios Data-Area Segment Address (PS) */
246 // Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
249 /* We do not support EBDA yet */
250 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
255 /* Pointing Device BIOS Interface (PS) */
258 DPRINT1("INT 15h, AH = C2h must be implemented in order to support vendor mouse drivers\n");
264 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
270 static VOID WINAPI
BiosRomBasic(LPWORD Stack
)
272 /* ROM Basic is unsupported, display a message to the user */
273 DisplayMessage(L
"NTVDM doesn't support ROM Basic. The VDM is closing.");
280 static VOID WINAPI
BiosBootstrapLoader(LPWORD Stack
)
283 * In real bioses one loads the bootsector read from a diskette
284 * or from a disk, to 0000:7C00 and then one runs it.
285 * Since we are 32-bit VM and we hardcode our DOS at the moment,
286 * just call the DOS 32-bit initialization code.
289 DPRINT1("BiosBootstrapLoader -->\n");
291 DPRINT1("<-- BiosBootstrapLoader\n");
294 static VOID WINAPI
BiosTimeService(LPWORD Stack
)
300 /* Set AL to 1 if midnight had passed, 0 otherwise */
301 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
303 /* Return the tick count in CX:DX */
304 setCX(HIWORD(Bda
->TickCounter
));
305 setDX(LOWORD(Bda
->TickCounter
));
307 /* Reset the midnight flag */
308 Bda
->MidnightPassed
= FALSE
;
315 /* Set the tick count to CX:DX */
316 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
318 /* Reset the midnight flag */
319 Bda
->MidnightPassed
= FALSE
;
326 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
332 static VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
334 /* Increase the system tick count */
340 static VOID
PicSetIRQMask(USHORT off
, USHORT on
)
342 UCHAR pic1off
= off
, pic1on
= on
, pic2off
= off
>>8, pic2on
= on
>>8;
343 IOWriteB(PIC_MASTER_DATA
, (IOReadB(PIC_MASTER_DATA
) & ~pic1off
) | pic1on
);
344 IOWriteB(PIC_SLAVE_DATA
, (IOReadB(PIC_SLAVE_DATA
) & ~pic2off
) | pic2on
);
348 VOID
EnableHwIRQ(UCHAR hwirq
, EMULATOR_INT32_PROC func
)
352 PicSetIRQMask(1 << hwirq
, 0);
354 vector
= BIOS_PIC_MASTER_INT
+ hwirq
;
356 vector
= BIOS_PIC_SLAVE_INT
+ hwirq
- 8;
358 RegisterBiosInt32(vector
, func
);
362 VOID
PicIRQComplete(LPWORD Stack
)
364 /* Get the interrupt number */
365 BYTE IntNum
= LOBYTE(Stack
[STACK_INT_NUM
]);
368 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
371 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
373 /* It was an IRQ from the master PIC */
374 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
376 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
378 /* It was an IRQ from the slave PIC */
379 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
380 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);
384 static VOID WINAPI
BiosHandleMasterPicIRQ(LPWORD Stack
)
388 IOWriteB(PIC_MASTER_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
389 IrqNumber
= IOReadB(PIC_MASTER_CMD
);
391 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber
);
393 PicIRQComplete(Stack
);
396 static VOID WINAPI
BiosHandleSlavePicIRQ(LPWORD Stack
)
400 IOWriteB(PIC_SLAVE_CMD
, PIC_OCW3_READ_ISR
/* == 0x0B */);
401 IrqNumber
= IOReadB(PIC_SLAVE_CMD
);
403 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber
);
405 PicIRQComplete(Stack
);
409 static VOID WINAPI
BiosTimerIrq(LPWORD Stack
)
412 * Perform the system timer interrupt.
414 * Do not call directly BiosSystemTimerInterrupt(Stack);
415 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
416 * for their purpose...
418 /** EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT); **/
419 Int32Call(&BiosContext
, BIOS_SYS_TIMER_INTERRUPT
);
420 PicIRQComplete(Stack
);
424 static VOID
BiosHwSetup(VOID
)
426 /* Initialize the master and the slave PICs (cascade mode) */
427 IOWriteB(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
428 IOWriteB(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
431 * Set the interrupt vector offsets for each PIC
432 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
434 IOWriteB(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
435 IOWriteB(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
437 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
438 IOWriteB(PIC_MASTER_DATA
, 1 << 2);
439 /* Tell the slave PIC its cascade identity */
440 IOWriteB(PIC_SLAVE_DATA
, 2);
442 /* Make sure both PICs are in 8086 mode */
443 IOWriteB(PIC_MASTER_DATA
, PIC_ICW4_8086
);
444 IOWriteB(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
446 /* Clear the masks for both PICs */
447 // IOWriteB(PIC_MASTER_DATA, 0x00);
448 // IOWriteB(PIC_SLAVE_DATA , 0x00);
449 /* Disable all IRQs */
450 IOWriteB(PIC_MASTER_DATA
, 0xFF);
451 IOWriteB(PIC_SLAVE_DATA
, 0xFF);
454 /* Initialize PIT Counter 0 */
455 IOWriteB(PIT_COMMAND_PORT
, 0x34);
456 IOWriteB(PIT_DATA_PORT(0), 0x00);
457 IOWriteB(PIT_DATA_PORT(0), 0x00);
459 /* Initialize PIT Counter 1 */
460 IOWriteB(PIT_COMMAND_PORT
, 0x74);
461 IOWriteB(PIT_DATA_PORT(1), 0x00);
462 IOWriteB(PIT_DATA_PORT(1), 0x00);
464 /* Initialize PIT Counter 2 */
465 IOWriteB(PIT_COMMAND_PORT
, 0xB4);
466 IOWriteB(PIT_DATA_PORT(2), 0x00);
467 IOWriteB(PIT_DATA_PORT(2), 0x00);
469 EnableHwIRQ(0, BiosTimerIrq
);
472 static VOID
InitializeBiosInt32(VOID
)
476 /* Initialize the callback context */
477 InitializeContext(&BiosContext
, BIOS_SEGMENT
, 0x0000);
479 /* Register the default BIOS 32-bit Interrupts */
480 for (i
= 0x00; i
<= 0xFF; i
++)
482 RegisterBiosInt32(i
, NULL
);
485 /* Initialize the exception vector interrupts to a default Exception handler */
486 for (i
= 0; i
< 8; i
++)
487 RegisterBiosInt32(i
, BiosException
);
489 /* Initialize HW vector interrupts to a default HW handler */
490 for (i
= BIOS_PIC_MASTER_INT
; i
< BIOS_PIC_MASTER_INT
+ 8; i
++)
491 RegisterBiosInt32(i
, BiosHandleMasterPicIRQ
);
492 for (i
= BIOS_PIC_SLAVE_INT
; i
< BIOS_PIC_SLAVE_INT
+ 8; i
++)
493 RegisterBiosInt32(i
, BiosHandleSlavePicIRQ
);
495 /* Initialize software vector handlers */
496 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
497 RegisterBiosInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
498 RegisterBiosInt32(BIOS_MISC_INTERRUPT
, BiosMiscService
);
499 RegisterBiosInt32(BIOS_ROM_BASIC
, BiosRomBasic
);
500 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER
, BiosBootstrapLoader
);
501 RegisterBiosInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
502 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
504 /* Some interrupts are in fact addresses to tables */
505 ((PULONG
)BaseAddress
)[0x1E] = (ULONG
)NULL
;
506 ((PULONG
)BaseAddress
)[0x41] = (ULONG
)NULL
;
507 ((PULONG
)BaseAddress
)[0x46] = (ULONG
)NULL
;
508 ((PULONG
)BaseAddress
)[0x48] = (ULONG
)NULL
;
509 ((PULONG
)BaseAddress
)[0x49] = (ULONG
)NULL
;
512 static VOID
InitializeBiosInfo(VOID
)
514 RtlZeroMemory(Bct
, sizeof(*Bct
));
516 Bct
->Length
= sizeof(*Bct
);
517 Bct
->Model
= BIOS_MODEL
;
518 Bct
->SubModel
= BIOS_SUBMODEL
;
519 Bct
->Revision
= BIOS_REVISION
;
520 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
521 Bct
->Feature
[1] = 0x00; // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
522 Bct
->Feature
[2] = 0x00;
523 Bct
->Feature
[3] = 0x00;
524 Bct
->Feature
[4] = 0x00;
527 static VOID
InitializeBiosData(VOID
)
531 /* System BIOS Copyright */
532 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE000), BiosCopyright
, sizeof(BiosCopyright
)-1);
534 /* System BIOS Version */
535 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE080), BiosVersion
, sizeof(BiosVersion
)-1); // FIXME: or E061, or E100 ??
537 /* System BIOS Date */
538 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF5), BiosDate
, sizeof(BiosDate
)-1);
540 /* System BIOS Model (same as Bct->Model) */
541 *(PBYTE
)(SEG_OFF_TO_PTR(0xF000, 0xFFFE)) = BIOS_MODEL
;
543 /* Initialize the BDA contents */
544 RtlZeroMemory(Bda
, sizeof(*Bda
));
545 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
548 * Retrieve the conventional memory size
549 * in kB from CMOS, typically 640 kB.
551 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_LOW
);
552 Low
= IOReadB(CMOS_DATA_PORT
);
553 IOWriteB(CMOS_ADDRESS_PORT
, CMOS_REG_BASE_MEMORY_HIGH
);
554 High
= IOReadB(CMOS_DATA_PORT
);
555 Bda
->MemorySize
= MAKEWORD(Low
, High
);
558 /* PUBLIC FUNCTIONS ***********************************************************/
561 * The BIOS POST (Power On-Self Test)
563 BOOLEAN
Bios32Initialize(VOID
)
567 /* Initialize the stack */
568 // That's what says IBM... (stack at 30:00FF going downwards)
571 setSS(0x0050); // Stack at 50:0400, going downwards
574 /* Set data segment */
577 /* Initialize the BDA and the BIOS ROM Information */
578 InitializeBiosData();
579 InitializeBiosInfo();
581 /* Register the BIOS 32-bit Interrupts */
582 InitializeBiosInt32();
584 /* Initialize platform hardware (PIC/PIT chips, ...) */
587 /* Initialize the Keyboard, Video and Mouse BIOS */
588 if (!KbdBios32Initialize() || !VidBios32Initialize() || !MouseBios32Initialize())
591 ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! /////////////////////
594 Success
= LoadRom("boot.bin", (PVOID
)0xE0000, NULL
);
595 DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success
? "succeeded" : "failed", GetLastError());
597 SearchAndInitRoms(&BiosContext
);
600 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE05B), PostCode
, sizeof(PostCode
));
601 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF0), Bootstrap
, sizeof(Bootstrap
));
607 VOID
Bios32Cleanup(VOID
)
609 MouseBios32Cleanup();