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 EmulatorWriteMemory(&EmulatorContext
,
764 TO_LINEAR(TEXT_VIDEO_SEG
,
765 Page
* Bda
->VideoPageSize
766 + (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
770 /* Advance the cursor */
774 /* Check if it passed the end of the row */
775 if (Column
>= Bda
->ScreenColumns
)
777 /* Return to the first column and go to the next line */
782 /* Scroll the screen up if needed */
783 if (Row
> Bda
->ScreenRows
)
785 /* The screen must be scrolled up */
786 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
788 BiosScrollWindow(SCROLL_DIRECTION_UP
,
797 /* Set the cursor position */
798 BiosSetCursorPosition(Row
, Column
, Page
);
801 VOID
BiosVideoService(LPWORD Stack
)
808 BiosSetVideoMode(getAL());
813 /* Set Text-Mode Cursor Shape */
817 Bda
->CursorStartLine
= getCH();
818 Bda
->CursorEndLine
= getCL();
820 /* Modify the CRTC registers */
821 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
822 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
823 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
824 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
829 /* Set Cursor Position */
832 BiosSetCursorPosition(getDH(), getDL(), getBH());
836 /* Get Cursor Position */
839 /* Make sure the selected video page exists */
840 if (getBH() >= BIOS_MAX_PAGES
) break;
842 /* Return the result */
844 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
845 setDX(Bda
->CursorPosition
[getBH()]);
849 /* Query Light Pen */
853 * On modern BIOSes, this function returns 0
854 * so that we can ignore the other registers.
860 /* Select Active Display Page */
863 BiosSetVideoPage(getAL());
867 /* Scroll Window Up/Down */
871 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
873 /* Call the internal function */
874 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
875 : SCROLL_DIRECTION_DOWN
,
884 /* Read/Write Character From Cursor Position */
889 WORD CharacterData
= MAKEWORD(getAL(), getBL());
893 /* Check if the page exists */
894 if (Page
>= BIOS_MAX_PAGES
) break;
896 /* Find the offset of the character */
897 Offset
= Page
* Bda
->VideoPageSize
+
898 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
899 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
903 /* Read from the video memory */
904 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
905 (LPVOID
)&CharacterData
,
908 /* Return the character in AX */
909 setAX(CharacterData
);
913 /* Write to video memory */
914 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
915 (LPVOID
)&CharacterData
,
916 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
922 /* Teletype Output */
925 BiosPrintCharacter(getAL(), getBL(), getBH());
929 /* Get Current Video Mode */
932 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
933 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
937 /* Palette Control */
942 /* Set Single Palette Register */
945 /* Reset the flip-flop */
946 VgaReadPort(VGA_STAT_COLOR
);
948 /* Write the index */
949 VgaWritePort(VGA_AC_INDEX
, getBL());
952 VgaWritePort(VGA_AC_WRITE
, getBH());
957 /* Set Overscan Color */
960 /* Reset the flip-flop */
961 VgaReadPort(VGA_STAT_COLOR
);
963 /* Write the index */
964 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
967 VgaWritePort(VGA_AC_WRITE
, getBH());
972 /* Set All Palette Registers */
976 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
978 /* Set the palette registers */
979 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
981 /* Reset the flip-flop */
982 VgaReadPort(VGA_STAT_COLOR
);
984 /* Write the index */
985 VgaWritePort(VGA_AC_INDEX
, i
);
988 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
991 /* Set the overscan register */
992 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
993 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
998 /* Get Single Palette Register */
1001 /* Reset the flip-flop */
1002 VgaReadPort(VGA_STAT_COLOR
);
1004 /* Write the index */
1005 VgaWritePort(VGA_AC_INDEX
, getBL());
1008 setBH(VgaReadPort(VGA_AC_READ
));
1013 /* Get Overscan Color */
1016 /* Reset the flip-flop */
1017 VgaReadPort(VGA_STAT_COLOR
);
1019 /* Write the index */
1020 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1023 setBH(VgaReadPort(VGA_AC_READ
));
1028 /* Get All Palette Registers */
1032 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1034 /* Get the palette registers */
1035 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1037 /* Reset the flip-flop */
1038 VgaReadPort(VGA_STAT_COLOR
);
1040 /* Write the index */
1041 VgaWritePort(VGA_AC_INDEX
, i
);
1044 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1047 /* Get the overscan register */
1048 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1049 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1054 /* Set Individual DAC Register */
1057 /* Write the index */
1058 // Certainly in BL and not in BX as said by Ralf Brown...
1059 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1061 /* Write the data in this order: Red, Green, Blue */
1062 VgaWritePort(VGA_DAC_DATA
, getDH());
1063 VgaWritePort(VGA_DAC_DATA
, getCH());
1064 VgaWritePort(VGA_DAC_DATA
, getCL());
1069 /* Set Block of DAC Registers */
1073 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1075 /* Write the index */
1076 // Certainly in BL and not in BX as said by Ralf Brown...
1077 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1079 for (i
= 0; i
< getCX(); i
++)
1081 /* Write the data in this order: Red, Green, Blue */
1082 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1083 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1084 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1090 /* Get Individual DAC Register */
1093 /* Write the index */
1094 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1096 /* Read the data in this order: Red, Green, Blue */
1097 setDH(VgaReadPort(VGA_DAC_DATA
));
1098 setCH(VgaReadPort(VGA_DAC_DATA
));
1099 setCL(VgaReadPort(VGA_DAC_DATA
));
1104 /* Get Block of DAC Registers */
1108 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1110 /* Write the index */
1111 // Certainly in BL and not in BX as said by Ralf Brown...
1112 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1114 for (i
= 0; i
< getCX(); i
++)
1116 /* Write the data in this order: Red, Green, Blue */
1117 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1118 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1119 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1127 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1139 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1141 /* Call the internal function */
1142 BiosScrollWindow(getBL(),
1151 /* Display combination code */
1156 case 0x00: /* Get Display combiantion code */
1157 setAX(MAKEWORD(0x1A, 0x1A));
1158 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1160 case 0x01: /* Set Display combination code */
1161 DPRINT1("Set Display combination code - Unsupported\n");
1171 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1177 VOID
BiosKeyboardService(LPWORD Stack
)
1181 /* Wait for keystroke and read */
1183 /* Wait for extended keystroke and read */
1184 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1186 /* Read the character (and wait if necessary) */
1187 setAX(BiosGetCharacter());
1191 /* Get keystroke status */
1193 /* Get extended keystroke status */
1194 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1196 WORD Data
= BiosPeekCharacter();
1200 /* There is a character, clear ZF and return it */
1201 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1206 /* No character, set ZF */
1207 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1213 /* Get shift status */
1216 /* Return the lower byte of the keyboard shift status word */
1217 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1224 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1228 /* Push keystroke */
1231 /* Return 0 if success, 1 if failure */
1232 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1236 /* Get extended shift status */
1240 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1241 * but the high byte is organized differently:
1242 * the bytes 2 and 3 of the high byte are not the same...
1244 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1246 /* Return the extended keyboard shift status word */
1247 setAX(KeybdShiftFlags
);
1253 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1259 VOID
BiosTimeService(LPWORD Stack
)
1265 /* Set AL to 1 if midnight had passed, 0 otherwise */
1266 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1268 /* Return the tick count in CX:DX */
1269 setCX(HIWORD(Bda
->TickCounter
));
1270 setDX(LOWORD(Bda
->TickCounter
));
1272 /* Reset the midnight flag */
1273 Bda
->MidnightPassed
= FALSE
;
1280 /* Set the tick count to CX:DX */
1281 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1283 /* Reset the midnight flag */
1284 Bda
->MidnightPassed
= FALSE
;
1291 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1297 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1299 /* Increase the system tick count */
1303 VOID
BiosEquipmentService(LPWORD Stack
)
1305 /* Return the equipment list */
1306 setAX(Bda
->EquipmentList
);
1309 VOID
BiosGetMemorySize(LPWORD Stack
)
1311 /* Return the conventional memory size in kB, typically 640 kB */
1312 setAX(Bda
->MemorySize
);
1315 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1322 /* Perform the system timer interrupt */
1323 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1330 BYTE ScanCode
, VirtualKey
;
1333 /* Loop while there is a scancode available */
1336 /* Get the scan code and virtual key code */
1337 ScanCode
= KeyboardReadData();
1338 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1340 /* Check if this is a key press or release */
1341 if (!(ScanCode
& (1 << 7)))
1344 if (VirtualKey
== VK_NUMLOCK
||
1345 VirtualKey
== VK_CAPITAL
||
1346 VirtualKey
== VK_SCROLL
||
1347 VirtualKey
== VK_INSERT
)
1349 /* For toggle keys, toggle the lowest bit in the keyboard map */
1350 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1353 /* Set the highest bit */
1354 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1356 /* Find out which character this is */
1358 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1364 /* Push it onto the BIOS keyboard queue */
1365 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1369 /* Key release, unset the highest bit */
1370 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1373 while (KeyboardReadStatus() & 1);
1375 /* Clear the keyboard flags */
1376 Bda
->KeybdShiftFlags
= 0;
1378 /* Set the appropriate flags based on the state */
1379 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1380 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1381 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1382 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1383 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1384 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1385 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1386 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1387 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1388 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1389 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1390 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1391 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1392 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1393 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1394 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1400 /* Send End-of-Interrupt to the PIC */
1401 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1402 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);