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 HANDLE ConsoleBuffers
[BIOS_MAX_PAGES
] = { NULL
};
30 static LPVOID ConsoleFramebuffers
[BIOS_MAX_PAGES
] = { NULL
};
31 static HANDLE ConsoleMutexes
[BIOS_MAX_PAGES
] = { NULL
};
32 static BOOLEAN VideoNeedsUpdate
= TRUE
;
33 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
34 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
35 static VIDEO_MODE VideoModes
[] =
37 /* Width | Height | Text | Colors | Gray | Pages | Segment */
38 { 40, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 00h */
39 { 40, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 01h */
40 { 80, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 02h */
41 { 80, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 03h */
42 { 320, 200, FALSE
, 4, FALSE
, 4, 0xB800}, /* Mode 04h */
43 { 320, 200, FALSE
, 4, TRUE
, 4, 0xB800}, /* Mode 05h */
44 { 640, 200, FALSE
, 2, FALSE
, 2, 0xB800}, /* Mode 06h */
45 { 80, 25, TRUE
, 3, FALSE
, 1, 0xB000}, /* Mode 07h */
46 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 08h - not used */
47 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 09h - not used */
48 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ah - not used */
49 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Bh - not used */
50 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ch - not used */
51 { 320, 200, FALSE
, 16, FALSE
, 8, 0xA000}, /* Mode 0Dh */
52 { 640, 200, FALSE
, 16, FALSE
, 4, 0xA000}, /* Mode 0Eh */
53 { 640, 350, FALSE
, 3, FALSE
, 2, 0xA000}, /* Mode 0Fh */
54 { 640, 350, FALSE
, 4, FALSE
, 2, 0xA000}, /* Mode 10h */
55 { 640, 480, FALSE
, 2, FALSE
, 1, 0xA000}, /* Mode 11h */
56 { 640, 480, FALSE
, 16, FALSE
, 1, 0xA000}, /* Mode 12h */
57 { 640, 480, FALSE
, 256, FALSE
, 1, 0xA000} /* Mode 13h */
60 /* PRIVATE FUNCTIONS **********************************************************/
62 static INT
BiosColorNumberToBits(DWORD Colors
)
66 /* Find the index of the highest-order bit */
67 for (i
= 31; i
>= 0; i
--) if (Colors
& (1 << i
)) break;
69 /* Special case for zero */
75 static DWORD
BiosGetVideoPageSize()
78 DWORD BufferSize
= VideoModes
[CurrentVideoMode
].Width
79 * VideoModes
[CurrentVideoMode
].Height
80 * BiosColorNumberToBits(VideoModes
[CurrentVideoMode
].Colors
)
83 for (i
= 0; i
< 32; i
++) if ((1 << i
) >= BufferSize
) break;
88 static BYTE
BiosVideoAddressToPage(ULONG Address
)
90 return (Address
- (VideoModes
[CurrentVideoMode
].Segment
<< 4))
91 / BiosGetVideoPageSize();
94 static COORD
BiosVideoAddressToCoord(ULONG Address
)
96 COORD Result
= {0, 0};
98 BYTE PageStart
= BiosVideoAddressToPage(Address
) * BiosGetVideoPageSize();
99 DWORD Offset
= Address
- (VideoModes
[CurrentVideoMode
].Segment
<< 4) - PageStart
;
101 if (VideoModes
[CurrentVideoMode
].Text
)
103 Result
.X
= (Offset
/ sizeof(WORD
)) % VideoModes
[CurrentVideoMode
].Width
;
104 Result
.Y
= (Offset
/ sizeof(WORD
)) / VideoModes
[CurrentVideoMode
].Width
;
108 BitsPerPixel
= BiosColorNumberToBits(VideoModes
[CurrentVideoMode
].Colors
);
110 Result
.X
= ((Offset
* 8) / BitsPerPixel
)
111 % VideoModes
[CurrentVideoMode
].Width
;
112 Result
.Y
= ((Offset
* 8) / BitsPerPixel
)
113 / VideoModes
[CurrentVideoMode
].Width
;
119 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
121 /* If it's full, fail */
122 if (!BiosKbdBufferEmpty
&& (BiosKbdBufferStart
== BiosKbdBufferEnd
))
127 /* Otherwise, add the value to the queue */
128 BiosKbdBuffer
[BiosKbdBufferEnd
] = Data
;
130 BiosKbdBufferEnd
%= BIOS_KBD_BUFFER_SIZE
;
131 BiosKbdBufferEmpty
= FALSE
;
137 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
139 /* If it's empty, fail */
140 if (BiosKbdBufferEmpty
) return FALSE
;
142 /* Otherwise, get the value and return success */
143 *Data
= BiosKbdBuffer
[BiosKbdBufferStart
];
147 static BOOLEAN
BiosKbdBufferPop()
149 /* If it's empty, fail */
150 if (BiosKbdBufferEmpty
) return FALSE
;
152 /* Otherwise, remove the value and return success */
153 BiosKbdBufferStart
++;
154 BiosKbdBufferStart
%= BIOS_KBD_BUFFER_SIZE
;
155 if (BiosKbdBufferStart
== BiosKbdBufferEnd
) BiosKbdBufferEmpty
= TRUE
;
160 /* PUBLIC FUNCTIONS ***********************************************************/
162 BYTE
BiosGetVideoMode()
164 return CurrentVideoMode
;
167 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
171 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
172 LPBITMAPINFO BitmapInfo
;
175 /* Make sure this is a valid video mode */
176 if (ModeNumber
> BIOS_MAX_VIDEO_MODE
) return FALSE
;
177 if (VideoModes
[ModeNumber
].Pages
== 0) return FALSE
;
179 /* Free the current buffers */
180 for (i
= 0; i
< VideoModes
[CurrentVideoMode
].Pages
; i
++)
182 if (ConsoleBuffers
[i
] != NULL
) CloseHandle(ConsoleBuffers
[i
]);
183 if (!VideoModes
[CurrentVideoMode
].Text
) CloseHandle(ConsoleMutexes
[i
]);
186 if (VideoModes
[ModeNumber
].Text
)
188 /* Page 0 is CONOUT$ */
189 ConsoleBuffers
[0] = CreateFile(TEXT("CONOUT$"),
190 GENERIC_READ
| GENERIC_WRITE
,
191 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
197 /* Set the current page to page 0 */
198 CurrentVideoPage
= 0;
200 /* Create console buffers for other pages */
201 for (i
= 1; i
< VideoModes
[ModeNumber
].Pages
; i
++)
203 ConsoleBuffers
[i
] = CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
204 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
206 CONSOLE_TEXTMODE_BUFFER
,
210 /* Set the size for the buffers */
211 for (i
= 0; i
< VideoModes
[ModeNumber
].Pages
; i
++)
213 Coord
.X
= VideoModes
[ModeNumber
].Width
;
214 Coord
.Y
= VideoModes
[ModeNumber
].Height
;
216 SetConsoleScreenBufferSize(ConsoleBuffers
[i
], Coord
);
221 /* Allocate a bitmap info structure */
222 BitmapInfo
= (LPBITMAPINFO
)HeapAlloc(GetProcessHeap(),
224 sizeof(BITMAPINFOHEADER
)
225 + VideoModes
[ModeNumber
].Colors
227 if (BitmapInfo
== NULL
) return FALSE
;
229 /* Fill the bitmap info header */
230 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
231 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
232 BitmapInfo
->bmiHeader
.biWidth
= VideoModes
[ModeNumber
].Width
;
233 BitmapInfo
->bmiHeader
.biHeight
= VideoModes
[ModeNumber
].Height
;
234 BitmapInfo
->bmiHeader
.biPlanes
= 1;
235 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
236 BitmapInfo
->bmiHeader
.biBitCount
= BiosColorNumberToBits(VideoModes
[ModeNumber
].Colors
);
238 /* Calculate the image size */
239 BitmapInfo
->bmiHeader
.biSizeImage
= BitmapInfo
->bmiHeader
.biWidth
240 * BitmapInfo
->bmiHeader
.biHeight
241 * (BitmapInfo
->bmiHeader
.biBitCount
>> 3);
243 /* Fill the palette data */
244 PaletteIndex
= (LPWORD
)((ULONG_PTR
)BitmapInfo
+ sizeof(BITMAPINFOHEADER
));
245 for (i
= 0; i
< VideoModes
[ModeNumber
].Colors
; i
++)
250 /* Create a console buffer for each page */
251 for (i
= 0; i
< VideoModes
[ModeNumber
].Pages
; i
++)
253 /* Fill the console graphics buffer info */
254 GraphicsBufferInfo
.dwBitMapInfoLength
= sizeof(BITMAPINFOHEADER
)
255 + VideoModes
[ModeNumber
].Colors
257 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
258 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
260 /* Create the buffer */
261 ConsoleBuffers
[i
] = CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
262 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
264 CONSOLE_GRAPHICS_BUFFER
,
265 &GraphicsBufferInfo
);
267 /* Save the framebuffer address and mutex */
268 ConsoleFramebuffers
[i
] = GraphicsBufferInfo
.lpBitMap
;
269 ConsoleMutexes
[i
] = GraphicsBufferInfo
.hMutex
;
272 /* Free the bitmap information */
273 HeapFree(GetProcessHeap(), 0, BitmapInfo
);
276 /* Set the active page console buffer */
277 SetConsoleActiveScreenBuffer(ConsoleBuffers
[CurrentVideoPage
]);
279 /* Update the mode number */
280 CurrentVideoMode
= ModeNumber
;
285 inline DWORD
BiosGetVideoMemoryStart()
287 return (VideoModes
[CurrentVideoMode
].Segment
<< 4);
290 inline VOID
BiosVerticalRefresh()
292 /* Ignore if we're in text mode */
293 if (VideoModes
[CurrentVideoMode
].Text
) return;
295 /* Ignore if there's nothing to update */
296 if (!VideoNeedsUpdate
) return;
298 /* Redraw the screen */
299 InvalidateConsoleDIBits(ConsoleBuffers
[CurrentVideoPage
],
302 /* Clear the update flag */
303 VideoNeedsUpdate
= FALSE
;
306 BOOLEAN
BiosInitialize()
310 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
311 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
313 /* Generate ISR stubs and fill the IVT */
314 for (i
= 0; i
< 256; i
++)
316 IntVecTable
[i
* 2] = Offset
;
317 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
319 if (i
!= SPECIAL_INT_NUM
)
321 BiosCode
[Offset
++] = 0xFA; // cli
323 BiosCode
[Offset
++] = 0x6A; // push i
324 BiosCode
[Offset
++] = (BYTE
)i
;
326 BiosCode
[Offset
++] = 0xCD; // int SPECIAL_INT_NUM
327 BiosCode
[Offset
++] = SPECIAL_INT_NUM
;
329 BiosCode
[Offset
++] = 0x83; // add sp, 2
330 BiosCode
[Offset
++] = 0xC4;
331 BiosCode
[Offset
++] = 0x02;
334 BiosCode
[Offset
++] = 0xCF; // iret
337 /* Get the input and output handles to the real console */
338 BiosConsoleInput
= CreateFile(TEXT("CONIN$"),
339 GENERIC_READ
| GENERIC_WRITE
,
340 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
346 BiosConsoleOutput
= CreateFile(TEXT("CONOUT$"),
347 GENERIC_READ
| GENERIC_WRITE
,
348 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
354 /* Make sure it was successful */
355 if ((BiosConsoleInput
== INVALID_HANDLE_VALUE
)
356 || (BiosConsoleOutput
== INVALID_HANDLE_VALUE
))
361 /* Save the console screen buffer information */
362 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
367 /* Set the default video mode */
368 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
370 /* Set the console input mode */
371 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
373 /* Initialize the PIC */
374 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
375 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
377 /* Set the interrupt offsets */
378 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
379 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
381 /* Tell the master PIC there is a slave at IRQ 2 */
382 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
383 PicWriteData(PIC_SLAVE_DATA
, 2);
385 /* Make sure the PIC is in 8086 mode */
386 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
387 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
389 /* Clear the masks for both PICs */
390 PicWriteData(PIC_MASTER_DATA
, 0x00);
391 PicWriteData(PIC_SLAVE_DATA
, 0x00);
393 PitWriteCommand(0x34);
394 PitWriteData(0, 0x00);
395 PitWriteData(0, 0x00);
404 /* Restore the old screen buffer */
405 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
407 /* Restore the screen buffer size */
408 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
410 /* Free the buffers */
411 for (i
= 0; i
< VideoModes
[CurrentVideoMode
].Pages
; i
++)
413 if (ConsoleBuffers
[i
] != NULL
) CloseHandle(ConsoleBuffers
[i
]);
414 if (!VideoModes
[CurrentVideoMode
].Text
) CloseHandle(ConsoleMutexes
[i
]);
417 /* Close the console handles */
418 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
419 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
422 VOID
BiosUpdateConsole(ULONG StartAddress
, ULONG EndAddress
)
427 COORD Origin
= { 0, 0 };
428 COORD UnitSize
= { 1, 1 };
432 /* Start from the character address */
435 if (VideoModes
[CurrentVideoMode
].Text
)
437 /* Loop through all the addresses */
438 for (i
= StartAddress
; i
< EndAddress
; i
+= 2)
440 /* Get the coordinates */
441 Coordinates
= BiosVideoAddressToCoord(i
);
443 /* Get the page number */
444 Page
= BiosVideoAddressToPage(i
);
446 /* Make sure the page is valid */
447 if (Page
>= VideoModes
[CurrentVideoMode
].Pages
) continue;
449 /* Fill the rectangle structure */
450 Rect
.Left
= Coordinates
.X
;
451 Rect
.Top
= Coordinates
.Y
;
452 Rect
.Right
= Rect
.Left
;
453 Rect
.Bottom
= Rect
.Top
;
455 /* Fill the character data */
456 Character
.Char
.AsciiChar
= *((PCHAR
)((ULONG_PTR
)BaseAddress
+ i
));
457 Character
.Attributes
= *((PBYTE
)((ULONG_PTR
)BaseAddress
+ i
+ 1));
459 /* Write the character */
460 WriteConsoleOutputA(ConsoleBuffers
[Page
],
469 /* Wait for the mutex object */
470 WaitForSingleObject(ConsoleMutexes
[CurrentVideoPage
], INFINITE
);
472 /* Copy the data to the framebuffer */
473 RtlCopyMemory((LPVOID
)((ULONG_PTR
)ConsoleFramebuffers
[CurrentVideoPage
]
474 + StartAddress
- BiosGetVideoMemoryStart()),
475 (LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
476 EndAddress
- StartAddress
);
478 /* Release the mutex */
479 ReleaseMutex(ConsoleMutexes
[CurrentVideoPage
]);
481 /* Check if this is the first time the rectangle is updated */
482 if (!VideoNeedsUpdate
)
484 UpdateRectangle
.Left
= UpdateRectangle
.Top
= (SHORT
)0x7FFF;
485 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= (SHORT
)0x8000;
488 /* Expand the update rectangle */
489 for (i
= StartAddress
; i
< EndAddress
; i
++)
491 /* Get the coordinates */
492 Coordinates
= BiosVideoAddressToCoord(i
);
494 /* Expand the rectangle to include the point */
495 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Coordinates
.X
);
496 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Coordinates
.X
);
497 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Coordinates
.Y
);
498 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Coordinates
.Y
);
501 /* Set the update flag */
502 VideoNeedsUpdate
= TRUE
;
506 VOID
BiosUpdateVideoMemory(ULONG StartAddress
, ULONG EndAddress
)
514 if (VideoModes
[CurrentVideoMode
].Text
)
516 /* Loop through all the addresses */
517 for (i
= StartAddress
; i
< EndAddress
; i
++)
519 /* Get the coordinates */
520 Coordinates
= BiosVideoAddressToCoord(i
);
522 /* Get the page number */
523 Page
= BiosVideoAddressToPage(i
);
525 /* Make sure the page is valid */
526 if (Page
>= VideoModes
[CurrentVideoMode
].Pages
) continue;
528 /* Check if this is a character byte or an attribute byte */
529 if ((i
- (VideoModes
[CurrentVideoMode
].Segment
<< 4)) % 2 == 0)
531 /* This is a regular character */
532 ReadConsoleOutputCharacterA(ConsoleBuffers
[Page
],
533 (LPSTR
)((ULONG_PTR
)BaseAddress
+ i
),
540 /* This is an attribute */
541 ReadConsoleOutputAttribute(ConsoleBuffers
[Page
],
547 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
) = LOBYTE(Attribute
);
553 /* Wait for the mutex object */
554 WaitForSingleObject(ConsoleMutexes
[CurrentVideoPage
], INFINITE
);
556 /* Copy the data to the emulator memory */
557 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
558 (LPVOID
)((ULONG_PTR
)ConsoleFramebuffers
[CurrentVideoPage
]
559 + StartAddress
- BiosGetVideoMemoryStart()),
560 EndAddress
- StartAddress
);
562 /* Release the mutex */
563 ReleaseMutex(ConsoleMutexes
[CurrentVideoPage
]);
567 WORD
BiosPeekCharacter()
571 /* Check if there is a key available */
572 if (BiosKbdBufferEmpty
) return 0xFFFF;
574 /* Get the key from the queue, but don't remove it */
575 BiosKbdBufferTop(&CharacterData
);
577 return CharacterData
;
580 WORD
BiosGetCharacter()
583 INPUT_RECORD InputRecord
;
586 /* Check if there is a key available */
587 if (!BiosKbdBufferEmpty
)
589 /* Get the key from the queue, and remove it */
590 BiosKbdBufferTop(&CharacterData
);
597 /* Wait for a console event */
598 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
600 /* Read the event, and make sure it's a keypress */
601 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
602 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
603 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
605 /* Save the scan code and end the loop */
606 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
607 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
613 return CharacterData
;
616 VOID
BiosVideoService()
619 BOOLEAN Invisible
= FALSE
;
621 CONSOLE_CURSOR_INFO CursorInfo
;
622 CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo
;
625 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
626 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
627 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
628 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
635 BiosSetVideoMode(LOBYTE(Eax
));
639 /* Set Text-Mode Cursor Shape */
642 /* Retrieve and validate the input */
643 Invisible
= ((HIBYTE(Ecx
) >> 5) & 0x03) ? TRUE
: FALSE
;
644 CursorHeight
= (HIBYTE(Ecx
) & 0x1F) - (LOBYTE(Ecx
) & 0x1F);
645 if (CursorHeight
< 1) CursorHeight
= 1;
646 if (CursorHeight
> 100) CursorHeight
= 100;
649 CursorInfo
.dwSize
= (CursorHeight
* 100) / CONSOLE_FONT_HEIGHT
;
650 CursorInfo
.bVisible
= !Invisible
;
651 SetConsoleCursorInfo(ConsoleBuffers
[CurrentVideoPage
], &CursorInfo
);
656 /* Set Cursor Position */
659 /* Make sure the selected video page exists */
660 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
662 Position
.X
= LOBYTE(Edx
);
663 Position
.Y
= HIBYTE(Edx
);
665 SetConsoleCursorPosition(ConsoleBuffers
[HIBYTE(Ebx
)], Position
);
669 /* Get Cursor Position */
674 /* Make sure the selected video page exists */
675 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
677 /* Retrieve the data */
678 GetConsoleCursorInfo(ConsoleBuffers
[HIBYTE(Ebx
)], &CursorInfo
);
679 GetConsoleScreenBufferInfo(ConsoleBuffers
[HIBYTE(Ebx
)],
682 /* Find the first line */
683 StartLine
= 32 - ((CursorInfo
.dwSize
* 32) / 100);
685 /* Return the result */
686 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
687 EmulatorSetRegister(EMULATOR_REG_CX
, (StartLine
<< 8) | 0x1F);
688 EmulatorSetRegister(EMULATOR_REG_DX
,
689 LOWORD(ScreenBufferInfo
.dwCursorPosition
.Y
) << 8
690 || LOWORD(ScreenBufferInfo
.dwCursorPosition
.X
));
694 /* Select Active Display Page */
697 /* Check if the page exists */
698 if (LOBYTE(Eax
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
700 /* Check if this is the same page */
701 if (LOBYTE(Eax
) == CurrentVideoPage
) break;
703 /* Change the video page */
704 CurrentVideoPage
= LOBYTE(Eax
);
706 /* Set the active page console buffer */
707 SetConsoleActiveScreenBuffer(ConsoleBuffers
[CurrentVideoPage
]);
709 /* Restore the cursor to (0, 0) */
710 Position
.X
= Position
.Y
= 0;
711 SetConsoleCursorPosition(BiosConsoleOutput
, Position
);
716 /* Scroll Up/Down Window */
720 Rect
.Top
= HIBYTE(Ecx
);
721 Rect
.Left
= LOBYTE(Ecx
);
722 Rect
.Bottom
= HIBYTE(Edx
);
723 Rect
.Right
= LOBYTE(Edx
);
724 Character
.Char
.UnicodeChar
= L
' ';
725 Character
.Attributes
= HIBYTE(Ebx
);
726 Position
.X
= Rect
.Left
;
727 if (HIBYTE(Eax
) == 0x06) Position
.Y
= Rect
.Top
- LOBYTE(Eax
);
728 else Position
.Y
= Rect
.Top
+ LOBYTE(Eax
);
730 ScrollConsoleScreenBuffer(ConsoleBuffers
[CurrentVideoPage
],
738 /* Read Character And Attribute At Cursor Position */
741 COORD BufferSize
= { 1, 1 }, Origin
= { 0, 0 };
743 /* Make sure the selected video page exists */
744 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
746 /* Get the cursor position */
747 GetConsoleScreenBufferInfo(ConsoleBuffers
[HIBYTE(Ebx
)],
750 /* Read at cursor position */
751 Rect
.Left
= ScreenBufferInfo
.dwCursorPosition
.X
;
752 Rect
.Top
= ScreenBufferInfo
.dwCursorPosition
.Y
;
754 /* Read the console output */
755 ReadConsoleOutput(ConsoleBuffers
[HIBYTE(Ebx
)],
761 /* Return the result */
762 EmulatorSetRegister(EMULATOR_REG_AX
,
763 (LOBYTE(Character
.Attributes
) << 8)
764 | Character
.Char
.AsciiChar
);
769 /* Write Character And Attribute At Cursor Position */
774 /* Make sure the selected video page exists */
775 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
777 /* Get the cursor position */
778 GetConsoleScreenBufferInfo(ConsoleBuffers
[HIBYTE(Ebx
)],
781 /* Write the attribute to the output */
782 FillConsoleOutputAttribute(ConsoleBuffers
[HIBYTE(Ebx
)],
785 ScreenBufferInfo
.dwCursorPosition
,
788 /* Write the character to the output */
789 FillConsoleOutputCharacterA(ConsoleBuffers
[HIBYTE(Ebx
)],
792 ScreenBufferInfo
.dwCursorPosition
,
798 /* Write Character Only At Cursor Position */
803 /* Make sure the selected video page exists */
804 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
806 /* Get the cursor position */
807 GetConsoleScreenBufferInfo(ConsoleBuffers
[HIBYTE(Ebx
)],
810 /* Write the character to the output */
811 FillConsoleOutputCharacterA(ConsoleBuffers
[HIBYTE(Ebx
)],
814 ScreenBufferInfo
.dwCursorPosition
,
820 /* Teletype Output */
823 CHAR Character
= LOBYTE(Eax
);
826 /* Make sure the page exists */
827 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
829 /* Set the attribute */
830 SetConsoleTextAttribute(ConsoleBuffers
[HIBYTE(Ebx
)], LOBYTE(Ebx
));
832 /* Write the character */
833 WriteConsoleA(ConsoleBuffers
[HIBYTE(Ebx
)],
842 /* Get Current Video Mode */
845 EmulatorSetRegister(EMULATOR_REG_AX
,
846 (VideoModes
[CurrentVideoMode
].Width
<< 8)
848 EmulatorSetRegister(EMULATOR_REG_BX
,
849 (CurrentVideoPage
<< 8) | LOBYTE(Ebx
));
856 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
862 VOID
BiosKeyboardService()
864 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
870 /* Read the character (and wait if necessary) */
871 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
878 WORD Data
= BiosPeekCharacter();
882 /* There is a character, clear ZF and return it */
883 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
884 EmulatorClearFlag(EMULATOR_FLAG_ZF
);
888 /* No character, set ZF */
889 EmulatorSetFlag(EMULATOR_FLAG_ZF
);
897 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
903 VOID
BiosTimeService()
905 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
906 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
907 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
913 /* Set AL to 1 if midnight had passed, 0 otherwise */
915 if (BiosPassedMidnight
) Eax
|= 1;
917 /* Return the tick count in CX:DX */
918 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
919 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(BiosTickCount
));
920 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(BiosTickCount
));
922 /* Reset the midnight flag */
923 BiosPassedMidnight
= FALSE
;
930 /* Set the tick count to CX:DX */
931 BiosTickCount
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
933 /* Reset the midnight flag */
934 BiosPassedMidnight
= FALSE
;
941 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
947 VOID
BiosSystemTimerInterrupt()
949 /* Increase the system tick count */
953 VOID
BiosEquipmentService()
955 /* Return the equipment list */
956 EmulatorSetRegister(EMULATOR_REG_AX
, BIOS_EQUIPMENT_LIST
);
959 VOID
BiosHandleIrq(BYTE IrqNumber
)
966 /* Perform the system timer interrupt */
967 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
975 BYTE ScanCode
, VirtualKey
;
978 /* Check if there is a scancode available */
979 if (!(KeyboardReadStatus() & 1)) break;
981 /* Get the scan code and virtual key code */
982 ScanCode
= KeyboardReadData();
983 VirtualKey
= MapVirtualKey(ScanCode
, MAPVK_VSC_TO_VK
);
985 /* Check if this is a key press or release */
986 if (!(ScanCode
& (1 << 7)))
989 if (VirtualKey
== VK_NUMLOCK
990 || VirtualKey
== VK_CAPITAL
991 || VirtualKey
== VK_SCROLL
)
993 /* For toggle keys, toggle the lowest bit in the keyboard map */
994 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
997 /* Set the highest bit */
998 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1000 /* Find out which character this is */
1001 ToAscii(ScanCode
, VirtualKey
, BiosKeyboardMap
, &Character
, 0);
1003 /* Push it onto the BIOS keyboard queue */
1004 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
1008 /* Key release, unset the highest bit */
1009 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1016 /* Send End-of-Interrupt to the PIC */
1017 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1018 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);