[NTVDM]
[reactos.git] / subsystems / ntvdm / bios.c
index 61e6beb..a08e831 100644 (file)
 
 #define NDEBUG
 
-#include "bios.h"
 #include "emulator.h"
+#include "bios.h"
+
 #include "vga.h"
 #include "pic.h"
 #include "ps2.h"
 #include "timer.h"
 
+#include "int32.h"
+#include "registers.h"
+
 /* PRIVATE VARIABLES **********************************************************/
 
 PBIOS_DATA_AREA Bda;
@@ -24,6 +28,7 @@ static BYTE BiosKeyboardMap[256];
 static HANDLE BiosConsoleInput  = INVALID_HANDLE_VALUE;
 static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
+static HANDLE InputThread = NULL;
 
 /*
  * VGA Register Configurations for BIOS Video Modes
@@ -268,7 +273,7 @@ static LPBYTE VideoModes[] =
 static BOOLEAN BiosKbdBufferPush(WORD Data)
 {
     /* Get the location of the element after the tail */
-    WORD NextElement = Bda->KeybdBufferTail + 2;
+    WORD NextElement = Bda->KeybdBufferTail + sizeof(WORD);
 
     /* Wrap it around if it's at or beyond the end */
     if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart;
@@ -377,6 +382,8 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
     COORD Resolution;
     LPBYTE Values = VideoModes[ModeNumber];
 
+    DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber, Values);
+
     if (Values == NULL) return FALSE;
 
     /* Write the misc register */
@@ -410,6 +417,9 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
         VgaWritePort(VGA_AC_WRITE, *(Values++));
     }
 
+    /* Reset the palette */
+    VgaResetPalette();
+
     /* Update the values in the BDA */
     Bda->VideoMode = ModeNumber;
     Bda->VideoPage = 0;
@@ -422,7 +432,7 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
 
     Resolution = VgaGetDisplayResolution();
     Bda->ScreenColumns = Resolution.X;
-    Bda->ScreenRows = Resolution.Y - 1;
+    Bda->ScreenRows    = Resolution.Y - 1;
 
     return TRUE;
 }
@@ -451,53 +461,30 @@ BOOLEAN BiosSetVideoPage(BYTE PageNumber)
 
 BOOLEAN BiosInitialize(VOID)
 {
-    INT i;
-    WORD Offset = 0;
-    LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
-    LPBYTE BiosCode = (LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(BIOS_SEGMENT, 0));
-
     /* Initialize the BDA */
-    Bda = (PBIOS_DATA_AREA)((ULONG_PTR)BaseAddress + TO_LINEAR(BDA_SEGMENT, 0));
+    Bda = (PBIOS_DATA_AREA)SEG_OFF_TO_PTR(BDA_SEGMENT, 0);
     Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
+    /*
+     * Conventional memory size is 640 kB,
+     * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
+     * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
+     * for more information.
+     */
+    Bda->MemorySize = 0x0280;
     Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
     Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
 
-    /* Generate ISR stubs and fill the IVT */
-    for (i = 0; i < 256; i++)
-    {
-        IntVecTable[i * 2] = Offset;
-        IntVecTable[i * 2 + 1] = BIOS_SEGMENT;
-
-        BiosCode[Offset++] = 0xFB; // sti
-
-        BiosCode[Offset++] = 0x6A; // push i
-        BiosCode[Offset++] = (BYTE)i;
+    /* Initialize the 32-bit Interrupt system */
+    InitializeInt32(BIOS_SEGMENT);
 
-        BiosCode[Offset++] = 0x6A; // push 0
-        BiosCode[Offset++] = 0x00;
-
-        BiosCode[Offset++] = 0xF8; // clc
-
-        BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence
-        BiosCode[Offset++] = HIBYTE(EMULATOR_BOP);
-        BiosCode[Offset++] = LOBYTE(EMULATOR_INT_BOP);
-        BiosCode[Offset++] = HIBYTE(EMULATOR_INT_BOP);
-
-        BiosCode[Offset++] = 0x73; // jnc +3
-        BiosCode[Offset++] = 0x03;
-
-        // HACK: The following instruction should be HLT!
-        BiosCode[Offset++] = 0x90; // nop
-
-        BiosCode[Offset++] = 0xEB; // jmp -10
-        BiosCode[Offset++] = 0xF6;
-
-        BiosCode[Offset++] = 0x83; // add sp, 4
-        BiosCode[Offset++] = 0xC4;
-        BiosCode[Offset++] = 0x04;
-
-        BiosCode[Offset++] = 0xCF; // iret
-    }
+    /* Register the BIOS 32-bit Interrupts */
+    RegisterInt32(BIOS_VIDEO_INTERRUPT    , BiosVideoService        );
+    RegisterInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService    );
+    RegisterInt32(BIOS_MEMORY_SIZE        , BiosGetMemorySize       );
+    RegisterInt32(BIOS_MISC_INTERRUPT     , BiosMiscService         );
+    RegisterInt32(BIOS_KBD_INTERRUPT      , BiosKeyboardService     );
+    RegisterInt32(BIOS_TIME_INTERRUPT     , BiosTimeService         );
+    RegisterInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
 
     /* Get the input handle to the real console, and check for success */
     BiosConsoleInput = CreateFileW(L"CONIN$",
@@ -550,6 +537,9 @@ BOOLEAN BiosInitialize(VOID)
     /* Set the console input mode */
     SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
 
+    /* Start the input thread */
+    InputThread = CreateThread(NULL, 0, &InputThreadProc, BiosConsoleInput, 0, NULL);
+
     /* Initialize the PIC */
     PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
     PicWriteCommand(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
@@ -579,6 +569,9 @@ BOOLEAN BiosInitialize(VOID)
 
 VOID BiosCleanup(VOID)
 {
+    /* Close the input thread handle */
+    if (InputThread != NULL) CloseHandle(InputThread);
+
     /* Restore the old screen buffer */
     SetConsoleActiveScreenBuffer(BiosConsoleOutput);
 
@@ -592,15 +585,11 @@ VOID BiosCleanup(VOID)
 
 WORD BiosPeekCharacter(VOID)
 {
-    WORD CharacterData;
-    
-    /* Check if there is a key available */
-    if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return 0xFFFF;
+    WORD CharacterData = 0;
 
     /* Get the key from the queue, but don't remove it */
-    BiosKbdBufferTop(&CharacterData);
-
-    return CharacterData;
+    if (BiosKbdBufferTop(&CharacterData)) return CharacterData;
+    else return 0xFFFF;
 }
 
 WORD BiosGetCharacter(VOID)
@@ -623,13 +612,23 @@ WORD BiosGetCharacter(VOID)
     return CharacterData;
 }
 
+VOID BiosGetCursorPosition(PBYTE Row, PBYTE Column, BYTE Page)
+{
+    /* Make sure the selected video page is valid */
+    if (Page >= BIOS_MAX_PAGES) return;
+
+    /* Get the cursor location */
+    *Row    = HIBYTE(Bda->CursorPosition[Page]);
+    *Column = LOBYTE(Bda->CursorPosition[Page]);
+}
+
 VOID BiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page)
 {
     /* Make sure the selected video page is valid */
     if (Page >= BIOS_MAX_PAGES) return;
 
     /* Update the position in the BDA */
-    Bda->CursorPosition[Page] = (Row << 8) | Column;
+    Bda->CursorPosition[Page] = MAKEWORD(Column, Row);
 
     /* Check if this is the current video page */
     if (Page == Bda->VideoPage)
@@ -669,7 +668,7 @@ BOOLEAN BiosScrollWindow(INT Direction,
         /* Fill the window */
         for (i = 0; i < WindowSize; i++)
         {
-            WindowData[i] = ' ' | (FillAttribute << 8);
+            WindowData[i] = MAKEWORD(' ', FillAttribute);
         }
 
         goto Done;
@@ -689,15 +688,14 @@ Done:
 
 VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
 {
-    WORD CharData = (Attribute << 8) | Character;
+    WORD CharData = MAKEWORD(Character, Attribute);
     BYTE Row, Column;
 
     /* Make sure the page exists */
     if (Page >= BIOS_MAX_PAGES) return;
 
     /* Get the cursor location */
-    Row = HIBYTE(Bda->CursorPosition[Page]);
-    Column = LOBYTE(Bda->CursorPosition[Page]);
+    BiosGetCursorPosition(&Row, &Column, Page);
 
     if (Character == '\a')
     {
@@ -720,12 +718,23 @@ VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
         }
 
         /* Erase the existing character */
-        CharData = (Attribute << 8) | ' ';
-        VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
-                       Page * Bda->VideoPageSize
-                       + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
-                       (LPVOID)&CharData,
-                       sizeof(WORD));
+        CharData = MAKEWORD(' ', Attribute);
+        EmulatorWriteMemory(&EmulatorContext,
+                            TO_LINEAR(TEXT_VIDEO_SEG,
+                                Page * Bda->VideoPageSize +
+                                (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
+                            (LPVOID)&CharData,
+                            sizeof(WORD));
+    }
+    else if (Character == '\t')
+    {
+        /* Horizontal Tabulation control character */
+        do
+        {
+            // Taken from DosBox
+            BiosPrintCharacter(' ', Attribute, Page);
+            BiosGetCursorPosition(&Row, &Column, Page);
+        } while (Column % 8);
     }
     else if (Character == '\n')
     {
@@ -742,11 +751,12 @@ VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
         /* Default character */
 
         /* Write the character */
-        VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
-                       Page * Bda->VideoPageSize
-                       + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
-                       (LPVOID)&CharData,
-                       sizeof(WORD));
+        EmulatorWriteMemory(&EmulatorContext,
+                            TO_LINEAR(TEXT_VIDEO_SEG,
+                                Page * Bda->VideoPageSize +
+                                (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
+                            (LPVOID)&CharData,
+                            sizeof(WORD));
 
         /* Advance the cursor */
         Column++;
@@ -771,25 +781,22 @@ VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
                          Rectangle,
                          Page,
                          DEFAULT_ATTRIBUTE);
+
+        Row--;
     }
 
     /* Set the cursor position */
     BiosSetCursorPosition(Row, Column, Page);
 }
 
-VOID BiosVideoService(LPWORD Stack)
+VOID WINAPI BiosVideoService(LPWORD Stack)
 {
-    DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
-    DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
-    DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
-    DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
-
-    switch (HIBYTE(Eax))
+    switch (getAH())
     {
         /* Set Video Mode */
         case 0x00:
         {
-            BiosSetVideoMode(LOBYTE(Eax));
+            BiosSetVideoMode(getAL());
             VgaClearMemory();
             break;
         }
@@ -798,8 +805,8 @@ VOID BiosVideoService(LPWORD Stack)
         case 0x01:
         {
             /* Update the BDA */
-            Bda->CursorStartLine = HIBYTE(Ecx);
-            Bda->CursorEndLine = LOBYTE(Ecx);
+            Bda->CursorStartLine = getCH();
+            Bda->CursorEndLine   = getCL();
 
             /* Modify the CRTC registers */
             VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_START_REG);
@@ -813,7 +820,7 @@ VOID BiosVideoService(LPWORD Stack)
         /* Set Cursor Position */
         case 0x02:
         {
-            BiosSetCursorPosition(HIBYTE(Edx), LOBYTE(Edx), HIBYTE(Ebx));
+            BiosSetCursorPosition(getDH(), getDL(), getBH());
             break;
         }
 
@@ -821,14 +828,12 @@ VOID BiosVideoService(LPWORD Stack)
         case 0x03:
         {
             /* Make sure the selected video page exists */
-            if (HIBYTE(Ebx) >= BIOS_MAX_PAGES) break;
+            if (getBH() >= BIOS_MAX_PAGES) break;
 
             /* Return the result */
-            EmulatorSetRegister(EMULATOR_REG_AX, 0);
-            EmulatorSetRegister(EMULATOR_REG_CX,
-                                (Bda->CursorStartLine << 8) | Bda->CursorEndLine);
-            EmulatorSetRegister(EMULATOR_REG_DX, Bda->CursorPosition[HIBYTE(Ebx)]);
-
+            setAX(0);
+            setCX(MAKEWORD(Bda->CursorEndLine, Bda->CursorStartLine));
+            setDX(Bda->CursorPosition[getBH()]);
             break;
         }
 
@@ -839,14 +844,14 @@ VOID BiosVideoService(LPWORD Stack)
              * On modern BIOSes, this function returns 0
              * so that we can ignore the other registers.
              */
-            EmulatorSetRegister(EMULATOR_REG_AX, 0);
+            setAX(0);
             break;
         }
 
         /* Select Active Display Page */
         case 0x05:
         {
-            BiosSetVideoPage(LOBYTE(Eax));
+            BiosSetVideoPage(getAL());
             break;
         }
 
@@ -854,21 +859,15 @@ VOID BiosVideoService(LPWORD Stack)
         case 0x06:
         case 0x07:
         {
-            SMALL_RECT Rectangle =
-            {
-                LOBYTE(Ecx),
-                HIBYTE(Ecx),
-                LOBYTE(Edx),
-                HIBYTE(Edx)
-            };
+            SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() };
 
             /* Call the internal function */
-            BiosScrollWindow((HIBYTE(Eax) == 0x06) ? SCROLL_DIRECTION_UP
-                                                   : SCROLL_DIRECTION_DOWN,
-                             LOBYTE(Eax),
+            BiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP
+                                               : SCROLL_DIRECTION_DOWN,
+                             getAL(),
                              Rectangle,
                              Bda->VideoPage,
-                             HIBYTE(Ebx));
+                             getBH());
 
             break;
         }
@@ -878,19 +877,19 @@ VOID BiosVideoService(LPWORD Stack)
         case 0x09:
         case 0x0A:
         {
-            WORD CharacterData = MAKEWORD(LOBYTE(Eax), LOBYTE(Ebx));
-            BYTE Page = HIBYTE(Ebx);
+            WORD CharacterData = MAKEWORD(getAL(), getBL());
+            BYTE Page = getBH();
             DWORD Offset;
 
             /* Check if the page exists */
             if (Page >= BIOS_MAX_PAGES) break;
 
             /* Find the offset of the character */
-            Offset = Page * Bda->VideoPageSize
-                     + (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns
-                     + LOBYTE(Bda->CursorPosition[Page])) * 2;
+            Offset = Page * Bda->VideoPageSize +
+                     (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns +
+                      LOBYTE(Bda->CursorPosition[Page])) * 2;
 
-            if (HIBYTE(Eax) == 0x08)
+            if (getAH() == 0x08)
             {
                 /* Read from the video memory */
                 VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
@@ -898,14 +897,14 @@ VOID BiosVideoService(LPWORD Stack)
                               sizeof(WORD));
 
                 /* Return the character in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX, CharacterData);
+                setAX(CharacterData);
             }
             else
             {
                 /* Write to video memory */
                 VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
                                (LPVOID)&CharacterData,
-                               (HIBYTE(Ebx) == 0x09) ? sizeof(WORD) : sizeof(BYTE));
+                               (getBH() == 0x09) ? sizeof(WORD) : sizeof(BYTE));
             }
 
             break;
@@ -914,17 +913,213 @@ VOID BiosVideoService(LPWORD Stack)
         /* Teletype Output */
         case 0x0E:
         {
-            BiosPrintCharacter(LOBYTE(Eax), LOBYTE(Ebx), HIBYTE(Ebx));
+            BiosPrintCharacter(getAL(), getBL(), getBH());
             break;
         }
 
         /* Get Current Video Mode */
         case 0x0F:
         {
-            EmulatorSetRegister(EMULATOR_REG_AX,
-                                MAKEWORD(Bda->VideoMode, Bda->ScreenColumns));
-            EmulatorSetRegister(EMULATOR_REG_BX,
-                                MAKEWORD(LOBYTE(Ebx), Bda->VideoPage));
+            setAX(MAKEWORD(Bda->VideoMode, Bda->ScreenColumns));
+            setBX(MAKEWORD(getBL(), Bda->VideoPage));
+            break;
+        }
+
+        /* Palette Control */
+        case 0x10:
+        {
+            switch (getAL())
+            {
+                /* Set Single Palette Register */
+                case 0x00:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, getBL());
+
+                    /* Write the data */
+                    VgaWritePort(VGA_AC_WRITE, getBH());
+
+                    break;
+                }
+
+                /* Set Overscan Color */
+                case 0x01:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+
+                    /* Write the data */
+                    VgaWritePort(VGA_AC_WRITE, getBH());
+
+                    break;
+                }
+
+                /* Set All Palette Registers */
+                case 0x02:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Set the palette registers */
+                    for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
+                    {
+                        /* Reset the flip-flop */
+                        VgaReadPort(VGA_STAT_COLOR);
+
+                        /* Write the index */
+                        VgaWritePort(VGA_AC_INDEX, i);
+
+                        /* Write the data */
+                        VgaWritePort(VGA_AC_WRITE, Buffer[i]);
+                    }
+
+                    /* Set the overscan register */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+                    VgaWritePort(VGA_AC_WRITE, Buffer[VGA_AC_PAL_F_REG + 1]);
+
+                    break;
+                }
+
+                /* Get Single Palette Register */
+                case 0x07:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, getBL());
+
+                    /* Read the data */
+                    setBH(VgaReadPort(VGA_AC_READ));
+
+                    break;
+                }
+
+                /* Get Overscan Color */
+                case 0x08:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+
+                    /* Read the data */
+                    setBH(VgaReadPort(VGA_AC_READ));
+
+                    break;
+                }
+
+                /* Get All Palette Registers */
+                case 0x09:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Get the palette registers */
+                    for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
+                    {
+                        /* Reset the flip-flop */
+                        VgaReadPort(VGA_STAT_COLOR);
+
+                        /* Write the index */
+                        VgaWritePort(VGA_AC_INDEX, i);
+
+                        /* Read the data */
+                        Buffer[i] = VgaReadPort(VGA_AC_READ);
+                    }
+
+                    /* Get the overscan register */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+                    Buffer[VGA_AC_PAL_F_REG + 1] = VgaReadPort(VGA_AC_READ);
+
+                    break;
+                }
+
+                /* Set Individual DAC Register */
+                case 0x10:
+                {
+                    /* Write the index */
+                    // Certainly in BL and not in BX as said by Ralf Brown...
+                    VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
+
+                    /* Write the data in this order: Red, Green, Blue */
+                    VgaWritePort(VGA_DAC_DATA, getDH());
+                    VgaWritePort(VGA_DAC_DATA, getCH());
+                    VgaWritePort(VGA_DAC_DATA, getCL());
+
+                    break;
+                }
+
+                /* Set Block of DAC Registers */
+                case 0x12:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Write the index */
+                    // Certainly in BL and not in BX as said by Ralf Brown...
+                    VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
+
+                    for (i = 0; i < getCX(); i++)
+                    {
+                        /* Write the data in this order: Red, Green, Blue */
+                        VgaWritePort(VGA_DAC_DATA, *Buffer++);
+                        VgaWritePort(VGA_DAC_DATA, *Buffer++);
+                        VgaWritePort(VGA_DAC_DATA, *Buffer++);
+                    }
+
+                    break;
+                }
+
+                /* Get Individual DAC Register */
+                case 0x15:
+                {
+                    /* Write the index */
+                    VgaWritePort(VGA_DAC_READ_INDEX, getBL());
+
+                    /* Read the data in this order: Red, Green, Blue */
+                    setDH(VgaReadPort(VGA_DAC_DATA));
+                    setCH(VgaReadPort(VGA_DAC_DATA));
+                    setCL(VgaReadPort(VGA_DAC_DATA));
+
+                    break;
+                }
+
+                /* Get Block of DAC Registers */
+                case 0x17:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Write the index */
+                    // Certainly in BL and not in BX as said by Ralf Brown...
+                    VgaWritePort(VGA_DAC_READ_INDEX, getBL());
+
+                    for (i = 0; i < getCX(); i++)
+                    {
+                        /* Write the data in this order: Red, Green, Blue */
+                        *Buffer++ = VgaReadPort(VGA_DAC_DATA);
+                        *Buffer++ = VgaReadPort(VGA_DAC_DATA);
+                        *Buffer++ = VgaReadPort(VGA_DAC_DATA);
+                    }
+
+                    break;
+                }
+
+                default:
+                {
+                    DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
+                            getAL());
+                    break;
+                }
+            }
 
             break;
         }
@@ -932,17 +1127,11 @@ VOID BiosVideoService(LPWORD Stack)
         /* Scroll Window */
         case 0x12:
         {
-            SMALL_RECT Rectangle =
-            {
-                LOBYTE(Ecx),
-                HIBYTE(Ecx),
-                LOBYTE(Edx),
-                HIBYTE(Edx)
-            };
+            SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() };
 
             /* Call the internal function */
-            BiosScrollWindow(LOBYTE(Ebx),
-                             LOBYTE(Eax),
+            BiosScrollWindow(getBL(),
+                             getAL(),
                              Rectangle,
                              Bda->VideoPage,
                              DEFAULT_ATTRIBUTE);
@@ -953,11 +1142,11 @@ VOID BiosVideoService(LPWORD Stack)
         /* Display combination code */
         case 0x1A:
         {
-            switch(LOBYTE(Eax))
+            switch(getAL())
             {
                 case 0x00: /* Get Display combiantion code */
-                   EmulatorSetRegister(EMULATOR_REG_AX, MAKEWORD(0x1A, 0x1A));
-                   EmulatorSetRegister(EMULATOR_REG_BX, MAKEWORD(0x08, 0x0)); /* VGA w/ color analog display */
+                   setAX(MAKEWORD(0x1A, 0x1A));
+                   setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
                    break;
                 case 0x01: /* Set Display combination code */
                    DPRINT1("Set Display combination code - Unsupported\n");
@@ -971,34 +1160,73 @@ VOID BiosVideoService(LPWORD Stack)
         default:
         {
             DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n",
-                    HIBYTE(Eax));
+                    getAH());
         }
     }
 }
 
-VOID BiosKeyboardService(LPWORD Stack)
+VOID WINAPI BiosEquipmentService(LPWORD Stack)
 {
-    DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
+    /* Return the equipment list */
+    setAX(Bda->EquipmentList);
+}
+
+VOID WINAPI BiosGetMemorySize(LPWORD Stack)
+{
+    /* Return the conventional memory size in kB, typically 640 kB */
+    setAX(Bda->MemorySize);
+}
+
+VOID WINAPI BiosMiscService(LPWORD Stack)
+{
+    switch (getAH())
+    {
+        /* Get Extended Memory Size */
+        case 0x88:
+        {
+            /* Return the number of KB of RAM after 1 MB */
+            setAX((MAX_ADDRESS - 0x100000) / 1024);
+
+            /* Clear CF */
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+            break;
+        }
 
-    switch (HIBYTE(Eax))
+        default:
+        {
+            DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
+                    getAH());
+        }
+    }
+}
+
+VOID WINAPI BiosKeyboardService(LPWORD Stack)
+{
+    switch (getAH())
     {
+        /* Wait for keystroke and read */
         case 0x00:
+        /* Wait for extended keystroke and read */
+        case 0x10:  // FIXME: Temporarily do the same as INT 16h, 00h
         {
             /* Read the character (and wait if necessary) */
-            EmulatorSetRegister(EMULATOR_REG_AX, BiosGetCharacter());
-
+            setAX(BiosGetCharacter());
             break;
         }
 
+        /* Get keystroke status */
         case 0x01:
+        /* Get extended keystroke status */
+        case 0x11:  // FIXME: Temporarily do the same as INT 16h, 01h
         {
             WORD Data = BiosPeekCharacter();
 
             if (Data != 0xFFFF)
             {
                 /* There is a character, clear ZF and return it */
-                EmulatorSetRegister(EMULATOR_REG_AX, Data);
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
+                setAX(Data);
             }
             else
             {
@@ -1008,33 +1236,65 @@ VOID BiosKeyboardService(LPWORD Stack)
 
             break;
         }
-        
+
+        /* Get shift status */
+        case 0x02:
+        {
+            /* Return the lower byte of the keyboard shift status word */
+            setAL(LOBYTE(Bda->KeybdShiftFlags));
+            break;
+        }
+
+        /* Reserved */
+        case 0x04:
+        {
+            DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
+            break;
+        }
+
+        /* Push keystroke */
+        case 0x05:
+        {
+            /* Return 0 if success, 1 if failure */
+            setAL(BiosKbdBufferPush(getCX()) == FALSE);
+            break;
+        }
+
+        /* Get extended shift status */
+        case 0x12:
+        {
+            /*
+             * Be careful! The returned word is similar to Bda->KeybdShiftFlags
+             * but the high byte is organized differently:
+             * the bytes 2 and 3 of the high byte are not the same...
+             */
+            WORD KeybdShiftFlags = (Bda->KeybdShiftFlags & 0xF3FF);
+
+            /* Return the extended keyboard shift status word */
+            setAX(KeybdShiftFlags);
+            break;
+        }
+
         default:
         {
             DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
-                    HIBYTE(Eax));
+                    getAH());
         }
     }
 }
 
-VOID BiosTimeService(LPWORD Stack)
+VOID WINAPI BiosTimeService(LPWORD Stack)
 {
-    DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
-    DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
-    DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
-
-    switch (HIBYTE(Eax))
+    switch (getAH())
     {
         case 0x00:
         {
             /* Set AL to 1 if midnight had passed, 0 otherwise */
-            Eax &= 0xFFFFFF00;
-            if (Bda->MidnightPassed) Eax |= 1;
+            setAL(Bda->MidnightPassed ? 0x01 : 0x00);
 
             /* Return the tick count in CX:DX */
-            EmulatorSetRegister(EMULATOR_REG_AX, Eax);
-            EmulatorSetRegister(EMULATOR_REG_CX, HIWORD(Bda->TickCounter));
-            EmulatorSetRegister(EMULATOR_REG_DX, LOWORD(Bda->TickCounter));
+            setCX(HIWORD(Bda->TickCounter));
+            setDX(LOWORD(Bda->TickCounter));
 
             /* Reset the midnight flag */
             Bda->MidnightPassed = FALSE;
@@ -1045,7 +1305,7 @@ VOID BiosTimeService(LPWORD Stack)
         case 0x01:
         {
             /* Set the tick count to CX:DX */
-            Bda->TickCounter = MAKELONG(LOWORD(Edx), LOWORD(Ecx));
+            Bda->TickCounter = MAKELONG(getDX(), getCX());
 
             /* Reset the midnight flag */
             Bda->MidnightPassed = FALSE;
@@ -1056,23 +1316,17 @@ VOID BiosTimeService(LPWORD Stack)
         default:
         {
             DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
-                    HIBYTE(Eax));
+                    getAH());
         }
     }
 }
 
-VOID BiosSystemTimerInterrupt(LPWORD Stack)
+VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
 {
     /* Increase the system tick count */
     Bda->TickCounter++;
 }
 
-VOID BiosEquipmentService(LPWORD Stack)
-{
-    /* Return the equipment list */
-    EmulatorSetRegister(EMULATOR_REG_AX, Bda->EquipmentList);
-}
-
 VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
 {
     switch (IrqNumber)
@@ -1082,7 +1336,6 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
         {
             /* Perform the system timer interrupt */
             EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT);
-
             break;
         }
 
@@ -1091,9 +1344,9 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
         {
             BYTE ScanCode, VirtualKey;
             WORD Character;
-            
+
             /* Loop while there is a scancode available */
-            while (KeyboardReadStatus() & 1)
+            do
             {
                 /* Get the scan code and virtual key code */
                 ScanCode = KeyboardReadData();
@@ -1103,23 +1356,28 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
                 if (!(ScanCode & (1 << 7)))
                 {
                     /* Key press */
-                    if (VirtualKey == VK_NUMLOCK
-                        || VirtualKey == VK_CAPITAL
-                        || VirtualKey == VK_SCROLL)
+                    if (VirtualKey == VK_NUMLOCK ||
+                        VirtualKey == VK_CAPITAL ||
+                        VirtualKey == VK_SCROLL  ||
+                        VirtualKey == VK_INSERT)
                     {
                         /* For toggle keys, toggle the lowest bit in the keyboard map */
                         BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
                     }
-    
+
                     /* Set the highest bit */
                     BiosKeyboardMap[VirtualKey] |= (1 << 7);
 
                     /* Find out which character this is */
-                    if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) > 0)
+                    Character = 0;
+                    if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
                     {
-                        /* Push it onto the BIOS keyboard queue */
-                        BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF));
+                        /* Not ASCII */
+                        Character = 0;
                     }
+
+                    /* Push it onto the BIOS keyboard queue */
+                    BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
                 }
                 else
                 {
@@ -1127,13 +1385,35 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
                     BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
                 }
             }
+            while (KeyboardReadStatus() & 1);
+
+            /* Clear the keyboard flags */
+            Bda->KeybdShiftFlags = 0;
+
+            /* Set the appropriate flags based on the state */
+            if (BiosKeyboardMap[VK_RSHIFT]   & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
+            if (BiosKeyboardMap[VK_LSHIFT]   & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
+            if (BiosKeyboardMap[VK_CONTROL]  & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
+            if (BiosKeyboardMap[VK_MENU]     & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
+            if (BiosKeyboardMap[VK_SCROLL]   & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
+            if (BiosKeyboardMap[VK_NUMLOCK]  & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
+            if (BiosKeyboardMap[VK_CAPITAL]  & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
+            if (BiosKeyboardMap[VK_INSERT]   & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
+            if (BiosKeyboardMap[VK_RMENU]    & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT;
+            if (BiosKeyboardMap[VK_LMENU]    & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
+            if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
+            if (BiosKeyboardMap[VK_PAUSE]    & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
+            if (BiosKeyboardMap[VK_SCROLL]   & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
+            if (BiosKeyboardMap[VK_NUMLOCK]  & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
+            if (BiosKeyboardMap[VK_CAPITAL]  & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
+            if (BiosKeyboardMap[VK_INSERT]   & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
 
             break;
         }
     }
 
     /* Send End-of-Interrupt to the PIC */
-    if (IrqNumber > 8) PicWriteCommand(PIC_SLAVE_CMD, PIC_OCW2_EOI);
+    if (IrqNumber >= 8) PicWriteCommand(PIC_SLAVE_CMD, PIC_OCW2_EOI);
     PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
 }