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 *******************************************************************/
17 /* PRIVATE VARIABLES **********************************************************/
19 static BYTE BiosKeyboardMap
[256];
20 static WORD BiosKbdBuffer
[BIOS_KBD_BUFFER_SIZE
];
21 static UINT BiosKbdBufferStart
= 0, BiosKbdBufferEnd
= 0;
22 static BOOLEAN BiosKbdBufferEmpty
= TRUE
;
23 static DWORD BiosTickCount
= 0;
24 static BOOLEAN BiosPassedMidnight
= FALSE
;
25 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
26 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
27 static BYTE CurrentVideoMode
= BIOS_DEFAULT_VIDEO_MODE
;
28 static BYTE CurrentVideoPage
= 0;
29 static COORD BiosCursorPositions
[BIOS_MAX_PAGES
];
30 static HANDLE BiosGraphicsOutput
= NULL
;
31 static LPVOID ConsoleFramebuffer
= NULL
;
32 static HANDLE ConsoleMutex
= NULL
;
33 static BOOLEAN VideoNeedsUpdate
= TRUE
;
34 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
35 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
36 static VIDEO_MODE VideoModes
[] =
38 /* Width | Height | Text | Bpp | Gray | Pages | Segment */
39 { 40, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 00h */
40 { 40, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 01h */
41 { 80, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 02h */
42 { 80, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 03h */
43 { 320, 200, FALSE
, 2, FALSE
, 1, 0xB800}, /* Mode 04h */
44 { 320, 200, FALSE
, 2, TRUE
, 1, 0xB800}, /* Mode 05h */
45 { 640, 200, FALSE
, 1, FALSE
, 1, 0xB800}, /* Mode 06h */
46 { 80, 25, TRUE
, 8, FALSE
, 1, 0xB000}, /* Mode 07h */
47 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 08h - not used */
48 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 09h - not used */
49 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ah - not used */
50 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Bh - not used */
51 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ch - not used */
52 { 320, 200, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 0Dh */
53 { 640, 200, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 0Eh */
54 { 640, 350, FALSE
, 1, FALSE
, 1, 0xA000}, /* Mode 0Fh */
55 { 640, 350, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 10h */
56 { 640, 480, FALSE
, 1, FALSE
, 1, 0xA000}, /* Mode 11h */
57 { 640, 480, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 12h */
58 { 320, 200, FALSE
, 8, FALSE
, 1, 0xA000} /* Mode 13h */
61 /* PRIVATE FUNCTIONS **********************************************************/
63 static DWORD
BiosGetVideoPageSize()
66 DWORD BufferSize
= VideoModes
[CurrentVideoMode
].Width
67 * VideoModes
[CurrentVideoMode
].Height
68 * VideoModes
[CurrentVideoMode
].Bpp
71 for (i
= 0; i
< 32; i
++) if ((1 << i
) >= BufferSize
) break;
76 static BYTE
BiosVideoAddressToPage(ULONG Address
)
78 return (Address
- BiosGetVideoMemoryStart())
79 / BiosGetVideoPageSize();
82 static COORD
BiosVideoAddressToCoord(ULONG Address
)
84 COORD Result
= {0, 0};
85 DWORD PageStart
= BiosVideoAddressToPage(Address
) * BiosGetVideoPageSize();
86 DWORD Offset
= Address
- BiosGetVideoMemoryStart() - PageStart
;
88 if (VideoModes
[CurrentVideoMode
].Text
)
90 Result
.X
= (Offset
/ sizeof(WORD
)) % VideoModes
[CurrentVideoMode
].Width
;
91 Result
.Y
= (Offset
/ sizeof(WORD
)) / VideoModes
[CurrentVideoMode
].Width
;
95 Result
.X
= ((Offset
* 8) / VideoModes
[CurrentVideoMode
].Bpp
)
96 % VideoModes
[CurrentVideoMode
].Width
;
97 Result
.Y
= ((Offset
* 8) / VideoModes
[CurrentVideoMode
].Bpp
)
98 / VideoModes
[CurrentVideoMode
].Width
;
104 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
106 /* If it's full, fail */
107 if (!BiosKbdBufferEmpty
&& (BiosKbdBufferStart
== BiosKbdBufferEnd
))
112 /* Otherwise, add the value to the queue */
113 BiosKbdBuffer
[BiosKbdBufferEnd
] = Data
;
115 BiosKbdBufferEnd
%= BIOS_KBD_BUFFER_SIZE
;
116 BiosKbdBufferEmpty
= FALSE
;
122 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
124 /* If it's empty, fail */
125 if (BiosKbdBufferEmpty
) return FALSE
;
127 /* Otherwise, get the value and return success */
128 *Data
= BiosKbdBuffer
[BiosKbdBufferStart
];
132 static BOOLEAN
BiosKbdBufferPop()
134 /* If it's empty, fail */
135 if (BiosKbdBufferEmpty
) return FALSE
;
137 /* Otherwise, remove the value and return success */
138 BiosKbdBufferStart
++;
139 BiosKbdBufferStart
%= BIOS_KBD_BUFFER_SIZE
;
140 if (BiosKbdBufferStart
== BiosKbdBufferEnd
) BiosKbdBufferEmpty
= TRUE
;
145 static BOOLEAN
BiosCreateGraphicsBuffer(BYTE ModeNumber
)
148 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
149 LPBITMAPINFO BitmapInfo
;
152 /* Allocate a bitmap info structure */
153 BitmapInfo
= (LPBITMAPINFO
)HeapAlloc(GetProcessHeap(),
155 sizeof(BITMAPINFOHEADER
)
156 + (1 << VideoModes
[ModeNumber
].Bpp
)
158 if (BitmapInfo
== NULL
) return FALSE
;
160 /* Fill the bitmap info header */
161 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
162 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
163 BitmapInfo
->bmiHeader
.biWidth
= VideoModes
[ModeNumber
].Width
;
164 BitmapInfo
->bmiHeader
.biHeight
= VideoModes
[ModeNumber
].Height
;
165 BitmapInfo
->bmiHeader
.biPlanes
= 1;
166 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
167 BitmapInfo
->bmiHeader
.biBitCount
= VideoModes
[ModeNumber
].Bpp
;
169 /* Calculate the image size */
170 BitmapInfo
->bmiHeader
.biSizeImage
= BitmapInfo
->bmiHeader
.biWidth
171 * BitmapInfo
->bmiHeader
.biHeight
172 * (BitmapInfo
->bmiHeader
.biBitCount
>> 3);
174 /* Fill the palette data */
175 PaletteIndex
= (LPWORD
)((ULONG_PTR
)BitmapInfo
+ sizeof(BITMAPINFOHEADER
));
176 for (i
= 0; i
< (1 << VideoModes
[ModeNumber
].Bpp
); i
++)
181 /* Fill the console graphics buffer info */
182 GraphicsBufferInfo
.dwBitMapInfoLength
= sizeof(BITMAPINFOHEADER
)
183 + (1 << VideoModes
[ModeNumber
].Bpp
)
185 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
186 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
188 /* Create the buffer */
189 BiosGraphicsOutput
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
190 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
192 CONSOLE_GRAPHICS_BUFFER
,
193 &GraphicsBufferInfo
);
195 /* Save the framebuffer address and mutex */
196 ConsoleFramebuffer
= GraphicsBufferInfo
.lpBitMap
;
197 ConsoleMutex
= GraphicsBufferInfo
.hMutex
;
199 /* Free the bitmap information */
200 HeapFree(GetProcessHeap(), 0, BitmapInfo
);
205 static VOID
BiosDestroyGraphicsBuffer()
207 CloseHandle(ConsoleMutex
);
208 CloseHandle(BiosGraphicsOutput
);
211 /* PUBLIC FUNCTIONS ***********************************************************/
213 BYTE
BiosGetVideoMode()
215 return CurrentVideoMode
;
218 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
222 /* Make sure this is a valid video mode */
223 if (ModeNumber
> BIOS_MAX_VIDEO_MODE
) return FALSE
;
224 if (VideoModes
[ModeNumber
].Pages
== 0) return FALSE
;
226 /* Set the new video mode size */
227 Coord
.X
= VideoModes
[ModeNumber
].Width
;
228 Coord
.Y
= VideoModes
[ModeNumber
].Height
;
230 if (VideoModes
[ModeNumber
].Text
&& VideoModes
[CurrentVideoMode
].Text
)
232 /* Switching from text mode to another text mode */
234 /* Resize the text buffer */
235 SetConsoleScreenBufferSize(BiosConsoleOutput
, Coord
);
237 else if (VideoModes
[ModeNumber
].Text
&& !VideoModes
[CurrentVideoMode
].Text
)
239 /* Switching from graphics mode to text mode */
241 /* Resize the text buffer */
242 SetConsoleScreenBufferSize(BiosConsoleOutput
, Coord
);
244 /* Change the active screen buffer to the text buffer */
245 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
247 /* Cleanup the graphics buffer */
248 BiosDestroyGraphicsBuffer();
250 else if (!VideoModes
[ModeNumber
].Text
&& VideoModes
[CurrentVideoMode
].Text
)
252 /* Switching from text mode to graphics mode */
253 if (!BiosCreateGraphicsBuffer(ModeNumber
)) return FALSE
;
255 SetConsoleActiveScreenBuffer(BiosGraphicsOutput
);
257 else if (!VideoModes
[ModeNumber
].Text
&& !VideoModes
[CurrentVideoMode
].Text
)
259 /* Switching from graphics mode to another graphics mode */
261 /* Temporarily switch to the text mode buffer */
262 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
264 /* Cleanup the current graphics mode buffer */
265 BiosDestroyGraphicsBuffer();
267 /* Create a new graphics mode buffer */
268 if (!BiosCreateGraphicsBuffer(ModeNumber
)) return FALSE
;
271 SetConsoleActiveScreenBuffer(BiosGraphicsOutput
);
274 /* Set the video mode */
275 CurrentVideoMode
= ModeNumber
;
280 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
283 CONSOLE_SCREEN_BUFFER_INFO BufferInfo
;
285 /* Make sure this is a valid page number */
286 if (PageNumber
>= VideoModes
[CurrentVideoMode
].Pages
) return FALSE
;
288 /* Save the current console buffer in the video memory */
289 PageStart
= BiosGetVideoMemoryStart() + CurrentVideoPage
* BiosGetVideoPageSize();
290 BiosUpdateVideoMemory(PageStart
, PageStart
+ BiosGetVideoPageSize());
292 /* Save the cursor */
293 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BufferInfo
)) return FALSE
;
294 BiosCursorPositions
[CurrentVideoPage
] = BufferInfo
.dwCursorPosition
;
297 CurrentVideoPage
= PageNumber
;
299 /* Update the console */
300 PageStart
= BiosGetVideoMemoryStart() + CurrentVideoPage
* BiosGetVideoPageSize();
301 BiosUpdateConsole(PageStart
, PageStart
+ BiosGetVideoPageSize());
304 SetConsoleCursorPosition(BiosConsoleOutput
, BiosCursorPositions
[PageNumber
]);
309 inline DWORD
BiosGetVideoMemoryStart()
311 return (VideoModes
[CurrentVideoMode
].Segment
<< 4);
314 inline VOID
BiosVerticalRefresh()
316 /* Ignore if we're in text mode */
317 if (VideoModes
[CurrentVideoMode
].Text
) return;
319 /* Ignore if there's nothing to update */
320 if (!VideoNeedsUpdate
) return;
322 /* Redraw the screen */
323 InvalidateConsoleDIBits(BiosGraphicsOutput
, &UpdateRectangle
);
325 /* Clear the update flag */
326 VideoNeedsUpdate
= FALSE
;
329 BOOLEAN
BiosInitialize()
333 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
334 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
336 /* Generate ISR stubs and fill the IVT */
337 for (i
= 0; i
< 256; i
++)
339 IntVecTable
[i
* 2] = Offset
;
340 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
342 if (i
!= SPECIAL_INT_NUM
)
344 BiosCode
[Offset
++] = 0xFA; // cli
346 BiosCode
[Offset
++] = 0x6A; // push i
347 BiosCode
[Offset
++] = (BYTE
)i
;
349 BiosCode
[Offset
++] = 0xCD; // int SPECIAL_INT_NUM
350 BiosCode
[Offset
++] = SPECIAL_INT_NUM
;
352 BiosCode
[Offset
++] = 0x83; // add sp, 2
353 BiosCode
[Offset
++] = 0xC4;
354 BiosCode
[Offset
++] = 0x02;
357 BiosCode
[Offset
++] = 0xCF; // iret
360 /* Get the input and output handles to the real console */
361 BiosConsoleInput
= CreateFile(TEXT("CONIN$"),
362 GENERIC_READ
| GENERIC_WRITE
,
363 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
369 BiosConsoleOutput
= CreateFile(TEXT("CONOUT$"),
370 GENERIC_READ
| GENERIC_WRITE
,
371 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
377 /* Make sure it was successful */
378 if ((BiosConsoleInput
== INVALID_HANDLE_VALUE
)
379 || (BiosConsoleOutput
== INVALID_HANDLE_VALUE
))
384 /* Save the console screen buffer information */
385 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
390 /* Store the cursor position */
391 ZeroMemory(&BiosCursorPositions
, sizeof(BiosCursorPositions
));
392 BiosCursorPositions
[0] = BiosSavedBufferInfo
.dwCursorPosition
;
394 /* Set the default video mode */
395 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
397 /* Set the console input mode */
398 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
400 /* Initialize the PIC */
401 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
402 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
404 /* Set the interrupt offsets */
405 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
406 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
408 /* Tell the master PIC there is a slave at IRQ 2 */
409 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
410 PicWriteData(PIC_SLAVE_DATA
, 2);
412 /* Make sure the PIC is in 8086 mode */
413 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
414 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
416 /* Clear the masks for both PICs */
417 PicWriteData(PIC_MASTER_DATA
, 0x00);
418 PicWriteData(PIC_SLAVE_DATA
, 0x00);
420 PitWriteCommand(0x34);
421 PitWriteData(0, 0x00);
422 PitWriteData(0, 0x00);
429 /* Restore the old screen buffer */
430 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
432 /* Restore the screen buffer size */
433 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
435 /* Free the graphics buffer */
436 if (!VideoModes
[CurrentVideoMode
].Text
) BiosDestroyGraphicsBuffer();
438 /* Close the console handles */
439 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
440 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
443 VOID
BiosUpdateConsole(ULONG StartAddress
, ULONG EndAddress
)
447 COORD Origin
= { 0, 0 };
448 COORD UnitSize
= { 1, 1 };
452 /* Start from the character address */
455 if (VideoModes
[CurrentVideoMode
].Text
)
457 /* Loop through all the addresses */
458 for (i
= StartAddress
; i
< EndAddress
; i
+= 2)
460 /* Get the coordinates */
461 Coordinates
= BiosVideoAddressToCoord(i
);
463 /* Make sure this is the current page */
464 if (BiosVideoAddressToPage(i
) != CurrentVideoPage
) continue;
466 /* Fill the rectangle structure */
467 Rect
.Left
= Coordinates
.X
;
468 Rect
.Top
= Coordinates
.Y
;
469 Rect
.Right
= Rect
.Left
;
470 Rect
.Bottom
= Rect
.Top
;
472 /* Fill the character data */
473 Character
.Char
.AsciiChar
= *((PCHAR
)((ULONG_PTR
)BaseAddress
+ i
));
474 Character
.Attributes
= *((PBYTE
)((ULONG_PTR
)BaseAddress
+ i
+ 1));
476 /* Write the character */
477 WriteConsoleOutputA(BiosConsoleOutput
,
486 /* Wait for the mutex object */
487 WaitForSingleObject(ConsoleMutex
, INFINITE
);
489 /* Copy the data to the framebuffer */
490 RtlCopyMemory((LPVOID
)((ULONG_PTR
)ConsoleFramebuffer
491 + StartAddress
- BiosGetVideoMemoryStart()),
492 (LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
493 EndAddress
- StartAddress
);
495 /* Release the mutex */
496 ReleaseMutex(ConsoleMutex
);
498 /* Check if this is the first time the rectangle is updated */
499 if (!VideoNeedsUpdate
)
501 UpdateRectangle
.Left
= UpdateRectangle
.Top
= (SHORT
)0x7FFF;
502 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= (SHORT
)0x8000;
505 /* Expand the update rectangle */
506 for (i
= StartAddress
; i
< EndAddress
; i
++)
508 /* Get the coordinates */
509 Coordinates
= BiosVideoAddressToCoord(i
);
511 /* Expand the rectangle to include the point */
512 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Coordinates
.X
);
513 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Coordinates
.X
);
514 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Coordinates
.Y
);
515 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Coordinates
.Y
);
518 /* Set the update flag */
519 VideoNeedsUpdate
= TRUE
;
523 VOID
BiosUpdateVideoMemory(ULONG StartAddress
, ULONG EndAddress
)
530 if (VideoModes
[CurrentVideoMode
].Text
)
532 /* Loop through all the addresses */
533 for (i
= StartAddress
; i
< EndAddress
; i
++)
535 /* Get the coordinates */
536 Coordinates
= BiosVideoAddressToCoord(i
);
538 /* Make sure this is the current page */
539 if (BiosVideoAddressToPage(i
) != CurrentVideoPage
) continue;
541 /* Check if this is a character byte or an attribute byte */
542 if ((i
- BiosGetVideoMemoryStart()) % 2 == 0)
544 /* This is a regular character */
545 ReadConsoleOutputCharacterA(BiosConsoleOutput
,
546 (LPSTR
)((ULONG_PTR
)BaseAddress
+ i
),
553 /* This is an attribute */
554 ReadConsoleOutputAttribute(BiosConsoleOutput
,
560 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
) = LOBYTE(Attribute
);
566 /* Wait for the mutex object */
567 WaitForSingleObject(ConsoleMutex
, INFINITE
);
569 /* Copy the data to the emulator memory */
570 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
571 (LPVOID
)((ULONG_PTR
)ConsoleFramebuffer
572 + StartAddress
- BiosGetVideoMemoryStart()),
573 EndAddress
- StartAddress
);
575 /* Release the mutex */
576 ReleaseMutex(ConsoleMutex
);
580 WORD
BiosPeekCharacter()
584 /* Check if there is a key available */
585 if (BiosKbdBufferEmpty
) return 0xFFFF;
587 /* Get the key from the queue, but don't remove it */
588 BiosKbdBufferTop(&CharacterData
);
590 return CharacterData
;
593 WORD
BiosGetCharacter()
596 INPUT_RECORD InputRecord
;
599 /* Check if there is a key available */
600 if (!BiosKbdBufferEmpty
)
602 /* Get the key from the queue, and remove it */
603 BiosKbdBufferTop(&CharacterData
);
610 /* Wait for a console event */
611 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
613 /* Read the event, and make sure it's a keypress */
614 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
615 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
616 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
618 /* Save the scan code and end the loop */
619 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
620 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
626 return CharacterData
;
629 VOID
BiosVideoService()
632 BOOLEAN Invisible
= FALSE
;
634 CONSOLE_CURSOR_INFO CursorInfo
;
635 CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo
;
638 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
639 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
640 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
641 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
643 // TODO: Add support for multiple pages!
649 BiosSetVideoMode(LOBYTE(Eax
));
653 /* Set Text-Mode Cursor Shape */
656 /* Retrieve and validate the input */
657 Invisible
= ((HIBYTE(Ecx
) >> 5) & 0x03) ? TRUE
: FALSE
;
658 CursorHeight
= (HIBYTE(Ecx
) & 0x1F) - (LOBYTE(Ecx
) & 0x1F);
659 if (CursorHeight
< 1) CursorHeight
= 1;
660 if (CursorHeight
> 100) CursorHeight
= 100;
663 CursorInfo
.dwSize
= (CursorHeight
* 100) / CONSOLE_FONT_HEIGHT
;
664 CursorInfo
.bVisible
= !Invisible
;
665 SetConsoleCursorInfo(BiosConsoleOutput
, &CursorInfo
);
670 /* Set Cursor Position */
673 /* Make sure the selected video page exists */
674 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
676 Position
.X
= LOBYTE(Edx
);
677 Position
.Y
= HIBYTE(Edx
);
679 BiosCursorPositions
[HIBYTE(Ebx
)] = Position
;
681 /* Check if this is the current video page */
682 if (HIBYTE(Ebx
) == CurrentVideoPage
)
684 /* Yes, change the actual cursor */
685 SetConsoleCursorPosition(BiosConsoleOutput
, Position
);
691 /* Get Cursor Position */
696 /* Make sure the selected video page exists */
697 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
699 /* Retrieve the data */
700 GetConsoleCursorInfo(BiosConsoleOutput
, &CursorInfo
);
702 /* Find the first line */
703 StartLine
= 32 - ((CursorInfo
.dwSize
* 32) / 100);
705 /* Return the result */
706 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
707 EmulatorSetRegister(EMULATOR_REG_CX
, (StartLine
<< 8) | 0x1F);
708 EmulatorSetRegister(EMULATOR_REG_DX
,
709 (LOBYTE(BiosCursorPositions
[HIBYTE(Ebx
)].Y
) << 8)
710 | LOBYTE(BiosCursorPositions
[HIBYTE(Ebx
)].X
));
714 /* Select Active Display Page */
717 /* Check if the page exists */
718 if (LOBYTE(Eax
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
720 /* Check if this is the same page */
721 if (LOBYTE(Eax
) == CurrentVideoPage
) break;
723 /* Change the video page */
724 BiosSetVideoPage(LOBYTE(Eax
));
729 /* Scroll Up/Down Window */
733 Rect
.Top
= HIBYTE(Ecx
);
734 Rect
.Left
= LOBYTE(Ecx
);
735 Rect
.Bottom
= HIBYTE(Edx
);
736 Rect
.Right
= LOBYTE(Edx
);
737 Character
.Char
.UnicodeChar
= L
' ';
738 Character
.Attributes
= HIBYTE(Ebx
);
739 Position
.X
= Rect
.Left
;
741 if (HIBYTE(Eax
) == 0x06) Position
.Y
= Rect
.Top
- LOBYTE(Eax
);
742 else Position
.Y
= Rect
.Top
+ LOBYTE(Eax
);
744 ScrollConsoleScreenBuffer(BiosConsoleOutput
,
752 /* Read Character And Attribute At Cursor Position */
755 COORD BufferSize
= { 1, 1 }, Origin
= { 0, 0 };
757 /* Make sure the selected video page exists */
758 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
760 if (HIBYTE(Ebx
) == CurrentVideoPage
)
762 /* Get the cursor position */
763 GetConsoleScreenBufferInfo(BiosConsoleOutput
,
766 /* Read at cursor position */
767 Rect
.Left
= ScreenBufferInfo
.dwCursorPosition
.X
;
768 Rect
.Top
= ScreenBufferInfo
.dwCursorPosition
.Y
;
770 /* Read the console output */
771 ReadConsoleOutput(BiosConsoleOutput
,
777 /* Return the result */
778 EmulatorSetRegister(EMULATOR_REG_AX
,
779 (LOBYTE(Character
.Attributes
) << 8)
780 | Character
.Char
.AsciiChar
);
784 // TODO: NOT IMPLEMENTED
790 /* Write Character And Attribute At Cursor Position */
795 /* Make sure the selected video page exists */
796 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
798 if (HIBYTE(Ebx
) == CurrentVideoPage
)
800 /* Get the cursor position */
801 GetConsoleScreenBufferInfo(BiosConsoleOutput
,
804 /* Write the attribute to the output */
805 FillConsoleOutputAttribute(BiosConsoleOutput
,
808 ScreenBufferInfo
.dwCursorPosition
,
811 /* Write the character to the output */
812 FillConsoleOutputCharacterA(BiosConsoleOutput
,
815 ScreenBufferInfo
.dwCursorPosition
,
820 // TODO: NOT IMPLEMENTED
826 /* Write Character Only At Cursor Position */
831 /* Make sure the selected video page exists */
832 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
834 if (HIBYTE(Ebx
) == CurrentVideoPage
)
836 /* Get the cursor position */
837 GetConsoleScreenBufferInfo(BiosConsoleOutput
, &ScreenBufferInfo
);
839 /* Write the character to the output */
840 FillConsoleOutputCharacterA(BiosConsoleOutput
,
843 ScreenBufferInfo
.dwCursorPosition
,
848 // TODO: NOT IMPLEMENTED
854 /* Teletype Output */
857 CHAR Character
= LOBYTE(Eax
);
860 /* Make sure the page exists */
861 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
863 /* Set the attribute */
864 SetConsoleTextAttribute(BiosConsoleOutput
, LOBYTE(Ebx
));
866 /* Write the character */
867 WriteConsoleA(BiosConsoleOutput
,
876 /* Get Current Video Mode */
879 EmulatorSetRegister(EMULATOR_REG_AX
,
880 (VideoModes
[CurrentVideoMode
].Width
<< 8)
882 EmulatorSetRegister(EMULATOR_REG_BX
,
883 (CurrentVideoPage
<< 8) | LOBYTE(Ebx
));
890 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
896 VOID
BiosKeyboardService()
898 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
904 /* Read the character (and wait if necessary) */
905 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
912 WORD Data
= BiosPeekCharacter();
916 /* There is a character, clear ZF and return it */
917 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
918 EmulatorClearFlag(EMULATOR_FLAG_ZF
);
922 /* No character, set ZF */
923 EmulatorSetFlag(EMULATOR_FLAG_ZF
);
931 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
937 VOID
BiosTimeService()
939 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
940 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
941 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
947 /* Set AL to 1 if midnight had passed, 0 otherwise */
949 if (BiosPassedMidnight
) Eax
|= 1;
951 /* Return the tick count in CX:DX */
952 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
953 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(BiosTickCount
));
954 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(BiosTickCount
));
956 /* Reset the midnight flag */
957 BiosPassedMidnight
= FALSE
;
964 /* Set the tick count to CX:DX */
965 BiosTickCount
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
967 /* Reset the midnight flag */
968 BiosPassedMidnight
= FALSE
;
975 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
981 VOID
BiosSystemTimerInterrupt()
983 /* Increase the system tick count */
987 VOID
BiosEquipmentService()
989 /* Return the equipment list */
990 EmulatorSetRegister(EMULATOR_REG_AX
, BIOS_EQUIPMENT_LIST
);
993 VOID
BiosHandleIrq(BYTE IrqNumber
)
1000 /* Perform the system timer interrupt */
1001 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1009 BYTE ScanCode
, VirtualKey
;
1012 /* Check if there is a scancode available */
1013 if (!(KeyboardReadStatus() & 1)) break;
1015 /* Get the scan code and virtual key code */
1016 ScanCode
= KeyboardReadData();
1017 VirtualKey
= MapVirtualKey(ScanCode
, MAPVK_VSC_TO_VK
);
1019 /* Check if this is a key press or release */
1020 if (!(ScanCode
& (1 << 7)))
1023 if (VirtualKey
== VK_NUMLOCK
1024 || VirtualKey
== VK_CAPITAL
1025 || VirtualKey
== VK_SCROLL
)
1027 /* For toggle keys, toggle the lowest bit in the keyboard map */
1028 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1031 /* Set the highest bit */
1032 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1034 /* Find out which character this is */
1035 ToAscii(ScanCode
, VirtualKey
, BiosKeyboardMap
, &Character
, 0);
1037 /* Push it onto the BIOS keyboard queue */
1038 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
1042 /* Key release, unset the highest bit */
1043 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1050 /* Send End-of-Interrupt to the PIC */
1051 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1052 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);