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