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 PBIOS_DATA_AREA Bda
;
20 static BYTE BiosKeyboardMap
[256];
21 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
22 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
23 static HANDLE BiosGraphicsOutput
= NULL
;
24 static LPVOID ConsoleFramebuffer
= NULL
;
25 static HANDLE ConsoleMutex
= NULL
;
26 static BYTE CurrentVideoMode
, CurrentVideoPage
;
27 static BOOLEAN VideoNeedsUpdate
= TRUE
;
28 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
29 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
31 static VIDEO_MODE VideoModes
[] =
33 /* Width | Height | Text | Bpp | Gray | Pages | Segment */
34 { 40, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 00h */
35 { 40, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 01h */
36 { 80, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 02h */
37 { 80, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 03h */
38 { 320, 200, FALSE
, 2, FALSE
, 1, 0xB800}, /* Mode 04h */
39 { 320, 200, FALSE
, 2, TRUE
, 1, 0xB800}, /* Mode 05h */
40 { 640, 200, FALSE
, 1, FALSE
, 1, 0xB800}, /* Mode 06h */
41 { 80, 25, TRUE
, 8, FALSE
, 1, 0xB000}, /* Mode 07h */
42 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 08h - not used */
43 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 09h - not used */
44 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ah - not used */
45 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Bh - not used */
46 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ch - not used */
47 { 320, 200, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 0Dh */
48 { 640, 200, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 0Eh */
49 { 640, 350, FALSE
, 1, FALSE
, 1, 0xA000}, /* Mode 0Fh */
50 { 640, 350, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 10h */
51 { 640, 480, FALSE
, 1, FALSE
, 1, 0xA000}, /* Mode 11h */
52 { 640, 480, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 12h */
53 { 320, 200, FALSE
, 8, FALSE
, 1, 0xA000} /* Mode 13h */
56 /* PRIVATE FUNCTIONS **********************************************************/
58 static DWORD
BiosGetVideoPageSize()
61 DWORD BufferSize
= VideoModes
[CurrentVideoMode
].Width
62 * VideoModes
[CurrentVideoMode
].Height
63 * VideoModes
[CurrentVideoMode
].Bpp
66 for (i
= 0; i
< 32; i
++) if ((1 << i
) >= BufferSize
) break;
71 static BYTE
BiosVideoAddressToPage(ULONG Address
)
73 return (Address
- BiosGetVideoMemoryStart())
74 / BiosGetVideoPageSize();
77 static COORD
BiosVideoAddressToCoord(ULONG Address
)
79 COORD Result
= {0, 0};
80 DWORD PageStart
= BiosVideoAddressToPage(Address
) * BiosGetVideoPageSize();
81 DWORD Offset
= Address
- BiosGetVideoMemoryStart() - PageStart
;
83 if (VideoModes
[CurrentVideoMode
].Text
)
85 Result
.X
= (Offset
/ sizeof(WORD
)) % VideoModes
[CurrentVideoMode
].Width
;
86 Result
.Y
= (Offset
/ sizeof(WORD
)) / VideoModes
[CurrentVideoMode
].Width
;
90 Result
.X
= ((Offset
* 8) / VideoModes
[CurrentVideoMode
].Bpp
)
91 % VideoModes
[CurrentVideoMode
].Width
;
92 Result
.Y
= ((Offset
* 8) / VideoModes
[CurrentVideoMode
].Bpp
)
93 / VideoModes
[CurrentVideoMode
].Width
;
99 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
101 /* Get the location of the element after the head */
102 WORD NextElement
= Bda
->KeybdBufferHead
+ 2;
104 /* Wrap it around if it's at or beyond the end */
105 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
107 /* If it's full, fail */
108 if (NextElement
== Bda
->KeybdBufferTail
) return FALSE
;
110 /* Put the value in the queue */
111 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
112 Bda
->KeybdBufferTail
+= sizeof(WORD
);
114 /* Check if we are at, or have passed, the end of the buffer */
115 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
117 /* Return it to the beginning */
118 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
125 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
127 /* If it's empty, fail */
128 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
130 /* Otherwise, get the value and return success */
131 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
136 static BOOLEAN
BiosKbdBufferPop()
138 /* If it's empty, fail */
139 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
141 /* Remove the value from the queue */
142 Bda
->KeybdBufferHead
+= sizeof(WORD
);
144 /* Check if we are at, or have passed, the end of the buffer */
145 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
147 /* Return it to the beginning */
148 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
155 static BOOLEAN
BiosCreateGraphicsBuffer(BYTE ModeNumber
)
158 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
159 LPBITMAPINFO BitmapInfo
;
162 /* Allocate a bitmap info structure */
163 BitmapInfo
= (LPBITMAPINFO
)HeapAlloc(GetProcessHeap(),
165 sizeof(BITMAPINFOHEADER
)
166 + (1 << VideoModes
[ModeNumber
].Bpp
)
168 if (BitmapInfo
== NULL
) return FALSE
;
170 /* Fill the bitmap info header */
171 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
172 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
173 BitmapInfo
->bmiHeader
.biWidth
= VideoModes
[ModeNumber
].Width
;
174 BitmapInfo
->bmiHeader
.biHeight
= VideoModes
[ModeNumber
].Height
;
175 BitmapInfo
->bmiHeader
.biPlanes
= 1;
176 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
177 BitmapInfo
->bmiHeader
.biBitCount
= VideoModes
[ModeNumber
].Bpp
;
179 /* Calculate the image size */
180 BitmapInfo
->bmiHeader
.biSizeImage
= BitmapInfo
->bmiHeader
.biWidth
181 * BitmapInfo
->bmiHeader
.biHeight
182 * (BitmapInfo
->bmiHeader
.biBitCount
>> 3);
184 /* Fill the palette data */
185 PaletteIndex
= (LPWORD
)((ULONG_PTR
)BitmapInfo
+ sizeof(BITMAPINFOHEADER
));
186 for (i
= 0; i
< (1 << VideoModes
[ModeNumber
].Bpp
); i
++)
191 /* Fill the console graphics buffer info */
192 GraphicsBufferInfo
.dwBitMapInfoLength
= sizeof(BITMAPINFOHEADER
)
193 + (1 << VideoModes
[ModeNumber
].Bpp
)
195 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
196 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
198 /* Create the buffer */
199 BiosGraphicsOutput
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
200 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
202 CONSOLE_GRAPHICS_BUFFER
,
203 &GraphicsBufferInfo
);
205 /* Save the framebuffer address and mutex */
206 ConsoleFramebuffer
= GraphicsBufferInfo
.lpBitMap
;
207 ConsoleMutex
= GraphicsBufferInfo
.hMutex
;
209 /* Free the bitmap information */
210 HeapFree(GetProcessHeap(), 0, BitmapInfo
);
215 static VOID
BiosDestroyGraphicsBuffer()
217 CloseHandle(ConsoleMutex
);
218 CloseHandle(BiosGraphicsOutput
);
221 /* PUBLIC FUNCTIONS ***********************************************************/
223 BYTE
BiosGetVideoMode()
225 return CurrentVideoMode
;
228 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
232 /* Make sure this is a valid video mode */
233 if (ModeNumber
> BIOS_MAX_VIDEO_MODE
) return FALSE
;
234 if (VideoModes
[ModeNumber
].Pages
== 0) return FALSE
;
236 /* Set the new video mode size */
237 Coord
.X
= VideoModes
[ModeNumber
].Width
;
238 Coord
.Y
= VideoModes
[ModeNumber
].Height
;
240 if (VideoModes
[ModeNumber
].Text
&& VideoModes
[CurrentVideoMode
].Text
)
242 /* Switching from text mode to another text mode */
244 /* Resize the text buffer */
245 SetConsoleScreenBufferSize(BiosConsoleOutput
, Coord
);
247 else if (VideoModes
[ModeNumber
].Text
&& !VideoModes
[CurrentVideoMode
].Text
)
249 /* Switching from graphics mode to text mode */
251 /* Resize the text buffer */
252 SetConsoleScreenBufferSize(BiosConsoleOutput
, Coord
);
254 /* Change the active screen buffer to the text buffer */
255 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
257 /* Cleanup the graphics buffer */
258 BiosDestroyGraphicsBuffer();
260 else if (!VideoModes
[ModeNumber
].Text
&& VideoModes
[CurrentVideoMode
].Text
)
262 /* Switching from text mode to graphics mode */
263 if (!BiosCreateGraphicsBuffer(ModeNumber
)) return FALSE
;
265 SetConsoleActiveScreenBuffer(BiosGraphicsOutput
);
267 else if (!VideoModes
[ModeNumber
].Text
&& !VideoModes
[CurrentVideoMode
].Text
)
269 /* Switching from graphics mode to another graphics mode */
271 /* Temporarily switch to the text mode buffer */
272 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
274 /* Cleanup the current graphics mode buffer */
275 BiosDestroyGraphicsBuffer();
277 /* Create a new graphics mode buffer */
278 if (!BiosCreateGraphicsBuffer(ModeNumber
)) return FALSE
;
281 SetConsoleActiveScreenBuffer(BiosGraphicsOutput
);
284 /* Change the mode number */
285 CurrentVideoMode
= ModeNumber
;
286 CurrentVideoPage
= 0;
289 Bda
->VideoMode
= CurrentVideoMode
;
290 Bda
->VideoPage
= CurrentVideoPage
;
291 Bda
->VideoPageSize
= BiosGetVideoPageSize();
292 Bda
->VideoPageOffset
= 0;
293 Bda
->ScreenColumns
= VideoModes
[ModeNumber
].Width
;
298 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
302 CONSOLE_SCREEN_BUFFER_INFO BufferInfo
;
304 /* Make sure this is a valid page number */
305 if (PageNumber
>= VideoModes
[CurrentVideoMode
].Pages
) return FALSE
;
307 /* Save the current console buffer in the video memory */
308 PageStart
= BiosGetVideoMemoryStart() + CurrentVideoPage
* BiosGetVideoPageSize();
309 BiosUpdateVideoMemory(PageStart
, PageStart
+ BiosGetVideoPageSize());
311 /* Save the cursor */
312 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BufferInfo
)) return FALSE
;
313 Bda
->CursorPosition
[CurrentVideoPage
] = MAKEWORD(BufferInfo
.dwCursorPosition
.X
,
314 BufferInfo
.dwCursorPosition
.Y
);
317 CurrentVideoPage
= PageNumber
;
320 Bda
->VideoPage
= CurrentVideoPage
;
321 Bda
->VideoPageSize
= BiosGetVideoPageSize();
322 Bda
->VideoPageOffset
= CurrentVideoPage
* Bda
->VideoPageSize
;
324 /* Update the console */
325 PageStart
= BiosGetVideoMemoryStart() + Bda
->VideoPage
* BiosGetVideoPageSize();
326 BiosUpdateConsole(PageStart
, PageStart
+ BiosGetVideoPageSize());
329 Coordinates
.X
= LOBYTE(Bda
->CursorPosition
[Bda
->VideoPage
]);
330 Coordinates
.Y
= HIBYTE(Bda
->CursorPosition
[Bda
->VideoPage
]);
331 SetConsoleCursorPosition(BiosConsoleOutput
, Coordinates
);
336 inline DWORD
BiosGetVideoMemoryStart()
338 return (VideoModes
[CurrentVideoMode
].Segment
<< 4);
341 inline VOID
BiosVerticalRefresh()
343 /* Ignore if we're in text mode */
344 if (VideoModes
[CurrentVideoMode
].Text
) return;
346 /* Ignore if there's nothing to update */
347 if (!VideoNeedsUpdate
) return;
349 /* Redraw the screen */
350 InvalidateConsoleDIBits(BiosGraphicsOutput
, &UpdateRectangle
);
352 /* Clear the update flag */
353 VideoNeedsUpdate
= FALSE
;
356 BOOLEAN
BiosInitialize()
360 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
361 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
363 /* Initialize the BDA */
364 Bda
= (PBIOS_DATA_AREA
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BDA_SEGMENT
, 0));
365 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
366 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
367 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
369 /* Generate ISR stubs and fill the IVT */
370 for (i
= 0; i
< 256; i
++)
372 IntVecTable
[i
* 2] = Offset
;
373 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
375 if (i
!= SPECIAL_INT_NUM
)
377 BiosCode
[Offset
++] = 0xFA; // cli
379 BiosCode
[Offset
++] = 0x6A; // push i
380 BiosCode
[Offset
++] = (BYTE
)i
;
382 BiosCode
[Offset
++] = 0xCD; // int SPECIAL_INT_NUM
383 BiosCode
[Offset
++] = SPECIAL_INT_NUM
;
385 BiosCode
[Offset
++] = 0x83; // add sp, 2
386 BiosCode
[Offset
++] = 0xC4;
387 BiosCode
[Offset
++] = 0x02;
390 BiosCode
[Offset
++] = 0xCF; // iret
393 /* Get the input and output handles to the real console */
394 BiosConsoleInput
= CreateFile(TEXT("CONIN$"),
395 GENERIC_READ
| GENERIC_WRITE
,
396 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
402 BiosConsoleOutput
= CreateFile(TEXT("CONOUT$"),
403 GENERIC_READ
| GENERIC_WRITE
,
404 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
410 /* Make sure it was successful */
411 if ((BiosConsoleInput
== INVALID_HANDLE_VALUE
)
412 || (BiosConsoleOutput
== INVALID_HANDLE_VALUE
))
417 /* Save the console screen buffer information */
418 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
423 /* Store the cursor position */
424 Bda
->CursorPosition
[0] = MAKEWORD(BiosSavedBufferInfo
.dwCursorPosition
.X
,
425 BiosSavedBufferInfo
.dwCursorPosition
.Y
);
427 /* Set the default video mode */
428 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
430 /* Set the console input mode */
431 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
433 /* Initialize the PIC */
434 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
435 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
437 /* Set the interrupt offsets */
438 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
439 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
441 /* Tell the master PIC there is a slave at IRQ 2 */
442 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
443 PicWriteData(PIC_SLAVE_DATA
, 2);
445 /* Make sure the PIC is in 8086 mode */
446 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
447 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
449 /* Clear the masks for both PICs */
450 PicWriteData(PIC_MASTER_DATA
, 0x00);
451 PicWriteData(PIC_SLAVE_DATA
, 0x00);
453 PitWriteCommand(0x34);
454 PitWriteData(0, 0x00);
455 PitWriteData(0, 0x00);
462 /* Restore the old screen buffer */
463 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
465 /* Restore the screen buffer size */
466 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
468 /* Free the graphics buffer */
469 if (!VideoModes
[CurrentVideoMode
].Text
) BiosDestroyGraphicsBuffer();
471 /* Close the console handles */
472 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
473 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
476 VOID
BiosUpdateConsole(ULONG StartAddress
, ULONG EndAddress
)
480 COORD Origin
= { 0, 0 };
481 COORD UnitSize
= { 1, 1 };
485 /* Start from the character address */
488 if (VideoModes
[CurrentVideoMode
].Text
)
490 /* Loop through all the addresses */
491 for (i
= StartAddress
; i
< EndAddress
; i
+= 2)
493 /* Get the coordinates */
494 Coordinates
= BiosVideoAddressToCoord(i
);
496 /* Make sure this is the current page */
497 if (BiosVideoAddressToPage(i
) != CurrentVideoPage
) continue;
499 /* Fill the rectangle structure */
500 Rect
.Left
= Coordinates
.X
;
501 Rect
.Top
= Coordinates
.Y
;
502 Rect
.Right
= Rect
.Left
;
503 Rect
.Bottom
= Rect
.Top
;
505 /* Fill the character data */
506 Character
.Char
.AsciiChar
= *((PCHAR
)((ULONG_PTR
)BaseAddress
+ i
));
507 Character
.Attributes
= *((PBYTE
)((ULONG_PTR
)BaseAddress
+ i
+ 1));
509 /* Write the character */
510 WriteConsoleOutputA(BiosConsoleOutput
,
519 /* Wait for the mutex object */
520 WaitForSingleObject(ConsoleMutex
, INFINITE
);
522 /* Copy the data to the framebuffer */
523 RtlCopyMemory((LPVOID
)((ULONG_PTR
)ConsoleFramebuffer
524 + StartAddress
- BiosGetVideoMemoryStart()),
525 (LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
526 EndAddress
- StartAddress
);
528 /* Release the mutex */
529 ReleaseMutex(ConsoleMutex
);
531 /* Check if this is the first time the rectangle is updated */
532 if (!VideoNeedsUpdate
)
534 UpdateRectangle
.Left
= UpdateRectangle
.Top
= (SHORT
)0x7FFF;
535 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= (SHORT
)0x8000;
538 /* Expand the update rectangle */
539 for (i
= StartAddress
; i
< EndAddress
; i
++)
541 /* Get the coordinates */
542 Coordinates
= BiosVideoAddressToCoord(i
);
544 /* Expand the rectangle to include the point */
545 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Coordinates
.X
);
546 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Coordinates
.X
);
547 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Coordinates
.Y
);
548 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Coordinates
.Y
);
551 /* Set the update flag */
552 VideoNeedsUpdate
= TRUE
;
556 VOID
BiosUpdateVideoMemory(ULONG StartAddress
, ULONG EndAddress
)
563 if (VideoModes
[CurrentVideoMode
].Text
)
565 /* Loop through all the addresses */
566 for (i
= StartAddress
; i
< EndAddress
; i
++)
568 /* Get the coordinates */
569 Coordinates
= BiosVideoAddressToCoord(i
);
571 /* Make sure this is the current page */
572 if (BiosVideoAddressToPage(i
) != CurrentVideoPage
) continue;
574 /* Check if this is a character byte or an attribute byte */
575 if ((i
- BiosGetVideoMemoryStart()) % 2 == 0)
577 /* This is a regular character */
578 ReadConsoleOutputCharacterA(BiosConsoleOutput
,
579 (LPSTR
)((ULONG_PTR
)BaseAddress
+ i
),
586 /* This is an attribute */
587 ReadConsoleOutputAttribute(BiosConsoleOutput
,
593 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
) = LOBYTE(Attribute
);
599 /* Wait for the mutex object */
600 WaitForSingleObject(ConsoleMutex
, INFINITE
);
602 /* Copy the data to the emulator memory */
603 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
604 (LPVOID
)((ULONG_PTR
)ConsoleFramebuffer
605 + StartAddress
- BiosGetVideoMemoryStart()),
606 EndAddress
- StartAddress
);
608 /* Release the mutex */
609 ReleaseMutex(ConsoleMutex
);
613 WORD
BiosPeekCharacter()
617 /* Check if there is a key available */
618 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return 0xFFFF;
620 /* Get the key from the queue, but don't remove it */
621 BiosKbdBufferTop(&CharacterData
);
623 return CharacterData
;
626 WORD
BiosGetCharacter()
629 INPUT_RECORD InputRecord
;
632 /* Check if there is a key available */
633 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
635 /* Get the key from the queue, and remove it */
636 BiosKbdBufferTop(&CharacterData
);
643 /* Wait for a console event */
644 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
646 /* Read the event, and make sure it's a keypress */
647 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
648 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
649 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
651 /* Save the scan code and end the loop */
652 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
653 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
659 return CharacterData
;
662 VOID
BiosVideoService()
665 BOOLEAN Invisible
= FALSE
;
667 CONSOLE_CURSOR_INFO CursorInfo
;
670 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
671 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
672 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
673 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
680 BiosSetVideoMode(LOBYTE(Eax
));
684 /* Set Text-Mode Cursor Shape */
687 /* Retrieve and validate the input */
688 Invisible
= ((HIBYTE(Ecx
) >> 5) & 0x03) ? TRUE
: FALSE
;
689 CursorHeight
= (HIBYTE(Ecx
) & 0x1F) - (LOBYTE(Ecx
) & 0x1F);
690 if (CursorHeight
< 1) CursorHeight
= 1;
691 if (CursorHeight
> 100) CursorHeight
= 100;
694 Bda
->CursorStartLine
= HIBYTE(Ecx
);
695 Bda
->CursorEndLine
= LOBYTE(Ecx
) & 0x1F;
698 CursorInfo
.dwSize
= (CursorHeight
* 100) / CONSOLE_FONT_HEIGHT
;
699 CursorInfo
.bVisible
= !Invisible
;
700 SetConsoleCursorInfo(BiosConsoleOutput
, &CursorInfo
);
705 /* Set Cursor Position */
708 /* Make sure the selected video page exists */
709 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
711 Bda
->CursorPosition
[HIBYTE(Ebx
)] = LOWORD(Edx
);
713 /* Check if this is the current video page */
714 if (HIBYTE(Ebx
) == CurrentVideoPage
)
716 /* Yes, change the actual cursor */
717 Position
.X
= LOBYTE(Edx
);
718 Position
.Y
= HIBYTE(Edx
);
719 SetConsoleCursorPosition(BiosConsoleOutput
, Position
);
725 /* Get Cursor Position */
728 /* Make sure the selected video page exists */
729 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
731 /* Return the result */
732 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
733 EmulatorSetRegister(EMULATOR_REG_CX
,
734 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
735 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
740 /* Select Active Display Page */
743 /* Check if the page exists */
744 if (LOBYTE(Eax
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
746 /* Check if this is the same page */
747 if (LOBYTE(Eax
) == CurrentVideoPage
) break;
749 /* Change the video page */
750 BiosSetVideoPage(LOBYTE(Eax
));
755 /* Scroll Up/Down Window */
756 // TODO: Implement for different pages
760 BYTE Lines
= LOBYTE(Eax
);
762 Rect
.Top
= HIBYTE(Ecx
);
763 Rect
.Left
= LOBYTE(Ecx
);
764 Rect
.Bottom
= HIBYTE(Edx
);
765 Rect
.Right
= LOBYTE(Edx
);
766 Character
.Char
.UnicodeChar
= L
' ';
767 Character
.Attributes
= HIBYTE(Ebx
);
768 Position
.X
= Rect
.Left
;
770 /* 0 means clear entire window */
771 if (Lines
== 0) Lines
= Rect
.Bottom
- Rect
.Top
;
773 if (HIBYTE(Eax
) == 0x06) Position
.Y
= Rect
.Top
- Lines
;
774 else Position
.Y
= Rect
.Top
+ Lines
;
776 ScrollConsoleScreenBuffer(BiosConsoleOutput
,
785 /* Read Character And Attribute At Cursor Position */
790 /* Make sure this is text mode */
791 if (!VideoModes
[CurrentVideoMode
].Text
) break;
793 /* Make sure the selected video page exists */
794 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
796 /* Find the address */
797 Address
= BiosGetVideoMemoryStart()
798 + HIBYTE(Ebx
) * BiosGetVideoPageSize()
799 + (HIBYTE(Bda
->CursorPosition
[HIBYTE(Ebx
)])
800 * VideoModes
[CurrentVideoMode
].Height
801 + LOBYTE(Bda
->CursorPosition
[HIBYTE(Ebx
)]))
802 * VideoModes
[CurrentVideoMode
].Bpp
/ 8;
804 /* Update the video memory at that address */
805 BiosUpdateVideoMemory(Address
,
806 Address
+ VideoModes
[CurrentVideoMode
].Bpp
/ 8);
808 /* Return the result in AX */
809 EmulatorSetRegister(EMULATOR_REG_AX
,
810 *((LPWORD
)((ULONG_PTR
)BaseAddress
+ Address
)));
815 /* Write Character And Attribute At Cursor Position */
819 BYTE PixelSize
= VideoModes
[CurrentVideoMode
].Bpp
/ 8;
820 WORD Data
= (LOBYTE(Ebx
) << 8) | LOBYTE(Eax
);
821 WORD Repeat
= LOWORD(Ecx
);
822 DWORD Address
= BiosGetVideoMemoryStart()
823 + CurrentVideoPage
* BiosGetVideoPageSize()
824 + (HIBYTE(Bda
->CursorPosition
[CurrentVideoPage
])
825 * VideoModes
[CurrentVideoMode
].Height
826 + LOBYTE(Bda
->CursorPosition
[CurrentVideoPage
]))
829 /* Make sure this is text mode */
830 if (!VideoModes
[CurrentVideoMode
].Text
) break;
832 /* Make sure the selected video page exists */
833 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
835 /* Make sure we don't write over the end of video memory */
837 (CONSOLE_VIDEO_MEM_END
- Address
)
840 /* Copy the values to the memory */
841 for (i
= 0; i
< Repeat
; i
++)
843 if (PixelSize
== sizeof(BYTE
) || HIBYTE(Eax
) == 0x0A)
845 /* Just characters, no attributes */
846 *((LPBYTE
)((ULONG_PTR
)BaseAddress
+ Address
) + i
* PixelSize
) = LOBYTE(Data
);
848 else if (PixelSize
== sizeof(WORD
))
850 /* First byte for characters, second for attributes */
851 *((LPWORD
)((ULONG_PTR
)BaseAddress
+ Address
) + i
) = Data
;
855 /* Update the range */
856 BiosUpdateConsole(Address
,
857 Address
+ Repeat
* (VideoModes
[CurrentVideoMode
].Bpp
/ 8));
862 /* Teletype Output */
865 CHAR Character
= LOBYTE(Eax
);
868 /* Make sure the page exists */
869 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
871 /* Set the attribute */
872 SetConsoleTextAttribute(BiosConsoleOutput
, LOBYTE(Ebx
));
874 /* Write the character */
875 WriteConsoleA(BiosConsoleOutput
,
884 /* Get Current Video Mode */
887 EmulatorSetRegister(EMULATOR_REG_AX
,
888 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
889 EmulatorSetRegister(EMULATOR_REG_BX
,
890 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
898 Rect
.Top
= HIBYTE(Ecx
);
899 Rect
.Left
= LOBYTE(Ecx
);
900 Rect
.Bottom
= HIBYTE(Edx
);
901 Rect
.Right
= LOBYTE(Edx
);
902 Character
.Char
.UnicodeChar
= L
' ';
903 Character
.Attributes
= 0x07;
904 Position
.X
= Rect
.Left
;
905 Position
.Y
= Rect
.Top
;
907 if (LOBYTE(Ebx
) == 0) Position
.Y
-= LOBYTE(Eax
);
908 else if (LOBYTE(Ebx
) == 1) Position
.Y
+= LOBYTE(Eax
);
909 else if (LOBYTE(Ebx
) == 2) Position
.X
-= LOBYTE(Eax
);
910 else if (LOBYTE(Ebx
) == 3) Position
.X
+= LOBYTE(Eax
);
912 ScrollConsoleScreenBuffer(BiosConsoleOutput
,
923 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
929 VOID
BiosKeyboardService()
931 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
937 /* Read the character (and wait if necessary) */
938 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
945 WORD Data
= BiosPeekCharacter();
949 /* There is a character, clear ZF and return it */
950 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
951 EmulatorClearFlag(EMULATOR_FLAG_ZF
);
955 /* No character, set ZF */
956 EmulatorSetFlag(EMULATOR_FLAG_ZF
);
964 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
970 VOID
BiosTimeService()
972 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
973 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
974 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
980 /* Set AL to 1 if midnight had passed, 0 otherwise */
982 if (Bda
->MidnightPassed
) Eax
|= 1;
984 /* Return the tick count in CX:DX */
985 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
986 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
987 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
989 /* Reset the midnight flag */
990 Bda
->MidnightPassed
= FALSE
;
997 /* Set the tick count to CX:DX */
998 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
1000 /* Reset the midnight flag */
1001 Bda
->MidnightPassed
= FALSE
;
1008 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1014 VOID
BiosSystemTimerInterrupt()
1016 /* Increase the system tick count */
1020 VOID
BiosEquipmentService()
1022 /* Return the equipment list */
1023 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
1026 VOID
BiosHandleIrq(BYTE IrqNumber
)
1033 /* Perform the system timer interrupt */
1034 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1042 BYTE ScanCode
, VirtualKey
;
1045 /* Check if there is a scancode available */
1046 if (!(KeyboardReadStatus() & 1)) break;
1048 /* Get the scan code and virtual key code */
1049 ScanCode
= KeyboardReadData();
1050 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1052 /* Check if this is a key press or release */
1053 if (!(ScanCode
& (1 << 7)))
1056 if (VirtualKey
== VK_NUMLOCK
1057 || VirtualKey
== VK_CAPITAL
1058 || VirtualKey
== VK_SCROLL
)
1060 /* For toggle keys, toggle the lowest bit in the keyboard map */
1061 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1064 /* Set the highest bit */
1065 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1067 /* Find out which character this is */
1068 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) > 0)
1070 /* Push it onto the BIOS keyboard queue */
1071 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
1076 /* Key release, unset the highest bit */
1077 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1084 /* Send End-of-Interrupt to the PIC */
1085 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1086 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);