* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
#include "ntvdm.h"
+#include "emulator.h"
+#include "bios.h"
+#include "vga.h"
+#include "dos.h"
+#include "timer.h"
+#include "pic.h"
+#include "ps2.h"
+
+/*
+ * Activate this line if you want to be able to test NTVDM with:
+ * ntvdm.exe <program>
+ */
+#define TESTING
+
+/* PUBLIC VARIABLES ***********************************************************/
BOOLEAN VdmRunning = TRUE;
LPVOID BaseAddress = NULL;
-LPCWSTR ExceptionName[] =
-{
- L"Division By Zero",
- L"Debug",
- L"Unexpected Error",
- L"Breakpoint",
- L"Integer Overflow",
- L"Bound Range Exceeded",
- L"Invalid Opcode",
- L"FPU Not Available"
-};
+
+/* PUBLIC FUNCTIONS ***********************************************************/
VOID DisplayMessage(LPCWSTR Format, ...)
{
va_start(Parameters, Format);
_vsnwprintf(Buffer, 256, Format, Parameters);
- MessageBox(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
+ MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
va_end(Parameters);
}
{
/* Perform interrupt 0x23 */
EmulatorInterrupt(0x23);
+ break;
}
default:
{
INT wmain(INT argc, WCHAR *argv[])
{
INT i;
- BOOLEAN PrintUsage = TRUE;
- CHAR CommandLine[128];
- DWORD CurrentTickCount, LastTickCount = 0, Cycles = 0, LastCyclePrintout = 0;
+ CHAR CommandLine[DOS_CMDLINE_LENGTH];
+ DWORD CurrentTickCount;
+ DWORD Cycles = 0;
+ DWORD LastCyclePrintout = GetTickCount();
+ DWORD LastVerticalRefresh = GetTickCount();
LARGE_INTEGER Frequency, LastTimerTick, Counter;
LONGLONG TimerTicks;
+ HANDLE InputThread = NULL;
/* Set the handler routine */
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
- /* Parse the command line arguments */
- for (i = 1; i < argc; i++)
- {
- if (argv[i][0] != L'-' && argv[i][0] != L'/') continue;
+#ifndef TESTING
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
- switch (argv[i][1])
- {
- case L'f':
- case L'F':
- {
- if (argv[i+1] != NULL)
- {
- /* The DOS command line must be ASCII */
- WideCharToMultiByte(CP_ACP, 0, argv[i+1], -1, CommandLine, 128, NULL, NULL);
-
- /* This is the only mandatory parameter */
- PrintUsage = FALSE;
- }
- break;
- }
- default:
- {
- wprintf(L"Unknown option: %s", argv[i]);
- }
- }
+ /* The DOS command line must be ASCII */
+ WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, sizeof(CommandLine), NULL, NULL);
+#else
+ if (argc == 2 && argv[1] != NULL)
+ {
+ WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
}
-
- if (PrintUsage)
+ else
{
- wprintf(L"ReactOS Virtual DOS Machine\n\n");
- wprintf(L"Usage: NTVDM /F <PROGRAM>\n");
+ wprintf(L"\nReactOS Virtual DOS Machine\n\n"
+ L"Usage: NTVDM <executable>\n");
return 0;
}
+#endif
- if (!EmulatorInitialize()) return 1;
+ if (!EmulatorInitialize())
+ {
+ wprintf(L"FATAL: Failed to initialize the CPU emulator\n");
+ goto Cleanup;
+ }
/* Initialize the performance counter (needed for hardware timers) */
if (!QueryPerformanceFrequency(&Frequency))
DisplayMessage(L"Could not start program: %S", CommandLine);
return -1;
}
-
+
+ /* Start the input thread */
+ InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL);
+
/* Set the last timer tick to the current time */
QueryPerformanceCounter(&LastTimerTick);
/* Main loop */
while (VdmRunning)
{
+ /* Get the resolution of the system timer */
+ DWORD TimerResolution = PitGetResolution();
+
/* Get the current number of ticks */
CurrentTickCount = GetTickCount();
-
- /* Get the current performance counter value */
- QueryPerformanceCounter(&Counter);
-
- /* Get the number of PIT ticks that have passed */
- TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
- * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
-
+
+ if (TimerResolution > 1000)
+ {
+ /* Get the current performance counter value */
+ QueryPerformanceCounter(&Counter);
+
+ /* Get the number of PIT ticks that have passed */
+ TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
+ * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
+ }
+ else
+ {
+ /* Use the standard tick count */
+ Counter.QuadPart = CurrentTickCount;
+
+ /* Get the number of PIT ticks that have passed */
+ TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
+ * PIT_BASE_FREQUENCY) / 1000;
+ }
+
/* Update the PIT */
- for (i = 0; i < TimerTicks; i++) PitDecrementCount();
- LastTimerTick = Counter;
-
- /* Check for console input events every millisecond */
- if (CurrentTickCount != LastTickCount)
+ if (TimerTicks > 0)
+ {
+ for (i = 0; i < TimerTicks; i++) PitDecrementCount();
+ LastTimerTick = Counter;
+ }
+
+ /* Check for vertical retrace */
+ if ((CurrentTickCount - LastVerticalRefresh) >= 16)
{
- CheckForInputEvents();
- LastTickCount = CurrentTickCount;
+ VgaRefreshDisplay();
+ LastVerticalRefresh = CurrentTickCount;
}
-
+
+ /* Horizontal retrace occurs as fast as possible */
+ VgaHorizontalRetrace();
+
/* Continue CPU emulation */
- for (i = 0; i < STEPS_PER_CYCLE; i++) EmulatorStep();
-
- Cycles += STEPS_PER_CYCLE;
+ for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
+ {
+ EmulatorStep();
+ Cycles++;
+ }
+
if ((CurrentTickCount - LastCyclePrintout) >= 1000)
{
- DPRINT1("NTVDM: %d Instructions Per Second\n", Cycles);
+ DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
LastCyclePrintout = CurrentTickCount;
Cycles = 0;
}
}
+ /* Perform another screen refresh */
+ VgaRefreshDisplay();
+
Cleanup:
+ if (InputThread != NULL) CloseHandle(InputThread);
+ BiosCleanup();
EmulatorCleanup();
return 0;