[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 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15 #include "bios.h"
16 #include "vga.h"
17 #include "dos.h"
18 #include "timer.h"
19 #include "pic.h"
20 #include "ps2.h"
21
22 /*
23 * Activate this line if you want to be able to test NTVDM with:
24 * ntvdm.exe <program>
25 */
26 #define TESTING
27
28 /* PUBLIC VARIABLES ***********************************************************/
29
30 BOOLEAN VdmRunning = TRUE;
31 LPVOID BaseAddress = NULL;
32 LPCWSTR ExceptionName[] =
33 {
34 L"Division By Zero",
35 L"Debug",
36 L"Unexpected Error",
37 L"Breakpoint",
38 L"Integer Overflow",
39 L"Bound Range Exceeded",
40 L"Invalid Opcode",
41 L"FPU Not Available"
42 };
43
44 /* PUBLIC FUNCTIONS ***********************************************************/
45
46 VOID DisplayMessage(LPCWSTR Format, ...)
47 {
48 WCHAR Buffer[256];
49 va_list Parameters;
50
51 va_start(Parameters, Format);
52 _vsnwprintf(Buffer, 256, Format, Parameters);
53 MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
54 va_end(Parameters);
55 }
56
57 BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
58 {
59 switch (ControlType)
60 {
61 case CTRL_C_EVENT:
62 case CTRL_BREAK_EVENT:
63 {
64 /* Perform interrupt 0x23 */
65 EmulatorInterrupt(0x23);
66 break;
67 }
68 default:
69 {
70 /* Stop the VDM if the user logs out or closes the console */
71 VdmRunning = FALSE;
72 }
73 }
74 return TRUE;
75 }
76
77 INT wmain(INT argc, WCHAR *argv[])
78 {
79 INT i;
80 CHAR CommandLine[DOS_CMDLINE_LENGTH];
81 DWORD CurrentTickCount;
82 DWORD Cycles = 0;
83 DWORD LastCyclePrintout = GetTickCount();
84 DWORD LastVerticalRefresh = GetTickCount();
85 LARGE_INTEGER Frequency, LastTimerTick, Counter;
86 LONGLONG TimerTicks;
87 HANDLE InputThread = NULL;
88
89 /* Set the handler routine */
90 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
91
92 #ifndef TESTING
93 UNREFERENCED_PARAMETER(argc);
94 UNREFERENCED_PARAMETER(argv);
95
96 /* The DOS command line must be ASCII */
97 WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, sizeof(CommandLine), NULL, NULL);
98 #else
99 if (argc == 2 && argv[1] != NULL)
100 {
101 WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
102 }
103 else
104 {
105 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
106 L"Usage: NTVDM <executable>\n");
107 return 0;
108 }
109 #endif
110
111 if (!EmulatorInitialize())
112 {
113 wprintf(L"FATAL: Failed to initialize the CPU emulator\n");
114 goto Cleanup;
115 }
116
117 /* Initialize the performance counter (needed for hardware timers) */
118 if (!QueryPerformanceFrequency(&Frequency))
119 {
120 wprintf(L"FATAL: Performance counter not available\n");
121 goto Cleanup;
122 }
123
124 /* Initialize the system BIOS */
125 if (!BiosInitialize())
126 {
127 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
128 goto Cleanup;
129 }
130
131 /* Initialize the VDM DOS kernel */
132 if (!DosInitialize())
133 {
134 wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
135 goto Cleanup;
136 }
137
138 /* Start the process from the command line */
139 if (!DosCreateProcess(CommandLine, 0))
140 {
141 DisplayMessage(L"Could not start program: %S", CommandLine);
142 return -1;
143 }
144
145 /* Start the input thread */
146 InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL);
147
148 /* Set the last timer tick to the current time */
149 QueryPerformanceCounter(&LastTimerTick);
150
151 /* Main loop */
152 while (VdmRunning)
153 {
154 /* Get the resolution of the system timer */
155 DWORD TimerResolution = PitGetResolution();
156
157 /* Get the current number of ticks */
158 CurrentTickCount = GetTickCount();
159
160 if (TimerResolution > 1000)
161 {
162 /* Get the current performance counter value */
163 QueryPerformanceCounter(&Counter);
164
165 /* Get the number of PIT ticks that have passed */
166 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
167 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
168 }
169 else
170 {
171 /* Use the standard tick count */
172 Counter.QuadPart = CurrentTickCount;
173
174 /* Get the number of PIT ticks that have passed */
175 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
176 * PIT_BASE_FREQUENCY) / 1000;
177 }
178
179 /* Update the PIT */
180 if (TimerTicks > 0)
181 {
182 for (i = 0; i < TimerTicks; i++) PitDecrementCount();
183 LastTimerTick = Counter;
184 }
185
186 /* Check for vertical retrace */
187 if ((CurrentTickCount - LastVerticalRefresh) >= 16)
188 {
189 VgaRefreshDisplay();
190 LastVerticalRefresh = CurrentTickCount;
191 }
192
193 /* Horizontal retrace occurs as fast as possible */
194 VgaHorizontalRetrace();
195
196 /* Continue CPU emulation */
197 for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
198 {
199 EmulatorStep();
200 Cycles++;
201 }
202
203 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
204 {
205 DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
206 LastCyclePrintout = CurrentTickCount;
207 Cycles = 0;
208 }
209 }
210
211 /* Perform another screen refresh */
212 VgaRefreshDisplay();
213
214 Cleanup:
215 if (InputThread != NULL) CloseHandle(InputThread);
216 BiosCleanup();
217 EmulatorCleanup();
218
219 return 0;
220 }
221
222 /* EOF */