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 /* PRIVATE VARIABLES **********************************************************/
22 static PBIOS_DATA_AREA Bda
;
23 static BYTE BiosKeyboardMap
[256];
24 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
25 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
26 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
28 static BYTE VideoMode_40x25_text
[] =
30 /* Miscellaneous Register */
33 /* Sequencer Registers */
34 0x03, 0x08, 0x03, 0x00, 0x02,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
40 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0F,
41 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
45 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
46 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x01, 0x0F, 0x13, 0x00
49 static BYTE VideoMode_80x25_text
[] =
51 /* Miscellaneous Register */
54 /* Sequencer Registers */
55 0x03, 0x00, 0x03, 0x00, 0x02,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
61 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0F,
62 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
66 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
67 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x01, 0x0F, 0x13, 0x00
70 static BYTE VideoMode_320x200_4color
[] =
72 /* Miscellaneous Register */
75 /* Sequencer Registers */
76 0x03, 0x09, 0x03, 0x00, 0x02,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x0F, 0xFF,
82 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
87 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
88 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x03, 0x00, 0x00
91 static BYTE VideoMode_640x480_16color
[] =
93 /* Miscellaneous Register */
96 /* Sequencer Registers */
97 0x03, 0x01, 0x08, 0x00, 0x06,
100 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0F, 0xFF,
103 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
108 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
109 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
112 static BYTE VideoMode_320x200_256color
[] =
114 /* Miscellaneous Register */
117 /* Sequencer Registers */
118 0x03, 0x01, 0x0F, 0x00, 0x0E,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
124 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
129 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
130 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
133 static LPBYTE VideoModes
[] =
135 VideoMode_40x25_text
, /* Mode 00h */
136 VideoMode_40x25_text
, /* Mode 01h */
137 VideoMode_80x25_text
, /* Mode 02h */
138 VideoMode_80x25_text
, /* Mode 03h */
139 VideoMode_320x200_4color
, /* Mode 04h */
140 VideoMode_320x200_4color
, /* Mode 05h */
153 VideoMode_640x480_16color
, /* Mode 12h */
154 VideoMode_320x200_256color
, /* Mode 13h */
157 /* PRIVATE FUNCTIONS **********************************************************/
159 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
161 /* Get the location of the element after the head */
162 WORD NextElement
= Bda
->KeybdBufferHead
+ 2;
164 /* Wrap it around if it's at or beyond the end */
165 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
167 /* If it's full, fail */
168 if (NextElement
== Bda
->KeybdBufferTail
) return FALSE
;
170 /* Put the value in the queue */
171 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
172 Bda
->KeybdBufferTail
+= sizeof(WORD
);
174 /* Check if we are at, or have passed, the end of the buffer */
175 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
177 /* Return it to the beginning */
178 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
185 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
187 /* If it's empty, fail */
188 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
190 /* Otherwise, get the value and return success */
191 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
196 static BOOLEAN
BiosKbdBufferPop(VOID
)
198 /* If it's empty, fail */
199 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
201 /* Remove the value from the queue */
202 Bda
->KeybdBufferHead
+= sizeof(WORD
);
204 /* Check if we are at, or have passed, the end of the buffer */
205 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
207 /* Return it to the beginning */
208 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
215 static VOID
BiosReadWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
220 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
222 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
224 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
226 /* Read from video memory */
227 VgaReadMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
231 /* Write the data to the buffer in row order */
232 Buffer
[Counter
++] = Character
;
237 static VOID
BiosWriteWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
242 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
244 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
246 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
248 Character
= Buffer
[Counter
++];
250 /* Read from video memory */
251 VgaWriteMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
258 /* PUBLIC FUNCTIONS ***********************************************************/
260 BYTE
BiosGetVideoMode(VOID
)
262 return Bda
->VideoMode
;
265 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
269 LPBYTE Values
= VideoModes
[ModeNumber
];
271 if (Values
== NULL
) return FALSE
;
273 /* Write the misc register */
274 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
276 /* Write the sequencer registers */
277 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
279 VgaWritePort(VGA_SEQ_INDEX
, i
);
280 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
283 /* Write the GC registers */
284 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
286 VgaWritePort(VGA_GC_INDEX
, i
);
287 VgaWritePort(VGA_GC_DATA
, *(Values
++));
290 /* Write the CRTC registers */
291 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
293 VgaWritePort(VGA_CRTC_INDEX
, i
);
294 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
297 /* Write the AC registers */
298 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
300 VgaWritePort(VGA_AC_INDEX
, i
);
301 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
304 /* Update the values in the BDA */
305 Bda
->VideoMode
= ModeNumber
;
307 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
308 Bda
->VideoPageOffset
= 0;
309 Bda
->CharacterHeight
= 16;
311 Resolution
= VgaGetDisplayResolution();
312 Bda
->ScreenColumns
= Resolution
.X
;
313 Bda
->ScreenRows
= Resolution
.Y
- 1;
318 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
320 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
322 /* Set the values in the BDA */
323 Bda
->VideoPage
= PageNumber
;
324 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
325 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
327 /* Set the start address in the CRTC */
328 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
329 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
330 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
331 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
336 BOOLEAN
BiosInitialize(VOID
)
340 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
341 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
343 /* Initialize the BDA */
344 Bda
= (PBIOS_DATA_AREA
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BDA_SEGMENT
, 0));
345 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
346 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
347 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
349 /* Generate ISR stubs and fill the IVT */
350 for (i
= 0; i
< 256; i
++)
352 IntVecTable
[i
* 2] = Offset
;
353 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
355 BiosCode
[Offset
++] = 0xFA; // cli
357 BiosCode
[Offset
++] = 0x6A; // push i
358 BiosCode
[Offset
++] = (BYTE
)i
;
360 BiosCode
[Offset
++] = LOBYTE(EMULATOR_BOP
); // BOP sequence
361 BiosCode
[Offset
++] = HIBYTE(EMULATOR_BOP
);
362 BiosCode
[Offset
++] = LOBYTE(EMULATOR_INT_BOP
);
363 BiosCode
[Offset
++] = HIBYTE(EMULATOR_INT_BOP
);
365 BiosCode
[Offset
++] = 0x83; // add sp, 2
366 BiosCode
[Offset
++] = 0xC4;
367 BiosCode
[Offset
++] = 0x02;
369 BiosCode
[Offset
++] = 0xCF; // iret
372 /* Get the input handle to the real console, and check for success */
373 BiosConsoleInput
= CreateFileW(L
"CONIN$",
374 GENERIC_READ
| GENERIC_WRITE
,
375 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
380 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
385 /* Get the output handle to the real console, and check for success */
386 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
387 GENERIC_READ
| GENERIC_WRITE
,
388 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
393 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
395 CloseHandle(BiosConsoleInput
);
399 /* Save the console screen buffer information */
400 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
402 CloseHandle(BiosConsoleOutput
);
403 CloseHandle(BiosConsoleInput
);
408 VgaInitialize(BiosConsoleOutput
);
410 /* Update the cursor position */
411 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
412 BiosSavedBufferInfo
.dwCursorPosition
.X
,
415 /* Set the console input mode */
416 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
418 /* Initialize the PIC */
419 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
420 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
422 /* Set the interrupt offsets */
423 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
424 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
426 /* Tell the master PIC there is a slave at IRQ 2 */
427 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
428 PicWriteData(PIC_SLAVE_DATA
, 2);
430 /* Make sure the PIC is in 8086 mode */
431 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
432 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
434 /* Clear the masks for both PICs */
435 PicWriteData(PIC_MASTER_DATA
, 0x00);
436 PicWriteData(PIC_SLAVE_DATA
, 0x00);
438 PitWriteCommand(0x34);
439 PitWriteData(0, 0x00);
440 PitWriteData(0, 0x00);
445 VOID
BiosCleanup(VOID
)
447 /* Restore the old screen buffer */
448 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
450 /* Restore the screen buffer size */
451 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
453 /* Close the console handles */
454 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
455 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
458 WORD
BiosPeekCharacter(VOID
)
462 /* Check if there is a key available */
463 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return 0xFFFF;
465 /* Get the key from the queue, but don't remove it */
466 BiosKbdBufferTop(&CharacterData
);
468 return CharacterData
;
471 WORD
BiosGetCharacter(VOID
)
474 INPUT_RECORD InputRecord
;
477 /* Check if there is a key available */
478 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
480 /* Get the key from the queue, and remove it */
481 BiosKbdBufferTop(&CharacterData
);
488 /* Wait for a console event */
489 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
491 /* Read the event, and make sure it's a keypress */
492 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
493 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
494 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
496 /* Save the scan code and end the loop */
497 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
498 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
504 return CharacterData
;
507 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
509 /* Make sure the selected video page is valid */
510 if (Page
>= BIOS_MAX_PAGES
) return;
512 /* Update the position in the BDA */
513 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
515 /* Check if this is the current video page */
516 if (Page
== Bda
->VideoPage
)
518 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
520 /* Modify the CRTC registers */
521 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
522 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
523 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
524 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
528 BOOLEAN
BiosScrollWindow(INT Direction
,
530 SMALL_RECT Rectangle
,
536 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
537 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
539 /* Allocate a buffer for the window */
540 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
542 WindowSize
* sizeof(WORD
));
543 if (WindowData
== NULL
) return FALSE
;
545 /* Read the window data */
546 BiosReadWindow(WindowData
, Rectangle
, Page
);
550 /* Fill the window */
551 for (i
= 0; i
< WindowSize
; i
++)
553 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
559 // TODO: Scroll the window!
562 /* Write back the window data */
563 BiosWriteWindow(WindowData
, Rectangle
, Page
);
565 /* Free the window buffer */
566 HeapFree(GetProcessHeap(), 0, WindowData
);
571 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
573 WORD CharData
= (Attribute
<< 8) | Character
;
576 /* Make sure the page exists */
577 if (Page
>= BIOS_MAX_PAGES
) return;
579 /* Get the cursor location */
580 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
581 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
583 /* Write the character */
584 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
585 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
589 /* Advance the cursor */
592 /* Check if it passed the end of the row */
593 if (Column
== Bda
->ScreenColumns
)
595 /* Return to the first column */
598 if (Row
== Bda
->ScreenRows
)
600 /* The screen must be scrolled */
601 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
603 BiosScrollWindow(SCROLL_DIRECTION_UP
,
612 /* Set the cursor position */
613 BiosSetCursorPosition(Row
, Column
, Page
);
616 VOID
BiosVideoService(LPWORD Stack
)
618 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
619 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
620 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
621 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
628 BiosSetVideoMode(LOBYTE(Eax
));
632 /* Set Text-Mode Cursor Shape */
636 Bda
->CursorStartLine
= HIBYTE(Ecx
);
637 Bda
->CursorEndLine
= LOBYTE(Ecx
);
639 /* Modify the CRTC registers */
640 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
641 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
642 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
643 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
648 /* Set Cursor Position */
651 BiosSetCursorPosition(HIBYTE(Edx
), LOBYTE(Edx
), HIBYTE(Ebx
));
655 /* Get Cursor Position */
658 /* Make sure the selected video page exists */
659 if (HIBYTE(Ebx
) >= BIOS_MAX_PAGES
) break;
661 /* Return the result */
662 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
663 EmulatorSetRegister(EMULATOR_REG_CX
,
664 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
665 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
670 /* Query Light Pen */
674 * On modern BIOSes, this function returns 0
675 * so that we can ignore the other registers.
677 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
681 /* Select Active Display Page */
684 /* Check if the page exists */
685 if (LOBYTE(Eax
) >= BIOS_MAX_PAGES
) break;
687 /* Check if this is the same page */
688 if (LOBYTE(Eax
) == Bda
->VideoPage
) break;
690 /* Change the video page */
691 BiosSetVideoPage(LOBYTE(Eax
));
696 /* Scroll Window Up/Down */
700 SMALL_RECT Rectangle
=
708 /* Call the internal function */
709 BiosScrollWindow((HIBYTE(Eax
)== 0x06)
710 ? SCROLL_DIRECTION_UP
: SCROLL_DIRECTION_DOWN
,
719 /* Read/Write Character From Cursor Position */
724 WORD CharacterData
= MAKEWORD(LOBYTE(Eax
), LOBYTE(Ebx
));
725 BYTE Page
= HIBYTE(Ebx
);
728 /* Check if the page exists */
729 if (Page
>= BIOS_MAX_PAGES
) break;
731 /* Find the offset of the character */
732 Offset
= Page
* Bda
->VideoPageSize
733 + (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
734 + LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
736 if (HIBYTE(Eax
) == 0x08)
738 /* Read from the video memory */
739 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
740 (LPVOID
)&CharacterData
,
743 /* Return the character in AX */
744 EmulatorSetRegister(EMULATOR_REG_AX
, CharacterData
);
748 /* Write to video memory */
749 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
750 (LPVOID
)&CharacterData
,
751 (HIBYTE(Ebx
) == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
757 /* Teletype Output */
760 BiosPrintCharacter(LOBYTE(Eax
), LOBYTE(Ebx
), HIBYTE(Ebx
));
764 /* Get Current Video Mode */
767 EmulatorSetRegister(EMULATOR_REG_AX
,
768 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
769 EmulatorSetRegister(EMULATOR_REG_BX
,
770 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
778 SMALL_RECT Rectangle
=
786 /* Call the internal function */
787 BiosScrollWindow(LOBYTE(Ebx
),
798 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
804 VOID
BiosKeyboardService(LPWORD Stack
)
806 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
812 /* Read the character (and wait if necessary) */
813 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
820 WORD Data
= BiosPeekCharacter();
824 /* There is a character, clear ZF and return it */
825 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
826 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
830 /* No character, set ZF */
831 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
839 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
845 VOID
BiosTimeService(LPWORD Stack
)
847 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
848 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
849 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
855 /* Set AL to 1 if midnight had passed, 0 otherwise */
857 if (Bda
->MidnightPassed
) Eax
|= 1;
859 /* Return the tick count in CX:DX */
860 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
861 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
862 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
864 /* Reset the midnight flag */
865 Bda
->MidnightPassed
= FALSE
;
872 /* Set the tick count to CX:DX */
873 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
875 /* Reset the midnight flag */
876 Bda
->MidnightPassed
= FALSE
;
883 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
889 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
891 /* Increase the system tick count */
895 VOID
BiosEquipmentService(LPWORD Stack
)
897 /* Return the equipment list */
898 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
901 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
908 /* Perform the system timer interrupt */
909 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
917 BYTE ScanCode
, VirtualKey
;
920 /* Check if there is a scancode available */
921 if (!(KeyboardReadStatus() & 1)) break;
923 /* Get the scan code and virtual key code */
924 ScanCode
= KeyboardReadData();
925 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
927 /* Check if this is a key press or release */
928 if (!(ScanCode
& (1 << 7)))
931 if (VirtualKey
== VK_NUMLOCK
932 || VirtualKey
== VK_CAPITAL
933 || VirtualKey
== VK_SCROLL
)
935 /* For toggle keys, toggle the lowest bit in the keyboard map */
936 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
939 /* Set the highest bit */
940 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
942 /* Find out which character this is */
943 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) > 0)
945 /* Push it onto the BIOS keyboard queue */
946 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
951 /* Key release, unset the highest bit */
952 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
959 /* Send End-of-Interrupt to the PIC */
960 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
961 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);