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 /* Check if there is a key available */
600 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return 0xFFFF;
602 /* Get the key from the queue, but don't remove it */
603 BiosKbdBufferTop(&CharacterData
);
605 return CharacterData
;
608 WORD
BiosGetCharacter(VOID
)
610 WORD CharacterData
= 0;
612 /* Check if there is a key available */
613 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
615 /* Get the key from the queue, and remove it */
616 BiosKbdBufferTop(&CharacterData
);
621 /* Set the handler CF to repeat the BOP */
622 EmulatorSetFlag(EMULATOR_FLAG_CF
);
625 return CharacterData
;
628 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
630 /* Make sure the selected video page is valid */
631 if (Page
>= BIOS_MAX_PAGES
) return;
633 /* Update the position in the BDA */
634 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
636 /* Check if this is the current video page */
637 if (Page
== Bda
->VideoPage
)
639 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
641 /* Modify the CRTC registers */
642 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
643 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
644 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
645 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
649 BOOLEAN
BiosScrollWindow(INT Direction
,
651 SMALL_RECT Rectangle
,
657 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
658 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
660 /* Allocate a buffer for the window */
661 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
663 WindowSize
* sizeof(WORD
));
664 if (WindowData
== NULL
) return FALSE
;
666 /* Read the window data */
667 BiosReadWindow(WindowData
, Rectangle
, Page
);
671 /* Fill the window */
672 for (i
= 0; i
< WindowSize
; i
++)
674 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
680 // TODO: Scroll the window!
683 /* Write back the window data */
684 BiosWriteWindow(WindowData
, Rectangle
, Page
);
686 /* Free the window buffer */
687 HeapFree(GetProcessHeap(), 0, WindowData
);
692 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
694 WORD CharData
= (Attribute
<< 8) | Character
;
697 /* Make sure the page exists */
698 if (Page
>= BIOS_MAX_PAGES
) return;
700 /* Get the cursor location */
701 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
702 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
704 if (Character
== '\a')
706 /* Bell control character */
707 // NOTE: We may use what the terminal emulator offers to us...
711 else if (Character
== '\b')
713 /* Backspace control character */
720 Column
= Bda
->ScreenColumns
- 1;
724 /* Erase the existing character */
725 CharData
= (Attribute
<< 8) | ' ';
726 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
727 Page
* Bda
->VideoPageSize
728 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
732 else if (Character
== '\n')
734 /* Line Feed control character */
737 else if (Character
== '\r')
739 /* Carriage Return control character */
744 /* Default character */
746 /* Write the character */
747 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
748 Page
* Bda
->VideoPageSize
749 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
753 /* Advance the cursor */
757 /* Check if it passed the end of the row */
758 if (Column
>= Bda
->ScreenColumns
)
760 /* Return to the first column and go to the next line */
765 /* Scroll the screen up if needed */
766 if (Row
> Bda
->ScreenRows
)
768 /* The screen must be scrolled up */
769 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
771 BiosScrollWindow(SCROLL_DIRECTION_UP
,
778 /* Set the cursor position */
779 BiosSetCursorPosition(Row
, Column
, Page
);
782 VOID
BiosVideoService(LPWORD Stack
)
784 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
785 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
786 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
787 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
794 BiosSetVideoMode(LOBYTE(Eax
));
799 /* Set Text-Mode Cursor Shape */
803 Bda
->CursorStartLine
= HIBYTE(Ecx
);
804 Bda
->CursorEndLine
= LOBYTE(Ecx
);
806 /* Modify the CRTC registers */
807 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
808 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
809 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
810 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
815 /* Set Cursor Position */
818 BiosSetCursorPosition(HIBYTE(Edx
), LOBYTE(Edx
), HIBYTE(Ebx
));
822 /* Get Cursor Position */
825 /* Make sure the selected video page exists */
826 if (HIBYTE(Ebx
) >= BIOS_MAX_PAGES
) break;
828 /* Return the result */
829 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
830 EmulatorSetRegister(EMULATOR_REG_CX
,
831 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
832 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
837 /* Query Light Pen */
841 * On modern BIOSes, this function returns 0
842 * so that we can ignore the other registers.
844 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
848 /* Select Active Display Page */
851 BiosSetVideoPage(LOBYTE(Eax
));
855 /* Scroll Window Up/Down */
859 SMALL_RECT Rectangle
=
867 /* Call the internal function */
868 BiosScrollWindow((HIBYTE(Eax
) == 0x06) ? SCROLL_DIRECTION_UP
869 : SCROLL_DIRECTION_DOWN
,
878 /* Read/Write Character From Cursor Position */
883 WORD CharacterData
= MAKEWORD(LOBYTE(Eax
), LOBYTE(Ebx
));
884 BYTE Page
= HIBYTE(Ebx
);
887 /* Check if the page exists */
888 if (Page
>= BIOS_MAX_PAGES
) break;
890 /* Find the offset of the character */
891 Offset
= Page
* Bda
->VideoPageSize
892 + (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
893 + LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
895 if (HIBYTE(Eax
) == 0x08)
897 /* Read from the video memory */
898 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
899 (LPVOID
)&CharacterData
,
902 /* Return the character in AX */
903 EmulatorSetRegister(EMULATOR_REG_AX
, CharacterData
);
907 /* Write to video memory */
908 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
909 (LPVOID
)&CharacterData
,
910 (HIBYTE(Ebx
) == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
916 /* Teletype Output */
919 BiosPrintCharacter(LOBYTE(Eax
), LOBYTE(Ebx
), HIBYTE(Ebx
));
923 /* Get Current Video Mode */
926 EmulatorSetRegister(EMULATOR_REG_AX
,
927 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
928 EmulatorSetRegister(EMULATOR_REG_BX
,
929 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
937 SMALL_RECT Rectangle
=
945 /* Call the internal function */
946 BiosScrollWindow(LOBYTE(Ebx
),
955 /* Display combination code */
960 case 0x00: /* Get Display combiantion code */
961 EmulatorSetRegister(EMULATOR_REG_AX
, MAKEWORD(0x1A, 0x1A));
962 EmulatorSetRegister(EMULATOR_REG_BX
, MAKEWORD(0x08, 0x0)); /* VGA w/ color analog display */
964 case 0x01: /* Set Display combination code */
965 DPRINT1("Set Display combination code - Unsupported\n");
975 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
981 VOID
BiosKeyboardService(LPWORD Stack
)
983 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
987 /* Wait for keystroke and read */
989 /* Wait for extended keystroke and read */
990 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
992 /* Read the character (and wait if necessary) */
993 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
997 /* Get keystroke status */
999 /* Get extended keystroke status */
1000 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1002 WORD Data
= BiosPeekCharacter();
1006 /* There is a character, clear ZF and return it */
1007 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
1008 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1012 /* No character, set ZF */
1013 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1019 /* Get shift status */
1022 /* Return the lower byte of the keyboard shift status word */
1023 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1030 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1034 /* Push keystroke */
1037 /* Return 0 if success, 1 if failure */
1038 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1042 /* Get extended shift status */
1046 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1047 * but the high byte is organized differently:
1048 * the bytes 2 and 3 of the high byte are not the same...
1050 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1052 /* Return the extended keyboard shift status word */
1053 setAX(KeybdShiftFlags
);
1059 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1065 VOID
BiosTimeService(LPWORD Stack
)
1067 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
1068 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
1069 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
1071 switch (HIBYTE(Eax
))
1075 /* Set AL to 1 if midnight had passed, 0 otherwise */
1077 if (Bda
->MidnightPassed
) Eax
|= 1;
1079 /* Return the tick count in CX:DX */
1080 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
1081 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
1082 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
1084 /* Reset the midnight flag */
1085 Bda
->MidnightPassed
= FALSE
;
1092 /* Set the tick count to CX:DX */
1093 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
1095 /* Reset the midnight flag */
1096 Bda
->MidnightPassed
= FALSE
;
1103 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1109 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1111 /* Increase the system tick count */
1115 VOID
BiosEquipmentService(LPWORD Stack
)
1117 /* Return the equipment list */
1118 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
1121 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1128 /* Perform the system timer interrupt */
1129 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1137 BYTE ScanCode
, VirtualKey
;
1140 /* Loop while there is a scancode available */
1141 while (KeyboardReadStatus() & 1)
1143 /* Get the scan code and virtual key code */
1144 ScanCode
= KeyboardReadData();
1145 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1147 /* Check if this is a key press or release */
1148 if (!(ScanCode
& (1 << 7)))
1151 if (VirtualKey
== VK_NUMLOCK
1152 || VirtualKey
== VK_CAPITAL
1153 || VirtualKey
== VK_SCROLL
)
1155 /* For toggle keys, toggle the lowest bit in the keyboard map */
1156 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1159 /* Set the highest bit */
1160 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1162 /* Find out which character this is */
1164 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1170 /* Push it onto the BIOS keyboard queue */
1171 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
1176 /* Key release, unset the highest bit */
1177 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1185 /* Send End-of-Interrupt to the PIC */
1186 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1187 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);