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