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 #include "registers.h"
22 /* PRIVATE VARIABLES **********************************************************/
25 static BYTE BiosKeyboardMap
[256];
26 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
27 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
28 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
31 * VGA Register Configurations for BIOS Video Modes
32 * The configurations come from DosBox.
34 static BYTE VideoMode_40x25_text
[] =
36 /* Miscellaneous Register */
39 /* Sequencer Registers */
40 0x00, 0x08, 0x03, 0x00, 0x07,
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
46 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
47 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
51 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
52 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
55 static BYTE VideoMode_80x25_text
[] =
57 /* Miscellaneous Register */
60 /* Sequencer Registers */
61 0x00, 0x00, 0x03, 0x00, 0x07,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
67 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
68 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
72 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
73 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
76 static BYTE VideoMode_320x200_4color
[] =
78 /* Miscellaneous Register */
81 /* Sequencer Registers */
82 0x00, 0x09, 0x00, 0x00, 0x02,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
88 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
89 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
93 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
94 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
97 static BYTE VideoMode_640x200_2color
[] =
99 /* Miscellaneous Register */
102 /* Sequencer Registers */
103 0x00, 0x09, 0x0F, 0x00, 0x02,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
109 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
114 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
115 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00
118 static BYTE VideoMode_320x200_16color
[] =
120 /* Miscellaneous Register */
123 /* Sequencer Registers */
124 0x00, 0x09, 0x0F, 0x00, 0x02,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
130 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
135 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
136 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
139 static BYTE VideoMode_640x200_16color
[] =
141 /* Miscellaneous Register */
144 /* Sequencer Registers */
145 0x00, 0x01, 0x0F, 0x00, 0x02,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
151 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
152 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
156 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
157 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
160 static BYTE VideoMode_640x350_16color
[] =
162 /* Miscellaneous Register */
165 /* Sequencer Registers */
166 0x00, 0x01, 0x0F, 0x00, 0x02,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
172 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
177 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
178 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
181 static BYTE VideoMode_640x480_2color
[] =
183 /* Miscellaneous Register */
186 /* Sequencer Registers */
187 0x00, 0x01, 0x0F, 0x00, 0x02,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
193 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
198 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
199 0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
202 static BYTE VideoMode_640x480_16color
[] =
204 /* Miscellaneous Register */
207 /* Sequencer Registers */
208 0x00, 0x01, 0x0F, 0x00, 0x02,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
214 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
219 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
220 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
223 static BYTE VideoMode_320x200_256color
[] =
225 /* Miscellaneous Register */
228 /* Sequencer Registers */
229 0x00, 0x01, 0x0F, 0x00, 0x0E,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
235 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
240 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
241 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
244 static LPBYTE VideoModes
[] =
246 VideoMode_40x25_text
, /* Mode 00h */
247 VideoMode_40x25_text
, /* Mode 01h */
248 VideoMode_80x25_text
, /* Mode 02h */
249 VideoMode_80x25_text
, /* Mode 03h */
250 VideoMode_320x200_4color
, /* Mode 04h */
251 VideoMode_320x200_4color
, /* Mode 05h */
252 VideoMode_640x200_2color
, /* Mode 06h */
259 VideoMode_320x200_16color
, /* Mode 0Dh */
260 VideoMode_640x200_16color
, /* Mode 0Eh */
262 VideoMode_640x350_16color
, /* Mode 10h */
263 VideoMode_640x480_2color
, /* Mode 11h */
264 VideoMode_640x480_16color
, /* Mode 12h */
265 VideoMode_320x200_256color
, /* Mode 13h */
268 /* PRIVATE FUNCTIONS **********************************************************/
270 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
272 /* Get the location of the element after the tail */
273 WORD NextElement
= Bda
->KeybdBufferTail
+ sizeof(WORD
);
275 /* Wrap it around if it's at or beyond the end */
276 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
278 /* If it's full, fail */
279 if (NextElement
== Bda
->KeybdBufferHead
) return FALSE
;
281 /* Put the value in the queue */
282 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
283 Bda
->KeybdBufferTail
+= sizeof(WORD
);
285 /* Check if we are at, or have passed, the end of the buffer */
286 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
288 /* Return it to the beginning */
289 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
296 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
298 /* If it's empty, fail */
299 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
301 /* Otherwise, get the value and return success */
302 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
307 static BOOLEAN
BiosKbdBufferPop(VOID
)
309 /* If it's empty, fail */
310 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
312 /* Remove the value from the queue */
313 Bda
->KeybdBufferHead
+= sizeof(WORD
);
315 /* Check if we are at, or have passed, the end of the buffer */
316 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
318 /* Return it to the beginning */
319 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
326 static VOID
BiosReadWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
331 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
333 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
335 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
337 /* Read from video memory */
338 VgaReadMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
342 /* Write the data to the buffer in row order */
343 Buffer
[Counter
++] = Character
;
348 static VOID
BiosWriteWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
353 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
355 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
357 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
359 Character
= Buffer
[Counter
++];
361 /* Write to video memory */
362 VgaWriteMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
369 /* PUBLIC FUNCTIONS ***********************************************************/
371 BYTE
BiosGetVideoMode(VOID
)
373 return Bda
->VideoMode
;
376 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
380 LPBYTE Values
= VideoModes
[ModeNumber
];
382 if (Values
== NULL
) return FALSE
;
384 /* Write the misc register */
385 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
387 /* Write the sequencer registers */
388 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
390 VgaWritePort(VGA_SEQ_INDEX
, i
);
391 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
394 /* Write the GC registers */
395 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
397 VgaWritePort(VGA_GC_INDEX
, i
);
398 VgaWritePort(VGA_GC_DATA
, *(Values
++));
401 /* Write the CRTC registers */
402 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
404 VgaWritePort(VGA_CRTC_INDEX
, i
);
405 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
408 /* Write the AC registers */
409 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
411 VgaWritePort(VGA_AC_INDEX
, i
);
412 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
415 /* Update the values in the BDA */
416 Bda
->VideoMode
= ModeNumber
;
418 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
419 Bda
->VideoPageOffset
= 0;
421 /* Get the character height */
422 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_MAX_SCAN_LINE_REG
);
423 Bda
->CharacterHeight
= 1 + (VgaReadPort(VGA_CRTC_DATA
) & 0x1F);
425 Resolution
= VgaGetDisplayResolution();
426 Bda
->ScreenColumns
= Resolution
.X
;
427 Bda
->ScreenRows
= Resolution
.Y
- 1;
432 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
434 /* Check if the page exists */
435 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
437 /* Check if this is the same page */
438 if (PageNumber
== Bda
->VideoPage
) return TRUE
;
440 /* Set the values in the BDA */
441 Bda
->VideoPage
= PageNumber
;
442 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
443 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
445 /* Set the start address in the CRTC */
446 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
447 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
448 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
449 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
454 BOOLEAN
BiosInitialize(VOID
)
458 LPWORD IntVecTable
= (LPWORD
)BaseAddress
;
459 LPBYTE BiosCode
= (LPBYTE
)SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0);
461 /* Initialize the BDA */
462 Bda
= (PBIOS_DATA_AREA
)SEG_OFF_TO_PTR(BDA_SEGMENT
, 0);
463 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
464 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
465 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
467 /* Generate ISR stubs and fill the IVT */
468 for (i
= 0x00; i
<= 0xFF; i
++)
470 IntVecTable
[i
* 2] = Offset
;
471 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
473 BiosCode
[Offset
++] = 0xFB; // sti
475 BiosCode
[Offset
++] = 0x6A; // push i
476 BiosCode
[Offset
++] = (UCHAR
)i
;
478 BiosCode
[Offset
++] = 0x6A; // push 0
479 BiosCode
[Offset
++] = 0x00;
482 BiosCode
[Offset
++] = 0xF8; // clc
484 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
485 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
486 BiosCode
[Offset
++] = EMULATOR_INT_BOP
;
488 BiosCode
[Offset
++] = 0x73; // jnc EXIT (offset +3)
489 BiosCode
[Offset
++] = 0x03;
491 // HACK: The following instruction should be HLT!
492 BiosCode
[Offset
++] = 0x90; // nop
494 BiosCode
[Offset
++] = 0xEB; // jmp BOP_SEQ (offset -9)
495 BiosCode
[Offset
++] = 0xF7;
498 BiosCode
[Offset
++] = 0x83; // add sp, 4
499 BiosCode
[Offset
++] = 0xC4;
500 BiosCode
[Offset
++] = 0x04;
502 BiosCode
[Offset
++] = 0xCF; // iret
505 /* Get the input handle to the real console, and check for success */
506 BiosConsoleInput
= CreateFileW(L
"CONIN$",
507 GENERIC_READ
| GENERIC_WRITE
,
508 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
513 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
518 /* Get the output handle to the real console, and check for success */
519 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
520 GENERIC_READ
| GENERIC_WRITE
,
521 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
526 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
528 CloseHandle(BiosConsoleInput
);
532 /* Save the console screen buffer information */
533 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
535 CloseHandle(BiosConsoleOutput
);
536 CloseHandle(BiosConsoleInput
);
541 if (!VgaInitialize(BiosConsoleOutput
))
543 CloseHandle(BiosConsoleOutput
);
544 CloseHandle(BiosConsoleInput
);
548 /* Update the cursor position */
549 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
550 BiosSavedBufferInfo
.dwCursorPosition
.X
,
553 /* Set the console input mode */
554 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
556 /* Initialize the PIC */
557 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
558 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
560 /* Set the interrupt offsets */
561 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
562 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
564 /* Tell the master PIC there is a slave at IRQ 2 */
565 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
566 PicWriteData(PIC_SLAVE_DATA
, 2);
568 /* Make sure the PIC is in 8086 mode */
569 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
570 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
572 /* Clear the masks for both PICs */
573 PicWriteData(PIC_MASTER_DATA
, 0x00);
574 PicWriteData(PIC_SLAVE_DATA
, 0x00);
576 PitWriteCommand(0x34);
577 PitWriteData(0, 0x00);
578 PitWriteData(0, 0x00);
583 VOID
BiosCleanup(VOID
)
585 /* Restore the old screen buffer */
586 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
588 /* Restore the screen buffer size */
589 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
591 /* Close the console handles */
592 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
593 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
596 WORD
BiosPeekCharacter(VOID
)
598 WORD CharacterData
= 0;
600 /* Get the key from the queue, but don't remove it */
601 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
605 WORD
BiosGetCharacter(VOID
)
607 WORD CharacterData
= 0;
609 /* Check if there is a key available */
610 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
612 /* Get the key from the queue, and remove it */
613 BiosKbdBufferTop(&CharacterData
);
618 /* Set the handler CF to repeat the BOP */
619 EmulatorSetFlag(EMULATOR_FLAG_CF
);
622 return CharacterData
;
625 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
627 /* Make sure the selected video page is valid */
628 if (Page
>= BIOS_MAX_PAGES
) return;
630 /* Update the position in the BDA */
631 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
633 /* Check if this is the current video page */
634 if (Page
== Bda
->VideoPage
)
636 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
638 /* Modify the CRTC registers */
639 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
640 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
641 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
642 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
646 BOOLEAN
BiosScrollWindow(INT Direction
,
648 SMALL_RECT Rectangle
,
654 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
655 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
657 /* Allocate a buffer for the window */
658 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
660 WindowSize
* sizeof(WORD
));
661 if (WindowData
== NULL
) return FALSE
;
663 /* Read the window data */
664 BiosReadWindow(WindowData
, Rectangle
, Page
);
668 /* Fill the window */
669 for (i
= 0; i
< WindowSize
; i
++)
671 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
677 // TODO: Scroll the window!
680 /* Write back the window data */
681 BiosWriteWindow(WindowData
, Rectangle
, Page
);
683 /* Free the window buffer */
684 HeapFree(GetProcessHeap(), 0, WindowData
);
689 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
691 WORD CharData
= (Attribute
<< 8) | Character
;
694 /* Make sure the page exists */
695 if (Page
>= BIOS_MAX_PAGES
) return;
697 /* Get the cursor location */
698 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
699 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
701 if (Character
== '\a')
703 /* Bell control character */
704 // NOTE: We may use what the terminal emulator offers to us...
708 else if (Character
== '\b')
710 /* Backspace control character */
717 Column
= Bda
->ScreenColumns
- 1;
721 /* Erase the existing character */
722 CharData
= (Attribute
<< 8) | ' ';
723 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
724 Page
* Bda
->VideoPageSize
725 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
729 else if (Character
== '\n')
731 /* Line Feed control character */
734 else if (Character
== '\r')
736 /* Carriage Return control character */
741 /* Default character */
743 /* Write the character */
744 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
745 Page
* Bda
->VideoPageSize
746 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
750 /* Advance the cursor */
754 /* Check if it passed the end of the row */
755 if (Column
>= Bda
->ScreenColumns
)
757 /* Return to the first column and go to the next line */
762 /* Scroll the screen up if needed */
763 if (Row
> Bda
->ScreenRows
)
765 /* The screen must be scrolled up */
766 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
768 BiosScrollWindow(SCROLL_DIRECTION_UP
,
775 /* Set the cursor position */
776 BiosSetCursorPosition(Row
, Column
, Page
);
779 VOID
BiosVideoService(LPWORD Stack
)
786 BiosSetVideoMode(getAL());
791 /* Set Text-Mode Cursor Shape */
795 Bda
->CursorStartLine
= getCH();
796 Bda
->CursorEndLine
= getCL();
798 /* Modify the CRTC registers */
799 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
800 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
801 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
802 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
807 /* Set Cursor Position */
810 BiosSetCursorPosition(getDH(), getDL(), getBH());
814 /* Get Cursor Position */
817 /* Make sure the selected video page exists */
818 if (getBH() >= BIOS_MAX_PAGES
) break;
820 /* Return the result */
822 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
823 setDX(Bda
->CursorPosition
[getBH()]);
827 /* Query Light Pen */
831 * On modern BIOSes, this function returns 0
832 * so that we can ignore the other registers.
838 /* Select Active Display Page */
841 BiosSetVideoPage(getAL());
845 /* Scroll Window Up/Down */
849 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
851 /* Call the internal function */
852 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
853 : SCROLL_DIRECTION_DOWN
,
862 /* Read/Write Character From Cursor Position */
867 WORD CharacterData
= MAKEWORD(getAL(), getBL());
871 /* Check if the page exists */
872 if (Page
>= BIOS_MAX_PAGES
) break;
874 /* Find the offset of the character */
875 Offset
= Page
* Bda
->VideoPageSize
+
876 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
877 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
881 /* Read from the video memory */
882 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
883 (LPVOID
)&CharacterData
,
886 /* Return the character in AX */
887 setAX(CharacterData
);
891 /* Write to video memory */
892 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
893 (LPVOID
)&CharacterData
,
894 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
900 /* Teletype Output */
903 BiosPrintCharacter(getAL(), getBL(), getBH());
907 /* Get Current Video Mode */
910 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
911 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
918 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
920 /* Call the internal function */
921 BiosScrollWindow(getBL(),
930 /* Display combination code */
935 case 0x00: /* Get Display combiantion code */
936 setAX(MAKEWORD(0x1A, 0x1A));
937 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
939 case 0x01: /* Set Display combination code */
940 DPRINT1("Set Display combination code - Unsupported\n");
950 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
956 VOID
BiosKeyboardService(LPWORD Stack
)
960 /* Wait for keystroke and read */
962 /* Wait for extended keystroke and read */
963 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
965 /* Read the character (and wait if necessary) */
966 setAX(BiosGetCharacter());
970 /* Get keystroke status */
972 /* Get extended keystroke status */
973 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
975 WORD Data
= BiosPeekCharacter();
979 /* There is a character, clear ZF and return it */
980 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
985 /* No character, set ZF */
986 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
992 /* Get shift status */
995 /* Return the lower byte of the keyboard shift status word */
996 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1003 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1007 /* Push keystroke */
1010 /* Return 0 if success, 1 if failure */
1011 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1015 /* Get extended shift status */
1019 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1020 * but the high byte is organized differently:
1021 * the bytes 2 and 3 of the high byte are not the same...
1023 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1025 /* Return the extended keyboard shift status word */
1026 setAX(KeybdShiftFlags
);
1032 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1038 VOID
BiosTimeService(LPWORD Stack
)
1044 /* Set AL to 1 if midnight had passed, 0 otherwise */
1045 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1047 /* Return the tick count in CX:DX */
1048 setCX(HIWORD(Bda
->TickCounter
));
1049 setDX(LOWORD(Bda
->TickCounter
));
1051 /* Reset the midnight flag */
1052 Bda
->MidnightPassed
= FALSE
;
1059 /* Set the tick count to CX:DX */
1060 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1062 /* Reset the midnight flag */
1063 Bda
->MidnightPassed
= FALSE
;
1070 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1076 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1078 /* Increase the system tick count */
1082 VOID
BiosEquipmentService(LPWORD Stack
)
1084 /* Return the equipment list */
1085 setAX(Bda
->EquipmentList
);
1088 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1095 /* Perform the system timer interrupt */
1096 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1103 BYTE ScanCode
, VirtualKey
;
1106 /* Loop while there is a scancode available */
1109 /* Get the scan code and virtual key code */
1110 ScanCode
= KeyboardReadData();
1111 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1113 /* Check if this is a key press or release */
1114 if (!(ScanCode
& (1 << 7)))
1117 if (VirtualKey
== VK_NUMLOCK
||
1118 VirtualKey
== VK_CAPITAL
||
1119 VirtualKey
== VK_SCROLL
||
1120 VirtualKey
== VK_INSERT
)
1122 /* For toggle keys, toggle the lowest bit in the keyboard map */
1123 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1126 /* Set the highest bit */
1127 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1129 /* Find out which character this is */
1131 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1137 /* Push it onto the BIOS keyboard queue */
1138 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1142 /* Key release, unset the highest bit */
1143 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1146 while (KeyboardReadStatus() & 1);
1148 /* Clear the keyboard flags */
1149 Bda
->KeybdShiftFlags
= 0;
1151 /* Set the appropriate flags based on the state */
1152 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1153 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1154 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1155 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1156 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1157 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1158 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1159 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1160 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1161 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1162 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1163 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1164 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1165 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1166 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1167 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1173 /* Send End-of-Interrupt to the PIC */
1174 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1175 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);