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
;
471 * Conventional memory size is 640 kB,
472 * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
473 * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
474 * for more information.
476 Bda
->MemorySize
= 0x0280;
477 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
478 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
480 /* Generate ISR stubs and fill the IVT */
481 for (i
= 0x00; i
<= 0xFF; i
++)
483 IntVecTable
[i
* 2] = Offset
;
484 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
486 BiosCode
[Offset
++] = 0xFB; // sti
488 BiosCode
[Offset
++] = 0x6A; // push i
489 BiosCode
[Offset
++] = (UCHAR
)i
;
491 BiosCode
[Offset
++] = 0x6A; // push 0
492 BiosCode
[Offset
++] = 0x00;
495 BiosCode
[Offset
++] = 0xF8; // clc
497 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
498 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
499 BiosCode
[Offset
++] = EMULATOR_INT_BOP
;
501 BiosCode
[Offset
++] = 0x73; // jnc EXIT (offset +3)
502 BiosCode
[Offset
++] = 0x03;
504 // HACK: The following instruction should be HLT!
505 BiosCode
[Offset
++] = 0x90; // nop
507 BiosCode
[Offset
++] = 0xEB; // jmp BOP_SEQ (offset -9)
508 BiosCode
[Offset
++] = 0xF7;
511 BiosCode
[Offset
++] = 0x83; // add sp, 4
512 BiosCode
[Offset
++] = 0xC4;
513 BiosCode
[Offset
++] = 0x04;
515 BiosCode
[Offset
++] = 0xCF; // iret
518 /* Get the input handle to the real console, and check for success */
519 BiosConsoleInput
= CreateFileW(L
"CONIN$",
520 GENERIC_READ
| GENERIC_WRITE
,
521 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
526 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
531 /* Get the output handle to the real console, and check for success */
532 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
533 GENERIC_READ
| GENERIC_WRITE
,
534 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
539 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
541 CloseHandle(BiosConsoleInput
);
545 /* Save the console screen buffer information */
546 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
548 CloseHandle(BiosConsoleOutput
);
549 CloseHandle(BiosConsoleInput
);
554 if (!VgaInitialize(BiosConsoleOutput
))
556 CloseHandle(BiosConsoleOutput
);
557 CloseHandle(BiosConsoleInput
);
561 /* Update the cursor position */
562 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
563 BiosSavedBufferInfo
.dwCursorPosition
.X
,
566 /* Set the console input mode */
567 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
569 /* Start the input thread */
570 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, BiosConsoleInput
, 0, NULL
);
572 /* Initialize the PIC */
573 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
574 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
576 /* Set the interrupt offsets */
577 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
578 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
580 /* Tell the master PIC there is a slave at IRQ 2 */
581 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
582 PicWriteData(PIC_SLAVE_DATA
, 2);
584 /* Make sure the PIC is in 8086 mode */
585 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
586 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
588 /* Clear the masks for both PICs */
589 PicWriteData(PIC_MASTER_DATA
, 0x00);
590 PicWriteData(PIC_SLAVE_DATA
, 0x00);
592 PitWriteCommand(0x34);
593 PitWriteData(0, 0x00);
594 PitWriteData(0, 0x00);
599 VOID
BiosCleanup(VOID
)
601 /* Restore the old screen buffer */
602 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
604 /* Restore the screen buffer size */
605 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
607 /* Close the console handles */
608 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
609 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
611 /* Close the input thread handle */
612 if (InputThread
!= NULL
) CloseHandle(InputThread
);
615 WORD
BiosPeekCharacter(VOID
)
617 WORD CharacterData
= 0;
619 /* Get the key from the queue, but don't remove it */
620 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
624 WORD
BiosGetCharacter(VOID
)
626 WORD CharacterData
= 0;
628 /* Check if there is a key available */
629 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
631 /* Get the key from the queue, and remove it */
632 BiosKbdBufferTop(&CharacterData
);
637 /* Set the handler CF to repeat the BOP */
638 EmulatorSetFlag(EMULATOR_FLAG_CF
);
641 return CharacterData
;
644 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
646 /* Make sure the selected video page is valid */
647 if (Page
>= BIOS_MAX_PAGES
) return;
649 /* Update the position in the BDA */
650 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
652 /* Check if this is the current video page */
653 if (Page
== Bda
->VideoPage
)
655 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
657 /* Modify the CRTC registers */
658 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
659 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
660 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
661 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
665 BOOLEAN
BiosScrollWindow(INT Direction
,
667 SMALL_RECT Rectangle
,
673 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
674 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
676 /* Allocate a buffer for the window */
677 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
679 WindowSize
* sizeof(WORD
));
680 if (WindowData
== NULL
) return FALSE
;
682 /* Read the window data */
683 BiosReadWindow(WindowData
, Rectangle
, Page
);
687 /* Fill the window */
688 for (i
= 0; i
< WindowSize
; i
++)
690 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
696 // TODO: Scroll the window!
699 /* Write back the window data */
700 BiosWriteWindow(WindowData
, Rectangle
, Page
);
702 /* Free the window buffer */
703 HeapFree(GetProcessHeap(), 0, WindowData
);
708 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
710 WORD CharData
= (Attribute
<< 8) | Character
;
713 /* Make sure the page exists */
714 if (Page
>= BIOS_MAX_PAGES
) return;
716 /* Get the cursor location */
717 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
718 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
720 if (Character
== '\a')
722 /* Bell control character */
723 // NOTE: We may use what the terminal emulator offers to us...
727 else if (Character
== '\b')
729 /* Backspace control character */
736 Column
= Bda
->ScreenColumns
- 1;
740 /* Erase the existing character */
741 CharData
= (Attribute
<< 8) | ' ';
742 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
743 Page
* Bda
->VideoPageSize
744 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
748 else if (Character
== '\n')
750 /* Line Feed control character */
753 else if (Character
== '\r')
755 /* Carriage Return control character */
760 /* Default character */
762 /* Write the character */
763 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
764 Page
* Bda
->VideoPageSize
765 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
769 /* Advance the cursor */
773 /* Check if it passed the end of the row */
774 if (Column
>= Bda
->ScreenColumns
)
776 /* Return to the first column and go to the next line */
781 /* Scroll the screen up if needed */
782 if (Row
> Bda
->ScreenRows
)
784 /* The screen must be scrolled up */
785 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
787 BiosScrollWindow(SCROLL_DIRECTION_UP
,
794 /* Set the cursor position */
795 BiosSetCursorPosition(Row
, Column
, Page
);
798 VOID
BiosVideoService(LPWORD Stack
)
805 BiosSetVideoMode(getAL());
810 /* Set Text-Mode Cursor Shape */
814 Bda
->CursorStartLine
= getCH();
815 Bda
->CursorEndLine
= getCL();
817 /* Modify the CRTC registers */
818 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
819 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
820 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
821 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
826 /* Set Cursor Position */
829 BiosSetCursorPosition(getDH(), getDL(), getBH());
833 /* Get Cursor Position */
836 /* Make sure the selected video page exists */
837 if (getBH() >= BIOS_MAX_PAGES
) break;
839 /* Return the result */
841 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
842 setDX(Bda
->CursorPosition
[getBH()]);
846 /* Query Light Pen */
850 * On modern BIOSes, this function returns 0
851 * so that we can ignore the other registers.
857 /* Select Active Display Page */
860 BiosSetVideoPage(getAL());
864 /* Scroll Window Up/Down */
868 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
870 /* Call the internal function */
871 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
872 : SCROLL_DIRECTION_DOWN
,
881 /* Read/Write Character From Cursor Position */
886 WORD CharacterData
= MAKEWORD(getAL(), getBL());
890 /* Check if the page exists */
891 if (Page
>= BIOS_MAX_PAGES
) break;
893 /* Find the offset of the character */
894 Offset
= Page
* Bda
->VideoPageSize
+
895 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
896 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
900 /* Read from the video memory */
901 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
902 (LPVOID
)&CharacterData
,
905 /* Return the character in AX */
906 setAX(CharacterData
);
910 /* Write to video memory */
911 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
912 (LPVOID
)&CharacterData
,
913 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
919 /* Teletype Output */
922 BiosPrintCharacter(getAL(), getBL(), getBH());
926 /* Get Current Video Mode */
929 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
930 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
934 /* Palette Control */
939 /* Set Single Palette Register */
942 /* Reset the flip-flop */
943 VgaReadPort(VGA_STAT_COLOR
);
945 /* Write the index */
946 VgaWritePort(VGA_AC_INDEX
, getBL());
949 VgaWritePort(VGA_AC_WRITE
, getBH());
954 /* Set Overscan Color */
957 /* Reset the flip-flop */
958 VgaReadPort(VGA_STAT_COLOR
);
960 /* Write the index */
961 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
964 VgaWritePort(VGA_AC_WRITE
, getBH());
969 /* Set All Palette Registers */
973 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
975 /* Set the palette registers */
976 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
978 /* Reset the flip-flop */
979 VgaReadPort(VGA_STAT_COLOR
);
981 /* Write the index */
982 VgaWritePort(VGA_AC_INDEX
, i
);
985 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
988 /* Set the overscan register */
989 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
990 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
995 /* Get Single Palette Register */
998 /* Reset the flip-flop */
999 VgaReadPort(VGA_STAT_COLOR
);
1001 /* Write the index */
1002 VgaWritePort(VGA_AC_INDEX
, getBL());
1005 setBH(VgaReadPort(VGA_AC_READ
));
1010 /* Get Overscan Color */
1013 /* Reset the flip-flop */
1014 VgaReadPort(VGA_STAT_COLOR
);
1016 /* Write the index */
1017 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1020 setBH(VgaReadPort(VGA_AC_READ
));
1025 /* Get All Palette Registers */
1029 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1031 /* Get the palette registers */
1032 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1034 /* Reset the flip-flop */
1035 VgaReadPort(VGA_STAT_COLOR
);
1037 /* Write the index */
1038 VgaWritePort(VGA_AC_INDEX
, i
);
1041 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1044 /* Get the overscan register */
1045 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1046 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1051 /* Set Individual DAC Register */
1054 /* Write the index */
1055 // Certainly in BL and not in BX as said by Ralf Brown...
1056 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1058 /* Write the data in this order: Red, Green, Blue */
1059 VgaWritePort(VGA_DAC_DATA
, getDH());
1060 VgaWritePort(VGA_DAC_DATA
, getCH());
1061 VgaWritePort(VGA_DAC_DATA
, getCL());
1066 /* Set Block of DAC Registers */
1070 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1072 /* Write the index */
1073 // Certainly in BL and not in BX as said by Ralf Brown...
1074 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1076 for (i
= 0; i
< getCX(); i
++)
1078 /* Write the data in this order: Red, Green, Blue */
1079 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1080 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1081 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1087 /* Get Individual DAC Register */
1090 /* Write the index */
1091 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1093 /* Read the data in this order: Red, Green, Blue */
1094 setDH(VgaReadPort(VGA_DAC_DATA
));
1095 setCH(VgaReadPort(VGA_DAC_DATA
));
1096 setCL(VgaReadPort(VGA_DAC_DATA
));
1101 /* Get Block of DAC Registers */
1105 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1107 /* Write the index */
1108 // Certainly in BL and not in BX as said by Ralf Brown...
1109 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1111 for (i
= 0; i
< getCX(); i
++)
1113 /* Write the data in this order: Red, Green, Blue */
1114 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1115 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1116 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1124 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1136 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1138 /* Call the internal function */
1139 BiosScrollWindow(getBL(),
1148 /* Display combination code */
1153 case 0x00: /* Get Display combiantion code */
1154 setAX(MAKEWORD(0x1A, 0x1A));
1155 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1157 case 0x01: /* Set Display combination code */
1158 DPRINT1("Set Display combination code - Unsupported\n");
1168 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1174 VOID
BiosKeyboardService(LPWORD Stack
)
1178 /* Wait for keystroke and read */
1180 /* Wait for extended keystroke and read */
1181 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1183 /* Read the character (and wait if necessary) */
1184 setAX(BiosGetCharacter());
1188 /* Get keystroke status */
1190 /* Get extended keystroke status */
1191 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1193 WORD Data
= BiosPeekCharacter();
1197 /* There is a character, clear ZF and return it */
1198 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1203 /* No character, set ZF */
1204 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1210 /* Get shift status */
1213 /* Return the lower byte of the keyboard shift status word */
1214 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1221 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1225 /* Push keystroke */
1228 /* Return 0 if success, 1 if failure */
1229 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1233 /* Get extended shift status */
1237 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1238 * but the high byte is organized differently:
1239 * the bytes 2 and 3 of the high byte are not the same...
1241 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1243 /* Return the extended keyboard shift status word */
1244 setAX(KeybdShiftFlags
);
1250 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1256 VOID
BiosTimeService(LPWORD Stack
)
1262 /* Set AL to 1 if midnight had passed, 0 otherwise */
1263 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1265 /* Return the tick count in CX:DX */
1266 setCX(HIWORD(Bda
->TickCounter
));
1267 setDX(LOWORD(Bda
->TickCounter
));
1269 /* Reset the midnight flag */
1270 Bda
->MidnightPassed
= FALSE
;
1277 /* Set the tick count to CX:DX */
1278 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1280 /* Reset the midnight flag */
1281 Bda
->MidnightPassed
= FALSE
;
1288 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1294 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1296 /* Increase the system tick count */
1300 VOID
BiosEquipmentService(LPWORD Stack
)
1302 /* Return the equipment list */
1303 setAX(Bda
->EquipmentList
);
1306 VOID
BiosGetMemorySize(LPWORD Stack
)
1308 /* Return the conventional memory size in kB, typically 640 kB */
1309 setAX(Bda
->MemorySize
);
1312 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1319 /* Perform the system timer interrupt */
1320 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1327 BYTE ScanCode
, VirtualKey
;
1330 /* Loop while there is a scancode available */
1333 /* Get the scan code and virtual key code */
1334 ScanCode
= KeyboardReadData();
1335 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1337 /* Check if this is a key press or release */
1338 if (!(ScanCode
& (1 << 7)))
1341 if (VirtualKey
== VK_NUMLOCK
||
1342 VirtualKey
== VK_CAPITAL
||
1343 VirtualKey
== VK_SCROLL
||
1344 VirtualKey
== VK_INSERT
)
1346 /* For toggle keys, toggle the lowest bit in the keyboard map */
1347 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1350 /* Set the highest bit */
1351 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1353 /* Find out which character this is */
1355 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1361 /* Push it onto the BIOS keyboard queue */
1362 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1366 /* Key release, unset the highest bit */
1367 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1370 while (KeyboardReadStatus() & 1);
1372 /* Clear the keyboard flags */
1373 Bda
->KeybdShiftFlags
= 0;
1375 /* Set the appropriate flags based on the state */
1376 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1377 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1378 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1379 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1380 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1381 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1382 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1383 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1384 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1385 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1386 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1387 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1388 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1389 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1390 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1391 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1397 /* Send End-of-Interrupt to the PIC */
1398 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1399 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);