2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
22 #include "registers.h"
24 /* PRIVATE VARIABLES **********************************************************/
27 static BYTE BiosKeyboardMap
[256];
28 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
29 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
30 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
31 static HANDLE InputThread
= NULL
;
34 * VGA Register Configurations for BIOS Video Modes
35 * The configurations come from DosBox.
37 static BYTE VideoMode_40x25_text
[] =
39 /* Miscellaneous Register */
42 /* Sequencer Registers */
43 0x00, 0x08, 0x03, 0x00, 0x07,
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
49 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
50 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
54 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
55 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
58 static BYTE VideoMode_80x25_text
[] =
60 /* Miscellaneous Register */
63 /* Sequencer Registers */
64 0x00, 0x00, 0x03, 0x00, 0x07,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
70 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
71 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
75 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
76 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
79 static BYTE VideoMode_320x200_4color
[] =
81 /* Miscellaneous Register */
84 /* Sequencer Registers */
85 0x00, 0x09, 0x00, 0x00, 0x02,
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
91 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
96 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
97 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
100 static BYTE VideoMode_640x200_2color
[] =
102 /* Miscellaneous Register */
105 /* Sequencer Registers */
106 0x00, 0x09, 0x0F, 0x00, 0x02,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
112 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
117 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
118 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00
121 static BYTE VideoMode_320x200_16color
[] =
123 /* Miscellaneous Register */
126 /* Sequencer Registers */
127 0x00, 0x09, 0x0F, 0x00, 0x02,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
133 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
138 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
139 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
142 static BYTE VideoMode_640x200_16color
[] =
144 /* Miscellaneous Register */
147 /* Sequencer Registers */
148 0x00, 0x01, 0x0F, 0x00, 0x02,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
154 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
159 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
160 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
163 static BYTE VideoMode_640x350_16color
[] =
165 /* Miscellaneous Register */
168 /* Sequencer Registers */
169 0x00, 0x01, 0x0F, 0x00, 0x02,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
175 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
180 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
181 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
184 static BYTE VideoMode_640x480_2color
[] =
186 /* Miscellaneous Register */
189 /* Sequencer Registers */
190 0x00, 0x01, 0x0F, 0x00, 0x02,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
196 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
201 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
202 0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
205 static BYTE VideoMode_640x480_16color
[] =
207 /* Miscellaneous Register */
210 /* Sequencer Registers */
211 0x00, 0x01, 0x0F, 0x00, 0x02,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
217 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
222 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
223 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
226 static BYTE VideoMode_320x200_256color
[] =
228 /* Miscellaneous Register */
231 /* Sequencer Registers */
232 0x00, 0x01, 0x0F, 0x00, 0x0E,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
238 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
243 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
244 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
247 static LPBYTE VideoModes
[] =
249 VideoMode_40x25_text
, /* Mode 00h */
250 VideoMode_40x25_text
, /* Mode 01h */
251 VideoMode_80x25_text
, /* Mode 02h */
252 VideoMode_80x25_text
, /* Mode 03h */
253 VideoMode_320x200_4color
, /* Mode 04h */
254 VideoMode_320x200_4color
, /* Mode 05h */
255 VideoMode_640x200_2color
, /* Mode 06h */
262 VideoMode_320x200_16color
, /* Mode 0Dh */
263 VideoMode_640x200_16color
, /* Mode 0Eh */
265 VideoMode_640x350_16color
, /* Mode 10h */
266 VideoMode_640x480_2color
, /* Mode 11h */
267 VideoMode_640x480_16color
, /* Mode 12h */
268 VideoMode_320x200_256color
, /* Mode 13h */
271 /* PRIVATE FUNCTIONS **********************************************************/
273 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
275 /* Get the location of the element after the tail */
276 WORD NextElement
= Bda
->KeybdBufferTail
+ sizeof(WORD
);
278 /* Wrap it around if it's at or beyond the end */
279 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
281 /* If it's full, fail */
282 if (NextElement
== Bda
->KeybdBufferHead
) return FALSE
;
284 /* Put the value in the queue */
285 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
286 Bda
->KeybdBufferTail
+= sizeof(WORD
);
288 /* Check if we are at, or have passed, the end of the buffer */
289 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
291 /* Return it to the beginning */
292 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
299 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
301 /* If it's empty, fail */
302 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
304 /* Otherwise, get the value and return success */
305 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
310 static BOOLEAN
BiosKbdBufferPop(VOID
)
312 /* If it's empty, fail */
313 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
315 /* Remove the value from the queue */
316 Bda
->KeybdBufferHead
+= sizeof(WORD
);
318 /* Check if we are at, or have passed, the end of the buffer */
319 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
321 /* Return it to the beginning */
322 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
329 static VOID
BiosReadWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
334 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
336 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
338 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
340 /* Read from video memory */
341 VgaReadMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
345 /* Write the data to the buffer in row order */
346 Buffer
[Counter
++] = Character
;
351 static VOID
BiosWriteWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
356 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
358 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
360 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
362 Character
= Buffer
[Counter
++];
364 /* Write to video memory */
365 VgaWriteMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
372 /* PUBLIC FUNCTIONS ***********************************************************/
374 BYTE
BiosGetVideoMode(VOID
)
376 return Bda
->VideoMode
;
379 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
383 LPBYTE Values
= VideoModes
[ModeNumber
];
385 DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber
, Values
);
387 if (Values
== NULL
) return FALSE
;
389 /* Write the misc register */
390 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
392 /* Write the sequencer registers */
393 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
395 VgaWritePort(VGA_SEQ_INDEX
, i
);
396 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
399 /* Write the GC registers */
400 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
402 VgaWritePort(VGA_GC_INDEX
, i
);
403 VgaWritePort(VGA_GC_DATA
, *(Values
++));
406 /* Write the CRTC registers */
407 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
409 VgaWritePort(VGA_CRTC_INDEX
, i
);
410 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
413 /* Write the AC registers */
414 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
416 VgaWritePort(VGA_AC_INDEX
, i
);
417 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
420 /* Reset the palette */
423 /* Update the values in the BDA */
424 Bda
->VideoMode
= ModeNumber
;
426 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
427 Bda
->VideoPageOffset
= 0;
429 /* Get the character height */
430 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_MAX_SCAN_LINE_REG
);
431 Bda
->CharacterHeight
= 1 + (VgaReadPort(VGA_CRTC_DATA
) & 0x1F);
433 Resolution
= VgaGetDisplayResolution();
434 Bda
->ScreenColumns
= Resolution
.X
;
435 Bda
->ScreenRows
= Resolution
.Y
- 1;
440 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
442 /* Check if the page exists */
443 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
445 /* Check if this is the same page */
446 if (PageNumber
== Bda
->VideoPage
) return TRUE
;
448 /* Set the values in the BDA */
449 Bda
->VideoPage
= PageNumber
;
450 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
451 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
453 /* Set the start address in the CRTC */
454 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
455 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
456 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
457 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
462 BOOLEAN
BiosInitialize(VOID
)
466 LPDWORD IntVecTable
= (LPDWORD
)BaseAddress
;
467 LPBYTE BiosCode
= (LPBYTE
)SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0);
469 /* Initialize the BDA */
470 Bda
= (PBIOS_DATA_AREA
)SEG_OFF_TO_PTR(BDA_SEGMENT
, 0);
471 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
473 * Conventional memory size is 640 kB,
474 * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
475 * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
476 * for more information.
478 Bda
->MemorySize
= 0x0280;
479 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
480 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
482 /* Generate ISR stubs and fill the IVT */
483 for (i
= 0x00; i
<= 0xFF; i
++)
485 IntVecTable
[i
] = MAKELONG(Offset
, BIOS_SEGMENT
);
487 BiosCode
[Offset
++] = 0xFB; // sti
489 BiosCode
[Offset
++] = 0x6A; // push i
490 BiosCode
[Offset
++] = (UCHAR
)i
;
492 BiosCode
[Offset
++] = 0x6A; // push 0
493 BiosCode
[Offset
++] = 0x00;
496 BiosCode
[Offset
++] = 0xF8; // clc
498 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
499 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
500 BiosCode
[Offset
++] = EMULATOR_CTRL_BOP
; // Control BOP
501 BiosCode
[Offset
++] = CTRL_BOP_INT32
; // 32-bit Interrupt dispatcher
503 BiosCode
[Offset
++] = 0x73; // jnc EXIT (offset +3)
504 BiosCode
[Offset
++] = 0x03;
506 // HACK: The following instruction should be HLT!
507 BiosCode
[Offset
++] = 0x90; // nop
509 BiosCode
[Offset
++] = 0xEB; // jmp BOP_SEQ (offset -10)
510 BiosCode
[Offset
++] = 0xF6;
513 BiosCode
[Offset
++] = 0x83; // add sp, 4
514 BiosCode
[Offset
++] = 0xC4;
515 BiosCode
[Offset
++] = 0x04;
517 BiosCode
[Offset
++] = 0xCF; // iret
519 RegisterInt32(BIOS_VIDEO_INTERRUPT
, BiosVideoService
);
520 RegisterInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
521 RegisterInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
522 RegisterInt32(BIOS_KBD_INTERRUPT
, BiosKeyboardService
);
523 RegisterInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
524 RegisterInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
526 /* Get the input handle to the real console, and check for success */
527 BiosConsoleInput
= CreateFileW(L
"CONIN$",
528 GENERIC_READ
| GENERIC_WRITE
,
529 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
534 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
539 /* Get the output handle to the real console, and check for success */
540 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
541 GENERIC_READ
| GENERIC_WRITE
,
542 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
547 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
549 CloseHandle(BiosConsoleInput
);
553 /* Save the console screen buffer information */
554 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
556 CloseHandle(BiosConsoleOutput
);
557 CloseHandle(BiosConsoleInput
);
562 if (!VgaInitialize(BiosConsoleOutput
))
564 CloseHandle(BiosConsoleOutput
);
565 CloseHandle(BiosConsoleInput
);
569 /* Update the cursor position */
570 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
571 BiosSavedBufferInfo
.dwCursorPosition
.X
,
574 /* Set the console input mode */
575 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
577 /* Start the input thread */
578 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, BiosConsoleInput
, 0, NULL
);
580 /* Initialize the PIC */
581 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
582 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
584 /* Set the interrupt offsets */
585 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
586 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
588 /* Tell the master PIC there is a slave at IRQ 2 */
589 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
590 PicWriteData(PIC_SLAVE_DATA
, 2);
592 /* Make sure the PIC is in 8086 mode */
593 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
594 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
596 /* Clear the masks for both PICs */
597 PicWriteData(PIC_MASTER_DATA
, 0x00);
598 PicWriteData(PIC_SLAVE_DATA
, 0x00);
600 PitWriteCommand(0x34);
601 PitWriteData(0, 0x00);
602 PitWriteData(0, 0x00);
607 VOID
BiosCleanup(VOID
)
609 /* Restore the old screen buffer */
610 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
612 /* Restore the screen buffer size */
613 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
615 /* Close the console handles */
616 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
617 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
619 /* Close the input thread handle */
620 if (InputThread
!= NULL
) CloseHandle(InputThread
);
623 WORD
BiosPeekCharacter(VOID
)
625 WORD CharacterData
= 0;
627 /* Get the key from the queue, but don't remove it */
628 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
632 WORD
BiosGetCharacter(VOID
)
634 WORD CharacterData
= 0;
636 /* Check if there is a key available */
637 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
639 /* Get the key from the queue, and remove it */
640 BiosKbdBufferTop(&CharacterData
);
645 /* Set the handler CF to repeat the BOP */
646 EmulatorSetFlag(EMULATOR_FLAG_CF
);
649 return CharacterData
;
652 VOID
BiosGetCursorPosition(PBYTE Row
, PBYTE Column
, BYTE Page
)
654 /* Make sure the selected video page is valid */
655 if (Page
>= BIOS_MAX_PAGES
) return;
657 /* Get the cursor location */
658 *Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
659 *Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
662 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
664 /* Make sure the selected video page is valid */
665 if (Page
>= BIOS_MAX_PAGES
) return;
667 /* Update the position in the BDA */
668 Bda
->CursorPosition
[Page
] = MAKEWORD(Column
, Row
);
670 /* Check if this is the current video page */
671 if (Page
== Bda
->VideoPage
)
673 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
675 /* Modify the CRTC registers */
676 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
677 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
678 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
679 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
683 BOOLEAN
BiosScrollWindow(INT Direction
,
685 SMALL_RECT Rectangle
,
691 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
692 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
694 /* Allocate a buffer for the window */
695 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
697 WindowSize
* sizeof(WORD
));
698 if (WindowData
== NULL
) return FALSE
;
700 /* Read the window data */
701 BiosReadWindow(WindowData
, Rectangle
, Page
);
705 /* Fill the window */
706 for (i
= 0; i
< WindowSize
; i
++)
708 WindowData
[i
] = MAKEWORD(' ', FillAttribute
);
714 // TODO: Scroll the window!
717 /* Write back the window data */
718 BiosWriteWindow(WindowData
, Rectangle
, Page
);
720 /* Free the window buffer */
721 HeapFree(GetProcessHeap(), 0, WindowData
);
726 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
728 WORD CharData
= MAKEWORD(Character
, Attribute
);
731 /* Make sure the page exists */
732 if (Page
>= BIOS_MAX_PAGES
) return;
734 /* Get the cursor location */
735 BiosGetCursorPosition(&Row
, &Column
, Page
);
737 if (Character
== '\a')
739 /* Bell control character */
740 // NOTE: We may use what the terminal emulator offers to us...
744 else if (Character
== '\b')
746 /* Backspace control character */
753 Column
= Bda
->ScreenColumns
- 1;
757 /* Erase the existing character */
758 CharData
= MAKEWORD(' ', Attribute
);
759 EmulatorWriteMemory(&EmulatorContext
,
760 TO_LINEAR(TEXT_VIDEO_SEG
,
761 Page
* Bda
->VideoPageSize
+
762 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
766 else if (Character
== '\t')
768 /* Horizontal Tabulation control character */
772 BiosPrintCharacter(' ', Attribute
, Page
);
773 BiosGetCursorPosition(&Row
, &Column
, Page
);
774 } while (Column
% 8);
776 else if (Character
== '\n')
778 /* Line Feed control character */
781 else if (Character
== '\r')
783 /* Carriage Return control character */
788 /* Default character */
790 /* Write the character */
791 EmulatorWriteMemory(&EmulatorContext
,
792 TO_LINEAR(TEXT_VIDEO_SEG
,
793 Page
* Bda
->VideoPageSize
+
794 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
798 /* Advance the cursor */
802 /* Check if it passed the end of the row */
803 if (Column
>= Bda
->ScreenColumns
)
805 /* Return to the first column and go to the next line */
810 /* Scroll the screen up if needed */
811 if (Row
> Bda
->ScreenRows
)
813 /* The screen must be scrolled up */
814 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
816 BiosScrollWindow(SCROLL_DIRECTION_UP
,
825 /* Set the cursor position */
826 BiosSetCursorPosition(Row
, Column
, Page
);
829 VOID WINAPI
BiosVideoService(LPWORD Stack
)
836 BiosSetVideoMode(getAL());
841 /* Set Text-Mode Cursor Shape */
845 Bda
->CursorStartLine
= getCH();
846 Bda
->CursorEndLine
= getCL();
848 /* Modify the CRTC registers */
849 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
850 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
851 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
852 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
857 /* Set Cursor Position */
860 BiosSetCursorPosition(getDH(), getDL(), getBH());
864 /* Get Cursor Position */
867 /* Make sure the selected video page exists */
868 if (getBH() >= BIOS_MAX_PAGES
) break;
870 /* Return the result */
872 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
873 setDX(Bda
->CursorPosition
[getBH()]);
877 /* Query Light Pen */
881 * On modern BIOSes, this function returns 0
882 * so that we can ignore the other registers.
888 /* Select Active Display Page */
891 BiosSetVideoPage(getAL());
895 /* Scroll Window Up/Down */
899 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
901 /* Call the internal function */
902 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
903 : SCROLL_DIRECTION_DOWN
,
912 /* Read/Write Character From Cursor Position */
917 WORD CharacterData
= MAKEWORD(getAL(), getBL());
921 /* Check if the page exists */
922 if (Page
>= BIOS_MAX_PAGES
) break;
924 /* Find the offset of the character */
925 Offset
= Page
* Bda
->VideoPageSize
+
926 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
927 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
931 /* Read from the video memory */
932 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
933 (LPVOID
)&CharacterData
,
936 /* Return the character in AX */
937 setAX(CharacterData
);
941 /* Write to video memory */
942 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
943 (LPVOID
)&CharacterData
,
944 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
950 /* Teletype Output */
953 BiosPrintCharacter(getAL(), getBL(), getBH());
957 /* Get Current Video Mode */
960 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
961 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
965 /* Palette Control */
970 /* Set Single Palette Register */
973 /* Reset the flip-flop */
974 VgaReadPort(VGA_STAT_COLOR
);
976 /* Write the index */
977 VgaWritePort(VGA_AC_INDEX
, getBL());
980 VgaWritePort(VGA_AC_WRITE
, getBH());
985 /* Set Overscan Color */
988 /* Reset the flip-flop */
989 VgaReadPort(VGA_STAT_COLOR
);
991 /* Write the index */
992 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
995 VgaWritePort(VGA_AC_WRITE
, getBH());
1000 /* Set All Palette Registers */
1004 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1006 /* Set the palette registers */
1007 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1009 /* Reset the flip-flop */
1010 VgaReadPort(VGA_STAT_COLOR
);
1012 /* Write the index */
1013 VgaWritePort(VGA_AC_INDEX
, i
);
1015 /* Write the data */
1016 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
1019 /* Set the overscan register */
1020 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1021 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
1026 /* Get Single Palette Register */
1029 /* Reset the flip-flop */
1030 VgaReadPort(VGA_STAT_COLOR
);
1032 /* Write the index */
1033 VgaWritePort(VGA_AC_INDEX
, getBL());
1036 setBH(VgaReadPort(VGA_AC_READ
));
1041 /* Get Overscan Color */
1044 /* Reset the flip-flop */
1045 VgaReadPort(VGA_STAT_COLOR
);
1047 /* Write the index */
1048 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1051 setBH(VgaReadPort(VGA_AC_READ
));
1056 /* Get All Palette Registers */
1060 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1062 /* Get the palette registers */
1063 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1065 /* Reset the flip-flop */
1066 VgaReadPort(VGA_STAT_COLOR
);
1068 /* Write the index */
1069 VgaWritePort(VGA_AC_INDEX
, i
);
1072 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1075 /* Get the overscan register */
1076 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1077 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1082 /* Set Individual DAC Register */
1085 /* Write the index */
1086 // Certainly in BL and not in BX as said by Ralf Brown...
1087 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1089 /* Write the data in this order: Red, Green, Blue */
1090 VgaWritePort(VGA_DAC_DATA
, getDH());
1091 VgaWritePort(VGA_DAC_DATA
, getCH());
1092 VgaWritePort(VGA_DAC_DATA
, getCL());
1097 /* Set Block of DAC Registers */
1101 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1103 /* Write the index */
1104 // Certainly in BL and not in BX as said by Ralf Brown...
1105 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1107 for (i
= 0; i
< getCX(); i
++)
1109 /* Write the data in this order: Red, Green, Blue */
1110 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1111 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1112 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1118 /* Get Individual DAC Register */
1121 /* Write the index */
1122 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1124 /* Read the data in this order: Red, Green, Blue */
1125 setDH(VgaReadPort(VGA_DAC_DATA
));
1126 setCH(VgaReadPort(VGA_DAC_DATA
));
1127 setCL(VgaReadPort(VGA_DAC_DATA
));
1132 /* Get Block of DAC Registers */
1136 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1138 /* Write the index */
1139 // Certainly in BL and not in BX as said by Ralf Brown...
1140 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1142 for (i
= 0; i
< getCX(); i
++)
1144 /* Write the data in this order: Red, Green, Blue */
1145 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1146 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1147 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1155 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1167 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1169 /* Call the internal function */
1170 BiosScrollWindow(getBL(),
1179 /* Display combination code */
1184 case 0x00: /* Get Display combiantion code */
1185 setAX(MAKEWORD(0x1A, 0x1A));
1186 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1188 case 0x01: /* Set Display combination code */
1189 DPRINT1("Set Display combination code - Unsupported\n");
1199 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1205 VOID WINAPI
BiosEquipmentService(LPWORD Stack
)
1207 /* Return the equipment list */
1208 setAX(Bda
->EquipmentList
);
1211 VOID WINAPI
BiosGetMemorySize(LPWORD Stack
)
1213 /* Return the conventional memory size in kB, typically 640 kB */
1214 setAX(Bda
->MemorySize
);
1217 VOID WINAPI
BiosKeyboardService(LPWORD Stack
)
1221 /* Wait for keystroke and read */
1223 /* Wait for extended keystroke and read */
1224 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1226 /* Read the character (and wait if necessary) */
1227 setAX(BiosGetCharacter());
1231 /* Get keystroke status */
1233 /* Get extended keystroke status */
1234 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1236 WORD Data
= BiosPeekCharacter();
1240 /* There is a character, clear ZF and return it */
1241 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1246 /* No character, set ZF */
1247 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1253 /* Get shift status */
1256 /* Return the lower byte of the keyboard shift status word */
1257 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1264 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1268 /* Push keystroke */
1271 /* Return 0 if success, 1 if failure */
1272 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1276 /* Get extended shift status */
1280 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1281 * but the high byte is organized differently:
1282 * the bytes 2 and 3 of the high byte are not the same...
1284 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1286 /* Return the extended keyboard shift status word */
1287 setAX(KeybdShiftFlags
);
1293 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1299 VOID WINAPI
BiosTimeService(LPWORD Stack
)
1305 /* Set AL to 1 if midnight had passed, 0 otherwise */
1306 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1308 /* Return the tick count in CX:DX */
1309 setCX(HIWORD(Bda
->TickCounter
));
1310 setDX(LOWORD(Bda
->TickCounter
));
1312 /* Reset the midnight flag */
1313 Bda
->MidnightPassed
= FALSE
;
1320 /* Set the tick count to CX:DX */
1321 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1323 /* Reset the midnight flag */
1324 Bda
->MidnightPassed
= FALSE
;
1331 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1337 VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
1339 /* Increase the system tick count */
1343 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1350 /* Perform the system timer interrupt */
1351 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1358 BYTE ScanCode
, VirtualKey
;
1361 /* Loop while there is a scancode available */
1364 /* Get the scan code and virtual key code */
1365 ScanCode
= KeyboardReadData();
1366 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1368 /* Check if this is a key press or release */
1369 if (!(ScanCode
& (1 << 7)))
1372 if (VirtualKey
== VK_NUMLOCK
||
1373 VirtualKey
== VK_CAPITAL
||
1374 VirtualKey
== VK_SCROLL
||
1375 VirtualKey
== VK_INSERT
)
1377 /* For toggle keys, toggle the lowest bit in the keyboard map */
1378 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1381 /* Set the highest bit */
1382 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1384 /* Find out which character this is */
1386 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1392 /* Push it onto the BIOS keyboard queue */
1393 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1397 /* Key release, unset the highest bit */
1398 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1401 while (KeyboardReadStatus() & 1);
1403 /* Clear the keyboard flags */
1404 Bda
->KeybdShiftFlags
= 0;
1406 /* Set the appropriate flags based on the state */
1407 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1408 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1409 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1410 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1411 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1412 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1413 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1414 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1415 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1416 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1417 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1418 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1419 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1420 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1421 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1422 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1428 /* Send End-of-Interrupt to the PIC */
1429 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1430 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);