[NTVDM]
[reactos.git] / subsystems / ntvdm / ntvdm.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: ntvdm.c
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ntvdm.h"
12 #include "emulator.h"
13 #include "bios.h"
14 #include "dos.h"
15 #include "timer.h"
16 #include "pic.h"
17 #include "ps2.h"
18
19 /* PUBLIC VARIABLES ***********************************************************/
20
21 BOOLEAN VdmRunning = TRUE;
22 LPVOID BaseAddress = NULL;
23 LPCWSTR ExceptionName[] =
24 {
25 L"Division By Zero",
26 L"Debug",
27 L"Unexpected Error",
28 L"Breakpoint",
29 L"Integer Overflow",
30 L"Bound Range Exceeded",
31 L"Invalid Opcode",
32 L"FPU Not Available"
33 };
34
35 /* PUBLIC FUNCTIONS ***********************************************************/
36
37 VOID DisplayMessage(LPCWSTR Format, ...)
38 {
39 WCHAR Buffer[256];
40 va_list Parameters;
41
42 va_start(Parameters, Format);
43 _vsnwprintf(Buffer, 256, Format, Parameters);
44 MessageBox(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
45 va_end(Parameters);
46 }
47
48 BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
49 {
50 switch (ControlType)
51 {
52 case CTRL_C_EVENT:
53 case CTRL_BREAK_EVENT:
54 {
55 /* Perform interrupt 0x23 */
56 EmulatorInterrupt(0x23);
57 break;
58 }
59 default:
60 {
61 /* Stop the VDM if the user logs out or closes the console */
62 VdmRunning = FALSE;
63 }
64 }
65 return TRUE;
66 }
67
68 INT wmain(INT argc, WCHAR *argv[])
69 {
70 INT i;
71 CHAR CommandLine[128];
72 DWORD CurrentTickCount;
73 DWORD LastTickCount = GetTickCount();
74 DWORD Cycles = 0;
75 DWORD LastCyclePrintout = GetTickCount();
76 DWORD LastVerticalRefresh = GetTickCount();
77 LARGE_INTEGER Frequency, LastTimerTick, Counter;
78 LONGLONG TimerTicks;
79
80 /* Set the handler routine */
81 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
82
83 /* The DOS command line must be ASCII */
84 WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, 128, NULL, NULL);
85
86 if (!EmulatorInitialize())
87 {
88 wprintf(L"FATAL: Failed to initialize the CPU emulator\n");
89 goto Cleanup;
90 }
91
92 /* Initialize the performance counter (needed for hardware timers) */
93 if (!QueryPerformanceFrequency(&Frequency))
94 {
95 wprintf(L"FATAL: Performance counter not available\n");
96 goto Cleanup;
97 }
98
99 /* Initialize the system BIOS */
100 if (!BiosInitialize())
101 {
102 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
103 goto Cleanup;
104 }
105
106 /* Initialize the VDM DOS kernel */
107 if (!DosInitialize())
108 {
109 wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
110 goto Cleanup;
111 }
112
113 /* Start the process from the command line */
114 if (!DosCreateProcess(CommandLine, 0))
115 {
116 DisplayMessage(L"Could not start program: %S", CommandLine);
117 return -1;
118 }
119
120 /* Set the last timer tick to the current time */
121 QueryPerformanceCounter(&LastTimerTick);
122
123 /* Main loop */
124 while (VdmRunning)
125 {
126 /* Get the current number of ticks */
127 CurrentTickCount = GetTickCount();
128
129 /* Get the current performance counter value */
130 QueryPerformanceCounter(&Counter);
131
132 /* Get the number of PIT ticks that have passed */
133 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
134 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
135
136 /* Update the PIT */
137 for (i = 0; i < TimerTicks; i++) PitDecrementCount();
138 LastTimerTick = Counter;
139
140 /* Check for console input events every millisecond */
141 if (CurrentTickCount != LastTickCount)
142 {
143 CheckForInputEvents();
144 LastTickCount = CurrentTickCount;
145 }
146
147 /* Check for vertical refresh */
148 if ((CurrentTickCount - LastVerticalRefresh) >= 16)
149 {
150 BiosVerticalRefresh();
151 LastVerticalRefresh = CurrentTickCount;
152 }
153
154 /* Continue CPU emulation */
155 for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
156 {
157 EmulatorStep();
158 Cycles++;
159 }
160
161 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
162 {
163 DPRINT1("NTVDM: %d Instructions Per Second\n", Cycles);
164 LastCyclePrintout = CurrentTickCount;
165 Cycles = 0;
166 }
167 }
168
169 Cleanup:
170 BiosCleanup();
171 EmulatorCleanup();
172
173 return 0;
174 }
175
176 /* EOF */