[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
82 /* Set the handler routine */
83 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
84
85 #ifndef TESTING
86 UNREFERENCED_PARAMETER(argc);
87 UNREFERENCED_PARAMETER(argv);
88
89 /* The DOS command line must be ASCII */
90 WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, sizeof(CommandLine), NULL, NULL);
91 #else
92 if (argc == 2 && argv[1] != NULL)
93 {
94 WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
95 }
96 else
97 {
98 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
99 L"Usage: NTVDM <executable>\n");
100 return 0;
101 }
102 #endif
103
104 DPRINT1("\n\n\nNTVDM - Starting '%s'...\n\n\n", CommandLine);
105
106 /* Initialize the emulator */
107 if (!EmulatorInitialize())
108 {
109 wprintf(L"FATAL: Failed to initialize the CPU emulator\n");
110 goto Cleanup;
111 }
112
113 /* Initialize the performance counter (needed for hardware timers) */
114 if (!QueryPerformanceFrequency(&Frequency))
115 {
116 wprintf(L"FATAL: Performance counter not available\n");
117 goto Cleanup;
118 }
119
120 /* Initialize the PIC */
121 if (!PicInitialize())
122 {
123 wprintf(L"FATAL: Failed to initialize the PIC.\n");
124 goto Cleanup;
125 }
126
127 /* Initialize the PIT */
128 if (!PitInitialize())
129 {
130 wprintf(L"FATAL: Failed to initialize the PIT.\n");
131 goto Cleanup;
132 }
133
134 /* Initialize the CMOS */
135 if (!CmosInitialize())
136 {
137 wprintf(L"FATAL: Failed to initialize the VDM CMOS.\n");
138 goto Cleanup;
139 }
140
141 /* Initialize the PC Speaker */
142 SpeakerInitialize();
143
144 /* Initialize the system BIOS */
145 if (!BiosInitialize())
146 {
147 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
148 goto Cleanup;
149 }
150
151 /* Initialize the VDM DOS kernel */
152 if (!DosInitialize())
153 {
154 wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
155 goto Cleanup;
156 }
157
158 /* Start the process from the command line */
159 if (!DosCreateProcess(CommandLine, 0))
160 {
161 DisplayMessage(L"Could not start program: %S", CommandLine);
162 goto Cleanup;
163 }
164
165 /* Find the starting performance and tick count */
166 StartTickCount = GetTickCount();
167 QueryPerformanceCounter(&StartPerfCount);
168
169 /* Set the different last counts to the starting count */
170 LastClockUpdate = LastVerticalRefresh = LastCyclePrintout = StartTickCount;
171
172 /* Set the last timer ticks to the current time */
173 LastTimerTick = LastRtcTick = StartPerfCount;
174
175 /* Main loop */
176 while (VdmRunning)
177 {
178 DWORD PitResolution = PitGetResolution();
179 DWORD RtcFrequency = RtcGetTicksPerSecond();
180
181 /* Get the current number of ticks */
182 CurrentTickCount = GetTickCount();
183
184 if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
185 {
186 /* Calculate the approximate performance counter value instead */
187 Counter.QuadPart = StartPerfCount.QuadPart
188 + (CurrentTickCount - StartTickCount)
189 * (Frequency.QuadPart / 1000);
190 }
191 else
192 {
193 /* Get the current performance counter value */
194 QueryPerformanceCounter(&Counter);
195 }
196
197 /* Get the number of PIT ticks that have passed */
198 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
199 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
200
201 /* Update the PIT */
202 if (TimerTicks > 0)
203 {
204 PitDecrementCount(TimerTicks);
205 LastTimerTick = Counter;
206 }
207
208 /* Check for RTC update */
209 if ((CurrentTickCount - LastClockUpdate) >= 1000)
210 {
211 RtcTimeUpdate();
212 LastClockUpdate = CurrentTickCount;
213 }
214
215 /* Check for RTC periodic tick */
216 if ((Counter.QuadPart - LastRtcTick.QuadPart)
217 >= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
218 {
219 RtcPeriodicTick();
220 LastRtcTick = Counter;
221 }
222
223 /* Check for vertical retrace */
224 if ((CurrentTickCount - LastVerticalRefresh) >= 15)
225 {
226 VgaRefreshDisplay();
227 LastVerticalRefresh = CurrentTickCount;
228 }
229
230 /* Horizontal retrace occurs as fast as possible */
231 VgaHorizontalRetrace();
232
233 /* Continue CPU emulation */
234 for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
235 {
236 EmulatorStep();
237 Cycles++;
238 }
239
240 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
241 {
242 DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
243 LastCyclePrintout = CurrentTickCount;
244 Cycles = 0;
245 }
246 }
247
248 /* Perform another screen refresh */
249 VgaRefreshDisplay();
250
251 Cleanup:
252 SpeakerCleanup();
253 BiosCleanup();
254 CmosCleanup();
255 EmulatorCleanup();
256
257 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
258
259 return 0;
260 }
261
262 /* EOF */