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