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, 0x0E, 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, 0x0E, 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
);
407 /* Store the cursor position */
408 Bda
->CursorPosition
[0] = MAKEWORD(BiosSavedBufferInfo
.dwCursorPosition
.X
,
409 BiosSavedBufferInfo
.dwCursorPosition
.Y
);
411 VgaInitialize(BiosConsoleOutput
);
413 /* Set the console input mode */
414 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
416 /* Initialize the PIC */
417 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
418 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
420 /* Set the interrupt offsets */
421 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
422 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
424 /* Tell the master PIC there is a slave at IRQ 2 */
425 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
426 PicWriteData(PIC_SLAVE_DATA
, 2);
428 /* Make sure the PIC is in 8086 mode */
429 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
430 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
432 /* Clear the masks for both PICs */
433 PicWriteData(PIC_MASTER_DATA
, 0x00);
434 PicWriteData(PIC_SLAVE_DATA
, 0x00);
436 PitWriteCommand(0x34);
437 PitWriteData(0, 0x00);
438 PitWriteData(0, 0x00);
443 VOID
BiosCleanup(VOID
)
445 /* Restore the old screen buffer */
446 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
448 /* Restore the screen buffer size */
449 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
451 /* Close the console handles */
452 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
453 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
456 WORD
BiosPeekCharacter(VOID
)
460 /* Check if there is a key available */
461 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return 0xFFFF;
463 /* Get the key from the queue, but don't remove it */
464 BiosKbdBufferTop(&CharacterData
);
466 return CharacterData
;
469 WORD
BiosGetCharacter(VOID
)
472 INPUT_RECORD InputRecord
;
475 /* Check if there is a key available */
476 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
478 /* Get the key from the queue, and remove it */
479 BiosKbdBufferTop(&CharacterData
);
486 /* Wait for a console event */
487 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
489 /* Read the event, and make sure it's a keypress */
490 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
491 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
492 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
494 /* Save the scan code and end the loop */
495 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
496 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
502 return CharacterData
;
505 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
507 /* Make sure the selected video page is valid */
508 if (Page
>= BIOS_MAX_PAGES
) return;
510 /* Update the position in the BDA */
511 Bda
->CursorPosition
[Page
] = (Row
<< 8) | Column
;
513 /* Check if this is the current video page */
514 if (Page
== Bda
->VideoPage
)
516 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
518 /* Modify the CRTC registers */
519 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
520 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
521 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
522 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
526 BOOLEAN
BiosScrollWindow(INT Direction
,
528 SMALL_RECT Rectangle
,
534 DWORD WindowSize
= (Rectangle
.Bottom
- Rectangle
.Top
+ 1)
535 * (Rectangle
.Right
- Rectangle
.Left
+ 1);
537 /* Allocate a buffer for the window */
538 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
540 WindowSize
* sizeof(WORD
));
541 if (WindowData
== NULL
) return FALSE
;
543 /* Read the window data */
544 BiosReadWindow(WindowData
, Rectangle
, Page
);
548 /* Fill the window */
549 for (i
= 0; i
< WindowSize
; i
++)
551 WindowData
[i
] = ' ' | (FillAttribute
<< 8);
557 // TODO: Scroll the window!
560 /* Write back the window data */
561 BiosWriteWindow(WindowData
, Rectangle
, Page
);
563 /* Free the window buffer */
564 HeapFree(GetProcessHeap(), 0, WindowData
);
569 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
571 WORD CharData
= (Attribute
<< 8) | Character
;
574 /* Make sure the page exists */
575 if (Page
>= BIOS_MAX_PAGES
) return;
577 /* Get the cursor location */
578 Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
579 Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
581 /* Write the character */
582 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
,
583 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
587 /* Advance the cursor */
590 /* Check if it passed the end of the row */
591 if (Column
== Bda
->ScreenColumns
)
593 /* Return to the first column */
596 if (Row
== Bda
->ScreenRows
)
598 /* The screen must be scrolled */
599 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
601 BiosScrollWindow(SCROLL_DIRECTION_UP
,
610 /* Set the cursor position */
611 BiosSetCursorPosition(Row
, Column
, Page
);
614 VOID
BiosVideoService(LPWORD Stack
)
616 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
617 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
618 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
619 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
626 BiosSetVideoMode(LOBYTE(Eax
));
630 /* Set Text-Mode Cursor Shape */
634 Bda
->CursorStartLine
= HIBYTE(Ecx
);
635 Bda
->CursorEndLine
= LOBYTE(Ecx
);
637 /* Modify the CRTC registers */
638 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
639 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
640 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
641 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
646 /* Set Cursor Position */
649 BiosSetCursorPosition(HIBYTE(Edx
), LOBYTE(Edx
), HIBYTE(Ebx
));
653 /* Get Cursor Position */
656 /* Make sure the selected video page exists */
657 if (HIBYTE(Ebx
) >= BIOS_MAX_PAGES
) break;
659 /* Return the result */
660 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
661 EmulatorSetRegister(EMULATOR_REG_CX
,
662 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
663 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
668 /* Select Active Display Page */
671 /* Check if the page exists */
672 if (LOBYTE(Eax
) >= BIOS_MAX_PAGES
) break;
674 /* Check if this is the same page */
675 if (LOBYTE(Eax
) == Bda
->VideoPage
) break;
677 /* Change the video page */
678 BiosSetVideoPage(LOBYTE(Eax
));
683 /* Scroll Window Up/Down */
687 SMALL_RECT Rectangle
=
695 /* Call the internal function */
696 BiosScrollWindow((HIBYTE(Eax
)== 0x06)
697 ? SCROLL_DIRECTION_UP
: SCROLL_DIRECTION_DOWN
,
706 /* Read/Write Character From Cursor Position */
711 WORD CharacterData
= MAKEWORD(LOBYTE(Eax
), LOBYTE(Ebx
));
712 BYTE Page
= HIBYTE(Ebx
);
715 /* Check if the page exists */
716 if (Page
>= BIOS_MAX_PAGES
) break;
718 /* Find the offset of the character */
719 Offset
= Page
* Bda
->VideoPageSize
720 + (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
721 + LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
723 if (HIBYTE(Eax
) == 0x08)
725 /* Read from the video memory */
726 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
727 (LPVOID
)&CharacterData
,
730 /* Return the character in AX */
731 EmulatorSetRegister(EMULATOR_REG_AX
, CharacterData
);
735 /* Write to video memory */
736 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
737 (LPVOID
)&CharacterData
,
738 (HIBYTE(Ebx
) == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
744 /* Teletype Output */
747 BiosPrintCharacter(LOBYTE(Eax
), LOBYTE(Ebx
), HIBYTE(Ebx
));
751 /* Get Current Video Mode */
754 EmulatorSetRegister(EMULATOR_REG_AX
,
755 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
756 EmulatorSetRegister(EMULATOR_REG_BX
,
757 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
765 SMALL_RECT Rectangle
=
773 /* Call the internal function */
774 BiosScrollWindow(LOBYTE(Ebx
),
785 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
791 VOID
BiosKeyboardService(LPWORD Stack
)
793 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
799 /* Read the character (and wait if necessary) */
800 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
807 WORD Data
= BiosPeekCharacter();
811 /* There is a character, clear ZF and return it */
812 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
813 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
817 /* No character, set ZF */
818 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
826 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
832 VOID
BiosTimeService(LPWORD Stack
)
834 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
835 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
836 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
842 /* Set AL to 1 if midnight had passed, 0 otherwise */
844 if (Bda
->MidnightPassed
) Eax
|= 1;
846 /* Return the tick count in CX:DX */
847 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
848 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
849 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
851 /* Reset the midnight flag */
852 Bda
->MidnightPassed
= FALSE
;
859 /* Set the tick count to CX:DX */
860 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
862 /* Reset the midnight flag */
863 Bda
->MidnightPassed
= FALSE
;
870 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
876 VOID
BiosSystemTimerInterrupt(LPWORD Stack
)
878 /* Increase the system tick count */
882 VOID
BiosEquipmentService(LPWORD Stack
)
884 /* Return the equipment list */
885 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
888 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
895 /* Perform the system timer interrupt */
896 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
904 BYTE ScanCode
, VirtualKey
;
907 /* Check if there is a scancode available */
908 if (!(KeyboardReadStatus() & 1)) break;
910 /* Get the scan code and virtual key code */
911 ScanCode
= KeyboardReadData();
912 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
914 /* Check if this is a key press or release */
915 if (!(ScanCode
& (1 << 7)))
918 if (VirtualKey
== VK_NUMLOCK
919 || VirtualKey
== VK_CAPITAL
920 || VirtualKey
== VK_SCROLL
)
922 /* For toggle keys, toggle the lowest bit in the keyboard map */
923 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
926 /* Set the highest bit */
927 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
929 /* Find out which character this is */
930 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) > 0)
932 /* Push it onto the BIOS keyboard queue */
933 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
938 /* Key release, unset the highest bit */
939 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
946 /* Send End-of-Interrupt to the PIC */
947 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
948 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);