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
= 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
)
597 WORD CharacterData
= 0;
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
)
785 BiosSetVideoMode(getAL());
790 /* Set Text-Mode Cursor Shape */
794 Bda
->CursorStartLine
= getCH();
795 Bda
->CursorEndLine
= getCL();
797 /* Modify the CRTC registers */
798 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
799 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
800 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
801 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
806 /* Set Cursor Position */
809 BiosSetCursorPosition(getDH(), getDL(), getBH());
813 /* Get Cursor Position */
816 /* Make sure the selected video page exists */
817 if (getBH() >= BIOS_MAX_PAGES
) break;
819 /* Return the result */
821 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
822 setDX(Bda
->CursorPosition
[getBH()]);
826 /* Query Light Pen */
830 * On modern BIOSes, this function returns 0
831 * so that we can ignore the other registers.
837 /* Select Active Display Page */
840 BiosSetVideoPage(getAL());
844 /* Scroll Window Up/Down */
848 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
850 /* Call the internal function */
851 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
852 : SCROLL_DIRECTION_DOWN
,
861 /* Read/Write Character From Cursor Position */
866 WORD CharacterData
= MAKEWORD(getAL(), getBL());
870 /* Check if the page exists */
871 if (Page
>= BIOS_MAX_PAGES
) break;
873 /* Find the offset of the character */
874 Offset
= Page
* Bda
->VideoPageSize
+
875 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
876 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
880 /* Read from the video memory */
881 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
882 (LPVOID
)&CharacterData
,
885 /* Return the character in AX */
886 setAX(CharacterData
);
890 /* Write to video memory */
891 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
892 (LPVOID
)&CharacterData
,
893 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
899 /* Teletype Output */
902 BiosPrintCharacter(getAL(), getBL(), getBH());
906 /* Get Current Video Mode */
909 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
910 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
917 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
919 /* Call the internal function */
920 BiosScrollWindow(getBL(),
929 /* Display combination code */
934 case 0x00: /* Get Display combiantion code */
935 setAX(MAKEWORD(0x1A, 0x1A));
936 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
938 case 0x01: /* Set Display combination code */
939 DPRINT1("Set Display combination code - Unsupported\n");
949 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
955 VOID
BiosKeyboardService(LPWORD Stack
)
959 /* Wait for keystroke and read */
961 /* Wait for extended keystroke and read */
962 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
964 /* Read the character (and wait if necessary) */
965 setAX(BiosGetCharacter());
969 /* Get keystroke status */
971 /* Get extended keystroke status */
972 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
974 WORD Data
= BiosPeekCharacter();
978 /* There is a character, clear ZF and return it */
979 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
984 /* No character, set ZF */
985 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
991 /* Get shift status */
994 /* Return the lower byte of the keyboard shift status word */
995 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1002 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1006 /* Push keystroke */
1009 /* Return 0 if success, 1 if failure */
1010 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1014 /* Get extended shift status */
1018 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1019 * but the high byte is organized differently:
1020 * the bytes 2 and 3 of the high byte are not the same...
1022 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1024 /* Return the extended keyboard shift status word */
1025 setAX(KeybdShiftFlags
);
1031 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1037 VOID
BiosTimeService(LPWORD Stack
)
1043 /* Set AL to 1 if midnight had passed, 0 otherwise */
1044 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1046 /* Return the tick count in CX:DX */
1047 setCX(HIWORD(Bda
->TickCounter
));
1048 setDX(LOWORD(Bda
->TickCounter
));
1050 /* Reset the midnight flag */
1051 Bda
->MidnightPassed
= FALSE
;
1058 /* Set the tick count to CX:DX */
1059 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1061 /* Reset the midnight flag */
1062 Bda
->MidnightPassed
= FALSE
;
1069 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1075 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1077 /* Increase the system tick count */
1081 VOID
BiosEquipmentService(LPWORD Stack
)
1083 /* Return the equipment list */
1084 setAX(Bda
->EquipmentList
);
1087 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1094 /* Perform the system timer interrupt */
1095 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1102 BYTE ScanCode
, VirtualKey
;
1105 /* Loop while there is a scancode available */
1108 /* Get the scan code and virtual key code */
1109 ScanCode
= KeyboardReadData();
1110 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1112 /* Check if this is a key press or release */
1113 if (!(ScanCode
& (1 << 7)))
1116 if (VirtualKey
== VK_NUMLOCK
||
1117 VirtualKey
== VK_CAPITAL
||
1118 VirtualKey
== VK_SCROLL
||
1119 VirtualKey
== VK_INSERT
)
1121 /* For toggle keys, toggle the lowest bit in the keyboard map */
1122 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1125 /* Set the highest bit */
1126 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1128 /* Find out which character this is */
1130 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1136 /* Push it onto the BIOS keyboard queue */
1137 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1141 /* Key release, unset the highest bit */
1142 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1145 while (KeyboardReadStatus() & 1);
1147 /* Clear the keyboard flags */
1148 Bda
->KeybdShiftFlags
= 0;
1150 /* Set the appropriate flags based on the state */
1151 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1152 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1153 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1154 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1155 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1156 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1157 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1158 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1159 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1160 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1161 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1162 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1163 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1164 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1165 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1166 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1172 /* Send End-of-Interrupt to the PIC */
1173 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1174 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);