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