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