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 *******************************************************************/
20 /* PRIVATE VARIABLES **********************************************************/
22 static PBIOS_DATA_AREA Bda
;
23 static BYTE BiosKeyboardMap
[256];
24 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
25 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
26 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
29 * VGA Register Configurations for BIOS Video Modes
30 * The configurations come from DosBox.
32 static BYTE VideoMode_40x25_text
[] =
34 /* Miscellaneous Register */
37 /* Sequencer Registers */
38 0x00, 0x08, 0x03, 0x00, 0x07,
41 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
44 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
45 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
49 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x39, 0x0A, 0x3B,
50 0x0C, 0x3D, 0x0E, 0x3F, 0x01, 0x00, 0x12, 0x08, 0x04
53 static BYTE VideoMode_80x25_text
[] =
55 /* Miscellaneous Register */
58 /* Sequencer Registers */
59 0x00, 0x00, 0x03, 0x00, 0x07,
62 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
65 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
66 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
70 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x39, 0x0A, 0x3B,
71 0x0C, 0x3D, 0x0E, 0x3F, 0x01, 0x00, 0x12, 0x08, 0x04
74 static BYTE VideoMode_320x200_4color
[] =
76 /* Miscellaneous Register */
79 /* Sequencer Registers */
80 0x00, 0x09, 0x00, 0x00, 0x02,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
86 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
87 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
91 0x00, 0x13, 0x02, 0x17, 0x04, 0x04, 0x06, 0x07, 0x08, 0x11, 0x0A, 0x13,
92 0x0C, 0x15, 0x0E, 0x17, 0x01, 0x00, 0x12, 0x00, 0x04
95 static BYTE VideoMode_640x200_2color
[] =
97 /* Miscellaneous Register */
100 /* Sequencer Registers */
101 0x00, 0x09, 0x0F, 0x00, 0x02,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
107 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
112 0x00, 0x17, 0x02, 0x17, 0x04, 0x17, 0x06, 0x17, 0x08, 0x17, 0x0A, 0x17,
113 0x0C, 0x17, 0x0E, 0x17, 0x01, 0x00, 0x12, 0x00, 0x04
116 static BYTE VideoMode_320x200_16color
[] =
118 /* Miscellaneous Register */
121 /* Sequencer Registers */
122 0x00, 0x09, 0x0F, 0x00, 0x02,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
128 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
133 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x0A, 0x13,
134 0x0C, 0x15, 0x0E, 0x17, 0x01, 0x00, 0x12, 0x00, 0x04
137 static BYTE VideoMode_640x200_16color
[] =
139 /* Miscellaneous Register */
142 /* Sequencer Registers */
143 0x00, 0x01, 0x0F, 0x00, 0x02,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
149 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
154 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x0A, 0x13,
155 0x0C, 0x15, 0x0E, 0x17, 0x01, 0x00, 0x12, 0x00, 0x04
158 static BYTE VideoMode_640x350_16color
[] =
160 /* Miscellaneous Register */
163 /* Sequencer Registers */
164 0x00, 0x01, 0x0F, 0x00, 0x02,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
170 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
175 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x39, 0x0A, 0x3B,
176 0x0C, 0x3D, 0x0E, 0x3F, 0x01, 0x00, 0x12, 0x00, 0x04
179 static BYTE VideoMode_640x480_2color
[] =
181 /* Miscellaneous Register */
184 /* Sequencer Registers */
185 0x00, 0x01, 0x0F, 0x00, 0x02,
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
191 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
196 0x00, 0x3F, 0x02, 0x3F, 0x04, 0x3F, 0x06, 0x3F, 0x08, 0x3F, 0x0A, 0x3F,
197 0x0C, 0x3F, 0x0E, 0x3F, 0x01, 0x00, 0x12, 0x00, 0x04
200 static BYTE VideoMode_640x480_16color
[] =
202 /* Miscellaneous Register */
205 /* Sequencer Registers */
206 0x00, 0x01, 0x0F, 0x00, 0x02,
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
212 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
217 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x39, 0x0A, 0x3B,
218 0x0C, 0x3D, 0x0E, 0x3F, 0x01, 0x00, 0x12, 0x00, 0x04
221 static BYTE VideoMode_320x200_256color
[] =
223 /* Miscellaneous Register */
226 /* Sequencer Registers */
227 0x00, 0x01, 0x0F, 0x00, 0x0E,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
233 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
238 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
239 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x12, 0x00, 0x04
242 static LPBYTE VideoModes
[] =
244 VideoMode_40x25_text
, /* Mode 00h */
245 VideoMode_40x25_text
, /* Mode 01h */
246 VideoMode_80x25_text
, /* Mode 02h */
247 VideoMode_80x25_text
, /* Mode 03h */
248 VideoMode_320x200_4color
, /* Mode 04h */
249 VideoMode_320x200_4color
, /* Mode 05h */
250 VideoMode_640x200_2color
, /* Mode 06h */
257 VideoMode_320x200_16color
, /* Mode 0Dh */
258 VideoMode_640x200_16color
, /* Mode 0Eh */
260 VideoMode_640x350_16color
, /* Mode 10h */
261 VideoMode_640x480_2color
, /* Mode 11h */
262 VideoMode_640x480_16color
, /* Mode 12h */
263 VideoMode_320x200_256color
, /* Mode 13h */
266 /* PRIVATE FUNCTIONS **********************************************************/
268 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
270 /* Get the location of the element after the head */
271 WORD NextElement
= Bda
->KeybdBufferHead
+ 2;
273 /* Wrap it around if it's at or beyond the end */
274 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
276 /* If it's full, fail */
277 if (NextElement
== Bda
->KeybdBufferTail
) return FALSE
;
279 /* Put the value in the queue */
280 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
281 Bda
->KeybdBufferTail
+= sizeof(WORD
);
283 /* Check if we are at, or have passed, the end of the buffer */
284 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
286 /* Return it to the beginning */
287 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
294 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
296 /* If it's empty, fail */
297 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
299 /* Otherwise, get the value and return success */
300 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
305 static BOOLEAN
BiosKbdBufferPop(VOID
)
307 /* If it's empty, fail */
308 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
310 /* Remove the value from the queue */
311 Bda
->KeybdBufferHead
+= sizeof(WORD
);
313 /* Check if we are at, or have passed, the end of the buffer */
314 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
316 /* Return it to the beginning */
317 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
324 static VOID
BiosReadWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
329 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
331 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
333 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
335 /* Read from video memory */
336 VgaReadMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
340 /* Write the data to the buffer in row order */
341 Buffer
[Counter
++] = Character
;
346 static VOID
BiosWriteWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
351 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
353 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
355 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
357 Character
= Buffer
[Counter
++];
359 /* Read from video memory */
360 VgaWriteMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
367 /* PUBLIC FUNCTIONS ***********************************************************/
369 BYTE
BiosGetVideoMode(VOID
)
371 return Bda
->VideoMode
;
374 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
378 LPBYTE Values
= VideoModes
[ModeNumber
];
380 if (Values
== NULL
) return FALSE
;
382 /* Write the misc register */
383 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
385 /* Write the sequencer registers */
386 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
388 VgaWritePort(VGA_SEQ_INDEX
, i
);
389 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
392 /* Write the GC registers */
393 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
395 VgaWritePort(VGA_GC_INDEX
, i
);
396 VgaWritePort(VGA_GC_DATA
, *(Values
++));
399 /* Write the CRTC registers */
400 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
402 VgaWritePort(VGA_CRTC_INDEX
, i
);
403 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
406 /* Write the AC registers */
407 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
409 VgaWritePort(VGA_AC_INDEX
, i
);
410 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
413 /* Update the values in the BDA */
414 Bda
->VideoMode
= ModeNumber
;
416 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
417 Bda
->VideoPageOffset
= 0;
419 /* Get the character height */
420 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_MAX_SCAN_LINE_REG
);
421 Bda
->CharacterHeight
= 1 + (VgaReadPort(VGA_CRTC_DATA
) & 0x1F);
423 Resolution
= VgaGetDisplayResolution();
424 Bda
->ScreenColumns
= Resolution
.X
;
425 Bda
->ScreenRows
= Resolution
.Y
- 1;
430 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
432 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
434 /* Set the values in the BDA */
435 Bda
->VideoPage
= PageNumber
;
436 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
437 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
439 /* Set the start address in the CRTC */
440 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
441 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
442 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
443 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
448 BOOLEAN
BiosInitialize(VOID
)
452 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
453 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
455 /* Initialize the BDA */
456 Bda
= (PBIOS_DATA_AREA
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BDA_SEGMENT
, 0));
457 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
458 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
459 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
461 /* Generate ISR stubs and fill the IVT */
462 for (i
= 0; i
< 256; i
++)
464 IntVecTable
[i
* 2] = Offset
;
465 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
467 BiosCode
[Offset
++] = 0xFA; // cli
469 BiosCode
[Offset
++] = 0x6A; // push i
470 BiosCode
[Offset
++] = (BYTE
)i
;
472 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
473 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
474 BiosCode
[Offset
++] = LOBYTE(EMULATOR_INT_BOP
);
475 BiosCode
[Offset
++] = HIBYTE(EMULATOR_INT_BOP
);
477 BiosCode
[Offset
++] = 0x83; // add sp, 2
478 BiosCode
[Offset
++] = 0xC4;
479 BiosCode
[Offset
++] = 0x02;
481 BiosCode
[Offset
++] = 0xCF; // iret
484 /* Get the input handle to the real console, and check for success */
485 BiosConsoleInput
= CreateFileW(L
"CONIN$",
486 GENERIC_READ
| GENERIC_WRITE
,
487 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
492 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
497 /* Get the output handle to the real console, and check for success */
498 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
499 GENERIC_READ
| GENERIC_WRITE
,
500 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
505 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
507 CloseHandle(BiosConsoleInput
);
511 /* Save the console screen buffer information */
512 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
514 CloseHandle(BiosConsoleOutput
);
515 CloseHandle(BiosConsoleInput
);
520 if (!VgaInitialize(BiosConsoleOutput
))
522 CloseHandle(BiosConsoleOutput
);
523 CloseHandle(BiosConsoleInput
);
527 /* Update the cursor position */
528 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
529 BiosSavedBufferInfo
.dwCursorPosition
.X
,
532 /* Set the console input mode */
533 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
535 /* Initialize the PIC */
536 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
537 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
539 /* Set the interrupt offsets */
540 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
541 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
543 /* Tell the master PIC there is a slave at IRQ 2 */
544 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
545 PicWriteData(PIC_SLAVE_DATA
, 2);
547 /* Make sure the PIC is in 8086 mode */
548 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
549 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
551 /* Clear the masks for both PICs */
552 PicWriteData(PIC_MASTER_DATA
, 0x00);
553 PicWriteData(PIC_SLAVE_DATA
, 0x00);
555 PitWriteCommand(0x34);
556 PitWriteData(0, 0x00);
557 PitWriteData(0, 0x00);
562 VOID
BiosCleanup(VOID
)
564 /* Restore the old screen buffer */
565 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
567 /* Restore the screen buffer size */
568 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
570 /* Close the console handles */
571 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
572 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
575 WORD
BiosPeekCharacter(VOID
)
579 /* Check if there is a key available */
580 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return 0xFFFF;
582 /* Get the key from the queue, but don't remove it */
583 BiosKbdBufferTop(&CharacterData
);
585 return CharacterData
;
588 WORD
BiosGetCharacter(VOID
)
591 INPUT_RECORD InputRecord
;
594 /* Check if there is a key available */
595 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
597 /* Get the key from the queue, and remove it */
598 BiosKbdBufferTop(&CharacterData
);
605 /* Wait for a console event */
606 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
608 /* Read the event, and make sure it's a keypress */
609 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
610 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
611 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
613 /* Save the scan code and end the loop */
614 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
615 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
621 return CharacterData
;
624 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
626 /* Make sure the selected video page is valid */
627 if (Page
>= BIOS_MAX_PAGES
) return;
629 /* Update the position in the BDA */
630 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
632 /* Check if this is the current video page */
633 if (Page
== Bda
->VideoPage
)
635 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
637 /* Modify the CRTC registers */
638 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
639 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
640 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
641 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
645 BOOLEAN
BiosScrollWindow(INT Direction
,
647 SMALL_RECT Rectangle
,
653 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
654 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
656 /* Allocate a buffer for the window */
657 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
659 WindowSize
* sizeof(WORD
));
660 if (WindowData
== NULL
) return FALSE
;
662 /* Read the window data */
663 BiosReadWindow(WindowData
, Rectangle
, Page
);
667 /* Fill the window */
668 for (i
= 0; i
< WindowSize
; i
++)
670 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
676 // TODO: Scroll the window!
679 /* Write back the window data */
680 BiosWriteWindow(WindowData
, Rectangle
, Page
);
682 /* Free the window buffer */
683 HeapFree(GetProcessHeap(), 0, WindowData
);
688 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
690 WORD CharData
= (Attribute
<< 8) | Character
;
693 /* Make sure the page exists */
694 if (Page
>= BIOS_MAX_PAGES
) return;
696 /* Get the cursor location */
697 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
698 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
700 /* Write the character */
701 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
702 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
706 /* Advance the cursor */
709 /* Check if it passed the end of the row */
710 if (Column
== Bda
->ScreenColumns
)
712 /* Return to the first column */
715 if (Row
== Bda
->ScreenRows
)
717 /* The screen must be scrolled */
718 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
720 BiosScrollWindow(SCROLL_DIRECTION_UP
,
729 /* Set the cursor position */
730 BiosSetCursorPosition(Row
, Column
, Page
);
733 VOID
BiosVideoService(LPWORD Stack
)
735 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
736 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
737 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
738 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
745 BiosSetVideoMode(LOBYTE(Eax
));
751 /* Set Text-Mode Cursor Shape */
755 Bda
->CursorStartLine
= HIBYTE(Ecx
);
756 Bda
->CursorEndLine
= LOBYTE(Ecx
);
758 /* Modify the CRTC registers */
759 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
760 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
761 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
762 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
767 /* Set Cursor Position */
770 BiosSetCursorPosition(HIBYTE(Edx
), LOBYTE(Edx
), HIBYTE(Ebx
));
774 /* Get Cursor Position */
777 /* Make sure the selected video page exists */
778 if (HIBYTE(Ebx
) >= BIOS_MAX_PAGES
) break;
780 /* Return the result */
781 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
782 EmulatorSetRegister(EMULATOR_REG_CX
,
783 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
784 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
789 /* Query Light Pen */
793 * On modern BIOSes, this function returns 0
794 * so that we can ignore the other registers.
796 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
800 /* Select Active Display Page */
803 /* Check if the page exists */
804 if (LOBYTE(Eax
) >= BIOS_MAX_PAGES
) break;
806 /* Check if this is the same page */
807 if (LOBYTE(Eax
) == Bda
->VideoPage
) break;
809 /* Change the video page */
810 BiosSetVideoPage(LOBYTE(Eax
));
815 /* Scroll Window Up/Down */
819 SMALL_RECT Rectangle
=
827 /* Call the internal function */
828 BiosScrollWindow((HIBYTE(Eax
) == 0x06)
829 ? SCROLL_DIRECTION_UP
: SCROLL_DIRECTION_DOWN
,
838 /* Read/Write Character From Cursor Position */
843 WORD CharacterData
= MAKEWORD(LOBYTE(Eax
), LOBYTE(Ebx
));
844 BYTE Page
= HIBYTE(Ebx
);
847 /* Check if the page exists */
848 if (Page
>= BIOS_MAX_PAGES
) break;
850 /* Find the offset of the character */
851 Offset
= Page
* Bda
->VideoPageSize
852 + (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
853 + LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
855 if (HIBYTE(Eax
) == 0x08)
857 /* Read from the video memory */
858 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
859 (LPVOID
)&CharacterData
,
862 /* Return the character in AX */
863 EmulatorSetRegister(EMULATOR_REG_AX
, CharacterData
);
867 /* Write to video memory */
868 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
869 (LPVOID
)&CharacterData
,
870 (HIBYTE(Ebx
) == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
876 /* Teletype Output */
879 BiosPrintCharacter(LOBYTE(Eax
), LOBYTE(Ebx
), HIBYTE(Ebx
));
883 /* Get Current Video Mode */
886 EmulatorSetRegister(EMULATOR_REG_AX
,
887 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
888 EmulatorSetRegister(EMULATOR_REG_BX
,
889 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
897 SMALL_RECT Rectangle
=
905 /* Call the internal function */
906 BiosScrollWindow(LOBYTE(Ebx
),
917 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
923 VOID
BiosKeyboardService(LPWORD Stack
)
925 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
931 /* Read the character (and wait if necessary) */
932 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
939 WORD Data
= BiosPeekCharacter();
943 /* There is a character, clear ZF and return it */
944 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
945 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
949 /* No character, set ZF */
950 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
958 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
964 VOID
BiosTimeService(LPWORD Stack
)
966 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
967 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
968 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
974 /* Set AL to 1 if midnight had passed, 0 otherwise */
976 if (Bda
->MidnightPassed
) Eax
|= 1;
978 /* Return the tick count in CX:DX */
979 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
980 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
981 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
983 /* Reset the midnight flag */
984 Bda
->MidnightPassed
= FALSE
;
991 /* Set the tick count to CX:DX */
992 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
994 /* Reset the midnight flag */
995 Bda
->MidnightPassed
= FALSE
;
1002 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1008 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1010 /* Increase the system tick count */
1014 VOID
BiosEquipmentService(LPWORD Stack
)
1016 /* Return the equipment list */
1017 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
1020 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1027 /* Perform the system timer interrupt */
1028 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1036 BYTE ScanCode
, VirtualKey
;
1039 /* Check if there is a scancode available */
1040 if (!(KeyboardReadStatus() & 1)) break;
1042 /* Get the scan code and virtual key code */
1043 ScanCode
= KeyboardReadData();
1044 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1046 /* Check if this is a key press or release */
1047 if (!(ScanCode
& (1 << 7)))
1050 if (VirtualKey
== VK_NUMLOCK
1051 || VirtualKey
== VK_CAPITAL
1052 || VirtualKey
== VK_SCROLL
)
1054 /* For toggle keys, toggle the lowest bit in the keyboard map */
1055 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1058 /* Set the highest bit */
1059 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1061 /* Find out which character this is */
1062 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) > 0)
1064 /* Push it onto the BIOS keyboard queue */
1065 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
1070 /* Key release, unset the highest bit */
1071 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1078 /* Send End-of-Interrupt to the PIC */
1079 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1080 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);