[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 "cmos.h"
16 #include "bios.h"
17 #include "speaker.h"
18 #include "vga.h"
19 #include "dos.h"
20 #include "timer.h"
21 #include "pic.h"
22 #include "ps2.h"
23
24 /*
25 * Activate this line if you want to be able to test NTVDM with:
26 * ntvdm.exe <program>
27 */
28 #define TESTING
29
30 /* PUBLIC VARIABLES ***********************************************************/
31
32 BOOLEAN VdmRunning = TRUE;
33 LPVOID BaseAddress = NULL;
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 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
45 MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
46 va_end(Parameters);
47 }
48
49 BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
50 {
51 switch (ControlType)
52 {
53 case CTRL_C_EVENT:
54 case CTRL_BREAK_EVENT:
55 {
56 /* Perform interrupt 0x23 */
57 EmulatorInterrupt(0x23);
58 break;
59 }
60 default:
61 {
62 /* Stop the VDM if the user logs out or closes the console */
63 VdmRunning = FALSE;
64 }
65 }
66 return TRUE;
67 }
68
69 INT wmain(INT argc, WCHAR *argv[])
70 {
71 INT i;
72 CHAR CommandLine[DOS_CMDLINE_LENGTH];
73 LARGE_INTEGER StartPerfCount;
74 LARGE_INTEGER Frequency, LastTimerTick, LastRtcTick, Counter;
75 LONGLONG TimerTicks;
76 DWORD StartTickCount, CurrentTickCount;
77 DWORD LastClockUpdate;
78 DWORD LastVerticalRefresh;
79 DWORD LastCyclePrintout;
80 DWORD Cycles = 0;
81 INT KeyboardIntCounter = 0;
82
83 /* Set the handler routine */
84 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
85
86 #ifndef TESTING
87 UNREFERENCED_PARAMETER(argc);
88 UNREFERENCED_PARAMETER(argv);
89
90 /* The DOS command line must be ASCII */
91 WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, sizeof(CommandLine), NULL, NULL);
92 #else
93 if (argc == 2 && argv[1] != NULL)
94 {
95 WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
96 }
97 else
98 {
99 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
100 L"Usage: NTVDM <executable>\n");
101 return 0;
102 }
103 #endif
104
105 DPRINT1("\n\n\nNTVDM - Starting '%s'...\n\n\n", CommandLine);
106
107 /* Initialize the emulator */
108 if (!EmulatorInitialize())
109 {
110 wprintf(L"FATAL: Failed to initialize the CPU emulator\n");
111 goto Cleanup;
112 }
113
114 /* Initialize the performance counter (needed for hardware timers) */
115 if (!QueryPerformanceFrequency(&Frequency))
116 {
117 wprintf(L"FATAL: Performance counter not available\n");
118 goto Cleanup;
119 }
120
121 /* Initialize the PIC */
122 if (!PicInitialize())
123 {
124 wprintf(L"FATAL: Failed to initialize the PIC.\n");
125 goto Cleanup;
126 }
127
128 /* Initialize the PIT */
129 if (!PitInitialize())
130 {
131 wprintf(L"FATAL: Failed to initialize the PIT.\n");
132 goto Cleanup;
133 }
134
135 /* Initialize the CMOS */
136 if (!CmosInitialize())
137 {
138 wprintf(L"FATAL: Failed to initialize the VDM CMOS.\n");
139 goto Cleanup;
140 }
141
142 /* Initialize the PC Speaker */
143 SpeakerInitialize();
144
145 /* Initialize the system BIOS */
146 if (!BiosInitialize())
147 {
148 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
149 goto Cleanup;
150 }
151
152 /* Initialize the VDM DOS kernel */
153 if (!DosInitialize())
154 {
155 wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
156 goto Cleanup;
157 }
158
159 /* Start the process from the command line */
160 if (!DosCreateProcess(CommandLine, 0))
161 {
162 DisplayMessage(L"Could not start program: %S", CommandLine);
163 goto Cleanup;
164 }
165
166 /* Find the starting performance and tick count */
167 StartTickCount = GetTickCount();
168 QueryPerformanceCounter(&StartPerfCount);
169
170 /* Set the different last counts to the starting count */
171 LastClockUpdate = LastVerticalRefresh = LastCyclePrintout = StartTickCount;
172
173 /* Set the last timer ticks to the current time */
174 LastTimerTick = LastRtcTick = StartPerfCount;
175
176 /* Main loop */
177 while (VdmRunning)
178 {
179 DWORD PitResolution = PitGetResolution();
180 DWORD RtcFrequency = RtcGetTicksPerSecond();
181
182 /* Get the current number of ticks */
183 CurrentTickCount = GetTickCount();
184
185 if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
186 {
187 /* Calculate the approximate performance counter value instead */
188 Counter.QuadPart = StartPerfCount.QuadPart
189 + (CurrentTickCount - StartTickCount)
190 * (Frequency.QuadPart / 1000);
191 }
192 else
193 {
194 /* Get the current performance counter value */
195 QueryPerformanceCounter(&Counter);
196 }
197
198 /* Get the number of PIT ticks that have passed */
199 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
200 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
201
202 /* Update the PIT */
203 if (TimerTicks > 0)
204 {
205 PitDecrementCount(TimerTicks);
206 LastTimerTick = Counter;
207 }
208
209 /* Check for RTC update */
210 if ((CurrentTickCount - LastClockUpdate) >= 1000)
211 {
212 RtcTimeUpdate();
213 LastClockUpdate = CurrentTickCount;
214 }
215
216 /* Check for RTC periodic tick */
217 if ((Counter.QuadPart - LastRtcTick.QuadPart)
218 >= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
219 {
220 RtcPeriodicTick();
221 LastRtcTick = Counter;
222 }
223
224 /* Check for vertical retrace */
225 if ((CurrentTickCount - LastVerticalRefresh) >= 15)
226 {
227 VgaRefreshDisplay();
228 LastVerticalRefresh = CurrentTickCount;
229 }
230
231 KeyboardIntCounter++;
232 if (KeyboardIntCounter == KBD_INT_CYCLES)
233 {
234 GenerateKeyboardInterrupts();
235 KeyboardIntCounter = 0;
236 }
237
238 /* Horizontal retrace occurs as fast as possible */
239 VgaHorizontalRetrace();
240
241 /* Continue CPU emulation */
242 for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
243 {
244 EmulatorStep();
245 Cycles++;
246 }
247
248 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
249 {
250 DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
251 LastCyclePrintout = CurrentTickCount;
252 Cycles = 0;
253 }
254 }
255
256 /* Perform another screen refresh */
257 VgaRefreshDisplay();
258
259 Cleanup:
260 SpeakerCleanup();
261 BiosCleanup();
262 CmosCleanup();
263 EmulatorCleanup();
264
265 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
266
267 return 0;
268 }
269
270 /* EOF */