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