[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 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 /* Reset the palette */
419 VgaResetPalette();
420
421 /* Update the values in the BDA */
422 Bda->VideoMode = ModeNumber;
423 Bda->VideoPage = 0;
424 Bda->VideoPageSize = BIOS_PAGE_SIZE;
425 Bda->VideoPageOffset = 0;
426
427 /* Get the character height */
428 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_MAX_SCAN_LINE_REG);
429 Bda->CharacterHeight = 1 + (VgaReadPort(VGA_CRTC_DATA) & 0x1F);
430
431 Resolution = VgaGetDisplayResolution();
432 Bda->ScreenColumns = Resolution.X;
433 Bda->ScreenRows = Resolution.Y - 1;
434
435 return TRUE;
436 }
437
438 BOOLEAN BiosSetVideoPage(BYTE PageNumber)
439 {
440 /* Check if the page exists */
441 if (PageNumber >= BIOS_MAX_PAGES) return FALSE;
442
443 /* Check if this is the same page */
444 if (PageNumber == Bda->VideoPage) return TRUE;
445
446 /* Set the values in the BDA */
447 Bda->VideoPage = PageNumber;
448 Bda->VideoPageSize = BIOS_PAGE_SIZE;
449 Bda->VideoPageOffset = PageNumber * BIOS_PAGE_SIZE;
450
451 /* Set the start address in the CRTC */
452 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
453 VgaWritePort(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset));
454 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
455 VgaWritePort(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset));
456
457 return TRUE;
458 }
459
460 BOOLEAN BiosInitialize(VOID)
461 {
462 USHORT i;
463 WORD Offset = 0;
464 LPWORD IntVecTable = (LPWORD)BaseAddress;
465 LPBYTE BiosCode = (LPBYTE)SEG_OFF_TO_PTR(BIOS_SEGMENT, 0);
466
467 /* Initialize the BDA */
468 Bda = (PBIOS_DATA_AREA)SEG_OFF_TO_PTR(BDA_SEGMENT, 0);
469 Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
470 /*
471 * Conventional memory size is 640 kB,
472 * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
473 * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
474 * for more information.
475 */
476 Bda->MemorySize = 0x0280;
477 Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
478 Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
479
480 /* Generate ISR stubs and fill the IVT */
481 for (i = 0x00; i <= 0xFF; i++)
482 {
483 IntVecTable[i * 2] = Offset;
484 IntVecTable[i * 2 + 1] = BIOS_SEGMENT;
485
486 BiosCode[Offset++] = 0xFB; // sti
487
488 BiosCode[Offset++] = 0x6A; // push i
489 BiosCode[Offset++] = (UCHAR)i;
490
491 BiosCode[Offset++] = 0x6A; // push 0
492 BiosCode[Offset++] = 0x00;
493
494 // BOP_SEQ:
495 BiosCode[Offset++] = 0xF8; // clc
496
497 BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence
498 BiosCode[Offset++] = HIBYTE(EMULATOR_BOP);
499 BiosCode[Offset++] = EMULATOR_INT_BOP;
500
501 BiosCode[Offset++] = 0x73; // jnc EXIT (offset +3)
502 BiosCode[Offset++] = 0x03;
503
504 // HACK: The following instruction should be HLT!
505 BiosCode[Offset++] = 0x90; // nop
506
507 BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -9)
508 BiosCode[Offset++] = 0xF7;
509
510 // EXIT:
511 BiosCode[Offset++] = 0x83; // add sp, 4
512 BiosCode[Offset++] = 0xC4;
513 BiosCode[Offset++] = 0x04;
514
515 BiosCode[Offset++] = 0xCF; // iret
516 }
517
518 /* Get the input handle to the real console, and check for success */
519 BiosConsoleInput = CreateFileW(L"CONIN$",
520 GENERIC_READ | GENERIC_WRITE,
521 FILE_SHARE_READ | FILE_SHARE_WRITE,
522 NULL,
523 OPEN_EXISTING,
524 0,
525 NULL);
526 if (BiosConsoleInput == INVALID_HANDLE_VALUE)
527 {
528 return FALSE;
529 }
530
531 /* Get the output handle to the real console, and check for success */
532 BiosConsoleOutput = CreateFileW(L"CONOUT$",
533 GENERIC_READ | GENERIC_WRITE,
534 FILE_SHARE_READ | FILE_SHARE_WRITE,
535 NULL,
536 OPEN_EXISTING,
537 0,
538 NULL);
539 if (BiosConsoleOutput == INVALID_HANDLE_VALUE)
540 {
541 CloseHandle(BiosConsoleInput);
542 return FALSE;
543 }
544
545 /* Save the console screen buffer information */
546 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BiosSavedBufferInfo))
547 {
548 CloseHandle(BiosConsoleOutput);
549 CloseHandle(BiosConsoleInput);
550 return FALSE;
551 }
552
553 /* Initialize VGA */
554 if (!VgaInitialize(BiosConsoleOutput))
555 {
556 CloseHandle(BiosConsoleOutput);
557 CloseHandle(BiosConsoleInput);
558 return FALSE;
559 }
560
561 /* Update the cursor position */
562 BiosSetCursorPosition(BiosSavedBufferInfo.dwCursorPosition.Y,
563 BiosSavedBufferInfo.dwCursorPosition.X,
564 0);
565
566 /* Set the console input mode */
567 SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
568
569 /* Start the input thread */
570 InputThread = CreateThread(NULL, 0, &InputThreadProc, BiosConsoleInput, 0, NULL);
571
572 /* Initialize the PIC */
573 PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
574 PicWriteCommand(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
575
576 /* Set the interrupt offsets */
577 PicWriteData(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
578 PicWriteData(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT);
579
580 /* Tell the master PIC there is a slave at IRQ 2 */
581 PicWriteData(PIC_MASTER_DATA, 1 << 2);
582 PicWriteData(PIC_SLAVE_DATA , 2);
583
584 /* Make sure the PIC is in 8086 mode */
585 PicWriteData(PIC_MASTER_DATA, PIC_ICW4_8086);
586 PicWriteData(PIC_SLAVE_DATA , PIC_ICW4_8086);
587
588 /* Clear the masks for both PICs */
589 PicWriteData(PIC_MASTER_DATA, 0x00);
590 PicWriteData(PIC_SLAVE_DATA , 0x00);
591
592 PitWriteCommand(0x34);
593 PitWriteData(0, 0x00);
594 PitWriteData(0, 0x00);
595
596 return TRUE;
597 }
598
599 VOID BiosCleanup(VOID)
600 {
601 /* Restore the old screen buffer */
602 SetConsoleActiveScreenBuffer(BiosConsoleOutput);
603
604 /* Restore the screen buffer size */
605 SetConsoleScreenBufferSize(BiosConsoleOutput, BiosSavedBufferInfo.dwSize);
606
607 /* Close the console handles */
608 if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput);
609 if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput);
610
611 /* Close the input thread handle */
612 if (InputThread != NULL) CloseHandle(InputThread);
613 }
614
615 WORD BiosPeekCharacter(VOID)
616 {
617 WORD CharacterData = 0;
618
619 /* Get the key from the queue, but don't remove it */
620 if (BiosKbdBufferTop(&CharacterData)) return CharacterData;
621 else return 0xFFFF;
622 }
623
624 WORD BiosGetCharacter(VOID)
625 {
626 WORD CharacterData = 0;
627
628 /* Check if there is a key available */
629 if (Bda->KeybdBufferHead != Bda->KeybdBufferTail)
630 {
631 /* Get the key from the queue, and remove it */
632 BiosKbdBufferTop(&CharacterData);
633 BiosKbdBufferPop();
634 }
635 else
636 {
637 /* Set the handler CF to repeat the BOP */
638 EmulatorSetFlag(EMULATOR_FLAG_CF);
639 }
640
641 return CharacterData;
642 }
643
644 VOID BiosGetCursorPosition(PBYTE Row, PBYTE Column, BYTE Page)
645 {
646 /* Make sure the selected video page is valid */
647 if (Page >= BIOS_MAX_PAGES) return;
648
649 /* Get the cursor location */
650 *Row = HIBYTE(Bda->CursorPosition[Page]);
651 *Column = LOBYTE(Bda->CursorPosition[Page]);
652 }
653
654 VOID BiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page)
655 {
656 /* Make sure the selected video page is valid */
657 if (Page >= BIOS_MAX_PAGES) return;
658
659 /* Update the position in the BDA */
660 Bda->CursorPosition[Page] = MAKEWORD(Column, Row);
661
662 /* Check if this is the current video page */
663 if (Page == Bda->VideoPage)
664 {
665 WORD Offset = Row * Bda->ScreenColumns + Column;
666
667 /* Modify the CRTC registers */
668 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
669 VgaWritePort(VGA_CRTC_DATA , LOBYTE(Offset));
670 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
671 VgaWritePort(VGA_CRTC_DATA , HIBYTE(Offset));
672 }
673 }
674
675 BOOLEAN BiosScrollWindow(INT Direction,
676 DWORD Amount,
677 SMALL_RECT Rectangle,
678 BYTE Page,
679 BYTE FillAttribute)
680 {
681 DWORD i;
682 LPWORD WindowData;
683 DWORD WindowSize = (Rectangle.Bottom - Rectangle.Top + 1)
684 * (Rectangle.Right - Rectangle.Left + 1);
685
686 /* Allocate a buffer for the window */
687 WindowData = (LPWORD)HeapAlloc(GetProcessHeap(),
688 HEAP_ZERO_MEMORY,
689 WindowSize * sizeof(WORD));
690 if (WindowData == NULL) return FALSE;
691
692 /* Read the window data */
693 BiosReadWindow(WindowData, Rectangle, Page);
694
695 if (Amount == 0)
696 {
697 /* Fill the window */
698 for (i = 0; i < WindowSize; i++)
699 {
700 WindowData[i] = MAKEWORD(' ', FillAttribute);
701 }
702
703 goto Done;
704 }
705
706 // TODO: Scroll the window!
707
708 Done:
709 /* Write back the window data */
710 BiosWriteWindow(WindowData, Rectangle, Page);
711
712 /* Free the window buffer */
713 HeapFree(GetProcessHeap(), 0, WindowData);
714
715 return TRUE;
716 }
717
718 VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
719 {
720 WORD CharData = MAKEWORD(Character, Attribute);
721 BYTE Row, Column;
722
723 /* Make sure the page exists */
724 if (Page >= BIOS_MAX_PAGES) return;
725
726 /* Get the cursor location */
727 BiosGetCursorPosition(&Row, &Column, Page);
728
729 if (Character == '\a')
730 {
731 /* Bell control character */
732 // NOTE: We may use what the terminal emulator offers to us...
733 Beep(800, 200);
734 return;
735 }
736 else if (Character == '\b')
737 {
738 /* Backspace control character */
739 if (Column > 0)
740 {
741 Column--;
742 }
743 else if (Row > 0)
744 {
745 Column = Bda->ScreenColumns - 1;
746 Row--;
747 }
748
749 /* Erase the existing character */
750 CharData = MAKEWORD(' ', Attribute);
751 EmulatorWriteMemory(&EmulatorContext,
752 TO_LINEAR(TEXT_VIDEO_SEG,
753 Page * Bda->VideoPageSize +
754 (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
755 (LPVOID)&CharData,
756 sizeof(WORD));
757 }
758 else if (Character == '\t')
759 {
760 /* Horizontal Tabulation control character */
761 do
762 {
763 // Taken from DosBox
764 BiosPrintCharacter(' ', Attribute, Page);
765 BiosGetCursorPosition(&Row, &Column, Page);
766 } while (Column % 8);
767 }
768 else if (Character == '\n')
769 {
770 /* Line Feed control character */
771 Row++;
772 }
773 else if (Character == '\r')
774 {
775 /* Carriage Return control character */
776 Column = 0;
777 }
778 else
779 {
780 /* Default character */
781
782 /* Write the character */
783 EmulatorWriteMemory(&EmulatorContext,
784 TO_LINEAR(TEXT_VIDEO_SEG,
785 Page * Bda->VideoPageSize +
786 (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
787 (LPVOID)&CharData,
788 sizeof(WORD));
789
790 /* Advance the cursor */
791 Column++;
792 }
793
794 /* Check if it passed the end of the row */
795 if (Column >= Bda->ScreenColumns)
796 {
797 /* Return to the first column and go to the next line */
798 Column = 0;
799 Row++;
800 }
801
802 /* Scroll the screen up if needed */
803 if (Row > Bda->ScreenRows)
804 {
805 /* The screen must be scrolled up */
806 SMALL_RECT Rectangle = { 0, 0, Bda->ScreenColumns - 1, Bda->ScreenRows };
807
808 BiosScrollWindow(SCROLL_DIRECTION_UP,
809 1,
810 Rectangle,
811 Page,
812 DEFAULT_ATTRIBUTE);
813
814 Row--;
815 }
816
817 /* Set the cursor position */
818 BiosSetCursorPosition(Row, Column, Page);
819 }
820
821 VOID BiosVideoService(LPWORD Stack)
822 {
823 switch (getAH())
824 {
825 /* Set Video Mode */
826 case 0x00:
827 {
828 BiosSetVideoMode(getAL());
829 VgaClearMemory();
830 break;
831 }
832
833 /* Set Text-Mode Cursor Shape */
834 case 0x01:
835 {
836 /* Update the BDA */
837 Bda->CursorStartLine = getCH();
838 Bda->CursorEndLine = getCL();
839
840 /* Modify the CRTC registers */
841 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_START_REG);
842 VgaWritePort(VGA_CRTC_DATA , Bda->CursorStartLine);
843 VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_END_REG);
844 VgaWritePort(VGA_CRTC_DATA , Bda->CursorEndLine);
845
846 break;
847 }
848
849 /* Set Cursor Position */
850 case 0x02:
851 {
852 BiosSetCursorPosition(getDH(), getDL(), getBH());
853 break;
854 }
855
856 /* Get Cursor Position */
857 case 0x03:
858 {
859 /* Make sure the selected video page exists */
860 if (getBH() >= BIOS_MAX_PAGES) break;
861
862 /* Return the result */
863 setAX(0);
864 setCX(MAKEWORD(Bda->CursorEndLine, Bda->CursorStartLine));
865 setDX(Bda->CursorPosition[getBH()]);
866 break;
867 }
868
869 /* Query Light Pen */
870 case 0x04:
871 {
872 /*
873 * On modern BIOSes, this function returns 0
874 * so that we can ignore the other registers.
875 */
876 setAX(0);
877 break;
878 }
879
880 /* Select Active Display Page */
881 case 0x05:
882 {
883 BiosSetVideoPage(getAL());
884 break;
885 }
886
887 /* Scroll Window Up/Down */
888 case 0x06:
889 case 0x07:
890 {
891 SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() };
892
893 /* Call the internal function */
894 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
895 : SCROLL_DIRECTION_DOWN,
896 getAL(),
897 Rectangle,
898 Bda->VideoPage,
899 getBH());
900
901 break;
902 }
903
904 /* Read/Write Character From Cursor Position */
905 case 0x08:
906 case 0x09:
907 case 0x0A:
908 {
909 WORD CharacterData = MAKEWORD(getAL(), getBL());
910 BYTE Page = getBH();
911 DWORD Offset;
912
913 /* Check if the page exists */
914 if (Page >= BIOS_MAX_PAGES) break;
915
916 /* Find the offset of the character */
917 Offset = Page * Bda->VideoPageSize +
918 (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns +
919 LOBYTE(Bda->CursorPosition[Page])) * 2;
920
921 if (getAH() == 0x08)
922 {
923 /* Read from the video memory */
924 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
925 (LPVOID)&CharacterData,
926 sizeof(WORD));
927
928 /* Return the character in AX */
929 setAX(CharacterData);
930 }
931 else
932 {
933 /* Write to video memory */
934 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
935 (LPVOID)&CharacterData,
936 (getBH() == 0x09) ? sizeof(WORD) : sizeof(BYTE));
937 }
938
939 break;
940 }
941
942 /* Teletype Output */
943 case 0x0E:
944 {
945 BiosPrintCharacter(getAL(), getBL(), getBH());
946 break;
947 }
948
949 /* Get Current Video Mode */
950 case 0x0F:
951 {
952 setAX(MAKEWORD(Bda->VideoMode, Bda->ScreenColumns));
953 setBX(MAKEWORD(getBL(), Bda->VideoPage));
954 break;
955 }
956
957 /* Palette Control */
958 case 0x10:
959 {
960 switch (getAL())
961 {
962 /* Set Single Palette Register */
963 case 0x00:
964 {
965 /* Reset the flip-flop */
966 VgaReadPort(VGA_STAT_COLOR);
967
968 /* Write the index */
969 VgaWritePort(VGA_AC_INDEX, getBL());
970
971 /* Write the data */
972 VgaWritePort(VGA_AC_WRITE, getBH());
973
974 break;
975 }
976
977 /* Set Overscan Color */
978 case 0x01:
979 {
980 /* Reset the flip-flop */
981 VgaReadPort(VGA_STAT_COLOR);
982
983 /* Write the index */
984 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
985
986 /* Write the data */
987 VgaWritePort(VGA_AC_WRITE, getBH());
988
989 break;
990 }
991
992 /* Set All Palette Registers */
993 case 0x02:
994 {
995 INT i;
996 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
997
998 /* Set the palette registers */
999 for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
1000 {
1001 /* Reset the flip-flop */
1002 VgaReadPort(VGA_STAT_COLOR);
1003
1004 /* Write the index */
1005 VgaWritePort(VGA_AC_INDEX, i);
1006
1007 /* Write the data */
1008 VgaWritePort(VGA_AC_WRITE, Buffer[i]);
1009 }
1010
1011 /* Set the overscan register */
1012 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
1013 VgaWritePort(VGA_AC_WRITE, Buffer[VGA_AC_PAL_F_REG + 1]);
1014
1015 break;
1016 }
1017
1018 /* Get Single Palette Register */
1019 case 0x07:
1020 {
1021 /* Reset the flip-flop */
1022 VgaReadPort(VGA_STAT_COLOR);
1023
1024 /* Write the index */
1025 VgaWritePort(VGA_AC_INDEX, getBL());
1026
1027 /* Read the data */
1028 setBH(VgaReadPort(VGA_AC_READ));
1029
1030 break;
1031 }
1032
1033 /* Get Overscan Color */
1034 case 0x08:
1035 {
1036 /* Reset the flip-flop */
1037 VgaReadPort(VGA_STAT_COLOR);
1038
1039 /* Write the index */
1040 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
1041
1042 /* Read the data */
1043 setBH(VgaReadPort(VGA_AC_READ));
1044
1045 break;
1046 }
1047
1048 /* Get All Palette Registers */
1049 case 0x09:
1050 {
1051 INT i;
1052 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
1053
1054 /* Get the palette registers */
1055 for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
1056 {
1057 /* Reset the flip-flop */
1058 VgaReadPort(VGA_STAT_COLOR);
1059
1060 /* Write the index */
1061 VgaWritePort(VGA_AC_INDEX, i);
1062
1063 /* Read the data */
1064 Buffer[i] = VgaReadPort(VGA_AC_READ);
1065 }
1066
1067 /* Get the overscan register */
1068 VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
1069 Buffer[VGA_AC_PAL_F_REG + 1] = VgaReadPort(VGA_AC_READ);
1070
1071 break;
1072 }
1073
1074 /* Set Individual DAC Register */
1075 case 0x10:
1076 {
1077 /* Write the index */
1078 // Certainly in BL and not in BX as said by Ralf Brown...
1079 VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
1080
1081 /* Write the data in this order: Red, Green, Blue */
1082 VgaWritePort(VGA_DAC_DATA, getDH());
1083 VgaWritePort(VGA_DAC_DATA, getCH());
1084 VgaWritePort(VGA_DAC_DATA, getCL());
1085
1086 break;
1087 }
1088
1089 /* Set Block of DAC Registers */
1090 case 0x12:
1091 {
1092 INT i;
1093 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
1094
1095 /* Write the index */
1096 // Certainly in BL and not in BX as said by Ralf Brown...
1097 VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
1098
1099 for (i = 0; i < getCX(); i++)
1100 {
1101 /* Write the data in this order: Red, Green, Blue */
1102 VgaWritePort(VGA_DAC_DATA, *Buffer++);
1103 VgaWritePort(VGA_DAC_DATA, *Buffer++);
1104 VgaWritePort(VGA_DAC_DATA, *Buffer++);
1105 }
1106
1107 break;
1108 }
1109
1110 /* Get Individual DAC Register */
1111 case 0x15:
1112 {
1113 /* Write the index */
1114 VgaWritePort(VGA_DAC_READ_INDEX, getBL());
1115
1116 /* Read the data in this order: Red, Green, Blue */
1117 setDH(VgaReadPort(VGA_DAC_DATA));
1118 setCH(VgaReadPort(VGA_DAC_DATA));
1119 setCL(VgaReadPort(VGA_DAC_DATA));
1120
1121 break;
1122 }
1123
1124 /* Get Block of DAC Registers */
1125 case 0x17:
1126 {
1127 INT i;
1128 LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
1129
1130 /* Write the index */
1131 // Certainly in BL and not in BX as said by Ralf Brown...
1132 VgaWritePort(VGA_DAC_READ_INDEX, getBL());
1133
1134 for (i = 0; i < getCX(); i++)
1135 {
1136 /* Write the data in this order: Red, Green, Blue */
1137 *Buffer++ = VgaReadPort(VGA_DAC_DATA);
1138 *Buffer++ = VgaReadPort(VGA_DAC_DATA);
1139 *Buffer++ = VgaReadPort(VGA_DAC_DATA);
1140 }
1141
1142 break;
1143 }
1144
1145 default:
1146 {
1147 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1148 getAL());
1149 break;
1150 }
1151 }
1152
1153 break;
1154 }
1155
1156 /* Scroll Window */
1157 case 0x12:
1158 {
1159 SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() };
1160
1161 /* Call the internal function */
1162 BiosScrollWindow(getBL(),
1163 getAL(),
1164 Rectangle,
1165 Bda->VideoPage,
1166 DEFAULT_ATTRIBUTE);
1167
1168 break;
1169 }
1170
1171 /* Display combination code */
1172 case 0x1A:
1173 {
1174 switch(getAL())
1175 {
1176 case 0x00: /* Get Display combiantion code */
1177 setAX(MAKEWORD(0x1A, 0x1A));
1178 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1179 break;
1180 case 0x01: /* Set Display combination code */
1181 DPRINT1("Set Display combination code - Unsupported\n");
1182 break;
1183 default:
1184 break;
1185 }
1186 break;
1187 }
1188
1189 default:
1190 {
1191 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1192 getAH());
1193 }
1194 }
1195 }
1196
1197 VOID BiosEquipmentService(LPWORD Stack)
1198 {
1199 /* Return the equipment list */
1200 setAX(Bda->EquipmentList);
1201 }
1202
1203 VOID BiosGetMemorySize(LPWORD Stack)
1204 {
1205 /* Return the conventional memory size in kB, typically 640 kB */
1206 setAX(Bda->MemorySize);
1207 }
1208
1209 VOID BiosKeyboardService(LPWORD Stack)
1210 {
1211 switch (getAH())
1212 {
1213 /* Wait for keystroke and read */
1214 case 0x00:
1215 /* Wait for extended keystroke and read */
1216 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1217 {
1218 /* Read the character (and wait if necessary) */
1219 setAX(BiosGetCharacter());
1220 break;
1221 }
1222
1223 /* Get keystroke status */
1224 case 0x01:
1225 /* Get extended keystroke status */
1226 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1227 {
1228 WORD Data = BiosPeekCharacter();
1229
1230 if (Data != 0xFFFF)
1231 {
1232 /* There is a character, clear ZF and return it */
1233 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
1234 setAX(Data);
1235 }
1236 else
1237 {
1238 /* No character, set ZF */
1239 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
1240 }
1241
1242 break;
1243 }
1244
1245 /* Get shift status */
1246 case 0x02:
1247 {
1248 /* Return the lower byte of the keyboard shift status word */
1249 setAL(LOBYTE(Bda->KeybdShiftFlags));
1250 break;
1251 }
1252
1253 /* Reserved */
1254 case 0x04:
1255 {
1256 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1257 break;
1258 }
1259
1260 /* Push keystroke */
1261 case 0x05:
1262 {
1263 /* Return 0 if success, 1 if failure */
1264 setAL(BiosKbdBufferPush(getCX()) == FALSE);
1265 break;
1266 }
1267
1268 /* Get extended shift status */
1269 case 0x12:
1270 {
1271 /*
1272 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1273 * but the high byte is organized differently:
1274 * the bytes 2 and 3 of the high byte are not the same...
1275 */
1276 WORD KeybdShiftFlags = (Bda->KeybdShiftFlags & 0xF3FF);
1277
1278 /* Return the extended keyboard shift status word */
1279 setAX(KeybdShiftFlags);
1280 break;
1281 }
1282
1283 default:
1284 {
1285 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1286 getAH());
1287 }
1288 }
1289 }
1290
1291 VOID BiosTimeService(LPWORD Stack)
1292 {
1293 switch (getAH())
1294 {
1295 case 0x00:
1296 {
1297 /* Set AL to 1 if midnight had passed, 0 otherwise */
1298 setAL(Bda->MidnightPassed ? 0x01 : 0x00);
1299
1300 /* Return the tick count in CX:DX */
1301 setCX(HIWORD(Bda->TickCounter));
1302 setDX(LOWORD(Bda->TickCounter));
1303
1304 /* Reset the midnight flag */
1305 Bda->MidnightPassed = FALSE;
1306
1307 break;
1308 }
1309
1310 case 0x01:
1311 {
1312 /* Set the tick count to CX:DX */
1313 Bda->TickCounter = MAKELONG(getDX(), getCX());
1314
1315 /* Reset the midnight flag */
1316 Bda->MidnightPassed = FALSE;
1317
1318 break;
1319 }
1320
1321 default:
1322 {
1323 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1324 getAH());
1325 }
1326 }
1327 }
1328
1329 VOID BiosSystemTimerInterrupt(LPWORD Stack)
1330 {
1331 /* Increase the system tick count */
1332 Bda->TickCounter++;
1333 }
1334
1335 VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
1336 {
1337 switch (IrqNumber)
1338 {
1339 /* PIT IRQ */
1340 case 0:
1341 {
1342 /* Perform the system timer interrupt */
1343 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT);
1344 break;
1345 }
1346
1347 /* Keyboard IRQ */
1348 case 1:
1349 {
1350 BYTE ScanCode, VirtualKey;
1351 WORD Character;
1352
1353 /* Loop while there is a scancode available */
1354 do
1355 {
1356 /* Get the scan code and virtual key code */
1357 ScanCode = KeyboardReadData();
1358 VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
1359
1360 /* Check if this is a key press or release */
1361 if (!(ScanCode & (1 << 7)))
1362 {
1363 /* Key press */
1364 if (VirtualKey == VK_NUMLOCK ||
1365 VirtualKey == VK_CAPITAL ||
1366 VirtualKey == VK_SCROLL ||
1367 VirtualKey == VK_INSERT)
1368 {
1369 /* For toggle keys, toggle the lowest bit in the keyboard map */
1370 BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
1371 }
1372
1373 /* Set the highest bit */
1374 BiosKeyboardMap[VirtualKey] |= (1 << 7);
1375
1376 /* Find out which character this is */
1377 Character = 0;
1378 if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
1379 {
1380 /* Not ASCII */
1381 Character = 0;
1382 }
1383
1384 /* Push it onto the BIOS keyboard queue */
1385 BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
1386 }
1387 else
1388 {
1389 /* Key release, unset the highest bit */
1390 BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
1391 }
1392 }
1393 while (KeyboardReadStatus() & 1);
1394
1395 /* Clear the keyboard flags */
1396 Bda->KeybdShiftFlags = 0;
1397
1398 /* Set the appropriate flags based on the state */
1399 if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
1400 if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
1401 if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
1402 if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
1403 if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
1404 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
1405 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
1406 if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
1407 if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT;
1408 if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
1409 if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
1410 if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
1411 if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
1412 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
1413 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
1414 if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
1415
1416 break;
1417 }
1418 }
1419
1420 /* Send End-of-Interrupt to the PIC */
1421 if (IrqNumber >= 8) PicWriteCommand(PIC_SLAVE_CMD, PIC_OCW2_EOI);
1422 PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
1423 }
1424
1425 /* EOF */