[NTVDM]
[reactos.git] / subsystems / ntvdm / bios.c
index 878badf..cc6cf0f 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"
 
+/* MACROS *********************************************************************/
+
+//
+// These macros are defined for ease-of-use of some VGA I/O ports
+// whose addresses depend whether we are in Monochrome or Colour mode.
+//
+#define VGA_INSTAT1_READ    Bda->CrtBasePort + 6    // VGA_INSTAT1_READ_MONO or VGA_INSTAT1_READ_COLOR
+#define VGA_CRTC_INDEX      Bda->CrtBasePort        // VGA_CRTC_INDEX_MONO   or VGA_CRTC_INDEX_COLOR
+#define VGA_CRTC_DATA       Bda->CrtBasePort + 1    // VGA_CRTC_DATA_MONO    or VGA_CRTC_DATA_COLOR
+
 /* PRIVATE VARIABLES **********************************************************/
 
 PBIOS_DATA_AREA Bda;
@@ -31,238 +43,250 @@ static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
  * VGA Register Configurations for BIOS Video Modes
  * The configurations come from DosBox.
  */
-static BYTE VideoMode_40x25_text[] =
+static VGA_REGISTERS VideoMode_40x25_text =
 {
     /* Miscellaneous Register */
     0x67,
 
     /* Sequencer Registers */
-    0x00, 0x08, 0x03, 0x00, 0x07,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
+    {0x00, 0x08, 0x03, 0x00, 0x07},
 
     /* CRTC Registers */
-    0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
-    0xFF,
+    {0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
-    0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+     0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00}
 };
 
-static BYTE VideoMode_80x25_text[] =
+static VGA_REGISTERS VideoMode_80x25_text =
 {
     /* Miscellaneous Register */
     0x67,
 
     /* Sequencer Registers */
-    0x00, 0x00, 0x03, 0x00, 0x07,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
+    {0x00, 0x00, 0x03, 0x00, 0x07},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
-    0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+     0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00}
 };
 
-static BYTE VideoMode_320x200_4color[] =
+static VGA_REGISTERS VideoMode_320x200_4color =
 {
     /* Miscellaneous Register */
     0x63,
 
     /* Sequencer Registers */
-    0x00, 0x09, 0x00, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF,
+    {0x00, 0x09, 0x00, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
-    0xFF,
+    {0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
-    0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
+     0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static BYTE VideoMode_640x200_2color[] =
+static VGA_REGISTERS VideoMode_640x200_2color =
 {
     /* Miscellaneous Register */
     0x63,
 
     /* Sequencer Registers */
-    0x00, 0x09, 0x0F, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF,
+    {0x00, 0x09, 0x0F, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
-    0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00
+    {0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+     0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00}
 };
 
-static BYTE VideoMode_320x200_16color[] =
+static VGA_REGISTERS VideoMode_320x200_16color =
 {
     /* Miscellaneous Register */
     0x63,
 
     /* Sequencer Registers */
-    0x00, 0x09, 0x0F, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
+    {0x00, 0x09, 0x0F, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
-    0xFF,
+    {0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
-    0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+     0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static BYTE VideoMode_640x200_16color[] =
+static VGA_REGISTERS VideoMode_640x200_16color =
 {
     /* Miscellaneous Register */
     0x63,
 
     /* Sequencer Registers */
-    0x00, 0x01, 0x0F, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
+    {0x00, 0x01, 0x0F, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
-    0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+     0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static BYTE VideoMode_640x350_16color[] =
+static VGA_REGISTERS VideoMode_640x350_16color =
 {
     /* Miscellaneous Register */
     0xA3,
 
     /* Sequencer Registers */
-    0x00, 0x01, 0x0F, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
+    {0x00, 0x01, 0x0F, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
-    0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+     0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static BYTE VideoMode_640x480_2color[] =
+static VGA_REGISTERS VideoMode_640x480_2color =
 {
     /* Miscellaneous Register */
     0xE3,
 
     /* Sequencer Registers */
-    0x00, 0x01, 0x0F, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
+    {0x00, 0x01, 0x0F, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
-    0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+     0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static BYTE VideoMode_640x480_16color[] =
+static VGA_REGISTERS VideoMode_640x480_16color =
 {
     /* Miscellaneous Register */
     0xE3,
 
     /* Sequencer Registers */
-    0x00, 0x01, 0x0F, 0x00, 0x02,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
+    {0x00, 0x01, 0x0F, 0x00, 0x02},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
-    0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+     0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static BYTE VideoMode_320x200_256color[] =
+static VGA_REGISTERS VideoMode_320x200_256color =
 {
     /* Miscellaneous Register */
     0x63,
 
     /* Sequencer Registers */
-    0x00, 0x01, 0x0F, 0x00, 0x0E,
-
-    /* GC Registers */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
+    {0x00, 0x01, 0x0F, 0x00, 0x0E},
 
     /* CRTC Registers */
-    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
-    0xFF,
+    {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
+     0xFF},
+
+    /* GC Registers */
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF},
 
     /* AC Registers */
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
-    0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
+    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+     0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00}
 };
 
-static LPBYTE VideoModes[] =
+/* See http://wiki.osdev.org/Drawing_In_Protected_Mode#Locating_Video_Memory */
+static PVGA_REGISTERS VideoModes[BIOS_MAX_VIDEO_MODE + 1] =
 {
-    VideoMode_40x25_text,       /* Mode 00h */
-    VideoMode_40x25_text,       /* Mode 01h */
-    VideoMode_80x25_text,       /* Mode 02h */
-    VideoMode_80x25_text,       /* Mode 03h */
-    VideoMode_320x200_4color,   /* Mode 04h */
-    VideoMode_320x200_4color,   /* Mode 05h */
-    VideoMode_640x200_2color,   /* Mode 06h */
-    NULL,                       /* Mode 07h */
-    NULL,                       /* Mode 08h */
-    NULL,                       /* Mode 09h */
-    NULL,                       /* Mode 0Ah */
-    NULL,                       /* Mode 0Bh */
-    NULL,                       /* Mode 0Ch */
-    VideoMode_320x200_16color,  /* Mode 0Dh */
-    VideoMode_640x200_16color,  /* Mode 0Eh */
-    NULL,                       /* Mode 0Fh */
-    VideoMode_640x350_16color,  /* Mode 10h */
-    VideoMode_640x480_2color,   /* Mode 11h */
-    VideoMode_640x480_16color,  /* Mode 12h */
-    VideoMode_320x200_256color, /* Mode 13h */
+    &VideoMode_40x25_text,          /* Mode 00h */      // 16 color (mono)
+    &VideoMode_40x25_text,          /* Mode 01h */      // 16 color
+    &VideoMode_80x25_text,          /* Mode 02h */      // 16 color (mono)
+    &VideoMode_80x25_text,          /* Mode 03h */      // 16 color
+    &VideoMode_320x200_4color,      /* Mode 04h */      // 4 color
+    &VideoMode_320x200_4color,      /* Mode 05h */      // same (m)
+    &VideoMode_640x200_2color,      /* Mode 06h */      // 640*200 2 color
+    NULL,                           /* Mode 07h */      // MDA monochrome text 80*25
+    NULL,                           /* Mode 08h */      // PCjr
+    NULL,                           /* Mode 09h */      // PCjr
+    NULL,                           /* Mode 0Ah */      // PCjr
+    NULL,                           /* Mode 0Bh */      // Reserved
+    NULL,                           /* Mode 0Ch */      // Reserved
+    &VideoMode_320x200_16color,     /* Mode 0Dh */      // EGA 320*200 16 color
+    &VideoMode_640x200_16color,     /* Mode 0Eh */      // EGA 640*200 16 color
+    NULL,                           /* Mode 0Fh */      // EGA 640*350 mono
+    &VideoMode_640x350_16color,     /* Mode 10h */      // EGA 640*350 HiRes 16 color
+    &VideoMode_640x480_2color,      /* Mode 11h */      // VGA 640*480 mono
+    &VideoMode_640x480_16color,     /* Mode 12h */      // VGA
+    &VideoMode_320x200_256color,    /* Mode 13h */      // VGA
+};
+
+// FIXME: Are they computable with the previous data ??
+// Values taken from DosBox
+static WORD VideoModePageSize[BIOS_MAX_VIDEO_MODE + 1] =
+{
+    0x0800, 0x0800, 0x1000, 0x1000,
+    0x4000, 0x4000, 0x4000, 0x1000,
+    0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2000, 0x4000, 0x8000,
+    0x8000, 0xA000, 0xA000, 0x2000
 };
 
 /* PRIVATE FUNCTIONS **********************************************************/
@@ -366,59 +390,147 @@ static VOID BiosWriteWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page)
     }
 }
 
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-BYTE BiosGetVideoMode(VOID)
-{
-    return Bda->VideoMode;
-}
-
-BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
+static BOOLEAN VgaSetRegisters(PVGA_REGISTERS Registers)
 {
     INT i;
-    COORD Resolution;
-    LPBYTE Values = VideoModes[ModeNumber];
 
-    DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber, Values);
+    if (Registers == NULL) return FALSE;
 
-    if (Values == NULL) return FALSE;
+    /* Disable interrupts */
+    setIF(0);
+
+    /*
+     * Set the CRT base address according to the selected mode,
+     * monochrome or color. The following macros:
+     * VGA_INSTAT1_READ, VGA_CRTC_INDEX and VGA_CRTC_DATA are then
+     * used to access the correct VGA I/O ports.
+     */
+    Bda->CrtBasePort = (Registers->Misc & 0x01) ? VGA_CRTC_INDEX_COLOR
+                                                : VGA_CRTC_INDEX_MONO;
 
     /* Write the misc register */
-    VgaWritePort(VGA_MISC_WRITE, *(Values++));
+    VgaWritePort(VGA_MISC_WRITE, Registers->Misc);
+
+    /* Synchronous reset on */
+    VgaWritePort(VGA_SEQ_INDEX, VGA_SEQ_RESET_REG);
+    VgaWritePort(VGA_SEQ_DATA , VGA_SEQ_RESET_AR);
 
     /* Write the sequencer registers */
-    for (i = 0; i < VGA_SEQ_MAX_REG; i++)
+    for (i = 1; i < VGA_SEQ_MAX_REG; i++)
     {
         VgaWritePort(VGA_SEQ_INDEX, i);
-        VgaWritePort(VGA_SEQ_DATA, *(Values++));
+        VgaWritePort(VGA_SEQ_DATA, Registers->Sequencer[i]);
     }
 
-    /* Write the GC registers */
-    for (i = 0; i < VGA_GC_MAX_REG; i++)
-    {
-        VgaWritePort(VGA_GC_INDEX, i);
-        VgaWritePort(VGA_GC_DATA, *(Values++));
-    }
+    /* Synchronous reset off */
+    VgaWritePort(VGA_SEQ_INDEX, VGA_SEQ_RESET_REG);
+    VgaWritePort(VGA_SEQ_DATA , VGA_SEQ_RESET_SR | VGA_SEQ_RESET_AR);
+
+    /* Unlock CRTC registers 0-7 */
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_END_HORZ_BLANKING_REG);
+    VgaWritePort(VGA_CRTC_DATA, VgaReadPort(VGA_CRTC_DATA) | 0x80);
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_VERT_RETRACE_END_REG);
+    VgaWritePort(VGA_CRTC_DATA, VgaReadPort(VGA_CRTC_DATA) & ~0x80);
+    // Make sure they remain unlocked
+    Registers->CRT[VGA_CRTC_END_HORZ_BLANKING_REG] |= 0x80;
+    Registers->CRT[VGA_CRTC_VERT_RETRACE_END_REG] &= ~0x80;
 
     /* Write the CRTC registers */
     for (i = 0; i < VGA_CRTC_MAX_REG; i++)
     {
         VgaWritePort(VGA_CRTC_INDEX, i);
-        VgaWritePort(VGA_CRTC_DATA, *(Values++));
+        VgaWritePort(VGA_CRTC_DATA, Registers->CRT[i]);
+    }
+
+    /* Write the GC registers */
+    for (i = 0; i < VGA_GC_MAX_REG; i++)
+    {
+        VgaWritePort(VGA_GC_INDEX, i);
+        VgaWritePort(VGA_GC_DATA, Registers->Graphics[i]);
     }
 
     /* Write the AC registers */
     for (i = 0; i < VGA_AC_MAX_REG; i++)
     {
+        VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
         VgaWritePort(VGA_AC_INDEX, i);
-        VgaWritePort(VGA_AC_WRITE, *(Values++));
+        VgaWritePort(VGA_AC_WRITE, Registers->Attribute[i]);
     }
 
+    /* Set the PEL mask */
+    VgaWritePort(VGA_DAC_MASK, 0xFF);
+
+    /* Enable screen and disable palette access */
+    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+    VgaWritePort(VGA_AC_INDEX, 0x20);
+
+    /* Enable interrupts */
+    setIF(1);
+
+    return TRUE;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BYTE BiosGetVideoMode(VOID)
+{
+    return Bda->VideoMode;
+}
+
+BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
+{
+    INT i;
+    BYTE Page;
+
+    COORD Resolution;
+    PVGA_REGISTERS VgaMode = VideoModes[ModeNumber];
+
+    DPRINT1("Switching to mode %Xh; VgaMode = 0x%p\n", ModeNumber, VgaMode);
+
+    if (!VgaSetRegisters(VgaMode)) return FALSE;
+
+    // /* Disable screen and enable palette access */
+    // VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+    // VgaWritePort(VGA_AC_INDEX, 0x00);
+
+    if ((ModeNumber == 0x0D) || (ModeNumber == 0x0E) || (ModeNumber == 0x10))
+    {
+        /* EGA modes */
+        extern CONST COLORREF EgaPalette[VGA_MAX_COLORS / 4];
+        for (i = 0; i < sizeof(EgaPalette)/sizeof(EgaPalette[0]); i++)
+        {
+            VgaWritePort(VGA_DAC_WRITE_INDEX, i);
+            VgaWritePort(VGA_DAC_DATA, VGA_COLOR_TO_DAC(GetRValue(EgaPalette[i])));
+            VgaWritePort(VGA_DAC_DATA, VGA_COLOR_TO_DAC(GetGValue(EgaPalette[i])));
+            VgaWritePort(VGA_DAC_DATA, VGA_COLOR_TO_DAC(GetBValue(EgaPalette[i])));
+        }
+    }
+    else
+    {
+        /* Reset the palette */
+        VgaResetPalette();
+    }
+
+    // /* Enable screen and disable palette access */
+    // VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+    // VgaWritePort(VGA_AC_INDEX, 0x20);
+
+    // Bda->CrtModeControl;
+    // Bda->CrtColorPaletteMask;
+    // Bda->EGAFlags;
+    // Bda->VGAFlags;
+
     /* Update the values in the BDA */
-    Bda->VideoMode = ModeNumber;
-    Bda->VideoPage = 0;
-    Bda->VideoPageSize = BIOS_PAGE_SIZE;
-    Bda->VideoPageOffset = 0;
+    Bda->VideoMode       = ModeNumber;
+    Bda->VideoPageSize   = VideoModePageSize[ModeNumber];
+    Bda->VideoPage       = 0;
+    Bda->VideoPageOffset = Bda->VideoPage * Bda->VideoPageSize;
+
+    /* Set the start address in the CRTC */
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_LOW_REG);
+    VgaWritePort(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset));
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_HIGH_REG);
+    VgaWritePort(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset));
 
     /* Get the character height */
     VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_MAX_SCAN_LINE_REG);
@@ -426,83 +538,84 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
 
     Resolution = VgaGetDisplayResolution();
     Bda->ScreenColumns = Resolution.X;
-    Bda->ScreenRows = Resolution.Y - 1;
+    Bda->ScreenRows    = Resolution.Y - 1;
+
+    /* Set cursor position for each page */
+    for (Page = 0; Page < BIOS_MAX_PAGES; ++Page)
+        BiosSetCursorPosition(0, 0, Page);
 
     return TRUE;
 }
 
 BOOLEAN BiosSetVideoPage(BYTE PageNumber)
 {
+    BYTE Row, Column;
+
     /* Check if the page exists */
     if (PageNumber >= BIOS_MAX_PAGES) return FALSE;
 
     /* Check if this is the same page */
     if (PageNumber == Bda->VideoPage) return TRUE;
 
-    /* Set the values in the BDA */
-    Bda->VideoPage = PageNumber;
-    Bda->VideoPageSize = BIOS_PAGE_SIZE;
-    Bda->VideoPageOffset = PageNumber * BIOS_PAGE_SIZE;
+    /* Update the values in the BDA */
+    Bda->VideoPage       = PageNumber;
+    Bda->VideoPageOffset = Bda->VideoPage * Bda->VideoPageSize;
 
     /* Set the start address in the CRTC */
-    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_LOW_REG);
     VgaWritePort(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset));
-    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_HIGH_REG);
     VgaWritePort(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset));
 
+    /*
+     * Get the cursor location (we don't update anything on the BIOS side
+     * but we update the cursor location on the VGA side).
+     */
+    BiosGetCursorPosition(&Row, &Column, PageNumber);
+    BiosSetCursorPosition(Row, Column, PageNumber);
+
     return TRUE;
 }
 
 BOOLEAN BiosInitialize(VOID)
 {
-    USHORT i;
-    WORD Offset = 0;
-    LPWORD IntVecTable = (LPWORD)BaseAddress;
-    LPBYTE BiosCode = (LPBYTE)SEG_OFF_TO_PTR(BIOS_SEGMENT, 0);
-
     /* Initialize the BDA */
     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 = 0x00; i <= 0xFF; i++)
-    {
-        IntVecTable[i * 2] = Offset;
-        IntVecTable[i * 2 + 1] = BIOS_SEGMENT;
-
-        BiosCode[Offset++] = 0xFB; // sti
-
-        BiosCode[Offset++] = 0x6A; // push i
-        BiosCode[Offset++] = (UCHAR)i;
-
-        BiosCode[Offset++] = 0x6A; // push 0
-        BiosCode[Offset++] = 0x00;
-
-// BOP_SEQ:
-        BiosCode[Offset++] = 0xF8; // clc
-
-        BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence
-        BiosCode[Offset++] = HIBYTE(EMULATOR_BOP);
-        BiosCode[Offset++] = EMULATOR_INT_BOP;
-
-        BiosCode[Offset++] = 0x73; // jnc EXIT (offset +3)
-        BiosCode[Offset++] = 0x03;
-
-        // HACK: The following instruction should be HLT!
-        BiosCode[Offset++] = 0x90; // nop
-
-        BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -9)
-        BiosCode[Offset++] = 0xF7;
-
-// EXIT:
-        BiosCode[Offset++] = 0x83; // add sp, 4
-        BiosCode[Offset++] = 0xC4;
-        BiosCode[Offset++] = 0x04;
-
-        BiosCode[Offset++] = 0xCF; // iret
-    }
+    Bda->KeybdBufferHead = Bda->KeybdBufferTail = 0;
+
+    /* Initialize the 32-bit Interrupt system */
+    InitializeInt32(BIOS_SEGMENT);
+
+    /* 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);
+
+    /* Some interrupts are in fact addresses to tables */
+    ((PDWORD)BaseAddress)[0x1D] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x1E] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x1F] = (DWORD)NULL;
+
+    ((PDWORD)BaseAddress)[0x41] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x43] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x44] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x46] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x48] = (DWORD)NULL;
+    ((PDWORD)BaseAddress)[0x49] = (DWORD)NULL;
 
     /* Get the input handle to the real console, and check for success */
     BiosConsoleInput = CreateFileW(L"CONIN$",
@@ -555,6 +668,9 @@ BOOLEAN BiosInitialize(VOID)
     /* Set the console input mode */
     SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
 
+    /* Initialize PS2 */
+    PS2Initialize(BiosConsoleInput);
+
     /* Initialize the PIC */
     PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
     PicWriteCommand(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
@@ -584,6 +700,8 @@ BOOLEAN BiosInitialize(VOID)
 
 VOID BiosCleanup(VOID)
 {
+    PS2Cleanup();
+
     /* Restore the old screen buffer */
     SetConsoleActiveScreenBuffer(BiosConsoleOutput);
 
@@ -609,28 +727,38 @@ WORD BiosGetCharacter(VOID)
     WORD CharacterData = 0;
 
     /* Check if there is a key available */
-    if (Bda->KeybdBufferHead != Bda->KeybdBufferTail)
+    if (BiosKbdBufferTop(&CharacterData))
     {
-        /* Get the key from the queue, and remove it */
-        BiosKbdBufferTop(&CharacterData);
+        /* A key was available, remove it from the queue */
         BiosKbdBufferPop();
     }
     else
     {
-        /* Set the handler CF to repeat the BOP */
-        EmulatorSetFlag(EMULATOR_FLAG_CF);
+        /* No key available. Set the handler CF to repeat the BOP */
+        setCF(1);
+        // CharacterData = 0xFFFF;
     }
 
     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)
@@ -653,8 +781,9 @@ BOOLEAN BiosScrollWindow(INT Direction,
 {
     DWORD i;
     LPWORD WindowData;
-    DWORD WindowSize = (Rectangle.Bottom - Rectangle.Top + 1)
-                       * (Rectangle.Right - Rectangle.Left + 1);
+    WORD WindowWidth = Rectangle.Right - Rectangle.Left + 1;
+    WORD WindowHeight = Rectangle.Bottom - Rectangle.Top + 1;
+    DWORD WindowSize = WindowWidth * WindowHeight;
 
     /* Allocate a buffer for the window */
     WindowData = (LPWORD)HeapAlloc(GetProcessHeap(),
@@ -665,18 +794,59 @@ BOOLEAN BiosScrollWindow(INT Direction,
     /* Read the window data */
     BiosReadWindow(WindowData, Rectangle, Page);
 
-    if (Amount == 0)
+    if ((Amount == 0)
+        || (((Direction == SCROLL_DIRECTION_UP)
+        || (Direction == SCROLL_DIRECTION_DOWN))
+        && (Amount >= WindowHeight))
+        || (((Direction == SCROLL_DIRECTION_LEFT)
+        || (Direction == SCROLL_DIRECTION_RIGHT))
+        && (Amount >= WindowWidth)))
     {
         /* Fill the window */
         for (i = 0; i < WindowSize; i++)
         {
-            WindowData[i] = ' ' | (FillAttribute << 8);
+            WindowData[i] = MAKEWORD(' ', FillAttribute);
         }
 
         goto Done;
     }
 
-    // TODO: Scroll the window!
+    switch (Direction)
+    {
+        case SCROLL_DIRECTION_UP:
+        {
+            RtlMoveMemory(WindowData,
+                          &WindowData[WindowWidth * Amount],
+                          (WindowSize - WindowWidth * Amount) * sizeof(WORD));
+
+            for (i = 0; i < Amount * WindowWidth; i++)
+            {
+                WindowData[WindowSize - i - 1] = MAKEWORD(' ', FillAttribute);
+            }
+
+            break;
+        }
+
+        case SCROLL_DIRECTION_DOWN:
+        {
+            RtlMoveMemory(&WindowData[WindowWidth * Amount],
+                          WindowData,
+                          (WindowSize - WindowWidth * Amount) * sizeof(WORD));
+
+            for (i = 0; i < Amount * WindowWidth; i++)
+            {
+                WindowData[i] = MAKEWORD(' ', FillAttribute);
+            }
+
+            break;
+        }
+
+        default:
+        {
+            // TODO: NOT IMPLEMENTED!
+            UNIMPLEMENTED;
+        }
+    }
 
 Done:
     /* Write back the window data */
@@ -690,15 +860,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')
     {
@@ -721,12 +890,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')
     {
@@ -743,11 +923,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++;
@@ -772,13 +953,15 @@ 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)
 {
     switch (getAH())
     {
@@ -922,30 +1105,32 @@ VOID BiosVideoService(LPWORD Stack)
                 /* Set Single Palette Register */
                 case 0x00:
                 {
-                    /* Reset the flip-flop */
-                    VgaReadPort(VGA_STAT_COLOR);
-
                     /* Write the index */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
                     VgaWritePort(VGA_AC_INDEX, getBL());
 
                     /* Write the data */
                     VgaWritePort(VGA_AC_WRITE, getBH());
 
+                    /* Enable screen and disable palette access */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+                    VgaWritePort(VGA_AC_INDEX, 0x20);
                     break;
                 }
 
                 /* Set Overscan Color */
                 case 0x01:
                 {
-                    /* Reset the flip-flop */
-                    VgaReadPort(VGA_STAT_COLOR);
-
                     /* Write the index */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
                     VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
 
                     /* Write the data */
                     VgaWritePort(VGA_AC_WRITE, getBH());
 
+                    /* Enable screen and disable palette access */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+                    VgaWritePort(VGA_AC_INDEX, 0x20);
                     break;
                 }
 
@@ -958,10 +1143,8 @@ VOID BiosVideoService(LPWORD Stack)
                     /* 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 */
+                        VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
                         VgaWritePort(VGA_AC_INDEX, i);
 
                         /* Write the data */
@@ -972,36 +1155,41 @@ VOID BiosVideoService(LPWORD Stack)
                     VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
                     VgaWritePort(VGA_AC_WRITE, Buffer[VGA_AC_PAL_F_REG + 1]);
 
+                    /* Enable screen and disable palette access */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+                    VgaWritePort(VGA_AC_INDEX, 0x20);
                     break;
                 }
 
                 /* Get Single Palette Register */
                 case 0x07:
                 {
-                    /* Reset the flip-flop */
-                    VgaReadPort(VGA_STAT_COLOR);
-
                     /* Write the index */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
                     VgaWritePort(VGA_AC_INDEX, getBL());
 
                     /* Read the data */
                     setBH(VgaReadPort(VGA_AC_READ));
 
+                    /* Enable screen and disable palette access */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+                    VgaWritePort(VGA_AC_INDEX, 0x20);
                     break;
                 }
 
                 /* Get Overscan Color */
                 case 0x08:
                 {
-                    /* Reset the flip-flop */
-                    VgaReadPort(VGA_STAT_COLOR);
-
                     /* Write the index */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
                     VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
 
                     /* Read the data */
                     setBH(VgaReadPort(VGA_AC_READ));
 
+                    /* Enable screen and disable palette access */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+                    VgaWritePort(VGA_AC_INDEX, 0x20);
                     break;
                 }
 
@@ -1014,10 +1202,8 @@ VOID BiosVideoService(LPWORD Stack)
                     /* 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 */
+                        VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
                         VgaWritePort(VGA_AC_INDEX, i);
 
                         /* Read the data */
@@ -1028,6 +1214,9 @@ VOID BiosVideoService(LPWORD Stack)
                     VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
                     Buffer[VGA_AC_PAL_F_REG + 1] = VgaReadPort(VGA_AC_READ);
 
+                    /* Enable screen and disable palette access */
+                    VgaReadPort(VGA_INSTAT1_READ); // Put the AC register into index state
+                    VgaWritePort(VGA_AC_INDEX, 0x20);
                     break;
                 }
 
@@ -1154,7 +1343,75 @@ VOID BiosVideoService(LPWORD Stack)
     }
 }
 
-VOID BiosKeyboardService(LPWORD Stack)
+VOID WINAPI BiosEquipmentService(LPWORD Stack)
+{
+    /* 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())
+    {
+        /* Copy Extended Memory */
+        case 0x87:
+        {
+            DWORD Count = (DWORD)getCX() * 2;
+            PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI());
+            DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24);
+            DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16);
+            DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24);
+            DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16);
+
+            /* Check for flags */
+            if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF;
+            if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF;
+
+            if ((Count > SourceLimit) || (Count > DestLimit))
+            {
+                setAX(0x80);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+                break;
+            }
+
+            /* Copy */
+            RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase),
+                          (PVOID)((ULONG_PTR)BaseAddress + SourceBase),
+                          Count);
+
+            setAX(ERROR_SUCCESS);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* 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;
+        }
+
+        default:
+        {
+            DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
+                    getAH());
+        }
+    }
+}
+
+VOID WINAPI BiosKeyboardService(LPWORD Stack)
 {
     switch (getAH())
     {
@@ -1236,7 +1493,7 @@ VOID BiosKeyboardService(LPWORD Stack)
     }
 }
 
-VOID BiosTimeService(LPWORD Stack)
+VOID WINAPI BiosTimeService(LPWORD Stack)
 {
     switch (getAH())
     {
@@ -1274,18 +1531,12 @@ VOID BiosTimeService(LPWORD Stack)
     }
 }
 
-VOID BiosSystemTimerInterrupt(LPWORD Stack)
+VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
 {
     /* Increase the system tick count */
     Bda->TickCounter++;
 }
 
-VOID BiosEquipmentService(LPWORD Stack)
-{
-    /* Return the equipment list */
-    setAX(Bda->EquipmentList);
-}
-
 VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
 {
     switch (IrqNumber)
@@ -1304,47 +1555,42 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
             BYTE ScanCode, VirtualKey;
             WORD Character;
 
-            /* Loop while there is a scancode available */
-            do
-            {
-                /* Get the scan code and virtual key code */
-                ScanCode = KeyboardReadData();
-                VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
+            /* Get the scan code and virtual key code */
+            ScanCode = PS2ReadPort(PS2_DATA_PORT);
+            VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
 
-                /* Check if this is a key press or release */
-                if (!(ScanCode & (1 << 7)))
+            /* Check if this is a key press or release */
+            if (!(ScanCode & (1 << 7)))
+            {
+                /* Key press */
+                if (VirtualKey == VK_NUMLOCK ||
+                    VirtualKey == VK_CAPITAL ||
+                    VirtualKey == VK_SCROLL  ||
+                    VirtualKey == VK_INSERT)
                 {
-                    /* Key press */
-                    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);
+                    /* For toggle keys, toggle the lowest bit in the keyboard map */
+                    BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
+                }
 
-                    /* Find out which character this is */
-                    Character = 0;
-                    if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
-                    {
-                        /* Not ASCII */
-                        Character = 0;
-                    }
+                /* Set the highest bit */
+                BiosKeyboardMap[VirtualKey] |= (1 << 7);
 
-                    /* Push it onto the BIOS keyboard queue */
-                    BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
-                }
-                else
+                /* Find out which character this is */
+                Character = 0;
+                if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
                 {
-                    /* Key release, unset the highest bit */
-                    BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
+                    /* Not ASCII */
+                    Character = 0;
                 }
+
+                /* Push it onto the BIOS keyboard queue */
+                BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
+            }
+            else
+            {
+                /* Key release, unset the highest bit */
+                BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
             }
-            while (KeyboardReadStatus() & 1);
 
             /* Clear the keyboard flags */
             Bda->KeybdShiftFlags = 0;