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 COORD
BiosVideoAddressToCoord(ULONG Address
)
77 COORD Result
= {0, 0};
79 DWORD Offset
= Address
- (VideoModes
[CurrentVideoMode
].Segment
<< 4);
81 if (VideoModes
[CurrentVideoMode
].Text
)
83 Result
.X
= (Offset
/ sizeof(WORD
)) % VideoModes
[CurrentVideoMode
].Width
;
84 Result
.Y
= (Offset
/ sizeof(WORD
)) / VideoModes
[CurrentVideoMode
].Width
;
88 BitsPerPixel
= BiosColorNumberToBits(VideoModes
[CurrentVideoMode
].Colors
);
90 Result
.X
= ((Offset
* 8) / BitsPerPixel
)
91 % VideoModes
[CurrentVideoMode
].Width
;
92 Result
.Y
= ((Offset
* 8) / BitsPerPixel
)
93 / VideoModes
[CurrentVideoMode
].Width
;
99 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
101 /* If it's full, fail */
102 if (!BiosKbdBufferEmpty
&& (BiosKbdBufferStart
== BiosKbdBufferEnd
))
107 /* Otherwise, add the value to the queue */
108 BiosKbdBuffer
[BiosKbdBufferEnd
] = Data
;
110 BiosKbdBufferEnd
%= BIOS_KBD_BUFFER_SIZE
;
111 BiosKbdBufferEmpty
= FALSE
;
117 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
119 /* If it's empty, fail */
120 if (BiosKbdBufferEmpty
) return FALSE
;
122 /* Otherwise, get the value and return success */
123 *Data
= BiosKbdBuffer
[BiosKbdBufferStart
];
127 static BOOLEAN
BiosKbdBufferPop()
129 /* If it's empty, fail */
130 if (BiosKbdBufferEmpty
) return FALSE
;
132 /* Otherwise, remove the value and return success */
133 BiosKbdBufferStart
++;
134 BiosKbdBufferStart
%= BIOS_KBD_BUFFER_SIZE
;
135 if (BiosKbdBufferStart
== BiosKbdBufferEnd
) BiosKbdBufferEmpty
= TRUE
;
140 /* PUBLIC FUNCTIONS ***********************************************************/
142 BYTE
BiosGetVideoMode()
144 return CurrentVideoMode
;
147 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
151 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
152 LPBITMAPINFO BitmapInfo
;
155 /* Make sure this is a valid video mode */
156 if (ModeNumber
> BIOS_MAX_VIDEO_MODE
) return FALSE
;
157 if (VideoModes
[ModeNumber
].Pages
== 0) return FALSE
;
159 /* Free the current buffers */
160 for (i
= 0; i
< VideoModes
[CurrentVideoMode
].Pages
; i
++)
162 if (ConsoleBuffers
[i
] != NULL
) CloseHandle(ConsoleBuffers
[i
]);
163 if (!VideoModes
[CurrentVideoMode
].Text
) CloseHandle(ConsoleMutexes
[i
]);
166 if (VideoModes
[ModeNumber
].Text
)
168 /* Page 0 is CONOUT$ */
169 ConsoleBuffers
[0] = CreateFile(TEXT("CONOUT$"),
170 GENERIC_READ
| GENERIC_WRITE
,
171 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
177 /* Set the current page to page 0 */
178 CurrentVideoPage
= 0;
180 /* Create console buffers for other pages */
181 for (i
= 1; i
< VideoModes
[ModeNumber
].Pages
; i
++)
183 ConsoleBuffers
[i
] = CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
184 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
186 CONSOLE_TEXTMODE_BUFFER
,
190 /* Set the size for the buffers */
191 for (i
= 0; i
< VideoModes
[ModeNumber
].Pages
; i
++)
193 Coord
.X
= VideoModes
[ModeNumber
].Width
;
194 Coord
.Y
= VideoModes
[ModeNumber
].Height
;
196 SetConsoleScreenBufferSize(ConsoleBuffers
[i
], Coord
);
201 /* Allocate a bitmap info structure */
202 BitmapInfo
= (LPBITMAPINFO
)HeapAlloc(GetProcessHeap(),
204 sizeof(BITMAPINFOHEADER
)
205 + VideoModes
[ModeNumber
].Colors
207 if (BitmapInfo
== NULL
) return FALSE
;
209 /* Fill the bitmap info header */
210 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
211 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
212 BitmapInfo
->bmiHeader
.biWidth
= VideoModes
[ModeNumber
].Width
;
213 BitmapInfo
->bmiHeader
.biHeight
= VideoModes
[ModeNumber
].Height
;
214 BitmapInfo
->bmiHeader
.biPlanes
= 1;
215 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
216 BitmapInfo
->bmiHeader
.biBitCount
= BiosColorNumberToBits(VideoModes
[ModeNumber
].Colors
);
218 /* Calculate the image size */
219 BitmapInfo
->bmiHeader
.biSizeImage
= BitmapInfo
->bmiHeader
.biWidth
220 * BitmapInfo
->bmiHeader
.biHeight
221 * (BitmapInfo
->bmiHeader
.biBitCount
>> 3);
223 /* Fill the palette data */
224 PaletteIndex
= (LPWORD
)((ULONG_PTR
)BitmapInfo
+ sizeof(BITMAPINFOHEADER
));
225 for (i
= 0; i
< VideoModes
[ModeNumber
].Colors
; i
++)
230 /* Create a console buffer for each page */
231 for (i
= 0; i
< VideoModes
[ModeNumber
].Pages
; i
++)
233 /* Fill the console graphics buffer info */
234 GraphicsBufferInfo
.dwBitMapInfoLength
= sizeof(BITMAPINFOHEADER
)
235 + VideoModes
[ModeNumber
].Colors
237 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
238 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
240 /* Create the buffer */
241 ConsoleBuffers
[i
] = CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
242 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
244 CONSOLE_GRAPHICS_BUFFER
,
245 &GraphicsBufferInfo
);
247 /* Save the framebuffer address and mutex */
248 ConsoleFramebuffers
[i
] = GraphicsBufferInfo
.lpBitMap
;
249 ConsoleMutexes
[i
] = GraphicsBufferInfo
.hMutex
;
252 /* Free the bitmap information */
253 HeapFree(GetProcessHeap(), 0, BitmapInfo
);
256 /* Set the active page console buffer */
257 SetConsoleActiveScreenBuffer(ConsoleBuffers
[CurrentVideoPage
]);
259 /* Update the mode number */
260 CurrentVideoMode
= ModeNumber
;
265 inline DWORD
BiosGetVideoMemoryStart()
267 return (VideoModes
[CurrentVideoMode
].Segment
<< 4);
270 inline VOID
BiosVerticalRefresh()
272 /* Ignore if we're in text mode */
273 if (VideoModes
[CurrentVideoMode
].Text
) return;
275 /* Ignore if there's nothing to update */
276 if (!VideoNeedsUpdate
) return;
278 /* Redraw the screen */
279 InvalidateConsoleDIBits(ConsoleBuffers
[CurrentVideoPage
],
282 /* Clear the update flag */
283 VideoNeedsUpdate
= FALSE
;
286 BOOLEAN
BiosInitialize()
290 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
291 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
293 /* Generate ISR stubs and fill the IVT */
294 for (i
= 0; i
< 256; i
++)
296 IntVecTable
[i
* 2] = Offset
;
297 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
299 if (i
!= SPECIAL_INT_NUM
)
301 BiosCode
[Offset
++] = 0xFA; // cli
303 BiosCode
[Offset
++] = 0x6A; // push i
304 BiosCode
[Offset
++] = (BYTE
)i
;
306 BiosCode
[Offset
++] = 0xCD; // int SPECIAL_INT_NUM
307 BiosCode
[Offset
++] = SPECIAL_INT_NUM
;
309 BiosCode
[Offset
++] = 0x83; // add sp, 2
310 BiosCode
[Offset
++] = 0xC4;
311 BiosCode
[Offset
++] = 0x02;
314 BiosCode
[Offset
++] = 0xCF; // iret
317 /* Get the input and output handles to the real console */
318 BiosConsoleInput
= CreateFile(TEXT("CONIN$"),
319 GENERIC_READ
| GENERIC_WRITE
,
320 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
326 BiosConsoleOutput
= CreateFile(TEXT("CONOUT$"),
327 GENERIC_READ
| GENERIC_WRITE
,
328 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
334 /* Make sure it was successful */
335 if ((BiosConsoleInput
== INVALID_HANDLE_VALUE
)
336 || (BiosConsoleOutput
== INVALID_HANDLE_VALUE
))
341 /* Save the console screen buffer information */
342 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
347 /* Set the default video mode */
348 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
350 /* Set the console input mode */
351 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
353 /* Initialize the PIC */
354 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
355 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
357 /* Set the interrupt offsets */
358 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
359 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
361 /* Tell the master PIC there is a slave at IRQ 2 */
362 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
363 PicWriteData(PIC_SLAVE_DATA
, 2);
365 /* Make sure the PIC is in 8086 mode */
366 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
367 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
369 /* Clear the masks for both PICs */
370 PicWriteData(PIC_MASTER_DATA
, 0x00);
371 PicWriteData(PIC_SLAVE_DATA
, 0x00);
373 PitWriteCommand(0x34);
374 PitWriteData(0, 0x00);
375 PitWriteData(0, 0x00);
384 /* Restore the old screen buffer */
385 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
387 /* Restore the screen buffer size */
388 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
390 /* Free the buffers */
391 for (i
= 0; i
< VideoModes
[CurrentVideoMode
].Pages
; i
++)
393 if (ConsoleBuffers
[i
] != NULL
) CloseHandle(ConsoleBuffers
[i
]);
394 if (!VideoModes
[CurrentVideoMode
].Text
) CloseHandle(ConsoleMutexes
[i
]);
397 /* Close the console handles */
398 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
399 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
402 VOID
BiosUpdateConsole(ULONG StartAddress
, ULONG EndAddress
)
406 COORD Origin
= { 0, 0 };
407 COORD UnitSize
= { 1, 1 };
411 /* Start from the character address */
414 if (VideoModes
[CurrentVideoMode
].Text
)
416 /* Loop through all the addresses */
417 for (i
= StartAddress
; i
< EndAddress
; i
+= 2)
419 /* Get the coordinates */
420 Coordinates
= BiosVideoAddressToCoord(i
);
422 /* Fill the rectangle structure */
423 Rect
.Left
= Coordinates
.X
;
424 Rect
.Top
= Coordinates
.Y
;
425 Rect
.Right
= Rect
.Left
;
426 Rect
.Bottom
= Rect
.Top
;
428 /* Fill the character data */
429 Character
.Char
.AsciiChar
= *((PCHAR
)((ULONG_PTR
)BaseAddress
+ i
));
430 Character
.Attributes
= *((PBYTE
)((ULONG_PTR
)BaseAddress
+ i
+ 1));
432 /* Write the character */
433 WriteConsoleOutputA(BiosConsoleOutput
,
442 /* Wait for the mutex object */
443 WaitForSingleObject(ConsoleMutexes
[CurrentVideoPage
], INFINITE
);
445 /* Copy the data to the framebuffer */
446 RtlCopyMemory((LPVOID
)((ULONG_PTR
)ConsoleFramebuffers
[CurrentVideoPage
]
447 + StartAddress
- BiosGetVideoMemoryStart()),
448 (LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
449 EndAddress
- StartAddress
);
451 /* Release the mutex */
452 ReleaseMutex(ConsoleMutexes
[CurrentVideoPage
]);
454 /* Check if this is the first time the rectangle is updated */
455 if (!VideoNeedsUpdate
)
457 UpdateRectangle
.Left
= UpdateRectangle
.Top
= (SHORT
)0x7FFF;
458 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= (SHORT
)0x8000;
461 /* Expand the update rectangle */
462 for (i
= StartAddress
; i
< EndAddress
; i
++)
464 /* Get the coordinates */
465 Coordinates
= BiosVideoAddressToCoord(i
);
467 /* Expand the rectangle to include the point */
468 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Coordinates
.X
);
469 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Coordinates
.X
);
470 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Coordinates
.Y
);
471 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Coordinates
.Y
);
474 /* Set the update flag */
475 VideoNeedsUpdate
= TRUE
;
479 VOID
BiosUpdateVideoMemory(ULONG StartAddress
, ULONG EndAddress
)
486 if (VideoModes
[CurrentVideoMode
].Text
)
488 /* Loop through all the addresses */
489 for (i
= StartAddress
; i
< EndAddress
; i
++)
491 /* Get the coordinates */
492 Coordinates
= BiosVideoAddressToCoord(i
);
494 /* Check if this is a character byte or an attribute byte */
495 if ((i
- (VideoModes
[CurrentVideoMode
].Segment
<< 4)) % 2 == 0)
497 /* This is a regular character */
498 ReadConsoleOutputCharacterA(BiosConsoleOutput
,
499 (LPSTR
)((ULONG_PTR
)BaseAddress
+ i
),
506 /* This is an attribute */
507 ReadConsoleOutputAttribute(BiosConsoleOutput
,
513 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
) = LOBYTE(Attribute
);
519 /* Wait for the mutex object */
520 WaitForSingleObject(ConsoleMutexes
[CurrentVideoPage
], INFINITE
);
522 /* Copy the data to the emulator memory */
523 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ StartAddress
),
524 (LPVOID
)((ULONG_PTR
)ConsoleFramebuffers
[CurrentVideoPage
]
525 + StartAddress
- BiosGetVideoMemoryStart()),
526 EndAddress
- StartAddress
);
528 /* Release the mutex */
529 ReleaseMutex(ConsoleMutexes
[CurrentVideoPage
]);
533 WORD
BiosPeekCharacter()
537 /* Check if there is a key available */
538 if (BiosKbdBufferEmpty
) return 0xFFFF;
540 /* Get the key from the queue, but don't remove it */
541 BiosKbdBufferTop(&CharacterData
);
543 return CharacterData
;
546 WORD
BiosGetCharacter()
549 INPUT_RECORD InputRecord
;
552 /* Check if there is a key available */
553 if (!BiosKbdBufferEmpty
)
555 /* Get the key from the queue, and remove it */
556 BiosKbdBufferTop(&CharacterData
);
563 /* Wait for a console event */
564 WaitForSingleObject(BiosConsoleInput
, INFINITE
);
566 /* Read the event, and make sure it's a keypress */
567 if (!ReadConsoleInput(BiosConsoleInput
, &InputRecord
, 1, &Count
)) continue;
568 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
569 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
571 /* Save the scan code and end the loop */
572 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
573 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
579 return CharacterData
;
582 VOID
BiosVideoService()
585 BOOLEAN Invisible
= FALSE
;
587 CONSOLE_CURSOR_INFO CursorInfo
;
588 CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo
;
591 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
592 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
593 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
594 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
601 BiosSetVideoMode(LOBYTE(Eax
));
605 /* Set Text-Mode Cursor Shape */
608 /* Retrieve and validate the input */
609 Invisible
= ((HIBYTE(Ecx
) >> 5) & 0x03) ? TRUE
: FALSE
;
610 CursorHeight
= (HIBYTE(Ecx
) & 0x1F) - (LOBYTE(Ecx
) & 0x1F);
611 if (CursorHeight
< 1) CursorHeight
= 1;
612 if (CursorHeight
> 100) CursorHeight
= 100;
615 CursorInfo
.dwSize
= (CursorHeight
* 100) / CONSOLE_FONT_HEIGHT
;
616 CursorInfo
.bVisible
= !Invisible
;
617 SetConsoleCursorInfo(BiosConsoleOutput
, &CursorInfo
);
622 /* Set Cursor Position */
625 Position
.X
= LOBYTE(Edx
);
626 Position
.Y
= HIBYTE(Edx
);
628 SetConsoleCursorPosition(BiosConsoleOutput
, Position
);
632 /* Get Cursor Position */
637 /* Retrieve the data */
638 GetConsoleCursorInfo(BiosConsoleOutput
, &CursorInfo
);
639 GetConsoleScreenBufferInfo(BiosConsoleOutput
, &ScreenBufferInfo
);
641 /* Find the first line */
642 StartLine
= 32 - ((CursorInfo
.dwSize
* 32) / 100);
644 /* Return the result */
645 EmulatorSetRegister(EMULATOR_REG_AX
, 0);
646 EmulatorSetRegister(EMULATOR_REG_CX
, (StartLine
<< 8) | 0x1F);
647 EmulatorSetRegister(EMULATOR_REG_DX
,
648 LOWORD(ScreenBufferInfo
.dwCursorPosition
.Y
) << 8
649 || LOWORD(ScreenBufferInfo
.dwCursorPosition
.X
));
653 /* Scroll Up/Down Window */
657 Rect
.Top
= HIBYTE(Ecx
);
658 Rect
.Left
= LOBYTE(Ecx
);
659 Rect
.Bottom
= HIBYTE(Edx
);
660 Rect
.Right
= LOBYTE(Edx
);
661 Character
.Char
.UnicodeChar
= L
' ';
662 Character
.Attributes
= HIBYTE(Ebx
);
663 Position
.X
= Rect
.Left
;
664 if (HIBYTE(Eax
) == 0x06) Position
.Y
= Rect
.Top
- LOBYTE(Eax
);
665 else Position
.Y
= Rect
.Top
+ LOBYTE(Eax
);
667 ScrollConsoleScreenBuffer(BiosConsoleOutput
,
675 /* Read Character And Attribute At Cursor Position */
678 COORD BufferSize
= { 1, 1 }, Origin
= { 0, 0 };
680 /* Get the cursor position */
681 GetConsoleScreenBufferInfo(BiosConsoleOutput
, &ScreenBufferInfo
);
683 /* Read at cursor position */
684 Rect
.Left
= ScreenBufferInfo
.dwCursorPosition
.X
;
685 Rect
.Top
= ScreenBufferInfo
.dwCursorPosition
.Y
;
687 /* Read the console output */
688 ReadConsoleOutput(BiosConsoleOutput
, &Character
, BufferSize
, Origin
, &Rect
);
690 /* Return the result */
691 EmulatorSetRegister(EMULATOR_REG_AX
,
692 (LOBYTE(Character
.Attributes
) << 8)
693 | Character
.Char
.AsciiChar
);
698 /* Write Character And Attribute At Cursor Position */
703 /* Get the cursor position */
704 GetConsoleScreenBufferInfo(BiosConsoleOutput
, &ScreenBufferInfo
);
706 /* Write the attribute to the output */
707 FillConsoleOutputAttribute(BiosConsoleOutput
,
710 ScreenBufferInfo
.dwCursorPosition
,
713 /* Write the character to the output */
714 FillConsoleOutputCharacterA(BiosConsoleOutput
,
717 ScreenBufferInfo
.dwCursorPosition
,
723 /* Write Character Only At Cursor Position */
728 /* Get the cursor position */
729 GetConsoleScreenBufferInfo(BiosConsoleOutput
, &ScreenBufferInfo
);
731 /* Write the character to the output */
732 FillConsoleOutputCharacterA(BiosConsoleOutput
,
735 ScreenBufferInfo
.dwCursorPosition
,
741 /* Teletype Output */
744 CHAR Character
= LOBYTE(Eax
);
747 /* Make sure the page exists */
748 if (HIBYTE(Ebx
) >= VideoModes
[CurrentVideoMode
].Pages
) break;
750 /* Set the attribute */
751 SetConsoleTextAttribute(ConsoleBuffers
[HIBYTE(Ebx
)], LOBYTE(Ebx
));
753 /* Write the character */
754 WriteConsoleA(ConsoleBuffers
[HIBYTE(Ebx
)],
763 /* Get Current Video Mode */
766 EmulatorSetRegister(EMULATOR_REG_AX
,
767 (VideoModes
[CurrentVideoMode
].Width
<< 8)
769 EmulatorSetRegister(EMULATOR_REG_BX
,
770 (CurrentVideoPage
<< 8) | LOBYTE(Ebx
));
777 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
783 VOID
BiosKeyboardService()
785 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
791 /* Read the character (and wait if necessary) */
792 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
799 WORD Data
= BiosPeekCharacter();
803 /* There is a character, clear ZF and return it */
804 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
805 EmulatorClearFlag(EMULATOR_FLAG_ZF
);
809 /* No character, set ZF */
810 EmulatorSetFlag(EMULATOR_FLAG_ZF
);
818 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
824 VOID
BiosTimeService()
826 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
827 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
828 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
834 /* Set AL to 1 if midnight had passed, 0 otherwise */
836 if (BiosPassedMidnight
) Eax
|= 1;
838 /* Return the tick count in CX:DX */
839 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
840 EmulatorSetRegister(EMULATOR_REG_CX
, HIWORD(BiosTickCount
));
841 EmulatorSetRegister(EMULATOR_REG_DX
, LOWORD(BiosTickCount
));
843 /* Reset the midnight flag */
844 BiosPassedMidnight
= FALSE
;
851 /* Set the tick count to CX:DX */
852 BiosTickCount
= MAKELONG(LOWORD(Edx
), LOWORD(Ecx
));
854 /* Reset the midnight flag */
855 BiosPassedMidnight
= FALSE
;
862 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
868 VOID
BiosSystemTimerInterrupt()
870 /* Increase the system tick count */
874 VOID
BiosEquipmentService()
876 /* Return the equipment list */
877 EmulatorSetRegister(EMULATOR_REG_AX
, BIOS_EQUIPMENT_LIST
);
880 VOID
BiosHandleIrq(BYTE IrqNumber
)
887 /* Perform the system timer interrupt */
888 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
896 BYTE ScanCode
, VirtualKey
;
899 /* Check if there is a scancode available */
900 if (!(KeyboardReadStatus() & 1)) break;
902 /* Get the scan code and virtual key code */
903 ScanCode
= KeyboardReadData();
904 VirtualKey
= MapVirtualKey(ScanCode
, MAPVK_VSC_TO_VK
);
906 /* Check if this is a key press or release */
907 if (!(ScanCode
& (1 << 7)))
910 if (VirtualKey
== VK_NUMLOCK
911 || VirtualKey
== VK_CAPITAL
912 || VirtualKey
== VK_SCROLL
)
914 /* For toggle keys, toggle the lowest bit in the keyboard map */
915 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
918 /* Set the highest bit */
919 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
921 /* Find out which character this is */
922 ToAscii(ScanCode
, VirtualKey
, BiosKeyboardMap
, &Character
, 0);
924 /* Push it onto the BIOS keyboard queue */
925 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
929 /* Key release, unset the highest bit */
930 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
937 /* Send End-of-Interrupt to the PIC */
938 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
939 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);