From: Hermès Bélusca-Maïto Date: Sat, 1 Feb 2014 16:32:20 +0000 (+0000) Subject: [NTVDM] X-Git-Tag: backups/0.3.17@66124~1365^2~108 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=105aff1c51ddb020996a6f3a97be99e5e3f7ad76 [NTVDM] - Define and export VDDSimulate16 and host_simulate. - Move the big emulation loop from ntvdm.c to clock.c, and the console input pump thread from ps2.c to ntvdm.c. Indeed: * Moving the emulation loop out of the main initialization function will be helpful if one wants to modify how emulation is done, * The console input pump thread deals also with console UI bits that have nothing to do with keyboard/mouse/ps-2. Instead, the pump thread will dispatch keyboard and mouse events to the ps/2 controller. - Implement a custom menu in the console's system menu to be able to parametrize ROS VDM (work-in-progress); at the moment only a menu item to show/hide mouse pointer, and another one allowing us to quit properly the VDM are implemented. The menu code was taken from the GUI frontend in winsrv.dll. Only english and french translations available at the moment. svn path=/branches/ntvdm/; revision=61902 --- diff --git a/include/ddk/nt_vdd.h b/include/ddk/nt_vdd.h index 0f1267f4d48..b4b6c7df67d 100644 --- a/include/ddk/nt_vdd.h +++ b/include/ddk/nt_vdd.h @@ -32,6 +32,10 @@ extern "C" { * VDM Control */ +VOID +WINAPI +VDDSimulate16(VOID); + VOID WINAPI VDDTerminateVDM(VOID); diff --git a/subsystems/ntvdm/CMakeLists.txt b/subsystems/ntvdm/CMakeLists.txt index a1c1f1ec2f6..1d182277062 100644 --- a/subsystems/ntvdm/CMakeLists.txt +++ b/subsystems/ntvdm/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND SOURCE dos/dos32krnl/dos.c dos/dem.c bop.c + clock.c emulator.c int32.c io.c diff --git a/subsystems/ntvdm/clock.c b/subsystems/ntvdm/clock.c new file mode 100644 index 00000000000..0bb3e0c21c8 --- /dev/null +++ b/subsystems/ntvdm/clock.c @@ -0,0 +1,173 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: clock.c + * PURPOSE: Clock for VDM + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" + +// #include "clock.h" + +#include "hardware/cmos.h" +#include "hardware/ps2.h" +#include "hardware/timer.h" +#include "hardware/vga.h" + +/* DEFINES ********************************************************************/ + +/* + * Activate IPS_DISPLAY if you want to display the + * number of instructions per second, as well as + * the computed number of ticks for the PIT. + */ +// #define IPS_DISPLAY + +/* + * Activate WORKING_TIMER when the PIT timing problem is fixed. + */ +// #define WORKING_TIMER + + +/* Processor speed */ +#define STEPS_PER_CYCLE 256 +#define KBD_INT_CYCLES 16 + +/* VARIABLES ******************************************************************/ + +LARGE_INTEGER StartPerfCount, Frequency; + +LARGE_INTEGER LastTimerTick, LastRtcTick, Counter; +LONGLONG TimerTicks; +DWORD StartTickCount, CurrentTickCount; +DWORD LastClockUpdate; +DWORD LastVerticalRefresh; +INT KeyboardIntCounter = 0; + +#ifdef IPS_DISPLAY + DWORD LastCyclePrintout; + DWORD Cycles = 0; +#endif + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID ClockUpdate(VOID) +{ + UINT i; + +#ifdef WORKING_TIMER + DWORD PitResolution = PitGetResolution(); +#endif + DWORD RtcFrequency = RtcGetTicksPerSecond(); + + /* Get the current number of ticks */ + CurrentTickCount = GetTickCount(); + +#ifdef WORKING_TIMER + if ((PitResolution <= 1000) && (RtcFrequency <= 1000)) + { + /* Calculate the approximate performance counter value instead */ + Counter.QuadPart = StartPerfCount.QuadPart + + (CurrentTickCount - StartTickCount) + * (Frequency.QuadPart / 1000); + } + else +#endif + { + /* Get the current performance counter value */ + QueryPerformanceCounter(&Counter); + } + + /* Get the number of PIT ticks that have passed */ + TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart) + * PIT_BASE_FREQUENCY) / Frequency.QuadPart; + + /* Update the PIT */ + if (TimerTicks > 0) + { + PitClock(TimerTicks); + LastTimerTick = Counter; + } + + /* Check for RTC update */ + if ((CurrentTickCount - LastClockUpdate) >= 1000) + { + RtcTimeUpdate(); + LastClockUpdate = CurrentTickCount; + } + + /* Check for RTC periodic tick */ + if ((Counter.QuadPart - LastRtcTick.QuadPart) + >= (Frequency.QuadPart / (LONGLONG)RtcFrequency)) + { + RtcPeriodicTick(); + LastRtcTick = Counter; + } + + /* Check for vertical retrace */ + if ((CurrentTickCount - LastVerticalRefresh) >= 15) + { + VgaRefreshDisplay(); + LastVerticalRefresh = CurrentTickCount; + } + + if (++KeyboardIntCounter == KBD_INT_CYCLES) + { + GenerateKeyboardInterrupts(); + KeyboardIntCounter = 0; + } + + /* Horizontal retrace occurs as fast as possible */ + VgaHorizontalRetrace(); + + /* Continue CPU emulation */ + // EmulatorSimulate(); + for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++) + { + EmulatorStep(); +#ifdef IPS_DISPLAY + Cycles++; +#endif + } + +#ifdef IPS_DISPLAY + if ((CurrentTickCount - LastCyclePrintout) >= 1000) + { + DPRINT1("NTVDM: %lu Instructions Per Second; TimerTicks = %I64d\n", Cycles, TimerTicks); + LastCyclePrintout = CurrentTickCount; + Cycles = 0; + } +#endif +} + +BOOLEAN ClockInitialize(VOID) +{ + /* Initialize the performance counter (needed for hardware timers) */ + if (!QueryPerformanceFrequency(&Frequency)) + { + wprintf(L"FATAL: Performance counter not available\n"); + return FALSE; + } + + /* Find the starting performance and tick count */ + StartTickCount = GetTickCount(); + QueryPerformanceCounter(&StartPerfCount); + + /* Set the different last counts to the starting count */ + LastClockUpdate = LastVerticalRefresh = +#ifdef IPS_DISPLAY + LastCyclePrintout = +#endif + StartTickCount; + + /* Set the last timer ticks to the current time */ + LastTimerTick = LastRtcTick = StartPerfCount; + + return TRUE; +} diff --git a/subsystems/ntvdm/clock.h b/subsystems/ntvdm/clock.h new file mode 100644 index 00000000000..53927d2b257 --- /dev/null +++ b/subsystems/ntvdm/clock.h @@ -0,0 +1,20 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: clock.h + * PURPOSE: Clock for VDM + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _CLOCK_H_ +#define _CLOCK_H_ + +/* FUNCTIONS ******************************************************************/ + +VOID ClockUpdate(VOID); +BOOLEAN ClockInitialize(VOID); + +#endif // _CLOCK_H_ + +/* EOF */ diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index 4e8f9e81ff2..826ff3937ac 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -12,6 +12,7 @@ #include "emulator.h" +#include "clock.h" #include "bios/bios.h" #include "hardware/cmos.h" #include "hardware/pic.h" @@ -24,15 +25,19 @@ #include "vddsup.h" #include "io.h" +#include + /* PRIVATE VARIABLES **********************************************************/ FAST486_STATE EmulatorContext; LPVOID BaseAddress = NULL; BOOLEAN VdmRunning = TRUE; -static BOOLEAN A20Line = FALSE; +static BOOLEAN A20Line = FALSE; static BYTE Port61hState = 0x00; +static HANDLE InputThread = NULL; + LPCWSTR ExceptionName[] = { L"Division By Zero", @@ -120,12 +125,93 @@ UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State) return PicGetInterrupt(); } -VOID WINAPI EmulatorDebugBreak(LPWORD Stack) +VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack) +{ + WORD CodeSegment, InstructionPointer; + PBYTE Opcode; + + ASSERT(ExceptionNumber < 8); + + /* Get the CS:IP */ + InstructionPointer = Stack[STACK_IP]; + CodeSegment = Stack[STACK_CS]; + Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer); + + /* Display a message to the user */ + DisplayMessage(L"Exception: %s occured at %04X:%04X\n" + L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + ExceptionName[ExceptionNumber], + CodeSegment, + InstructionPointer, + Opcode[0], + Opcode[1], + Opcode[2], + Opcode[3], + Opcode[4], + Opcode[5], + Opcode[6], + Opcode[7], + Opcode[8], + Opcode[9]); + + /* Stop the VDM */ + VdmRunning = FALSE; + return; +} + +// FIXME: This function assumes 16-bit mode!!! +VOID EmulatorExecute(WORD Segment, WORD Offset) +{ + /* Tell Fast486 to move the instruction pointer */ + Fast486ExecuteAt(&EmulatorContext, Segment, Offset); +} + +VOID EmulatorStep(VOID) +{ + /* Dump the state for debugging purposes */ + // Fast486DumpState(&EmulatorContext); + + /* Execute the next instruction */ + Fast486StepInto(&EmulatorContext); +} + +VOID EmulatorSimulate(VOID) +{ + UNIMPLEMENTED; +} + +VOID EmulatorUnsimulate(VOID) +{ + UNIMPLEMENTED; +} + +VOID EmulatorInterrupt(BYTE Number) +{ + /* Call the Fast486 API */ + Fast486Interrupt(&EmulatorContext, Number); +} + +VOID EmulatorInterruptSignal(VOID) +{ + /* Call the Fast486 API */ + Fast486InterruptSignal(&EmulatorContext); +} + +VOID EmulatorSetA20(BOOLEAN Enabled) +{ + A20Line = Enabled; +} + +VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack) { DPRINT1("NTVDM: BOP_DEBUGGER\n"); DebugBreak(); } +VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack) +{ + EmulatorUnsimulate(); +} static BYTE WINAPI Port61hRead(ULONG Port) { @@ -211,6 +297,8 @@ static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State) /* PUBLIC FUNCTIONS ***********************************************************/ +DWORD WINAPI PumpConsoleInput(LPVOID Parameter); + BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) { /* Allocate memory for the 16-bit address space */ @@ -224,6 +312,13 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) /* Initialize I/O ports */ /* Initialize RAM */ + /* Initialize the internal clock */ + if (!ClockInitialize()) + { + wprintf(L"FATAL: Failed to initialize the clock\n"); + return FALSE; + } + /* Initialize the CPU */ Fast486Initialize(&EmulatorContext, EmulatorReadMemory, @@ -238,6 +333,8 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) /* Enable interrupts */ setIF(1); + /* Initialize DMA */ + /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */ PicInitialize(); PitInitialize(); @@ -258,12 +355,17 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) /* Set the console input mode */ // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT); + /* Start the input thread */ + InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL); + // if (InputThread == NULL) return FALSE; + /* Initialize the VGA */ // if (!VgaInitialize(ConsoleOutput)) return FALSE; VgaInitialize(ConsoleOutput); - /* Register the DebugBreak BOP */ - RegisterBop(BOP_DEBUGGER, EmulatorDebugBreak); + /* Register the emulator BOPs */ + RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop); + RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop); /* Initialize VDD support */ VDDSupInitialize(); @@ -274,6 +376,11 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) VOID EmulatorCleanup(VOID) { // VgaCleanup(); + + /* Close the input thread handle */ + if (InputThread != NULL) CloseHandle(InputThread); + InputThread = NULL; + PS2Cleanup(); SpeakerCleanup(); @@ -287,75 +394,15 @@ VOID EmulatorCleanup(VOID) if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress); } -VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack) -{ - WORD CodeSegment, InstructionPointer; - PBYTE Opcode; - ASSERT(ExceptionNumber < 8); - /* Get the CS:IP */ - InstructionPointer = Stack[STACK_IP]; - CodeSegment = Stack[STACK_CS]; - Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer); - - /* Display a message to the user */ - DisplayMessage(L"Exception: %s occured at %04X:%04X\n" - L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", - ExceptionName[ExceptionNumber], - CodeSegment, - InstructionPointer, - Opcode[0], - Opcode[1], - Opcode[2], - Opcode[3], - Opcode[4], - Opcode[5], - Opcode[6], - Opcode[7], - Opcode[8], - Opcode[9]); - - /* Stop the VDM */ - VdmRunning = FALSE; - return; -} - -// FIXME: This function assumes 16-bit mode!!! -VOID EmulatorExecute(WORD Segment, WORD Offset) -{ - /* Tell Fast486 to move the instruction pointer */ - Fast486ExecuteAt(&EmulatorContext, Segment, Offset); -} - -VOID EmulatorInterrupt(BYTE Number) -{ - /* Call the Fast486 API */ - Fast486Interrupt(&EmulatorContext, Number); -} - -VOID EmulatorInterruptSignal(VOID) -{ - /* Call the Fast486 API */ - Fast486InterruptSignal(&EmulatorContext); -} - -VOID EmulatorStep(VOID) -{ - /* Dump the state for debugging purposes */ - // Fast486DumpState(&EmulatorContext); - - /* Execute the next instruction */ - Fast486StepInto(&EmulatorContext); -} - -VOID EmulatorSetA20(BOOLEAN Enabled) +VOID +WINAPI +VDDSimulate16(VOID) { - A20Line = Enabled; + EmulatorSimulate(); } - - VOID WINAPI VDDTerminateVDM(VOID) diff --git a/subsystems/ntvdm/hardware/ps2.c b/subsystems/ntvdm/hardware/ps2.c index 2a9f2f84a14..c353337a113 100644 --- a/subsystems/ntvdm/hardware/ps2.c +++ b/subsystems/ntvdm/hardware/ps2.c @@ -25,7 +25,6 @@ static BYTE KeyboardData = 0, KeyboardResponse = 0; static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE; static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG; static HANDLE QueueMutex = NULL; -static HANDLE InputThread = NULL; /* PRIVATE FUNCTIONS **********************************************************/ @@ -281,71 +280,47 @@ static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data) } } -static DWORD WINAPI InputThreadProc(LPVOID Parameter) -{ - INT i; - HANDLE ConsoleInput = (HANDLE)Parameter; - INPUT_RECORD InputRecord; - DWORD Count; +/* PUBLIC FUNCTIONS ***********************************************************/ - while (VdmRunning) +VOID PS2Dispatch(PINPUT_RECORD InputRecord) +{ + /* Check the event type */ + switch (InputRecord->EventType) { - /* Wait for an input record */ - if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count)) + case KEY_EVENT: { - DWORD LastError = GetLastError(); - DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError); - return LastError; - } + WORD i; + BYTE ScanCode = (BYTE)InputRecord->Event.KeyEvent.wVirtualScanCode; - ASSERT(Count != 0); + /* If this is a key release, set the highest bit in the scan code */ + if (!InputRecord->Event.KeyEvent.bKeyDown) ScanCode |= 0x80; - /* Check the event type */ - switch (InputRecord.EventType) - { - case KEY_EVENT: + /* Push the scan code onto the keyboard queue */ + for (i = 0; i < InputRecord->Event.KeyEvent.wRepeatCount; i++) { - BYTE ScanCode = (BYTE)InputRecord.Event.KeyEvent.wVirtualScanCode; - - /* If this is a key release, set the highest bit in the scan code */ - if (!InputRecord.Event.KeyEvent.bKeyDown) ScanCode |= 0x80; - - /* Push the scan code onto the keyboard queue */ - for (i = 0; i < InputRecord.Event.KeyEvent.wRepeatCount; i++) - { - KeyboardQueuePush(ScanCode); - } - - break; + KeyboardQueuePush(ScanCode); } - case MOUSE_EVENT: - { - // TODO: NOT IMPLEMENTED - UNIMPLEMENTED; - break; - } + break; + } - default: - { - /* Ignored */ - break; - } + case MOUSE_EVENT: + { + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + break; } - } - return 0; + /* We ignore all the rest */ + default: + break; + } } -/* PUBLIC FUNCTIONS ***********************************************************/ - VOID GenerateKeyboardInterrupts(VOID) { - if (KeyboardQueuePop(&KeyboardData)) - { - /* IRQ 1 */ - PicInterruptRequest(1); - } + /* Generate an IRQ 1 if there is a key ready in the queue */ + if (KeyboardQueuePop(&KeyboardData)) PicInterruptRequest(1); } BOOLEAN PS2Initialize(HANDLE ConsoleInput) @@ -353,11 +328,6 @@ BOOLEAN PS2Initialize(HANDLE ConsoleInput) /* Create the mutex */ QueueMutex = CreateMutex(NULL, FALSE, NULL); - /* Start the input thread */ - InputThread = CreateThread(NULL, 0, &InputThreadProc, ConsoleInput, 0, NULL); - - // if (InputThread == NULL) return FALSE; - /* Register the I/O Ports */ RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort); RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort); @@ -367,10 +337,6 @@ BOOLEAN PS2Initialize(HANDLE ConsoleInput) VOID PS2Cleanup(VOID) { - /* Close the input thread handle */ - if (InputThread != NULL) CloseHandle(InputThread); - InputThread = NULL; - CloseHandle(QueueMutex); } diff --git a/subsystems/ntvdm/hardware/ps2.h b/subsystems/ntvdm/hardware/ps2.h index 103aa02404e..119e9bdc471 100644 --- a/subsystems/ntvdm/hardware/ps2.h +++ b/subsystems/ntvdm/hardware/ps2.h @@ -24,6 +24,7 @@ /* FUNCTIONS ******************************************************************/ +VOID PS2Dispatch(PINPUT_RECORD InputRecord); VOID GenerateKeyboardInterrupts(VOID); BOOLEAN PS2Initialize(HANDLE ConsoleInput); diff --git a/subsystems/ntvdm/lang/en-US.rc b/subsystems/ntvdm/lang/en-US.rc new file mode 100644 index 00000000000..4ec9d710cbb --- /dev/null +++ b/subsystems/ntvdm/lang/en-US.rc @@ -0,0 +1,13 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +STRINGTABLE +BEGIN + IDS_HIDE_MOUSE, "&Hide Mouse Pointer" + IDS_SHOW_MOUSE, "&Display Mouse Pointer" + IDS_VDM_MENU , "ReactOS &VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "&Quit the ReactOS VDM" +END diff --git a/subsystems/ntvdm/lang/fr-FR.rc b/subsystems/ntvdm/lang/fr-FR.rc new file mode 100644 index 00000000000..853aa5271a9 --- /dev/null +++ b/subsystems/ntvdm/lang/fr-FR.rc @@ -0,0 +1,13 @@ +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_HIDE_MOUSE, "Mas&quer le pointeur de la souris" + IDS_SHOW_MOUSE, "&Afficher le pointeur de la souris" + IDS_VDM_MENU , "ReactOS &VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "&Quitter la ReactOS VDM" +END diff --git a/subsystems/ntvdm/ntvdm.c b/subsystems/ntvdm/ntvdm.c index 91bccf7b849..23a4b45de28 100644 --- a/subsystems/ntvdm/ntvdm.c +++ b/subsystems/ntvdm/ntvdm.c @@ -13,12 +13,13 @@ #include "ntvdm.h" #include "emulator.h" -#include "bios/bios.h" -#include "dos/dem.h" -#include "hardware/cmos.h" +#include "clock.h" #include "hardware/ps2.h" -#include "hardware/timer.h" #include "hardware/vga.h" +#include "bios/bios.h" +#include "dos/dem.h" + +#include "resource.h" /* * Activate this line if you want to be able to test NTVDM with: @@ -26,19 +27,7 @@ */ #define TESTING -/* - * Activate IPS_DISPLAY if you want to display the - * number of instructions per second, as well as - * the computed number of ticks for the PIT. - */ -// #define IPS_DISPLAY - -/* - * Activate WORKING_TIMER when the PIT timing problem is fixed. - */ -// #define WORKING_TIMER - -/* PUBLIC VARIABLES ***********************************************************/ +/* VARIABLES ******************************************************************/ static HANDLE ConsoleInput = INVALID_HANDLE_VALUE; static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE; @@ -46,6 +35,134 @@ static DWORD OrgConsoleInputMode, OrgConsoleOutputMode; static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo; static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo; +static HMENU hConsoleMenu = NULL; +static INT VdmMenuPos = -1; +static BOOLEAN ShowPointer = FALSE; + +/* + * Those menu helpers were taken from the GUI frontend in winsrv.dll + */ +typedef struct _VDM_MENUITEM +{ + UINT uID; + const struct _VDM_MENUITEM *SubMenu; + WORD wCmdID; +} VDM_MENUITEM, *PVDM_MENUITEM; + +static const VDM_MENUITEM VdmMenuItems[] = +{ + { IDS_VDM_QUIT, NULL, ID_VDM_QUIT }, + + { 0, NULL, 0 } /* End of list */ +}; + +static const VDM_MENUITEM VdmMainMenuItems[] = +{ + { -1, NULL, 0 }, /* Separator */ + { IDS_HIDE_MOUSE, NULL, ID_SHOWHIDE_MOUSE }, /* Hide mouse; can be renamed to Show mouse */ + { IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */ + + { 0, NULL, 0 } /* End of list */ +}; + +static VOID +AppendMenuItems(HMENU hMenu, + const VDM_MENUITEM *Items) +{ + UINT i = 0; + WCHAR szMenuString[255]; + HMENU hSubMenu; + + do + { + if (Items[i].uID != (UINT)-1) + { + if (LoadStringW(GetModuleHandle(NULL), + Items[i].uID, + szMenuString, + sizeof(szMenuString) / sizeof(szMenuString[0])) > 0) + { + if (Items[i].SubMenu != NULL) + { + hSubMenu = CreatePopupMenu(); + if (hSubMenu != NULL) + { + AppendMenuItems(hSubMenu, Items[i].SubMenu); + + if (!AppendMenuW(hMenu, + MF_STRING | MF_POPUP, + (UINT_PTR)hSubMenu, + szMenuString)) + { + DestroyMenu(hSubMenu); + } + } + } + else + { + AppendMenuW(hMenu, + MF_STRING, + Items[i].wCmdID, + szMenuString); + } + } + } + else + { + AppendMenuW(hMenu, + MF_SEPARATOR, + 0, + NULL); + } + i++; + } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0)); +} + +static VOID +CreateVdmMenu(HANDLE ConOutHandle) +{ + hConsoleMenu = ConsoleMenuControl(ConsoleOutput, + ID_SHOWHIDE_MOUSE, + ID_VDM_QUIT); + if (hConsoleMenu != NULL) + { + VdmMenuPos = GetMenuItemCount(hConsoleMenu); + AppendMenuItems(hConsoleMenu, VdmMainMenuItems); + DrawMenuBar(GetConsoleWindow()); + } +} + +static VOID +DestroyVdmMenu(VOID) +{ + UINT i = 0; + const VDM_MENUITEM *Items = VdmMainMenuItems; + + do + { + DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION); + i++; + } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0)); + + DrawMenuBar(GetConsoleWindow()); +} + +static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr) +{ + WCHAR szMenuString[255] = L""; + + ShowConsoleCursor(ConOutHandle, ShowPtr); + + if (LoadStringW(GetModuleHandle(NULL), + (!ShowPtr ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE), + szMenuString, + sizeof(szMenuString) / sizeof(szMenuString[0])) > 0) + { + ModifyMenu(hConsoleMenu, ID_SHOWHIDE_MOUSE, + MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString); + } +} + /* PUBLIC FUNCTIONS ***********************************************************/ VOID DisplayMessage(LPCWSTR Format, ...) @@ -80,6 +197,74 @@ BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType) return TRUE; } +VOID ConsoleInitUI(VOID) +{ + CreateVdmMenu(ConsoleOutput); +} + +VOID ConsoleCleanupUI(VOID) +{ + /* Display again properly the mouse pointer */ + if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer); + + DestroyVdmMenu(); +} + +DWORD WINAPI PumpConsoleInput(LPVOID Parameter) +{ + HANDLE ConsoleInput = (HANDLE)Parameter; + INPUT_RECORD InputRecord; + DWORD Count; + + while (VdmRunning) + { + /* Wait for an input record */ + if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count)) + { + DWORD LastError = GetLastError(); + DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError); + return LastError; + } + + ASSERT(Count != 0); + + /* Check the event type */ + switch (InputRecord.EventType) + { + case KEY_EVENT: + case MOUSE_EVENT: + /* Send it to the PS/2 controller */ + PS2Dispatch(&InputRecord); + break; + + case MENU_EVENT: + { + switch (InputRecord.Event.MenuEvent.dwCommandId) + { + case ID_SHOWHIDE_MOUSE: + ShowHideMousePointer(ConsoleOutput, ShowPointer); + ShowPointer = !ShowPointer; + break; + + case ID_VDM_QUIT: + VdmRunning = FALSE; + break; + + default: + break; + } + + break; + } + + default: + break; + } + } + + return 0; +} + BOOL ConsoleInit(VOID) { /* Set the handler routine */ @@ -134,6 +319,9 @@ BOOL ConsoleInit(VOID) return FALSE; } + /* Initialize the UI */ + ConsoleInitUI(); + return TRUE; } @@ -166,6 +354,9 @@ VOID ConsoleCleanup(VOID) SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode); SetConsoleMode(ConsoleInput , OrgConsoleInputMode ); + /* Cleanup the UI */ + ConsoleCleanupUI(); + /* Close the console handles */ if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput); if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput); @@ -173,19 +364,7 @@ VOID ConsoleCleanup(VOID) INT wmain(INT argc, WCHAR *argv[]) { - INT i; CHAR CommandLine[DOS_CMDLINE_LENGTH]; - LARGE_INTEGER StartPerfCount; - LARGE_INTEGER Frequency, LastTimerTick, LastRtcTick, Counter; - LONGLONG TimerTicks; - DWORD StartTickCount, CurrentTickCount; - DWORD LastClockUpdate; - DWORD LastVerticalRefresh; -#ifdef IPS_DISPLAY - DWORD LastCyclePrintout; - DWORD Cycles = 0; -#endif - INT KeyboardIntCounter = 0; #ifndef TESTING UNREFERENCED_PARAMETER(argc); @@ -222,13 +401,6 @@ INT wmain(INT argc, WCHAR *argv[]) goto Cleanup; } - /* Initialize the performance counter (needed for hardware timers) */ - if (!QueryPerformanceFrequency(&Frequency)) - { - wprintf(L"FATAL: Performance counter not available\n"); - goto Cleanup; - } - /* Initialize the system BIOS */ if (!BiosInitialize(ConsoleInput, ConsoleOutput)) { @@ -250,107 +422,8 @@ INT wmain(INT argc, WCHAR *argv[]) goto Cleanup; } - /* Find the starting performance and tick count */ - StartTickCount = GetTickCount(); - QueryPerformanceCounter(&StartPerfCount); - - /* Set the different last counts to the starting count */ - LastClockUpdate = LastVerticalRefresh = -#ifdef IPS_DISPLAY - LastCyclePrintout = -#endif - StartTickCount; - - /* Set the last timer ticks to the current time */ - LastTimerTick = LastRtcTick = StartPerfCount; - /* Main loop */ - while (VdmRunning) - { -#ifdef WORKING_TIMER - DWORD PitResolution = PitGetResolution(); -#endif - DWORD RtcFrequency = RtcGetTicksPerSecond(); - - /* Get the current number of ticks */ - CurrentTickCount = GetTickCount(); - -#ifdef WORKING_TIMER - if ((PitResolution <= 1000) && (RtcFrequency <= 1000)) - { - /* Calculate the approximate performance counter value instead */ - Counter.QuadPart = StartPerfCount.QuadPart - + (CurrentTickCount - StartTickCount) - * (Frequency.QuadPart / 1000); - } - else -#endif - { - /* Get the current performance counter value */ - QueryPerformanceCounter(&Counter); - } - - /* Get the number of PIT ticks that have passed */ - TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart) - * PIT_BASE_FREQUENCY) / Frequency.QuadPart; - - /* Update the PIT */ - if (TimerTicks > 0) - { - PitClock(TimerTicks); - LastTimerTick = Counter; - } - - /* Check for RTC update */ - if ((CurrentTickCount - LastClockUpdate) >= 1000) - { - RtcTimeUpdate(); - LastClockUpdate = CurrentTickCount; - } - - /* Check for RTC periodic tick */ - if ((Counter.QuadPart - LastRtcTick.QuadPart) - >= (Frequency.QuadPart / (LONGLONG)RtcFrequency)) - { - RtcPeriodicTick(); - LastRtcTick = Counter; - } - - /* Check for vertical retrace */ - if ((CurrentTickCount - LastVerticalRefresh) >= 15) - { - VgaRefreshDisplay(); - LastVerticalRefresh = CurrentTickCount; - } - - KeyboardIntCounter++; - if (KeyboardIntCounter == KBD_INT_CYCLES) - { - GenerateKeyboardInterrupts(); - KeyboardIntCounter = 0; - } - - /* Horizontal retrace occurs as fast as possible */ - VgaHorizontalRetrace(); - - /* Continue CPU emulation */ - for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++) - { - EmulatorStep(); -#ifdef IPS_DISPLAY - Cycles++; -#endif - } - -#ifdef IPS_DISPLAY - if ((CurrentTickCount - LastCyclePrintout) >= 1000) - { - DPRINT1("NTVDM: %lu Instructions Per Second; TimerTicks = %I64d\n", Cycles, TimerTicks); - LastCyclePrintout = CurrentTickCount; - Cycles = 0; - } -#endif - } + while (VdmRunning) ClockUpdate(); /* Perform another screen refresh */ VgaRefreshDisplay(); diff --git a/subsystems/ntvdm/ntvdm.h b/subsystems/ntvdm/ntvdm.h index d95402aece7..dd6edbe4437 100644 --- a/subsystems/ntvdm/ntvdm.h +++ b/subsystems/ntvdm/ntvdm.h @@ -25,16 +25,11 @@ #include #include -// #include "registers.h" #include /* DEFINES ********************************************************************/ -/* Processor speed */ -#define STEPS_PER_CYCLE 256 -#define KBD_INT_CYCLES 16 - /* FUNCTIONS ******************************************************************/ VOID DisplayMessage(LPCWSTR Format, ...); diff --git a/subsystems/ntvdm/ntvdm.rc b/subsystems/ntvdm/ntvdm.rc index 9a14095e072..388bccc42e6 100644 --- a/subsystems/ntvdm/ntvdm.rc +++ b/subsystems/ntvdm/ntvdm.rc @@ -1,5 +1,6 @@ #include #include +// #include #include "resource.h" @@ -14,3 +15,10 @@ IDI_APPICON ICON "res/ntvdm.ico" /* UTF-8 */ #pragma code_page(65001) + +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif +#ifdef LANGUAGE_FR_FR + #include "lang/fr-FR.rc" +#endif diff --git a/subsystems/ntvdm/ntvdm.spec b/subsystems/ntvdm/ntvdm.spec index 8a9d7b3e60c..244bda9200f 100644 --- a/subsystems/ntvdm/ntvdm.spec +++ b/subsystems/ntvdm/ntvdm.spec @@ -1,3 +1,7 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM Registers exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + @ stdcall getAF() @ stdcall getAH() @ stdcall getAL() @@ -89,6 +93,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NTVDM CCPU MIPS exports ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + @ stdcall c_getAF() getAF @ stdcall c_getAH() getAH @ stdcall c_getAL() getAL @@ -174,6 +179,9 @@ @ stdcall c_setZF(long) setZF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM DOS-32 Emulation exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @ stdcall demClientErrorEx(long long long) @ stdcall demFileDelete(ptr) @@ -192,6 +200,9 @@ ;@ stdcall demWOWLFNInit +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM Miscellaneous exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @ stdcall MGetVdmPointer(long long long) @ stdcall Sim32pGetVDMPointer(long long) @@ -204,4 +215,6 @@ @ stdcall VDDInstallIOHook(long long ptr ptr) @ stdcall VDDDeInstallIOHook(long long ptr) +@ stdcall VDDSimulate16() +@ stdcall host_simulate() VDDSimulate16 @ stdcall VDDTerminateVDM() diff --git a/subsystems/ntvdm/resource.h b/subsystems/ntvdm/resource.h index 0a273ca3a1a..f7a6f3c2a1b 100644 --- a/subsystems/ntvdm/resource.h +++ b/subsystems/ntvdm/resource.h @@ -1,5 +1,14 @@ #pragma once +#define ID_SHOWHIDE_MOUSE 1000 +#define ID_VDM_QUIT 1001 + +#define IDS_HIDE_MOUSE 100 +#define IDS_SHOW_MOUSE 101 +#define IDS_VDM_MENU 102 + +#define IDS_VDM_QUIT 200 + #define IDI_APPICON 1 /* EOF */