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