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