[NTVDM]: Implement PC reset handling in the BIOS. Used by some apps for switching...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / bios32 / bios32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: bios32.c
5 * PURPOSE: VDM 32-bit BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 /* For BIOS Version number */
14 #include <reactos/buildno.h>
15
16 #include "ntvdm.h"
17 #include "emulator.h"
18 #include "cpu/cpu.h" // for EMULATOR_FLAG_CF
19 #include "cpu/bop.h"
20 #include "int32.h"
21
22 #include <bios/bios.h>
23 #include <bios/rom.h>
24 #include "bios32.h"
25 #include "bios32p.h"
26 #include "kbdbios32.h"
27 #include "vidbios32.h"
28 #include "moubios32.h"
29
30 #include "io.h"
31 #include "hardware/cmos.h"
32 #include "hardware/pic.h"
33 #include "hardware/pit.h"
34 #include "hardware/ps2.h"
35
36 /* PRIVATE VARIABLES **********************************************************/
37
38 CALLBACK16 BiosContext;
39
40 /*
41
42 Bochs BIOS, see rombios.h
43 =========================
44
45 // model byte 0xFC = AT
46 #define SYS_MODEL_ID 0xFC
47 #define SYS_SUBMODEL_ID 0x00
48 #define BIOS_REVISION 1
49 #define BIOS_CONFIG_TABLE 0xe6f5
50
51 #ifndef BIOS_BUILD_DATE
52 # define BIOS_BUILD_DATE "06/23/99"
53 #endif
54
55 // 1K of base memory used for Extended Bios Data Area (EBDA)
56 // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
57 #define EBDA_SEG 0x9FC0
58 #define EBDA_SIZE 1 // In KiB
59 #define BASE_MEM_IN_K (640 - EBDA_SIZE)
60
61
62 See rombios.c
63 =============
64
65 ROM BIOS compatibility entry points:
66 ===================================
67 $e05b ; POST Entry Point
68 $e2c3 ; NMI Handler Entry Point
69 $e3fe ; INT 13h Fixed Disk Services Entry Point
70 $e401 ; Fixed Disk Parameter Table
71 $e6f2 ; INT 19h Boot Load Service Entry Point
72 $e6f5 ; Configuration Data Table
73 $e729 ; Baud Rate Generator Table
74 $e739 ; INT 14h Serial Communications Service Entry Point
75 $e82e ; INT 16h Keyboard Service Entry Point
76 $e987 ; INT 09h Keyboard Service Entry Point
77 $ec59 ; INT 13h Diskette Service Entry Point
78 $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
79 $efc7 ; Diskette Controller Parameter Table
80 $efd2 ; INT 17h Printer Service Entry Point
81 $f045 ; INT 10 Functions 0-Fh Entry Point
82 $f065 ; INT 10h Video Support Service Entry Point
83 $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
84 $f841 ; INT 12h Memory Size Service Entry Point
85 $f84d ; INT 11h Equipment List Service Entry Point
86 $f859 ; INT 15h System Services Entry Point
87 $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
88 $fe6e ; INT 1Ah Time-of-day Service Entry Point
89 $fea5 ; INT 08h System Timer ISR Entry Point
90 $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
91 $ff53 ; IRET Instruction for Dummy Interrupt Handler
92 $ff54 ; INT 05h Print Screen Service Entry Point
93 $fff0 ; Power-up Entry Point
94 $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
95 $fffe ; System Model ID
96
97 */
98
99 /*
100 * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
101 * for more information.
102 */
103 #define BIOS_MODEL 0xFC // PC-AT
104 #define BIOS_SUBMODEL 0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
105 #define BIOS_REVISION 0x00
106 // FIXME: Find a nice PS/2 486 + 487 BIOS combination!
107
108 /*
109 * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
110 * Some programs alternatively look at "COPR. IBM" that is at F000:E008 .
111 */
112 static const CHAR BiosCopyright[] = "0000000 NTVDM IBM Compatible 486 32-bit BIOS Copyright (C) ReactOS Team 1996-2014";
113 static const CHAR BiosVersion[] = "ReactOS NTVDM 32-bit BIOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")";
114 static const CHAR BiosDate[] = "06/17/13";
115
116 C_ASSERT(sizeof(BiosCopyright)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
117 C_ASSERT(sizeof(BiosDate)-1 == 0x08);
118
119 /* 16-bit bootstrap code at F000:FFF0 */
120 static BYTE Bootstrap[] =
121 {
122 0xEA, // jmp far ptr
123 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
124 };
125
126 /*
127 * Normally at F000:E05B there is the POST that finally calls the bootstrap
128 * interrupt. It should also check the value of Bda->SoftReset. Since we do
129 * all the POST in 32 bit from the start, we just place there the bootstrap
130 * interrupt call.
131 */
132 static BYTE PostCode[] =
133 {
134 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_RESET, // Call BIOS POST
135 0xCD, BIOS_BOOTSTRAP_LOADER, // INT 0x19
136 // LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
137 };
138
139
140 /* PRIVATE FUNCTIONS **********************************************************/
141
142 static VOID WINAPI BiosException(LPWORD Stack)
143 {
144 /* Get the exception number and call the emulator API */
145 BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]);
146 EmulatorException(ExceptionNumber, Stack);
147 }
148
149 static VOID WINAPI BiosMiscService(LPWORD Stack)
150 {
151 switch (getAH())
152 {
153 /* OS Hooks for Multitasking */
154 case 0x80: // Device Open
155 case 0x81: // Device Close
156 case 0x82: // Program Termination
157 case 0x90: // Device Busy
158 case 0x91: // Device POST
159 {
160 /* Return success by default */
161 setAH(0x00);
162 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
163 break;
164 }
165
166 /* Keyboard intercept */
167 case 0x4F:
168 {
169 /* CF should be set but let's just set it again just in case */
170 /* Do not modify AL (the hardware scan code), but set CF to continue processing */
171 // setCF(1);
172 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
173 break;
174 }
175
176 /* Wait */
177 case 0x86:
178 {
179 /*
180 * Interval in microseconds in CX:DX
181 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
182 * for more information.
183 */
184 LARGE_INTEGER TimeOut;
185 TimeOut.QuadPart = MAKELONG(getDX(), getCX()) * -10LL;
186
187 // HACK: For now, use the NT API (time in hundreds of nanoseconds).
188 NtDelayExecution(FALSE, &TimeOut);
189
190 /* Clear CF */
191 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
192
193 break;
194 }
195
196 /* Copy Extended Memory */
197 case 0x87:
198 {
199 DWORD Count = (DWORD)getCX() * 2;
200 PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI());
201 DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24);
202 DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16);
203 DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24);
204 DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16);
205
206 /* Check for flags */
207 if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF;
208 if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF;
209
210 if ((Count > SourceLimit) || (Count > DestLimit))
211 {
212 setAX(0x80);
213 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
214
215 break;
216 }
217
218 /* Copy */
219 RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase),
220 (PVOID)((ULONG_PTR)BaseAddress + SourceBase),
221 Count);
222
223 setAX(ERROR_SUCCESS);
224 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
225 break;
226 }
227
228 /* Get Extended Memory Size */
229 case 0x88:
230 {
231 UCHAR Low, High;
232
233 /*
234 * Return the (usable) extended memory (after 1 MB)
235 * size in kB from CMOS.
236 */
237 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW | CMOS_DISABLE_NMI);
238 Low = IOReadB(CMOS_DATA_PORT);
239 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH | CMOS_DISABLE_NMI);
240 High = IOReadB(CMOS_DATA_PORT);
241 setAX(MAKEWORD(Low, High));
242
243 /* Clear CF */
244 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
245
246 break;
247 }
248
249 /* Switch to Protected Mode */
250 case 0x89:
251 {
252 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
253
254 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
255 break;
256 }
257
258 /* Get Configuration */
259 case 0xC0:
260 {
261 /* Return the BIOS ROM Configuration Table address in ES:BX */
262 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
263 setES(BIOS_SEGMENT);
264 setBX(0xE6F5);
265
266 /* Call successful; clear CF */
267 setAH(0x00);
268 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
269
270 break;
271 }
272
273 /* Return Extended-Bios Data-Area Segment Address (PS) */
274 case 0xC1:
275 {
276 // Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
277 // setES(???);
278
279 UNIMPLEMENTED;
280
281 /* We do not support EBDA yet */
282 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
283
284 break;
285 }
286
287 /* Pointing Device BIOS Interface (PS) */
288 case 0xC2:
289 {
290 BiosMousePs2Interface(Stack);
291 break;
292 }
293
294 default:
295 {
296 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
297 getAH());
298 }
299 }
300 }
301
302 static VOID WINAPI BiosRomBasic(LPWORD Stack)
303 {
304 /* ROM Basic is unsupported, display a message to the user */
305 DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing.");
306
307 /* Stop the VDM */
308 EmulatorTerminate();
309 return;
310 }
311
312
313 VOID DosBootsectorInitialize(VOID);
314
315 static VOID WINAPI BiosBootstrapLoader(LPWORD Stack)
316 {
317 /*
318 * In real BIOSes one loads the bootsector read from a diskette
319 * or from a disk, copy it to 0000:7C00 and then boot it.
320 * Since we are 32-bit VM and we hardcode our DOS at the moment,
321 * just call the DOS 32-bit initialization code.
322 */
323
324 DPRINT("BiosBootstrapLoader -->\n");
325
326 /* Load DOS */
327 DosBootsectorInitialize();
328
329 /*
330 * Position CPU to 0000:7C00 to boot the OS.
331 *
332 * Since we are called via the INT32 mechanism, we need to correctly set
333 * CS:IP, not by changing the current one (otherwise the interrupt could
334 * not be clean up and return properly), but by changing the CS:IP in the
335 * stack, so that when the interrupt returns, the modified CS:IP is popped
336 * off the stack and the CPU is correctly repositioned.
337 */
338 Stack[STACK_CS] = 0x0000;
339 Stack[STACK_IP] = 0x7C00;
340
341 DPRINT("<-- BiosBootstrapLoader\n");
342 }
343
344 static VOID WINAPI BiosTimeService(LPWORD Stack)
345 {
346 switch (getAH())
347 {
348 case 0x00:
349 {
350 /* Set AL to 1 if midnight had passed, 0 otherwise */
351 setAL(Bda->MidnightPassed ? 0x01 : 0x00);
352
353 /* Return the tick count in CX:DX */
354 setCX(HIWORD(Bda->TickCounter));
355 setDX(LOWORD(Bda->TickCounter));
356
357 /* Reset the midnight flag */
358 Bda->MidnightPassed = FALSE;
359
360 break;
361 }
362
363 case 0x01:
364 {
365 /* Set the tick count to CX:DX */
366 Bda->TickCounter = MAKELONG(getDX(), getCX());
367
368 /* Reset the midnight flag */
369 Bda->MidnightPassed = FALSE;
370
371 break;
372 }
373
374 default:
375 {
376 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
377 getAH());
378 }
379 }
380 }
381
382 static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
383 {
384 /* Increase the system tick count */
385 Bda->TickCounter++;
386 }
387
388
389 // From SeaBIOS
390 static VOID PicSetIRQMask(USHORT off, USHORT on)
391 {
392 UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8;
393 IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on);
394 IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on);
395 }
396
397 // From SeaBIOS
398 VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func)
399 {
400 UCHAR vector;
401
402 PicSetIRQMask(1 << hwirq, 0);
403 if (hwirq < 8)
404 vector = BIOS_PIC_MASTER_INT + hwirq;
405 else
406 vector = BIOS_PIC_SLAVE_INT + hwirq - 8;
407
408 RegisterBiosInt32(vector, func);
409 }
410
411
412 VOID PicIRQComplete(LPWORD Stack)
413 {
414 /* Get the interrupt number */
415 BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]);
416
417 /*
418 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
419 */
420
421 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
422 {
423 /* It was an IRQ from the master PIC */
424 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
425 }
426 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
427 {
428 /* It was an IRQ from the slave PIC */
429 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
430 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
431 }
432 }
433
434 static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack)
435 {
436 BYTE IrqNumber;
437
438 IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
439 IrqNumber = IOReadB(PIC_MASTER_CMD);
440
441 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber);
442
443 PicIRQComplete(Stack);
444 }
445
446 static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack)
447 {
448 BYTE IrqNumber;
449
450 IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
451 IrqNumber = IOReadB(PIC_SLAVE_CMD);
452
453 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber);
454
455 PicIRQComplete(Stack);
456 }
457
458 // Timer IRQ 0
459 static VOID WINAPI BiosTimerIrq(LPWORD Stack)
460 {
461 /*
462 * Perform the system timer interrupt.
463 *
464 * Do not call directly BiosSystemTimerInterrupt(Stack);
465 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
466 * for their purpose...
467 */
468 Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT);
469 // BiosSystemTimerInterrupt(Stack);
470 PicIRQComplete(Stack);
471 }
472
473
474 static VOID BiosHwSetup(VOID)
475 {
476 /* Initialize the master and the slave PICs (cascade mode) */
477 IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
478 IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
479
480 /*
481 * Set the interrupt vector offsets for each PIC
482 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
483 */
484 IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
485 IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT );
486
487 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
488 IOWriteB(PIC_MASTER_DATA, 1 << 2);
489 /* Tell the slave PIC its cascade identity */
490 IOWriteB(PIC_SLAVE_DATA , 2);
491
492 /* Make sure both PICs are in 8086 mode */
493 IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086);
494 IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086);
495
496 /* Clear the masks for both PICs */
497 // IOWriteB(PIC_MASTER_DATA, 0x00);
498 // IOWriteB(PIC_SLAVE_DATA , 0x00);
499 /* Disable all IRQs */
500 IOWriteB(PIC_MASTER_DATA, 0xFF);
501 IOWriteB(PIC_SLAVE_DATA , 0xFF);
502
503
504 /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
505 // NOTE: Some BIOSes set it to Mode 3 instead.
506 IOWriteB(PIT_COMMAND_PORT, 0x34);
507 // 18.2Hz refresh rate
508 IOWriteB(PIT_DATA_PORT(0), 0x00);
509 IOWriteB(PIT_DATA_PORT(0), 0x00);
510
511 /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
512 IOWriteB(PIT_COMMAND_PORT, 0x54);
513 // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
514 IOWriteB(PIT_DATA_PORT(1), 18);
515
516 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
517 IOWriteB(PIT_COMMAND_PORT, 0xB6);
518 // Count for 440Hz
519 IOWriteB(PIT_DATA_PORT(2), 0x97);
520 IOWriteB(PIT_DATA_PORT(2), 0x0A);
521
522 EnableHwIRQ(0, BiosTimerIrq);
523 }
524
525 static VOID InitializeBiosInt32(VOID)
526 {
527 USHORT i;
528
529 /* Initialize the callback context */
530 InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000);
531
532 /* Register the default BIOS interrupt vectors */
533
534 /* Zero out all of the IVT (0x00 -- 0xFF) */
535 RtlZeroMemory(BaseAddress, 0x0100 * sizeof(ULONG));
536
537 #ifdef ADVANCED_DEBUGGING
538 // Initialize all the interrupt vectors to the default one.
539 for (i = 0x00; i <= 0xFF; i++)
540 RegisterBiosInt32(i, NULL);
541 #endif
542
543 /* Initialize the exception interrupt vectors to a default Exception handler */
544 for (i = 0x00; i <= 0x07; i++)
545 RegisterBiosInt32(i, BiosException);
546
547 /* Initialize HW interrupt vectors to a default HW handler */
548 for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) // 0x08 -- 0x0F
549 RegisterBiosInt32(i, BiosHandleMasterPicIRQ);
550 for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++) // 0x70 -- 0x77
551 RegisterBiosInt32(i, BiosHandleSlavePicIRQ);
552
553 /* Initialize software vector handlers */
554 // BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
555 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService );
556 RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize );
557 // BIOS_DISK_INTERRUPT : 0x13 -- UNIMPLEMENTED
558 // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
559 RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService );
560 // BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
561 // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
562 RegisterBiosInt32(BIOS_ROM_BASIC , BiosRomBasic );
563 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER , BiosBootstrapLoader );
564 RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService );
565 // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
566 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
567
568 /* Vectors that should be implemented (see above) */
569 RegisterBiosInt32(0x13, NULL);
570 RegisterBiosInt32(0x14, NULL);
571 RegisterBiosInt32(0x17, NULL);
572 RegisterBiosInt32(0x1B, NULL);
573 RegisterBiosInt32(0x4A, NULL); // User Alarm Handler
574
575 /* Relocated services by the BIOS (when needed) */
576 RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
577 RegisterBiosInt32(0x42, NULL); // Relocated Default INT 10h Video Services
578
579 /* Miscellaneous unimplemented vector handlers that should better have a default one */
580 RegisterBiosInt32(0x4B, NULL); // Virtual DMA Specification Services
581 RegisterBiosInt32(0x5C, NULL); // NetBIOS
582
583 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
584 // They don't have any default handler at the moment.
585
586 /* Some vectors are in fact addresses to tables */
587 ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL; // Video Parameter Tables
588 ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL; // Diskette Parameters
589 ((PULONG)BaseAddress)[0x1F] = (ULONG)NULL; // 8x8 Graphics Font
590 ((PULONG)BaseAddress)[0x41] = (ULONG)NULL; // Hard Disk 0 Parameter Table Address
591 ((PULONG)BaseAddress)[0x43] = (ULONG)NULL; // Character Table (EGA, MCGA, VGA)
592 ((PULONG)BaseAddress)[0x46] = (ULONG)NULL; // Hard Disk 1 Drive Parameter Table Address
593 /* Tables that are always uninitialized */
594 ((PULONG)BaseAddress)[0x44] = (ULONG)NULL; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
595 ((PULONG)BaseAddress)[0x48] = (ULONG)NULL; // Cordless Keyboard Translation (PCjr)
596 ((PULONG)BaseAddress)[0x49] = (ULONG)NULL; // Non-Keyboard Scan-code Translation Table (PCJr)
597 }
598
599 static VOID InitializeBiosData(VOID)
600 {
601 UCHAR Low, High;
602
603 /* Initialize the BDA contents */
604 RtlZeroMemory(Bda, sizeof(*Bda));
605 Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
606
607 /*
608 * Retrieve the conventional memory size
609 * in kB from CMOS, typically 640 kB.
610 */
611 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW | CMOS_DISABLE_NMI);
612 Low = IOReadB(CMOS_DATA_PORT);
613 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH | CMOS_DISABLE_NMI);
614 High = IOReadB(CMOS_DATA_PORT);
615 Bda->MemorySize = MAKEWORD(Low, High);
616 }
617
618 static VOID InitializeBiosInfo(VOID)
619 {
620 RtlZeroMemory(Bct, sizeof(*Bct));
621
622 Bct->Length = sizeof(*Bct);
623 Bct->Model = BIOS_MODEL;
624 Bct->SubModel = BIOS_SUBMODEL;
625 Bct->Revision = BIOS_REVISION;
626 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
627 Bct->Feature[1] = 0x00; // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
628 Bct->Feature[2] = 0x00;
629 Bct->Feature[3] = 0x00;
630 Bct->Feature[4] = 0x00;
631 }
632
633
634
635 /*
636 * The BIOS POST (Power On-Self Test)
637 */
638 VOID
639 Bios32Post(VOID)
640 {
641 #if 0
642 BOOLEAN Success;
643 #endif
644 BYTE ShutdownStatus;
645
646 DPRINT("Bios32Post\n");
647
648 /* Initialize the stack */
649 // That's what says IBM... (stack at 30:00FF going downwards)
650 // setSS(0x0000);
651 // setSP(0x0400);
652 setSS(0x0050); // Stack at 50:0400, going downwards
653 setSP(0x0400);
654
655 /* Set data segment */
656 setDS(BDA_SEGMENT);
657
658 /*
659 * Perform early CMOS shutdown status checks
660 */
661
662 /* Read the CMOS shutdown status byte and reset it */
663 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
664 ShutdownStatus = IOReadB(CMOS_DATA_PORT);
665 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
666 IOWriteB(CMOS_DATA_PORT, 0x00);
667
668 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
669 Bda->SoftReset, ShutdownStatus);
670
671 switch (ShutdownStatus)
672 {
673 /* Shutdown after Memory Tests (unsupported) */
674 case 0x01: case 0x02: case 0x03:
675 /* Shutdown after Protected Mode Tests (unsupported) */
676 case 0x06: case 0x07: case 0x08:
677 /* Shutdown after Block Move Test (unsupported) */
678 case 0x09:
679 {
680 DisplayMessage(L"Unsupported CMOS Shutdown Status value 0x%02X. The VDM is stopping...", ShutdownStatus);
681 EmulatorTerminate();
682 return;
683 }
684
685 /* Shutdown to Boot Loader */
686 case 0x04:
687 {
688 DPRINT1("Fast restart to Bootstrap Loader...\n");
689 return;
690 }
691
692 /* Flush keyboard, issue an EOI... */
693 case 0x05:
694 {
695 IOReadB(PS2_DATA_PORT);
696
697 /* Send EOI */
698 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
699 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
700
701 // Fall back
702 }
703
704 /*
705 * ... and far JMP to user-specified location at 0040:0067
706 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
707 */
708 case 0x0A:
709 {
710 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
711 HIWORD(Bda->ResumeEntryPoint),
712 LOWORD(Bda->ResumeEntryPoint));
713
714 /* Position execution pointers to Bda->ResumeEntryPoint and return */
715 setCS(HIWORD(Bda->ResumeEntryPoint));
716 setIP(LOWORD(Bda->ResumeEntryPoint));
717 return;
718 }
719
720 /* Soft reset or unexpected shutdown... */
721 case 0x00:
722 /* ... or other possible shutdown codes: just continue the POST */
723 default:
724 break;
725 }
726
727 /*
728 * FIXME: UNIMPLEMENTED!
729 * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
730 * following actions:
731 * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
732 * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
733 */
734
735 // FIXME: This is a debug temporary check:
736 // Since NTVDM memory is by default initialized with 0xCC, it is also
737 // the case for the BDA. So at first boot we get SoftReset == 0xCCCC.
738 // After we zero out the BDA and put valid values in it.
739 // If for some reason an app calls the BIOS initialization code,
740 // SoftReset is normally zero (unless the app puts a non-null value in SoftReset)
741 // and we can detect that. With the current state of NTVDM, apps calling
742 // by hand the BIOS init code is a sign of a bug, e.g. see MSD.EXE version 2+.
743 if (Bda->SoftReset != 0xCCCC)
744 {
745 DisplayMessage(L"NTVDM is performing a COLD reboot! The program you are currently testing seems to not behave correctly! The VDM is stopping...");
746 EmulatorTerminate();
747 return;
748 }
749
750 /* Initialize the BDA and the BIOS ROM Information */
751 InitializeBiosData();
752 InitializeBiosInfo();
753
754 /*
755 * Initialize IVT and hardware
756 */
757
758 /* Register the BIOS 32-bit Interrupts */
759 InitializeBiosInt32();
760
761 /* Initialize platform hardware (PIC/PIT chips, ...) */
762 BiosHwSetup();
763
764 /* Initialize the Keyboard, Video and Mouse BIOS */
765 if (!KbdBios32Initialize() || !VidBios32Initialize() || !MouseBios32Initialize())
766 {
767 // return FALSE;
768
769 /* Stop the VDM */
770 EmulatorTerminate();
771 return;
772 }
773
774 #if 0
775 /* Initialize the Keyboard and Video BIOS */
776 if (!KbdBiosInitialize() || !VidBiosInitialize())
777 {
778 /* Stop the VDM */
779 EmulatorTerminate();
780 return;
781 }
782 #endif
783
784 ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! /////////////////////
785
786 #if 0
787 /* Load some ROMs */
788 Success = LoadRom("boot.bin", (PVOID)0xE0000, NULL);
789 DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError());
790 #endif
791
792 SearchAndInitRoms(&BiosContext);
793
794 /*
795 * End of the 32-bit POST portion. We then fall back into 16-bit where
796 * the rest of the POST code is executed, typically calling INT 19h
797 * to boot up the OS.
798 */
799 }
800
801 static VOID WINAPI Bios32ResetBop(LPWORD Stack)
802 {
803 DPRINT("Bios32ResetBop\n");
804
805 /* Disable interrupts */
806 setIF(0);
807
808 /* Do the POST */
809 Bios32Post();
810
811 /* Enable interrupts */
812 setIF(1);
813 }
814
815
816 /* PUBLIC FUNCTIONS ***********************************************************/
817
818 BOOLEAN Bios32Initialize(VOID)
819 {
820 /*
821 * Initialize BIOS32 static data
822 */
823
824 /* Bootstrap code */
825 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE05B), PostCode , sizeof(PostCode ));
826 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF0), Bootstrap, sizeof(Bootstrap));
827
828 /* System BIOS Copyright */
829 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE000), BiosCopyright, sizeof(BiosCopyright)-1);
830
831 /* System BIOS Version */
832 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE080), BiosVersion, sizeof(BiosVersion)-1);
833 // FIXME: or E061, or E100 ??
834
835 /* System BIOS Date */
836 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF5), BiosDate, sizeof(BiosDate)-1);
837
838 /* System BIOS Model (same as Bct->Model) */
839 *(PBYTE)(SEG_OFF_TO_PTR(0xF000, 0xFFFE)) = BIOS_MODEL;
840
841 /* Redefine our POST function */
842 RegisterBop(BOP_RESET, Bios32ResetBop);
843
844 /* We are done */
845 return TRUE;
846 }
847
848 VOID Bios32Cleanup(VOID)
849 {
850 MouseBios32Cleanup();
851 VidBios32Cleanup();
852 KbdBios32Cleanup();
853 }
854
855 /* EOF */