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
BiosGetCursorPosition(PBYTE Row
, PBYTE Column
, BYTE Page
)
646 /* Make sure the selected video page is valid */
647 if (Page
>= BIOS_MAX_PAGES
) return;
649 /* Get the cursor location */
650 *Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
651 *Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
654 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
656 /* Make sure the selected video page is valid */
657 if (Page
>= BIOS_MAX_PAGES
) return;
659 /* Update the position in the BDA */
660 Bda
->CursorPosition
[Page
] = MAKEWORD(Column
, Row
);
662 /* Check if this is the current video page */
663 if (Page
== Bda
->VideoPage
)
665 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
667 /* Modify the CRTC registers */
668 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
669 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
670 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
671 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
675 BOOLEAN
BiosScrollWindow(INT Direction
,
677 SMALL_RECT Rectangle
,
683 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
684 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
686 /* Allocate a buffer for the window */
687 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
689 WindowSize
* sizeof(WORD
));
690 if (WindowData
== NULL
) return FALSE
;
692 /* Read the window data */
693 BiosReadWindow(WindowData
, Rectangle
, Page
);
697 /* Fill the window */
698 for (i
= 0; i
< WindowSize
; i
++)
700 WindowData
[i
] = MAKEWORD(' ', FillAttribute
);
706 // TODO: Scroll the window!
709 /* Write back the window data */
710 BiosWriteWindow(WindowData
, Rectangle
, Page
);
712 /* Free the window buffer */
713 HeapFree(GetProcessHeap(), 0, WindowData
);
718 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
720 WORD CharData
= MAKEWORD(Character
, Attribute
);
723 /* Make sure the page exists */
724 if (Page
>= BIOS_MAX_PAGES
) return;
726 /* Get the cursor location */
727 BiosGetCursorPosition(&Row
, &Column
, Page
);
729 if (Character
== '\a')
731 /* Bell control character */
732 // NOTE: We may use what the terminal emulator offers to us...
736 else if (Character
== '\b')
738 /* Backspace control character */
745 Column
= Bda
->ScreenColumns
- 1;
749 /* Erase the existing character */
750 CharData
= MAKEWORD(' ', Attribute
);
751 EmulatorWriteMemory(&EmulatorContext
,
752 TO_LINEAR(TEXT_VIDEO_SEG
,
753 Page
* Bda
->VideoPageSize
+
754 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
758 else if (Character
== '\t')
760 /* Horizontal Tabulation control character */
764 BiosPrintCharacter(' ', Attribute
, Page
);
765 BiosGetCursorPosition(&Row
, &Column
, Page
);
766 } while (Column
% 8);
768 else if (Character
== '\n')
770 /* Line Feed control character */
773 else if (Character
== '\r')
775 /* Carriage Return control character */
780 /* Default character */
782 /* Write the character */
783 EmulatorWriteMemory(&EmulatorContext
,
784 TO_LINEAR(TEXT_VIDEO_SEG
,
785 Page
* Bda
->VideoPageSize
+
786 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
790 /* Advance the cursor */
794 /* Check if it passed the end of the row */
795 if (Column
>= Bda
->ScreenColumns
)
797 /* Return to the first column and go to the next line */
802 /* Scroll the screen up if needed */
803 if (Row
> Bda
->ScreenRows
)
805 /* The screen must be scrolled up */
806 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
808 BiosScrollWindow(SCROLL_DIRECTION_UP
,
817 /* Set the cursor position */
818 BiosSetCursorPosition(Row
, Column
, Page
);
821 VOID
BiosVideoService(LPWORD Stack
)
828 BiosSetVideoMode(getAL());
833 /* Set Text-Mode Cursor Shape */
837 Bda
->CursorStartLine
= getCH();
838 Bda
->CursorEndLine
= getCL();
840 /* Modify the CRTC registers */
841 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
842 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
843 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
844 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
849 /* Set Cursor Position */
852 BiosSetCursorPosition(getDH(), getDL(), getBH());
856 /* Get Cursor Position */
859 /* Make sure the selected video page exists */
860 if (getBH() >= BIOS_MAX_PAGES
) break;
862 /* Return the result */
864 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
865 setDX(Bda
->CursorPosition
[getBH()]);
869 /* Query Light Pen */
873 * On modern BIOSes, this function returns 0
874 * so that we can ignore the other registers.
880 /* Select Active Display Page */
883 BiosSetVideoPage(getAL());
887 /* Scroll Window Up/Down */
891 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
893 /* Call the internal function */
894 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
895 : SCROLL_DIRECTION_DOWN
,
904 /* Read/Write Character From Cursor Position */
909 WORD CharacterData
= MAKEWORD(getAL(), getBL());
913 /* Check if the page exists */
914 if (Page
>= BIOS_MAX_PAGES
) break;
916 /* Find the offset of the character */
917 Offset
= Page
* Bda
->VideoPageSize
+
918 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
919 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
923 /* Read from the video memory */
924 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
925 (LPVOID
)&CharacterData
,
928 /* Return the character in AX */
929 setAX(CharacterData
);
933 /* Write to video memory */
934 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
935 (LPVOID
)&CharacterData
,
936 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
942 /* Teletype Output */
945 BiosPrintCharacter(getAL(), getBL(), getBH());
949 /* Get Current Video Mode */
952 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
953 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
957 /* Palette Control */
962 /* Set Single Palette Register */
965 /* Reset the flip-flop */
966 VgaReadPort(VGA_STAT_COLOR
);
968 /* Write the index */
969 VgaWritePort(VGA_AC_INDEX
, getBL());
972 VgaWritePort(VGA_AC_WRITE
, getBH());
977 /* Set Overscan Color */
980 /* Reset the flip-flop */
981 VgaReadPort(VGA_STAT_COLOR
);
983 /* Write the index */
984 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
987 VgaWritePort(VGA_AC_WRITE
, getBH());
992 /* Set All Palette Registers */
996 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
998 /* Set the palette registers */
999 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1001 /* Reset the flip-flop */
1002 VgaReadPort(VGA_STAT_COLOR
);
1004 /* Write the index */
1005 VgaWritePort(VGA_AC_INDEX
, i
);
1007 /* Write the data */
1008 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
1011 /* Set the overscan register */
1012 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1013 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
1018 /* Get Single Palette Register */
1021 /* Reset the flip-flop */
1022 VgaReadPort(VGA_STAT_COLOR
);
1024 /* Write the index */
1025 VgaWritePort(VGA_AC_INDEX
, getBL());
1028 setBH(VgaReadPort(VGA_AC_READ
));
1033 /* Get Overscan Color */
1036 /* Reset the flip-flop */
1037 VgaReadPort(VGA_STAT_COLOR
);
1039 /* Write the index */
1040 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1043 setBH(VgaReadPort(VGA_AC_READ
));
1048 /* Get All Palette Registers */
1052 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1054 /* Get the palette registers */
1055 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1057 /* Reset the flip-flop */
1058 VgaReadPort(VGA_STAT_COLOR
);
1060 /* Write the index */
1061 VgaWritePort(VGA_AC_INDEX
, i
);
1064 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1067 /* Get the overscan register */
1068 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1069 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1074 /* Set Individual DAC Register */
1077 /* Write the index */
1078 // Certainly in BL and not in BX as said by Ralf Brown...
1079 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1081 /* Write the data in this order: Red, Green, Blue */
1082 VgaWritePort(VGA_DAC_DATA
, getDH());
1083 VgaWritePort(VGA_DAC_DATA
, getCH());
1084 VgaWritePort(VGA_DAC_DATA
, getCL());
1089 /* Set Block of DAC Registers */
1093 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1095 /* Write the index */
1096 // Certainly in BL and not in BX as said by Ralf Brown...
1097 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1099 for (i
= 0; i
< getCX(); i
++)
1101 /* Write the data in this order: Red, Green, Blue */
1102 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1103 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1104 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1110 /* Get Individual DAC Register */
1113 /* Write the index */
1114 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1116 /* Read the data in this order: Red, Green, Blue */
1117 setDH(VgaReadPort(VGA_DAC_DATA
));
1118 setCH(VgaReadPort(VGA_DAC_DATA
));
1119 setCL(VgaReadPort(VGA_DAC_DATA
));
1124 /* Get Block of DAC Registers */
1128 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1130 /* Write the index */
1131 // Certainly in BL and not in BX as said by Ralf Brown...
1132 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1134 for (i
= 0; i
< getCX(); i
++)
1136 /* Write the data in this order: Red, Green, Blue */
1137 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1138 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1139 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1147 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1159 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1161 /* Call the internal function */
1162 BiosScrollWindow(getBL(),
1171 /* Display combination code */
1176 case 0x00: /* Get Display combiantion code */
1177 setAX(MAKEWORD(0x1A, 0x1A));
1178 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1180 case 0x01: /* Set Display combination code */
1181 DPRINT1("Set Display combination code - Unsupported\n");
1191 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1197 VOID
BiosEquipmentService(LPWORD Stack
)
1199 /* Return the equipment list */
1200 setAX(Bda
->EquipmentList
);
1203 VOID
BiosGetMemorySize(LPWORD Stack
)
1205 /* Return the conventional memory size in kB, typically 640 kB */
1206 setAX(Bda
->MemorySize
);
1209 VOID
BiosKeyboardService(LPWORD Stack
)
1213 /* Wait for keystroke and read */
1215 /* Wait for extended keystroke and read */
1216 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1218 /* Read the character (and wait if necessary) */
1219 setAX(BiosGetCharacter());
1223 /* Get keystroke status */
1225 /* Get extended keystroke status */
1226 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1228 WORD Data
= BiosPeekCharacter();
1232 /* There is a character, clear ZF and return it */
1233 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1238 /* No character, set ZF */
1239 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1245 /* Get shift status */
1248 /* Return the lower byte of the keyboard shift status word */
1249 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1256 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1260 /* Push keystroke */
1263 /* Return 0 if success, 1 if failure */
1264 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1268 /* Get extended shift status */
1272 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1273 * but the high byte is organized differently:
1274 * the bytes 2 and 3 of the high byte are not the same...
1276 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1278 /* Return the extended keyboard shift status word */
1279 setAX(KeybdShiftFlags
);
1285 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1291 VOID
BiosTimeService(LPWORD Stack
)
1297 /* Set AL to 1 if midnight had passed, 0 otherwise */
1298 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1300 /* Return the tick count in CX:DX */
1301 setCX(HIWORD(Bda
->TickCounter
));
1302 setDX(LOWORD(Bda
->TickCounter
));
1304 /* Reset the midnight flag */
1305 Bda
->MidnightPassed
= FALSE
;
1312 /* Set the tick count to CX:DX */
1313 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1315 /* Reset the midnight flag */
1316 Bda
->MidnightPassed
= FALSE
;
1323 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1329 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
1331 /* Increase the system tick count */
1335 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1342 /* Perform the system timer interrupt */
1343 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1350 BYTE ScanCode
, VirtualKey
;
1353 /* Loop while there is a scancode available */
1356 /* Get the scan code and virtual key code */
1357 ScanCode
= KeyboardReadData();
1358 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1360 /* Check if this is a key press or release */
1361 if (!(ScanCode
& (1 << 7)))
1364 if (VirtualKey
== VK_NUMLOCK
||
1365 VirtualKey
== VK_CAPITAL
||
1366 VirtualKey
== VK_SCROLL
||
1367 VirtualKey
== VK_INSERT
)
1369 /* For toggle keys, toggle the lowest bit in the keyboard map */
1370 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1373 /* Set the highest bit */
1374 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1376 /* Find out which character this is */
1378 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1384 /* Push it onto the BIOS keyboard queue */
1385 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1389 /* Key release, unset the highest bit */
1390 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1393 while (KeyboardReadStatus() & 1);
1395 /* Clear the keyboard flags */
1396 Bda
->KeybdShiftFlags
= 0;
1398 /* Set the appropriate flags based on the state */
1399 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1400 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1401 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1402 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1403 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1404 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1405 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1406 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1407 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1408 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1409 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1410 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1411 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1412 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1413 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1414 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1420 /* Send End-of-Interrupt to the PIC */
1421 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1422 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);