[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 "speaker.h"
17 #include "vga.h"
18 #include "dos.h"
19 #include "timer.h"
20 #include "pic.h"
21 #include "ps2.h"
22 #include "cmos.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 MessageBoxW(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[DOS_CMDLINE_LENGTH];
72 DWORD CurrentTickCount;
73 DWORD Cycles = 0;
74 DWORD LastCyclePrintout = GetTickCount();
75 DWORD LastVerticalRefresh = GetTickCount();
76 DWORD LastClockUpdate = GetTickCount();
77 LARGE_INTEGER Frequency, LastTimerTick, LastRtcTick, Counter;
78 LONGLONG TimerTicks;
79 LARGE_INTEGER StartPerfCount;
80 DWORD StartTickCount;
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 if (!EmulatorInitialize())
105 {
106 wprintf(L"FATAL: Failed to initialize the CPU emulator\n");
107 goto Cleanup;
108 }
109
110 /* Initialize the performance counter (needed for hardware timers) */
111 if (!QueryPerformanceFrequency(&Frequency))
112 {
113 wprintf(L"FATAL: Performance counter not available\n");
114 goto Cleanup;
115 }
116
117 /* Initialize the system BIOS */
118 if (!BiosInitialize())
119 {
120 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
121 goto Cleanup;
122 }
123
124 /* Initialize the PC Speaker */
125 SpeakerInitialize();
126
127 /* Initialize the VDM DOS kernel */
128 if (!DosInitialize())
129 {
130 wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
131 goto Cleanup;
132 }
133
134 /* Start the process from the command line */
135 if (!DosCreateProcess(CommandLine, 0))
136 {
137 DisplayMessage(L"Could not start program: %S", CommandLine);
138 return -1;
139 }
140
141 /* Find the starting performance and tick count */
142 StartTickCount = GetTickCount();
143 QueryPerformanceCounter(&StartPerfCount);
144
145 /* Set the last timer ticks to the current time */
146 LastTimerTick = LastRtcTick = StartPerfCount;
147
148 /* Main loop */
149 while (VdmRunning)
150 {
151 DWORD PitResolution = PitGetResolution();
152 DWORD RtcFrequency = RtcGetTicksPerSecond();
153
154 /* Get the current number of ticks */
155 CurrentTickCount = GetTickCount();
156
157 if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
158 {
159 /* Calculate the approximate performance counter value instead */
160 Counter.QuadPart = StartPerfCount.QuadPart
161 + (CurrentTickCount - StartTickCount)
162 * (Frequency.QuadPart / 1000);
163 }
164 else
165 {
166 /* Get the current performance counter value */
167 QueryPerformanceCounter(&Counter);
168 }
169
170 /* Get the number of PIT ticks that have passed */
171 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
172 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
173
174 /* Update the PIT */
175 if (TimerTicks > 0)
176 {
177 PitDecrementCount(TimerTicks);
178 LastTimerTick = Counter;
179 }
180
181 /* Check for RTC update */
182 if ((CurrentTickCount - LastClockUpdate) >= 1000)
183 {
184 RtcTimeUpdate();
185 LastClockUpdate = CurrentTickCount;
186 }
187
188 /* Check for RTC periodic tick */
189 if ((Counter.QuadPart - LastRtcTick.QuadPart)
190 >= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
191 {
192 RtcPeriodicTick();
193 LastRtcTick = Counter;
194 }
195
196 /* Check for vertical retrace */
197 if ((CurrentTickCount - LastVerticalRefresh) >= 16)
198 {
199 VgaRefreshDisplay();
200 LastVerticalRefresh = CurrentTickCount;
201 }
202
203 /* Horizontal retrace occurs as fast as possible */
204 VgaHorizontalRetrace();
205
206 /* Continue CPU emulation */
207 for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
208 {
209 EmulatorStep();
210 Cycles++;
211 }
212
213 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
214 {
215 DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
216 LastCyclePrintout = CurrentTickCount;
217 Cycles = 0;
218 }
219 }
220
221 /* Perform another screen refresh */
222 VgaRefreshDisplay();
223
224 Cleanup:
225 SpeakerCleanup();
226 BiosCleanup();
227 EmulatorCleanup();
228
229 return 0;
230 }
231
232 /* EOF */