2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
22 #include "registers.h"
24 /* PRIVATE VARIABLES **********************************************************/
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 static HANDLE InputThread
= NULL
;
34 * VGA Register Configurations for BIOS Video Modes
35 * The configurations come from DosBox.
37 static BYTE VideoMode_40x25_text
[] =
39 /* Miscellaneous Register */
42 /* Sequencer Registers */
43 0x00, 0x08, 0x03, 0x00, 0x07,
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
49 0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
50 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
54 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
55 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
58 static BYTE VideoMode_80x25_text
[] =
60 /* Miscellaneous Register */
63 /* Sequencer Registers */
64 0x00, 0x00, 0x03, 0x00, 0x07,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
70 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
71 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
75 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
76 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
79 static BYTE VideoMode_320x200_4color
[] =
81 /* Miscellaneous Register */
84 /* Sequencer Registers */
85 0x00, 0x09, 0x00, 0x00, 0x02,
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
91 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
96 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
97 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
100 static BYTE VideoMode_640x200_2color
[] =
102 /* Miscellaneous Register */
105 /* Sequencer Registers */
106 0x00, 0x09, 0x0F, 0x00, 0x02,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
112 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
117 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
118 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00
121 static BYTE VideoMode_320x200_16color
[] =
123 /* Miscellaneous Register */
126 /* Sequencer Registers */
127 0x00, 0x09, 0x0F, 0x00, 0x02,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
133 0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
138 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
139 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
142 static BYTE VideoMode_640x200_16color
[] =
144 /* Miscellaneous Register */
147 /* Sequencer Registers */
148 0x00, 0x01, 0x0F, 0x00, 0x02,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
154 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
159 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
160 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
163 static BYTE VideoMode_640x350_16color
[] =
165 /* Miscellaneous Register */
168 /* Sequencer Registers */
169 0x00, 0x01, 0x0F, 0x00, 0x02,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
175 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
180 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
181 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
184 static BYTE VideoMode_640x480_2color
[] =
186 /* Miscellaneous Register */
189 /* Sequencer Registers */
190 0x00, 0x01, 0x0F, 0x00, 0x02,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
196 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
201 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
202 0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
205 static BYTE VideoMode_640x480_16color
[] =
207 /* Miscellaneous Register */
210 /* Sequencer Registers */
211 0x00, 0x01, 0x0F, 0x00, 0x02,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
217 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
222 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
223 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
226 static BYTE VideoMode_320x200_256color
[] =
228 /* Miscellaneous Register */
231 /* Sequencer Registers */
232 0x00, 0x01, 0x0F, 0x00, 0x0E,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
238 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
243 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
244 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
247 static LPBYTE VideoModes
[] =
249 VideoMode_40x25_text
, /* Mode 00h */
250 VideoMode_40x25_text
, /* Mode 01h */
251 VideoMode_80x25_text
, /* Mode 02h */
252 VideoMode_80x25_text
, /* Mode 03h */
253 VideoMode_320x200_4color
, /* Mode 04h */
254 VideoMode_320x200_4color
, /* Mode 05h */
255 VideoMode_640x200_2color
, /* Mode 06h */
262 VideoMode_320x200_16color
, /* Mode 0Dh */
263 VideoMode_640x200_16color
, /* Mode 0Eh */
265 VideoMode_640x350_16color
, /* Mode 10h */
266 VideoMode_640x480_2color
, /* Mode 11h */
267 VideoMode_640x480_16color
, /* Mode 12h */
268 VideoMode_320x200_256color
, /* Mode 13h */
271 /* PRIVATE FUNCTIONS **********************************************************/
273 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
275 /* Get the location of the element after the tail */
276 WORD NextElement
= Bda
->KeybdBufferTail
+ sizeof(WORD
);
278 /* Wrap it around if it's at or beyond the end */
279 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
281 /* If it's full, fail */
282 if (NextElement
== Bda
->KeybdBufferHead
) return FALSE
;
284 /* Put the value in the queue */
285 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
286 Bda
->KeybdBufferTail
+= sizeof(WORD
);
288 /* Check if we are at, or have passed, the end of the buffer */
289 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
291 /* Return it to the beginning */
292 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
299 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
301 /* If it's empty, fail */
302 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
304 /* Otherwise, get the value and return success */
305 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
310 static BOOLEAN
BiosKbdBufferPop(VOID
)
312 /* If it's empty, fail */
313 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
315 /* Remove the value from the queue */
316 Bda
->KeybdBufferHead
+= sizeof(WORD
);
318 /* Check if we are at, or have passed, the end of the buffer */
319 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
321 /* Return it to the beginning */
322 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
329 static VOID
BiosReadWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
334 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
336 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
338 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
340 /* Read from video memory */
341 VgaReadMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
345 /* Write the data to the buffer in row order */
346 Buffer
[Counter
++] = Character
;
351 static VOID
BiosWriteWindow(LPWORD Buffer
, SMALL_RECT Rectangle
, BYTE Page
)
356 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Page
* Bda
->VideoPageSize
);
358 for (i
= Rectangle
.Top
; i
<= Rectangle
.Bottom
; i
++)
360 for (j
= Rectangle
.Left
; j
<= Rectangle
.Right
; j
++)
362 Character
= Buffer
[Counter
++];
364 /* Write to video memory */
365 VgaWriteMemory(VideoAddress
+ (i
* Bda
->ScreenColumns
+ j
) * sizeof(WORD
),
372 /* PUBLIC FUNCTIONS ***********************************************************/
374 BYTE
BiosGetVideoMode(VOID
)
376 return Bda
->VideoMode
;
379 BOOLEAN
BiosSetVideoMode(BYTE ModeNumber
)
383 LPBYTE Values
= VideoModes
[ModeNumber
];
385 DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber
, Values
);
387 if (Values
== NULL
) return FALSE
;
389 /* Write the misc register */
390 VgaWritePort(VGA_MISC_WRITE
, *(Values
++));
392 /* Write the sequencer registers */
393 for (i
= 0; i
< VGA_SEQ_MAX_REG
; i
++)
395 VgaWritePort(VGA_SEQ_INDEX
, i
);
396 VgaWritePort(VGA_SEQ_DATA
, *(Values
++));
399 /* Write the GC registers */
400 for (i
= 0; i
< VGA_GC_MAX_REG
; i
++)
402 VgaWritePort(VGA_GC_INDEX
, i
);
403 VgaWritePort(VGA_GC_DATA
, *(Values
++));
406 /* Write the CRTC registers */
407 for (i
= 0; i
< VGA_CRTC_MAX_REG
; i
++)
409 VgaWritePort(VGA_CRTC_INDEX
, i
);
410 VgaWritePort(VGA_CRTC_DATA
, *(Values
++));
413 /* Write the AC registers */
414 for (i
= 0; i
< VGA_AC_MAX_REG
; i
++)
416 VgaWritePort(VGA_AC_INDEX
, i
);
417 VgaWritePort(VGA_AC_WRITE
, *(Values
++));
420 /* Reset the palette */
423 /* Update the values in the BDA */
424 Bda
->VideoMode
= ModeNumber
;
426 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
427 Bda
->VideoPageOffset
= 0;
429 /* Get the character height */
430 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_MAX_SCAN_LINE_REG
);
431 Bda
->CharacterHeight
= 1 + (VgaReadPort(VGA_CRTC_DATA
) & 0x1F);
433 Resolution
= VgaGetDisplayResolution();
434 Bda
->ScreenColumns
= Resolution
.X
;
435 Bda
->ScreenRows
= Resolution
.Y
- 1;
440 BOOLEAN
BiosSetVideoPage(BYTE PageNumber
)
442 /* Check if the page exists */
443 if (PageNumber
>= BIOS_MAX_PAGES
) return FALSE
;
445 /* Check if this is the same page */
446 if (PageNumber
== Bda
->VideoPage
) return TRUE
;
448 /* Set the values in the BDA */
449 Bda
->VideoPage
= PageNumber
;
450 Bda
->VideoPageSize
= BIOS_PAGE_SIZE
;
451 Bda
->VideoPageOffset
= PageNumber
* BIOS_PAGE_SIZE
;
453 /* Set the start address in the CRTC */
454 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
455 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Bda
->VideoPageOffset
));
456 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
457 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Bda
->VideoPageOffset
));
462 BOOLEAN
BiosInitialize(VOID
)
464 /* Initialize the BDA */
465 Bda
= (PBIOS_DATA_AREA
)SEG_OFF_TO_PTR(BDA_SEGMENT
, 0);
466 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
468 * Conventional memory size is 640 kB,
469 * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
470 * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
471 * for more information.
473 Bda
->MemorySize
= 0x0280;
474 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
475 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
477 /* Initialize the 32-bit Interrupt system */
478 InitializeInt32(BIOS_SEGMENT
);
480 /* Register the BIOS 32-bit Interrupts */
481 RegisterInt32(BIOS_VIDEO_INTERRUPT
, BiosVideoService
);
482 RegisterInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
483 RegisterInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
484 RegisterInt32(BIOS_MISC_INTERRUPT
, BiosMiscService
);
485 RegisterInt32(BIOS_KBD_INTERRUPT
, BiosKeyboardService
);
486 RegisterInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
487 RegisterInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
489 /* Get the input handle to the real console, and check for success */
490 BiosConsoleInput
= CreateFileW(L
"CONIN$",
491 GENERIC_READ
| GENERIC_WRITE
,
492 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
497 if (BiosConsoleInput
== INVALID_HANDLE_VALUE
)
502 /* Get the output handle to the real console, and check for success */
503 BiosConsoleOutput
= CreateFileW(L
"CONOUT$",
504 GENERIC_READ
| GENERIC_WRITE
,
505 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
510 if (BiosConsoleOutput
== INVALID_HANDLE_VALUE
)
512 CloseHandle(BiosConsoleInput
);
516 /* Save the console screen buffer information */
517 if (!GetConsoleScreenBufferInfo(BiosConsoleOutput
, &BiosSavedBufferInfo
))
519 CloseHandle(BiosConsoleOutput
);
520 CloseHandle(BiosConsoleInput
);
525 if (!VgaInitialize(BiosConsoleOutput
))
527 CloseHandle(BiosConsoleOutput
);
528 CloseHandle(BiosConsoleInput
);
532 /* Update the cursor position */
533 BiosSetCursorPosition(BiosSavedBufferInfo
.dwCursorPosition
.Y
,
534 BiosSavedBufferInfo
.dwCursorPosition
.X
,
537 /* Set the console input mode */
538 SetConsoleMode(BiosConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
540 /* Start the input thread */
541 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, BiosConsoleInput
, 0, NULL
);
543 /* Initialize the PIC */
544 PicWriteCommand(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
545 PicWriteCommand(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
547 /* Set the interrupt offsets */
548 PicWriteData(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
549 PicWriteData(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
551 /* Tell the master PIC there is a slave at IRQ 2 */
552 PicWriteData(PIC_MASTER_DATA
, 1 << 2);
553 PicWriteData(PIC_SLAVE_DATA
, 2);
555 /* Make sure the PIC is in 8086 mode */
556 PicWriteData(PIC_MASTER_DATA
, PIC_ICW4_8086
);
557 PicWriteData(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
559 /* Clear the masks for both PICs */
560 PicWriteData(PIC_MASTER_DATA
, 0x00);
561 PicWriteData(PIC_SLAVE_DATA
, 0x00);
563 PitWriteCommand(0x34);
564 PitWriteData(0, 0x00);
565 PitWriteData(0, 0x00);
570 VOID
BiosCleanup(VOID
)
572 /* Close the input thread handle */
573 if (InputThread
!= NULL
) CloseHandle(InputThread
);
575 /* Restore the old screen buffer */
576 SetConsoleActiveScreenBuffer(BiosConsoleOutput
);
578 /* Restore the screen buffer size */
579 SetConsoleScreenBufferSize(BiosConsoleOutput
, BiosSavedBufferInfo
.dwSize
);
581 /* Close the console handles */
582 if (BiosConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleOutput
);
583 if (BiosConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(BiosConsoleInput
);
586 WORD
BiosPeekCharacter(VOID
)
588 WORD CharacterData
= 0;
590 /* Get the key from the queue, but don't remove it */
591 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
595 WORD
BiosGetCharacter(VOID
)
597 WORD CharacterData
= 0;
599 /* Check if there is a key available */
600 if (Bda
->KeybdBufferHead
!= Bda
->KeybdBufferTail
)
602 /* Get the key from the queue, and remove it */
603 BiosKbdBufferTop(&CharacterData
);
608 /* Set the handler CF to repeat the BOP */
609 EmulatorSetFlag(EMULATOR_FLAG_CF
);
612 return CharacterData
;
615 VOID
BiosGetCursorPosition(PBYTE Row
, PBYTE Column
, BYTE Page
)
617 /* Make sure the selected video page is valid */
618 if (Page
>= BIOS_MAX_PAGES
) return;
620 /* Get the cursor location */
621 *Row
= HIBYTE(Bda
->CursorPosition
[Page
]);
622 *Column
= LOBYTE(Bda
->CursorPosition
[Page
]);
625 VOID
BiosSetCursorPosition(BYTE Row
, BYTE Column
, BYTE Page
)
627 /* Make sure the selected video page is valid */
628 if (Page
>= BIOS_MAX_PAGES
) return;
630 /* Update the position in the BDA */
631 Bda
->CursorPosition
[Page
] = MAKEWORD(Column
, Row
);
633 /* Check if this is the current video page */
634 if (Page
== Bda
->VideoPage
)
636 WORD Offset
= Row
* Bda
->ScreenColumns
+ Column
;
638 /* Modify the CRTC registers */
639 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_LOW_REG
);
640 VgaWritePort(VGA_CRTC_DATA
, LOBYTE(Offset
));
641 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_LOC_HIGH_REG
);
642 VgaWritePort(VGA_CRTC_DATA
, HIBYTE(Offset
));
646 BOOLEAN
BiosScrollWindow(INT Direction
,
648 SMALL_RECT Rectangle
,
654 WORD WindowWidth
= Rectangle
.Right
- Rectangle
.Left
+ 1;
655 WORD WindowHeight
= Rectangle
.Bottom
- Rectangle
.Top
+ 1;
656 DWORD WindowSize
= WindowWidth
* WindowHeight
;
658 /* Allocate a buffer for the window */
659 WindowData
= (LPWORD
)HeapAlloc(GetProcessHeap(),
661 WindowSize
* sizeof(WORD
));
662 if (WindowData
== NULL
) return FALSE
;
664 /* Read the window data */
665 BiosReadWindow(WindowData
, Rectangle
, Page
);
668 || (((Direction
== SCROLL_DIRECTION_UP
)
669 || (Direction
== SCROLL_DIRECTION_DOWN
))
670 && (Amount
>= WindowHeight
))
671 || (((Direction
== SCROLL_DIRECTION_LEFT
)
672 || (Direction
== SCROLL_DIRECTION_RIGHT
))
673 && (Amount
>= WindowWidth
)))
675 /* Fill the window */
676 for (i
= 0; i
< WindowSize
; i
++)
678 WindowData
[i
] = MAKEWORD(' ', FillAttribute
);
686 case SCROLL_DIRECTION_UP
:
688 RtlMoveMemory(WindowData
,
689 &WindowData
[WindowWidth
* Amount
],
690 (WindowSize
- WindowWidth
* Amount
) * sizeof(WORD
));
692 for (i
= 0; i
< Amount
* WindowWidth
; i
++)
694 WindowData
[WindowSize
- i
- 1] = MAKEWORD(' ', FillAttribute
);
700 case SCROLL_DIRECTION_DOWN
:
702 RtlMoveMemory(&WindowData
[WindowWidth
* Amount
],
704 (WindowSize
- WindowWidth
* Amount
) * sizeof(WORD
));
706 for (i
= 0; i
< Amount
* WindowWidth
; i
++)
708 WindowData
[i
] = MAKEWORD(' ', FillAttribute
);
716 // TODO: NOT IMPLEMENTED!
722 /* Write back the window data */
723 BiosWriteWindow(WindowData
, Rectangle
, Page
);
725 /* Free the window buffer */
726 HeapFree(GetProcessHeap(), 0, WindowData
);
731 VOID
BiosPrintCharacter(CHAR Character
, BYTE Attribute
, BYTE Page
)
733 WORD CharData
= MAKEWORD(Character
, Attribute
);
736 /* Make sure the page exists */
737 if (Page
>= BIOS_MAX_PAGES
) return;
739 /* Get the cursor location */
740 BiosGetCursorPosition(&Row
, &Column
, Page
);
742 if (Character
== '\a')
744 /* Bell control character */
745 // NOTE: We may use what the terminal emulator offers to us...
749 else if (Character
== '\b')
751 /* Backspace control character */
758 Column
= Bda
->ScreenColumns
- 1;
762 /* Erase the existing character */
763 CharData
= MAKEWORD(' ', Attribute
);
764 EmulatorWriteMemory(&EmulatorContext
,
765 TO_LINEAR(TEXT_VIDEO_SEG
,
766 Page
* Bda
->VideoPageSize
+
767 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
771 else if (Character
== '\t')
773 /* Horizontal Tabulation control character */
777 BiosPrintCharacter(' ', Attribute
, Page
);
778 BiosGetCursorPosition(&Row
, &Column
, Page
);
779 } while (Column
% 8);
781 else if (Character
== '\n')
783 /* Line Feed control character */
786 else if (Character
== '\r')
788 /* Carriage Return control character */
793 /* Default character */
795 /* Write the character */
796 EmulatorWriteMemory(&EmulatorContext
,
797 TO_LINEAR(TEXT_VIDEO_SEG
,
798 Page
* Bda
->VideoPageSize
+
799 (Row
* Bda
->ScreenColumns
+ Column
) * sizeof(WORD
)),
803 /* Advance the cursor */
807 /* Check if it passed the end of the row */
808 if (Column
>= Bda
->ScreenColumns
)
810 /* Return to the first column and go to the next line */
815 /* Scroll the screen up if needed */
816 if (Row
> Bda
->ScreenRows
)
818 /* The screen must be scrolled up */
819 SMALL_RECT Rectangle
= { 0, 0, Bda
->ScreenColumns
- 1, Bda
->ScreenRows
};
821 BiosScrollWindow(SCROLL_DIRECTION_UP
,
830 /* Set the cursor position */
831 BiosSetCursorPosition(Row
, Column
, Page
);
834 VOID WINAPI
BiosVideoService(LPWORD Stack
)
841 BiosSetVideoMode(getAL());
846 /* Set Text-Mode Cursor Shape */
850 Bda
->CursorStartLine
= getCH();
851 Bda
->CursorEndLine
= getCL();
853 /* Modify the CRTC registers */
854 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_START_REG
);
855 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorStartLine
);
856 VgaWritePort(VGA_CRTC_INDEX
, VGA_CRTC_CURSOR_END_REG
);
857 VgaWritePort(VGA_CRTC_DATA
, Bda
->CursorEndLine
);
862 /* Set Cursor Position */
865 BiosSetCursorPosition(getDH(), getDL(), getBH());
869 /* Get Cursor Position */
872 /* Make sure the selected video page exists */
873 if (getBH() >= BIOS_MAX_PAGES
) break;
875 /* Return the result */
877 setCX(MAKEWORD(Bda
->CursorEndLine
, Bda
->CursorStartLine
));
878 setDX(Bda
->CursorPosition
[getBH()]);
882 /* Query Light Pen */
886 * On modern BIOSes, this function returns 0
887 * so that we can ignore the other registers.
893 /* Select Active Display Page */
896 BiosSetVideoPage(getAL());
900 /* Scroll Window Up/Down */
904 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
906 /* Call the internal function */
907 BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
908 : SCROLL_DIRECTION_DOWN
,
917 /* Read/Write Character From Cursor Position */
922 WORD CharacterData
= MAKEWORD(getAL(), getBL());
926 /* Check if the page exists */
927 if (Page
>= BIOS_MAX_PAGES
) break;
929 /* Find the offset of the character */
930 Offset
= Page
* Bda
->VideoPageSize
+
931 (HIBYTE(Bda
->CursorPosition
[Page
]) * Bda
->ScreenColumns
+
932 LOBYTE(Bda
->CursorPosition
[Page
])) * 2;
936 /* Read from the video memory */
937 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
938 (LPVOID
)&CharacterData
,
941 /* Return the character in AX */
942 setAX(CharacterData
);
946 /* Write to video memory */
947 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG
, Offset
),
948 (LPVOID
)&CharacterData
,
949 (getBH() == 0x09) ? sizeof(WORD
) : sizeof(BYTE
));
955 /* Teletype Output */
958 BiosPrintCharacter(getAL(), getBL(), getBH());
962 /* Get Current Video Mode */
965 setAX(MAKEWORD(Bda
->VideoMode
, Bda
->ScreenColumns
));
966 setBX(MAKEWORD(getBL(), Bda
->VideoPage
));
970 /* Palette Control */
975 /* Set Single Palette Register */
978 /* Reset the flip-flop */
979 VgaReadPort(VGA_STAT_COLOR
);
981 /* Write the index */
982 VgaWritePort(VGA_AC_INDEX
, getBL());
985 VgaWritePort(VGA_AC_WRITE
, getBH());
990 /* Set Overscan Color */
993 /* Reset the flip-flop */
994 VgaReadPort(VGA_STAT_COLOR
);
996 /* Write the index */
997 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1000 VgaWritePort(VGA_AC_WRITE
, getBH());
1005 /* Set All Palette Registers */
1009 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1011 /* Set the palette registers */
1012 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1014 /* Reset the flip-flop */
1015 VgaReadPort(VGA_STAT_COLOR
);
1017 /* Write the index */
1018 VgaWritePort(VGA_AC_INDEX
, i
);
1020 /* Write the data */
1021 VgaWritePort(VGA_AC_WRITE
, Buffer
[i
]);
1024 /* Set the overscan register */
1025 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1026 VgaWritePort(VGA_AC_WRITE
, Buffer
[VGA_AC_PAL_F_REG
+ 1]);
1031 /* Get Single Palette Register */
1034 /* Reset the flip-flop */
1035 VgaReadPort(VGA_STAT_COLOR
);
1037 /* Write the index */
1038 VgaWritePort(VGA_AC_INDEX
, getBL());
1041 setBH(VgaReadPort(VGA_AC_READ
));
1046 /* Get Overscan Color */
1049 /* Reset the flip-flop */
1050 VgaReadPort(VGA_STAT_COLOR
);
1052 /* Write the index */
1053 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1056 setBH(VgaReadPort(VGA_AC_READ
));
1061 /* Get All Palette Registers */
1065 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1067 /* Get the palette registers */
1068 for (i
= 0; i
<= VGA_AC_PAL_F_REG
; i
++)
1070 /* Reset the flip-flop */
1071 VgaReadPort(VGA_STAT_COLOR
);
1073 /* Write the index */
1074 VgaWritePort(VGA_AC_INDEX
, i
);
1077 Buffer
[i
] = VgaReadPort(VGA_AC_READ
);
1080 /* Get the overscan register */
1081 VgaWritePort(VGA_AC_INDEX
, VGA_AC_OVERSCAN_REG
);
1082 Buffer
[VGA_AC_PAL_F_REG
+ 1] = VgaReadPort(VGA_AC_READ
);
1087 /* Set Individual DAC Register */
1090 /* Write the index */
1091 // Certainly in BL and not in BX as said by Ralf Brown...
1092 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1094 /* Write the data in this order: Red, Green, Blue */
1095 VgaWritePort(VGA_DAC_DATA
, getDH());
1096 VgaWritePort(VGA_DAC_DATA
, getCH());
1097 VgaWritePort(VGA_DAC_DATA
, getCL());
1102 /* Set Block of DAC Registers */
1106 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1108 /* Write the index */
1109 // Certainly in BL and not in BX as said by Ralf Brown...
1110 VgaWritePort(VGA_DAC_WRITE_INDEX
, getBL());
1112 for (i
= 0; i
< getCX(); i
++)
1114 /* Write the data in this order: Red, Green, Blue */
1115 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1116 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1117 VgaWritePort(VGA_DAC_DATA
, *Buffer
++);
1123 /* Get Individual DAC Register */
1126 /* Write the index */
1127 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1129 /* Read the data in this order: Red, Green, Blue */
1130 setDH(VgaReadPort(VGA_DAC_DATA
));
1131 setCH(VgaReadPort(VGA_DAC_DATA
));
1132 setCL(VgaReadPort(VGA_DAC_DATA
));
1137 /* Get Block of DAC Registers */
1141 LPBYTE Buffer
= SEG_OFF_TO_PTR(getES(), getDX());
1143 /* Write the index */
1144 // Certainly in BL and not in BX as said by Ralf Brown...
1145 VgaWritePort(VGA_DAC_READ_INDEX
, getBL());
1147 for (i
= 0; i
< getCX(); i
++)
1149 /* Write the data in this order: Red, Green, Blue */
1150 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1151 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1152 *Buffer
++ = VgaReadPort(VGA_DAC_DATA
);
1160 DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
1172 SMALL_RECT Rectangle
= { getCL(), getCH(), getDL(), getDH() };
1174 /* Call the internal function */
1175 BiosScrollWindow(getBL(),
1184 /* Display combination code */
1189 case 0x00: /* Get Display combiantion code */
1190 setAX(MAKEWORD(0x1A, 0x1A));
1191 setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
1193 case 0x01: /* Set Display combination code */
1194 DPRINT1("Set Display combination code - Unsupported\n");
1204 DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
1210 VOID WINAPI
BiosEquipmentService(LPWORD Stack
)
1212 /* Return the equipment list */
1213 setAX(Bda
->EquipmentList
);
1216 VOID WINAPI
BiosGetMemorySize(LPWORD Stack
)
1218 /* Return the conventional memory size in kB, typically 640 kB */
1219 setAX(Bda
->MemorySize
);
1222 VOID WINAPI
BiosMiscService(LPWORD Stack
)
1226 /* Copy Extended Memory */
1229 WORD Count
= getCX() * 2 - 1;
1230 PFAST486_GDT_ENTRY Gdt
= (PFAST486_GDT_ENTRY
)SEG_OFF_TO_PTR(getES(), getSI());
1231 DWORD SourceBase
= Gdt
[2].Base
+ (Gdt
[2].BaseMid
<< 16) + (Gdt
[2].BaseHigh
<< 24);
1232 DWORD SourceLimit
= Gdt
[2].Limit
+ (Gdt
[2].LimitHigh
<< 16);
1233 DWORD DestBase
= Gdt
[3].Base
+ (Gdt
[3].BaseMid
<< 16) + (Gdt
[3].BaseHigh
<< 24);
1234 DWORD DestLimit
= Gdt
[3].Limit
+ (Gdt
[3].LimitHigh
<< 16);
1236 /* Check for flags */
1237 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
1238 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
1240 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
1243 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1249 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
1250 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
1253 setAX(ERROR_SUCCESS
);
1254 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1258 /* Get Extended Memory Size */
1261 /* Return the number of KB of RAM after 1 MB */
1262 setAX((MAX_ADDRESS
- 0x100000) / 1024);
1265 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1272 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
1278 VOID WINAPI
BiosKeyboardService(LPWORD Stack
)
1282 /* Wait for keystroke and read */
1284 /* Wait for extended keystroke and read */
1285 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
1287 /* Read the character (and wait if necessary) */
1288 setAX(BiosGetCharacter());
1292 /* Get keystroke status */
1294 /* Get extended keystroke status */
1295 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
1297 WORD Data
= BiosPeekCharacter();
1301 /* There is a character, clear ZF and return it */
1302 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1307 /* No character, set ZF */
1308 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1314 /* Get shift status */
1317 /* Return the lower byte of the keyboard shift status word */
1318 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
1325 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
1329 /* Push keystroke */
1332 /* Return 0 if success, 1 if failure */
1333 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
1337 /* Get extended shift status */
1341 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
1342 * but the high byte is organized differently:
1343 * the bytes 2 and 3 of the high byte are not the same...
1345 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
1347 /* Return the extended keyboard shift status word */
1348 setAX(KeybdShiftFlags
);
1354 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
1360 VOID WINAPI
BiosTimeService(LPWORD Stack
)
1366 /* Set AL to 1 if midnight had passed, 0 otherwise */
1367 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
1369 /* Return the tick count in CX:DX */
1370 setCX(HIWORD(Bda
->TickCounter
));
1371 setDX(LOWORD(Bda
->TickCounter
));
1373 /* Reset the midnight flag */
1374 Bda
->MidnightPassed
= FALSE
;
1381 /* Set the tick count to CX:DX */
1382 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
1384 /* Reset the midnight flag */
1385 Bda
->MidnightPassed
= FALSE
;
1392 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
1398 VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
1400 /* Increase the system tick count */
1404 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
1411 /* Perform the system timer interrupt */
1412 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
1419 BYTE ScanCode
, VirtualKey
;
1422 /* Loop while there is a scancode available */
1425 /* Get the scan code and virtual key code */
1426 ScanCode
= KeyboardReadData();
1427 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
1429 /* Check if this is a key press or release */
1430 if (!(ScanCode
& (1 << 7)))
1433 if (VirtualKey
== VK_NUMLOCK
||
1434 VirtualKey
== VK_CAPITAL
||
1435 VirtualKey
== VK_SCROLL
||
1436 VirtualKey
== VK_INSERT
)
1438 /* For toggle keys, toggle the lowest bit in the keyboard map */
1439 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
1442 /* Set the highest bit */
1443 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
1445 /* Find out which character this is */
1447 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
1453 /* Push it onto the BIOS keyboard queue */
1454 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
1458 /* Key release, unset the highest bit */
1459 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
1462 while (KeyboardReadStatus() & 1);
1464 /* Clear the keyboard flags */
1465 Bda
->KeybdShiftFlags
= 0;
1467 /* Set the appropriate flags based on the state */
1468 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
1469 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
1470 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
1471 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
1472 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
1473 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
1474 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
1475 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
1476 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
1477 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
1478 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
1479 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
1480 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
1481 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
1482 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
1483 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
1489 /* Send End-of-Interrupt to the PIC */
1490 if (IrqNumber
>= 8) PicWriteCommand(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
1491 PicWriteCommand(PIC_MASTER_CMD
, PIC_OCW2_EOI
);