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