cf8fabeaed9048b4307bab553974e52d6417b509
[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 /* Wait On External Event */
168 case 0x41:
169 {
170 BYTE Value;
171 BOOLEAN Return;
172 static DWORD StartingCount;
173
174 /* Check if this is the first time this BOP occurred */
175 if (!getCF())
176 {
177 /* Set the starting count */
178 StartingCount = Bda->TickCounter;
179 }
180
181 if (getBL() != 0 && (Bda->TickCounter - StartingCount) >= getBL())
182 {
183 /* Timeout expired */
184 setCF(0);
185 break;
186 }
187
188 if (getAL() & (1 << 4))
189 {
190 /* Read from the I/O port */
191 Value = IOReadB(getDX());
192 }
193 else
194 {
195 /* Read from the memory */
196 Value = *(LPBYTE)SEG_OFF_TO_PTR(getES(), getDI());
197 }
198
199 switch (getAL() & 7)
200 {
201 /* Any external event */
202 case 0:
203 {
204 /* Return if this is not the first time the BOP occurred */
205 Return = getCF();
206 break;
207 }
208
209 /* Compare and return if equal */
210 case 1:
211 {
212 Return = Value == getBH();
213 break;
214 }
215
216 /* Compare and return if not equal */
217 case 2:
218 {
219 Return = Value != getBH();
220 break;
221 }
222
223 /* Test and return if not zero */
224 case 3:
225 {
226 Return = (Value & getBH()) != 0;
227 break;
228 }
229
230 /* Test and return if zero */
231 case 4:
232 {
233 Return = (Value & getBH()) == 0;
234 break;
235 }
236
237 default:
238 {
239 DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
240 Return = TRUE;
241 break;
242 }
243 }
244
245 /* Repeat the BOP if we shouldn't return */
246 setCF(!Return);
247
248 break;
249 }
250
251 /* Keyboard intercept */
252 case 0x4F:
253 {
254 /* CF should be set but let's just set it again just in case */
255 /* Do not modify AL (the hardware scan code), but set CF to continue processing */
256 // setCF(1);
257 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
258 break;
259 }
260
261 /* Wait */
262 case 0x86:
263 {
264 /*
265 * Interval in microseconds in CX:DX
266 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
267 * for more information.
268 */
269 LARGE_INTEGER TimeOut;
270 TimeOut.QuadPart = MAKELONG(getDX(), getCX()) * -10LL;
271
272 // HACK: For now, use the NT API (time in hundreds of nanoseconds).
273 NtDelayExecution(FALSE, &TimeOut);
274
275 /* Clear CF */
276 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
277
278 break;
279 }
280
281 /* Copy Extended Memory */
282 case 0x87:
283 {
284 DWORD Count = (DWORD)getCX() * 2;
285 PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI());
286 DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24);
287 DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16);
288 DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24);
289 DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16);
290
291 /* Check for flags */
292 if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF;
293 if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF;
294
295 if ((Count > SourceLimit) || (Count > DestLimit))
296 {
297 setAX(0x80);
298 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
299
300 break;
301 }
302
303 /* Copy */
304 RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase),
305 (PVOID)((ULONG_PTR)BaseAddress + SourceBase),
306 Count);
307
308 setAX(ERROR_SUCCESS);
309 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
310 break;
311 }
312
313 /* Get Extended Memory Size */
314 case 0x88:
315 {
316 UCHAR Low, High;
317
318 /*
319 * Return the (usable) extended memory (after 1 MB)
320 * size in kB from CMOS.
321 */
322 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW | CMOS_DISABLE_NMI);
323 Low = IOReadB(CMOS_DATA_PORT);
324 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH | CMOS_DISABLE_NMI);
325 High = IOReadB(CMOS_DATA_PORT);
326 setAX(MAKEWORD(Low, High));
327
328 /* Clear CF */
329 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
330
331 break;
332 }
333
334 /* Switch to Protected Mode */
335 case 0x89:
336 {
337 DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
338
339 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
340 break;
341 }
342
343 /* Get Configuration */
344 case 0xC0:
345 {
346 /* Return the BIOS ROM Configuration Table address in ES:BX */
347 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
348 setES(BIOS_SEGMENT);
349 setBX(0xE6F5);
350
351 /* Call successful; clear CF */
352 setAH(0x00);
353 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
354
355 break;
356 }
357
358 /* Return Extended-Bios Data-Area Segment Address (PS) */
359 case 0xC1:
360 {
361 // Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
362 // setES(???);
363
364 UNIMPLEMENTED;
365
366 /* We do not support EBDA yet */
367 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
368
369 break;
370 }
371
372 /* Pointing Device BIOS Interface (PS) */
373 case 0xC2:
374 {
375 BiosMousePs2Interface(Stack);
376 break;
377 }
378
379 /* Get System Memory Map */
380 case 0xE8:
381 {
382 if (getAL() == 0x01)
383 {
384 /* The amount of memory between 1M and 16M, in kilobytes */
385 ULONG Above1M = (min(MAX_ADDRESS, 0x01000000) - 0x00100000) >> 10;
386
387 /* The amount of memory above 16M, in 64K blocks */
388 ULONG Above16M = (MAX_ADDRESS > 0x01000000) ? (MAX_ADDRESS - 0x01000000) >> 16: 0;
389
390 setAX(Above1M);
391 setBX(Above16M);
392 setCX(Above1M);
393 setDX(Above16M);
394
395 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
396 }
397 else if (getAL() == 0x20 && getEDX() == 'SMAP')
398 {
399 ULONG Offset = getEBX();
400 ULONG Length;
401 ULONG BytesWritten = 0;
402 BOOLEAN Hooked;
403 PBIOS_MEMORY_MAP Map = (PBIOS_MEMORY_MAP)SEG_OFF_TO_PTR(getES(), getDI());
404
405 /* Assume the buffer won't be large enough */
406 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
407
408 while (BytesWritten < getECX() && (ULONG_PTR)Map < (MAX_ADDRESS - sizeof(BIOS_MEMORY_MAP)))
409 {
410 /* Let's ask our memory controller */
411 if (!MemQueryMemoryZone(Offset, &Length, &Hooked))
412 {
413 /* No more memory blocks */
414 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
415 break;
416 }
417
418 Map->BaseAddress = (ULONGLONG)Offset;
419 Map->Length = (ULONGLONG)Length;
420 Map->Type = Hooked ? BIOS_MEMORY_RESERVED : BIOS_MEMORY_AVAILABLE;
421
422 /* Go to the next record */
423 Map++;
424 Offset += Length;
425 BytesWritten += sizeof(BIOS_MEMORY_MAP);
426 }
427
428 setEAX('SMAP');
429 setEBX(Offset);
430 setECX(BytesWritten);
431 }
432 else
433 {
434 DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
435 getAL(),
436 getEDX());
437 }
438
439 break;
440 }
441
442 default:
443 {
444 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
445 getAH());
446 }
447 }
448 }
449
450 static VOID WINAPI BiosRomBasic(LPWORD Stack)
451 {
452 /* ROM Basic is unsupported, display a message to the user */
453 DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing.");
454
455 /* Stop the VDM */
456 EmulatorTerminate();
457 return;
458 }
459
460
461 VOID DosBootsectorInitialize(VOID);
462
463 static VOID WINAPI BiosBootstrapLoader(LPWORD Stack)
464 {
465 /*
466 * In real BIOSes one loads the bootsector read from a diskette
467 * or from a disk, copy it to 0000:7C00 and then boot it.
468 * Since we are 32-bit VM and we hardcode our DOS at the moment,
469 * just call the DOS 32-bit initialization code.
470 */
471
472 DPRINT("BiosBootstrapLoader -->\n");
473
474 /* Load DOS */
475 DosBootsectorInitialize();
476
477 /*
478 * Position CPU to 0000:7C00 to boot the OS.
479 *
480 * Since we are called via the INT32 mechanism, we need to correctly set
481 * CS:IP, not by changing the current one (otherwise the interrupt could
482 * not be clean up and return properly), but by changing the CS:IP in the
483 * stack, so that when the interrupt returns, the modified CS:IP is popped
484 * off the stack and the CPU is correctly repositioned.
485 */
486 Stack[STACK_CS] = 0x0000;
487 Stack[STACK_IP] = 0x7C00;
488
489 DPRINT("<-- BiosBootstrapLoader\n");
490 }
491
492 static VOID WINAPI BiosTimeService(LPWORD Stack)
493 {
494 switch (getAH())
495 {
496 case 0x00:
497 {
498 /* Set AL to 1 if midnight had passed, 0 otherwise */
499 setAL(Bda->MidnightPassed ? 0x01 : 0x00);
500
501 /* Return the tick count in CX:DX */
502 setCX(HIWORD(Bda->TickCounter));
503 setDX(LOWORD(Bda->TickCounter));
504
505 /* Reset the midnight flag */
506 Bda->MidnightPassed = FALSE;
507
508 break;
509 }
510
511 case 0x01:
512 {
513 /* Set the tick count to CX:DX */
514 Bda->TickCounter = MAKELONG(getDX(), getCX());
515
516 /* Reset the midnight flag */
517 Bda->MidnightPassed = FALSE;
518
519 break;
520 }
521
522 default:
523 {
524 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
525 getAH());
526 }
527 }
528 }
529
530 static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
531 {
532 /* Increase the system tick count */
533 Bda->TickCounter++;
534 }
535
536
537 // From SeaBIOS
538 static VOID PicSetIRQMask(USHORT off, USHORT on)
539 {
540 UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8;
541 IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on);
542 IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on);
543 }
544
545 // From SeaBIOS
546 VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func)
547 {
548 UCHAR vector;
549
550 PicSetIRQMask(1 << hwirq, 0);
551 if (hwirq < 8)
552 vector = BIOS_PIC_MASTER_INT + hwirq;
553 else
554 vector = BIOS_PIC_SLAVE_INT + hwirq - 8;
555
556 RegisterBiosInt32(vector, func);
557 }
558
559
560 VOID PicIRQComplete(LPWORD Stack)
561 {
562 /* Get the interrupt number */
563 BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]);
564
565 /*
566 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
567 */
568
569 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
570 {
571 /* It was an IRQ from the master PIC */
572 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
573 }
574 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
575 {
576 /* It was an IRQ from the slave PIC */
577 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
578 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
579 }
580 }
581
582 static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack)
583 {
584 BYTE IrqNumber;
585
586 IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
587 IrqNumber = IOReadB(PIC_MASTER_CMD);
588
589 DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber);
590
591 PicIRQComplete(Stack);
592 }
593
594 static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack)
595 {
596 BYTE IrqNumber;
597
598 IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
599 IrqNumber = IOReadB(PIC_SLAVE_CMD);
600
601 DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber);
602
603 PicIRQComplete(Stack);
604 }
605
606 // Timer IRQ 0
607 static VOID WINAPI BiosTimerIrq(LPWORD Stack)
608 {
609 /*
610 * Perform the system timer interrupt.
611 *
612 * Do not call directly BiosSystemTimerInterrupt(Stack);
613 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
614 * for their purpose...
615 */
616 Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT);
617 // BiosSystemTimerInterrupt(Stack);
618 PicIRQComplete(Stack);
619 }
620
621
622 static VOID BiosHwSetup(VOID)
623 {
624 /* Initialize the master and the slave PICs (cascade mode) */
625 IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
626 IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
627
628 /*
629 * Set the interrupt vector offsets for each PIC
630 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
631 */
632 IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
633 IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT );
634
635 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
636 IOWriteB(PIC_MASTER_DATA, 1 << 2);
637 /* Tell the slave PIC its cascade identity */
638 IOWriteB(PIC_SLAVE_DATA , 2);
639
640 /* Make sure both PICs are in 8086 mode */
641 IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086);
642 IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086);
643
644 /* Clear the masks for both PICs */
645 // IOWriteB(PIC_MASTER_DATA, 0x00);
646 // IOWriteB(PIC_SLAVE_DATA , 0x00);
647 /* Disable all IRQs */
648 IOWriteB(PIC_MASTER_DATA, 0xFF);
649 IOWriteB(PIC_SLAVE_DATA , 0xFF);
650
651
652 /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
653 // NOTE: Some BIOSes set it to Mode 3 instead.
654 IOWriteB(PIT_COMMAND_PORT, 0x34);
655 // 18.2Hz refresh rate
656 IOWriteB(PIT_DATA_PORT(0), 0x00);
657 IOWriteB(PIT_DATA_PORT(0), 0x00);
658
659 /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
660 IOWriteB(PIT_COMMAND_PORT, 0x54);
661 // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
662 IOWriteB(PIT_DATA_PORT(1), 18);
663
664 /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
665 IOWriteB(PIT_COMMAND_PORT, 0xB6);
666 // Count for 440Hz
667 IOWriteB(PIT_DATA_PORT(2), 0x97);
668 IOWriteB(PIT_DATA_PORT(2), 0x0A);
669
670 EnableHwIRQ(0, BiosTimerIrq);
671 }
672
673 static VOID InitializeBiosInt32(VOID)
674 {
675 USHORT i;
676
677 /* Initialize the callback context */
678 InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000);
679
680 /* Register the default BIOS interrupt vectors */
681
682 /* Zero out all of the IVT (0x00 -- 0xFF) */
683 RtlZeroMemory(BaseAddress, 0x0100 * sizeof(ULONG));
684
685 #ifdef ADVANCED_DEBUGGING
686 // Initialize all the interrupt vectors to the default one.
687 for (i = 0x00; i <= 0xFF; i++)
688 RegisterBiosInt32(i, NULL);
689 #endif
690
691 /* Initialize the exception interrupt vectors to a default Exception handler */
692 for (i = 0x00; i <= 0x07; i++)
693 RegisterBiosInt32(i, BiosException);
694
695 /* Initialize HW interrupt vectors to a default HW handler */
696 for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) // 0x08 -- 0x0F
697 RegisterBiosInt32(i, BiosHandleMasterPicIRQ);
698 for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++) // 0x70 -- 0x77
699 RegisterBiosInt32(i, BiosHandleSlavePicIRQ);
700
701 /* Initialize software vector handlers */
702 // BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
703 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService );
704 RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize );
705 // BIOS_DISK_INTERRUPT : 0x13 -- UNIMPLEMENTED
706 // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
707 RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService );
708 // BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
709 // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
710 RegisterBiosInt32(BIOS_ROM_BASIC , BiosRomBasic );
711 RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER , BiosBootstrapLoader );
712 RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService );
713 // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
714 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
715
716 /* Vectors that should be implemented (see above) */
717 RegisterBiosInt32(0x13, NULL);
718 RegisterBiosInt32(0x14, NULL);
719 RegisterBiosInt32(0x17, NULL);
720 RegisterBiosInt32(0x1B, NULL);
721 RegisterBiosInt32(0x4A, NULL); // User Alarm Handler
722
723 /* Relocated services by the BIOS (when needed) */
724 RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
725 RegisterBiosInt32(0x42, NULL); // Relocated Default INT 10h Video Services
726
727 /* Miscellaneous unimplemented vector handlers that should better have a default one */
728 RegisterBiosInt32(0x4B, NULL); // Virtual DMA Specification Services
729 RegisterBiosInt32(0x5C, NULL); // NetBIOS
730
731 // ROM-BASIC interrupts span from 0x80 up to 0xEF.
732 // They don't have any default handler at the moment.
733
734 /* Some vectors are in fact addresses to tables */
735 ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL; // Video Parameter Tables
736 ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL; // Diskette Parameters
737 ((PULONG)BaseAddress)[0x1F] = (ULONG)NULL; // 8x8 Graphics Font
738 ((PULONG)BaseAddress)[0x41] = (ULONG)NULL; // Hard Disk 0 Parameter Table Address
739 ((PULONG)BaseAddress)[0x43] = (ULONG)NULL; // Character Table (EGA, MCGA, VGA)
740 ((PULONG)BaseAddress)[0x46] = (ULONG)NULL; // Hard Disk 1 Drive Parameter Table Address
741 /* Tables that are always uninitialized */
742 ((PULONG)BaseAddress)[0x44] = (ULONG)NULL; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
743 ((PULONG)BaseAddress)[0x48] = (ULONG)NULL; // Cordless Keyboard Translation (PCjr)
744 ((PULONG)BaseAddress)[0x49] = (ULONG)NULL; // Non-Keyboard Scan-code Translation Table (PCJr)
745 }
746
747 static VOID InitializeBiosData(VOID)
748 {
749 UCHAR Low, High;
750
751 /* Initialize the BDA contents */
752 RtlZeroMemory(Bda, sizeof(*Bda));
753 Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
754
755 /*
756 * Retrieve the conventional memory size
757 * in kB from CMOS, typically 640 kB.
758 */
759 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW | CMOS_DISABLE_NMI);
760 Low = IOReadB(CMOS_DATA_PORT);
761 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH | CMOS_DISABLE_NMI);
762 High = IOReadB(CMOS_DATA_PORT);
763 Bda->MemorySize = MAKEWORD(Low, High);
764 }
765
766 static VOID InitializeBiosInfo(VOID)
767 {
768 RtlZeroMemory(Bct, sizeof(*Bct));
769
770 Bct->Length = sizeof(*Bct);
771 Bct->Model = BIOS_MODEL;
772 Bct->SubModel = BIOS_SUBMODEL;
773 Bct->Revision = BIOS_REVISION;
774 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
775 Bct->Feature[1] = 0x00; // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
776 Bct->Feature[2] = 0x00;
777 Bct->Feature[3] = 0x00;
778 Bct->Feature[4] = 0x00;
779 }
780
781
782
783 /*
784 * The BIOS POST (Power On-Self Test)
785 */
786 static VOID
787 WINAPI
788 Bios32Post(LPWORD Stack)
789 {
790 #if 0
791 BOOLEAN Success;
792 #endif
793 BYTE ShutdownStatus;
794
795 DPRINT("Bios32Post\n");
796
797 /* Disable interrupts */
798 setIF(0);
799
800 /* Initialize the stack */
801 // That's what says IBM... (stack at 30:00FF going downwards)
802 // setSS(0x0000);
803 // setSP(0x0400);
804 setSS(0x0050); // Stack at 50:0400, going downwards
805 setSP(0x0400);
806
807 /* Set data segment */
808 setDS(BDA_SEGMENT);
809
810 /*
811 * Perform early CMOS shutdown status checks
812 */
813
814 /* Read the CMOS shutdown status byte and reset it */
815 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
816 ShutdownStatus = IOReadB(CMOS_DATA_PORT);
817 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
818 IOWriteB(CMOS_DATA_PORT, 0x00);
819
820 DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
821 Bda->SoftReset, ShutdownStatus);
822
823 switch (ShutdownStatus)
824 {
825 /* Shutdown after Memory Tests (unsupported) */
826 case 0x01: case 0x02: case 0x03:
827 /* Shutdown after Protected Mode Tests (unsupported) */
828 case 0x06: case 0x07: case 0x08:
829 /* Shutdown after Block Move Test (unsupported) */
830 case 0x09:
831 {
832 DisplayMessage(L"Unsupported CMOS Shutdown Status value 0x%02X. The VDM is stopping...", ShutdownStatus);
833 EmulatorTerminate();
834 return;
835 }
836
837 /* Shutdown to Boot Loader */
838 case 0x04:
839 {
840 DPRINT1("Fast restart to Bootstrap Loader...\n");
841 goto Quit; // Reenable interrupts and exit.
842 }
843
844 /* Flush keyboard, issue an EOI... */
845 case 0x05:
846 {
847 IOReadB(PS2_DATA_PORT);
848
849 /* Send EOI */
850 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
851 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
852
853 // Fall back
854 }
855
856 /*
857 * ... and far JMP to user-specified location at 0040:0067
858 * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
859 */
860 case 0x0A:
861 {
862 DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
863 HIWORD(Bda->ResumeEntryPoint),
864 LOWORD(Bda->ResumeEntryPoint));
865
866 /* Position execution pointers and return with interrupts disabled */
867 setCS(HIWORD(Bda->ResumeEntryPoint));
868 setIP(LOWORD(Bda->ResumeEntryPoint));
869 return;
870 }
871
872 /* Soft reset or unexpected shutdown... */
873 case 0x00:
874 /* ... or other possible shutdown codes: just continue the POST */
875 default:
876 break;
877 }
878
879 /*
880 * FIXME: UNIMPLEMENTED!
881 * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
882 * following actions:
883 * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
884 * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
885 */
886
887 // FIXME: This is a debug temporary check:
888 // Since NTVDM memory is by default initialized with 0xCC, it is also
889 // the case for the BDA. So at first boot we get SoftReset == 0xCCCC.
890 // After we zero out the BDA and put valid values in it.
891 // If for some reason an app calls the BIOS initialization code,
892 // SoftReset is normally zero (unless the app puts a non-null value in SoftReset)
893 // and we can detect that. With the current state of NTVDM, apps calling
894 // by hand the BIOS init code is a sign of a bug, e.g. see MSD.EXE version 2+.
895 if (Bda->SoftReset != 0xCCCC)
896 {
897 DisplayMessage(L"NTVDM is performing a COLD reboot! The program you are currently testing seems to not behave correctly! The VDM is stopping...");
898 EmulatorTerminate();
899 return;
900 }
901
902 /* Initialize the BDA and the BIOS ROM Information */
903 InitializeBiosData();
904 InitializeBiosInfo();
905
906 /*
907 * Initialize IVT and hardware
908 */
909
910 /* Register the BIOS 32-bit Interrupts */
911 InitializeBiosInt32();
912
913 /* Initialize platform hardware (PIC/PIT chips, ...) */
914 BiosHwSetup();
915
916 /* Initialize the Keyboard, Video and Mouse BIOS */
917 if (!KbdBios32Initialize() || !VidBios32Initialize() || !MouseBios32Initialize())
918 {
919 /* Stop the VDM */
920 EmulatorTerminate();
921 return;
922 }
923
924 #if 0
925 /* Initialize the Keyboard and Video BIOS */
926 if (!KbdBiosInitialize() || !VidBiosInitialize())
927 {
928 /* Stop the VDM */
929 EmulatorTerminate();
930 return;
931 }
932 #endif
933
934 ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! /////////////////////
935
936 #if 0
937 /* Load some ROMs */
938 Success = LoadRom("boot.bin", (PVOID)0xE0000, NULL);
939 DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError());
940 #endif
941
942 SearchAndInitRoms(&BiosContext);
943
944 /*
945 * End of the 32-bit POST portion. We then fall back into 16-bit where
946 * the rest of the POST code is executed, typically calling INT 19h
947 * to boot up the OS.
948 */
949
950 Quit:
951 /* Enable interrupts */
952 setIF(1);
953 }
954
955
956 /* PUBLIC FUNCTIONS ***********************************************************/
957
958 BOOLEAN Bios32Initialize(VOID)
959 {
960 /*
961 * Initialize BIOS32 static data
962 */
963
964 /* Bootstrap code */
965 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE05B), PostCode , sizeof(PostCode ));
966 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF0), Bootstrap, sizeof(Bootstrap));
967
968 /* System BIOS Copyright */
969 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE000), BiosCopyright, sizeof(BiosCopyright)-1);
970
971 /* System BIOS Version */
972 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xE080), BiosVersion, sizeof(BiosVersion)-1);
973 // FIXME: or E061, or E100 ??
974
975 /* System BIOS Date */
976 RtlCopyMemory(SEG_OFF_TO_PTR(0xF000, 0xFFF5), BiosDate, sizeof(BiosDate)-1);
977
978 /* System BIOS Model (same as Bct->Model) */
979 *(PBYTE)(SEG_OFF_TO_PTR(0xF000, 0xFFFE)) = BIOS_MODEL;
980
981 /* Redefine our POST function */
982 RegisterBop(BOP_RESET, Bios32Post);
983
984 /* We are done */
985 return TRUE;
986 }
987
988 VOID Bios32Cleanup(VOID)
989 {
990 MouseBios32Cleanup();
991 VidBios32Cleanup();
992 KbdBios32Cleanup();
993 }
994
995 /* EOF */