[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
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 current number of ticks */
144 CurrentTickCount = GetTickCount();
145
146 /* Get the current performance counter value */
147 QueryPerformanceCounter(&Counter);
148
149 /* Get the number of PIT ticks that have passed */
150 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
151 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
152
153 /* Update the PIT */
154 if (TimerTicks > 0)
155 {
156 PitDecrementCount(TimerTicks);
157 LastTimerTick = Counter;
158 }
159
160 /* Check for vertical retrace */
161 if ((CurrentTickCount - LastVerticalRefresh) >= 16)
162 {
163 VgaRefreshDisplay();
164 LastVerticalRefresh = CurrentTickCount;
165 }
166
167 /* Horizontal retrace occurs as fast as possible */
168 VgaHorizontalRetrace();
169
170 /* Continue CPU emulation */
171 for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
172 {
173 EmulatorStep();
174 Cycles++;
175 }
176
177 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
178 {
179 DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
180 LastCyclePrintout = CurrentTickCount;
181 Cycles = 0;
182 }
183 }
184
185 /* Perform another screen refresh */
186 VgaRefreshDisplay();
187
188 Cleanup:
189 if (InputThread != NULL) CloseHandle(InputThread);
190 BiosCleanup();
191 EmulatorCleanup();
192
193 return 0;
194 }
195
196 /* EOF */