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