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