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