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