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 CursorRow
, CursorCol
;
20 static WORD ConsoleWidth
, ConsoleHeight
;
21 static BYTE BiosKeyboardMap
[256];
22 static WORD BiosKbdBuffer
[BIOS_KBD_BUFFER_SIZE
];
23 static UINT BiosKbdBufferStart
= 0, BiosKbdBufferEnd
= 0;
24 static BOOLEAN BiosKbdBufferEmpty
= TRUE
;
26 /* PRIVATE FUNCTIONS **********************************************************/
28 static COORD
BiosVideoAddressToCoord(ULONG Address
)
30 COORD Result
= {0, 0};
31 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo
;
32 HANDLE ConsoleOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
34 if (!GetConsoleScreenBufferInfo(ConsoleOutput
, &ConsoleInfo
))
40 Result
.X
= ((Address
- CONSOLE_VIDEO_MEM_START
) >> 1) % ConsoleInfo
.dwSize
.X
;
41 Result
.Y
= ((Address
- CONSOLE_VIDEO_MEM_START
) >> 1) / ConsoleInfo
.dwSize
.X
;
46 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
48 /* If it's full, fail */
49 if (!BiosKbdBufferEmpty
&& (BiosKbdBufferStart
== BiosKbdBufferEnd
))
54 /* Otherwise, add the value to the queue */
55 BiosKbdBuffer
[BiosKbdBufferEnd
] = Data
;
57 BiosKbdBufferEnd
%= BIOS_KBD_BUFFER_SIZE
;
58 BiosKbdBufferEmpty
= FALSE
;
64 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
66 /* If it's empty, fail */
67 if (BiosKbdBufferEmpty
) return FALSE
;
69 /* Otherwise, get the value and return success */
70 *Data
= BiosKbdBuffer
[BiosKbdBufferStart
];
74 static BOOLEAN
BiosKbdBufferPop()
76 /* If it's empty, fail */
77 if (BiosKbdBufferEmpty
) return FALSE
;
79 /* Otherwise, remove the value and return success */
81 BiosKbdBufferStart
%= BIOS_KBD_BUFFER_SIZE
;
82 if (BiosKbdBufferStart
== BiosKbdBufferEnd
) BiosKbdBufferEmpty
= TRUE
;
87 /* PUBLIC FUNCTIONS ***********************************************************/
89 BOOLEAN
BiosInitialize()
93 HANDLE ConsoleInput
= GetStdHandle(STD_INPUT_HANDLE
);
94 HANDLE ConsoleOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
95 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo
;
96 LPWORD IntVecTable
= (LPWORD
)((ULONG_PTR
)BaseAddress
);
97 LPBYTE BiosCode
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(BIOS_SEGMENT
, 0));
99 /* Generate ISR stubs and fill the IVT */
100 for (i
= 0; i
< 256; i
++)
102 IntVecTable
[i
* 2] = Offset
;
103 IntVecTable
[i
* 2 + 1] = BIOS_SEGMENT
;
105 if (i
!= SPECIAL_INT_NUM
)
107 BiosCode
[Offset
++] = 0xFA; // cli
109 BiosCode
[Offset
++] = 0x6A; // push i
110 BiosCode
[Offset
++] = (BYTE
)i
;
112 BiosCode
[Offset
++] = 0xCD; // int SPECIAL_INT_NUM
113 BiosCode
[Offset
++] = SPECIAL_INT_NUM
;
115 BiosCode
[Offset
++] = 0x83; // add sp, 2
116 BiosCode
[Offset
++] = 0xC4;
117 BiosCode
[Offset
++] = 0x02;
120 BiosCode
[Offset
++] = 0xCF; // iret
123 /* Get the console buffer info */
124 if (!GetConsoleScreenBufferInfo(ConsoleOutput
, &ConsoleInfo
))
129 /* Set the initial cursor position and console size */
130 CursorCol
= ConsoleInfo
.dwCursorPosition
.X
;
131 CursorRow
= ConsoleInfo
.dwCursorPosition
.Y
;
132 ConsoleWidth
= ConsoleInfo
.dwSize
.X
;
133 ConsoleHeight
= ConsoleInfo
.dwSize
.Y
;
135 /* Set the console input mode */
136 SetConsoleMode(ConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
138 /* Initialize the PIC */
139 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
140 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
142 /* Set the interrupt offsets */
143 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
144 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
146 /* Tell the master PIC there is a slave at IRQ 2 */
147 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
148 PicWriteData(PIC_SLAVE_DATA
, 2);
150 /* Make sure the PIC is in 8086 mode */
151 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
152 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
154 /* Clear the masks for both PICs */
155 PicWriteData(PIC_MASTER_DATA
, 0x00);
156 PicWriteData(PIC_SLAVE_DATA
, 0x00);
158 PitWriteCommand(0x34);
159 PitWriteData(0, 0x00);
160 PitWriteData(0, 0x00);
165 VOID
BiosUpdateConsole(ULONG StartAddress
, ULONG EndAddress
)
170 HANDLE ConsoleOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
172 /* Loop through all the addresses */
173 for (i
= StartAddress
; i
< EndAddress
; i
++)
175 /* Get the coordinates */
176 Coordinates
= BiosVideoAddressToCoord(i
);
178 /* Check if this is a character byte or an attribute byte */
179 if ((i
- CONSOLE_VIDEO_MEM_START
) % 2 == 0)
181 /* This is a regular character */
182 FillConsoleOutputCharacterA(ConsoleOutput
,
183 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
),
190 /* This is an attribute */
191 FillConsoleOutputAttribute(ConsoleOutput
,
192 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
),
200 VOID
BiosUpdateVideoMemory(ULONG StartAddress
, ULONG EndAddress
)
206 HANDLE ConsoleOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
208 /* Loop through all the addresses */
209 for (i
= StartAddress
; i
< EndAddress
; i
++)
211 /* Get the coordinates */
212 Coordinates
= BiosVideoAddressToCoord(i
);
214 /* Check if this is a character byte or an attribute byte */
215 if ((i
- CONSOLE_VIDEO_MEM_START
) % 2 == 0)
217 /* This is a regular character */
218 ReadConsoleOutputCharacterA(ConsoleOutput
,
219 (LPSTR
)((ULONG_PTR
)BaseAddress
+ i
),
226 /* This is an attribute */
227 ReadConsoleOutputAttribute(ConsoleOutput
,
233 *(PCHAR
)((ULONG_PTR
)BaseAddress
+ i
) = LOBYTE(Attribute
);
238 WORD
BiosPeekCharacter()
242 /* Check if there is a key available */
243 if (BiosKbdBufferEmpty
) return 0xFFFF;
245 /* Get the key from the queue, but don't remove it */
246 BiosKbdBufferTop(&CharacterData
);
248 return CharacterData
;
251 WORD
BiosGetCharacter()
254 HANDLE ConsoleInput
= GetStdHandle(STD_INPUT_HANDLE
);
255 INPUT_RECORD InputRecord
;
258 /* Check if there is a key available */
259 if (!BiosKbdBufferEmpty
)
261 /* Get the key from the queue, and remove it */
262 BiosKbdBufferTop(&CharacterData
);
269 /* Wait for a console event */
270 WaitForSingleObject(ConsoleInput
, INFINITE
);
272 /* Read the event, and make sure it's a keypress */
273 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
)) continue;
274 if (InputRecord
.EventType
!= KEY_EVENT
) continue;
275 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) continue;
277 /* Save the scan code and end the loop */
278 CharacterData
= (InputRecord
.Event
.KeyEvent
.wVirtualScanCode
<< 8)
279 | InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
285 return CharacterData
;
288 VOID
BiosVideoService()
290 HANDLE ConsoleOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
292 BOOLEAN Invisible
= FALSE
;
294 CONSOLE_CURSOR_INFO CursorInfo
;
297 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
298 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
299 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
300 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
304 /* Set Text-Mode Cursor Shape */
307 /* Retrieve and validate the input */
308 Invisible
= ((HIBYTE(Ecx
) >> 5) & 0x03) ? TRUE
: FALSE
;
309 CursorHeight
= (HIBYTE(Ecx
) & 0x1F) - (LOBYTE(Ecx
) & 0x1F);
310 if (CursorHeight
< 1) CursorHeight
= 1;
311 if (CursorHeight
> 100) CursorHeight
= 100;
314 CursorInfo
.dwSize
= (CursorHeight
* 100) / CONSOLE_FONT_HEIGHT
;
315 CursorInfo
.bVisible
= !Invisible
;
316 SetConsoleCursorInfo(ConsoleOutput
, &CursorInfo
);
321 /* Set Cursor Position */
324 Position
.X
= LOBYTE(Edx
);
325 Position
.Y
= HIBYTE(Edx
);
327 SetConsoleCursorPosition(ConsoleOutput
, Position
);
331 /* Scroll Up/Down Window */
335 Rect
.Top
= HIBYTE(Ecx
);
336 Rect
.Left
= LOBYTE(Ecx
);
337 Rect
.Bottom
= HIBYTE(Edx
);
338 Rect
.Right
= LOBYTE(Edx
);
339 Character
.Char
.UnicodeChar
= L
' ';
340 Character
.Attributes
= HIBYTE(Ebx
);
341 Position
.X
= Rect
.Left
;
342 if (HIBYTE(Eax
) == 0x06) Position
.Y
= Rect
.Top
- LOBYTE(Eax
);
343 else Position
.Y
= Rect
.Top
+ LOBYTE(Eax
);
345 ScrollConsoleScreenBuffer(ConsoleOutput
,
353 /* Read Character And Attribute At Cursor Position */
359 /* Write Character And Attribute At Cursor Position */
365 /* Write Character Only At Cursor Position */
373 VOID
BiosKeyboardService()
375 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
381 /* Read the character (and wait if necessary) */
382 EmulatorSetRegister(EMULATOR_REG_AX
, BiosGetCharacter());
389 WORD Data
= BiosPeekCharacter();
393 /* There is a character, clear ZF and return it */
394 EmulatorSetRegister(EMULATOR_REG_AX
, Data
);
395 EmulatorClearFlag(EMULATOR_FLAG_ZF
);
399 /* No character, set ZF */
400 EmulatorSetFlag(EMULATOR_FLAG_ZF
);
408 VOID
BiosHandleIrq(BYTE IrqNumber
)
415 /* Perform the system timer interrupt */
416 EmulatorInterrupt(0x1C);
424 BYTE ScanCode
, VirtualKey
;
427 /* Check if there is a scancode available */
428 if (!(KeyboardReadStatus() & 1)) break;
430 /* Get the scan code and virtual key code */
431 ScanCode
= KeyboardReadData();
432 VirtualKey
= MapVirtualKey(ScanCode
, MAPVK_VSC_TO_VK
);
434 /* Check if this is a key press or release */
435 if (!(ScanCode
& (1 << 7)))
438 if (VirtualKey
== VK_NUMLOCK
439 || VirtualKey
== VK_CAPITAL
440 || VirtualKey
== VK_SCROLL
)
442 /* For toggle keys, toggle the lowest bit in the keyboard map */
443 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
446 /* Set the highest bit */
447 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
449 /* Find out which character this is */
450 ToAscii(ScanCode
, VirtualKey
, BiosKeyboardMap
, &Character
, 0);
452 /* Push it onto the BIOS keyboard queue */
453 BiosKbdBufferPush((ScanCode
<< 8) | (Character
& 0xFF));
457 /* Key release, unset the highest bit */
458 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
465 /* Send End-of-Interrupt to the PIC */
466 if (IrqNumber
> 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
467 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);