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
;
29 static HANDLE InputThread
= NULL
;
32 * VGA Register Configurations for BIOS Video Modes
33 * The configurations come from DosBox.
35 static BYTE VideoMode_40x25_text
[] =
37 /* Miscellaneous Register */
40 /* Sequencer Registers */
41 0x00, 0x08, 0x03, 0x00, 0x07,
44 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
47 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
48 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
52 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
53 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
56 static BYTE VideoMode_80x25_text
[] =
58 /* Miscellaneous Register */
61 /* Sequencer Registers */
62 0x00, 0x00, 0x03, 0x00, 0x07,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
68 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
69 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
73 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
74 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
77 static BYTE VideoMode_320x200_4color
[] =
79 /* Miscellaneous Register */
82 /* Sequencer Registers */
83 0x00, 0x09, 0x00, 0x00, 0x02,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
89 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
94 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
95 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
98 static BYTE VideoMode_640x200_2color
[] =
100 /* Miscellaneous Register */
103 /* Sequencer Registers */
104 0x00, 0x09, 0x0F, 0x00, 0x02,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
110 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
115 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
116 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00
119 static BYTE VideoMode_320x200_16color
[] =
121 /* Miscellaneous Register */
124 /* Sequencer Registers */
125 0x00, 0x09, 0x0F, 0x00, 0x02,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
131 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
136 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
137 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
140 static BYTE VideoMode_640x200_16color
[] =
142 /* Miscellaneous Register */
145 /* Sequencer Registers */
146 0x00, 0x01, 0x0F, 0x00, 0x02,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
152 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
153 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
157 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
158 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
161 static BYTE VideoMode_640x350_16color
[] =
163 /* Miscellaneous Register */
166 /* Sequencer Registers */
167 0x00, 0x01, 0x0F, 0x00, 0x02,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
173 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
178 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
179 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
182 static BYTE VideoMode_640x480_2color
[] =
184 /* Miscellaneous Register */
187 /* Sequencer Registers */
188 0x00, 0x01, 0x0F, 0x00, 0x02,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
194 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
199 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
200 0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
203 static BYTE VideoMode_640x480_16color
[] =
205 /* Miscellaneous Register */
208 /* Sequencer Registers */
209 0x00, 0x01, 0x0F, 0x00, 0x02,
212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
215 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
220 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
221 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
224 static BYTE VideoMode_320x200_256color
[] =
226 /* Miscellaneous Register */
229 /* Sequencer Registers */
230 0x00, 0x01, 0x0F, 0x00, 0x0E,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
236 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
241 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
242 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
245 static LPBYTE VideoModes
[] =
247 VideoMode_40x25_text
, /* Mode 00h */
248 VideoMode_40x25_text
, /* Mode 01h */
249 VideoMode_80x25_text
, /* Mode 02h */
250 VideoMode_80x25_text
, /* Mode 03h */
251 VideoMode_320x200_4color
, /* Mode 04h */
252 VideoMode_320x200_4color
, /* Mode 05h */
253 VideoMode_640x200_2color
, /* Mode 06h */
260 VideoMode_320x200_16color
, /* Mode 0Dh */
261 VideoMode_640x200_16color
, /* Mode 0Eh */
263 VideoMode_640x350_16color
, /* Mode 10h */
264 VideoMode_640x480_2color
, /* Mode 11h */
265 VideoMode_640x480_16color
, /* Mode 12h */
266 VideoMode_320x200_256color
, /* Mode 13h */
269 /* PRIVATE FUNCTIONS **********************************************************/
271 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
273 /* Get the location of the element after the tail */
274 WORD NextElement
= Bda
->KeybdBufferTail
+ sizeof(WORD
);
276 /* Wrap it around if it's at or beyond the end */
277 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
279 /* If it's full, fail */
280 if (NextElement
== Bda
->KeybdBufferHead
) return FALSE
;
282 /* Put the value in the queue */
283 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
284 Bda
->KeybdBufferTail
+= sizeof(WORD
);
286 /* Check if we are at, or have passed, the end of the buffer */
287 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
289 /* Return it to the beginning */
290 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
297 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
299 /* If it's empty, fail */
300 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
302 /* Otherwise, get the value and return success */
303 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
308 static BOOLEAN
BiosKbdBufferPop(VOID
)
310 /* If it's empty, fail */
311 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
313 /* Remove the value from the queue */
314 Bda
->KeybdBufferHead
+= sizeof(WORD
);
316 /* Check if we are at, or have passed, the end of the buffer */
317 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
319 /* Return it to the beginning */
320 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
327 static VOID
BiosReadWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
332 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
334 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
336 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
338 /* Read from video memory */
339 VgaReadMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
343 /* Write the data to the buffer in row order */
344 Buffer
[Counter
++] = Character
;
349 static VOID
BiosWriteWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
354 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
356 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
358 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
360 Character
= Buffer
[Counter
++];
362 /* Write to video memory */
363 VgaWriteMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
370 /* PUBLIC FUNCTIONS ***********************************************************/
372 BYTE
BiosGetVideoMode(VOID
)
374 return Bda
->VideoMode
;
377 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
381 LPBYTE Values
= VideoModes
[ModeNumber
];
383 DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber
, Values
);
385 if (Values
== NULL
) return FALSE
;
387 /* Write the misc register */
388 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
390 /* Write the sequencer registers */
391 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
393 VgaWritePort(VGA_SEQ_INDEX
, i
);
394 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
397 /* Write the GC registers */
398 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
400 VgaWritePort(VGA_GC_INDEX
, i
);
401 VgaWritePort(VGA_GC_DATA
, *(Values
++));
404 /* Write the CRTC registers */
405 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
407 VgaWritePort(VGA_CRTC_INDEX
, i
);
408 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
411 /* Write the AC registers */
412 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
414 VgaWritePort(VGA_AC_INDEX
, i
);
415 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
418 /* Reset the palette */
421 /* Update the values in the BDA */
422 Bda
->VideoMode
= ModeNumber
;
424 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
425 Bda
->VideoPageOffset
= 0;
427 /* Get the character height */
428 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_MAX_SCAN_LINE_REG
);
429 Bda
->CharacterHeight
= 1 + (VgaReadPort(VGA_CRTC_DATA
) & 0x1F);
431 Resolution
= VgaGetDisplayResolution();
432 Bda
->ScreenColumns
= Resolution
.X
;
433 Bda
->ScreenRows
= Resolution
.Y
- 1;
438 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
440 /* Check if the page exists */
441 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
443 /* Check if this is the same page */
444 if (PageNumber
== Bda
->VideoPage
) return TRUE
;
446 /* Set the values in the BDA */
447 Bda
->VideoPage
= PageNumber
;
448 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
449 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
451 /* Set the start address in the CRTC */
452 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
453 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
454 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
455 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
460 BOOLEAN
BiosInitialize(VOID
)
464 LPWORD IntVecTable
= (LPWORD
)BaseAddress
;
465 LPBYTE BiosCode
= (LPBYTE
)SEG_OFF_TO_PTR(BIOS_SEGMENT
, 0);
467 /* Initialize the BDA */
468 Bda
= (PBIOS_DATA_AREA
)SEG_OFF_TO_PTR(BDA_SEGMENT
, 0);
469 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
470 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
471 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
473 /* Generate ISR stubs and fill the IVT */
474 for (i
= 0x00; i
<= 0xFF; i
++)
476 IntVecTable
[i
* 2] = Offset
;
477 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
479 BiosCode
[Offset
++] = 0xFB; // sti
481 BiosCode
[Offset
++] = 0x6A; // push i
482 BiosCode
[Offset
++] = (UCHAR
)i
;
484 BiosCode
[Offset
++] = 0x6A; // push 0
485 BiosCode
[Offset
++] = 0x00;
488 BiosCode
[Offset
++] = 0xF8; // clc
490 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
491 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
492 BiosCode
[Offset
++] = EMULATOR_INT_BOP
;
494 BiosCode
[Offset
++] = 0x73; // jnc EXIT (offset +3)
495 BiosCode
[Offset
++] = 0x03;
497 // HACK: The following instruction should be HLT!
498 BiosCode
[Offset
++] = 0x90; // nop
500 BiosCode
[Offset
++] = 0xEB; // jmp BOP_SEQ (offset -9)
501 BiosCode
[Offset
++] = 0xF7;
504 BiosCode
[Offset
++] = 0x83; // add sp, 4
505 BiosCode
[Offset
++] = 0xC4;
506 BiosCode
[Offset
++] = 0x04;
508 BiosCode
[Offset
++] = 0xCF; // iret
511 /* Get the input handle to the real console, and check for success */
512 BiosConsoleInput
= CreateFileW(L
"CONIN$",
513 GENERIC_READ
| GENERIC_WRITE
,
514 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
519 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
524 /* Get the output handle to the real console, and check for success */
525 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
526 GENERIC_READ
| GENERIC_WRITE
,
527 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
532 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
534 CloseHandle(BiosConsoleInput
);
538 /* Save the console screen buffer information */
539 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
541 CloseHandle(BiosConsoleOutput
);
542 CloseHandle(BiosConsoleInput
);
547 if (!VgaInitialize(BiosConsoleOutput
))
549 CloseHandle(BiosConsoleOutput
);
550 CloseHandle(BiosConsoleInput
);
554 /* Update the cursor position */
555 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
556 BiosSavedBufferInfo
.dwCursorPosition
.X
,
559 /* Set the console input mode */
560 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
562 /* Start the input thread */
563 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, BiosConsoleInput
, 0, NULL
);
565 /* Initialize the PIC */
566 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
567 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
569 /* Set the interrupt offsets */
570 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
571 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
573 /* Tell the master PIC there is a slave at IRQ 2 */
574 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
575 PicWriteData(PIC_SLAVE_DATA
, 2);
577 /* Make sure the PIC is in 8086 mode */
578 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
579 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
581 /* Clear the masks for both PICs */
582 PicWriteData(PIC_MASTER_DATA
, 0x00);
583 PicWriteData(PIC_SLAVE_DATA
, 0x00);
585 PitWriteCommand(0x34);
586 PitWriteData(0, 0x00);
587 PitWriteData(0, 0x00);
592 VOID
BiosCleanup(VOID
)
594 /* Restore the old screen buffer */
595 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
597 /* Restore the screen buffer size */
598 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
600 /* Close the console handles */
601 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
602 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
604 /* Close the input thread handle */
605 if (InputThread
!= NULL
) CloseHandle(InputThread
);
608 WORD
BiosPeekCharacter(VOID
)
610 WORD CharacterData
= 0;
612 /* Get the key from the queue, but don't remove it */
613 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
617 WORD
BiosGetCharacter(VOID
)
619 WORD CharacterData
= 0;
621 /* Check if there is a key available */
622 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
624 /* Get the key from the queue, and remove it */
625 BiosKbdBufferTop(&CharacterData
);
630 /* Set the handler CF to repeat the BOP */
631 EmulatorSetFlag(EMULATOR_FLAG_CF
);
634 return CharacterData
;
637 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
639 /* Make sure the selected video page is valid */
640 if (Page
>= BIOS_MAX_PAGES
) return;
642 /* Update the position in the BDA */
643 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
645 /* Check if this is the current video page */
646 if (Page
== Bda
->VideoPage
)
648 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
650 /* Modify the CRTC registers */
651 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
652 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
653 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
654 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
658 BOOLEAN
BiosScrollWindow(INT Direction
,
660 SMALL_RECT Rectangle
,
666 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
667 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
669 /* Allocate a buffer for the window */
670 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
672 WindowSize
* sizeof(WORD
));
673 if (WindowData
== NULL
) return FALSE
;
675 /* Read the window data */
676 BiosReadWindow(WindowData
, Rectangle
, Page
);
680 /* Fill the window */
681 for (i
= 0; i
< WindowSize
; i
++)
683 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
689 // TODO: Scroll the window!
692 /* Write back the window data */
693 BiosWriteWindow(WindowData
, Rectangle
, Page
);
695 /* Free the window buffer */
696 HeapFree(GetProcessHeap(), 0, WindowData
);
701 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
703 WORD CharData
= (Attribute
<< 8) | Character
;
706 /* Make sure the page exists */
707 if (Page
>= BIOS_MAX_PAGES
) return;
709 /* Get the cursor location */
710 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
711 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
713 if (Character
== '\a')
715 /* Bell control character */
716 // NOTE: We may use what the terminal emulator offers to us...
720 else if (Character
== '\b')
722 /* Backspace control character */
729 Column
= Bda
->ScreenColumns
- 1;
733 /* Erase the existing character */
734 CharData
= (Attribute
<< 8) | ' ';
735 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
736 Page
* Bda
->VideoPageSize
737 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
741 else if (Character
== '\n')
743 /* Line Feed control character */
746 else if (Character
== '\r')
748 /* Carriage Return control character */
753 /* Default character */
755 /* Write the character */
756 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
757 Page
* Bda
->VideoPageSize
758 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
762 /* Advance the cursor */
766 /* Check if it passed the end of the row */
767 if (Column
>= Bda
->ScreenColumns
)
769 /* Return to the first column and go to the next line */
774 /* Scroll the screen up if needed */
775 if (Row
> Bda
->ScreenRows
)
777 /* The screen must be scrolled up */
778 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
780 BiosScrollWindow(SCROLL_DIRECTION_UP
,
787 /* Set the cursor position */
788 BiosSetCursorPosition(Row
, Column
, Page
);
791 VOID
BiosVideoService(LPWORD Stack
)
798 BiosSetVideoMode(getAL());
803 /* Set Text-Mode Cursor Shape */
807 Bda
->CursorStartLine
= getCH();
808 Bda
->CursorEndLine
= getCL();
810 /* Modify the CRTC registers */
811 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
812 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
813 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
814 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
819 /* Set Cursor Position */
822 BiosSetCursorPosition(getDH(), getDL(), getBH());
826 /* Get Cursor Position */
829 /* Make sure the selected video page exists */
830 if (getBH() >= BIOS_MAX_PAGES
) break;
832 /* Return the result */
834 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
835 setDX(Bda
->CursorPosition
[getBH()]);
839 /* Query Light Pen */
843 * On modern BIOSes, this function returns 0
844 * so that we can ignore the other registers.
850 /* Select Active Display Page */
853 BiosSetVideoPage(getAL());
857 /* Scroll Window Up/Down */
861 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
863 /* Call the internal function */
864 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
865 : SCROLL_DIRECTION_DOWN
,
874 /* Read/Write Character From Cursor Position */
879 WORD CharacterData
= MAKEWORD(getAL(), getBL());
883 /* Check if the page exists */
884 if (Page
>= BIOS_MAX_PAGES
) break;
886 /* Find the offset of the character */
887 Offset
= Page
* Bda
->VideoPageSize
+
888 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
889 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
893 /* Read from the video memory */
894 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
895 (LPVOID
)&CharacterData
,
898 /* Return the character in AX */
899 setAX(CharacterData
);
903 /* Write to video memory */
904 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
905 (LPVOID
)&CharacterData
,
906 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
912 /* Teletype Output */
915 BiosPrintCharacter(getAL(), getBL(), getBH());
919 /* Get Current Video Mode */
922 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
923 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
927 /* Palette Control */
932 /* Set Single Palette Register */
935 /* Reset the flip-flop */
936 VgaReadPort(VGA_STAT_COLOR
);
938 /* Write the index */
939 VgaWritePort(VGA_AC_INDEX
, getBL());
942 VgaWritePort(VGA_AC_WRITE
, getBH());
947 /* Set Overscan Color */
950 /* Reset the flip-flop */
951 VgaReadPort(VGA_STAT_COLOR
);
953 /* Write the index */
954 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
957 VgaWritePort(VGA_AC_WRITE
, getBH());
962 /* Set All Palette Registers */
966 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
968 /* Set the palette registers */
969 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
971 /* Reset the flip-flop */
972 VgaReadPort(VGA_STAT_COLOR
);
974 /* Write the index */
975 VgaWritePort(VGA_AC_INDEX
, i
);
978 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
981 /* Set the overscan register */
982 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
983 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
988 /* Get Single Palette Register */
991 /* Reset the flip-flop */
992 VgaReadPort(VGA_STAT_COLOR
);
994 /* Write the index */
995 VgaWritePort(VGA_AC_INDEX
, getBL());
998 setBH(VgaReadPort(VGA_AC_READ
));
1003 /* Get Overscan Color */
1006 /* Reset the flip-flop */
1007 VgaReadPort(VGA_STAT_COLOR
);
1009 /* Write the index */
1010 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1013 setBH(VgaReadPort(VGA_AC_READ
));
1018 /* Get All Palette Registers */
1022 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1024 /* Get the palette registers */
1025 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1027 /* Reset the flip-flop */
1028 VgaReadPort(VGA_STAT_COLOR
);
1030 /* Write the index */
1031 VgaWritePort(VGA_AC_INDEX
, i
);
1034 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1037 /* Get the overscan register */
1038 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1039 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1044 /* Set Individual DAC Register */
1047 /* Write the index */
1048 // Certainly in BL and not in BX as said by Ralf Brown...
1049 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1051 /* Write the data in this order: Red, Green, Blue */
1052 VgaWritePort(VGA_DAC_DATA
, getDH());
1053 VgaWritePort(VGA_DAC_DATA
, getCH());
1054 VgaWritePort(VGA_DAC_DATA
, getCL());
1059 /* Set Block of DAC Registers */
1063 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1065 /* Write the index */
1066 // Certainly in BL and not in BX as said by Ralf Brown...
1067 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1069 for (i
= 0; i
< getCX(); i
++)
1071 /* Write the data in this order: Red, Green, Blue */
1072 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1073 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1074 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1080 /* Get Individual DAC Register */
1083 /* Write the index */
1084 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1086 /* Read the data in this order: Red, Green, Blue */
1087 setDH(VgaReadPort(VGA_DAC_DATA
));
1088 setCH(VgaReadPort(VGA_DAC_DATA
));
1089 setCL(VgaReadPort(VGA_DAC_DATA
));
1094 /* Get Block of DAC Registers */
1098 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1100 /* Write the index */
1101 // Certainly in BL and not in BX as said by Ralf Brown...
1102 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1104 for (i
= 0; i
< getCX(); i
++)
1106 /* Write the data in this order: Red, Green, Blue */
1107 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1108 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1109 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1117 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1129 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1131 /* Call the internal function */
1132 BiosScrollWindow(getBL(),
1141 /* Display combination code */
1146 case 0x00: /* Get Display combiantion code */
1147 setAX(MAKEWORD(0x1A, 0x1A));
1148 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1150 case 0x01: /* Set Display combination code */
1151 DPRINT1("Set Display combination code - Unsupported\n");
1161 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1167 VOID
BiosKeyboardService(LPWORD Stack
)
1171 /* Wait for keystroke and read */
1173 /* Wait for extended keystroke and read */
1174 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1176 /* Read the character (and wait if necessary) */
1177 setAX(BiosGetCharacter());
1181 /* Get keystroke status */
1183 /* Get extended keystroke status */
1184 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1186 WORD Data
= BiosPeekCharacter();
1190 /* There is a character, clear ZF and return it */
1191 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1196 /* No character, set ZF */
1197 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1203 /* Get shift status */
1206 /* Return the lower byte of the keyboard shift status word */
1207 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1214 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1218 /* Push keystroke */
1221 /* Return 0 if success, 1 if failure */
1222 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1226 /* Get extended shift status */
1230 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1231 * but the high byte is organized differently:
1232 * the bytes 2 and 3 of the high byte are not the same...
1234 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1236 /* Return the extended keyboard shift status word */
1237 setAX(KeybdShiftFlags
);
1243 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1249 VOID
BiosTimeService(LPWORD Stack
)
1255 /* Set AL to 1 if midnight had passed, 0 otherwise */
1256 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1258 /* Return the tick count in CX:DX */
1259 setCX(HIWORD(Bda
->TickCounter
));
1260 setDX(LOWORD(Bda
->TickCounter
));
1262 /* Reset the midnight flag */
1263 Bda
->MidnightPassed
= FALSE
;
1270 /* Set the tick count to CX:DX */
1271 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1273 /* Reset the midnight flag */
1274 Bda
->MidnightPassed
= FALSE
;
1281 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1287 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1289 /* Increase the system tick count */
1293 VOID
BiosEquipmentService(LPWORD Stack
)
1295 /* Return the equipment list */
1296 setAX(Bda
->EquipmentList
);
1299 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1306 /* Perform the system timer interrupt */
1307 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1314 BYTE ScanCode
, VirtualKey
;
1317 /* Loop while there is a scancode available */
1320 /* Get the scan code and virtual key code */
1321 ScanCode
= KeyboardReadData();
1322 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1324 /* Check if this is a key press or release */
1325 if (!(ScanCode
& (1 << 7)))
1328 if (VirtualKey
== VK_NUMLOCK
||
1329 VirtualKey
== VK_CAPITAL
||
1330 VirtualKey
== VK_SCROLL
||
1331 VirtualKey
== VK_INSERT
)
1333 /* For toggle keys, toggle the lowest bit in the keyboard map */
1334 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1337 /* Set the highest bit */
1338 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1340 /* Find out which character this is */
1342 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1348 /* Push it onto the BIOS keyboard queue */
1349 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1353 /* Key release, unset the highest bit */
1354 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1357 while (KeyboardReadStatus() & 1);
1359 /* Clear the keyboard flags */
1360 Bda
->KeybdShiftFlags
= 0;
1362 /* Set the appropriate flags based on the state */
1363 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1364 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1365 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1366 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1367 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1368 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1369 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1370 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1371 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1372 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1373 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1374 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1375 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1376 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1377 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1378 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1384 /* Send End-of-Interrupt to the PIC */
1385 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1386 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);