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"
18 #include "hardware/cmos.h"
19 #include "hardware/ps2.h"
20 #include "hardware/timer.h"
21 #include "hardware/vga.h"
24 * Activate this line if you want to be able to test NTVDM with:
30 * Activate IPS_DISPLAY if you want to display the
31 * number of instructions per second, as well as
32 * the computed number of ticks for the PIT.
34 // #define IPS_DISPLAY
37 * Activate WORKING_TIMER when the PIT timing problem is fixed.
39 // #define WORKING_TIMER
41 /* PUBLIC VARIABLES ***********************************************************/
43 static HANDLE ConsoleInput
= INVALID_HANDLE_VALUE
;
44 static HANDLE ConsoleOutput
= INVALID_HANDLE_VALUE
;
45 static DWORD OrgConsoleInputMode
, OrgConsoleOutputMode
;
46 static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo
;
47 static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo
;
49 /* PUBLIC FUNCTIONS ***********************************************************/
51 VOID
DisplayMessage(LPCWSTR Format
, ...)
56 va_start(Parameters
, Format
);
57 _vsnwprintf(Buffer
, 256, Format
, Parameters
);
58 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
59 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
63 BOOL WINAPI
ConsoleCtrlHandler(DWORD ControlType
)
68 case CTRL_BREAK_EVENT
:
71 EmulatorInterrupt(0x23);
76 /* Stop the VDM if the user logs out or closes the console */
83 BOOL
ConsoleInit(VOID
)
85 /* Set the handler routine */
86 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
88 /* Get the input handle to the real console, and check for success */
89 ConsoleInput
= CreateFileW(L
"CONIN$",
90 GENERIC_READ
| GENERIC_WRITE
,
91 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
96 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
98 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
102 /* Get the output handle to the real console, and check for success */
103 ConsoleOutput
= CreateFileW(L
"CONOUT$",
104 GENERIC_READ
| GENERIC_WRITE
,
105 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
110 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
112 CloseHandle(ConsoleInput
);
113 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
117 /* Save the original input and output console modes */
118 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
119 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
121 CloseHandle(ConsoleOutput
);
122 CloseHandle(ConsoleInput
);
123 wprintf(L
"FATAL: Cannot save console in/out modes\n");
127 /* Save the original cursor and console screen buffer information */
128 if (!GetConsoleCursorInfo(ConsoleOutput
, &OrgConsoleCursorInfo
) ||
129 !GetConsoleScreenBufferInfo(ConsoleOutput
, &OrgConsoleBufferInfo
))
131 CloseHandle(ConsoleOutput
);
132 CloseHandle(ConsoleInput
);
133 wprintf(L
"FATAL: Cannot save console cursor/screen-buffer info\n");
140 VOID
ConsoleCleanup(VOID
)
143 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo
;
145 /* Restore the old screen buffer */
146 SetConsoleActiveScreenBuffer(ConsoleOutput
);
148 /* Restore the original console size */
149 GetConsoleScreenBufferInfo(ConsoleOutput
, &ConsoleInfo
);
150 ConRect
.Left
= 0; // OrgConsoleBufferInfo.srWindow.Left;
151 // ConRect.Top = ConsoleInfo.dwCursorPosition.Y / (OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top + 1);
152 // ConRect.Top *= (OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top + 1);
153 ConRect
.Top
= ConsoleInfo
.dwCursorPosition
.Y
;
154 ConRect
.Right
= ConRect
.Left
+ OrgConsoleBufferInfo
.srWindow
.Right
- OrgConsoleBufferInfo
.srWindow
.Left
;
155 ConRect
.Bottom
= ConRect
.Top
+ OrgConsoleBufferInfo
.srWindow
.Bottom
- OrgConsoleBufferInfo
.srWindow
.Top
;
156 /* See the following trick explanation in vga.c:VgaEnterTextMode() */
157 SetConsoleScreenBufferSize(ConsoleOutput
, OrgConsoleBufferInfo
.dwSize
);
158 SetConsoleWindowInfo(ConsoleOutput
, TRUE
, &ConRect
);
159 // SetConsoleWindowInfo(ConsoleOutput, TRUE, &OrgConsoleBufferInfo.srWindow);
160 SetConsoleScreenBufferSize(ConsoleOutput
, OrgConsoleBufferInfo
.dwSize
);
162 /* Restore the original cursor shape */
163 SetConsoleCursorInfo(ConsoleOutput
, &OrgConsoleCursorInfo
);
165 /* Restore the original input and output console modes */
166 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
167 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
169 /* Close the console handles */
170 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
171 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
174 INT
wmain(INT argc
, WCHAR
*argv
[])
177 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
178 LARGE_INTEGER StartPerfCount
;
179 LARGE_INTEGER Frequency
, LastTimerTick
, LastRtcTick
, Counter
;
181 DWORD StartTickCount
, CurrentTickCount
;
182 DWORD LastClockUpdate
;
183 DWORD LastVerticalRefresh
;
185 DWORD LastCyclePrintout
;
188 INT KeyboardIntCounter
= 0;
191 UNREFERENCED_PARAMETER(argc
);
192 UNREFERENCED_PARAMETER(argv
);
194 /* The DOS command line must be ASCII */
195 WideCharToMultiByte(CP_ACP
, 0, GetCommandLine(), -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
197 if (argc
== 2 && argv
[1] != NULL
)
199 WideCharToMultiByte(CP_ACP
, 0, argv
[1], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
203 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
204 L
"Usage: NTVDM <executable>\n");
209 DPRINT1("\n\n\nNTVDM - Starting '%s'...\n\n\n", CommandLine
);
211 /* Initialize the console */
214 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
218 /* Initialize the emulator */
219 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
221 wprintf(L
"FATAL: Failed to initialize the emulator\n");
225 /* Initialize the performance counter (needed for hardware timers) */
226 if (!QueryPerformanceFrequency(&Frequency
))
228 wprintf(L
"FATAL: Performance counter not available\n");
232 /* Initialize the system BIOS */
233 if (!BiosInitialize(ConsoleInput
, ConsoleOutput
))
235 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
239 /* Initialize the VDM DOS kernel */
240 if (!DosInitialize(NULL
))
242 wprintf(L
"FATAL: Failed to initialize the VDM DOS kernel.\n");
246 /* Start the process from the command line */
247 if (!DosCreateProcess(CommandLine
, 0))
249 DisplayMessage(L
"Could not start program: %S", CommandLine
);
253 /* Find the starting performance and tick count */
254 StartTickCount
= GetTickCount();
255 QueryPerformanceCounter(&StartPerfCount
);
257 /* Set the different last counts to the starting count */
258 LastClockUpdate
= LastVerticalRefresh
=
264 /* Set the last timer ticks to the current time */
265 LastTimerTick
= LastRtcTick
= StartPerfCount
;
271 DWORD PitResolution
= PitGetResolution();
273 DWORD RtcFrequency
= RtcGetTicksPerSecond();
275 /* Get the current number of ticks */
276 CurrentTickCount
= GetTickCount();
279 if ((PitResolution
<= 1000) && (RtcFrequency
<= 1000))
281 /* Calculate the approximate performance counter value instead */
282 Counter
.QuadPart
= StartPerfCount
.QuadPart
283 + (CurrentTickCount
- StartTickCount
)
284 * (Frequency
.QuadPart
/ 1000);
289 /* Get the current performance counter value */
290 QueryPerformanceCounter(&Counter
);
293 /* Get the number of PIT ticks that have passed */
294 TimerTicks
= ((Counter
.QuadPart
- LastTimerTick
.QuadPart
)
295 * PIT_BASE_FREQUENCY
) / Frequency
.QuadPart
;
300 PitClock(TimerTicks
);
301 LastTimerTick
= Counter
;
304 /* Check for RTC update */
305 if ((CurrentTickCount
- LastClockUpdate
) >= 1000)
308 LastClockUpdate
= CurrentTickCount
;
311 /* Check for RTC periodic tick */
312 if ((Counter
.QuadPart
- LastRtcTick
.QuadPart
)
313 >= (Frequency
.QuadPart
/ (LONGLONG
)RtcFrequency
))
316 LastRtcTick
= Counter
;
319 /* Check for vertical retrace */
320 if ((CurrentTickCount
- LastVerticalRefresh
) >= 15)
323 LastVerticalRefresh
= CurrentTickCount
;
326 KeyboardIntCounter
++;
327 if (KeyboardIntCounter
== KBD_INT_CYCLES
)
329 GenerateKeyboardInterrupts();
330 KeyboardIntCounter
= 0;
333 /* Horizontal retrace occurs as fast as possible */
334 VgaHorizontalRetrace();
336 /* Continue CPU emulation */
337 for (i
= 0; (i
< STEPS_PER_CYCLE
) && VdmRunning
; i
++)
346 if ((CurrentTickCount
- LastCyclePrintout
) >= 1000)
348 DPRINT1("NTVDM: %lu Instructions Per Second; TimerTicks = %I64d\n", Cycles
, TimerTicks
);
349 LastCyclePrintout
= CurrentTickCount
;
355 /* Perform another screen refresh */
363 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");