a0147415e413f6f1fc2f6ed3306091cb3c3310ca
[reactos.git] / subsystems / ntvdm / bios.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: bios.c
5 * PURPOSE: VDM BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "bios.h"
14 #include "emulator.h"
15 #include "vga.h"
16 #include "pic.h"
17 #include "ps2.h"
18 #include "timer.h"
19
20 #include "registers.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 PBIOS_DATA_AREA Bda;
25 static BYTE BiosKeyboardMap[256];
26 static HANDLE BiosConsoleInput = INVALID_HANDLE_VALUE;
27 static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
28 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
29 static HANDLE InputThread = NULL;
30
31 /*
32 * VGA Register Configurations for BIOS Video Modes
33 * The configurations come from DosBox.
34 */
35 static BYTE VideoMode_40x25_text[] =
36 {
37 /* Miscellaneous Register */
38 0x67,
39
40 /* Sequencer Registers */
41 0x00, 0x08, 0x03, 0x00, 0x07,
42
43 /* GC Registers */
44 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
45
46 /* CRTC Registers */
47 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
48 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
49 0xFF,
50
51 /* AC Registers */
52 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
53 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
54 };
55
56 static BYTE VideoMode_80x25_text[] =
57 {
58 /* Miscellaneous Register */
59 0x67,
60
61 /* Sequencer Registers */
62 0x00, 0x00, 0x03, 0x00, 0x07,
63
64 /* GC Registers */
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
66
67 /* CRTC Registers */
68 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
69 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
70 0xFF,
71
72 /* AC Registers */
73 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
74 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
75 };
76
77 static BYTE VideoMode_320x200_4color[] =
78 {
79 /* Miscellaneous Register */
80 0x63,
81
82 /* Sequencer Registers */
83 0x00, 0x09, 0x00, 0x00, 0x02,
84
85 /* GC Registers */
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
87
88 /* CRTC Registers */
89 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
91 0xFF,
92
93 /* AC Registers */
94 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
95 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
96 };
97
98 static BYTE VideoMode_640x200_2color[] =
99 {
100 /* Miscellaneous Register */
101 0x63,
102
103 /* Sequencer Registers */
104 0x00, 0x09, 0x0F, 0x00, 0x02,
105
106 /* GC Registers */
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
108
109 /* CRTC Registers */
110 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
112 0xFF,
113
114 /* AC Registers */
115 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
116 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00
117 };
118
119 static BYTE VideoMode_320x200_16color[] =
120 {
121 /* Miscellaneous Register */
122 0x63,
123
124 /* Sequencer Registers */
125 0x00, 0x09, 0x0F, 0x00, 0x02,
126
127 /* GC Registers */
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
129
130 /* CRTC Registers */
131 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
133 0xFF,
134
135 /* AC Registers */
136 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
137 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
138 };
139
140 static BYTE VideoMode_640x200_16color[] =
141 {
142 /* Miscellaneous Register */
143 0x63,
144
145 /* Sequencer Registers */
146 0x00, 0x01, 0x0F, 0x00, 0x02,
147
148 /* GC Registers */
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
150
151 /* CRTC Registers */
152 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
153 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
154 0xFF,
155
156 /* AC Registers */
157 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
158 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
159 };
160
161 static BYTE VideoMode_640x350_16color[] =
162 {
163 /* Miscellaneous Register */
164 0xA3,
165
166 /* Sequencer Registers */
167 0x00, 0x01, 0x0F, 0x00, 0x02,
168
169 /* GC Registers */
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
171
172 /* CRTC Registers */
173 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
175 0xFF,
176
177 /* AC Registers */
178 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
179 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
180 };
181
182 static BYTE VideoMode_640x480_2color[] =
183 {
184 /* Miscellaneous Register */
185 0xE3,
186
187 /* Sequencer Registers */
188 0x00, 0x01, 0x0F, 0x00, 0x02,
189
190 /* GC Registers */
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
192
193 /* CRTC Registers */
194 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
196 0xFF,
197
198 /* AC Registers */
199 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
200 0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
201 };
202
203 static BYTE VideoMode_640x480_16color[] =
204 {
205 /* Miscellaneous Register */
206 0xE3,
207
208 /* Sequencer Registers */
209 0x00, 0x01, 0x0F, 0x00, 0x02,
210
211 /* GC Registers */
212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
213
214 /* CRTC Registers */
215 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
217 0xFF,
218
219 /* AC Registers */
220 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
221 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
222 };
223
224 static BYTE VideoMode_320x200_256color[] =
225 {
226 /* Miscellaneous Register */
227 0x63,
228
229 /* Sequencer Registers */
230 0x00, 0x01, 0x0F, 0x00, 0x0E,
231
232 /* GC Registers */
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
234
235 /* CRTC Registers */
236 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
238 0xFF,
239
240 /* AC Registers */
241 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
242 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
243 };
244
245 static LPBYTE VideoModes[] =
246 {
247 VideoMode_40x25_text, /* Mode 00h */
248 VideoMode_40x25_text, /* Mode 01h */
249 VideoMode_80x25_text, /* Mode 02h */
250 VideoMode_80x25_text, /* Mode 03h */
251 VideoMode_320x200_4color, /* Mode 04h */
252 VideoMode_320x200_4color, /* Mode 05h */
253 VideoMode_640x200_2color, /* Mode 06h */
254 NULL, /* Mode 07h */
255 NULL, /* Mode 08h */
256 NULL, /* Mode 09h */
257 NULL, /* Mode 0Ah */
258 NULL, /* Mode 0Bh */
259 NULL, /* Mode 0Ch */
260 VideoMode_320x200_16color, /* Mode 0Dh */
261 VideoMode_640x200_16color, /* Mode 0Eh */
262 NULL, /* Mode 0Fh */
263 VideoMode_640x350_16color, /* Mode 10h */
264 VideoMode_640x480_2color, /* Mode 11h */
265 VideoMode_640x480_16color, /* Mode 12h */
266 VideoMode_320x200_256color, /* Mode 13h */
267 };
268
269 /* PRIVATE FUNCTIONS **********************************************************/
270
271 static BOOLEAN BiosKbdBufferPush(WORD Data)
272 {
273 /* Get the location of the element after the tail */
274 WORD NextElement = Bda->KeybdBufferTail + sizeof(WORD);
275
276 /* Wrap it around if it's at or beyond the end */
277 if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart;
278
279 /* If it's full, fail */
280 if (NextElement == Bda->KeybdBufferHead) return FALSE;
281
282 /* Put the value in the queue */
283 *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data;
284 Bda->KeybdBufferTail += sizeof(WORD);
285
286 /* Check if we are at, or have passed, the end of the buffer */
287 if (Bda->KeybdBufferTail >= Bda->KeybdBufferEnd)
288 {
289 /* Return it to the beginning */
290 Bda->KeybdBufferTail = Bda->KeybdBufferStart;
291 }
292
293 /* Return success */
294 return TRUE;
295 }
296
297 static BOOLEAN BiosKbdBufferTop(LPWORD Data)
298 {
299 /* If it's empty, fail */
300 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
301
302 /* Otherwise, get the value and return success */
303 *Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead));
304
305 return TRUE;
306 }
307
308 static BOOLEAN BiosKbdBufferPop(VOID)
309 {
310 /* If it's empty, fail */
311 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
312
313 /* Remove the value from the queue */
314 Bda->KeybdBufferHead += sizeof(WORD);
315
316 /* Check if we are at, or have passed, the end of the buffer */
317 if (Bda->KeybdBufferHead >= Bda->KeybdBufferEnd)
318 {
319 /* Return it to the beginning */
320 Bda->KeybdBufferHead = Bda->KeybdBufferStart;
321 }
322
323 /* Return success */
324 return TRUE;
325 }
326
327 static VOID BiosReadWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page)
328 {
329 INT i, j;
330 INT Counter = 0;
331 WORD Character;
332 DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize);
333
334 for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
335 {
336 for (j = Rectangle.Left; j <= Rectangle.Right; j++)
337 {
338 /* Read from video memory */
339 VgaReadMemory(VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD),
340 (LPVOID)&Character,
341 sizeof(WORD));
342
343 /* Write the data to the buffer in row order */
344 Buffer[Counter++] = Character;
345 }
346 }
347 }
348
349 static VOID BiosWriteWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page)
350 {
351 INT i, j;
352 INT Counter = 0;
353 WORD Character;
354 DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize);
355
356 for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
357 {
358 for (j = Rectangle.Left; j <= Rectangle.Right; j++)
359 {
360 Character = Buffer[Counter++];
361
362 /* Write to video memory */
363 VgaWriteMemory(VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD),
364 (LPVOID)&Character,
365 sizeof(WORD));
366 }
367 }
368 }
369
370 /* PUBLIC FUNCTIONS ***********************************************************/
371
372 BYTE BiosGetVideoMode(VOID)
373 {
374 return Bda->VideoMode;
375 }
376
377 BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
378 {
379 INT i;
380 COORD Resolution;
381 LPBYTE Values = VideoModes[ModeNumber];
382
383 DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber, Values);
384
385 if (Values == NULL) return FALSE;
386
387 /* Write the misc register */
388 VgaWritePort(VGA_MISC_WRITE, *(Values++));
389
390 /* Write the sequencer registers */
391 for (i = 0; i < VGA_SEQ_MAX_REG; i++)
392 {
393 VgaWritePort(VGA_SEQ_INDEX, i);
394 VgaWritePort(VGA_SEQ_DATA, *(Values++));
395 }
396
397 /* Write the GC registers */
398 for (i = 0; i < VGA_GC_MAX_REG; i++)
399 {
400 VgaWritePort(VGA_GC_INDEX, i);
401 VgaWritePort(VGA_GC_DATA, *(Values++));
402 }
403
404 /* Write the CRTC registers */
405 for (i = 0; i < VGA_CRTC_MAX_REG; i++)
406 {
407 VgaWritePort(VGA_CRTC_INDEX, i);
408 VgaWritePort(VGA_CRTC_DATA, *(Values++));
409 }
410
411 /* Write the AC registers */
412 for (i = 0; i < VGA_AC_MAX_REG; i++)
413 {
414 VgaWritePort(VGA_AC_INDEX, i);
415 VgaWritePort(VGA_AC_WRITE, *(Values++));
416 }
417
418 /* Update the values in the BDA */
419 Bda->VideoMode = ModeNumber;
420 Bda->VideoPage = 0;
421 Bda->VideoPageSize = BIOS_PAGE_SIZE;
422 Bda->VideoPageOffset = 0;
423
424 /* Get the character height */
425 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_MAX_SCAN_LINE_REG);
426 Bda->CharacterHeight = 1 + (VgaReadPort(VGA_CRTC_DATA) & 0x1F);
427
428 Resolution = VgaGetDisplayResolution();
429 Bda->ScreenColumns = Resolution.X;
430 Bda->ScreenRows = Resolution.Y - 1;
431
432 return TRUE;
433 }
434
435 BOOLEAN BiosSetVideoPage(BYTE PageNumber)
436 {
437 /* Check if the page exists */
438 if (PageNumber >= BIOS_MAX_PAGES) return FALSE;
439
440 /* Check if this is the same page */
441 if (PageNumber == Bda->VideoPage) return TRUE;
442
443 /* Set the values in the BDA */
444 Bda->VideoPage = PageNumber;
445 Bda->VideoPageSize = BIOS_PAGE_SIZE;
446 Bda->VideoPageOffset = PageNumber * BIOS_PAGE_SIZE;
447
448 /* Set the start address in the CRTC */
449 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
450 VgaWritePort(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset));
451 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
452 VgaWritePort(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset));
453
454 return TRUE;
455 }
456
457 BOOLEAN BiosInitialize(VOID)
458 {
459 USHORT i;
460 WORD Offset = 0;
461 LPWORD IntVecTable = (LPWORD)BaseAddress;
462 LPBYTE BiosCode = (LPBYTE)SEG_OFF_TO_PTR(BIOS_SEGMENT, 0);
463
464 /* Initialize the BDA */
465 Bda = (PBIOS_DATA_AREA)SEG_OFF_TO_PTR(BDA_SEGMENT, 0);
466 Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
467 Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
468 Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
469
470 /* Generate ISR stubs and fill the IVT */
471 for (i = 0x00; i <= 0xFF; i++)
472 {
473 IntVecTable[i * 2] = Offset;
474 IntVecTable[i * 2 + 1] = BIOS_SEGMENT;
475
476 BiosCode[Offset++] = 0xFB; // sti
477
478 BiosCode[Offset++] = 0x6A; // push i
479 BiosCode[Offset++] = (UCHAR)i;
480
481 BiosCode[Offset++] = 0x6A; // push 0
482 BiosCode[Offset++] = 0x00;
483
484 // BOP_SEQ:
485 BiosCode[Offset++] = 0xF8; // clc
486
487 BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence
488 BiosCode[Offset++] = HIBYTE(EMULATOR_BOP);
489 BiosCode[Offset++] = EMULATOR_INT_BOP;
490
491 BiosCode[Offset++] = 0x73; // jnc EXIT (offset +3)
492 BiosCode[Offset++] = 0x03;
493
494 // HACK: The following instruction should be HLT!
495 BiosCode[Offset++] = 0x90; // nop
496
497 BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -9)
498 BiosCode[Offset++] = 0xF7;
499
500 // EXIT:
501 BiosCode[Offset++] = 0x83; // add sp, 4
502 BiosCode[Offset++] = 0xC4;
503 BiosCode[Offset++] = 0x04;
504
505 BiosCode[Offset++] = 0xCF; // iret
506 }
507
508 /* Get the input handle to the real console, and check for success */
509 BiosConsoleInput = CreateFileW(L"CONIN$",
510 GENERIC_READ | GENERIC_WRITE,
511 FILE_SHARE_READ | FILE_SHARE_WRITE,
512 NULL,
513 OPEN_EXISTING,
514 0,
515 NULL);
516 if (BiosConsoleInput == INVALID_HANDLE_VALUE)
517 {
518 return FALSE;
519 }
520
521 /* Get the output handle to the real console, and check for success */
522 BiosConsoleOutput = CreateFileW(L"CONOUT$",
523 GENERIC_READ | GENERIC_WRITE,
524 FILE_SHARE_READ | FILE_SHARE_WRITE,
525 NULL,
526 OPEN_EXISTING,
527 0,
528 NULL);
529 if (BiosConsoleOutput == INVALID_HANDLE_VALUE)
530 {
531 CloseHandle(BiosConsoleInput);
532 return FALSE;
533 }
534
535 /* Save the console screen buffer information */
536 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BiosSavedBufferInfo))
537 {
538 CloseHandle(BiosConsoleOutput);
539 CloseHandle(BiosConsoleInput);
540 return FALSE;
541 }
542
543 /* Initialize VGA */
544 if (!VgaInitialize(BiosConsoleOutput))
545 {
546 CloseHandle(BiosConsoleOutput);
547 CloseHandle(BiosConsoleInput);
548 return FALSE;
549 }
550
551 /* Update the cursor position */
552 BiosSetCursorPosition(BiosSavedBufferInfo.dwCursorPosition.Y,
553 BiosSavedBufferInfo.dwCursorPosition.X,
554 0);
555
556 /* Set the console input mode */
557 SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
558
559 /* Start the input thread */
560 InputThread = CreateThread(NULL, 0, &InputThreadProc, BiosConsoleInput, 0, NULL);
561
562 /* Initialize the PIC */
563 PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
564 PicWriteCommand(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
565
566 /* Set the interrupt offsets */
567 PicWriteData(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
568 PicWriteData(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT);
569
570 /* Tell the master PIC there is a slave at IRQ 2 */
571 PicWriteData(PIC_MASTER_DATA, 1 << 2);
572 PicWriteData(PIC_SLAVE_DATA , 2);
573
574 /* Make sure the PIC is in 8086 mode */
575 PicWriteData(PIC_MASTER_DATA, PIC_ICW4_8086);
576 PicWriteData(PIC_SLAVE_DATA , PIC_ICW4_8086);
577
578 /* Clear the masks for both PICs */
579 PicWriteData(PIC_MASTER_DATA, 0x00);
580 PicWriteData(PIC_SLAVE_DATA , 0x00);
581
582 PitWriteCommand(0x34);
583 PitWriteData(0, 0x00);
584 PitWriteData(0, 0x00);
585
586 return TRUE;
587 }
588
589 VOID BiosCleanup(VOID)
590 {
591 /* Restore the old screen buffer */
592 SetConsoleActiveScreenBuffer(BiosConsoleOutput);
593
594 /* Restore the screen buffer size */
595 SetConsoleScreenBufferSize(BiosConsoleOutput, BiosSavedBufferInfo.dwSize);
596
597 /* Close the console handles */
598 if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput);
599 if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput);
600
601 /* Close the input thread handle */
602 if (InputThread != NULL) CloseHandle(InputThread);
603 }
604
605 WORD BiosPeekCharacter(VOID)
606 {
607 WORD CharacterData = 0;
608
609 /* Get the key from the queue, but don't remove it */
610 if (BiosKbdBufferTop(&CharacterData)) return CharacterData;
611 else return 0xFFFF;
612 }
613
614 WORD BiosGetCharacter(VOID)
615 {
616 WORD CharacterData = 0;
617
618 /* Check if there is a key available */
619 if (Bda->KeybdBufferHead != Bda->KeybdBufferTail)
620 {
621 /* Get the key from the queue, and remove it */
622 BiosKbdBufferTop(&CharacterData);
623 BiosKbdBufferPop();
624 }
625 else
626 {
627 /* Set the handler CF to repeat the BOP */
628 EmulatorSetFlag(EMULATOR_FLAG_CF);
629 }
630
631 return CharacterData;
632 }
633
634 VOID BiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page)
635 {
636 /* Make sure the selected video page is valid */
637 if (Page >= BIOS_MAX_PAGES) return;
638
639 /* Update the position in the BDA */
640 Bda->CursorPosition[Page] = (Row << 8) | Column;
641
642 /* Check if this is the current video page */
643 if (Page == Bda->VideoPage)
644 {
645 WORD Offset = Row * Bda->ScreenColumns + Column;
646
647 /* Modify the CRTC registers */
648 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
649 VgaWritePort(VGA_CRTC_DATA , LOBYTE(Offset));
650 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
651 VgaWritePort(VGA_CRTC_DATA , HIBYTE(Offset));
652 }
653 }
654
655 BOOLEAN BiosScrollWindow(INT Direction,
656 DWORD Amount,
657 SMALL_RECT Rectangle,
658 BYTE Page,
659 BYTE FillAttribute)
660 {
661 DWORD i;
662 LPWORD WindowData;
663 DWORD WindowSize = (Rectangle.Bottom - Rectangle.Top + 1)
664 * (Rectangle.Right - Rectangle.Left + 1);
665
666 /* Allocate a buffer for the window */
667 WindowData = (LPWORD)HeapAlloc(GetProcessHeap(),
668 HEAP_ZERO_MEMORY,
669 WindowSize * sizeof(WORD));
670 if (WindowData == NULL) return FALSE;
671
672 /* Read the window data */
673 BiosReadWindow(WindowData, Rectangle, Page);
674
675 if (Amount == 0)
676 {
677 /* Fill the window */
678 for (i = 0; i < WindowSize; i++)
679 {
680 WindowData[i] = ' ' | (FillAttribute << 8);
681 }
682
683 goto Done;
684 }
685
686 // TODO: Scroll the window!
687
688 Done:
689 /* Write back the window data */
690 BiosWriteWindow(WindowData, Rectangle, Page);
691
692 /* Free the window buffer */
693 HeapFree(GetProcessHeap(), 0, WindowData);
694
695 return TRUE;
696 }
697
698 VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
699 {
700 WORD CharData = (Attribute << 8) | Character;
701 BYTE Row, Column;
702
703 /* Make sure the page exists */
704 if (Page >= BIOS_MAX_PAGES) return;
705
706 /* Get the cursor location */
707 Row = HIBYTE(Bda->CursorPosition[Page]);
708 Column = LOBYTE(Bda->CursorPosition[Page]);
709
710 if (Character == '\a')
711 {
712 /* Bell control character */
713 // NOTE: We may use what the terminal emulator offers to us...
714 Beep(800, 200);
715 return;
716 }
717 else if (Character == '\b')
718 {
719 /* Backspace control character */
720 if (Column > 0)
721 {
722 Column--;
723 }
724 else if (Row > 0)
725 {
726 Column = Bda->ScreenColumns - 1;
727 Row--;
728 }
729
730 /* Erase the existing character */
731 CharData = (Attribute << 8) | ' ';
732 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
733 Page * Bda->VideoPageSize
734 + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
735 (LPVOID)&CharData,
736 sizeof(WORD));
737 }
738 else if (Character == '\n')
739 {
740 /* Line Feed control character */
741 Row++;
742 }
743 else if (Character == '\r')
744 {
745 /* Carriage Return control character */
746 Column = 0;
747 }
748 else
749 {
750 /* Default character */
751
752 /* Write the character */
753 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
754 Page * Bda->VideoPageSize
755 + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
756 (LPVOID)&CharData,
757 sizeof(WORD));
758
759 /* Advance the cursor */
760 Column++;
761 }
762
763 /* Check if it passed the end of the row */
764 if (Column >= Bda->ScreenColumns)
765 {
766 /* Return to the first column and go to the next line */
767 Column = 0;
768 Row++;
769 }
770
771 /* Scroll the screen up if needed */
772 if (Row > Bda->ScreenRows)
773 {
774 /* The screen must be scrolled up */
775 SMALL_RECT Rectangle = { 0, 0, Bda->ScreenColumns - 1, Bda->ScreenRows };
776
777 BiosScrollWindow(SCROLL_DIRECTION_UP,
778 1,
779 Rectangle,
780 Page,
781 DEFAULT_ATTRIBUTE);
782 }
783
784 /* Set the cursor position */
785 BiosSetCursorPosition(Row, Column, Page);
786 }
787
788 VOID BiosVideoService(LPWORD Stack)
789 {
790 switch (getAH())
791 {
792 /* Set Video Mode */
793 case 0x00:
794 {
795 BiosSetVideoMode(getAL());
796 VgaClearMemory();
797 break;
798 }
799
800 /* Set Text-Mode Cursor Shape */
801 case 0x01:
802 {
803 /* Update the BDA */
804 Bda->CursorStartLine = getCH();
805 Bda->CursorEndLine = getCL();
806
807 /* Modify the CRTC registers */
808 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_START_REG);
809 VgaWritePort(VGA_CRTC_DATA , Bda->CursorStartLine);
810 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_END_REG);
811 VgaWritePort(VGA_CRTC_DATA , Bda->CursorEndLine);
812
813 break;
814 }
815
816 /* Set Cursor Position */
817 case 0x02:
818 {
819 BiosSetCursorPosition(getDH(), getDL(), getBH());
820 break;
821 }
822
823 /* Get Cursor Position */
824 case 0x03:
825 {
826 /* Make sure the selected video page exists */
827 if (getBH() >= BIOS_MAX_PAGES) break;
828
829 /* Return the result */
830 setAX(0);
831 setCX(MAKEWORD(Bda->CursorEndLine, Bda->CursorStartLine));
832 setDX(Bda->CursorPosition[getBH()]);
833 break;
834 }
835
836 /* Query Light Pen */
837 case 0x04:
838 {
839 /*
840 * On modern BIOSes, this function returns 0
841 * so that we can ignore the other registers.
842 */
843 setAX(0);
844 break;
845 }
846
847 /* Select Active Display Page */
848 case 0x05:
849 {
850 BiosSetVideoPage(getAL());
851 break;
852 }
853
854 /* Scroll Window Up/Down */
855 case 0x06:
856 case 0x07:
857 {
858 SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() };
859
860 /* Call the internal function */
861 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
862 : SCROLL_DIRECTION_DOWN,
863 getAL(),
864 Rectangle,
865 Bda->VideoPage,
866 getBH());
867
868 break;
869 }
870
871 /* Read/Write Character From Cursor Position */
872 case 0x08:
873 case 0x09:
874 case 0x0A:
875 {
876 WORD CharacterData = MAKEWORD(getAL(), getBL());
877 BYTE Page = getBH();
878 DWORD Offset;
879
880 /* Check if the page exists */
881 if (Page >= BIOS_MAX_PAGES) break;
882
883 /* Find the offset of the character */
884 Offset = Page * Bda->VideoPageSize +
885 (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns +
886 LOBYTE(Bda->CursorPosition[Page])) * 2;
887
888 if (getAH() == 0x08)
889 {
890 /* Read from the video memory */
891 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
892 (LPVOID)&CharacterData,
893 sizeof(WORD));
894
895 /* Return the character in AX */
896 setAX(CharacterData);
897 }
898 else
899 {
900 /* Write to video memory */
901 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
902 (LPVOID)&CharacterData,
903 (getBH() == 0x09) ? sizeof(WORD) : sizeof(BYTE));
904 }
905
906 break;
907 }
908
909 /* Teletype Output */
910 case 0x0E:
911 {
912 BiosPrintCharacter(getAL(), getBL(), getBH());
913 break;
914 }
915
916 /* Get Current Video Mode */
917 case 0x0F:
918 {
919 setAX(MAKEWORD(Bda->VideoMode, Bda->ScreenColumns));
920 setBX(MAKEWORD(getBL(), Bda->VideoPage));
921 break;
922 }
923
924 /* Palette Control */
925 case 0x10:
926 {
927 switch (getAL())
928 {
929 /* Set Single Palette Register */
930 case 0x00:
931 {
932 /* Reset the flip-flop */
933 VgaReadPort(VGA_STAT_COLOR);
934
935 /* Write the index */
936 VgaWritePort(VGA_AC_INDEX, getBL());
937
938 /* Write the data */
939 VgaWritePort(VGA_AC_WRITE, getBH());
940
941 break;
942 }
943
944 /* Set Overscan Color */
945 case 0x01:
946 {
947 /* Reset the flip-flop */
948 VgaReadPort(VGA_STAT_COLOR);
949
950 /* Write the index */
951 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
952
953 /* Write the data */
954 VgaWritePort(VGA_AC_WRITE, getBH());
955
956 break;
957 }
958
959 /* Set All Palette Registers */
960 case 0x02:
961 {
962 INT i;
963 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
964
965 /* Set the palette registers */
966 for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
967 {
968 /* Reset the flip-flop */
969 VgaReadPort(VGA_STAT_COLOR);
970
971 /* Write the index */
972 VgaWritePort(VGA_AC_INDEX, i);
973
974 /* Write the data */
975 VgaWritePort(VGA_AC_WRITE, Buffer[i]);
976 }
977
978 /* Set the overscan register */
979 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
980 VgaWritePort(VGA_AC_WRITE, Buffer[VGA_AC_PAL_F_REG + 1]);
981
982 break;
983 }
984
985 /* Get Single Palette Register */
986 case 0x07:
987 {
988 /* Reset the flip-flop */
989 VgaReadPort(VGA_STAT_COLOR);
990
991 /* Write the index */
992 VgaWritePort(VGA_AC_INDEX, getBL());
993
994 /* Read the data */
995 setBH(VgaReadPort(VGA_AC_READ));
996
997 break;
998 }
999
1000 /* Get Overscan Color */
1001 case 0x08:
1002 {
1003 /* Reset the flip-flop */
1004 VgaReadPort(VGA_STAT_COLOR);
1005
1006 /* Write the index */
1007 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
1008
1009 /* Read the data */
1010 setBH(VgaReadPort(VGA_AC_READ));
1011
1012 break;
1013 }
1014
1015 /* Get All Palette Registers */
1016 case 0x09:
1017 {
1018 INT i;
1019 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
1020
1021 /* Get the palette registers */
1022 for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
1023 {
1024 /* Reset the flip-flop */
1025 VgaReadPort(VGA_STAT_COLOR);
1026
1027 /* Write the index */
1028 VgaWritePort(VGA_AC_INDEX, i);
1029
1030 /* Read the data */
1031 Buffer[i] = VgaReadPort(VGA_AC_READ);
1032 }
1033
1034 /* Get the overscan register */
1035 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
1036 Buffer[VGA_AC_PAL_F_REG + 1] = VgaReadPort(VGA_AC_READ);
1037
1038 break;
1039 }
1040
1041 /* Set Individual DAC Register */
1042 case 0x10:
1043 {
1044 /* Write the index */
1045 // Certainly in BL and not in BX as said by Ralf Brown...
1046 VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
1047
1048 /* Write the data in this order: Red, Green, Blue */
1049 VgaWritePort(VGA_DAC_DATA, getDH());
1050 VgaWritePort(VGA_DAC_DATA, getCH());
1051 VgaWritePort(VGA_DAC_DATA, getCL());
1052
1053 break;
1054 }
1055
1056 /* Set Block of DAC Registers */
1057 case 0x12:
1058 {
1059 INT i;
1060 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
1061
1062 /* Write the index */
1063 // Certainly in BL and not in BX as said by Ralf Brown...
1064 VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
1065
1066 for (i = 0; i < getCX(); i++)
1067 {
1068 /* Write the data in this order: Red, Green, Blue */
1069 VgaWritePort(VGA_DAC_DATA, *Buffer++);
1070 VgaWritePort(VGA_DAC_DATA, *Buffer++);
1071 VgaWritePort(VGA_DAC_DATA, *Buffer++);
1072 }
1073
1074 break;
1075 }
1076
1077 /* Get Individual DAC Register */
1078 case 0x15:
1079 {
1080 /* Write the index */
1081 VgaWritePort(VGA_DAC_READ_INDEX, getBL());
1082
1083 /* Read the data in this order: Red, Green, Blue */
1084 setDH(VgaReadPort(VGA_DAC_DATA));
1085 setCH(VgaReadPort(VGA_DAC_DATA));
1086 setCL(VgaReadPort(VGA_DAC_DATA));
1087
1088 break;
1089 }
1090
1091 /* Get Block of DAC Registers */
1092 case 0x17:
1093 {
1094 INT i;
1095 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
1096
1097 /* Write the index */
1098 // Certainly in BL and not in BX as said by Ralf Brown...
1099 VgaWritePort(VGA_DAC_READ_INDEX, getBL());
1100
1101 for (i = 0; i < getCX(); i++)
1102 {
1103 /* Write the data in this order: Red, Green, Blue */
1104 *Buffer++ = VgaReadPort(VGA_DAC_DATA);
1105 *Buffer++ = VgaReadPort(VGA_DAC_DATA);
1106 *Buffer++ = VgaReadPort(VGA_DAC_DATA);
1107 }
1108
1109 break;
1110 }
1111
1112 default:
1113 {
1114 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1115 getAL());
1116 break;
1117 }
1118 }
1119
1120 break;
1121 }
1122
1123 /* Scroll Window */
1124 case 0x12:
1125 {
1126 SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() };
1127
1128 /* Call the internal function */
1129 BiosScrollWindow(getBL(),
1130 getAL(),
1131 Rectangle,
1132 Bda->VideoPage,
1133 DEFAULT_ATTRIBUTE);
1134
1135 break;
1136 }
1137
1138 /* Display combination code */
1139 case 0x1A:
1140 {
1141 switch(getAL())
1142 {
1143 case 0x00: /* Get Display combiantion code */
1144 setAX(MAKEWORD(0x1A, 0x1A));
1145 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1146 break;
1147 case 0x01: /* Set Display combination code */
1148 DPRINT1("Set Display combination code - Unsupported\n");
1149 break;
1150 default:
1151 break;
1152 }
1153 break;
1154 }
1155
1156 default:
1157 {
1158 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1159 getAH());
1160 }
1161 }
1162 }
1163
1164 VOID BiosKeyboardService(LPWORD Stack)
1165 {
1166 switch (getAH())
1167 {
1168 /* Wait for keystroke and read */
1169 case 0x00:
1170 /* Wait for extended keystroke and read */
1171 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1172 {
1173 /* Read the character (and wait if necessary) */
1174 setAX(BiosGetCharacter());
1175 break;
1176 }
1177
1178 /* Get keystroke status */
1179 case 0x01:
1180 /* Get extended keystroke status */
1181 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1182 {
1183 WORD Data = BiosPeekCharacter();
1184
1185 if (Data != 0xFFFF)
1186 {
1187 /* There is a character, clear ZF and return it */
1188 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
1189 setAX(Data);
1190 }
1191 else
1192 {
1193 /* No character, set ZF */
1194 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
1195 }
1196
1197 break;
1198 }
1199
1200 /* Get shift status */
1201 case 0x02:
1202 {
1203 /* Return the lower byte of the keyboard shift status word */
1204 setAL(LOBYTE(Bda->KeybdShiftFlags));
1205 break;
1206 }
1207
1208 /* Reserved */
1209 case 0x04:
1210 {
1211 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1212 break;
1213 }
1214
1215 /* Push keystroke */
1216 case 0x05:
1217 {
1218 /* Return 0 if success, 1 if failure */
1219 setAL(BiosKbdBufferPush(getCX()) == FALSE);
1220 break;
1221 }
1222
1223 /* Get extended shift status */
1224 case 0x12:
1225 {
1226 /*
1227 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1228 * but the high byte is organized differently:
1229 * the bytes 2 and 3 of the high byte are not the same...
1230 */
1231 WORD KeybdShiftFlags = (Bda->KeybdShiftFlags & 0xF3FF);
1232
1233 /* Return the extended keyboard shift status word */
1234 setAX(KeybdShiftFlags);
1235 break;
1236 }
1237
1238 default:
1239 {
1240 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1241 getAH());
1242 }
1243 }
1244 }
1245
1246 VOID BiosTimeService(LPWORD Stack)
1247 {
1248 switch (getAH())
1249 {
1250 case 0x00:
1251 {
1252 /* Set AL to 1 if midnight had passed, 0 otherwise */
1253 setAL(Bda->MidnightPassed ? 0x01 : 0x00);
1254
1255 /* Return the tick count in CX:DX */
1256 setCX(HIWORD(Bda->TickCounter));
1257 setDX(LOWORD(Bda->TickCounter));
1258
1259 /* Reset the midnight flag */
1260 Bda->MidnightPassed = FALSE;
1261
1262 break;
1263 }
1264
1265 case 0x01:
1266 {
1267 /* Set the tick count to CX:DX */
1268 Bda->TickCounter = MAKELONG(getDX(), getCX());
1269
1270 /* Reset the midnight flag */
1271 Bda->MidnightPassed = FALSE;
1272
1273 break;
1274 }
1275
1276 default:
1277 {
1278 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1279 getAH());
1280 }
1281 }
1282 }
1283
1284 VOID BiosSystemTimerInterrupt(LPWORD Stack)
1285 {
1286 /* Increase the system tick count */
1287 Bda->TickCounter++;
1288 }
1289
1290 VOID BiosEquipmentService(LPWORD Stack)
1291 {
1292 /* Return the equipment list */
1293 setAX(Bda->EquipmentList);
1294 }
1295
1296 VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
1297 {
1298 switch (IrqNumber)
1299 {
1300 /* PIT IRQ */
1301 case 0:
1302 {
1303 /* Perform the system timer interrupt */
1304 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT);
1305 break;
1306 }
1307
1308 /* Keyboard IRQ */
1309 case 1:
1310 {
1311 BYTE ScanCode, VirtualKey;
1312 WORD Character;
1313
1314 /* Loop while there is a scancode available */
1315 do
1316 {
1317 /* Get the scan code and virtual key code */
1318 ScanCode = KeyboardReadData();
1319 VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
1320
1321 /* Check if this is a key press or release */
1322 if (!(ScanCode & (1 << 7)))
1323 {
1324 /* Key press */
1325 if (VirtualKey == VK_NUMLOCK ||
1326 VirtualKey == VK_CAPITAL ||
1327 VirtualKey == VK_SCROLL ||
1328 VirtualKey == VK_INSERT)
1329 {
1330 /* For toggle keys, toggle the lowest bit in the keyboard map */
1331 BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
1332 }
1333
1334 /* Set the highest bit */
1335 BiosKeyboardMap[VirtualKey] |= (1 << 7);
1336
1337 /* Find out which character this is */
1338 Character = 0;
1339 if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
1340 {
1341 /* Not ASCII */
1342 Character = 0;
1343 }
1344
1345 /* Push it onto the BIOS keyboard queue */
1346 BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
1347 }
1348 else
1349 {
1350 /* Key release, unset the highest bit */
1351 BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
1352 }
1353 }
1354 while (KeyboardReadStatus() & 1);
1355
1356 /* Clear the keyboard flags */
1357 Bda->KeybdShiftFlags = 0;
1358
1359 /* Set the appropriate flags based on the state */
1360 if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
1361 if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
1362 if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
1363 if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
1364 if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
1365 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
1366 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
1367 if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
1368 if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT;
1369 if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
1370 if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
1371 if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
1372 if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
1373 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
1374 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
1375 if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
1376
1377 break;
1378 }
1379 }
1380
1381 /* Send End-of-Interrupt to the PIC */
1382 if (IrqNumber >= 8) PicWriteCommand(PIC_SLAVE_CMD, PIC_OCW2_EOI);
1383 PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
1384 }
1385
1386 /* EOF */