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 DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber
, Values
);
384 if (Values
== NULL
) return FALSE
;
386 /* Write the misc register */
387 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
389 /* Write the sequencer registers */
390 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
392 VgaWritePort(VGA_SEQ_INDEX
, i
);
393 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
396 /* Write the GC registers */
397 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
399 VgaWritePort(VGA_GC_INDEX
, i
);
400 VgaWritePort(VGA_GC_DATA
, *(Values
++));
403 /* Write the CRTC registers */
404 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
406 VgaWritePort(VGA_CRTC_INDEX
, i
);
407 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
410 /* Write the AC registers */
411 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
413 VgaWritePort(VGA_AC_INDEX
, i
);
414 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
417 /* Update the values in the BDA */
418 Bda
->VideoMode
= ModeNumber
;
420 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
421 Bda
->VideoPageOffset
= 0;
423 /* Get the character height */
424 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_MAX_SCAN_LINE_REG
);
425 Bda
->CharacterHeight
= 1 + (VgaReadPort(VGA_CRTC_DATA
) & 0x1F);
427 Resolution
= VgaGetDisplayResolution();
428 Bda
->ScreenColumns
= Resolution
.X
;
429 Bda
->ScreenRows
= Resolution
.Y
- 1;
434 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
436 /* Check if the page exists */
437 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
439 /* Check if this is the same page */
440 if (PageNumber
== Bda
->VideoPage
) return TRUE
;
442 /* Set the values in the BDA */
443 Bda
->VideoPage
= PageNumber
;
444 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
445 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
447 /* Set the start address in the CRTC */
448 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
449 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
450 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
451 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
456 BOOLEAN
BiosInitialize(VOID
)
460 LPWORD IntVecTable
= (LPWORD
)BaseAddress
;
461 LPBYTE BiosCode
= (LPBYTE
)SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0);
463 /* Initialize the BDA */
464 Bda
= (PBIOS_DATA_AREA
)SEG_OFF_TO_PTR(BDA_SEGMENT
, 0);
465 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
466 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
467 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
469 /* Generate ISR stubs and fill the IVT */
470 for (i
= 0x00; i
<= 0xFF; i
++)
472 IntVecTable
[i
* 2] = Offset
;
473 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
475 BiosCode
[Offset
++] = 0xFB; // sti
477 BiosCode
[Offset
++] = 0x6A; // push i
478 BiosCode
[Offset
++] = (UCHAR
)i
;
480 BiosCode
[Offset
++] = 0x6A; // push 0
481 BiosCode
[Offset
++] = 0x00;
484 BiosCode
[Offset
++] = 0xF8; // clc
486 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
487 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
488 BiosCode
[Offset
++] = EMULATOR_INT_BOP
;
490 BiosCode
[Offset
++] = 0x73; // jnc EXIT (offset +3)
491 BiosCode
[Offset
++] = 0x03;
493 // HACK: The following instruction should be HLT!
494 BiosCode
[Offset
++] = 0x90; // nop
496 BiosCode
[Offset
++] = 0xEB; // jmp BOP_SEQ (offset -9)
497 BiosCode
[Offset
++] = 0xF7;
500 BiosCode
[Offset
++] = 0x83; // add sp, 4
501 BiosCode
[Offset
++] = 0xC4;
502 BiosCode
[Offset
++] = 0x04;
504 BiosCode
[Offset
++] = 0xCF; // iret
507 /* Get the input handle to the real console, and check for success */
508 BiosConsoleInput
= CreateFileW(L
"CONIN$",
509 GENERIC_READ
| GENERIC_WRITE
,
510 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
515 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
520 /* Get the output handle to the real console, and check for success */
521 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
522 GENERIC_READ
| GENERIC_WRITE
,
523 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
528 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
530 CloseHandle(BiosConsoleInput
);
534 /* Save the console screen buffer information */
535 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
537 CloseHandle(BiosConsoleOutput
);
538 CloseHandle(BiosConsoleInput
);
543 if (!VgaInitialize(BiosConsoleOutput
))
545 CloseHandle(BiosConsoleOutput
);
546 CloseHandle(BiosConsoleInput
);
550 /* Update the cursor position */
551 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
552 BiosSavedBufferInfo
.dwCursorPosition
.X
,
555 /* Set the console input mode */
556 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
558 /* Initialize the PIC */
559 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
560 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
562 /* Set the interrupt offsets */
563 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
564 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
566 /* Tell the master PIC there is a slave at IRQ 2 */
567 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
568 PicWriteData(PIC_SLAVE_DATA
, 2);
570 /* Make sure the PIC is in 8086 mode */
571 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
572 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
574 /* Clear the masks for both PICs */
575 PicWriteData(PIC_MASTER_DATA
, 0x00);
576 PicWriteData(PIC_SLAVE_DATA
, 0x00);
578 PitWriteCommand(0x34);
579 PitWriteData(0, 0x00);
580 PitWriteData(0, 0x00);
585 VOID
BiosCleanup(VOID
)
587 /* Restore the old screen buffer */
588 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
590 /* Restore the screen buffer size */
591 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
593 /* Close the console handles */
594 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
595 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
598 WORD
BiosPeekCharacter(VOID
)
600 WORD CharacterData
= 0;
602 /* Get the key from the queue, but don't remove it */
603 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
607 WORD
BiosGetCharacter(VOID
)
609 WORD CharacterData
= 0;
611 /* Check if there is a key available */
612 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
614 /* Get the key from the queue, and remove it */
615 BiosKbdBufferTop(&CharacterData
);
620 /* Set the handler CF to repeat the BOP */
621 EmulatorSetFlag(EMULATOR_FLAG_CF
);
624 return CharacterData
;
627 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
629 /* Make sure the selected video page is valid */
630 if (Page
>= BIOS_MAX_PAGES
) return;
632 /* Update the position in the BDA */
633 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
635 /* Check if this is the current video page */
636 if (Page
== Bda
->VideoPage
)
638 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
640 /* Modify the CRTC registers */
641 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
642 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
643 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
644 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
648 BOOLEAN
BiosScrollWindow(INT Direction
,
650 SMALL_RECT Rectangle
,
656 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
657 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
659 /* Allocate a buffer for the window */
660 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
662 WindowSize
* sizeof(WORD
));
663 if (WindowData
== NULL
) return FALSE
;
665 /* Read the window data */
666 BiosReadWindow(WindowData
, Rectangle
, Page
);
670 /* Fill the window */
671 for (i
= 0; i
< WindowSize
; i
++)
673 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
679 // TODO: Scroll the window!
682 /* Write back the window data */
683 BiosWriteWindow(WindowData
, Rectangle
, Page
);
685 /* Free the window buffer */
686 HeapFree(GetProcessHeap(), 0, WindowData
);
691 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
693 WORD CharData
= (Attribute
<< 8) | Character
;
696 /* Make sure the page exists */
697 if (Page
>= BIOS_MAX_PAGES
) return;
699 /* Get the cursor location */
700 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
701 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
703 if (Character
== '\a')
705 /* Bell control character */
706 // NOTE: We may use what the terminal emulator offers to us...
710 else if (Character
== '\b')
712 /* Backspace control character */
719 Column
= Bda
->ScreenColumns
- 1;
723 /* Erase the existing character */
724 CharData
= (Attribute
<< 8) | ' ';
725 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
726 Page
* Bda
->VideoPageSize
727 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
731 else if (Character
== '\n')
733 /* Line Feed control character */
736 else if (Character
== '\r')
738 /* Carriage Return control character */
743 /* Default character */
745 /* Write the character */
746 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
747 Page
* Bda
->VideoPageSize
748 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
752 /* Advance the cursor */
756 /* Check if it passed the end of the row */
757 if (Column
>= Bda
->ScreenColumns
)
759 /* Return to the first column and go to the next line */
764 /* Scroll the screen up if needed */
765 if (Row
> Bda
->ScreenRows
)
767 /* The screen must be scrolled up */
768 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
770 BiosScrollWindow(SCROLL_DIRECTION_UP
,
777 /* Set the cursor position */
778 BiosSetCursorPosition(Row
, Column
, Page
);
781 VOID
BiosVideoService(LPWORD Stack
)
788 BiosSetVideoMode(getAL());
793 /* Set Text-Mode Cursor Shape */
797 Bda
->CursorStartLine
= getCH();
798 Bda
->CursorEndLine
= getCL();
800 /* Modify the CRTC registers */
801 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
802 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
803 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
804 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
809 /* Set Cursor Position */
812 BiosSetCursorPosition(getDH(), getDL(), getBH());
816 /* Get Cursor Position */
819 /* Make sure the selected video page exists */
820 if (getBH() >= BIOS_MAX_PAGES
) break;
822 /* Return the result */
824 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
825 setDX(Bda
->CursorPosition
[getBH()]);
829 /* Query Light Pen */
833 * On modern BIOSes, this function returns 0
834 * so that we can ignore the other registers.
840 /* Select Active Display Page */
843 BiosSetVideoPage(getAL());
847 /* Scroll Window Up/Down */
851 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
853 /* Call the internal function */
854 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
855 : SCROLL_DIRECTION_DOWN
,
864 /* Read/Write Character From Cursor Position */
869 WORD CharacterData
= MAKEWORD(getAL(), getBL());
873 /* Check if the page exists */
874 if (Page
>= BIOS_MAX_PAGES
) break;
876 /* Find the offset of the character */
877 Offset
= Page
* Bda
->VideoPageSize
+
878 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
879 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
883 /* Read from the video memory */
884 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
885 (LPVOID
)&CharacterData
,
888 /* Return the character in AX */
889 setAX(CharacterData
);
893 /* Write to video memory */
894 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
895 (LPVOID
)&CharacterData
,
896 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
902 /* Teletype Output */
905 BiosPrintCharacter(getAL(), getBL(), getBH());
909 /* Get Current Video Mode */
912 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
913 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
920 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
922 /* Call the internal function */
923 BiosScrollWindow(getBL(),
932 /* Display combination code */
937 case 0x00: /* Get Display combiantion code */
938 setAX(MAKEWORD(0x1A, 0x1A));
939 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
941 case 0x01: /* Set Display combination code */
942 DPRINT1("Set Display combination code - Unsupported\n");
952 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
958 VOID
BiosKeyboardService(LPWORD Stack
)
962 /* Wait for keystroke and read */
964 /* Wait for extended keystroke and read */
965 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
967 /* Read the character (and wait if necessary) */
968 setAX(BiosGetCharacter());
972 /* Get keystroke status */
974 /* Get extended keystroke status */
975 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
977 WORD Data
= BiosPeekCharacter();
981 /* There is a character, clear ZF and return it */
982 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
987 /* No character, set ZF */
988 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
994 /* Get shift status */
997 /* Return the lower byte of the keyboard shift status word */
998 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1005 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1009 /* Push keystroke */
1012 /* Return 0 if success, 1 if failure */
1013 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1017 /* Get extended shift status */
1021 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1022 * but the high byte is organized differently:
1023 * the bytes 2 and 3 of the high byte are not the same...
1025 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1027 /* Return the extended keyboard shift status word */
1028 setAX(KeybdShiftFlags
);
1034 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1040 VOID
BiosTimeService(LPWORD Stack
)
1046 /* Set AL to 1 if midnight had passed, 0 otherwise */
1047 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1049 /* Return the tick count in CX:DX */
1050 setCX(HIWORD(Bda
->TickCounter
));
1051 setDX(LOWORD(Bda
->TickCounter
));
1053 /* Reset the midnight flag */
1054 Bda
->MidnightPassed
= FALSE
;
1061 /* Set the tick count to CX:DX */
1062 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1064 /* Reset the midnight flag */
1065 Bda
->MidnightPassed
= FALSE
;
1072 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1078 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1080 /* Increase the system tick count */
1084 VOID
BiosEquipmentService(LPWORD Stack
)
1086 /* Return the equipment list */
1087 setAX(Bda
->EquipmentList
);
1090 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1097 /* Perform the system timer interrupt */
1098 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1105 BYTE ScanCode
, VirtualKey
;
1108 /* Loop while there is a scancode available */
1111 /* Get the scan code and virtual key code */
1112 ScanCode
= KeyboardReadData();
1113 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1115 /* Check if this is a key press or release */
1116 if (!(ScanCode
& (1 << 7)))
1119 if (VirtualKey
== VK_NUMLOCK
||
1120 VirtualKey
== VK_CAPITAL
||
1121 VirtualKey
== VK_SCROLL
||
1122 VirtualKey
== VK_INSERT
)
1124 /* For toggle keys, toggle the lowest bit in the keyboard map */
1125 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1128 /* Set the highest bit */
1129 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1131 /* Find out which character this is */
1133 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1139 /* Push it onto the BIOS keyboard queue */
1140 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1144 /* Key release, unset the highest bit */
1145 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1148 while (KeyboardReadStatus() & 1);
1150 /* Clear the keyboard flags */
1151 Bda
->KeybdShiftFlags
= 0;
1153 /* Set the appropriate flags based on the state */
1154 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1155 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1156 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1157 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1158 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1159 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1160 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1161 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1162 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1163 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1164 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1165 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1166 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1167 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1168 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1169 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1175 /* Send End-of-Interrupt to the PIC */
1176 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1177 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);