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