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