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
+ 2;
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
)((ULONG_PTR
)BaseAddress
);
459 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
461 /* Initialize the BDA */
462 Bda
= (PBIOS_DATA_AREA
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(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
= 0; i
< 256; 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
++] = (BYTE
)i
;
478 BiosCode
[Offset
++] = 0x6A; // push 0
479 BiosCode
[Offset
++] = 0x00;
481 BiosCode
[Offset
++] = 0xF8; // clc
483 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
484 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
485 BiosCode
[Offset
++] = LOBYTE(EMULATOR_INT_BOP
);
486 BiosCode
[Offset
++] = HIBYTE(EMULATOR_INT_BOP
);
488 BiosCode
[Offset
++] = 0x73; // jnc +3
489 BiosCode
[Offset
++] = 0x03;
491 // HACK: The following instruction should be HLT!
492 BiosCode
[Offset
++] = 0x90; // nop
494 BiosCode
[Offset
++] = 0xEB; // jmp -10
495 BiosCode
[Offset
++] = 0xF6;
497 BiosCode
[Offset
++] = 0x83; // add sp, 4
498 BiosCode
[Offset
++] = 0xC4;
499 BiosCode
[Offset
++] = 0x04;
501 BiosCode
[Offset
++] = 0xCF; // iret
504 /* Get the input handle to the real console, and check for success */
505 BiosConsoleInput
= CreateFileW(L
"CONIN$",
506 GENERIC_READ
| GENERIC_WRITE
,
507 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
512 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
517 /* Get the output handle to the real console, and check for success */
518 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
519 GENERIC_READ
| GENERIC_WRITE
,
520 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
525 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
527 CloseHandle(BiosConsoleInput
);
531 /* Save the console screen buffer information */
532 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
534 CloseHandle(BiosConsoleOutput
);
535 CloseHandle(BiosConsoleInput
);
540 if (!VgaInitialize(BiosConsoleOutput
))
542 CloseHandle(BiosConsoleOutput
);
543 CloseHandle(BiosConsoleInput
);
547 /* Update the cursor position */
548 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
549 BiosSavedBufferInfo
.dwCursorPosition
.X
,
552 /* Set the console input mode */
553 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
555 /* Initialize the PIC */
556 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
557 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
559 /* Set the interrupt offsets */
560 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
561 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
563 /* Tell the master PIC there is a slave at IRQ 2 */
564 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
565 PicWriteData(PIC_SLAVE_DATA
, 2);
567 /* Make sure the PIC is in 8086 mode */
568 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
569 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
571 /* Clear the masks for both PICs */
572 PicWriteData(PIC_MASTER_DATA
, 0x00);
573 PicWriteData(PIC_SLAVE_DATA
, 0x00);
575 PitWriteCommand(0x34);
576 PitWriteData(0, 0x00);
577 PitWriteData(0, 0x00);
582 VOID
BiosCleanup(VOID
)
584 /* Restore the old screen buffer */
585 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
587 /* Restore the screen buffer size */
588 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
590 /* Close the console handles */
591 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
592 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
595 WORD
BiosPeekCharacter(VOID
)
599 /* Get the key from the queue, but don't remove it */
600 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
604 WORD
BiosGetCharacter(VOID
)
606 WORD CharacterData
= 0;
608 /* Check if there is a key available */
609 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
611 /* Get the key from the queue, and remove it */
612 BiosKbdBufferTop(&CharacterData
);
617 /* Set the handler CF to repeat the BOP */
618 EmulatorSetFlag(EMULATOR_FLAG_CF
);
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 if (Character
== '\a')
702 /* Bell control character */
703 // NOTE: We may use what the terminal emulator offers to us...
707 else if (Character
== '\b')
709 /* Backspace control character */
716 Column
= Bda
->ScreenColumns
- 1;
720 /* Erase the existing character */
721 CharData
= (Attribute
<< 8) | ' ';
722 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
723 Page
* Bda
->VideoPageSize
724 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
728 else if (Character
== '\n')
730 /* Line Feed control character */
733 else if (Character
== '\r')
735 /* Carriage Return control character */
740 /* Default character */
742 /* Write the character */
743 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
744 Page
* Bda
->VideoPageSize
745 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
749 /* Advance the cursor */
753 /* Check if it passed the end of the row */
754 if (Column
>= Bda
->ScreenColumns
)
756 /* Return to the first column and go to the next line */
761 /* Scroll the screen up if needed */
762 if (Row
> Bda
->ScreenRows
)
764 /* The screen must be scrolled up */
765 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
767 BiosScrollWindow(SCROLL_DIRECTION_UP
,
774 /* Set the cursor position */
775 BiosSetCursorPosition(Row
, Column
, Page
);
778 VOID
BiosVideoService(LPWORD Stack
)
780 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
781 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
782 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
783 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
790 BiosSetVideoMode(LOBYTE(Eax
));
795 /* Set Text-Mode Cursor Shape */
799 Bda
->CursorStartLine
= HIBYTE(Ecx
);
800 Bda
->CursorEndLine
= LOBYTE(Ecx
);
802 /* Modify the CRTC registers */
803 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
804 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
805 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
806 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
811 /* Set Cursor Position */
814 BiosSetCursorPosition(HIBYTE(Edx
), LOBYTE(Edx
), HIBYTE(Ebx
));
818 /* Get Cursor Position */
821 /* Make sure the selected video page exists */
822 if (HIBYTE(Ebx
) >= BIOS_MAX_PAGES
) break;
824 /* Return the result */
825 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
826 EmulatorSetRegister(EMULATOR_REG_CX
,
827 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
828 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
833 /* Query Light Pen */
837 * On modern BIOSes, this function returns 0
838 * so that we can ignore the other registers.
840 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
844 /* Select Active Display Page */
847 BiosSetVideoPage(LOBYTE(Eax
));
851 /* Scroll Window Up/Down */
855 SMALL_RECT Rectangle
=
863 /* Call the internal function */
864 BiosScrollWindow((HIBYTE(Eax
) == 0x06) ? SCROLL_DIRECTION_UP
865 : SCROLL_DIRECTION_DOWN
,
874 /* Read/Write Character From Cursor Position */
879 WORD CharacterData
= MAKEWORD(LOBYTE(Eax
), LOBYTE(Ebx
));
880 BYTE Page
= HIBYTE(Ebx
);
883 /* Check if the page exists */
884 if (Page
>= BIOS_MAX_PAGES
) break;
886 /* Find the offset of the character */
887 Offset
= Page
* Bda
->VideoPageSize
888 + (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
889 + LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
891 if (HIBYTE(Eax
) == 0x08)
893 /* Read from the video memory */
894 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
895 (LPVOID
)&CharacterData
,
898 /* Return the character in AX */
899 EmulatorSetRegister(EMULATOR_REG_AX
, CharacterData
);
903 /* Write to video memory */
904 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
905 (LPVOID
)&CharacterData
,
906 (HIBYTE(Ebx
) == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
912 /* Teletype Output */
915 BiosPrintCharacter(LOBYTE(Eax
), LOBYTE(Ebx
), HIBYTE(Ebx
));
919 /* Get Current Video Mode */
922 EmulatorSetRegister(EMULATOR_REG_AX
,
923 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
924 EmulatorSetRegister(EMULATOR_REG_BX
,
925 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
933 SMALL_RECT Rectangle
=
941 /* Call the internal function */
942 BiosScrollWindow(LOBYTE(Ebx
),
951 /* Display combination code */
956 case 0x00: /* Get Display combiantion code */
957 EmulatorSetRegister(EMULATOR_REG_AX
, MAKEWORD(0x1A, 0x1A));
958 EmulatorSetRegister(EMULATOR_REG_BX
, MAKEWORD(0x08, 0x0)); /* VGA w/ color analog display */
960 case 0x01: /* Set Display combination code */
961 DPRINT1("Set Display combination code - Unsupported\n");
971 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
977 VOID
BiosKeyboardService(LPWORD Stack
)
979 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
983 /* Wait for keystroke and read */
985 /* Wait for extended keystroke and read */
986 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
988 /* Read the character (and wait if necessary) */
989 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
993 /* Get keystroke status */
995 /* Get extended keystroke status */
996 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
998 WORD Data
= BiosPeekCharacter();
1002 /* There is a character, clear ZF and return it */
1003 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
1004 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1008 /* No character, set ZF */
1009 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1015 /* Get shift status */
1018 /* Return the lower byte of the keyboard shift status word */
1019 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1026 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1030 /* Push keystroke */
1033 /* Return 0 if success, 1 if failure */
1034 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1038 /* Get extended shift status */
1042 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1043 * but the high byte is organized differently:
1044 * the bytes 2 and 3 of the high byte are not the same...
1046 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1048 /* Return the extended keyboard shift status word */
1049 setAX(KeybdShiftFlags
);
1055 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1061 VOID
BiosTimeService(LPWORD Stack
)
1063 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
1064 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
1065 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
1067 switch (HIBYTE(Eax
))
1071 /* Set AL to 1 if midnight had passed, 0 otherwise */
1073 if (Bda
->MidnightPassed
) Eax
|= 1;
1075 /* Return the tick count in CX:DX */
1076 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
1077 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
1078 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
1080 /* Reset the midnight flag */
1081 Bda
->MidnightPassed
= FALSE
;
1088 /* Set the tick count to CX:DX */
1089 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
1091 /* Reset the midnight flag */
1092 Bda
->MidnightPassed
= FALSE
;
1099 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1105 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1107 /* Increase the system tick count */
1111 VOID
BiosEquipmentService(LPWORD Stack
)
1113 /* Return the equipment list */
1114 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
1117 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1124 /* Perform the system timer interrupt */
1125 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1133 BYTE ScanCode
, VirtualKey
;
1136 /* Loop while there is a scancode available */
1139 /* Get the scan code and virtual key code */
1140 ScanCode
= KeyboardReadData();
1141 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1143 /* Check if this is a key press or release */
1144 if (!(ScanCode
& (1 << 7)))
1147 if (VirtualKey
== VK_NUMLOCK
1148 || VirtualKey
== VK_CAPITAL
1149 || VirtualKey
== VK_SCROLL
1150 || VirtualKey
== VK_INSERT
)
1152 /* For toggle keys, toggle the lowest bit in the keyboard map */
1153 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1156 /* Set the highest bit */
1157 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1159 /* Find out which character this is */
1161 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1167 /* Push it onto the BIOS keyboard queue */
1168 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1172 /* Key release, unset the highest bit */
1173 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1176 while (KeyboardReadStatus() & 1);
1178 /* Clear the keyboard flags */
1179 Bda
->KeybdShiftFlags
= 0;
1181 /* Set the appropriate flags based on the state */
1182 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1183 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1184 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1185 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1186 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1187 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1188 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1189 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1190 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1191 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1192 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1193 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1194 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1195 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1196 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1197 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1203 /* Send End-of-Interrupt to the PIC */
1204 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1205 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);