Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / subsystems / mvdm / ntvdm / bios / bios32 / bios32.c
diff --git a/subsystems/mvdm/ntvdm/bios/bios32/bios32.c b/subsystems/mvdm/ntvdm/bios/bios32/bios32.c
new file mode 100644 (file)
index 0000000..1a65f0e
--- /dev/null
@@ -0,0 +1,1293 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            subsystems/mvdm/ntvdm/bios/bios32/bios32.c
+ * PURPOSE:         VDM 32-bit BIOS
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* BIOS Version number and Copyright */
+#include <reactos/buildno.h>
+#include <reactos/version.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#include "emulator.h"
+#include "cpu/cpu.h" // for EMULATOR_FLAG_CF
+#include "cpu/bop.h"
+#include "int32.h"
+#include <isvbop.h>
+
+#include <bios/bios.h>
+#include <bios/rom.h>
+#include "bios32.h"
+#include "bios32p.h"
+#include "dskbios32.h"
+#include "kbdbios32.h"
+#include "vidbios32.h"
+#include "moubios32.h"
+
+#include "memory.h"
+#include "io.h"
+#include "hardware/cmos.h"
+#include "hardware/pic.h"
+#include "hardware/pit.h"
+#include "hardware/ps2.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+CALLBACK16 BiosContext;
+
+/*
+
+Bochs BIOS, see rombios.h
+=========================
+
+// model byte 0xFC = AT
+#define SYS_MODEL_ID     0xFC
+#define SYS_SUBMODEL_ID  0x00
+#define BIOS_REVISION    1
+#define BIOS_CONFIG_TABLE 0xe6f5
+
+#ifndef BIOS_BUILD_DATE
+#  define BIOS_BUILD_DATE "06/23/99"
+#endif
+
+// 1K of base memory used for Extended Bios Data Area (EBDA)
+// EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
+#define EBDA_SEG           0x9FC0
+#define EBDA_SIZE          1              // In KiB
+#define BASE_MEM_IN_K   (640 - EBDA_SIZE)
+
+
+See rombios.c
+=============
+
+ROM BIOS compatibility entry points:
+===================================
+$e05b ; POST Entry Point
+$e2c3 ; NMI Handler Entry Point
+$e3fe ; INT 13h Fixed Disk Services Entry Point
+$e401 ; Fixed Disk Parameter Table
+$e6f2 ; INT 19h Boot Load Service Entry Point
+$e6f5 ; Configuration Data Table
+$e729 ; Baud Rate Generator Table
+$e739 ; INT 14h Serial Communications Service Entry Point
+$e82e ; INT 16h Keyboard Service Entry Point
+$e987 ; INT 09h Keyboard Service Entry Point
+$ec59 ; INT 13h Diskette Service Entry Point
+$ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
+$efc7 ; Diskette Controller Parameter Table
+$efd2 ; INT 17h Printer Service Entry Point
+$f045 ; INT 10 Functions 0-Fh Entry Point
+$f065 ; INT 10h Video Support Service Entry Point
+$f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
+$f841 ; INT 12h Memory Size Service Entry Point
+$f84d ; INT 11h Equipment List Service Entry Point
+$f859 ; INT 15h System Services Entry Point
+$fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+$fe6e ; INT 1Ah Time-of-day Service Entry Point
+$fea5 ; INT 08h System Timer ISR Entry Point
+$fef3 ; Initial Interrupt Vector Offsets Loaded by POST
+$ff53 ; IRET Instruction for Dummy Interrupt Handler
+$ff54 ; INT 05h Print Screen Service Entry Point
+$fff0 ; Power-up Entry Point
+$fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
+$fffe ; System Model ID
+
+*/
+
+/*
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
+ * for more information.
+ */
+#define BIOS_MODEL      0xFC // PC-AT
+#define BIOS_SUBMODEL   0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
+#define BIOS_REVISION   0x00
+// FIXME: Find a nice PS/2 486 + 487 BIOS combination!
+
+static const BIOS_CONFIG_TABLE BiosConfigTable =
+{
+    sizeof(BIOS_CONFIG_TABLE) - sizeof(((BIOS_CONFIG_TABLE*)0)->Length),    // Length: Number of bytes following
+
+    BIOS_MODEL,     // BIOS Model
+    BIOS_SUBMODEL,  // BIOS Sub-Model
+    BIOS_REVISION,  // BIOS Revision
+
+    // Feature bytes
+    {
+        0x78,       // At the moment we don't have any Extended BIOS Area; see http://www.ctyme.com/intr/rb-1594.htm#Table510
+        0x00,       // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
+        0x10,       // Bit 4: POST supports ROM-to-RAM enable/disable
+        0x00,
+        0x00
+    }
+};
+
+
+/*
+ * WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
+ * Some programs otherwise look for "COPR. IBM" at F000:E008 .
+ */
+static const CHAR BiosCopyright[] = "0000000 NTVDM IBM COMPATIBLE 486 BIOS COPYRIGHT (C) ReactOS Team 1996-"COPYRIGHT_YEAR;
+static const CHAR BiosVersion[]   = "ReactOS NTVDM 32-bit BIOS Version "KERNEL_VERSION_STR"\0"
+                                    "BIOS32 Version "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")";
+static const CHAR BiosDate[]      = "06/17/13";
+
+C_ASSERT(sizeof(BiosCopyright)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
+C_ASSERT(sizeof(BiosDate)-1      == 0x08);
+
+/* 16-bit bootstrap code at F000:FFF0 */
+static const BYTE Bootstrap[] =
+{
+    0xEA,                   // jmp far ptr
+    0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
+};
+
+/*
+ * POST code at F000:E05B. All the POST is done in 32 bit
+ * and only at the end it calls the bootstrap interrupt.
+ */
+static const BYTE PostCode[] =
+{
+    LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_RESET,  // Call BIOS POST
+    0xCD, BIOS_BOOTSTRAP_LOADER,                            // INT 0x19
+    0xCD, BIOS_ROM_BASIC,                                   // INT 0x18
+    LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
+};
+
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static VOID BiosCharPrint(CHAR Character)
+{
+    /* Save AX and BX */
+    USHORT AX = getAX();
+    USHORT BX = getBX();
+
+    /*
+     * Set the parameters:
+     * AL contains the character to print,
+     * BL contains the character attribute,
+     * BH contains the video page to use.
+     */
+    setAL(Character);
+    setBL(DEFAULT_ATTRIBUTE);
+    setBH(Bda->VideoPage);
+
+    /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
+    setAH(0x0E);
+    Int32Call(&BiosContext, BIOS_VIDEO_INTERRUPT);
+
+    /* Restore AX and BX */
+    setBX(BX);
+    setAX(AX);
+}
+
+static VOID WINAPI BiosException(LPWORD Stack)
+{
+    /* Get the exception number and call the emulator API */
+    BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]);
+    EmulatorException(ExceptionNumber, 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);
+}
+
+static VOID WINAPI BiosMiscService(LPWORD Stack)
+{
+    switch (getAH())
+    {
+        /* OS Hooks for Multitasking */
+        case 0x80:  // Device Open
+        case 0x81:  // Device Close
+        case 0x82:  // Program Termination
+        case 0x90:  // Device Busy
+        case 0x91:  // Device POST
+        {
+            /* Return success by default */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Wait on External Event */
+        case 0x41:
+        {
+            BYTE Value;
+            BOOLEAN Return;
+            static DWORD StartingCount;
+
+            /* Check if this is the first time this BOP occurred */
+            if (!getCF())
+            {
+                /* Set the starting count */
+                StartingCount = Bda->TickCounter;
+            }
+
+            if (getBL() != 0 && (Bda->TickCounter - StartingCount) >= getBL())
+            {
+                /* Timeout expired */
+                setCF(0);
+                break;
+            }
+
+            if (getAL() & (1 << 4))
+            {
+                /* Read from the I/O port */
+                Value = IOReadB(getDX());
+            }
+            else
+            {
+                /* Read from the memory */
+                Value = *(LPBYTE)SEG_OFF_TO_PTR(getES(), getDI());
+            }
+
+            switch (getAL() & 7)
+            {
+                /* Any external event */
+                case 0:
+                {
+                    /* Return if this is not the first time the BOP occurred */
+                    Return = getCF();
+                    break;
+                }
+
+                /* Compare and return if equal */
+                case 1:
+                {
+                    Return = Value == getBH();
+                    break;
+                }
+
+                /* Compare and return if not equal */
+                case 2:
+                {
+                    Return = Value != getBH();
+                    break;
+                }
+
+                /* Test and return if not zero */
+                case 3:
+                {
+                    Return = (Value & getBH()) != 0;
+                    break;
+                }
+
+                /* Test and return if zero */
+                case 4:
+                {
+                    Return = (Value & getBH()) == 0;
+                    break;
+                }
+
+                default:
+                {
+                    DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
+                    Return = TRUE;
+                    break;
+                }
+            }
+
+            /* Repeat the BOP if we shouldn't return */
+            setCF(!Return);
+            break;
+        }
+
+        /* Keyboard intercept */
+        case 0x4F:
+        {
+            /* CF should be set but let's just set it again just in case */
+            /* Do not modify AL (the hardware scan code), but set CF to continue processing */
+            // setCF(1);
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Wait */
+        case 0x86:
+        {
+            /*
+             * Interval in microseconds in CX:DX
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
+             * for more information.
+             */
+
+            static ULONG CompletionTime = 0;
+
+            /* Check if we're already looping */
+            if (getCF())
+            {
+                if (GetTickCount() >= CompletionTime)
+                {
+                    /* Stop looping */
+                    setCF(0);
+
+                    /* Clear the CF on the stack too */
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                }
+            }
+            else
+            {
+                /* Set the CF on the stack */
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+                /* Set the completion time and start looping */
+                CompletionTime = GetTickCount() + (MAKELONG(getDX(), getCX()) / 1000);
+                setCF(1);
+            }
+
+            break;
+        }
+
+        /* 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:
+        {
+            UCHAR Low, High;
+
+            /*
+             * Return the (usable) extended memory (after 1 MB)
+             * size in kB from CMOS.
+             */
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW  | CMOS_DISABLE_NMI);
+            Low  = IOReadB(CMOS_DATA_PORT);
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH | CMOS_DISABLE_NMI);
+            High = IOReadB(CMOS_DATA_PORT);
+            setAX(MAKEWORD(Low, High));
+
+            /* Clear CF */
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Switch to Protected Mode */
+        case 0x89:
+        {
+            DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
+
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+            goto Default;
+        }
+
+        /* Get Configuration */
+        case 0xC0:
+        {
+            /* Return the BIOS ROM Configuration Table address in ES:BX */
+            // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
+            setES(BIOS_SEGMENT);
+            setBX(0xE6F5);
+
+            /* Call successful; clear CF */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Return Extended-Bios Data-Area Segment Address (PS) */
+        case 0xC1:
+        {
+            /* We do not support EBDA yet */
+            UNIMPLEMENTED;
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+            goto Default;
+        }
+
+        /* Pointing Device BIOS Interface (PS) */
+        case 0xC2:
+        {
+            // FIXME: Reenable this call when we understand why
+            // our included mouse driver doesn't correctly reeanble
+            // mouse reporting!
+            // BiosMousePs2Interface(Stack);
+            // break;
+            goto Default;
+        }
+
+        /* Get CPU Type and Mask Revision */
+        case 0xC9:
+        {
+            /*
+             * We can see this function as a CPUID replacement.
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-1613.htm
+             * for more information.
+             */
+
+            /*
+             * Fast486 is a 486DX with FPU included,
+             * but old enough to not support CPUID.
+             */
+            setCX(0x0400);
+
+            /* Call successful; clear CF */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Get System Memory Map */
+        case 0xE8:
+        {
+            if (getAL() == 0x01)
+            {
+                /* The amount of memory between 1M and 16M, in kilobytes */
+                ULONG Above1M = (min(MAX_ADDRESS, 0x01000000) - 0x00100000) >> 10;
+
+                /* The amount of memory above 16M, in 64K blocks */
+                ULONG Above16M = (MAX_ADDRESS > 0x01000000) ? ((MAX_ADDRESS - 0x01000000) >> 16) : 0;
+
+                setAX(Above1M);
+                setBX(Above16M);
+                setCX(Above1M);
+                setDX(Above16M);
+
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else if (getAL() == 0x20 && getEDX() == 'SMAP')
+            {
+                ULONG Offset = getEBX();
+                ULONG Length;
+                ULONG BytesWritten = 0;
+                BOOLEAN Hooked;
+                PBIOS_MEMORY_MAP Map = (PBIOS_MEMORY_MAP)SEG_OFF_TO_PTR(getES(), getDI());
+
+                /* Assume the buffer won't be large enough */
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+                while (BytesWritten < getECX() && (ULONG_PTR)Map < (MAX_ADDRESS - sizeof(BIOS_MEMORY_MAP)))
+                {
+                    /* Let's ask our memory controller */
+                    if (!MemQueryMemoryZone(Offset, &Length, &Hooked))
+                    {
+                        /* No more memory blocks */
+                        Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                        break;
+                    }
+
+                    Map->BaseAddress = (ULONGLONG)Offset;
+                    Map->Length = (ULONGLONG)Length;
+                    Map->Type = Hooked ? BIOS_MEMORY_RESERVED : BIOS_MEMORY_AVAILABLE;
+
+                    /* Go to the next record */
+                    Map++;
+                    Offset += Length;
+                    BytesWritten += sizeof(BIOS_MEMORY_MAP);
+                }
+
+                setEAX('SMAP');
+                setEBX(Offset);
+                setECX(BytesWritten);
+            }
+            else
+            {
+                DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
+                        getAL(), getEDX());
+            }
+
+            break;
+        }
+
+        default: Default:
+        {
+            DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
+                    getAH());
+
+            /*
+             * The original signification of the error code 0x86 is that
+             * no PC Cassette is present. The CF is also set in this case.
+             * To keep backward compatibility, newer BIOSes use this value
+             * to indicate an unimplemented call in INT 15h.
+             */
+            setAH(0x86);
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+        }
+    }
+}
+
+static VOID WINAPI BiosRomBasic(LPWORD Stack)
+{
+    PrintMessageAnsi(BiosCharPrint, "FATAL: INT18: BOOT FAILURE.");
+
+    /* ROM Basic is unsupported, display a message to the user */
+    DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing.");
+
+    /* Stop the VDM */
+    EmulatorTerminate();
+}
+
+
+extern VOID DosBootsectorInitialize(VOID);
+extern VOID WINAPI BiosDiskService(LPWORD Stack);
+
+static VOID WINAPI BiosBootstrapLoader(LPWORD Stack)
+{
+    USHORT BootOrder;
+
+    USHORT AX, BX, CX, DX, ES;
+    AX = getAX();
+    BX = getBX();
+    CX = getCX();
+    DX = getDX();
+    ES = getES();
+
+    /*
+     * Read the boot sequence order from the CMOS, old behaviour AMI-style.
+     * 
+     * For more information, see:
+     * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/orgs.asm
+     * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/boot.c
+     * http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/cmos.cc
+     * https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html
+     * http://www.bioscentral.com/misc/cmosmap.htm
+     */
+    IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SYSOP);
+    BootOrder = (IOReadB(CMOS_DATA_PORT) & 0x20) >> 5;
+
+    /*
+     * BootOrder =
+     * 0: Hard Disk, then Floppy Disk
+     * 1: Floppy Disk, then Hard Disk
+     * In all cases, if booting from those devices failed,
+     * ROM DOS-32 is started. If it fails, INT 18h is called.
+     */
+
+    DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder);
+
+    /*
+     * Format of the BootOrder command:
+     * 2 bytes. Each half-byte contains the ID of the drive to boot.
+     * Currently defined:
+     * 0x0: 1st Floppy disk
+     * 0x1: 1st Hard disk
+     * Other, or 0xF: Stop boot sequence.
+     */
+    BootOrder = 0xFF00 | ((1 << (4 * BootOrder)) & 0xFF);
+
+Retry:
+    switch (BootOrder & 0x0F)
+    {
+        /* Boot from 1st floppy drive */
+        case 0:
+        {
+            setAH(0x02); // Read sectors
+            setAL(0x01); // Number of sectors
+            setDH(0x00); // Head 0
+            setCH(0x00); // Cylinder 0
+            setCL(0x01); // Sector 1
+            setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
+            setES(0x0000);  // Write data in 0000:7C00
+            setBX(0x7C00);
+            BiosDiskService(Stack);
+            if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
+            DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
+
+            break;
+        }
+
+        /* Boot from 1st HDD drive */
+        case 1:
+        {
+            setAH(0x02); // Read sectors
+            setAL(0x01); // Number of sectors
+            setDH(0x00); // Head 0
+            setCH(0x00); // Cylinder 0
+            setCL(0x01); // Sector 1
+            setDL(0x80); // First HDD drive (used by loader code, so should not be cleared)
+            setES(0x0000);  // Write data in 0000:7C00
+            setBX(0x7C00);
+            BiosDiskService(Stack);
+            if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
+            DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
+
+            break;
+        }
+
+        default:
+            goto StartDos;
+    }
+
+    /* Go to next drive and invalidate the last half-byte. */
+    BootOrder = (BootOrder >> 4) | 0xF000;
+    goto Retry;
+
+StartDos:
+    /* Clear everything, we are going to load DOS32 */
+    setAX(AX);
+    setBX(BX);
+    setCX(CX);
+    setDX(DX);
+    setES(ES);
+    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+    /* Load our DOS */
+    DosBootsectorInitialize();
+
+Quit:
+    /*
+     * Jump to 0000:7C00 to boot the OS.
+     *
+     * Since we are called via the INT32 mechanism, we need to correctly set
+     * CS:IP, not by changing the current one (otherwise the interrupt could
+     * not be clean up and return properly), but by changing the CS:IP in the
+     * stack, so that when the interrupt returns, the modified CS:IP is popped
+     * off the stack and the CPU is correctly repositioned.
+     */
+    Stack[STACK_CS] = 0x0000;
+    Stack[STACK_IP] = 0x7C00;
+
+    DPRINT("<-- BiosBootstrapLoader\n");
+}
+
+static VOID WINAPI BiosTimeService(LPWORD Stack)
+{
+    switch (getAH())
+    {
+        /* Get System Time */
+        case 0x00:
+        {
+            /* Set AL to 1 if midnight had passed, 0 otherwise */
+            setAL(Bda->MidnightPassed ? 0x01 : 0x00);
+
+            /* Return the tick count in CX:DX */
+            setCX(HIWORD(Bda->TickCounter));
+            setDX(LOWORD(Bda->TickCounter));
+
+            /* Reset the midnight flag */
+            Bda->MidnightPassed = FALSE;
+
+            break;
+        }
+
+        /* Set System Time */
+        case 0x01:
+        {
+            /* Set the tick count to CX:DX */
+            Bda->TickCounter = MAKELONG(getDX(), getCX());
+
+            /* Reset the midnight flag */
+            Bda->MidnightPassed = FALSE;
+
+            break;
+        }
+
+        /* Get Real-Time Clock Time */
+        case 0x02:
+        {
+            UCHAR StatusB;
+
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_HOURS);
+            setCH(IOReadB(CMOS_DATA_PORT));
+
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_MINUTES);
+            setCL(IOReadB(CMOS_DATA_PORT));
+
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SECONDS);
+            setDH(IOReadB(CMOS_DATA_PORT));
+
+            /* Daylight Savings Time */
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_STATUS_B);
+            StatusB = IOReadB(CMOS_DATA_PORT);
+            setDL(StatusB & 0x01);
+
+            /* Clear CF */
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        // /* Set Real-Time Clock Time */
+        // case 0x03:
+        // {
+        //     break;
+        // }
+
+        /* Get Real-Time Clock Date */
+        case 0x04:
+        {
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_CENTURY);
+            setCH(IOReadB(CMOS_DATA_PORT));
+
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_YEAR);
+            setCL(IOReadB(CMOS_DATA_PORT));
+
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_MONTH);
+            setDH(IOReadB(CMOS_DATA_PORT));
+
+            IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_DAY);
+            setDL(IOReadB(CMOS_DATA_PORT));
+
+            /* Clear CF */
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        // /* Set Real-Time Clock Date */
+        // case 0x05:
+        // {
+        //     break;
+        // }
+
+        default:
+        {
+            DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
+                    getAH());
+        }
+    }
+}
+
+static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
+{
+    /* Increase the system tick count */
+    Bda->TickCounter++;
+}
+
+
+// From SeaBIOS
+static VOID PicSetIRQMask(USHORT off, USHORT on)
+{
+    UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8;
+    IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on);
+    IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on);
+}
+
+// From SeaBIOS
+VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func)
+{
+    UCHAR vector;
+
+    PicSetIRQMask(1 << hwirq, 0);
+    if (hwirq < 8)
+        vector = BIOS_PIC_MASTER_INT + hwirq;
+    else
+        vector = BIOS_PIC_SLAVE_INT  + hwirq - 8;
+
+    RegisterBiosInt32(vector, func);
+}
+
+
+VOID PicIRQComplete(BYTE IntNum)
+{
+    /*
+     * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
+     */
+    if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
+    {
+        /* It was an IRQ from the master PIC */
+        IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
+    }
+    else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
+    {
+        /* It was an IRQ from the slave PIC */
+        IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
+        IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
+    }
+}
+
+static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack)
+{
+    BYTE IrqNumber;
+
+    IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
+    IrqNumber = IOReadB(PIC_MASTER_CMD);
+
+    DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber);
+
+    PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
+}
+
+static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack)
+{
+    BYTE IrqNumber;
+
+    IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
+    IrqNumber = IOReadB(PIC_SLAVE_CMD);
+
+    DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber);
+
+    PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
+}
+
+// Timer IRQ 0
+static VOID WINAPI BiosTimerIrq(LPWORD Stack)
+{
+    /*
+     * Perform the system timer interrupt.
+     *
+     * Do not call directly BiosSystemTimerInterrupt(Stack);
+     * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
+     * for their purpose...
+     */
+
+    WORD AX = getAX();
+    WORD CX = getCX();
+    WORD DX = getDX();
+    WORD BX = getBX();
+    WORD BP = getBP();
+    WORD SI = getSI();
+    WORD DI = getDI();
+    WORD DS = getDS();
+    WORD ES = getES();
+
+    Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT);
+
+    setAX(AX);
+    setCX(CX);
+    setDX(DX);
+    setBX(BX);
+    setBP(BP);
+    setSI(SI);
+    setDI(DI);
+    setDS(DS);
+    setES(ES);
+    setCF(0);
+
+    // BiosSystemTimerInterrupt(Stack);
+    PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
+}
+
+
+static VOID BiosHwSetup(VOID)
+{
+    /* Initialize the master and the slave PICs (cascade mode) */
+    IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
+    IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
+
+    /*
+     * Set the interrupt vector offsets for each PIC
+     * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
+     */
+    IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
+    IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT );
+
+    /* Tell the master PIC that there is a slave PIC at IRQ 2 */
+    IOWriteB(PIC_MASTER_DATA, 1 << 2);
+    /* Tell the slave PIC its cascade identity */
+    IOWriteB(PIC_SLAVE_DATA , 2);
+
+    /* Make sure both PICs are in 8086 mode */
+    IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086);
+    IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086);
+
+    /* Clear the masks for both PICs */
+    // IOWriteB(PIC_MASTER_DATA, 0x00);
+    // IOWriteB(PIC_SLAVE_DATA , 0x00);
+    /* Disable all IRQs */
+    IOWriteB(PIC_MASTER_DATA, 0xFF);
+    IOWriteB(PIC_SLAVE_DATA , 0xFF);
+
+
+    /* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
+    // NOTE: Some BIOSes set it to Mode 3 instead.
+    IOWriteB(PIT_COMMAND_PORT, 0x34);
+    // 18.2Hz refresh rate
+    IOWriteB(PIT_DATA_PORT(0), 0x00);
+    IOWriteB(PIT_DATA_PORT(0), 0x00);
+
+    /* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
+    IOWriteB(PIT_COMMAND_PORT, 0x54);
+    // DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
+    IOWriteB(PIT_DATA_PORT(1),   18);
+
+    /* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
+    IOWriteB(PIT_COMMAND_PORT, 0xB6);
+    // Count for 440Hz
+    IOWriteB(PIT_DATA_PORT(2), 0x97);
+    IOWriteB(PIT_DATA_PORT(2), 0x0A);
+
+
+    /* Initialize PS/2 keyboard port */
+    // Enable the port
+    IOWriteB(PS2_CONTROL_PORT, 0xAE);
+    // Port interrupts and clock enabled,
+    // enable keyboard scancode translation.
+    // POST passed, force keyboard unlocking.
+    IOWriteB(PS2_CONTROL_PORT, 0x60);
+    IOWriteB(PS2_DATA_PORT   , 0x6D);
+    // Enable data reporting
+    IOWriteB(PS2_DATA_PORT   , 0xF4);
+
+    EnableHwIRQ(0, BiosTimerIrq);
+}
+
+static VOID InitializeBiosInt32(VOID)
+{
+    USHORT i;
+
+    /* Initialize the callback context */
+    InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000);
+
+    /* Register the default BIOS interrupt vectors */
+
+    /*
+     * Zero out all of the IVT (0x00 -- 0xFF). Some applications
+     * indeed expect to have free vectors at the end of the IVT.
+     */
+    RtlZeroMemory(BaseAddress, 0x0100 * sizeof(ULONG));
+
+#if defined(ADVANCED_DEBUGGING) && (ADVANCED_DEBUGGING_LEVEL >= 3)
+    // Initialize all the interrupt vectors to the default one.
+    for (i = 0x00; i <= 0xFF; i++)
+        RegisterBiosInt32(i, NULL);
+#endif
+
+    /* Initialize the exception interrupt vectors to a default Exception handler */
+    for (i = 0x00; i <= 0x07; i++)
+        RegisterBiosInt32(i, BiosException);
+
+    /* Initialize HW interrupt vectors to a default HW handler */
+    for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) // 0x08 -- 0x0F
+        RegisterBiosInt32(i, BiosHandleMasterPicIRQ);
+    for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT  + 8; i++) // 0x70 -- 0x77
+        RegisterBiosInt32(i, BiosHandleSlavePicIRQ);
+
+    /* Initialize software vector handlers */
+    // BIOS_VIDEO_INTERRUPT  : 0x10 (vidbios32.c)
+    RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService    );
+    RegisterBiosInt32(BIOS_MEMORY_SIZE        , BiosGetMemorySize       );
+    // BIOS_DISK_INTERRUPT   : 0x13 (dskbios32.c)
+    // BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
+    RegisterBiosInt32(BIOS_MISC_INTERRUPT     , BiosMiscService         );
+    // BIOS_KBD_INTERRUPT    : 0x16 (kbdbios32.c)
+    // BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
+    RegisterBiosInt32(BIOS_ROM_BASIC          , BiosRomBasic            );
+    RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER   , BiosBootstrapLoader     );
+    RegisterBiosInt32(BIOS_TIME_INTERRUPT     , BiosTimeService         );
+    // BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
+    RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
+
+    /* Vectors that should be implemented (see above) */
+    RegisterBiosInt32(0x14, NULL);
+    RegisterBiosInt32(0x17, NULL);
+    RegisterBiosInt32(0x1B, NULL);
+    RegisterBiosInt32(0x4A, NULL); // User Alarm Handler
+
+    /* Relocated services by the BIOS (when needed) */
+    RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
+    RegisterBiosInt32(0x42, NULL); // Relocated Default INT 10h Video Services
+
+    /* Miscellaneous unimplemented vector handlers that should better have a default one */
+    RegisterBiosInt32(0x4B, NULL); // Virtual DMA Specification Services
+    RegisterBiosInt32(0x5C, NULL); // NetBIOS
+
+    // ROM-BASIC interrupts span from 0x80 up to 0xEF.
+    // They don't have any default handler at the moment.
+
+    /* Some vectors are in fact addresses to tables */
+    ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL; // Video Parameter Tables
+    ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL; // Diskette Parameters
+    ((PULONG)BaseAddress)[0x1F] = (ULONG)NULL; // 8x8 Graphics Font
+    ((PULONG)BaseAddress)[0x41] = (ULONG)NULL; // Hard Disk 0 Parameter Table Address
+    ((PULONG)BaseAddress)[0x43] = (ULONG)NULL; // Character Table (EGA, MCGA, VGA)
+    ((PULONG)BaseAddress)[0x46] = (ULONG)NULL; // Hard Disk 1 Drive Parameter Table Address
+    /* Tables that are always uninitialized */
+    ((PULONG)BaseAddress)[0x44] = (ULONG)NULL; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
+    ((PULONG)BaseAddress)[0x48] = (ULONG)NULL; // Cordless Keyboard Translation (PCjr)
+    ((PULONG)BaseAddress)[0x49] = (ULONG)NULL; // Non-Keyboard Scan-code Translation Table (PCJr)
+}
+
+static VOID InitializeBiosData(VOID)
+{
+    UCHAR Low, High;
+
+    /* Initialize the BDA contents */
+    RtlZeroMemory(Bda, sizeof(*Bda));
+
+    /*
+     * Retrieve the basic equipment list from the CMOS
+     */
+    IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_EQUIPMENT_LIST | CMOS_DISABLE_NMI);
+    Bda->EquipmentList = IOReadB(CMOS_DATA_PORT);
+    // TODO: Update it if required.
+    Bda->EquipmentList &= 0x00FF; // High byte cleared for now...
+
+    /*
+     * Retrieve the conventional memory size
+     * in kB from the CMOS, typically 640 kB.
+     */
+    IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW  | CMOS_DISABLE_NMI);
+    Low  = IOReadB(CMOS_DATA_PORT);
+    IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH | CMOS_DISABLE_NMI);
+    High = IOReadB(CMOS_DATA_PORT);
+    Bda->MemorySize = MAKEWORD(Low, High);
+}
+
+
+/*
+ * The BIOS POST (Power On-Self Test)
+ */
+/*static*/ VOID
+WINAPI
+Bios32Post(LPWORD Stack)
+{
+    static BOOLEAN FirstBoot = TRUE;
+    BYTE ShutdownStatus;
+
+    /*
+     * Initialize BIOS/Keyboard/Video RAM dynamic data
+     */
+
+    DPRINT("Bios32Post\n");
+
+    /* Disable interrupts */
+    setIF(0);
+
+    /* Set the data segment */
+    setDS(BDA_SEGMENT);
+
+    /* Initialize the stack */
+    // Temporary stack for POST (to be used only before initializing the INT vectors)
+    // setSS(0x0000);
+    // setSP(0x0400);
+    //
+    // Stack to be used after the initialization of the INT vectors
+    setSS(0x0000);  // Stack at 00:8000, going downwards
+    setSP(0x8000);
+
+    /*
+     * Perform early CMOS shutdown status checks
+     */
+
+    /* Read the CMOS shutdown status byte and reset it */
+    IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
+    ShutdownStatus = IOReadB(CMOS_DATA_PORT);
+    IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
+    IOWriteB(CMOS_DATA_PORT, 0x00);
+
+    DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
+            Bda->SoftReset, ShutdownStatus);
+
+    switch (ShutdownStatus)
+    {
+        /* Shutdown after Memory Tests (unsupported) */
+        case 0x01: case 0x02: case 0x03:
+        /* Shutdown after Protected Mode Tests (unsupported) */
+        case 0x06: case 0x07: case 0x08:
+        /* Shutdown after Block Move Test (unsupported) */
+        case 0x09:
+        {
+            DisplayMessage(L"Unsupported CMOS Shutdown Status value 0x%02X. The VDM will shut down.", ShutdownStatus);
+            EmulatorTerminate();
+            return;
+        }
+
+        /* Shutdown to Boot Loader */
+        case 0x04:
+        {
+            DPRINT1("Fast restart to Bootstrap Loader...\n");
+            goto Quit; // Reenable interrupts and exit.
+        }
+
+        /* Flush keyboard, issue an EOI... */
+        case 0x05:
+        {
+            IOReadB(PS2_DATA_PORT);
+
+            /* Send EOI */
+            IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
+            IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
+
+            // Fall back
+        }
+
+        /*
+         * ... and far JMP to user-specified location at 0040:0067
+         * (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
+         */
+        case 0x0A:
+        {
+            DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
+                    HIWORD(Bda->ResumeEntryPoint),
+                    LOWORD(Bda->ResumeEntryPoint));
+
+            /* Position execution pointers and return with interrupts disabled */
+            setCS(HIWORD(Bda->ResumeEntryPoint));
+            setIP(LOWORD(Bda->ResumeEntryPoint));
+            return;
+        }
+
+        /* Soft reset or unexpected shutdown... */
+        case 0x00:
+        /* ... or other possible shutdown codes: just continue the POST */
+        default:
+            break;
+    }
+
+    /*
+     * FIXME: UNIMPLEMENTED!
+     * Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
+     * following actions:
+     * - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
+     * - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
+     */
+    switch (Bda->SoftReset)
+    {
+        case 0x0000:
+        {
+            if (!FirstBoot)
+            {
+                DisplayMessage(L"NTVDM is performing a COLD reboot! The program you are currently testing does not seem to behave correctly! The VDM will shut down...");
+                EmulatorTerminate();
+                return;
+            }
+            break;
+        }
+
+        case 0x1234:
+        {
+            DisplayMessage(L"NTVDM is performing a WARM reboot! This is not supported at the moment. The VDM will shut down...");
+            EmulatorTerminate();
+            return;
+        }
+
+        default:
+            break;
+    }
+
+    FirstBoot = FALSE;
+
+    /* Initialize the BDA */
+    InitializeBiosData();
+
+    /* Initialize the User Data Area at 0050:XXXX */
+    RtlZeroMemory(SEG_OFF_TO_PTR(0x50, 0x0000), sizeof(USER_DATA_AREA));
+
+
+
+    //////// NOTE: This is more or less bios-specific ////////
+
+    /*
+     * Initialize IVT and hardware
+     */
+
+    // WriteUnProtectRom(...);
+
+    /* Register the BIOS 32-bit Interrupts */
+    InitializeBiosInt32();
+
+    /* Initialize platform hardware (PIC/PIT chips, ...) */
+    BiosHwSetup();
+
+    /* Initialize the Keyboard, Video and Mouse BIOS */
+    KbdBios32Post();
+    VidBiosPost();
+    MouseBios32Post();
+    DiskBios32Post();
+
+    // WriteProtectRom(...);
+
+    //////// End of more or less bios-specific section ///////
+
+
+
+    SearchAndInitRoms(&BiosContext);
+
+    /*
+     * End of the 32-bit POST portion. We then fall back into 16-bit where
+     * the rest of the POST code is executed, typically calling INT 19h
+     * to boot up the OS.
+     */
+
+Quit:
+    /* Enable interrupts */
+    setIF(1);
+}
+
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BOOLEAN Bios32Initialize(VOID)
+{
+    /*
+     * Initialize BIOS/Keyboard/Video ROM static data
+     */
+
+    /* System BIOS Copyright */
+    RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE000), BiosCopyright, sizeof(BiosCopyright)-1);
+
+    /* System BIOS Version */
+    RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE070), BiosVersion, sizeof(BiosVersion)-1);
+
+    /* System BIOS Date */
+    RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFF5), BiosDate, sizeof(BiosDate)-1);
+
+    /* Bootstrap code */
+    RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE05B), PostCode , sizeof(PostCode ));
+    RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFF0), Bootstrap, sizeof(Bootstrap));
+
+    /* BIOS ROM Information */
+    RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE6F5), &BiosConfigTable, sizeof(BiosConfigTable));
+
+    /* System BIOS Model (same as Bct->Model) */
+    *(PBYTE)(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFFE)) = BIOS_MODEL;
+
+    /* Initialize the Keyboard and Video BIOS */
+    if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
+    {
+        /* Stop the VDM */
+        EmulatorTerminate();
+        return FALSE;
+    }
+
+    /* Redefine our POST function */
+    RegisterBop(BOP_RESET, Bios32Post);
+
+    WriteProtectRom((PVOID)TO_LINEAR(BIOS_SEGMENT, 0x0000),
+                    ROM_AREA_END - TO_LINEAR(BIOS_SEGMENT, 0x0000) + 1);
+
+    /* We are done */
+    return TRUE;
+}
+
+VOID Bios32Cleanup(VOID)
+{
+    DiskBios32Cleanup();
+    MouseBios32Cleanup();
+    VidBios32Cleanup();
+    KbdBiosCleanup();
+}
+
+/* EOF */