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 *******************************************************************/
19 /* PRIVATE VARIABLES **********************************************************/
21 static PBIOS_DATA_AREA Bda
;
22 static BYTE BiosKeyboardMap
[256];
23 static HANDLE BiosConsoleInput
= INVALID_HANDLE_VALUE
;
24 static HANDLE BiosConsoleOutput
= INVALID_HANDLE_VALUE
;
25 static HANDLE BiosGraphicsOutput
= NULL
;
26 static LPVOID ConsoleFramebuffer
= NULL
;
27 static HANDLE ConsoleMutex
= NULL
;
28 static BYTE CurrentVideoMode
, CurrentVideoPage
;
29 static BOOLEAN VideoNeedsUpdate
= TRUE
;
30 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
31 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo
;
33 static VIDEO_MODE VideoModes
[] =
35 /* Width | Height | Text | Bpp | Gray | Pages | Segment */
36 { 40, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 00h */
37 { 40, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 01h */
38 { 80, 25, TRUE
, 16, TRUE
, 8, 0xB800}, /* Mode 02h */
39 { 80, 25, TRUE
, 16, FALSE
, 8, 0xB800}, /* Mode 03h */
40 { 320, 200, FALSE
, 2, FALSE
, 1, 0xB800}, /* Mode 04h */
41 { 320, 200, FALSE
, 2, TRUE
, 1, 0xB800}, /* Mode 05h */
42 { 640, 200, FALSE
, 1, FALSE
, 1, 0xB800}, /* Mode 06h */
43 { 80, 25, TRUE
, 8, FALSE
, 1, 0xB000}, /* Mode 07h */
44 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 08h - not used */
45 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 09h - not used */
46 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ah - not used */
47 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Bh - not used */
48 { 0, 0, FALSE
, 0, FALSE
, 0, 0x0000}, /* Mode 0Ch - not used */
49 { 320, 200, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 0Dh */
50 { 640, 200, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 0Eh */
51 { 640, 350, FALSE
, 1, FALSE
, 1, 0xA000}, /* Mode 0Fh */
52 { 640, 350, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 10h */
53 { 640, 480, FALSE
, 1, FALSE
, 1, 0xA000}, /* Mode 11h */
54 { 640, 480, FALSE
, 4, FALSE
, 1, 0xA000}, /* Mode 12h */
55 { 320, 200, FALSE
, 8, FALSE
, 1, 0xA000} /* Mode 13h */
58 /* PRIVATE FUNCTIONS **********************************************************/
60 static DWORD
BiosGetVideoPageSize()
63 DWORD BufferSize
= VideoModes
[CurrentVideoMode
].Width
64 * VideoModes
[CurrentVideoMode
].Height
65 * VideoModes
[CurrentVideoMode
].Bpp
68 for (i
= 0; i
< 32; i
++) if ((1 << i
) >= BufferSize
) break;
73 static BYTE
BiosVideoAddressToPage(ULONG Address
)
75 return (Address
- BiosGetVideoMemoryStart())
76 / BiosGetVideoPageSize();
79 static COORD
BiosVideoAddressToCoord(ULONG Address
)
81 COORD Result
= {0, 0};
82 DWORD PageStart
= BiosVideoAddressToPage(Address
) * BiosGetVideoPageSize();
83 DWORD Offset
= Address
- BiosGetVideoMemoryStart() - PageStart
;
85 if (VideoModes
[CurrentVideoMode
].Text
)
87 Result
.X
= (Offset
/ sizeof(WORD
)) % VideoModes
[CurrentVideoMode
].Width
;
88 Result
.Y
= (Offset
/ sizeof(WORD
)) / VideoModes
[CurrentVideoMode
].Width
;
92 Result
.X
= ((Offset
* 8) / VideoModes
[CurrentVideoMode
].Bpp
)
93 % VideoModes
[CurrentVideoMode
].Width
;
94 Result
.Y
= ((Offset
* 8) / VideoModes
[CurrentVideoMode
].Bpp
)
95 / VideoModes
[CurrentVideoMode
].Width
;
101 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
103 /* Get the location of the element after the head */
104 WORD NextElement
= Bda
->KeybdBufferHead
+ 2;
106 /* Wrap it around if it's at or beyond the end */
107 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
109 /* If it's full, fail */
110 if (NextElement
== Bda
->KeybdBufferTail
) return FALSE
;
112 /* Put the value in the queue */
113 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
114 Bda
->KeybdBufferTail
+= sizeof(WORD
);
116 /* Check if we are at, or have passed, the end of the buffer */
117 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
119 /* Return it to the beginning */
120 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
127 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
129 /* If it's empty, fail */
130 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
132 /* Otherwise, get the value and return success */
133 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
138 static BOOLEAN
BiosKbdBufferPop()
140 /* If it's empty, fail */
141 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
143 /* Remove the value from the queue */
144 Bda
->KeybdBufferHead
+= sizeof(WORD
);
146 /* Check if we are at, or have passed, the end of the buffer */
147 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
149 /* Return it to the beginning */
150 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
157 static BOOLEAN
BiosCreateGraphicsBuffer(BYTE ModeNumber
)
160 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
161 LPBITMAPINFO BitmapInfo
;
164 /* Allocate a bitmap info structure */
165 BitmapInfo
= (LPBITMAPINFO
)HeapAlloc(GetProcessHeap(),
167 sizeof(BITMAPINFOHEADER
)
168 + (1 << VideoModes
[ModeNumber
].Bpp
)
170 if (BitmapInfo
== NULL
) return FALSE
;
172 /* Fill the bitmap info header */
173 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
174 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
175 BitmapInfo
->bmiHeader
.biWidth
= VideoModes
[ModeNumber
].Width
;
176 BitmapInfo
->bmiHeader
.biHeight
= VideoModes
[ModeNumber
].Height
;
177 BitmapInfo
->bmiHeader
.biPlanes
= 1;
178 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
179 BitmapInfo
->bmiHeader
.biBitCount
= VideoModes
[ModeNumber
].Bpp
;
181 /* Calculate the image size */
182 BitmapInfo
->bmiHeader
.biSizeImage
= BitmapInfo
->bmiHeader
.biWidth
183 * BitmapInfo
->bmiHeader
.biHeight
184 * (BitmapInfo
->bmiHeader
.biBitCount
>> 3);
186 /* Fill the palette data */
187 PaletteIndex
= (LPWORD
)((ULONG_PTR
)BitmapInfo
+ sizeof(BITMAPINFOHEADER
));
188 for (i
= 0; i
< (1 << VideoModes
[ModeNumber
].Bpp
); i
++)
193 /* Fill the console graphics buffer info */
194 GraphicsBufferInfo
.dwBitMapInfoLength
= sizeof(BITMAPINFOHEADER
)
195 + (1 << VideoModes
[ModeNumber
].Bpp
)
197 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
198 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
200 /* Create the buffer */
201 BiosGraphicsOutput
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
202 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
204 CONSOLE_GRAPHICS_BUFFER
,
205 &GraphicsBufferInfo
);
207 /* Save the framebuffer address and mutex */
208 ConsoleFramebuffer
= GraphicsBufferInfo
.lpBitMap
;
209 ConsoleMutex
= GraphicsBufferInfo
.hMutex
;
211 /* Free the bitmap information */
212 HeapFree(GetProcessHeap(), 0, BitmapInfo
);
217 static VOID
BiosDestroyGraphicsBuffer()
219 CloseHandle(ConsoleMutex
);
220 CloseHandle(BiosGraphicsOutput
);
223 /* PUBLIC FUNCTIONS ***********************************************************/
225 BYTE
BiosGetVideoMode()
227 return CurrentVideoMode
;
230 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
234 /* Make sure this is a valid video mode */
235 if (ModeNumber
> BIOS_MAX_VIDEO_MODE
) return FALSE
;
236 if (VideoModes
[ModeNumber
].Pages
== 0) return FALSE
;
238 /* Set the new video mode size */
239 Coord
.X
= VideoModes
[ModeNumber
].Width
;
240 Coord
.Y
= VideoModes
[ModeNumber
].Height
;
242 if (VideoModes
[ModeNumber
].Text
&& VideoModes
[CurrentVideoMode
].Text
)
244 /* Switching from text mode to another text mode */
246 /* Resize the text buffer */
247 SetConsoleScreenBufferSize(BiosConsoleOutput
, Coord
);
249 else if (VideoModes
[ModeNumber
].Text
&& !VideoModes
[CurrentVideoMode
].Text
)
251 /* Switching from graphics mode to text mode */
253 /* Resize the text buffer */
254 SetConsoleScreenBufferSize(BiosConsoleOutput
, Coord
);
256 /* Change the active screen buffer to the text buffer */
257 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
259 /* Cleanup the graphics buffer */
260 BiosDestroyGraphicsBuffer();
262 else if (!VideoModes
[ModeNumber
].Text
&& VideoModes
[CurrentVideoMode
].Text
)
264 /* Switching from text mode to graphics mode */
265 if (!BiosCreateGraphicsBuffer(ModeNumber
)) return FALSE
;
267 SetConsoleActiveScreenBuffer(BiosGraphicsOutput
);
269 else if (!VideoModes
[ModeNumber
].Text
&& !VideoModes
[CurrentVideoMode
].Text
)
271 /* Switching from graphics mode to another graphics mode */
273 /* Temporarily switch to the text mode buffer */
274 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
276 /* Cleanup the current graphics mode buffer */
277 BiosDestroyGraphicsBuffer();
279 /* Create a new graphics mode buffer */
280 if (!BiosCreateGraphicsBuffer(ModeNumber
)) return FALSE
;
283 SetConsoleActiveScreenBuffer(BiosGraphicsOutput
);
286 /* Change the mode number */
287 CurrentVideoMode
= ModeNumber
;
288 CurrentVideoPage
= 0;
291 Bda
->VideoMode
= CurrentVideoMode
;
292 Bda
->VideoPage
= CurrentVideoPage
;
293 Bda
->VideoPageSize
= BiosGetVideoPageSize();
294 Bda
->VideoPageOffset
= 0;
295 Bda
->ScreenColumns
= VideoModes
[ModeNumber
].Width
;
300 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
304 CONSOLE_SCREEN_BUFFER_INFO BufferInfo
;
306 /* Make sure this is a valid page number */
307 if (PageNumber
>= VideoModes
[CurrentVideoMode
].Pages
) return FALSE
;
309 /* Save the current console buffer in the video memory */
310 PageStart
= BiosGetVideoMemoryStart() + CurrentVideoPage
* BiosGetVideoPageSize();
311 BiosUpdateVideoMemory(PageStart
, PageStart
+ BiosGetVideoPageSize());
313 /* Save the cursor */
314 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BufferInfo
)) return FALSE
;
315 Bda
->CursorPosition
[CurrentVideoPage
] = MAKEWORD(BufferInfo
.dwCursorPosition
.X
,
316 BufferInfo
.dwCursorPosition
.Y
);
319 CurrentVideoPage
= PageNumber
;
322 Bda
->VideoPage
= CurrentVideoPage
;
323 Bda
->VideoPageSize
= BiosGetVideoPageSize();
324 Bda
->VideoPageOffset
= CurrentVideoPage
* Bda
->VideoPageSize
;
326 /* Update the console */
327 PageStart
= BiosGetVideoMemoryStart() + Bda
->VideoPage
* BiosGetVideoPageSize();
328 BiosUpdateConsole(PageStart
, PageStart
+ BiosGetVideoPageSize());
331 Coordinates
.X
= LOBYTE(Bda
->CursorPosition
[Bda
->VideoPage
]);
332 Coordinates
.Y
= HIBYTE(Bda
->CursorPosition
[Bda
->VideoPage
]);
333 SetConsoleCursorPosition(BiosConsoleOutput
, Coordinates
);
338 inline DWORD
BiosGetVideoMemoryStart()
340 return (VideoModes
[CurrentVideoMode
].Segment
<< 4);
343 inline VOID
BiosVerticalRefresh()
345 /* Ignore if we're in text mode */
346 if (VideoModes
[CurrentVideoMode
].Text
) return;
348 /* Ignore if there's nothing to update */
349 if (!VideoNeedsUpdate
) return;
351 /* Redraw the screen */
352 InvalidateConsoleDIBits(BiosGraphicsOutput
, &UpdateRectangle
);
354 /* Clear the update flag */
355 VideoNeedsUpdate
= FALSE
;
358 BOOLEAN
BiosInitialize()
362 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
363 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
365 /* Initialize the BDA */
366 Bda
= (PBIOS_DATA_AREA
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BDA_SEGMENT
, 0));
367 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
368 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
369 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
371 /* Generate ISR stubs and fill the IVT */
372 for (i
= 0; i
< 256; i
++)
374 IntVecTable
[i
* 2] = Offset
;
375 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
377 if (i
!= SPECIAL_INT_NUM
)
379 BiosCode
[Offset
++] = 0xFA; // cli
381 BiosCode
[Offset
++] = 0x6A; // push i
382 BiosCode
[Offset
++] = (BYTE
)i
;
384 BiosCode
[Offset
++] = 0xCD; // int SPECIAL_INT_NUM
385 BiosCode
[Offset
++] = SPECIAL_INT_NUM
;
387 BiosCode
[Offset
++] = 0x83; // add sp, 2
388 BiosCode
[Offset
++] = 0xC4;
389 BiosCode
[Offset
++] = 0x02;
392 BiosCode
[Offset
++] = 0xCF; // iret
395 /* Get the input and output handles to the real console */
396 BiosConsoleInput
= CreateFile(TEXT("CONIN$"),
397 GENERIC_READ
| GENERIC_WRITE
,
398 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
404 BiosConsoleOutput
= CreateFile(TEXT("CONOUT$"),
405 GENERIC_READ
| GENERIC_WRITE
,
406 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
412 /* Make sure it was successful */
413 if ((BiosConsoleInput
== INVALID_HANDLE_VALUE
)
414 || (BiosConsoleOutput
== INVALID_HANDLE_VALUE
))
419 /* Save the console screen buffer information */
420 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
425 /* Store the cursor position */
426 Bda
->CursorPosition
[0] = MAKEWORD(BiosSavedBufferInfo
.dwCursorPosition
.X
,
427 BiosSavedBufferInfo
.dwCursorPosition
.Y
);
429 /* Set the default video mode */
430 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
432 /* Set the console input mode */
433 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
435 /* Initialize the PIC */
436 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
437 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
439 /* Set the interrupt offsets */
440 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
441 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
443 /* Tell the master PIC there is a slave at IRQ 2 */
444 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
445 PicWriteData(PIC_SLAVE_DATA
, 2);
447 /* Make sure the PIC is in 8086 mode */
448 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
449 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
451 /* Clear the masks for both PICs */
452 PicWriteData(PIC_MASTER_DATA
, 0x00);
453 PicWriteData(PIC_SLAVE_DATA
, 0x00);
455 PitWriteCommand(0x34);
456 PitWriteData(0, 0x00);
457 PitWriteData(0, 0x00);
464 /* Restore the old screen buffer */
465 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
467 /* Restore the screen buffer size */
468 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
470 /* Free the graphics buffer */
471 if (!VideoModes
[CurrentVideoMode
].Text
) BiosDestroyGraphicsBuffer();
473 /* Close the console handles */
474 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
475 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
478 VOID
BiosUpdateConsole(ULONG StartAddress
, ULONG EndAddress
)
482 COORD Origin
= { 0, 0 };
483 COORD UnitSize
= { 1, 1 };
487 /* Start from the character address */
490 if (VideoModes
[CurrentVideoMode
].Text
)
492 /* Loop through all the addresses */
493 for (i
= StartAddress
; i
< EndAddress
; i
+= 2)
495 /* Get the coordinates */
496 Coordinates
= BiosVideoAddressToCoord(i
);
498 /* Make sure this is the current page */
499 if (BiosVideoAddressToPage(i
) != CurrentVideoPage
) continue;
501 /* Fill the rectangle structure */
502 Rect
.Left
= Coordinates
.X
;
503 Rect
.Top
= Coordinates
.Y
;
504 Rect
.Right
= Rect
.Left
;
505 Rect
.Bottom
= Rect
.Top
;
507 /* Fill the character data */
508 Character
.Char
.AsciiChar
= *((PCHAR
)((ULONG_PTR
)BaseAddress
+ i
));
509 Character
.Attributes
= *((PBYTE
)((ULONG_PTR
)BaseAddress
+ i
+ 1));
511 /* Write the character */
512 WriteConsoleOutputA(BiosConsoleOutput
,
521 /* Wait for the mutex object */
522 WaitForSingleObject(ConsoleMutex
, INFINITE
);
524 /* Copy the data to the framebuffer */
525 RtlCopyMemory((LPVOID
)((ULONG_PTR
)ConsoleFramebuffer
526 + StartAddress
- BiosGetVideoMemoryStart()),
527 (LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
528 EndAddress
- StartAddress
);
530 /* Release the mutex */
531 ReleaseMutex(ConsoleMutex
);
533 /* Check if this is the first time the rectangle is updated */
534 if (!VideoNeedsUpdate
)
536 UpdateRectangle
.Left
= UpdateRectangle
.Top
= (SHORT
)0x7FFF;
537 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= (SHORT
)0x8000;
540 /* Expand the update rectangle */
541 for (i
= StartAddress
; i
< EndAddress
; i
++)
543 /* Get the coordinates */
544 Coordinates
= BiosVideoAddressToCoord(i
);
546 /* Expand the rectangle to include the point */
547 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Coordinates
.X
);
548 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Coordinates
.X
);
549 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Coordinates
.Y
);
550 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Coordinates
.Y
);
553 /* Set the update flag */
554 VideoNeedsUpdate
= TRUE
;
558 VOID
BiosUpdateVideoMemory(ULONG StartAddress
, ULONG EndAddress
)
565 if (VideoModes
[CurrentVideoMode
].Text
)
567 /* Loop through all the addresses */
568 for (i
= StartAddress
; i
< EndAddress
; i
++)
570 /* Get the coordinates */
571 Coordinates
= BiosVideoAddressToCoord(i
);
573 /* Make sure this is the current page */
574 if (BiosVideoAddressToPage(i
) != CurrentVideoPage
) continue;
576 /* Check if this is a character byte or an attribute byte */
577 if ((i
- BiosGetVideoMemoryStart()) % 2 == 0)
579 /* This is a regular character */
580 ReadConsoleOutputCharacterA(BiosConsoleOutput
,
581 (LPSTR
)((ULONG_PTR
)BaseAddress
+ i
),
588 /* This is an attribute */
589 ReadConsoleOutputAttribute(BiosConsoleOutput
,
595 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
) = LOBYTE(Attribute
);
601 /* Wait for the mutex object */
602 WaitForSingleObject(ConsoleMutex
, INFINITE
);
604 /* Copy the data to the emulator memory */
605 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
606 (LPVOID
)((ULONG_PTR
)ConsoleFramebuffer
607 + StartAddress
- BiosGetVideoMemoryStart()),
608 EndAddress
- StartAddress
);
610 /* Release the mutex */
611 ReleaseMutex(ConsoleMutex
);
615 WORD
BiosPeekCharacter()
619 /* Check if there is a key available */
620 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return 0xFFFF;
622 /* Get the key from the queue, but don't remove it */
623 BiosKbdBufferTop(&CharacterData
);
625 return CharacterData
;
628 WORD
BiosGetCharacter()
631 INPUT_RECORD InputRecord
;
634 /* Check if there is a key available */
635 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
637 /* Get the key from the queue, and remove it */
638 BiosKbdBufferTop(&CharacterData
);
645 /* Wait for a console event */
646 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
648 /* Read the event, and make sure it's a keypress */
649 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
650 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
651 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
653 /* Save the scan code and end the loop */
654 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
655 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
661 return CharacterData
;
664 VOID
BiosVideoService()
667 BOOLEAN Invisible
= FALSE
;
669 CONSOLE_CURSOR_INFO CursorInfo
;
672 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
673 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
674 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
675 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
682 BiosSetVideoMode(LOBYTE(Eax
));
686 /* Set Text-Mode Cursor Shape */
689 /* Retrieve and validate the input */
690 Invisible
= ((HIBYTE(Ecx
) >> 5) & 0x03) ? TRUE
: FALSE
;
691 CursorHeight
= (HIBYTE(Ecx
) & 0x1F) - (LOBYTE(Ecx
) & 0x1F);
692 if (CursorHeight
< 1) CursorHeight
= 1;
693 if (CursorHeight
> 100) CursorHeight
= 100;
696 Bda
->CursorStartLine
= HIBYTE(Ecx
);
697 Bda
->CursorEndLine
= LOBYTE(Ecx
) & 0x1F;
700 CursorInfo
.dwSize
= (CursorHeight
* 100) / CONSOLE_FONT_HEIGHT
;
701 CursorInfo
.bVisible
= !Invisible
;
702 SetConsoleCursorInfo(BiosConsoleOutput
, &CursorInfo
);
707 /* Set Cursor Position */
710 /* Make sure the selected video page exists */
711 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
713 Bda
->CursorPosition
[HIBYTE(Ebx
)] = LOWORD(Edx
);
715 /* Check if this is the current video page */
716 if (HIBYTE(Ebx
) == CurrentVideoPage
)
718 /* Yes, change the actual cursor */
719 Position
.X
= LOBYTE(Edx
);
720 Position
.Y
= HIBYTE(Edx
);
721 SetConsoleCursorPosition(BiosConsoleOutput
, Position
);
727 /* Get Cursor Position */
730 /* Make sure the selected video page exists */
731 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
733 /* Return the result */
734 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
735 EmulatorSetRegister(EMULATOR_REG_CX
,
736 (Bda
->CursorStartLine
<< 8) | Bda
->CursorEndLine
);
737 EmulatorSetRegister(EMULATOR_REG_DX
, Bda
->CursorPosition
[HIBYTE(Ebx
)]);
742 /* Select Active Display Page */
745 /* Check if the page exists */
746 if (LOBYTE(Eax
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
748 /* Check if this is the same page */
749 if (LOBYTE(Eax
) == CurrentVideoPage
) break;
751 /* Change the video page */
752 BiosSetVideoPage(LOBYTE(Eax
));
757 /* Scroll Up/Down Window */
758 // TODO: Implement for different pages
762 BYTE Lines
= LOBYTE(Eax
);
764 Rect
.Top
= HIBYTE(Ecx
);
765 Rect
.Left
= LOBYTE(Ecx
);
766 Rect
.Bottom
= HIBYTE(Edx
);
767 Rect
.Right
= LOBYTE(Edx
);
768 Character
.Char
.UnicodeChar
= L
' ';
769 Character
.Attributes
= HIBYTE(Ebx
);
770 Position
.X
= Rect
.Left
;
772 /* 0 means clear entire window */
773 if (Lines
== 0) Lines
= Rect
.Bottom
- Rect
.Top
;
775 if (HIBYTE(Eax
) == 0x06) Position
.Y
= Rect
.Top
- Lines
;
776 else Position
.Y
= Rect
.Top
+ Lines
;
778 ScrollConsoleScreenBuffer(BiosConsoleOutput
,
787 /* Read Character And Attribute At Cursor Position */
792 /* Make sure this is text mode */
793 if (!VideoModes
[CurrentVideoMode
].Text
) break;
795 /* Make sure the selected video page exists */
796 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
798 /* Find the address */
799 Address
= BiosGetVideoMemoryStart()
800 + HIBYTE(Ebx
) * BiosGetVideoPageSize()
801 + (HIBYTE(Bda
->CursorPosition
[HIBYTE(Ebx
)])
802 * VideoModes
[CurrentVideoMode
].Height
803 + LOBYTE(Bda
->CursorPosition
[HIBYTE(Ebx
)]))
804 * VideoModes
[CurrentVideoMode
].Bpp
/ 8;
806 /* Update the video memory at that address */
807 BiosUpdateVideoMemory(Address
,
808 Address
+ VideoModes
[CurrentVideoMode
].Bpp
/ 8);
810 /* Return the result in AX */
811 EmulatorSetRegister(EMULATOR_REG_AX
,
812 *((LPWORD
)((ULONG_PTR
)BaseAddress
+ Address
)));
817 /* Write Character And Attribute At Cursor Position */
821 BYTE PixelSize
= VideoModes
[CurrentVideoMode
].Bpp
/ 8;
822 WORD Data
= (LOBYTE(Ebx
) << 8) | LOBYTE(Eax
);
823 WORD Repeat
= LOWORD(Ecx
);
824 DWORD Address
= BiosGetVideoMemoryStart()
825 + CurrentVideoPage
* BiosGetVideoPageSize()
826 + (HIBYTE(Bda
->CursorPosition
[CurrentVideoPage
])
827 * VideoModes
[CurrentVideoMode
].Height
828 + LOBYTE(Bda
->CursorPosition
[CurrentVideoPage
]))
831 /* Make sure this is text mode */
832 if (!VideoModes
[CurrentVideoMode
].Text
) break;
834 /* Make sure the selected video page exists */
835 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
837 /* Make sure we don't write over the end of video memory */
839 (CONSOLE_VIDEO_MEM_END
- Address
)
842 /* Copy the values to the memory */
843 for (i
= 0; i
< Repeat
; i
++)
845 if (PixelSize
== sizeof(BYTE
) || HIBYTE(Eax
) == 0x0A)
847 /* Just characters, no attributes */
848 *((LPBYTE
)((ULONG_PTR
)BaseAddress
+ Address
) + i
* PixelSize
) = LOBYTE(Data
);
850 else if (PixelSize
== sizeof(WORD
))
852 /* First byte for characters, second for attributes */
853 *((LPWORD
)((ULONG_PTR
)BaseAddress
+ Address
) + i
) = Data
;
857 /* Update the range */
858 BiosUpdateConsole(Address
,
859 Address
+ Repeat
* (VideoModes
[CurrentVideoMode
].Bpp
/ 8));
864 /* Teletype Output */
867 CHAR Character
= LOBYTE(Eax
);
870 /* Make sure the page exists */
871 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
873 /* Set the attribute */
874 SetConsoleTextAttribute(BiosConsoleOutput
, LOBYTE(Ebx
));
876 /* Write the character */
877 WriteConsoleA(BiosConsoleOutput
,
886 /* Get Current Video Mode */
889 EmulatorSetRegister(EMULATOR_REG_AX
,
890 MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
891 EmulatorSetRegister(EMULATOR_REG_BX
,
892 MAKEWORD(LOBYTE(Ebx
), Bda
->VideoPage
));
900 Rect
.Top
= HIBYTE(Ecx
);
901 Rect
.Left
= LOBYTE(Ecx
);
902 Rect
.Bottom
= HIBYTE(Edx
);
903 Rect
.Right
= LOBYTE(Edx
);
904 Character
.Char
.UnicodeChar
= L
' ';
905 Character
.Attributes
= 0x07;
906 Position
.X
= Rect
.Left
;
907 Position
.Y
= Rect
.Top
;
909 if (LOBYTE(Ebx
) == 0) Position
.Y
-= LOBYTE(Eax
);
910 else if (LOBYTE(Ebx
) == 1) Position
.Y
+= LOBYTE(Eax
);
911 else if (LOBYTE(Ebx
) == 2) Position
.X
-= LOBYTE(Eax
);
912 else if (LOBYTE(Ebx
) == 3) Position
.X
+= LOBYTE(Eax
);
914 ScrollConsoleScreenBuffer(BiosConsoleOutput
,
925 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
931 VOID
BiosKeyboardService()
933 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
939 /* Read the character (and wait if necessary) */
940 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
947 WORD Data
= BiosPeekCharacter();
951 /* There is a character, clear ZF and return it */
952 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
953 EmulatorClearFlag(EMULATOR_FLAG_ZF
);
957 /* No character, set ZF */
958 EmulatorSetFlag(EMULATOR_FLAG_ZF
);
966 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
972 VOID
BiosTimeService()
974 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
975 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
976 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
982 /* Set AL to 1 if midnight had passed, 0 otherwise */
984 if (Bda
->MidnightPassed
) Eax
|= 1;
986 /* Return the tick count in CX:DX */
987 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
988 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(Bda
->TickCounter
));
989 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(Bda
->TickCounter
));
991 /* Reset the midnight flag */
992 Bda
->MidnightPassed
= FALSE
;
999 /* Set the tick count to CX:DX */
1000 Bda
->TickCounter
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
1002 /* Reset the midnight flag */
1003 Bda
->MidnightPassed
= FALSE
;
1010 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1016 VOID
BiosSystemTimerInterrupt()
1018 /* Increase the system tick count */
1022 VOID
BiosEquipmentService()
1024 /* Return the equipment list */
1025 EmulatorSetRegister(EMULATOR_REG_AX
, Bda
->EquipmentList
);
1028 VOID
BiosHandleIrq(BYTE IrqNumber
)
1035 /* Perform the system timer interrupt */
1036 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1044 BYTE ScanCode
, VirtualKey
;
1047 /* Check if there is a scancode available */
1048 if (!(KeyboardReadStatus() & 1)) break;
1050 /* Get the scan code and virtual key code */
1051 ScanCode
= KeyboardReadData();
1052 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1054 /* Check if this is a key press or release */
1055 if (!(ScanCode
& (1 << 7)))
1058 if (VirtualKey
== VK_NUMLOCK
1059 || VirtualKey
== VK_CAPITAL
1060 || VirtualKey
== VK_SCROLL
)
1062 /* For toggle keys, toggle the lowest bit in the keyboard map */
1063 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1066 /* Set the highest bit */
1067 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1069 /* Find out which character this is */
1070 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) > 0)
1072 /* Push it onto the BIOS keyboard queue */
1073 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
1078 /* Key release, unset the highest bit */
1079 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1086 /* Send End-of-Interrupt to the PIC */
1087 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1088 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);