2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
16 #include "bios/bios.h"
17 #include "hardware/cmos.h"
18 #include "hardware/ps2.h"
19 #include "hardware/timer.h"
20 #include "hardware/vga.h"
24 * Activate this line if you want to be able to test NTVDM with:
29 /* PUBLIC VARIABLES ***********************************************************/
31 static HANDLE ConsoleInput
= INVALID_HANDLE_VALUE
;
32 static HANDLE ConsoleOutput
= INVALID_HANDLE_VALUE
;
33 static DWORD OrgConsoleInputMode
, OrgConsoleOutputMode
;
34 static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo
;
35 static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo
;
37 /* PUBLIC FUNCTIONS ***********************************************************/
39 VOID
DisplayMessage(LPCWSTR Format
, ...)
44 va_start(Parameters
, Format
);
45 _vsnwprintf(Buffer
, 256, Format
, Parameters
);
46 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
47 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
51 BOOL WINAPI
ConsoleCtrlHandler(DWORD ControlType
)
56 case CTRL_BREAK_EVENT
:
58 /* Perform interrupt 0x23 */
59 EmulatorInterrupt(0x23);
64 /* Stop the VDM if the user logs out or closes the console */
71 BOOL
ConsoleInit(VOID
)
73 /* Set the handler routine */
74 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
76 /* Get the input handle to the real console, and check for success */
77 ConsoleInput
= CreateFileW(L
"CONIN$",
78 GENERIC_READ
| GENERIC_WRITE
,
79 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
84 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
86 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
90 /* Get the output handle to the real console, and check for success */
91 ConsoleOutput
= CreateFileW(L
"CONOUT$",
92 GENERIC_READ
| GENERIC_WRITE
,
93 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
98 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
100 CloseHandle(ConsoleInput
);
101 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
105 /* Save the original input and output console modes */
106 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
107 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
109 CloseHandle(ConsoleOutput
);
110 CloseHandle(ConsoleInput
);
111 wprintf(L
"FATAL: Cannot save console in/out modes\n");
115 /* Save the original cursor and console screen buffer information */
116 if (!GetConsoleCursorInfo(ConsoleOutput
, &OrgConsoleCursorInfo
) ||
117 !GetConsoleScreenBufferInfo(ConsoleOutput
, &OrgConsoleBufferInfo
))
119 CloseHandle(ConsoleOutput
);
120 CloseHandle(ConsoleInput
);
121 wprintf(L
"FATAL: Cannot save console cursor/screen-buffer info\n");
128 VOID
ConsoleCleanup(VOID
)
131 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo
;
133 /* Restore the old screen buffer */
134 SetConsoleActiveScreenBuffer(ConsoleOutput
);
136 /* Restore the original console size */
137 GetConsoleScreenBufferInfo(ConsoleOutput
, &ConsoleInfo
);
138 ConRect
.Left
= 0; // OrgConsoleBufferInfo.srWindow.Left;
139 // ConRect.Top = ConsoleInfo.dwCursorPosition.Y / (OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top + 1);
140 // ConRect.Top *= (OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top + 1);
141 ConRect
.Top
= ConsoleInfo
.dwCursorPosition
.Y
;
142 ConRect
.Right
= ConRect
.Left
+ OrgConsoleBufferInfo
.srWindow
.Right
- OrgConsoleBufferInfo
.srWindow
.Left
;
143 ConRect
.Bottom
= ConRect
.Top
+ OrgConsoleBufferInfo
.srWindow
.Bottom
- OrgConsoleBufferInfo
.srWindow
.Top
;
144 /* See the following trick explanation in vga.c:VgaEnterTextMode() */
145 SetConsoleScreenBufferSize(ConsoleOutput
, OrgConsoleBufferInfo
.dwSize
);
146 SetConsoleWindowInfo(ConsoleOutput
, TRUE
, &ConRect
);
147 // SetConsoleWindowInfo(ConsoleOutput, TRUE, &OrgConsoleBufferInfo.srWindow);
148 SetConsoleScreenBufferSize(ConsoleOutput
, OrgConsoleBufferInfo
.dwSize
);
150 /* Restore the original cursor shape */
151 SetConsoleCursorInfo(ConsoleOutput
, &OrgConsoleCursorInfo
);
153 /* Restore the original input and output console modes */
154 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
155 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
157 /* Close the console handles */
158 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
159 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
162 INT
wmain(INT argc
, WCHAR
*argv
[])
165 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
166 LARGE_INTEGER StartPerfCount
;
167 LARGE_INTEGER Frequency
, LastTimerTick
, LastRtcTick
, Counter
;
169 DWORD StartTickCount
, CurrentTickCount
;
170 DWORD LastClockUpdate
;
171 DWORD LastVerticalRefresh
;
172 DWORD LastCyclePrintout
;
174 INT KeyboardIntCounter
= 0;
177 UNREFERENCED_PARAMETER(argc
);
178 UNREFERENCED_PARAMETER(argv
);
180 /* The DOS command line must be ASCII */
181 WideCharToMultiByte(CP_ACP
, 0, GetCommandLine(), -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
183 if (argc
== 2 && argv
[1] != NULL
)
185 WideCharToMultiByte(CP_ACP
, 0, argv
[1], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
189 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
190 L
"Usage: NTVDM <executable>\n");
195 DPRINT1("\n\n\nNTVDM - Starting '%s'...\n\n\n", CommandLine
);
197 /* Initialize the console */
200 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
204 /* Initialize the emulator */
205 if (!EmulatorInitialize())
207 wprintf(L
"FATAL: Failed to initialize the emulator\n");
211 /* Initialize the performance counter (needed for hardware timers) */
212 if (!QueryPerformanceFrequency(&Frequency
))
214 wprintf(L
"FATAL: Performance counter not available\n");
218 /* Initialize the system BIOS */
219 if (!BiosInitialize(ConsoleInput
, ConsoleOutput
))
221 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
225 /* Initialize the VDM DOS kernel */
226 if (!DosInitialize())
228 wprintf(L
"FATAL: Failed to initialize the VDM DOS kernel.\n");
232 /* Start the process from the command line */
233 if (!DosCreateProcess(CommandLine
, 0))
235 DisplayMessage(L
"Could not start program: %S", CommandLine
);
239 /* Find the starting performance and tick count */
240 StartTickCount
= GetTickCount();
241 QueryPerformanceCounter(&StartPerfCount
);
243 /* Set the different last counts to the starting count */
244 LastClockUpdate
= LastVerticalRefresh
= LastCyclePrintout
= StartTickCount
;
246 /* Set the last timer ticks to the current time */
247 LastTimerTick
= LastRtcTick
= StartPerfCount
;
252 DWORD PitResolution
= PitGetResolution();
253 DWORD RtcFrequency
= RtcGetTicksPerSecond();
255 /* Get the current number of ticks */
256 CurrentTickCount
= GetTickCount();
258 if ((PitResolution
<= 1000) && (RtcFrequency
<= 1000))
260 /* Calculate the approximate performance counter value instead */
261 Counter
.QuadPart
= StartPerfCount
.QuadPart
262 + (CurrentTickCount
- StartTickCount
)
263 * (Frequency
.QuadPart
/ 1000);
267 /* Get the current performance counter value */
268 QueryPerformanceCounter(&Counter
);
271 /* Get the number of PIT ticks that have passed */
272 TimerTicks
= ((Counter
.QuadPart
- LastTimerTick
.QuadPart
)
273 * PIT_BASE_FREQUENCY
) / Frequency
.QuadPart
;
278 PitDecrementCount(TimerTicks
);
279 LastTimerTick
= Counter
;
282 /* Check for RTC update */
283 if ((CurrentTickCount
- LastClockUpdate
) >= 1000)
286 LastClockUpdate
= CurrentTickCount
;
289 /* Check for RTC periodic tick */
290 if ((Counter
.QuadPart
- LastRtcTick
.QuadPart
)
291 >= (Frequency
.QuadPart
/ (LONGLONG
)RtcFrequency
))
294 LastRtcTick
= Counter
;
297 /* Check for vertical retrace */
298 if ((CurrentTickCount
- LastVerticalRefresh
) >= 15)
301 LastVerticalRefresh
= CurrentTickCount
;
304 KeyboardIntCounter
++;
305 if (KeyboardIntCounter
== KBD_INT_CYCLES
)
307 GenerateKeyboardInterrupts();
308 KeyboardIntCounter
= 0;
311 /* Horizontal retrace occurs as fast as possible */
312 VgaHorizontalRetrace();
314 /* Continue CPU emulation */
315 for (i
= 0; (i
< STEPS_PER_CYCLE
) && VdmRunning
; i
++)
321 if ((CurrentTickCount
- LastCyclePrintout
) >= 1000)
323 DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles
);
324 LastCyclePrintout
= CurrentTickCount
;
329 /* Perform another screen refresh */
337 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");