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
.X
,
412 BiosSavedBufferInfo
.dwCursorPosition
.Y
,
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 /* Select Active Display Page */
673 /* Check if the page exists */
674 if (LOBYTE(Eax
) >= BIOS_MAX_PAGES
) break;
676 /* Check if this is the same page */
677 if (LOBYTE(Eax
) == Bda
->VideoPage
) break;
679 /* Change the video page */
680 BiosSetVideoPage(LOBYTE(Eax
));
685 /* Scroll Window Up/Down */
689 SMALL_RECT Rectangle
=
697 /* Call the internal function */
698 BiosScrollWindow((HIBYTE(Eax
)== 0x06)
699 ? SCROLL_DIRECTION_UP
: SCROLL_DIRECTION_DOWN
,
708 /* Read/Write Character From Cursor Position */
713 WORD CharacterData
= MAKEWORD(LOBYTE(Eax
), LOBYTE(Ebx
));
714 BYTE Page
= HIBYTE(Ebx
);
717 /* Check if the page exists */
718 if (Page
>= BIOS_MAX_PAGES
) break;
720 /* Find the offset of the character */
721 Offset
= Page
* Bda
->VideoPageSize
722 + (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
723 + LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
725 if (HIBYTE(Eax
) == 0x08)
727 /* Read from the video memory */
728 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
729 (LPVOID
)&CharacterData
,
732 /* Return the character in AX */
733 EmulatorSetRegister(EMULATOR_REG_AX
, CharacterData
);
737 /* Write to video memory */
738 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
739 (LPVOID
)&CharacterData
,
740 (HIBYTE(Ebx
) == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
746 /* Teletype Output */
749 BiosPrintCharacter(LOBYTE(Eax
), LOBYTE(Ebx
), HIBYTE(Ebx
));
753 /* Get Current Video Mode */
756 EmulatorSetRegister(EMULATOR_REG_AX
,
757 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
758 EmulatorSetRegister(EMULATOR_REG_BX
,
759 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
767 SMALL_RECT Rectangle
=
775 /* Call the internal function */
776 BiosScrollWindow(LOBYTE(Ebx
),
787 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
793 VOID
BiosKeyboardService(LPWORD Stack
)
795 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
801 /* Read the character (and wait if necessary) */
802 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
809 WORD Data
= BiosPeekCharacter();
813 /* There is a character, clear ZF and return it */
814 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
815 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
819 /* No character, set ZF */
820 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
828 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
834 VOID
BiosTimeService(LPWORD Stack
)
836 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
837 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
838 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
844 /* Set AL to 1 if midnight had passed, 0 otherwise */
846 if (Bda
->MidnightPassed
) Eax
|= 1;
848 /* Return the tick count in CX:DX */
849 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
850 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
851 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
853 /* Reset the midnight flag */
854 Bda
->MidnightPassed
= FALSE
;
861 /* Set the tick count to CX:DX */
862 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
864 /* Reset the midnight flag */
865 Bda
->MidnightPassed
= FALSE
;
872 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
878 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
880 /* Increase the system tick count */
884 VOID
BiosEquipmentService(LPWORD Stack
)
886 /* Return the equipment list */
887 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
890 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
897 /* Perform the system timer interrupt */
898 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
906 BYTE ScanCode
, VirtualKey
;
909 /* Check if there is a scancode available */
910 if (!(KeyboardReadStatus() & 1)) break;
912 /* Get the scan code and virtual key code */
913 ScanCode
= KeyboardReadData();
914 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
916 /* Check if this is a key press or release */
917 if (!(ScanCode
& (1 << 7)))
920 if (VirtualKey
== VK_NUMLOCK
921 || VirtualKey
== VK_CAPITAL
922 || VirtualKey
== VK_SCROLL
)
924 /* For toggle keys, toggle the lowest bit in the keyboard map */
925 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
928 /* Set the highest bit */
929 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
931 /* Find out which character this is */
932 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) > 0)
934 /* Push it onto the BIOS keyboard queue */
935 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
940 /* Key release, unset the highest bit */
941 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
948 /* Send End-of-Interrupt to the PIC */
949 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
950 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);