Exemplo n.º 1
0
Arquivo: cpu.c Projeto: staring/RosFE
VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
{
    WORD CodeSegment, InstructionPointer;
    PBYTE Opcode;

    ASSERT(ExceptionNumber < 8);

    /* Get the CS:IP */
    InstructionPointer = Stack[STACK_IP];
    CodeSegment = Stack[STACK_CS];
    Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);

    /* Display a message to the user */
    DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
                   L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
                   ExceptionName[ExceptionNumber],
                   CodeSegment,
                   InstructionPointer,
                   Opcode[0],
                   Opcode[1],
                   Opcode[2],
                   Opcode[3],
                   Opcode[4],
                   Opcode[5],
                   Opcode[6],
                   Opcode[7],
                   Opcode[8],
                   Opcode[9]);

    /* Stop the VDM */
    EmulatorTerminate();
    return;
}
Exemplo n.º 2
0
static VOID WINAPI DosSystemBop(LPWORD Stack)
{
    /* Get the Function Number and skip it */
    BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
    setIP(getIP() + 1);

    switch (FuncNum)
    {
        case 0x11:  // Load the DOS kernel
        {
            BOOLEAN Success = FALSE;
            HANDLE  hDosKernel;
            ULONG   ulDosKernelSize = 0;

            DPRINT1("You are loading Windows NT DOS!\n");

            /* Open the DOS kernel file */
            hDosKernel = FileOpen("ntdos.sys", &ulDosKernelSize);

            /* If we failed, bail out */
            if (hDosKernel == NULL) goto Quit;

            /*
             * Attempt to load the DOS kernel into memory.
             * The segment where to load the DOS kernel is defined
             * by the DOS BIOS and is found in DI:0000 .
             */
            Success = FileLoadByHandle(hDosKernel,
                                       REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
                                       ulDosKernelSize,
                                       &ulDosKernelSize);

            DPRINT1("Windows NT DOS loading %s at 0x%04X:0x%04X, size 0x%x ; GetLastError() = %u\n",
                    (Success ? "succeeded" : "failed"),
                    getDI(), 0x0000,
                    ulDosKernelSize,
                    GetLastError());

            /* Close the DOS kernel file */
            FileClose(hDosKernel);

Quit:
            if (!Success)
            {
                /* We failed everything, stop the VDM */
                EmulatorTerminate();
            }

            break;
        }

        default:
        {

            DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
            // setCF(1); // Disable, otherwise we enter an infinite loop
            break;
        }
    }
}
Exemplo n.º 3
0
VOID
WINAPI
VDDTerminateVDM(VOID)
{
    /* Stop the VDM */
    EmulatorTerminate();
}
Exemplo n.º 4
0
VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
{
    switch (MenuEvent->dwCommandId)
    {
        case ID_SHOWHIDE_MOUSE:
            ShowHideMousePointer(ConsoleOutput, ShowPointer);
            ShowPointer = !ShowPointer;
            break;

        case ID_VDM_DUMPMEM_TXT:
            DumpMemory(TRUE);
            break;

        case ID_VDM_DUMPMEM_BIN:
            DumpMemory(FALSE);
            break;

        case ID_VDM_QUIT:
            /* Stop the VDM */
            EmulatorTerminate();
            break;

        default:
            break;
    }
}
Exemplo n.º 5
0
static BOOL
WINAPI
ConsoleCtrlHandler(DWORD ControlType)
{
    switch (ControlType)
    {
        case CTRL_C_EVENT:
        case CTRL_BREAK_EVENT:
        {
            /* HACK: Stop the VDM */
            DPRINT1("Ctrl-C/Break: Stop the VDM\n");
            EmulatorTerminate();
            break;
        }
        case CTRL_LAST_CLOSE_EVENT:
        {
            if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
            {
                /* Exit immediately */
#ifndef STANDALONE
                if (CommandThread) TerminateThread(CommandThread, 0);
#endif
                EmulatorTerminate();
            }
#ifndef STANDALONE
            else
            {
                /* Stop accepting new commands */
                AcceptCommands = FALSE;
            }
#endif

            break;
        }
        default:
        {
            /* Stop the VDM if the user logs out or closes the console */
            EmulatorTerminate();
        }
    }
    return TRUE;
}
Exemplo n.º 6
0
Arquivo: dem.c Projeto: GYGit/reactos
static VOID DemLoadNTDOSKernel(VOID)
{
    BOOLEAN Success = FALSE;
    LPCSTR  DosKernelFileName = "ntdos.sys";
    HANDLE  hDosKernel;
    ULONG   ulDosKernelSize = 0;

    DPRINT1("You are loading Windows NT DOS!\n");

    /* Open the DOS kernel file */
    hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize);
    if (hDosKernel == NULL) goto Quit;

    /*
     * Attempt to load the DOS kernel into memory.
     * The segment where to load the DOS kernel is defined
     * by the DOS BIOS and is found in DI:0000 .
     */
    Success = FileLoadByHandle(hDosKernel,
                               REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
                               ulDosKernelSize,
                               &ulDosKernelSize);

    DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
            DosKernelFileName,
            (Success ? "succeeded" : "failed"),
            getDI(), 0x0000,
            ulDosKernelSize,
            GetLastError());

    /* Close the DOS kernel file */
    FileClose(hDosKernel);

Quit:
    if (!Success)
    {
        /* We failed everything, stop the VDM */
        BiosDisplayMessage("Windows NT DOS kernel file '%s' loading failed (Error: %u). The VDM will shut down.\n",
                           DosKernelFileName, GetLastError());
        EmulatorTerminate();
        return;
    }
}
Exemplo n.º 7
0
Arquivo: cpu.c Projeto: staring/RosFE
VOID CpuSimulate(VOID)
{
    if (CpuCallLevel > MaxCpuCallLevel)
    {
        DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
                       CpuCallLevel, MaxCpuCallLevel);

        /* Stop the VDM */
        EmulatorTerminate();
        return;
    }
    CpuCallLevel++;
    DPRINT("CpuSimulate --> Level %d\n", CpuCallLevel);

    CpuRunning = TRUE;
    while (VdmRunning && CpuRunning) ClockUpdate();

    DPRINT("CpuSimulate <-- Level %d\n", CpuCallLevel);
    CpuCallLevel--;
    if (!VdmRunning || CpuCallLevel < 0) CpuCallLevel = 0;

    /* This takes into account for reentrance */
    if (VdmRunning && (CpuCallLevel > 0)) CpuRunning = TRUE;
}
Exemplo n.º 8
0
Arquivo: dem.c Projeto: GYGit/reactos
static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
{
    /* Get the Function Number and skip it */
    BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
    setIP(getIP() + 1);

    switch (FuncNum)
    {
        /* Kill the VDM */
        case 0x00:
        {
            /* Stop the VDM */
            EmulatorTerminate();
            return;
        }

        /*
         * Get a new app to start
         *
         * Input
         *     DS:DX : Data block.
         *
         * Output
         *     CF    : 0: Success; 1: Failure.
         */
        case 0x01:
        {
            CmdStartProcess();
            break;
        }

        /*
         * Check binary format
         *
         * Input
         *     DS:DX : Program to check.
         *
         * Output
         *     CF    : 0: Success; 1: Failure.
         *     AX    : Error code.
         */
        case 0x07:
        {
            DWORD BinaryType;
            LPSTR ProgramName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());

            if (!GetBinaryTypeA(ProgramName, &BinaryType))
            {
                /* An error happened, bail out */
                setCF(1);
                setAX(LOWORD(GetLastError()));
                break;
            }

            // FIXME: We only support DOS binaries for now...
            ASSERT(BinaryType == SCS_DOS_BINARY);
            if (BinaryType != SCS_DOS_BINARY)
            {
                /* An error happened, bail out */
                setCF(1);
                setAX(LOWORD(ERROR_BAD_EXE_FORMAT));
                break;
            }

            /* Return success: DOS application */
            setCF(0);
            break;
        }

        /*
         * Start an external command
         *
         * Input
         *     DS:SI : Command to start.
         *     ES    : Environment block segment.
         *     AL    : Current drive number.
         *     AH    : 0: Directly start the command;
         *             1: Use "cmd.exe /c" to start the command.
         *
         * Output
         *     CF    : 0: Shell-out; 1: Continue.
         *     AL    : Error/Exit code.
         */
        case 0x08:
        {
            CmdStartExternalCommand();
            break;
        }

        /*
         * Start the default 32-bit command interpreter (COMSPEC)
         *
         * Input
         *     ES    : Environment block segment.
         *     AL    : Current drive number.
         *
         * Output
         *     CF    : 0: Shell-out; 1: Continue.
         *     AL    : Error/Exit code.
         */
        case 0x0A:
        {
            CmdStartComSpec32();
            break;
        }

        /*
         * Set exit code
         *
         * Input
         *     DX    : Exit code
         *
         * Output
         *     CF    : 0: Shell-out; 1: Continue.
         */
        case 0x0B:
        {
            CmdSetExitCode();
            break;
        }

        /*
         * Get start information
         *
         * Output
         *     AL    : 0 (resp. 1): Started from (resp. without) an existing console.
         */
        case 0x10:
        {
#ifndef STANDALONE
            /*
             * When a new instance of our (internal) COMMAND.COM is started,
             * we check whether we need to run a 32-bit COMSPEC. This goes by
             * checking whether we were started in a new console (no parent
             * console process) or from an existing one.
             *
             * However COMMAND.COM can also be started in the case where a
             * 32-bit process (started by a 16-bit parent) wants to start a new
             * 16-bit process: to ensure DOS reentry we need to start a new
             * instance of COMMAND.COM. On Windows the COMMAND.COM is started
             * just before the 32-bit process (in fact, it is this COMMAND.COM
             * which starts the 32-bit process via an undocumented command-line
             * switch '/z', which syntax is:
             *     COMMAND.COM /z\bAPPNAME.EXE
             * notice the '\b' character inserted in-between. Then COMMAND.COM
             * issues a BOP_CMD 08h with AH=00h to start the process).
             *
             * Instead, we do the reverse, i.e. we start the 32-bit process,
             * and *only* if needed, i.e. if this process wants to start a
             * new 16-bit process, we start our COMMAND.COM.
             *
             * The problem we then face is that our COMMAND.COM will possibly
             * want to start a new COMSPEC, however we do not want this.
             * The chosen solution is to flag this case -- done with the 'Reentry'
             * boolean -- so that COMMAND.COM will not attempt to start COMSPEC
             * but instead will directly try to start the 16-bit process.
             */
            // setAL(SessionId != 0);
            setAL((SessionId != 0) && !Reentry);
            /* Reset 'Reentry' */
            Reentry = FALSE;
#else
            setAL(0);
#endif
            break;
        }

        default:
        {
            DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum);
            // setCF(1); // Disable, otherwise we enter an infinite loop
            break;
        }
    }
}
Exemplo n.º 9
0
Arquivo: dem.c Projeto: GYGit/reactos
static VOID WINAPI DosStart(LPWORD Stack)
{
    BOOLEAN Success;
    DWORD Result;
#ifndef STANDALONE
    INT i;
#endif

    DPRINT("DosStart\n");

    /*
     * We succeeded, deregister the DOS Starting BOP
     * so that no app will be able to call us back.
     */
    RegisterBop(BOP_START_DOS, NULL);

    /* Initialize the callback context */
    InitializeContext(&DosContext, BIOS_CODE_SEGMENT, 0x0010);

    Success  = DosBIOSInitialize();
//  Success &= DosKRNLInitialize();
    if (!Success)
    {
        BiosDisplayMessage("DOS32 loading failed (Error: %u). The VDM will shut down.\n", GetLastError());
        EmulatorTerminate();
        return;
    }

    /* Load the mouse driver */
    DosMouseInitialize();

#ifndef STANDALONE

    /* Parse the command line arguments */
    for (i = 1; i < NtVdmArgc; i++)
    {
        if (wcsncmp(NtVdmArgv[i], L"-i", 2) == 0)
        {
            /* This is the session ID (hex format) */
            SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 16);
        }
    }

    /* Initialize Win32-VDM environment */
    Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
    if (Env == NULL)
    {
        DosDisplayMessage("Failed to initialize the global environment (Error: %u). The VDM will shut down.\n", GetLastError());
        EmulatorTerminate();
        return;
    }

    /* Clear the structure */
    RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));

    /* Get the initial information */
    CommandInfo.TaskId = SessionId;
    CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS;
    GetNextVDMCommand(&CommandInfo);

#else

    /* Retrieve the command to start */
    if (NtVdmArgc >= 2)
    {
        WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, AppName, sizeof(AppName), NULL, NULL);

        if (NtVdmArgc >= 3)
            WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CmdLine, sizeof(CmdLine), NULL, NULL);
        else
            strcpy(CmdLine, "");
    }
    else
    {
        DosDisplayMessage("Invalid DOS command line\n");
        EmulatorTerminate();
        return;
    }

#endif

    /*
     * At this point, CS:IP points to the DOS BIOS exit code. If the
     * root command interpreter fails to start (or if it exits), DOS
     * exits and the VDM terminates.
     */

    /* Start the root command interpreter */
    // TODO: Really interpret the 'SHELL=' line of CONFIG.NT, and use it!

    /*
     * Prepare the stack for DosStartComSpec:
     * push Flags, CS and IP, and an extra WORD.
     */
    setSP(getSP() - sizeof(WORD));
    *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = (WORD)getEFLAGS();
    setSP(getSP() - sizeof(WORD));
    *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getCS();
    setSP(getSP() - sizeof(WORD));
    *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getIP();
    setSP(getSP() - sizeof(WORD));

    Result = DosStartComSpec(TRUE, SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0),
                             MAKELONG(getIP(), getCS()),
#ifndef STANDALONE
                             &RootCmd.ComSpecPsp
#else
                             NULL
#endif
                             );
    if (Result != ERROR_SUCCESS)
    {
        /* Unprepare the stack for DosStartComSpec */
        setSP(getSP() + 4*sizeof(WORD));

        DosDisplayMessage("Failed to start the Command Interpreter (Error: %u). The VDM will shut down.\n", Result);
        EmulatorTerminate();
        return;
    }

#ifndef STANDALONE
    RootCmd.Terminated = FALSE;
    InsertComSpecInfo(&RootCmd);
#endif

    /**/
    /* Attach to the console and resume the VM */
    DosProcessConsoleAttach();
    EmulatorResume();
    /**/

    return;
}
Exemplo n.º 10
0
VOID WINAPI ThirdPartyVDDBop(LPWORD Stack)
{
    /* Get the Function Number and skip it */
    BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
    setIP(getIP() + 1);

    switch (FuncNum)
    {
        /* RegisterModule */
        case 0:
        {
            BOOL Success = TRUE;
            WORD RetVal  = 0;
            WORD Entry   = 0;
            LPCSTR DllName = NULL,
                   InitRoutineName     = NULL,
                   DispatchRoutineName = NULL;
            HMODULE hDll = NULL;
            VDD_PROC InitRoutine     = NULL,
                     DispatchRoutine = NULL;

            DPRINT("RegisterModule() called\n");

            /* Clear the Carry Flag (no error happened so far) */
            setCF(0);

            /* Retrieve the next free entry in the table (used later on) */
            Entry = GetNextFreeVDDEntry();
            if (Entry >= MAX_VDD_MODULES)
            {
                DPRINT1("Failed to create a new VDD module entry\n");
                Success = FALSE;
                RetVal = 4;
                goto Quit;
            }

            /* Retrieve the VDD name in DS:SI */
            DllName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI());

            /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */
            if (TO_LINEAR(getES(), getDI()) != 0)
                InitRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getES(), getDI());

            /* Retrieve the dispatch routine API name in DS:BX */
            DispatchRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getBX());

            DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n",
                    (DllName ? DllName : "n/a"),
                    (InitRoutineName ? InitRoutineName : "n/a"),
                    (DispatchRoutineName ? DispatchRoutineName : "n/a"));

            /* Load the VDD DLL */
            hDll = LoadLibraryA(DllName);
            if (hDll == NULL)
            {
                DWORD LastError = GetLastError();
                Success = FALSE;

                if (LastError == ERROR_NOT_ENOUGH_MEMORY)
                {
                    DPRINT1("Not enough memory to load DLL '%s'\n", DllName);
                    RetVal = 4;
                    goto Quit;
                }
                else
                {
                    DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName, LastError);
                    RetVal = 1;
                    goto Quit;
                }
            }

            /* Load the initialization routine if needed */
            if (InitRoutineName)
            {
                InitRoutine = (VDD_PROC)GetProcAddress(hDll, InitRoutineName);
                if (InitRoutine == NULL)
                {
                    DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName);
                    Success = FALSE;
                    RetVal = 3;
                    goto Quit;
                }
            }

            /* Load the dispatch routine */
            DispatchRoutine = (VDD_PROC)GetProcAddress(hDll, DispatchRoutineName);
            if (DispatchRoutine == NULL)
            {
                DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName);
                Success = FALSE;
                RetVal = 2;
                goto Quit;
            }

            /* If we arrived there, that means everything is OK */

            /* Register the VDD DLL */
            VDDList[Entry].hDll = hDll;
            VDDList[Entry].DispatchRoutine = DispatchRoutine;

            /* Call the initialization routine if needed */
            if (InitRoutine) InitRoutine();

            /* We succeeded. RetVal will contain a valid VDD DLL handle */
            Success = TRUE;
            RetVal  = ENTRY_TO_HANDLE(Entry); // Convert the entry to a valid handle

Quit:
            if (!Success)
            {
                /* Unload the VDD DLL */
                if (hDll) FreeLibrary(hDll);

                /* Set the Carry Flag to indicate that an error happened */
                setCF(1);
            }
            // else
            // {
                // /* Clear the Carry Flag (success) */
                // setCF(0);
            // }
            setAX(RetVal);
            break;
        }

        /* UnRegisterModule */
        case 1:
        {
            WORD Handle = getAX();
            WORD Entry  = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry

            DPRINT("UnRegisterModule() called\n");

            /* Sanity checks */
            if (!IS_VALID_HANDLE(Handle) || VDDList[Entry].hDll == NULL)
            {
                DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
                /* Stop the VDM */
                EmulatorTerminate();
                return;
            }

            /* Unregister the VDD DLL */
            FreeLibrary(VDDList[Entry].hDll);
            VDDList[Entry].hDll = NULL;
            VDDList[Entry].DispatchRoutine = NULL;
            break;
        }

        /* DispatchCall */
        case 2:
        {
            WORD Handle = getAX();
            WORD Entry  = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry

            DPRINT("DispatchCall() called\n");

            /* Sanity checks */
            if (!IS_VALID_HANDLE(Handle)    ||
                VDDList[Entry].hDll == NULL ||
                VDDList[Entry].DispatchRoutine == NULL)
            {
                DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
                /* Stop the VDM */
                EmulatorTerminate();
                return;
            }

            /* Call the dispatch routine */
            VDDList[Entry].DispatchRoutine();
            break;
        }

        default:
        {
            DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum);
            setCF(1);
            break;
        }
    }
}
Exemplo n.º 11
0
Arquivo: dem.c Projeto: staring/RosFE
static VOID WINAPI DosStart(LPWORD Stack)
{
#ifdef STANDALONE
    DWORD Result;
    CHAR ApplicationName[MAX_PATH];
    CHAR CommandLine[DOS_CMDLINE_LENGTH];
#endif

    DPRINT("DosStart\n");

    /*
     * We succeeded, deregister the DOS Starting BOP
     * so that no app will be able to call us back.
     */
    RegisterBop(BOP_START_DOS, NULL);

    /* Load the mouse driver */
    DosMouseInitialize();

#ifndef STANDALONE

    /* Create the GetNextVDMCommand thread */
    CommandThread = CreateThread(NULL, 0, &CommandThreadProc, NULL, 0, NULL);
    if (CommandThread == NULL)
    {
        wprintf(L"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
        goto Quit;
    }

    /* Wait for the command thread to exit */
    WaitForSingleObject(CommandThread, INFINITE);

    /* Close the thread handle */
    CloseHandle(CommandThread);

#else

    if (NtVdmArgc >= 2)
    {
        WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL);

        if (NtVdmArgc >= 3)
            WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
        else
            strcpy(CommandLine, "");
    }
    else
    {
        DisplayMessage(L"Invalid DOS command line\n");
        goto Quit;
    }

    /* Start the process from the command line */
    DPRINT1("Starting '%s' ('%s')...\n", ApplicationName, CommandLine);
    Result = DosStartProcess(ApplicationName,
                             CommandLine,
                             SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0));
    if (Result != ERROR_SUCCESS)
    {
        DisplayMessage(L"Could not start '%S'. Error: %u", ApplicationName, Result);
        goto Quit;
    }

#endif

Quit:
    /* Stop the VDM */
    EmulatorTerminate();
}
Exemplo n.º 12
0
static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
{
    if (Port == PS2_CONTROL_PORT)
    {
        switch (Data)
        {
        /* Read configuration byte */
        case 0x20:
        {
            OutputBuffer = ControllerConfig;
            StatusRegister |= (1 << 0); // There is something to read
            break;
        }

        /* Write configuration byte */
        case 0x60:
        /* Write controller output port */
        case 0xD1:
        /* Write to the first PS/2 port output buffer */
        case 0xD2:
        /* Write to the second PS/2 port output buffer */
        case 0xD3:
        /* Write to the second PS/2 port input buffer */
        case 0xD4:
        {
            /* These commands require a response */
            ControllerCommand = Data;
            StatusRegister |= (1 << 3); // This is a controller command
            break;
        }

        /* Disable second PS/2 port */
        case 0xA7:
        {
            Ports[1].IsEnabled = FALSE;
            break;
        }

        /* Enable second PS/2 port */
        case 0xA8:
        {
            Ports[1].IsEnabled = TRUE;
            break;
        }

        /* Test second PS/2 port */
        case 0xA9:
        {
            OutputBuffer = 0x00; // Success code
            StatusRegister |= (1 << 0); // There is something to read
            break;
        }

        /* Test PS/2 controller */
        case 0xAA:
        {
            OutputBuffer = 0x55; // Success code
            StatusRegister |= (1 << 0); // There is something to read
            break;
        }

        /* Test first PS/2 port */
        case 0xAB:
        {
            OutputBuffer = 0x00; // Success code
            StatusRegister |= (1 << 0); // There is something to read
            break;
        }

        /* Disable first PS/2 port */
        case 0xAD:
        {
            Ports[0].IsEnabled = FALSE;
            break;
        }

        /* Enable first PS/2 port */
        case 0xAE:
        {
            Ports[0].IsEnabled = TRUE;
            break;
        }

        /* Read controller output port */
        case 0xD0:
        {
            // TODO: Not implemented
            break;
        }

        /* CPU Reset */
        case 0xF0:
        case 0xF2:
        case 0xF4:
        case 0xF6:
        case 0xF8:
        case 0xFA:
        case 0xFC:
        case 0xFE:
        {
            /* Stop the VDM */
            EmulatorTerminate();
            break;
        }
        }
    }
    else if (Port == PS2_DATA_PORT)
    {
        /* Check if the controller is waiting for a response */
        if (StatusRegister & (1 << 3)) // If we have data for the controller
        {
            StatusRegister &= ~(1 << 3);

            /* Check which command it was */
            switch (ControllerCommand)
            {
            /* Write configuration byte */
            case 0x60:
            {
                ControllerConfig = Data;
                break;
            }

            /* Write controller output */
            case 0xD1:
            {
                /* Check if bit 0 is unset */
                if (!(Data & (1 << 0)))
                {
                    /* CPU disabled - Stop the VDM */
                    EmulatorTerminate();
                }

                /* Update the A20 line setting */
                EmulatorSetA20(Data & (1 << 1));

                break;
            }

            /* Push the data byte into the first PS/2 port queue */
            case 0xD2:
            {
                PS2QueuePush(0, Data);
                break;
            }

            /* Push the data byte into the second PS/2 port queue */
            case 0xD3:
            {
                PS2QueuePush(1, Data);
                break;
            }

            /*
             * Send a command to the second PS/2 port (by default
             * it is a command for the first PS/2 port)
             */
            case 0xD4:
            {
                PS2SendCommand(&Ports[1], Data);
                break;
            }
            }

            return;
        }

        /* By default, send a command to the first PS/2 port */
        PS2SendCommand(&Ports[0], Data);
    }
}