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
));
917 /* Palette Control */
922 /* Set Single Palette Register */
925 /* Reset the flip-flop */
926 VgaReadPort(VGA_STAT_COLOR
);
928 /* Write the index */
929 VgaWritePort(VGA_AC_INDEX
, getBL());
932 VgaWritePort(VGA_AC_WRITE
, getBH());
937 /* Set Overscan Color */
940 /* Reset the flip-flop */
941 VgaReadPort(VGA_STAT_COLOR
);
943 /* Write the index */
944 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
947 VgaWritePort(VGA_AC_WRITE
, getBH());
952 /* Set All Palette Registers */
956 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
958 /* Set the palette registers */
959 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
961 /* Reset the flip-flop */
962 VgaReadPort(VGA_STAT_COLOR
);
964 /* Write the index */
965 VgaWritePort(VGA_AC_INDEX
, i
);
968 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
971 /* Set the overscan register */
972 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
973 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
978 /* Get Single Palette Register */
981 /* Reset the flip-flop */
982 VgaReadPort(VGA_STAT_COLOR
);
984 /* Write the index */
985 VgaWritePort(VGA_AC_INDEX
, getBL());
988 setBH(VgaReadPort(VGA_AC_READ
));
993 /* Get Overscan Color */
996 /* Reset the flip-flop */
997 VgaReadPort(VGA_STAT_COLOR
);
999 /* Write the index */
1000 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1003 setBH(VgaReadPort(VGA_AC_READ
));
1008 /* Get All Palette Registers */
1012 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1014 /* Get the palette registers */
1015 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1017 /* Reset the flip-flop */
1018 VgaReadPort(VGA_STAT_COLOR
);
1020 /* Write the index */
1021 VgaWritePort(VGA_AC_INDEX
, i
);
1024 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1027 /* Get the overscan register */
1028 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1029 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1034 /* Set Individual DAC Register */
1037 /* Write the index */
1038 // Certainly in BL and not in BX as said by Ralf Brown...
1039 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1041 /* Write the data in this order: Red, Green, Blue */
1042 VgaWritePort(VGA_DAC_DATA
, getDH());
1043 VgaWritePort(VGA_DAC_DATA
, getCH());
1044 VgaWritePort(VGA_DAC_DATA
, getCL());
1049 /* Set Block of DAC Registers */
1053 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1055 /* Write the index */
1056 // Certainly in BL and not in BX as said by Ralf Brown...
1057 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1059 for (i
= 0; i
< getCX(); i
++)
1061 /* Write the data in this order: Red, Green, Blue */
1062 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1063 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1064 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1070 /* Get Individual DAC Register */
1073 /* Write the index */
1074 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1076 /* Read the data in this order: Red, Green, Blue */
1077 setDH(VgaReadPort(VGA_DAC_DATA
));
1078 setCH(VgaReadPort(VGA_DAC_DATA
));
1079 setCL(VgaReadPort(VGA_DAC_DATA
));
1084 /* Get Block of DAC Registers */
1088 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1090 /* Write the index */
1091 // Certainly in BL and not in BX as said by Ralf Brown...
1092 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1094 for (i
= 0; i
< getCX(); i
++)
1096 /* Write the data in this order: Red, Green, Blue */
1097 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1098 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1099 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1107 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1119 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1121 /* Call the internal function */
1122 BiosScrollWindow(getBL(),
1131 /* Display combination code */
1136 case 0x00: /* Get Display combiantion code */
1137 setAX(MAKEWORD(0x1A, 0x1A));
1138 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1140 case 0x01: /* Set Display combination code */
1141 DPRINT1("Set Display combination code - Unsupported\n");
1151 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1157 VOID
BiosKeyboardService(LPWORD Stack
)
1161 /* Wait for keystroke and read */
1163 /* Wait for extended keystroke and read */
1164 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1166 /* Read the character (and wait if necessary) */
1167 setAX(BiosGetCharacter());
1171 /* Get keystroke status */
1173 /* Get extended keystroke status */
1174 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1176 WORD Data
= BiosPeekCharacter();
1180 /* There is a character, clear ZF and return it */
1181 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1186 /* No character, set ZF */
1187 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1193 /* Get shift status */
1196 /* Return the lower byte of the keyboard shift status word */
1197 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1204 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1208 /* Push keystroke */
1211 /* Return 0 if success, 1 if failure */
1212 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1216 /* Get extended shift status */
1220 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1221 * but the high byte is organized differently:
1222 * the bytes 2 and 3 of the high byte are not the same...
1224 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1226 /* Return the extended keyboard shift status word */
1227 setAX(KeybdShiftFlags
);
1233 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1239 VOID
BiosTimeService(LPWORD Stack
)
1245 /* Set AL to 1 if midnight had passed, 0 otherwise */
1246 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1248 /* Return the tick count in CX:DX */
1249 setCX(HIWORD(Bda
->TickCounter
));
1250 setDX(LOWORD(Bda
->TickCounter
));
1252 /* Reset the midnight flag */
1253 Bda
->MidnightPassed
= FALSE
;
1260 /* Set the tick count to CX:DX */
1261 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1263 /* Reset the midnight flag */
1264 Bda
->MidnightPassed
= FALSE
;
1271 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1277 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1279 /* Increase the system tick count */
1283 VOID
BiosEquipmentService(LPWORD Stack
)
1285 /* Return the equipment list */
1286 setAX(Bda
->EquipmentList
);
1289 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1296 /* Perform the system timer interrupt */
1297 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1304 BYTE ScanCode
, VirtualKey
;
1307 /* Loop while there is a scancode available */
1310 /* Get the scan code and virtual key code */
1311 ScanCode
= KeyboardReadData();
1312 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1314 /* Check if this is a key press or release */
1315 if (!(ScanCode
& (1 << 7)))
1318 if (VirtualKey
== VK_NUMLOCK
||
1319 VirtualKey
== VK_CAPITAL
||
1320 VirtualKey
== VK_SCROLL
||
1321 VirtualKey
== VK_INSERT
)
1323 /* For toggle keys, toggle the lowest bit in the keyboard map */
1324 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1327 /* Set the highest bit */
1328 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1330 /* Find out which character this is */
1332 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1338 /* Push it onto the BIOS keyboard queue */
1339 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1343 /* Key release, unset the highest bit */
1344 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1347 while (KeyboardReadStatus() & 1);
1349 /* Clear the keyboard flags */
1350 Bda
->KeybdShiftFlags
= 0;
1352 /* Set the appropriate flags based on the state */
1353 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1354 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1355 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1356 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1357 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1358 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1359 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1360 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1361 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1362 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1363 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1364 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1365 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1366 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1367 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1368 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1374 /* Send End-of-Interrupt to the PIC */
1375 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1376 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);