diff --git a/reactos/cmake/msvc.cmake b/reactos/cmake/msvc.cmake index 18121baf88cc2..7d3cedd45228e 100644 --- a/reactos/cmake/msvc.cmake +++ b/reactos/cmake/msvc.cmake @@ -56,7 +56,8 @@ add_compile_flags("/wd4290") # - C4163: 'identifier': not available as an intrinsic function # - C4229: modifiers on data are ignored # - C4700: uninitialized variable usage -add_compile_flags("/we4013 /we4020 /we4022 /we4047 /we4098 /we4113 /we4129 /we4163 /we4229 /we4700") +# - C4603: macro is not defined or definition is different after precompiled header use +add_compile_flags("/we4013 /we4020 /we4022 /we4047 /we4098 /we4113 /we4129 /we4163 /we4229 /we4700 /we4603") # Enable warnings above the default level, but don't treat them as errors: # - C4115: named type definition in parentheses diff --git a/reactos/dll/ntdll/csr/capture.c b/reactos/dll/ntdll/csr/capture.c index d22644b599235..0a779eca9f91c 100644 --- a/reactos/dll/ntdll/csr/capture.c +++ b/reactos/dll/ntdll/csr/capture.c @@ -101,6 +101,9 @@ CsrAllocateCaptureBuffer(IN ULONG ArgumentCount, /* Align it to a 4-byte boundary */ BufferSize = (BufferSize + 3) & ~3; + /* Add the size of the alignment padding for each argument */ + BufferSize += ArgumentCount * 3; + /* Allocate memory from the port heap */ CaptureBuffer = RtlAllocateHeap(CsrPortHeap, HEAP_ZERO_MEMORY, BufferSize); if (CaptureBuffer == NULL) return NULL; diff --git a/reactos/dll/win32/kernel32/client/proc.c b/reactos/dll/win32/kernel32/client/proc.c index 0f3bc305d03af..84bcd6d2509eb 100644 --- a/reactos/dll/win32/kernel32/client/proc.c +++ b/reactos/dll/win32/kernel32/client/proc.c @@ -2301,7 +2301,7 @@ CreateProcessInternalW(IN HANDLE hUserToken, BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege; BOOLEAN QuerySection, SkipSaferAndAppCompat; CONTEXT Context; - BASE_API_MESSAGE CsrMsg; + BASE_API_MESSAGE CsrMsg[2]; PBASE_CREATE_PROCESS CreateProcessMsg; PCSR_CAPTURE_BUFFER CaptureBuffer; PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState; @@ -2431,8 +2431,8 @@ CreateProcessInternalW(IN HANDLE hUserToken, IsWowApp = FALSE; /* Set message structures */ - CreateProcessMsg = &CsrMsg.Data.CreateProcessRequest; - CheckVdmMsg = &CsrMsg.Data.CheckVDMRequest; + CreateProcessMsg = &CsrMsg[0].Data.CreateProcessRequest; + CheckVdmMsg = &CsrMsg[1].Data.CheckVDMRequest; /* Clear the more complex structures by zeroing out their entire memory */ RtlZeroMemory(&Context, sizeof(Context)); @@ -3197,7 +3197,7 @@ CreateProcessInternalW(IN HANDLE hUserToken, /* Pick which kind of WOW mode we want to run in */ VdmBinaryType = (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) ? - BINARY_TYPE_WOW : BINARY_TYPE_SEPARATE_WOW; + BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW; /* Get all the VDM settings and current status */ Status = BaseCheckVDM(VdmBinaryType, @@ -3205,7 +3205,7 @@ CreateProcessInternalW(IN HANDLE hUserToken, lpCommandLine, lpCurrentDirectory, &VdmAnsiEnv, - &CsrMsg, + &CsrMsg[1], &VdmTask, dwCreationFlags, &StartupInfo, @@ -3342,7 +3342,7 @@ CreateProcessInternalW(IN HANDLE hUserToken, lpCommandLine, lpCurrentDirectory, &VdmAnsiEnv, - &CsrMsg, + &CsrMsg[1], &VdmTask, dwCreationFlags, &StartupInfo, @@ -3966,6 +3966,8 @@ CreateProcessInternalW(IN HANDLE hUserToken, &VdmWaitObject, VdmTask, VdmBinaryType); + + if (!Result) { /* Bail out on failure */ DPRINT1("Failed to update VDM with wait object\n"); @@ -4335,7 +4337,7 @@ CreateProcessInternalW(IN HANDLE hUserToken, } /* We are finally ready to call CSRSS to tell it about our new process! */ - CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg, + CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0], CaptureBuffer, CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess), @@ -4349,12 +4351,12 @@ CreateProcessInternalW(IN HANDLE hUserToken, } /* Check if CSRSS failed to accept ownership of the new Windows process */ - if (!NT_SUCCESS(CsrMsg.Status)) + if (!NT_SUCCESS(CsrMsg[0].Status)) { /* Terminate the process and enter failure path with the CSRSS status */ DPRINT1("Failed to tell csrss about new process\n"); - BaseSetLastNTError(CsrMsg.Status); - NtTerminateProcess(ProcessHandle, CsrMsg.Status); + BaseSetLastNTError(CsrMsg[0].Status); + NtTerminateProcess(ProcessHandle, CsrMsg[0].Status); Result = FALSE; goto Quickie; } diff --git a/reactos/dll/win32/kernel32/client/vdm.c b/reactos/dll/win32/kernel32/client/vdm.c index 3ff2c46616815..c37a3cf3589cc 100644 --- a/reactos/dll/win32/kernel32/client/vdm.c +++ b/reactos/dll/win32/kernel32/client/vdm.c @@ -64,7 +64,7 @@ BaseIsDosApplication(IN PUNICODE_STRING PathName, return 0; } -BOOL +NTSTATUS WINAPI BaseCheckVDM(IN ULONG BinaryType, IN PCWCH ApplicationName, @@ -77,9 +77,354 @@ BaseCheckVDM(IN ULONG BinaryType, IN LPSTARTUPINFOW StartupInfo, IN HANDLE hUserToken OPTIONAL) { - /* This is not supported */ - UNIMPLEMENTED; - return FALSE; + NTSTATUS Status; + PBASE_CHECK_VDM CheckVdm = &ApiMessage->Data.CheckVDMRequest; + PCSR_CAPTURE_BUFFER CaptureBuffer; + PWCHAR CurrentDir = NULL; + PWCHAR ShortAppName = NULL; + PWCHAR ShortCurrentDir = NULL; + ULONG Length; + PCHAR AnsiCmdLine = NULL; + PCHAR AnsiAppName = NULL; + PCHAR AnsiCurDirectory = NULL; + PCHAR AnsiDesktop = NULL; + PCHAR AnsiTitle = NULL; + PCHAR AnsiReserved = NULL; + STARTUPINFOA AnsiStartupInfo; + ULONG NumStrings = 5; + + if (CurrentDirectory == NULL) + { + /* Allocate memory for the current directory path */ + Length = GetCurrentDirectoryW(0, NULL); + CurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + Length * sizeof(WCHAR)); + if (CurrentDir == NULL) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Get the current directory */ + GetCurrentDirectoryW(Length, CurrentDir); + CurrentDirectory = CurrentDir; + } + + /* Calculate the size of the short application name */ + Length = GetShortPathNameW(ApplicationName, NULL, 0); + + /* Allocate memory for the short application name */ + ShortAppName = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + Length * sizeof(WCHAR)); + if (!ShortAppName) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Get the short application name */ + if (!GetShortPathNameW(ApplicationName, ShortAppName, Length)) + { + /* Try to determine which error occurred */ + switch (GetLastError()) + { + case ERROR_NOT_ENOUGH_MEMORY: + { + Status = STATUS_NO_MEMORY; + break; + } + + case ERROR_INVALID_PARAMETER: + { + Status = STATUS_INVALID_PARAMETER; + break; + } + + default: + { + Status = STATUS_OBJECT_PATH_INVALID; + } + } + + goto Cleanup; + } + + /* Calculate the size of the short current directory path */ + Length = GetShortPathNameW(CurrentDirectory, NULL, 0); + + /* Allocate memory for the short current directory path */ + ShortCurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + Length * sizeof(WCHAR)); + if (!ShortCurrentDir) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Get the short current directory path */ + if (!GetShortPathNameW(CurrentDirectory, ShortCurrentDir, Length)) + { + /* Try to determine which error occurred */ + switch (GetLastError()) + { + case ERROR_NOT_ENOUGH_MEMORY: + { + Status = STATUS_NO_MEMORY; + break; + } + + case ERROR_INVALID_PARAMETER: + { + Status = STATUS_INVALID_PARAMETER; + break; + } + + default: + { + Status = STATUS_OBJECT_PATH_INVALID; + } + } + goto Cleanup; + } + + /* Setup the input parameters */ + CheckVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle; + CheckVdm->BinaryType = BinaryType; + CheckVdm->CodePage = CP_ACP; + CheckVdm->dwCreationFlags = CreationFlags; + CheckVdm->CurDrive = CurrentDirectory[0] - L'A'; + CheckVdm->CmdLen = wcslen(CommandLine) + 1; + CheckVdm->AppLen = wcslen(ShortAppName) + 1; + CheckVdm->PifLen = 0; // TODO: PIF file support! + CheckVdm->CurDirectoryLen = wcslen(ShortCurrentDir) + 1; + CheckVdm->EnvLen = AnsiEnvironment->Length; + CheckVdm->DesktopLen = (StartupInfo->lpDesktop != NULL) ? (wcslen(StartupInfo->lpDesktop) + 1) : 0; + CheckVdm->TitleLen = (StartupInfo->lpTitle != NULL) ? (wcslen(StartupInfo->lpTitle) + 1) : 0; + CheckVdm->ReservedLen = (StartupInfo->lpReserved != NULL) ? (wcslen(StartupInfo->lpReserved) + 1) : 0; + + if (StartupInfo->dwFlags & STARTF_USESTDHANDLES) + { + /* Set the standard handles */ + CheckVdm->StdIn = StartupInfo->hStdInput; + CheckVdm->StdOut = StartupInfo->hStdOutput; + CheckVdm->StdErr = StartupInfo->hStdError; + } + + /* Allocate memory for the ANSI strings */ + AnsiCmdLine = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CmdLen); + AnsiAppName = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->AppLen); + AnsiCurDirectory = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CurDirectoryLen); + if (StartupInfo->lpDesktop) AnsiDesktop = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + CheckVdm->DesktopLen); + if (StartupInfo->lpTitle) AnsiTitle = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + CheckVdm->TitleLen); + if (StartupInfo->lpReserved) AnsiReserved = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + CheckVdm->ReservedLen); + + if (!AnsiCmdLine + || !AnsiAppName + || !AnsiCurDirectory + || (StartupInfo->lpDesktop && !AnsiDesktop) + || (StartupInfo->lpTitle && !AnsiTitle) + || (StartupInfo->lpReserved && !AnsiReserved)) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Convert the command line into an ANSI string */ + WideCharToMultiByte(CP_ACP, + 0, + CommandLine, + CheckVdm->CmdLen, + AnsiCmdLine, + CheckVdm->CmdLen, + NULL, + NULL); + + /* Convert the short application name into an ANSI string */ + WideCharToMultiByte(CP_ACP, + 0, + ShortAppName, + CheckVdm->AppLen, + AnsiAppName, + CheckVdm->AppLen, + NULL, + NULL); + + /* Convert the short current directory path into an ANSI string */ + WideCharToMultiByte(CP_ACP, + 0, + ShortCurrentDir, + CheckVdm->CurDirectoryLen, + AnsiCurDirectory, + CheckVdm->CurDirectoryLen, + NULL, + NULL); + + if (StartupInfo->lpDesktop) + { + /* Convert the desktop name into an ANSI string */ + WideCharToMultiByte(CP_ACP, + 0, + StartupInfo->lpDesktop, + CheckVdm->DesktopLen, + AnsiDesktop, + CheckVdm->DesktopLen, + NULL, + NULL); + NumStrings++; + } + + if (StartupInfo->lpTitle) + { + /* Convert the title into an ANSI string */ + WideCharToMultiByte(CP_ACP, + 0, + StartupInfo->lpTitle, + CheckVdm->TitleLen, + AnsiTitle, + CheckVdm->TitleLen, + NULL, + NULL); + NumStrings++; + } + + if (StartupInfo->lpReserved) + { + /* Convert the reserved value into an ANSI string */ + WideCharToMultiByte(CP_ACP, + 0, + StartupInfo->lpReserved, + CheckVdm->ReservedLen, + AnsiReserved, + CheckVdm->ReservedLen, + NULL, + NULL); + NumStrings++; + } + + /* Fill the ANSI startup info structure */ + RtlCopyMemory(&AnsiStartupInfo, StartupInfo, sizeof(STARTUPINFO)); + AnsiStartupInfo.lpReserved = AnsiReserved; + AnsiStartupInfo.lpDesktop = AnsiDesktop; + AnsiStartupInfo.lpTitle = AnsiTitle; + + /* Allocate the capture buffer */ + CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings, + CheckVdm->CmdLen + + CheckVdm->AppLen + + CheckVdm->PifLen + + CheckVdm->CurDirectoryLen + + CheckVdm->DesktopLen + + CheckVdm->TitleLen + + CheckVdm->ReservedLen + + CheckVdm->EnvLen + + sizeof(STARTUPINFOA)); + if (CaptureBuffer == NULL) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Capture the command line */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiCmdLine, + CheckVdm->CmdLen, + (PVOID*)&CheckVdm->CmdLine); + + /* Capture the application name */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiAppName, + CheckVdm->AppLen, + (PVOID*)&CheckVdm->AppName); + + CheckVdm->PifFile = NULL; // TODO: PIF file support! + + /* Capture the current directory */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiCurDirectory, + CheckVdm->CurDirectoryLen, + (PVOID*)&CheckVdm->CurDirectory); + + /* Capture the environment */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiEnvironment->Buffer, + CheckVdm->EnvLen, + (PVOID*)&CheckVdm->Env); + + /* Capture the startup info structure */ + CsrCaptureMessageBuffer(CaptureBuffer, + &AnsiStartupInfo, + sizeof(STARTUPINFOA), + (PVOID*)&CheckVdm->StartupInfo); + + if (StartupInfo->lpDesktop) + { + /* Capture the desktop name */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiDesktop, + CheckVdm->DesktopLen, + (PVOID*)&CheckVdm->Desktop); + } + else CheckVdm->Desktop = NULL; + + if (StartupInfo->lpTitle) + { + /* Capture the title */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiTitle, + CheckVdm->TitleLen, + (PVOID*)&CheckVdm->Title); + } + else CheckVdm->Title = NULL; + + if (StartupInfo->lpReserved) + { + /* Capture the reserved parameter */ + CsrCaptureMessageBuffer(CaptureBuffer, + AnsiReserved, + CheckVdm->ReservedLen, + (PVOID*)&CheckVdm->Reserved); + } + else CheckVdm->Reserved = NULL; + + /* Send the message to CSRSS */ + Status = CsrClientCallServer((PCSR_API_MESSAGE)ApiMessage, + CaptureBuffer, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCheckVDM), + sizeof(BASE_CHECK_VDM)); + + /* Write back the task ID */ + *iTask = CheckVdm->iTask; + +Cleanup: + + /* Free the ANSI strings */ + if (AnsiCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCmdLine); + if (AnsiAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiAppName); + if (AnsiCurDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCurDirectory); + if (AnsiDesktop) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiDesktop); + if (AnsiTitle) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiTitle); + if (AnsiReserved) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiReserved); + + /* Free the capture buffer */ + CsrFreeCaptureBuffer(CaptureBuffer); + + /* Free the short paths */ + if (ShortAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName); + if (ShortCurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortCurrentDir); + + /* Free the current directory, if it was allocated here */ + if (CurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDir); + + return Status; } BOOL @@ -89,10 +434,9 @@ BaseUpdateVDMEntry(IN ULONG UpdateIndex, IN ULONG IndexInfo, IN ULONG BinaryType) { -#if 0 // Unimplemented in BASESRV NTSTATUS Status; BASE_API_MESSAGE ApiMessage; - PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry = &ApiMessage.Data.UpdateVdmEntry; + PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry = &ApiMessage.Data.UpdateVDMEntryRequest; /* Check what update is being sent */ switch (UpdateIndex) @@ -155,7 +499,7 @@ BaseUpdateVDMEntry(IN ULONG UpdateIndex, /* Return it to the caller */ *WaitHandle = UpdateVdmEntry->WaitObjectForParent; } -#endif + /* We made it */ return TRUE; } @@ -165,11 +509,10 @@ WINAPI BaseCheckForVDM(IN HANDLE ProcessHandle, OUT LPDWORD ExitCode) { -#if 0 // Unimplemented in BASESRV NTSTATUS Status; EVENT_BASIC_INFORMATION EventBasicInfo; BASE_API_MESSAGE ApiMessage; - PBASE_GET_VDM_EXIT_CODE GetVdmExitCode = &ApiMessage.Data.GetVdmExitCode; + PBASE_GET_VDM_EXIT_CODE GetVdmExitCode = &ApiMessage.Data.GetVDMExitCodeRequest; /* It's VDM if the process is actually a wait handle (an event) */ Status = NtQueryEvent(ProcessHandle, @@ -192,7 +535,6 @@ BaseCheckForVDM(IN HANDLE ProcessHandle, /* Get the exit code from the reply */ *ExitCode = GetVdmExitCode->ExitCode; -#endif return TRUE; } @@ -335,7 +677,7 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment, if (!lpEnvironment) { /* Nope, create one */ - Status = RtlCreateEnvironment(TRUE, (PWCHAR*)&Environment); + Status = RtlCreateEnvironment(TRUE, &Environment); if (!NT_SUCCESS(Status)) goto Quickie; } else @@ -376,6 +718,7 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment, p = NewEnvironment; /* FIXME: Code here */ + DPRINT1("BaseCreateVDMEnvironment is half-plemented!\n"); /* Terminate it */ *p++ = UNICODE_NULL; @@ -574,7 +917,7 @@ InternalGetBinaryType(HANDLE hFile) return BINARY_PE_EXE32; } - if(!memcmp(magic, "NE", 1)) + if(!memcmp(magic, "NE", 2)) { /* This is a Windows executable (NE) header. This can * mean either a 16-bit OS/2 or a 16-bit Windows or even a @@ -755,46 +1098,399 @@ CmdBatNotification ( } /* - * @unimplemented + * @implemented */ -DWORD +VOID WINAPI -ExitVDM ( - DWORD Unknown0, - DWORD Unknown1 - ) +ExitVDM(BOOL IsWow, ULONG iWowTask) { - STUB; - return 0; -} + BASE_API_MESSAGE ApiMessage; + PBASE_EXIT_VDM ExitVdm = &ApiMessage.Data.ExitVDMRequest; + + /* Setup the input parameters */ + ExitVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle; + ExitVdm->iWowTask = IsWow ? iWowTask : 0; /* Always zero for DOS tasks */ + ExitVdm->WaitObjectForVDM = NULL; + /* Call CSRSS */ + CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + NULL, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitVDM), + sizeof(BASE_EXIT_VDM)); + + /* Close the returned wait object handle, if any */ + if (NT_SUCCESS(ApiMessage.Status) && (ExitVdm->WaitObjectForVDM != NULL)) + { + CloseHandle(ExitVdm->WaitObjectForVDM); + } +} /* - * @unimplemented + * @implemented */ -DWORD +BOOL WINAPI -GetNextVDMCommand ( - DWORD Unknown0 - ) +GetNextVDMCommand(PVDM_COMMAND_INFO CommandData) { - STUB; - return 0; + NTSTATUS Status; + BOOL Result = FALSE; + BASE_API_MESSAGE ApiMessage; + PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommand = &ApiMessage.Data.GetNextVDMCommandRequest; + PBASE_IS_FIRST_VDM IsFirstVdm = &ApiMessage.Data.IsFirstVDMRequest; + PBASE_SET_REENTER_COUNT SetReenterCount = &ApiMessage.Data.SetReenterCountRequest; + PCSR_CAPTURE_BUFFER CaptureBuffer = NULL; + ULONG NumStrings = 0; + + if (CommandData != NULL) + { + if (CommandData->VDMState & (VDM_NOT_LOADED | VDM_NOT_READY | VDM_READY)) + { + /* Clear the structure */ + ZeroMemory(GetNextVdmCommand, sizeof(*GetNextVdmCommand)); + + /* Setup the input parameters */ + GetNextVdmCommand->iTask = CommandData->TaskId; + GetNextVdmCommand->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle; + GetNextVdmCommand->CmdLen = CommandData->CmdLen; + GetNextVdmCommand->AppLen = CommandData->AppLen; + GetNextVdmCommand->PifLen = CommandData->PifLen; + GetNextVdmCommand->CurDirectoryLen = CommandData->CurDirectoryLen; + GetNextVdmCommand->EnvLen = CommandData->EnvLen; + GetNextVdmCommand->DesktopLen = CommandData->DesktopLen; + GetNextVdmCommand->TitleLen = CommandData->TitleLen; + GetNextVdmCommand->ReservedLen = CommandData->ReservedLen; + GetNextVdmCommand->VDMState = CommandData->VDMState; + + /* Count the number of strings */ + if (CommandData->CmdLen) NumStrings++; + if (CommandData->AppLen) NumStrings++; + if (CommandData->PifLen) NumStrings++; + if (CommandData->CurDirectoryLen) NumStrings++; + if (CommandData->EnvLen) NumStrings++; + if (CommandData->DesktopLen) NumStrings++; + if (CommandData->TitleLen) NumStrings++; + if (CommandData->ReservedLen) NumStrings++; + + /* Allocate the capture buffer */ + CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings + 1, + GetNextVdmCommand->CmdLen + + GetNextVdmCommand->AppLen + + GetNextVdmCommand->PifLen + + GetNextVdmCommand->CurDirectoryLen + + GetNextVdmCommand->EnvLen + + GetNextVdmCommand->DesktopLen + + GetNextVdmCommand->TitleLen + + GetNextVdmCommand->ReservedLen + + sizeof(STARTUPINFOA)); + if (CaptureBuffer == NULL) + { + BaseSetLastNTError(STATUS_NO_MEMORY); + goto Cleanup; + } + + /* Allocate memory for the startup info */ + CsrAllocateMessagePointer(CaptureBuffer, + sizeof(STARTUPINFOA), + (PVOID*)&GetNextVdmCommand->StartupInfo); + + if (CommandData->CmdLen) + { + /* Allocate memory for the command line */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->CmdLen, + (PVOID*)&GetNextVdmCommand->CmdLine); + } + + if (CommandData->AppLen) + { + /* Allocate memory for the application name */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->AppLen, + (PVOID*)&GetNextVdmCommand->AppName); + } + + if (CommandData->PifLen) + { + /* Allocate memory for the PIF file name */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->PifLen, + (PVOID*)&GetNextVdmCommand->PifFile); + } + + if (CommandData->CurDirectoryLen) + { + /* Allocate memory for the current directory */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->CurDirectoryLen, + (PVOID*)&GetNextVdmCommand->CurDirectory); + } + + if (CommandData->EnvLen) + { + /* Allocate memory for the environment */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->EnvLen, + (PVOID*)&GetNextVdmCommand->Env); + } + + if (CommandData->DesktopLen) + { + /* Allocate memory for the desktop name */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->DesktopLen, + (PVOID*)&GetNextVdmCommand->Desktop); + } + + if (CommandData->TitleLen) + { + /* Allocate memory for the title */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->TitleLen, + (PVOID*)&GetNextVdmCommand->Title); + } + + if (CommandData->ReservedLen) + { + /* Allocate memory for the reserved parameter */ + CsrAllocateMessagePointer(CaptureBuffer, + CommandData->ReservedLen, + (PVOID*)&GetNextVdmCommand->Reserved); + } + + do + { + /* Call CSRSS */ + Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + CaptureBuffer, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetNextVDMCommand), + sizeof(BASE_GET_NEXT_VDM_COMMAND)); + + if (!NT_SUCCESS(Status)) + { + BaseSetLastNTError(Status); + goto Cleanup; + } + + /* Did we receive an event handle? */ + if (GetNextVdmCommand->WaitObjectForVDM != NULL) + { + /* Wait for the event to become signaled and try again */ + Status = NtWaitForSingleObject(GetNextVdmCommand->WaitObjectForVDM, + FALSE, + NULL); + if (!NT_SUCCESS(Status)) + { + BaseSetLastNTError(Status); + goto Cleanup; + } + + /* Set the retry flag and clear the exit code */ + GetNextVdmCommand->VDMState |= VDM_FLAG_RETRY; + GetNextVdmCommand->ExitCode = 0; + } + } + while (GetNextVdmCommand->WaitObjectForVDM != NULL); + + /* Write back the standard handles */ + CommandData->StdIn = GetNextVdmCommand->StdIn; + CommandData->StdOut = GetNextVdmCommand->StdOut; + CommandData->StdErr = GetNextVdmCommand->StdErr; + + /* Write back the startup info */ + RtlMoveMemory(&CommandData->StartupInfo, + GetNextVdmCommand->StartupInfo, + sizeof(STARTUPINFOA)); + + if (CommandData->CmdLen) + { + /* Write back the command line */ + RtlMoveMemory(CommandData->CmdLine, + GetNextVdmCommand->CmdLine, + GetNextVdmCommand->CmdLen); + + /* Set the actual length */ + CommandData->CmdLen = GetNextVdmCommand->CmdLen; + } + + if (CommandData->AppLen) + { + /* Write back the application name */ + RtlMoveMemory(CommandData->AppName, + GetNextVdmCommand->AppName, + GetNextVdmCommand->AppLen); + + /* Set the actual length */ + CommandData->AppLen = GetNextVdmCommand->AppLen; + } + + if (CommandData->PifLen) + { + /* Write back the PIF file name */ + RtlMoveMemory(CommandData->PifFile, + GetNextVdmCommand->PifFile, + GetNextVdmCommand->PifLen); + + /* Set the actual length */ + CommandData->PifLen = GetNextVdmCommand->PifLen; + } + + if (CommandData->CurDirectoryLen) + { + /* Write back the current directory */ + RtlMoveMemory(CommandData->CurDirectory, + GetNextVdmCommand->CurDirectory, + GetNextVdmCommand->CurDirectoryLen); + + /* Set the actual length */ + CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen; + } + + if (CommandData->EnvLen) + { + /* Write back the environment */ + RtlMoveMemory(CommandData->Env, + GetNextVdmCommand->Env, + GetNextVdmCommand->EnvLen); + + /* Set the actual length */ + CommandData->EnvLen = GetNextVdmCommand->EnvLen; + } + + if (CommandData->DesktopLen) + { + /* Write back the desktop name */ + RtlMoveMemory(CommandData->Desktop, + GetNextVdmCommand->Desktop, + GetNextVdmCommand->DesktopLen); + + /* Set the actual length */ + CommandData->DesktopLen = GetNextVdmCommand->DesktopLen; + } + + if (CommandData->TitleLen) + { + /* Write back the title */ + RtlMoveMemory(CommandData->Title, + GetNextVdmCommand->Title, + GetNextVdmCommand->TitleLen); + + /* Set the actual length */ + CommandData->TitleLen = GetNextVdmCommand->TitleLen; + } + + if (CommandData->ReservedLen) + { + /* Write back the reserved parameter */ + RtlMoveMemory(CommandData->Reserved, + GetNextVdmCommand->Reserved, + GetNextVdmCommand->ReservedLen); + + /* Set the actual length */ + CommandData->ReservedLen = GetNextVdmCommand->ReservedLen; + } + + /* Write the remaining output parameters */ + CommandData->TaskId = GetNextVdmCommand->iTask; + CommandData->CreationFlags = GetNextVdmCommand->dwCreationFlags; + CommandData->CodePage = GetNextVdmCommand->CodePage; + CommandData->ExitCode = GetNextVdmCommand->ExitCode; + CommandData->CurrentDrive = GetNextVdmCommand->CurrentDrive; + CommandData->VDMState = GetNextVdmCommand->VDMState; + CommandData->ComingFromBat = GetNextVdmCommand->fComingFromBat; + + /* It was successful */ + Result = TRUE; + } + else if ((CommandData->VDMState == VDM_INC_REENTER_COUNT) + || (CommandData->VDMState == VDM_DEC_REENTER_COUNT)) + { + /* Setup the input parameters */ + SetReenterCount->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle; + SetReenterCount->fIncDec = CommandData->VDMState; + + /* Call CSRSS */ + Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + NULL, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetReenterCount), + sizeof(BASE_SET_REENTER_COUNT)); + BaseSetLastNTError(Status); + Result = NT_SUCCESS(Status); + } + else + { + BaseSetLastNTError(STATUS_INVALID_PARAMETER); + Result = FALSE; + } + } + else + { + /* Call CSRSS */ + Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + NULL, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepIsFirstVDM), + sizeof(BASE_IS_FIRST_VDM)); + if (!NT_SUCCESS(Status)) + { + BaseSetLastNTError(Status); + goto Cleanup; + } + + /* Return TRUE if this is the first VDM */ + Result = IsFirstVdm->FirstVDM; + } + +Cleanup: + if (CaptureBuffer != NULL) CsrFreeCaptureBuffer(CaptureBuffer); + return Result; } /* - * @unimplemented + * @implemented */ DWORD WINAPI -GetVDMCurrentDirectories ( - DWORD Unknown0, - DWORD Unknown1 - ) +GetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs) { - STUB; - return 0; + BASE_API_MESSAGE ApiMessage; + PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest; + PCSR_CAPTURE_BUFFER CaptureBuffer; + + /* Allocate the capture buffer */ + CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs); + if (CaptureBuffer == NULL) + { + BaseSetLastNTError(STATUS_NO_MEMORY); + return 0; + } + + /* Setup the input parameters */ + VDMCurrentDirsRequest->cchCurDirs = cchCurDirs; + CsrAllocateMessagePointer(CaptureBuffer, + cchCurDirs, + (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs); + + /* Call CSRSS */ + CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + CaptureBuffer, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMCurDirs), + sizeof(BASE_GETSET_VDM_CURDIRS)); + + /* Set the last error */ + BaseSetLastNTError(ApiMessage.Status); + + if (NT_SUCCESS(ApiMessage.Status)) + { + /* Copy the result */ + RtlMoveMemory(lpszzCurDirs, VDMCurrentDirsRequest->lpszzCurDirs, cchCurDirs); + } + + /* Free the capture buffer */ + CsrFreeCaptureBuffer(CaptureBuffer); + + /* Return the size if it was successful, or if the buffer was too small */ + return (NT_SUCCESS(ApiMessage.Status) || (ApiMessage.Status == STATUS_BUFFER_TOO_SMALL)) + ? VDMCurrentDirsRequest->cchCurDirs : 0; } @@ -851,17 +1547,44 @@ RegisterWowExec ( /* - * @unimplemented + * @implemented */ BOOL WINAPI -SetVDMCurrentDirectories ( - DWORD Unknown0, - DWORD Unknown1 - ) +SetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs) { - STUB; - return FALSE; + BASE_API_MESSAGE ApiMessage; + PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest; + PCSR_CAPTURE_BUFFER CaptureBuffer; + + /* Allocate the capture buffer */ + CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs); + if (CaptureBuffer == NULL) + { + BaseSetLastNTError(STATUS_NO_MEMORY); + return FALSE; + } + + /* Setup the input parameters */ + VDMCurrentDirsRequest->cchCurDirs = cchCurDirs; + CsrCaptureMessageBuffer(CaptureBuffer, + lpszzCurDirs, + cchCurDirs, + (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs); + + /* Call CSRSS */ + CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + CaptureBuffer, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetVDMCurDirs), + sizeof(BASE_GETSET_VDM_CURDIRS)); + + /* Free the capture buffer */ + CsrFreeCaptureBuffer(CaptureBuffer); + + /* Set the last error */ + BaseSetLastNTError(ApiMessage.Status); + + return NT_SUCCESS(ApiMessage.Status) ? TRUE : FALSE; } /* @@ -882,12 +1605,15 @@ VDMConsoleOperation ( /* * @unimplemented */ -DWORD +BOOL WINAPI -VDMOperationStarted ( - DWORD Unknown0 - ) +VDMOperationStarted(IN ULONG Unknown0) { - STUB; - return 0; + DPRINT1("VDMOperationStarted(%d)\n", Unknown0); + + return + BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler, + NULL, + 0, + Unknown0); } diff --git a/reactos/dll/win32/kernel32/include/vdm.h b/reactos/dll/win32/kernel32/include/vdm.h index 696597dfb5d0b..56e872683c350 100644 --- a/reactos/dll/win32/kernel32/include/vdm.h +++ b/reactos/dll/win32/kernel32/include/vdm.h @@ -8,42 +8,6 @@ #pragma once -/* CONSTANTS ******************************************************************/ - -typedef enum _VDM_ENTRY_CODE -{ - VdmEntryUndo, - VdmEntryUpdateProcess, - VdmEntryUpdateControlCHandler -} VDM_ENTRY_CODE; - -// -// Undo States -// -#define VDM_UNDO_PARTIAL 0x01 -#define VDM_UNDO_FULL 0x02 -#define VDM_UNDO_REUSE 0x04 -#define VDM_UNDO_COMPLETED 0x08 - -// -// Binary Types to share with VDM -// -#define BINARY_TYPE_EXE 0x01 -#define BINARY_TYPE_COM 0x02 -#define BINARY_TYPE_PIF 0x03 -#define BINARY_TYPE_DOS 0x10 -#define BINARY_TYPE_SEPARATE_WOW 0x20 -#define BINARY_TYPE_WOW 0x40 -#define BINARY_TYPE_WOW_EX 0x80 - -// -// VDM States -// -#define VDM_NOT_LOADED 0x01 -#define VDM_NOT_READY 0x02 -#define VDM_READY 0x04 - - /* FUNCTION PROTOTYPES ********************************************************/ BOOL @@ -87,7 +51,7 @@ BaseCheckForVDM( OUT LPDWORD ExitCode ); -BOOL +NTSTATUS WINAPI BaseCheckVDM( IN ULONG BinaryType, diff --git a/reactos/dll/win32/kernel32/k32.h b/reactos/dll/win32/kernel32/k32.h index e544b0c7eef4a..7228f83ed80c7 100644 --- a/reactos/dll/win32/kernel32/k32.h +++ b/reactos/dll/win32/kernel32/k32.h @@ -46,6 +46,7 @@ #include #include #include +#include /* DDK Driver Headers */ #include diff --git a/reactos/include/ddk/isvbop.h b/reactos/include/ddk/isvbop.h new file mode 100644 index 0000000000000..088e9e6dcdad6 --- /dev/null +++ b/reactos/include/ddk/isvbop.h @@ -0,0 +1,51 @@ +/* + * isvbop.h + * + * Windows NT Device Driver Kit + * + * This file is part of the ReactOS DDK package. + * + * Contributors: + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* + * The corresponding ASM header of this file is isvbop.inc. + */ + +#pragma once + +/* BOP Identifiers */ +#define BOP_3RDPARTY 0x58 // 3rd-party VDD BOP +#define BOP_UNSIMULATE 0xFE // Stop execution + +#if defined(__GNUC__) + +#define RegisterModule() __asm__(".byte 0xC4, 0xC4, %c0, 0" : : "i"(BOP_3RDPARTY)) +#define UnRegisterModule() __asm__(".byte 0xC4, 0xC4, %c0, 1" : : "i"(BOP_3RDPARTY)) +#define DispatchCall() __asm__(".byte 0xC4, 0xC4, %c0, 2" : : "i"(BOP_3RDPARTY)) +#define VDDUnSimulate16() __asm__(".byte 0xC4, 0xC4, %c0" : : "i"(BOP_UNSIMULATE)) + +#elif defined(_MSC_VER) + +#define RegisterModule() _asm _emit 0xC4 _asm _emit 0xC4 _asm _emit BOP_3RDPARTY _asm _emit 0 +#define UnRegisterModule() _asm _emit 0xC4 _asm _emit 0xC4 _asm _emit BOP_3RDPARTY _asm _emit 1 +#define DispatchCall() _asm _emit 0xC4 _asm _emit 0xC4 _asm _emit BOP_3RDPARTY _asm _emit 2 +#define VDDUnSimulate16() _asm _emit 0xC4 _asm _emit 0xC4 _asm _emit BOP_UNSIMULATE + +#else +#error Unknown compiler for inline assembler +#endif + +/* EOF */ diff --git a/reactos/include/ddk/isvbop.inc b/reactos/include/ddk/isvbop.inc new file mode 100644 index 0000000000000..7a13b4bc27a90 --- /dev/null +++ b/reactos/include/ddk/isvbop.inc @@ -0,0 +1,49 @@ +/* + * isvbop.inc + * + * Windows NT Device Driver Kit + * + * This file is part of the ReactOS DDK package. + * + * Contributors: + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* + * This is the corresponding ASM header for isvbop.h. + * Please refer to isvbop.h for information about these interfaces. + */ + +#include + +BOP_3RDPARTY = HEX(58) +BOP_UNSIMULATE = HEX(FE) + +MACRO(RegisterModule) + .byte HEX(C4), HEX(C4), BOP_3RDPARTY, 0 +ENDM + +MACRO(UnRegisterModule) + .byte HEX(C4), HEX(C4), BOP_3RDPARTY, 1 +ENDM + +MACRO(DispatchCall) + .byte HEX(C4), HEX(C4), BOP_3RDPARTY, 2 +ENDM + +MACRO(VDDUnSimulate16) + .byte HEX(C4), HEX(C4), BOP_UNSIMULATE +ENDM + +/* EOF */ diff --git a/reactos/include/ddk/nt_vdd.h b/reactos/include/ddk/nt_vdd.h new file mode 100644 index 0000000000000..b4b6c7df67d8e --- /dev/null +++ b/reactos/include/ddk/nt_vdd.h @@ -0,0 +1,161 @@ +/* + * nt_vdd.h + * + * Windows NT Device Driver Kit + * + * This file is part of the ReactOS DDK package. + * + * Contributors: + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#pragma once + +#define _NT_VDD + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * VDM Control + */ + +VOID +WINAPI +VDDSimulate16(VOID); + +VOID +WINAPI +VDDTerminateVDM(VOID); + + +/* + * I/O Port services + */ + +typedef VOID (*PFNVDD_INB) (WORD iport, PBYTE data); +typedef VOID (*PFNVDD_INW) (WORD iport, PWORD data); +typedef VOID (*PFNVDD_INSB) (WORD iport, PBYTE data, WORD count); +typedef VOID (*PFNVDD_INSW) (WORD iport, PWORD data, WORD count); +typedef VOID (*PFNVDD_OUTB) (WORD iport, BYTE data); +typedef VOID (*PFNVDD_OUTW) (WORD iport, WORD data); +typedef VOID (*PFNVDD_OUTSB) (WORD iport, PBYTE data, WORD count); +typedef VOID (*PFNVDD_OUTSW) (WORD iport, PWORD data, WORD count); + +typedef struct _VDD_IO_HANDLERS +{ + PFNVDD_INB inb_handler; + PFNVDD_INW inw_handler; + PFNVDD_INSB insb_handler; + PFNVDD_INSW insw_handler; + PFNVDD_OUTB outb_handler; + PFNVDD_OUTW outw_handler; + PFNVDD_OUTSB outsb_handler; + PFNVDD_OUTSW outsw_handler; +} VDD_IO_HANDLERS, *PVDD_IO_HANDLERS; + +typedef struct _VDD_IO_PORTRANGE +{ + WORD First; + WORD Last; +} VDD_IO_PORTRANGE, *PVDD_IO_PORTRANGE; + +BOOL +WINAPI +VDDInstallIOHook +( + HANDLE hVdd, + WORD cPortRange, + PVDD_IO_PORTRANGE pPortRange, + PVDD_IO_HANDLERS IOhandler +); + +VOID +WINAPI +VDDDeInstallIOHook +( + HANDLE hVdd, + WORD cPortRange, + PVDD_IO_PORTRANGE pPortRange +); + + +/* + * Memory services + */ + +typedef enum +{ + VDM_V86, + VDM_PM +} VDM_MODE; + +#ifndef MSW_PE +#define MSW_PE 0x0001 +#endif + +#define getMODE() ((getMSW() & MSW_PE) ? VDM_PM : VDM_V86) + +PBYTE +WINAPI +Sim32pGetVDMPointer +( + IN ULONG Address, + IN BOOLEAN ProtectedMode +); + +PBYTE +WINAPI +MGetVdmPointer +( + IN ULONG Address, + IN ULONG Size, + IN BOOLEAN ProtectedMode +); + +PVOID +WINAPI +VdmMapFlat +( + IN USHORT Segment, + IN ULONG Offset, + IN VDM_MODE Mode +); + +BOOL +WINAPI +VdmFlushCache +( + IN USHORT Segment, + IN ULONG Offset, + IN ULONG Size, + IN VDM_MODE Mode +); + +BOOL +WINAPI +VdmUnmapFlat +( + IN USHORT Segment, + IN ULONG Offset, + IN PVOID Buffer, + IN VDM_MODE Mode +); + +#ifdef __cplusplus +} +#endif + +/* EOF */ diff --git a/reactos/include/ddk/vddsvc.h b/reactos/include/ddk/vddsvc.h new file mode 100644 index 0000000000000..eb80822314170 --- /dev/null +++ b/reactos/include/ddk/vddsvc.h @@ -0,0 +1,254 @@ +/* + * vddsvc.h + * + * Windows NT Device Driver Kit + * + * This file is part of the ReactOS DDK package. + * + * Contributors: + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#pragma once + +#ifndef _NT_VDD +#include +#endif + +/* + * Interrupts services + */ +#define ICA_MASTER 0 +#define ICA_SLAVE 1 + +VOID +WINAPI +call_ica_hw_interrupt +( + INT ms, + BYTE line, + INT count +); + +#define VDDSimulateInterrupt(ms, line, count) \ + call_ica_hw_interrupt((ms), (line), (count)) // Windows specifies a count of 1 ... + + +/* + * Registers manipulation + */ +PVOID WINAPI getIntelRegistersPointer(VOID); + +#ifdef __i386__ + +ULONG WINAPI getEAX(VOID); +VOID WINAPI setEAX(ULONG); +USHORT WINAPI getAX(VOID); +VOID WINAPI setAX(USHORT); +UCHAR WINAPI getAH(VOID); +VOID WINAPI setAH(UCHAR); +UCHAR WINAPI getAL(VOID); +VOID WINAPI setAL(UCHAR); + +ULONG WINAPI getEBX(VOID); +VOID WINAPI setEBX(ULONG); +USHORT WINAPI getBX(VOID); +VOID WINAPI setBX(USHORT); +UCHAR WINAPI getBH(VOID); +VOID WINAPI setBH(UCHAR); +UCHAR WINAPI getBL(VOID); +VOID WINAPI setBL(UCHAR); + +ULONG WINAPI getECX(VOID); +VOID WINAPI setECX(ULONG); +USHORT WINAPI getCX(VOID); +VOID WINAPI setCX(USHORT); +UCHAR WINAPI getCH(VOID); +VOID WINAPI setCH(UCHAR); +UCHAR WINAPI getCL(VOID); +VOID WINAPI setCL(UCHAR); + +ULONG WINAPI getEDX(VOID); +VOID WINAPI setEDX(ULONG); +USHORT WINAPI getDX(VOID); +VOID WINAPI setDX(USHORT); +UCHAR WINAPI getDH(VOID); +VOID WINAPI setDH(UCHAR); +UCHAR WINAPI getDL(VOID); +VOID WINAPI setDL(UCHAR); + + + +ULONG WINAPI getESP(VOID); +VOID WINAPI setESP(ULONG); +USHORT WINAPI getSP(VOID); +VOID WINAPI setSP(USHORT); + +ULONG WINAPI getEBP(VOID); +VOID WINAPI setEBP(ULONG); +USHORT WINAPI getBP(VOID); +VOID WINAPI setBP(USHORT); + +ULONG WINAPI getESI(VOID); +VOID WINAPI setESI(ULONG); +USHORT WINAPI getSI(VOID); +VOID WINAPI setSI(USHORT); + +ULONG WINAPI getEDI(VOID); +VOID WINAPI setEDI(ULONG); +USHORT WINAPI getDI(VOID); +VOID WINAPI setDI(USHORT); + +ULONG WINAPI getEIP(VOID); +VOID WINAPI setEIP(ULONG); +USHORT WINAPI getIP(VOID); +VOID WINAPI setIP(USHORT); + +USHORT WINAPI getCS(VOID); +VOID WINAPI setCS(USHORT); +USHORT WINAPI getSS(VOID); +VOID WINAPI setSS(USHORT); +USHORT WINAPI getDS(VOID); +VOID WINAPI setDS(USHORT); +USHORT WINAPI getES(VOID); +VOID WINAPI setES(USHORT); +USHORT WINAPI getFS(VOID); +VOID WINAPI setFS(USHORT); +USHORT WINAPI getGS(VOID); +VOID WINAPI setGS(USHORT); + +ULONG WINAPI getCF(VOID); +VOID WINAPI setCF(ULONG); +ULONG WINAPI getPF(VOID); +VOID WINAPI setPF(ULONG); +ULONG WINAPI getAF(VOID); +VOID WINAPI setAF(ULONG); +ULONG WINAPI getZF(VOID); +VOID WINAPI setZF(ULONG); +ULONG WINAPI getSF(VOID); +VOID WINAPI setSF(ULONG); +ULONG WINAPI getIF(VOID); +VOID WINAPI setIF(ULONG); +ULONG WINAPI getDF(VOID); +VOID WINAPI setDF(ULONG); +ULONG WINAPI getOF(VOID); +VOID WINAPI setOF(ULONG); + +ULONG WINAPI getEFLAGS(VOID); +VOID WINAPI setEFLAGS(ULONG); + +USHORT WINAPI getMSW(VOID); +VOID WINAPI setMSW(USHORT); + +#else + +ULONG WINAPI c_getEAX(VOID); +VOID WINAPI c_setEAX(ULONG); +USHORT WINAPI c_getAX(VOID); +VOID WINAPI c_setAX(USHORT); +UCHAR WINAPI c_getAH(VOID); +VOID WINAPI c_setAH(UCHAR); +UCHAR WINAPI c_getAL(VOID); +VOID WINAPI c_setAL(UCHAR); + +ULONG WINAPI c_getEBX(VOID); +VOID WINAPI c_setEBX(ULONG); +USHORT WINAPI c_getBX(VOID); +VOID WINAPI c_setBX(USHORT); +UCHAR WINAPI c_getBH(VOID); +VOID WINAPI c_setBH(UCHAR); +UCHAR WINAPI c_getBL(VOID); +VOID WINAPI c_setBL(UCHAR); + +ULONG WINAPI c_getECX(VOID); +VOID WINAPI c_setECX(ULONG); +USHORT WINAPI c_getCX(VOID); +VOID WINAPI c_setCX(USHORT); +UCHAR WINAPI c_getCH(VOID); +VOID WINAPI c_setCH(UCHAR); +UCHAR WINAPI c_getCL(VOID); +VOID WINAPI c_setCL(UCHAR); + +ULONG WINAPI c_getEDX(VOID); +VOID WINAPI c_setEDX(ULONG); +USHORT WINAPI c_getDX(VOID); +VOID WINAPI c_setDX(USHORT); +UCHAR WINAPI c_getDH(VOID); +VOID WINAPI c_setDH(UCHAR); +UCHAR WINAPI c_getDL(VOID); +VOID WINAPI c_setDL(UCHAR); + + + +ULONG WINAPI c_getESP(VOID); +VOID WINAPI c_setESP(ULONG); +USHORT WINAPI c_getSP(VOID); +VOID WINAPI c_setSP(USHORT); + +ULONG WINAPI c_getEBP(VOID); +VOID WINAPI c_setEBP(ULONG); +USHORT WINAPI c_getBP(VOID); +VOID WINAPI c_setBP(USHORT); + +ULONG WINAPI c_getESI(VOID); +VOID WINAPI c_setESI(ULONG); +USHORT WINAPI c_getSI(VOID); +VOID WINAPI c_setSI(USHORT); + +ULONG WINAPI c_getEDI(VOID); +VOID WINAPI c_setEDI(ULONG); +USHORT WINAPI c_getDI(VOID); +VOID WINAPI c_setDI(USHORT); + +ULONG WINAPI c_getEIP(VOID); +VOID WINAPI c_setEIP(ULONG); +USHORT WINAPI c_getIP(VOID); +VOID WINAPI c_setIP(USHORT); + +USHORT WINAPI c_getCS(VOID); +VOID WINAPI c_setCS(USHORT); +USHORT WINAPI c_getSS(VOID); +VOID WINAPI c_setSS(USHORT); +USHORT WINAPI c_getDS(VOID); +VOID WINAPI c_setDS(USHORT); +USHORT WINAPI c_getES(VOID); +VOID WINAPI c_setES(USHORT); +USHORT WINAPI c_getFS(VOID); +VOID WINAPI c_setFS(USHORT); +USHORT WINAPI c_getGS(VOID); +VOID WINAPI c_setGS(USHORT); + +ULONG WINAPI c_getCF(VOID); +VOID WINAPI c_setCF(ULONG); +ULONG WINAPI c_getPF(VOID); +VOID WINAPI c_setPF(ULONG); +ULONG WINAPI c_getAF(VOID); +VOID WINAPI c_setAF(ULONG); +ULONG WINAPI c_getZF(VOID); +VOID WINAPI c_setZF(ULONG); +ULONG WINAPI c_getSF(VOID); +VOID WINAPI c_setSF(ULONG); +ULONG WINAPI c_getIF(VOID); +VOID WINAPI c_setIF(ULONG); +ULONG WINAPI c_getDF(VOID); +VOID WINAPI c_setDF(ULONG); +ULONG WINAPI c_getOF(VOID); +VOID WINAPI c_setOF(ULONG); + +USHORT WINAPI c_getMSW(VOID); +VOID WINAPI c_setMSW(USHORT); + +#endif + +/* EOF */ diff --git a/reactos/include/reactos/libs/fast486/fast486.h b/reactos/include/reactos/libs/fast486/fast486.h new file mode 100644 index 0000000000000..339ecb6a77ef8 --- /dev/null +++ b/reactos/include/reactos/libs/fast486/fast486.h @@ -0,0 +1,562 @@ +/* + * Fast486 386/486 CPU Emulation Library + * fast486.h + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _FAST486_H_ +#define _FAST486_H_ + +#pragma once + +/* DEFINES ********************************************************************/ + +#ifndef FASTCALL +#define FASTCALL __fastcall +#endif + +#define FAST486_NUM_GEN_REGS 8 +#define FAST486_NUM_SEG_REGS 6 +#define FAST486_NUM_CTRL_REGS 3 +#define FAST486_NUM_DBG_REGS 6 +#define FAST486_NUM_FPU_REGS 8 + +#define FAST486_CR0_PE (1 << 0) +#define FAST486_CR0_MP (1 << 1) +#define FAST486_CR0_EM (1 << 2) +#define FAST486_CR0_TS (1 << 3) +#define FAST486_CR0_ET (1 << 4) +#define FAST486_CR0_NE (1 << 5) +#define FAST486_CR0_WP (1 << 16) +#define FAST486_CR0_AM (1 << 18) +#define FAST486_CR0_NW (1 << 29) +#define FAST486_CR0_CD (1 << 30) +#define FAST486_CR0_PG (1 << 31) + +#define FAST486_DR4_B0 (1 << 0) +#define FAST486_DR4_B1 (1 << 1) +#define FAST486_DR4_B2 (1 << 2) +#define FAST486_DR4_B3 (1 << 3) +#define FAST486_DR4_BD (1 << 13) +#define FAST486_DR4_BS (1 << 14) +#define FAST486_DR4_BT (1 << 15) + +#define FAST486_DR5_L0 (1 << 0) +#define FAST486_DR5_G0 (1 << 1) +#define FAST486_DR5_L1 (1 << 2) +#define FAST486_DR5_G1 (1 << 3) +#define FAST486_DR5_L2 (1 << 4) +#define FAST486_DR5_G2 (1 << 5) +#define FAST486_DR5_L3 (1 << 6) +#define FAST486_DR5_G3 (1 << 7) +#define FAST486_DR5_LE (1 << 8) +#define FAST486_DR5_GE (1 << 9) +#define FAST486_DR5_GD (1 << 13) + +#define FAST486_DBG_BREAK_EXEC 0 +#define FAST486_DBG_BREAK_WRITE 1 +#define FAST486_DBG_BREAK_READWRITE 3 + +#define FAST486_DR4_RESERVED 0xFFFF1FF0 +#define FAST486_DR5_RESERVED 0x0000DC00 + +#define FAST486_IDT_TASK_GATE 0x5 +#define FAST486_IDT_INT_GATE 0x6 +#define FAST486_IDT_TRAP_GATE 0x7 +#define FAST486_IDT_INT_GATE_32 0xE +#define FAST486_IDT_TRAP_GATE_32 0xF + +#define FAST486_LDT_SIGNATURE 0x02 +#define FAST486_TSS_SIGNATURE 0x09 + +#define FAST486_PREFIX_SEG (1 << 0) +#define FAST486_PREFIX_OPSIZE (1 << 1) +#define FAST486_PREFIX_ADSIZE (1 << 2) +#define FAST486_PREFIX_LOCK (1 << 3) +#define FAST486_PREFIX_REPNZ (1 << 4) +#define FAST486_PREFIX_REP (1 << 5) + +struct _FAST486_STATE; +typedef struct _FAST486_STATE FAST486_STATE, *PFAST486_STATE; + +typedef enum _FAST486_GEN_REGS +{ + FAST486_REG_EAX, + FAST486_REG_ECX, + FAST486_REG_EDX, + FAST486_REG_EBX, + FAST486_REG_ESP, + FAST486_REG_EBP, + FAST486_REG_ESI, + FAST486_REG_EDI +} FAST486_GEN_REGS, *PFAST486_GEN_REGS; + +typedef enum _FAST486_SEG_REGS +{ + FAST486_REG_ES, + FAST486_REG_CS, + FAST486_REG_SS, + FAST486_REG_DS, + FAST486_REG_FS, + FAST486_REG_GS +} FAST486_SEG_REGS, *PFAST486_SEG_REGS; + +typedef enum _FAST486_CTRL_REGS +{ + FAST486_REG_CR0 = 0, + FAST486_REG_CR2 = 1, + FAST486_REG_CR3 = 2, +} FAST486_CTRL_REGS, *PFAST486_CTRL_REGS; + +typedef enum _FAST486_DBG_REGS +{ + FAST486_REG_DR0 = 0, + FAST486_REG_DR1 = 1, + FAST486_REG_DR2 = 2, + FAST486_REG_DR3 = 3, + FAST486_REG_DR4 = 4, + FAST486_REG_DR5 = 5, + FAST486_REG_DR6 = 4, // alias to DR4 + FAST486_REG_DR7 = 5 // alias to DR5 +} FAST486_DBG_REGS, *PFAST486_DBG_REGS; + +typedef enum _FAST486_EXCEPTIONS +{ + FAST486_EXCEPTION_DE = 0x00, + FAST486_EXCEPTION_DB = 0x01, + FAST486_EXCEPTION_BP = 0x03, + FAST486_EXCEPTION_OF = 0x04, + FAST486_EXCEPTION_BR = 0x05, + FAST486_EXCEPTION_UD = 0x06, + FAST486_EXCEPTION_NM = 0x07, + FAST486_EXCEPTION_DF = 0x08, + FAST486_EXCEPTION_TS = 0x0A, + FAST486_EXCEPTION_NP = 0x0B, + FAST486_EXCEPTION_SS = 0x0C, + FAST486_EXCEPTION_GP = 0x0D, + FAST486_EXCEPTION_PF = 0x0E, + FAST486_EXCEPTION_MF = 0x10, + FAST486_EXCEPTION_AC = 0x11, + FAST486_EXCEPTION_MC = 0x12 +} FAST486_EXCEPTIONS, *PFAST486_EXCEPTIONS; + +typedef enum _FAST486_INT_STATUS +{ + FAST486_INT_NONE = 0, + FAST486_INT_EXECUTE = 1, + FAST486_INT_SIGNAL = 2 +} FAST486_INT_STATUS, *PFAST486_INT_STATUS; + +typedef +VOID +(NTAPI *FAST486_MEM_READ_PROC) +( + PFAST486_STATE State, + ULONG Address, + PVOID Buffer, + ULONG Size +); + +typedef +VOID +(NTAPI *FAST486_MEM_WRITE_PROC) +( + PFAST486_STATE State, + ULONG Address, + PVOID Buffer, + ULONG Size +); + +typedef +VOID +(NTAPI *FAST486_IO_READ_PROC) +( + PFAST486_STATE State, + ULONG Port, + PVOID Buffer, + ULONG DataCount, + UCHAR DataSize +); + +typedef +VOID +(NTAPI *FAST486_IO_WRITE_PROC) +( + PFAST486_STATE State, + ULONG Port, + PVOID Buffer, + ULONG DataCount, + UCHAR DataSize +); + +typedef +VOID +(NTAPI *FAST486_IDLE_PROC) +( + PFAST486_STATE State +); + +typedef +VOID +(NTAPI *FAST486_BOP_PROC) +( + PFAST486_STATE State, + UCHAR BopCode +); + +typedef +UCHAR +(NTAPI *FAST486_INT_ACK_PROC) +( + PFAST486_STATE State +); + +typedef union _FAST486_REG +{ + union + { + struct + { + UCHAR LowByte; + UCHAR HighByte; + }; + USHORT LowWord; + }; + ULONG Long; +} FAST486_REG, *PFAST486_REG; + +typedef struct _FAST486_SEG_REG +{ + USHORT Selector; + + /* Descriptor cache */ + ULONG Accessed : 1; + ULONG ReadWrite : 1; + ULONG DirConf : 1; + ULONG Executable : 1; + ULONG SystemType : 1; + ULONG Dpl : 2; + ULONG Present : 1; + ULONG Size : 1; + ULONG Limit; + ULONG Base; +} FAST486_SEG_REG, *PFAST486_SEG_REG; + +typedef struct +{ + USHORT Selector; + ULONG Base; + ULONG Limit; +} FAST486_LDT_REG; + +typedef struct +{ + USHORT Selector; + ULONG Base; + ULONG Limit; + BOOLEAN Busy; +} FAST486_TASK_REG, *PFAST486_TASK_REG; + +#pragma pack(push, 1) + +typedef struct +{ + ULONG Limit : 16; + ULONG Base : 16; + ULONG BaseMid : 8; + ULONG Accessed : 1; + ULONG ReadWrite : 1; + ULONG DirConf : 1; + ULONG Executable : 1; + ULONG SystemType : 1; + ULONG Dpl : 2; + ULONG Present : 1; + ULONG LimitHigh : 4; + ULONG Avl : 1; + ULONG Reserved : 1; + ULONG Size : 1; + ULONG Granularity : 1; + ULONG BaseHigh : 8; +} FAST486_GDT_ENTRY, *PFAST486_GDT_ENTRY; + +/* Verify the structure size */ +C_ASSERT(sizeof(FAST486_GDT_ENTRY) == sizeof(ULONGLONG)); + +typedef struct +{ + ULONG Limit : 16; + ULONG Base : 16; + ULONG BaseMid : 8; + ULONG Signature : 5; + ULONG Dpl : 2; + ULONG Present : 1; + ULONG LimitHigh : 4; + ULONG Avl : 1; + ULONG Reserved : 2; + ULONG Granularity : 1; + ULONG BaseHigh : 8; +} FAST486_SYSTEM_DESCRIPTOR, *PFAST486_SYSTEM_DESCRIPTOR; + +/* Verify the structure size */ +C_ASSERT(sizeof(FAST486_SYSTEM_DESCRIPTOR) == sizeof(ULONGLONG)); + +typedef struct +{ + ULONG Offset : 16; + ULONG Selector : 16; + ULONG ParamCount : 5; + ULONG Reserved : 3; + ULONG Type : 4; + ULONG SystemType : 1; + ULONG Dpl : 2; + ULONG Present : 1; + ULONG OffsetHigh : 16; +} FAST486_CALL_GATE, *PFAST486_CALL_GATE; + +/* Verify the structure size */ +C_ASSERT(sizeof(FAST486_CALL_GATE) == sizeof(ULONGLONG)); + +typedef struct +{ + ULONG Offset : 16; + ULONG Selector : 16; + ULONG Zero : 8; + ULONG Type : 4; + ULONG Storage : 1; + ULONG Dpl : 2; + ULONG Present : 1; + ULONG OffsetHigh : 16; +} FAST486_IDT_ENTRY, *PFAST486_IDT_ENTRY; + +/* Verify the structure size */ +C_ASSERT(sizeof(FAST486_IDT_ENTRY) == sizeof(ULONGLONG)); + +#pragma pack(pop) + +typedef struct _FAST486_TABLE_REG +{ + USHORT Size; + ULONG Address; +} FAST486_TABLE_REG, *PFAST486_TABLE_REG; + +typedef union _FAST486_FLAGS_REG +{ + USHORT LowWord; + ULONG Long; + + struct + { + ULONG Cf : 1; + ULONG AlwaysSet : 1; + ULONG Pf : 1; + ULONG Reserved0 : 1; + ULONG Af : 1; + ULONG Reserved1 : 1; + ULONG Zf : 1; + ULONG Sf : 1; + ULONG Tf : 1; + ULONG If : 1; + ULONG Df : 1; + ULONG Of : 1; + ULONG Iopl : 2; + ULONG Nt : 1; + ULONG Reserved2 : 1; + ULONG Rf : 1; + ULONG Vm : 1; + ULONG Ac : 1; + + // ULONG Reserved : 13; + }; +} FAST486_FLAGS_REG, *PFAST486_FLAGS_REG; + +typedef struct _FAST486_TSS +{ + ULONG Link; + ULONG Esp0; + ULONG Ss0; + ULONG Esp1; + ULONG Ss1; + ULONG Esp2; + ULONG Ss2; + ULONG Cr3; + ULONG Eip; + ULONG Eflags; + ULONG Eax; + ULONG Ecx; + ULONG Edx; + ULONG Ebx; + ULONG Esp; + ULONG Ebp; + ULONG Esi; + ULONG Edi; + ULONG Es; + ULONG Cs; + ULONG Ss; + ULONG Ds; + ULONG Fs; + ULONG Gs; + ULONG Ldtr; + ULONG IopbOffset; +} FAST486_TSS, *PFAST486_TSS; + +typedef struct _FAST486_FPU_DATA_REG +{ + ULONGLONG Mantissa; + USHORT Exponent; +} FAST486_FPU_DATA_REG, *PFAST486_FPU_DATA_REG; + +typedef union _FAST486_FPU_STATUS_REG +{ + USHORT Value; + + struct + { + ULONG Ie : 1; + ULONG De : 1; + ULONG Ze : 1; + ULONG Oe : 1; + ULONG Ue : 1; + ULONG Pe : 1; + ULONG Sf : 1; + ULONG Es : 1; + ULONG Code0 : 1; + ULONG Code1 : 1; + ULONG Code2 : 1; + ULONG Top : 3; + ULONG Code3 : 1; + ULONG Busy : 1; + }; +} FAST486_FPU_STATUS_REG, *PFAST486_FPU_STATUS_REG; + +typedef union _FAST486_FPU_CONTROL_REG +{ + USHORT Value; + + struct + { + ULONG Im : 1; + ULONG Dm : 1; + ULONG Zm : 1; + ULONG Om : 1; + ULONG Um : 1; + ULONG Pm : 1; + ULONG Reserved : 2; + ULONG Pc : 2; + ULONG Rc : 2; + ULONG Inf : 1; + // ULONG Reserved1 : 3; + }; +} FAST486_FPU_CONTROL_REG, *PFAST486_FPU_CONTROL_REG; + +struct _FAST486_STATE +{ + FAST486_MEM_READ_PROC MemReadCallback; + FAST486_MEM_WRITE_PROC MemWriteCallback; + FAST486_IO_READ_PROC IoReadCallback; + FAST486_IO_WRITE_PROC IoWriteCallback; + FAST486_IDLE_PROC IdleCallback; + FAST486_BOP_PROC BopCallback; + FAST486_INT_ACK_PROC IntAckCallback; + FAST486_REG GeneralRegs[FAST486_NUM_GEN_REGS]; + FAST486_SEG_REG SegmentRegs[FAST486_NUM_SEG_REGS]; + FAST486_REG InstPtr, SavedInstPtr; + FAST486_FLAGS_REG Flags; + FAST486_TABLE_REG Gdtr, Idtr; + FAST486_LDT_REG Ldtr; + FAST486_TASK_REG TaskReg; + UCHAR Cpl; + ULONG ControlRegisters[FAST486_NUM_CTRL_REGS]; + ULONG DebugRegisters[FAST486_NUM_DBG_REGS]; + ULONG ExceptionCount; + ULONG PrefixFlags; + FAST486_SEG_REGS SegmentOverride; + FAST486_INT_STATUS IntStatus; + UCHAR PendingIntNum; + PULONG Tlb; + FAST486_FPU_DATA_REG FpuRegisters[FAST486_NUM_FPU_REGS]; + FAST486_FPU_STATUS_REG FpuStatus; + FAST486_FPU_CONTROL_REG FpuControl; + USHORT FpuTag; +}; + +/* FUNCTIONS ******************************************************************/ + +VOID +NTAPI +Fast486Initialize(PFAST486_STATE State, + FAST486_MEM_READ_PROC MemReadCallback, + FAST486_MEM_WRITE_PROC MemWriteCallback, + FAST486_IO_READ_PROC IoReadCallback, + FAST486_IO_WRITE_PROC IoWriteCallback, + FAST486_IDLE_PROC IdleCallback, + FAST486_BOP_PROC BopCallback, + FAST486_INT_ACK_PROC IntAckCallback, + PULONG Tlb); + +VOID +NTAPI +Fast486Reset(PFAST486_STATE State); + +VOID +NTAPI +Fast486Continue(PFAST486_STATE State); + +VOID +NTAPI +Fast486StepInto(PFAST486_STATE State); + +VOID +NTAPI +Fast486StepOver(PFAST486_STATE State); + +VOID +NTAPI +Fast486StepOut(PFAST486_STATE State); + +VOID +NTAPI +Fast486DumpState(PFAST486_STATE State); + +VOID +NTAPI +Fast486Interrupt(PFAST486_STATE State, UCHAR Number); + +VOID +NTAPI +Fast486InterruptSignal(PFAST486_STATE State); + +VOID +NTAPI +Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset); + +VOID +NTAPI +Fast486SetStack(PFAST486_STATE State, USHORT Segment, ULONG Offset); + +VOID +NTAPI +Fast486SetSegment +( + PFAST486_STATE State, + FAST486_SEG_REGS Segment, + USHORT Selector +); + +#endif // _FAST486_H_ + +/* EOF */ diff --git a/reactos/include/reactos/subsys/win/vdm.h b/reactos/include/reactos/subsys/win/vdm.h new file mode 100644 index 0000000000000..e493dcdc009c7 --- /dev/null +++ b/reactos/include/reactos/subsys/win/vdm.h @@ -0,0 +1,117 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Base API Server DLL + * FILE: include/reactos/subsys/win/vdm.h + * PURPOSE: Public definitions for the Virtual Dos Machine + * PROGRAMMERS: Aleksandar Andrejevic + * Alex Ionescu (alex.ionescu@reactos.org) + */ + +#ifndef _VDM_H +#define _VDM_H + +#pragma once + +/* CONSTANTS & TYPES **********************************************************/ + +typedef enum _VDM_ENTRY_CODE +{ + VdmEntryUndo, + VdmEntryUpdateProcess, + VdmEntryUpdateControlCHandler +} VDM_ENTRY_CODE; + +// +// Undo States +// +#define VDM_UNDO_PARTIAL 0x01 +#define VDM_UNDO_FULL 0x02 +#define VDM_UNDO_REUSE 0x04 +#define VDM_UNDO_COMPLETED 0x08 + +// +// Binary Types to share with VDM +// +#define BINARY_TYPE_EXE 0x01 +#define BINARY_TYPE_COM 0x02 +#define BINARY_TYPE_PIF 0x03 +#define BINARY_TYPE_DOS 0x10 +#define BINARY_TYPE_SEPARATE_WOW 0x20 +#define BINARY_TYPE_WOW 0x40 +#define BINARY_TYPE_WOW_EX 0x80 + +// +// VDM States +// +#define VDM_NOT_LOADED 0x01 +#define VDM_NOT_READY 0x02 +#define VDM_READY 0x04 + +// +// VDM Flags +// +#define VDM_FLAG_FIRST_TASK 0x01 +#define VDM_FLAG_WOW 0x02 +#define VDM_FLAG_DOS 0x04 +#define VDM_FLAG_RETRY 0x08 +#define VDM_INC_REENTER_COUNT 0x10 +#define VDM_DEC_REENTER_COUNT 0x20 +#define VDM_FLAG_NESTED_TASK 0x40 +#define VDM_FLAG_DONT_WAIT 0x80 +#define VDM_GET_FIRST_COMMAND 0x100 +#define VDM_GET_ENVIRONMENT 0x400 +#define VDM_FLAG_SEPARATE_WOW 0x800 +#define VDM_LIST_WOW_PROCESSES 0x1000 +#define VDM_LIST_WOW_TASKS 0x4000 +#define VDM_ADD_WOW_TASK 0x8000 + +typedef struct +{ + ULONG TaskId; + ULONG CreationFlags; + ULONG ExitCode; + ULONG CodePage; + HANDLE StdIn; + HANDLE StdOut; + HANDLE StdErr; + LPSTR CmdLine; + LPSTR AppName; + LPSTR PifFile; + LPSTR CurDirectory; + LPSTR Env; + ULONG EnvLen; + STARTUPINFOA StartupInfo; + LPSTR Desktop; + ULONG DesktopLen; + LPSTR Title; + ULONG TitleLen; + LPVOID Reserved; + ULONG ReservedLen; + USHORT CmdLen; + USHORT AppLen; + USHORT PifLen; + USHORT CurDirectoryLen; + USHORT VDMState; + USHORT CurrentDrive; + BOOLEAN ComingFromBat; +} VDM_COMMAND_INFO, *PVDM_COMMAND_INFO; + + +/* FUNCTION PROTOTYPES ********************************************************/ + +BOOL +WINAPI +GetNextVDMCommand( + IN OUT PVDM_COMMAND_INFO CommandData OPTIONAL +); + +VOID +WINAPI +ExitVDM( + IN BOOL IsWow, + IN ULONG iWowTask +); + +#endif // _VDM_H + +/* EOF */ diff --git a/reactos/lib/CMakeLists.txt b/reactos/lib/CMakeLists.txt index 8918d67109467..a030ad192e9ef 100644 --- a/reactos/lib/CMakeLists.txt +++ b/reactos/lib/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(cryptlib) #add_subdirectory(dnslib) Nothing links to this lib. add_subdirectory(drivers) add_subdirectory(epsapi) +add_subdirectory(fast486) add_subdirectory(fslib) add_subdirectory(lsalib) add_subdirectory(ppcmmu) diff --git a/reactos/lib/fast486/CMakeLists.txt b/reactos/lib/fast486/CMakeLists.txt new file mode 100644 index 0000000000000..2fbdb31586fc4 --- /dev/null +++ b/reactos/lib/fast486/CMakeLists.txt @@ -0,0 +1,12 @@ + +include_directories(${REACTOS_SOURCE_DIR}/include/reactos/libs/fast486) + +list(APPEND SOURCE + fast486.c + opcodes.c + opgroups.c + extraops.c + common.c + fpu.c) + +add_library(fast486 ${SOURCE}) diff --git a/reactos/lib/fast486/COPYING b/reactos/lib/fast486/COPYING new file mode 100644 index 0000000000000..d159169d10508 --- /dev/null +++ b/reactos/lib/fast486/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/reactos/lib/fast486/common.c b/reactos/lib/fast486/common.c new file mode 100644 index 0000000000000..36f026ec33176 --- /dev/null +++ b/reactos/lib/fast486/common.c @@ -0,0 +1,356 @@ +/* + * Fast486 386/486 CPU Emulation Library + * common.c + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* INCLUDES *******************************************************************/ + +#include + +// #define NDEBUG +#include + +#include +#include "common.h" + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN +Fast486ReadMemory(PFAST486_STATE State, + FAST486_SEG_REGS SegmentReg, + ULONG Offset, + BOOLEAN InstFetch, + PVOID Buffer, + ULONG Size) +{ + ULONG LinearAddress; + PFAST486_SEG_REG CachedDescriptor; + + ASSERT(SegmentReg < FAST486_NUM_SEG_REGS); + + /* Get the cached descriptor */ + CachedDescriptor = &State->SegmentRegs[SegmentReg]; + + if ((Offset + Size - 1) > CachedDescriptor->Limit) + { + /* Read beyond limit */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Check for protected mode */ + if (State->ControlRegisters[0] & FAST486_CR0_PE) + { + /* Privilege checks */ + + if (!CachedDescriptor->Present) + { + Fast486Exception(State, FAST486_EXCEPTION_NP); + return FALSE; + } + + if ((!InstFetch && (GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)) + || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl)) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (InstFetch) + { + if (!CachedDescriptor->Executable) + { + /* Data segment not executable */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + } + else + { + if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite)) + { + /* Code segment not readable */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + } + } + + /* Find the linear address */ + LinearAddress = CachedDescriptor->Base + Offset; + + /* Read from the linear address */ + return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size); +} + +BOOLEAN +Fast486WriteMemory(PFAST486_STATE State, + FAST486_SEG_REGS SegmentReg, + ULONG Offset, + PVOID Buffer, + ULONG Size) +{ + ULONG LinearAddress; + PFAST486_SEG_REG CachedDescriptor; + + ASSERT(SegmentReg < FAST486_NUM_SEG_REGS); + + /* Get the cached descriptor */ + CachedDescriptor = &State->SegmentRegs[SegmentReg]; + + if ((Offset + Size - 1) > CachedDescriptor->Limit) + { + /* Write beyond limit */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Check for protected mode */ + if (State->ControlRegisters[0] & FAST486_CR0_PE) + { + /* Privilege checks */ + + if (!CachedDescriptor->Present) + { + Fast486Exception(State, FAST486_EXCEPTION_NP); + return FALSE; + } + + if ((GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl) + || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl)) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (CachedDescriptor->Executable) + { + /* Code segment not writable */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + else if (!CachedDescriptor->ReadWrite) + { + /* Data segment not writeable */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + } + + /* Find the linear address */ + LinearAddress = CachedDescriptor->Base + Offset; + + /* Write to the linear address */ + return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size); +} + +BOOLEAN +Fast486InterruptInternal(PFAST486_STATE State, + USHORT SegmentSelector, + ULONG Offset, + BOOLEAN InterruptGate) +{ + /* Check for protected mode */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + { + FAST486_TSS Tss; + USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector; + ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long; + + /* Check if the interrupt handler is more privileged */ + if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector)) + { + /* Read the TSS */ + if (!Fast486ReadLinearMemory(State, + State->TaskReg.Base, + &Tss, + sizeof(Tss))) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the new (higher) privilege level */ + switch (GET_SEGMENT_RPL(SegmentSelector)) + { + case 0: + { + if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0)) + { + /* Exception occurred */ + return FALSE; + } + State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0; + + break; + } + + case 1: + { + if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1)) + { + /* Exception occurred */ + return FALSE; + } + State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1; + + break; + } + + case 2: + { + if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2)) + { + /* Exception occurred */ + return FALSE; + } + State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2; + + break; + } + + default: + { + /* Should never reach here! */ + ASSERT(FALSE); + } + } + + /* Push SS selector */ + if (!Fast486StackPush(State, OldSs)) return FALSE; + + /* Push stack pointer */ + if (!Fast486StackPush(State, OldEsp)) return FALSE; + } + } + else + { + if (State->SegmentRegs[FAST486_REG_CS].Size) + { + /* Set OPSIZE, because INT always pushes 16-bit values in real mode */ + State->PrefixFlags |= FAST486_PREFIX_OPSIZE; + } + } + + /* Push EFLAGS */ + if (!Fast486StackPush(State, State->Flags.Long)) return FALSE; + + /* Push CS selector */ + if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) return FALSE; + + /* Push the instruction pointer */ + if (!Fast486StackPush(State, State->InstPtr.Long)) return FALSE; + + if (InterruptGate) + { + /* Disable interrupts after a jump to an interrupt gate handler */ + State->Flags.If = FALSE; + } + + /* Load new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector)) + { + /* An exception occurred during the jump */ + return FALSE; + } + + if (State->SegmentRegs[FAST486_REG_CS].Size) + { + /* 32-bit code segment, use EIP */ + State->InstPtr.Long = Offset; + } + else + { + /* 16-bit code segment, use IP */ + State->InstPtr.LowWord = LOWORD(Offset); + } + + return TRUE; +} + +VOID +FASTCALL +Fast486ExceptionWithErrorCode(PFAST486_STATE State, + FAST486_EXCEPTIONS ExceptionCode, + ULONG ErrorCode) +{ + FAST486_IDT_ENTRY IdtEntry; + + /* Increment the exception count */ + State->ExceptionCount++; + + /* Check if the exception occurred more than once */ + if (State->ExceptionCount > 1) + { + /* Then this is a double fault */ + ExceptionCode = FAST486_EXCEPTION_DF; + } + + /* Check if this is a triple fault */ + if (State->ExceptionCount == 3) + { + /* Reset the CPU */ + Fast486Reset(State); + return; + } + + /* Restore the IP to the saved IP */ + State->InstPtr = State->SavedInstPtr; + + if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry)) + { + /* + * If this function failed, that means Fast486Exception + * was called again, so just return in this case. + */ + return; + } + + /* Perform the interrupt */ + if (!Fast486InterruptInternal(State, + IdtEntry.Selector, + MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh), + IdtEntry.Type)) + { + /* + * If this function failed, that means Fast486Exception + * was called again, so just return in this case. + */ + return; + } + + if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode) + && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)) + { + /* Push the error code */ + if (!Fast486StackPush(State, ErrorCode)) + { + /* + * If this function failed, that means Fast486Exception + * was called again, so just return in this case. + */ + return; + } + } + + /* Reset the exception count */ + State->ExceptionCount = 0; +} + +/* EOF */ diff --git a/reactos/lib/fast486/common.h b/reactos/lib/fast486/common.h new file mode 100644 index 0000000000000..1bfdb311ef222 --- /dev/null +++ b/reactos/lib/fast486/common.h @@ -0,0 +1,180 @@ +/* + * Fast486 386/486 CPU Emulation Library + * common.h + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#pragma once + +/* DEFINES ********************************************************************/ + +#define SIGN_FLAG_BYTE 0x80 +#define SIGN_FLAG_WORD 0x8000 +#define SIGN_FLAG_LONG 0x80000000 +#define REAL_MODE_FLAGS_MASK 0x57FD5 +#define PROT_MODE_FLAGS_MASK 0x50DD5 + +/* Block size for string operations */ +#define STRING_BLOCK_SIZE 4096 + +#define GET_SEGMENT_RPL(s) ((s) & 3) +#define GET_SEGMENT_INDEX(s) ((s) & 0xFFF8) +#define SEGMENT_TABLE_INDICATOR (1 << 2) +#define EXCEPTION_HAS_ERROR_CODE(x) (((x) == 8) || ((x) >= 10 && (x) <= 14)) + +#define NO_LOCK_PREFIX()\ +if (State->PrefixFlags & FAST486_PREFIX_LOCK)\ +{\ + Fast486Exception(State, FAST486_EXCEPTION_UD);\ + return FALSE;\ +} + +#define TOGGLE_OPSIZE(x)\ + if (State->PrefixFlags & FAST486_PREFIX_OPSIZE) x = !x; + +#define TOGGLE_ADSIZE(x)\ + if (State->PrefixFlags & FAST486_PREFIX_ADSIZE) x = !x; + +#define SWAP(x, y) { (x) ^= (y); (y) ^= (x); (x) ^= (y); } + +#define ALIGNMENT_CHECK(x, a) if (State->Flags.Ac \ + && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_AM)\ + && (State->Cpl == 3)\ + && (((x) % (a)) != 0))\ +{\ + Fast486Exception(State, FAST486_EXCEPTION_AC);\ + return FALSE;\ +} + +#define PAGE_ALIGN(x) ((x) & 0xFFFFF000) +#define PAGE_OFFSET(x) ((x) & 0x00000FFF) +#define GET_ADDR_PDE(x) ((x) >> 22) +#define GET_ADDR_PTE(x) (((x) >> 12) & 0x3FF) +#define INVALID_TLB_FIELD 0xFFFFFFFF + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +typedef struct _FAST486_MOD_REG_RM +{ + FAST486_GEN_REGS Register; + BOOLEAN Memory; + union + { + FAST486_GEN_REGS SecondRegister; + ULONG MemoryAddress; + }; +} FAST486_MOD_REG_RM, *PFAST486_MOD_REG_RM; + +#pragma pack(push, 1) + +typedef union _FAST486_PAGE_DIR +{ + struct + { + ULONG Present : 1; + ULONG Writeable : 1; + ULONG Usermode : 1; + ULONG WriteThrough : 1; + ULONG NoCache : 1; + ULONG Accessed : 1; + ULONG AlwaysZero : 1; + ULONG Size : 1; + ULONG Unused : 4; + ULONG TableAddress : 20; + }; + ULONG Value; +} FAST486_PAGE_DIR, *PFAST486_PAGE_DIR; + +C_ASSERT(sizeof(FAST486_PAGE_DIR) == sizeof(ULONG)); + +typedef union _FAST486_PAGE_TABLE +{ + struct + { + ULONG Present : 1; + ULONG Writeable : 1; + ULONG Usermode : 1; + ULONG WriteThrough : 1; + ULONG NoCache : 1; + ULONG Accessed : 1; + ULONG Dirty : 1; + ULONG AlwaysZero : 1; + ULONG Global : 1; + ULONG Unused : 3; + ULONG Address : 20; + }; + ULONG Value; +} FAST486_PAGE_TABLE, *PFAST486_PAGE_TABLE; + +C_ASSERT(sizeof(FAST486_PAGE_DIR) == sizeof(ULONG)); + +#pragma pack(pop) + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN +Fast486ReadMemory +( + PFAST486_STATE State, + FAST486_SEG_REGS SegmentReg, + ULONG Offset, + BOOLEAN InstFetch, + PVOID Buffer, + ULONG Size +); + +BOOLEAN +Fast486WriteMemory +( + PFAST486_STATE State, + FAST486_SEG_REGS SegmentReg, + ULONG Offset, + PVOID Buffer, + ULONG Size +); + +BOOLEAN +Fast486InterruptInternal +( + PFAST486_STATE State, + USHORT SegmentSelector, + ULONG Offset, + BOOLEAN InterruptGate +); + +VOID +FASTCALL +Fast486ExceptionWithErrorCode +( + PFAST486_STATE State, + FAST486_EXCEPTIONS ExceptionCode, + ULONG ErrorCode +); + +/* INLINED FUNCTIONS **********************************************************/ + +#include "common.inl" + +#endif // _COMMON_H_ + +/* EOF */ diff --git a/reactos/lib/fast486/common.inl b/reactos/lib/fast486/common.inl new file mode 100644 index 0000000000000..fff8fc01474a5 --- /dev/null +++ b/reactos/lib/fast486/common.inl @@ -0,0 +1,1324 @@ +/* + * Fast486 386/486 CPU Emulation Library + * common.inl + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "common.h" + +/* PUBLIC FUNCTIONS ***********************************************************/ + +FORCEINLINE +INT +Fast486GetCurrentPrivLevel(PFAST486_STATE State) +{ + /* Return the CPL, or 3 if we're in virtual 8086 mode */ + return (!State->Flags.Vm) ? State->Cpl : 3; +} + +FORCEINLINE +ULONG +Fast486GetPageTableEntry(PFAST486_STATE State, + ULONG VirtualAddress, + BOOLEAN MarkAsDirty) +{ + ULONG PdeIndex = GET_ADDR_PDE(VirtualAddress); + ULONG PteIndex = GET_ADDR_PTE(VirtualAddress); + FAST486_PAGE_DIR DirectoryEntry; + FAST486_PAGE_TABLE TableEntry; + ULONG PageDirectory = State->ControlRegisters[FAST486_REG_CR3]; + + if ((State->Tlb != NULL) + && (State->Tlb[VirtualAddress >> 12] != INVALID_TLB_FIELD)) + { + /* Return the cached entry */ + return State->Tlb[VirtualAddress >> 12]; + } + + /* Read the directory entry */ + State->MemReadCallback(State, + PageDirectory + PdeIndex * sizeof(ULONG), + &DirectoryEntry.Value, + sizeof(DirectoryEntry)); + + /* Make sure it is present */ + if (!DirectoryEntry.Present) return 0; + + /* Was the directory entry accessed before? */ + if (!DirectoryEntry.Accessed) + { + /* Well, it is now */ + DirectoryEntry.Accessed = TRUE; + + /* Write back the directory entry */ + State->MemWriteCallback(State, + PageDirectory + PdeIndex * sizeof(ULONG), + &DirectoryEntry.Value, + sizeof(DirectoryEntry)); + } + + /* Read the table entry */ + State->MemReadCallback(State, + (DirectoryEntry.TableAddress << 12) + + PteIndex * sizeof(ULONG), + &TableEntry.Value, + sizeof(TableEntry)); + + /* Make sure it is present */ + if (!TableEntry.Present) return 0; + + if (MarkAsDirty) TableEntry.Dirty = TRUE; + + /* Was the table entry accessed before? */ + if (!TableEntry.Accessed) + { + /* Well, it is now */ + TableEntry.Accessed = TRUE; + + /* Write back the table entry */ + State->MemWriteCallback(State, + (DirectoryEntry.TableAddress << 12) + + PteIndex * sizeof(ULONG), + &TableEntry.Value, + sizeof(TableEntry)); + } + + /* + * The resulting permissions depend on the permissions + * in the page directory table too + */ + TableEntry.Writeable &= DirectoryEntry.Writeable; + TableEntry.Usermode &= DirectoryEntry.Usermode; + + if (State->Tlb != NULL) + { + /* Set the TLB entry */ + State->Tlb[VirtualAddress >> 12] = TableEntry.Value; + } + + /* Return the table entry */ + return TableEntry.Value; +} + +FORCEINLINE +BOOLEAN +Fast486ReadLinearMemory(PFAST486_STATE State, + ULONG LinearAddress, + PVOID Buffer, + ULONG Size) +{ + /* Check if paging is enabled */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG) + { + ULONG Page; + FAST486_PAGE_TABLE TableEntry; + INT Cpl = Fast486GetCurrentPrivLevel(State); + ULONG BufferOffset = 0; + + for (Page = PAGE_ALIGN(LinearAddress); + Page <= PAGE_ALIGN(LinearAddress + Size - 1); + Page += PAGE_SIZE) + { + ULONG PageOffset = 0, PageLength = PAGE_SIZE; + + /* Get the table entry */ + TableEntry.Value = Fast486GetPageTableEntry(State, Page, FALSE); + + if (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0))) + { + /* Exception */ + Fast486ExceptionWithErrorCode(State, + FAST486_EXCEPTION_PF, + TableEntry.Value & 0x07); + return FALSE; + } + + /* Check if this is the first page */ + if (Page == PAGE_ALIGN(LinearAddress)) + { + /* Start reading from the offset from the beginning of the page */ + PageOffset = PAGE_OFFSET(LinearAddress); + PageLength -= PageOffset; + } + + /* Check if this is the last page */ + if (Page == PAGE_ALIGN(LinearAddress + Size - 1)) + { + /* Read only a part of the page */ + PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1; + } + + /* Read the memory */ + State->MemReadCallback(State, + (TableEntry.Address << 12) | PageOffset, + (PVOID)((ULONG_PTR)Buffer + BufferOffset), + PageLength); + + BufferOffset += PageLength; + } + } + else + { + /* Read the memory */ + State->MemReadCallback(State, LinearAddress, Buffer, Size); + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486WriteLinearMemory(PFAST486_STATE State, + ULONG LinearAddress, + PVOID Buffer, + ULONG Size) +{ + /* Check if paging is enabled */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG) + { + ULONG Page; + FAST486_PAGE_TABLE TableEntry; + INT Cpl = Fast486GetCurrentPrivLevel(State); + ULONG BufferOffset = 0; + + for (Page = PAGE_ALIGN(LinearAddress); + Page <= PAGE_ALIGN(LinearAddress + Size - 1); + Page += PAGE_SIZE) + { + ULONG PageOffset = 0, PageLength = PAGE_SIZE; + + /* Get the table entry */ + TableEntry.Value = Fast486GetPageTableEntry(State, Page, TRUE); + + if ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0))) + || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP) + && !TableEntry.Writeable)) + { + /* Exception */ + Fast486ExceptionWithErrorCode(State, + FAST486_EXCEPTION_PF, + TableEntry.Value & 0x07); + return FALSE; + } + + /* Check if this is the first page */ + if (Page == PAGE_ALIGN(LinearAddress)) + { + /* Start writing from the offset from the beginning of the page */ + PageOffset = PAGE_OFFSET(LinearAddress); + PageLength -= PageOffset; + } + + /* Check if this is the last page */ + if (Page == PAGE_ALIGN(LinearAddress + Size - 1)) + { + /* Write only a part of the page */ + PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1; + } + + /* Write the memory */ + State->MemWriteCallback(State, + (TableEntry.Address << 12) | PageOffset, + (PVOID)((ULONG_PTR)Buffer + BufferOffset), + PageLength); + + BufferOffset += PageLength; + } + } + else + { + /* Write the memory */ + State->MemWriteCallback(State, LinearAddress, Buffer, Size); + } + + return TRUE; +} + +FORCEINLINE +VOID +Fast486Exception(PFAST486_STATE State, + FAST486_EXCEPTIONS ExceptionCode) +{ + /* Call the internal function */ + Fast486ExceptionWithErrorCode(State, ExceptionCode, 0); +} + +FORCEINLINE +BOOLEAN +Fast486StackPush(PFAST486_STATE State, + ULONG Value) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* The OPSIZE prefix toggles the size */ + if (State->PrefixFlags & FAST486_PREFIX_OPSIZE) Size = !Size; + + if (Size) + { + /* 32-bit size */ + + /* Check if ESP is between 1 and 3 */ + if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1 + && State->GeneralRegs[FAST486_REG_ESP].Long <= 3) + { + Fast486Exception(State, FAST486_EXCEPTION_SS); + return FALSE; + } + + /* Subtract ESP by 4 */ + State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG); + + /* Store the value in SS:ESP */ + return Fast486WriteMemory(State, + FAST486_REG_SS, + State->GeneralRegs[FAST486_REG_ESP].Long, + &Value, + sizeof(ULONG)); + } + else + { + /* 16-bit size */ + USHORT ShortValue = LOWORD(Value); + + /* Check if SP is 1 */ + if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 1) + { + Fast486Exception(State, FAST486_EXCEPTION_SS); + return FALSE; + } + + /* Subtract SP by 2 */ + State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT); + + /* Store the value in SS:SP */ + return Fast486WriteMemory(State, + FAST486_REG_SS, + State->GeneralRegs[FAST486_REG_ESP].LowWord, + &ShortValue, + sizeof(USHORT)); + } +} + +FORCEINLINE +BOOLEAN +Fast486StackPop(PFAST486_STATE State, + PULONG Value) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* The OPSIZE prefix toggles the size */ + TOGGLE_OPSIZE(Size); + + if (Size) + { + /* 32-bit size */ + ULONG LongValue; + + /* Check if ESP is 0xFFFFFFFF */ + if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF) + { + Fast486Exception(State, FAST486_EXCEPTION_SS); + return FALSE; + } + + /* Read the value from SS:ESP */ + if (!Fast486ReadMemory(State, + FAST486_REG_SS, + State->GeneralRegs[FAST486_REG_ESP].Long, + FALSE, + &LongValue, + sizeof(LongValue))) + { + /* An exception occurred */ + return FALSE; + } + + /* Increment ESP by 4 */ + State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(ULONG); + + /* Store the value in the result */ + *Value = LongValue; + } + else + { + /* 16-bit size */ + USHORT ShortValue; + + /* Check if SP is 0xFFFF */ + if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF) + { + Fast486Exception(State, FAST486_EXCEPTION_SS); + return FALSE; + } + + /* Read the value from SS:SP */ + if (!Fast486ReadMemory(State, + FAST486_REG_SS, + State->GeneralRegs[FAST486_REG_ESP].LowWord, + FALSE, + &ShortValue, + sizeof(ShortValue))) + { + /* An exception occurred */ + return FALSE; + } + + /* Increment SP by 2 */ + State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(USHORT); + + /* Store the value in the result */ + *Value = ShortValue; + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486LoadSegment(PFAST486_STATE State, + FAST486_SEG_REGS Segment, + USHORT Selector) +{ + PFAST486_SEG_REG CachedDescriptor; + FAST486_GDT_ENTRY GdtEntry; + + ASSERT(Segment < FAST486_NUM_SEG_REGS); + + /* Get the cached descriptor */ + CachedDescriptor = &State->SegmentRegs[Segment]; + + /* Check for protected mode */ + if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm) + { + if (!(Selector & SEGMENT_TABLE_INDICATOR)) + { + /* Make sure the GDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + /* Read the GDT */ + if (!Fast486ReadLinearMemory(State, + State->Gdtr.Address + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Make sure the LDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + /* Read the LDT */ + if (!Fast486ReadLinearMemory(State, + State->Ldtr.Base + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + + if (Segment == FAST486_REG_SS) + { + /* Loading the stack segment */ + + if (GET_SEGMENT_INDEX(Selector) == 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!GdtEntry.SystemType) + { + /* This is a special descriptor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + if (GdtEntry.Executable || !GdtEntry.ReadWrite) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State)) + || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + if (!GdtEntry.Present) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_SS, Selector); + return FALSE; + } + } + else if (Segment == FAST486_REG_CS) + { + /* Loading the code segment */ + + if (GET_SEGMENT_INDEX(Selector) == 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!GdtEntry.SystemType) + { + // TODO: Call/interrupt/task gates NOT IMPLEMENTED! + UNIMPLEMENTED; + } + else + { + if (!GdtEntry.Present) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); + return FALSE; + } + + if (!GdtEntry.Executable) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + if (GdtEntry.DirConf) + { + /* Conforming Code Segment */ + + if (GdtEntry.Dpl > Fast486GetCurrentPrivLevel(State)) + { + /* Must be accessed from lower-privileged code */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + } + else + { + /* Regular code segment */ + + if ((GET_SEGMENT_RPL(Selector) > Fast486GetCurrentPrivLevel(State)) + || (Fast486GetCurrentPrivLevel(State) != GdtEntry.Dpl)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + } + + /* Update CPL */ + State->Cpl = GET_SEGMENT_RPL(Selector); + } + } + else + { + /* Loading a data segment */ + + if (!GdtEntry.SystemType) + { + /* This is a special descriptor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl) + || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + if (!GdtEntry.Present) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); + return FALSE; + } + } + + /* Update the cache entry */ + CachedDescriptor->Selector = Selector; + CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24); + CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16); + CachedDescriptor->Accessed = GdtEntry.Accessed; + CachedDescriptor->ReadWrite = GdtEntry.ReadWrite; + CachedDescriptor->DirConf = GdtEntry.DirConf; + CachedDescriptor->Executable = GdtEntry.Executable; + CachedDescriptor->SystemType = GdtEntry.SystemType; + CachedDescriptor->Dpl = GdtEntry.Dpl; + CachedDescriptor->Present = GdtEntry.Present; + CachedDescriptor->Size = GdtEntry.Size; + + /* Check for page granularity */ + if (GdtEntry.Granularity) CachedDescriptor->Limit <<= 12; + } + else + { + /* Update the selector and base */ + CachedDescriptor->Selector = Selector; + CachedDescriptor->Base = Selector << 4; + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486FetchByte(PFAST486_STATE State, + PUCHAR Data) +{ + PFAST486_SEG_REG CachedDescriptor; + + /* Get the cached descriptor of CS */ + CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS]; + + /* Read from memory */ + if (!Fast486ReadMemory(State, + FAST486_REG_CS, + (CachedDescriptor->Size) ? State->InstPtr.Long + : State->InstPtr.LowWord, + TRUE, + Data, + sizeof(UCHAR))) + { + /* Exception occurred during instruction fetch */ + return FALSE; + } + + /* Advance the instruction pointer */ + if (CachedDescriptor->Size) State->InstPtr.Long++; + else State->InstPtr.LowWord++; + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486FetchWord(PFAST486_STATE State, + PUSHORT Data) +{ + PFAST486_SEG_REG CachedDescriptor; + + /* Get the cached descriptor of CS */ + CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS]; + + /* Read from memory */ + // FIXME: Fix byte order on big-endian machines + if (!Fast486ReadMemory(State, + FAST486_REG_CS, + (CachedDescriptor->Size) ? State->InstPtr.Long + : State->InstPtr.LowWord, + TRUE, + Data, + sizeof(USHORT))) + { + /* Exception occurred during instruction fetch */ + return FALSE; + } + + /* Advance the instruction pointer */ + if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT); + else State->InstPtr.LowWord += sizeof(USHORT); + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486FetchDword(PFAST486_STATE State, + PULONG Data) +{ + PFAST486_SEG_REG CachedDescriptor; + + /* Get the cached descriptor of CS */ + CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS]; + + /* Read from memory */ + // FIXME: Fix byte order on big-endian machines + if (!Fast486ReadMemory(State, + FAST486_REG_CS, + (CachedDescriptor->Size) ? State->InstPtr.Long + : State->InstPtr.LowWord, + TRUE, + Data, + sizeof(ULONG))) + { + /* Exception occurred during instruction fetch */ + return FALSE; + } + + /* Advance the instruction pointer */ + if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG); + else State->InstPtr.LowWord += sizeof(ULONG); + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486GetIntVector(PFAST486_STATE State, + UCHAR Number, + PFAST486_IDT_ENTRY IdtEntry) +{ + ULONG FarPointer; + + /* Check for protected mode */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + { + /* Read from the IDT */ + if (!Fast486ReadLinearMemory(State, + State->Idtr.Address + + Number * sizeof(*IdtEntry), + IdtEntry, + sizeof(*IdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Read from the real-mode IVT */ + + /* Paging is always disabled in real mode */ + State->MemReadCallback(State, + State->Idtr.Address + + Number * sizeof(FarPointer), + &FarPointer, + sizeof(FarPointer)); + + /* Fill a fake IDT entry */ + IdtEntry->Offset = LOWORD(FarPointer); + IdtEntry->Selector = HIWORD(FarPointer); + IdtEntry->Zero = 0; + IdtEntry->Type = FAST486_IDT_INT_GATE; + IdtEntry->Storage = FALSE; + IdtEntry->Dpl = 0; + IdtEntry->Present = TRUE; + IdtEntry->OffsetHigh = 0; + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486CalculateParity(UCHAR Number) +{ + // See http://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable too... + return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1; +} + +FORCEINLINE +BOOLEAN +Fast486ParseModRegRm(PFAST486_STATE State, + BOOLEAN AddressSize, + PFAST486_MOD_REG_RM ModRegRm) +{ + UCHAR ModRmByte, Mode, RegMem; + + /* Fetch the MOD REG R/M byte */ + if (!Fast486FetchByte(State, &ModRmByte)) + { + /* Exception occurred */ + return FALSE; + } + + /* Unpack the mode and R/M */ + Mode = ModRmByte >> 6; + RegMem = ModRmByte & 0x07; + + /* Set the register operand */ + ModRegRm->Register = (ModRmByte >> 3) & 0x07; + + /* Check the mode */ + if ((ModRmByte >> 6) == 3) + { + /* The second operand is also a register */ + ModRegRm->Memory = FALSE; + ModRegRm->SecondRegister = RegMem; + + /* Done parsing */ + return TRUE; + } + + /* The second operand is memory */ + ModRegRm->Memory = TRUE; + + if (AddressSize) + { + if (RegMem == FAST486_REG_ESP) + { + UCHAR SibByte; + ULONG Scale, Index, Base; + + /* Fetch the SIB byte */ + if (!Fast486FetchByte(State, &SibByte)) + { + /* Exception occurred */ + return FALSE; + } + + /* Unpack the scale, index and base */ + Scale = 1 << (SibByte >> 6); + Index = (SibByte >> 3) & 0x07; + if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long; + else Index = 0; + + if (((SibByte & 0x07) != FAST486_REG_EBP) || (Mode != 0)) + { + /* Use the register a base */ + Base = State->GeneralRegs[SibByte & 0x07].Long; + } + else + { + /* Fetch the base */ + if (!Fast486FetchDword(State, &Base)) + { + /* Exception occurred */ + return FALSE; + } + } + + if ((SibByte & 0x07) == FAST486_REG_ESP) + { + /* Check if there is no segment override */ + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + /* Add a SS: prefix */ + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_SS; + } + } + + /* Calculate the address */ + ModRegRm->MemoryAddress = Base + Index * Scale; + } + else if (RegMem == FAST486_REG_EBP) + { + if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long; + else ModRegRm->MemoryAddress = 0; + } + else + { + /* Get the base from the register */ + ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long; + } + + /* Check if there is no segment override */ + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + /* Check if the default segment should be SS */ + if ((RegMem == FAST486_REG_EBP) && Mode) + { + /* Add a SS: prefix */ + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_SS; + } + } + + if (Mode == 1) + { + CHAR Offset; + + /* Fetch the byte */ + if (!Fast486FetchByte(State, (PUCHAR)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Add the signed offset to the address */ + ModRegRm->MemoryAddress += (LONG)Offset; + } + else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP))) + { + LONG Offset; + + /* Fetch the dword */ + if (!Fast486FetchDword(State, (PULONG)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Add the signed offset to the address */ + ModRegRm->MemoryAddress += Offset; + } + } + else + { + /* Check the operand */ + switch (RegMem) + { + case 0: + { + /* [BX + SI] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord + + State->GeneralRegs[FAST486_REG_ESI].LowWord; + + break; + } + + case 1: + { + /* [BX + DI] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord + + State->GeneralRegs[FAST486_REG_EDI].LowWord; + + break; + } + + case 2: + { + /* SS:[BP + SI] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord + + State->GeneralRegs[FAST486_REG_ESI].LowWord; + + break; + } + + case 3: + { + /* SS:[BP + DI] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord + + State->GeneralRegs[FAST486_REG_EDI].LowWord; + + break; + } + + case 4: + { + /* [SI] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord; + + break; + } + + case 5: + { + /* [DI] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord; + + break; + } + + case 6: + { + if (Mode) + { + /* [BP] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord; + } + else + { + /* [constant] (added later) */ + ModRegRm->MemoryAddress = 0; + } + + break; + } + + case 7: + { + /* [BX] */ + ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord; + + break; + } + } + + /* Check if there is no segment override */ + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + /* Check if the default segment should be SS */ + if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode)) + { + /* Add a SS: prefix */ + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_SS; + } + } + + if (Mode == 1) + { + CHAR Offset; + + /* Fetch the byte */ + if (!Fast486FetchByte(State, (PUCHAR)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Add the signed offset to the address */ + ModRegRm->MemoryAddress += (LONG)Offset; + } + else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6))) + { + SHORT Offset; + + /* Fetch the word */ + if (!Fast486FetchWord(State, (PUSHORT)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Add the signed offset to the address */ + ModRegRm->MemoryAddress += (LONG)Offset; + } + + /* Clear the top 16 bits */ + ModRegRm->MemoryAddress &= 0x0000FFFF; + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486ReadModrmByteOperands(PFAST486_STATE State, + PFAST486_MOD_REG_RM ModRegRm, + PUCHAR RegValue, + PUCHAR RmValue) +{ + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + if (RegValue) + { + /* Get the register value */ + if (ModRegRm->Register & 0x04) + { + /* AH, CH, DH, BH */ + *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte; + } + else + { + /* AL, CL, DL, BL */ + *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte; + } + } + + if (RmValue) + { + if (!ModRegRm->Memory) + { + /* Get the second register value */ + if (ModRegRm->SecondRegister & 0x04) + { + /* AH, CH, DH, BH */ + *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte; + } + else + { + /* AL, CL, DL, BL */ + *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte; + } + } + else + { + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read memory */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm->MemoryAddress, + FALSE, + RmValue, + sizeof(UCHAR))) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486ReadModrmWordOperands(PFAST486_STATE State, + PFAST486_MOD_REG_RM ModRegRm, + PUSHORT RegValue, + PUSHORT RmValue) +{ + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + if (RegValue) + { + /* Get the register value */ + *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord; + } + + if (RmValue) + { + if (!ModRegRm->Memory) + { + /* Get the second register value */ + *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord; + } + else + { + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read memory */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm->MemoryAddress, + FALSE, + RmValue, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486ReadModrmDwordOperands(PFAST486_STATE State, + PFAST486_MOD_REG_RM ModRegRm, + PULONG RegValue, + PULONG RmValue) +{ + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + if (RegValue) + { + /* Get the register value */ + *RegValue = State->GeneralRegs[ModRegRm->Register].Long; + } + + if (RmValue) + { + if (!ModRegRm->Memory) + { + /* Get the second register value */ + *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long; + } + else + { + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read memory */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm->MemoryAddress, + FALSE, + RmValue, + sizeof(ULONG))) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486WriteModrmByteOperands(PFAST486_STATE State, + PFAST486_MOD_REG_RM ModRegRm, + BOOLEAN WriteRegister, + UCHAR Value) +{ + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + if (WriteRegister) + { + /* Store the value in the register */ + if (ModRegRm->Register & 0x04) + { + /* AH, CH, DH, BH */ + State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value; + } + else + { + /* AL, CL, DL, BL */ + State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value; + } + } + else + { + if (!ModRegRm->Memory) + { + /* Store the value in the second register */ + if (ModRegRm->SecondRegister & 0x04) + { + /* AH, CH, DH, BH */ + State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value; + } + else + { + /* AL, CL, DL, BL */ + State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value; + } + } + else + { + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Write memory */ + if (!Fast486WriteMemory(State, + Segment, + ModRegRm->MemoryAddress, + &Value, + sizeof(UCHAR))) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486WriteModrmWordOperands(PFAST486_STATE State, + PFAST486_MOD_REG_RM ModRegRm, + BOOLEAN WriteRegister, + USHORT Value) +{ + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + if (WriteRegister) + { + /* Store the value in the register */ + State->GeneralRegs[ModRegRm->Register].LowWord = Value; + } + else + { + if (!ModRegRm->Memory) + { + /* Store the value in the second register */ + State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value; + } + else + { + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Write memory */ + if (!Fast486WriteMemory(State, + Segment, + ModRegRm->MemoryAddress, + &Value, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +FORCEINLINE +BOOLEAN +Fast486WriteModrmDwordOperands(PFAST486_STATE State, + PFAST486_MOD_REG_RM ModRegRm, + BOOLEAN WriteRegister, + ULONG Value) +{ + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + if (WriteRegister) + { + /* Store the value in the register */ + State->GeneralRegs[ModRegRm->Register].Long = Value; + } + else + { + if (!ModRegRm->Memory) + { + /* Store the value in the second register */ + State->GeneralRegs[ModRegRm->SecondRegister].Long = Value; + } + else + { + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Write memory */ + if (!Fast486WriteMemory(State, + Segment, + ModRegRm->MemoryAddress, + &Value, + sizeof(ULONG))) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +/* EOF */ diff --git a/reactos/lib/fast486/extraops.c b/reactos/lib/fast486/extraops.c new file mode 100644 index 0000000000000..f9fe386a63890 --- /dev/null +++ b/reactos/lib/fast486/extraops.c @@ -0,0 +1,2387 @@ +/* + * Fast486 386/486 CPU Emulation Library + * extraops.c + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* INCLUDES *******************************************************************/ + +#include + +// #define NDEBUG +#include + +#include +#include "opcodes.h" +#include "common.h" +#include "opgroups.h" +#include "extraops.h" + +/* PUBLIC VARIABLES ***********************************************************/ + +FAST486_OPCODE_HANDLER_PROC +Fast486ExtendedHandlers[FAST486_NUM_OPCODE_HANDLERS] = +{ + Fast486OpcodeGroup0F00, + Fast486OpcodeGroup0F01, + Fast486ExtOpcodeLar, + Fast486ExtOpcodeLsl, + NULL, // Invalid + NULL, // Invalid + Fast486ExtOpcodeClts, + NULL, // Invalid + NULL, // TODO: OPCODE 0x08 NOT IMPLEMENTED + NULL, // TODO: OPCODE 0x09 NOT IMPLEMENTED + NULL, // Invalid + NULL, // Reserved (UD1) + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + Fast486ExtOpcodeStoreControlReg, + Fast486ExtOpcodeStoreDebugReg, + Fast486ExtOpcodeLoadControlReg, + Fast486ExtOpcodeLoadDebugReg, + NULL, // TODO: OPCODE 0x24 NOT IMPLEMENTED + NULL, // Invalid + NULL, // TODO: OPCODE 0x26 NOT IMPLEMENTED + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalJmp, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodeConditionalSet, + Fast486ExtOpcodePushFs, + Fast486ExtOpcodePopFs, + NULL, // Invalid + Fast486ExtOpcodeBitTest, + Fast486ExtOpcodeShld, + Fast486ExtOpcodeShld, + NULL, // Invalid + NULL, // Invalid + Fast486ExtOpcodePushGs, + Fast486ExtOpcodePopGs, + NULL, // Invalid + Fast486ExtOpcodeBts, + Fast486ExtOpcodeShrd, + Fast486ExtOpcodeShrd, + NULL, // Invalid + Fast486ExtOpcodeImul, + Fast486ExtOpcodeCmpXchgByte, + Fast486ExtOpcodeCmpXchg, + Fast486ExtOpcodeLss, + Fast486ExtOpcodeBtr, + Fast486ExtOpcodeLfsLgs, + Fast486ExtOpcodeLfsLgs, + Fast486ExtOpcodeMovzxByte, + Fast486ExtOpcodeMovzxWord, + NULL, // Invalid + Fast486OpcodeGroup0FB9, + Fast486OpcodeGroup0FBA, + Fast486ExtOpcodeBtc, + Fast486ExtOpcodeBsf, + Fast486ExtOpcodeBsr, + Fast486ExtOpcodeMovsxByte, + Fast486ExtOpcodeMovsxWord, + Fast486ExtOpcodeXaddByte, + Fast486ExtOpcodeXadd, + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + Fast486ExtOpcodeBswap, + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid + NULL, // Invalid +}; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLar) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + USHORT Selector; + FAST486_GDT_ENTRY GdtEntry; + DWORD AccessRights; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + /* Not recognized */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + Selector = LOWORD(Value); + } + else + { + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Selector)) + { + /* Exception occurred */ + return FALSE; + } + } + + if (!(Selector & SEGMENT_TABLE_INDICATOR)) + { + /* Check if the GDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1)) + { + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Read the GDT */ + if (!Fast486ReadLinearMemory(State, + State->Gdtr.Address + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Check if the LDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1)) + { + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Read the LDT */ + if (!Fast486ReadLinearMemory(State, + State->Ldtr.Base + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Privilege check */ + if (((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)) + || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl)) + { + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Set ZF */ + State->Flags.Zf = TRUE; + + /* Get the access rights */ + AccessRights = ((PDWORD)&GdtEntry)[1] & 0x00F0FF00; + + /* Return the access rights */ + if (OperandSize) + { + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, TRUE, AccessRights)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, TRUE, LOWORD(AccessRights))) + { + /* Exception occurred */ + return FALSE; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLsl) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + USHORT Selector; + ULONG Limit; + FAST486_GDT_ENTRY GdtEntry; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + /* Not recognized */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + Selector = LOWORD(Value); + } + else + { + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Selector)) + { + /* Exception occurred */ + return FALSE; + } + } + + if (!(Selector & SEGMENT_TABLE_INDICATOR)) + { + /* Check if the GDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1)) + { + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Read the GDT */ + if (!Fast486ReadLinearMemory(State, + State->Gdtr.Address + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Check if the LDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1)) + { + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Read the LDT */ + if (!Fast486ReadLinearMemory(State, + State->Ldtr.Base + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Privilege check */ + if (((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)) + || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl)) + { + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Calculate the limit */ + Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16); + if (GdtEntry.Granularity) Limit <<= 12; + + /* Set ZF */ + State->Flags.Zf = TRUE; + + if (OperandSize) + { + /* Return the limit */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, TRUE, Limit)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Return the limit */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, TRUE, LOWORD(Limit))) + { + /* Exception occurred */ + return FALSE; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeClts) +{ + NO_LOCK_PREFIX(); + + /* The current privilege level must be zero */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Clear the task switch bit */ + State->ControlRegisters[FAST486_REG_CR0] &= ~FAST486_CR0_TS; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeStoreControlReg) +{ + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* The current privilege level must be zero */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if ((ModRegRm.Register == 1) || (ModRegRm.Register > 3)) + { + /* CR1, CR4, CR5, CR6 and CR7 don't exist */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (ModRegRm.Register != 0) + { + /* CR2 and CR3 and are stored in array indexes 1 and 2 */ + ModRegRm.Register--; + } + + /* Store the value of the control register */ + State->GeneralRegs[ModRegRm.SecondRegister].Long = State->ControlRegisters[ModRegRm.Register]; + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeStoreDebugReg) +{ + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* The current privilege level must be zero */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if ((ModRegRm.Register == 6) || (ModRegRm.Register == 7)) + { + /* DR6 and DR7 are aliases to DR4 and DR5 */ + ModRegRm.Register -= 2; + } + + if (State->DebugRegisters[FAST486_REG_DR5] & FAST486_DR5_GD) + { + /* Disallow access to debug registers */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Store the value of the debug register */ + State->GeneralRegs[ModRegRm.SecondRegister].Long = State->DebugRegisters[ModRegRm.Register]; + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLoadControlReg) +{ + ULONG Value; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* The current privilege level must be zero */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if ((ModRegRm.Register == 1) || (ModRegRm.Register > 3)) + { + /* CR1, CR4, CR5, CR6 and CR7 don't exist */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (ModRegRm.Register != 0) + { + /* CR2 and CR3 and are stored in array indexes 1 and 2 */ + ModRegRm.Register--; + } + + /* Get the value */ + Value = State->GeneralRegs[ModRegRm.SecondRegister].Long; + + if (ModRegRm.Register == (INT)FAST486_REG_CR0) + { + /* CR0 checks */ + + if (((Value & (FAST486_CR0_PG | FAST486_CR0_PE)) == FAST486_CR0_PG) + || ((Value & (FAST486_CR0_CD | FAST486_CR0_NW)) == FAST486_CR0_NW)) + { + /* Invalid value */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + } + + /* Load a value to the control register */ + State->ControlRegisters[ModRegRm.Register] = Value; + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLoadDebugReg) +{ + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* The current privilege level must be zero */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if ((ModRegRm.Register == 6) || (ModRegRm.Register == 7)) + { + /* DR6 and DR7 are aliases to DR4 and DR5 */ + ModRegRm.Register -= 2; + } + + if (State->DebugRegisters[FAST486_REG_DR5] & FAST486_DR5_GD) + { + /* Disallow access to debug registers */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Load a value to the debug register */ + State->DebugRegisters[ModRegRm.Register] = State->GeneralRegs[ModRegRm.SecondRegister].Long; + + if (ModRegRm.Register == (INT)FAST486_REG_DR4) + { + /* The reserved bits are 1 */ + State->DebugRegisters[ModRegRm.Register] |= FAST486_DR4_RESERVED; + } + else if (ModRegRm.Register == (INT)FAST486_REG_DR5) + { + /* The reserved bits are 0 */ + State->DebugRegisters[ModRegRm.Register] &= ~FAST486_DR5_RESERVED; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePushFs) +{ + /* Call the internal API */ + return Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePopFs) +{ + ULONG NewSelector; + + if (!Fast486StackPop(State, &NewSelector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486LoadSegment(State, FAST486_REG_FS, LOWORD(NewSelector)); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBitTest) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + UINT DataSize; + ULONG BitNumber; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Get the bit number */ + BitNumber = OperandSize ? State->GeneralRegs[ModRegRm.Register].Long + : (ULONG)State->GeneralRegs[ModRegRm.Register].LowWord; + + if (ModRegRm.Memory) + { + /* + * For memory operands, add the bit offset divided by + * the data size to the address + */ + ModRegRm.MemoryAddress += BitNumber / DataSize; + } + + /* Normalize the bit number */ + BitNumber %= DataSize; + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + } + else + { + USHORT Value; + + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeShld) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + UCHAR Count; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xA4); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode == 0xA4) + { + /* Fetch the count */ + if (!Fast486FetchByte(State, &Count)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* The count is in CL */ + Count = State->GeneralRegs[FAST486_REG_ECX].LowByte; + } + + /* Normalize the count */ + Count &= 0x1F; + + /* Do nothing if the count is zero */ + if (Count == 0) return TRUE; + + if (OperandSize) + { + ULONG Source, Destination, Result; + + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = (Destination << Count) | (Source >> (32 - Count)); + + /* Update flags */ + State->Flags.Cf = (Destination >> (32 - Count)) & 1; + if (Count == 1) State->Flags.Of = (Result & SIGN_FLAG_LONG) + != (Destination & SIGN_FLAG_LONG); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Result); + } + else + { + USHORT Source, Destination, Result; + ULONG DoubleSource; + + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + DoubleSource = Source | (Source << 16); + + /* Calculate the result */ + Result = (Destination << Count) | (DoubleSource >> (32 - Count)); + + /* Update flags */ + if (Count <= 16) State->Flags.Cf = (Destination >> (16 - Count)) & 1; + else State->Flags.Cf = (Source >> (32 - Count)) & 1; + + if (Count == 1) State->Flags.Of = (Result & SIGN_FLAG_WORD) + != (Destination & SIGN_FLAG_WORD); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePushGs) +{ + /* Call the internal API */ + return Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePopGs) +{ + ULONG NewSelector; + + if (!Fast486StackPop(State, &NewSelector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486LoadSegment(State, FAST486_REG_GS, LOWORD(NewSelector)); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBts) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + UINT DataSize; + ULONG BitNumber; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Get the bit number */ + BitNumber = OperandSize ? State->GeneralRegs[ModRegRm.Register].Long + : (ULONG)State->GeneralRegs[ModRegRm.Register].LowWord; + + if (ModRegRm.Memory) + { + /* + * For memory operands, add the bit offset divided by + * the data size to the address + */ + ModRegRm.MemoryAddress += BitNumber / DataSize; + } + + /* Normalize the bit number */ + BitNumber %= DataSize; + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + /* Set the bit */ + Value |= 1 << BitNumber; + + /* Write back the result */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT Value; + + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + /* Set the bit */ + Value |= 1 << BitNumber; + + /* Write back the result */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeShrd) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + UCHAR Count; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xAC); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode == 0xAC) + { + /* Fetch the count */ + if (!Fast486FetchByte(State, &Count)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* The count is in CL */ + Count = State->GeneralRegs[FAST486_REG_ECX].LowByte; + } + + /* Normalize the count */ + Count &= 0x1F; + + /* Do nothing if the count is zero */ + if (Count == 0) return TRUE; + + if (OperandSize) + { + ULONG Source, Destination, Result; + + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = (Destination >> Count) | (Source << (32 - Count)); + + /* Update flags */ + State->Flags.Cf = (Destination >> (Count - 1)) & 1; + if (Count == 1) State->Flags.Of = (Result & SIGN_FLAG_LONG) + != (Destination & SIGN_FLAG_LONG); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Result); + } + else + { + USHORT Source, Destination, Result; + + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = (Destination >> Count) | (Source << (16 - Count)); + + if (Count >= 16) Result |= (ULONG)(Source | (Source << 16)) >> (Count - 16); + + /* Update flags */ + if (Count <= 16) State->Flags.Cf = (Destination >> (Count - 1)) & 1; + else State->Flags.Cf = (Source >> (Count - 17)) & 1; + + if (Count == 1) State->Flags.Of = (Result & SIGN_FLAG_WORD) + != (Destination & SIGN_FLAG_WORD); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeImul) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + LONG Source, Destination; + LONGLONG Result; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + (PULONG)&Destination, + (PULONG)&Source)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = (LONGLONG)Source * (LONGLONG)Destination; + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = ((Result < -2147483648LL) || (Result > 2147483647LL)); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, TRUE, (ULONG)((LONG)Result)); + } + else + { + SHORT Source, Destination; + LONG Result; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + (PUSHORT)&Destination, + (PUSHORT)&Source)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = (LONG)Source * (LONG)Destination; + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = ((Result < -32768) || (Result > 32767)); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, TRUE, (USHORT)((SHORT)Result)); + } +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeCmpXchgByte) +{ + FAST486_MOD_REG_RM ModRegRm; + UCHAR Accumulator = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR Source, Destination, Result; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Compare AL with the destination */ + Result = Accumulator - Destination; + + /* Update the flags */ + State->Flags.Cf = (Accumulator < Destination); + State->Flags.Of = ((Accumulator & SIGN_FLAG_BYTE) != (Destination & SIGN_FLAG_BYTE)) + && ((Accumulator & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = (Accumulator & 0x0F) < (Destination & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + if (State->Flags.Zf) + { + /* Load the source operand into the destination */ + return Fast486WriteModrmByteOperands(State, &ModRegRm, FALSE, Source); + } + else + { + /* Load the destination into AL */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Destination; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeCmpXchg) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Source, Destination, Result; + ULONG Accumulator = State->GeneralRegs[FAST486_REG_EAX].Long; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Compare EAX with the destination */ + Result = Accumulator - Destination; + + /* Update the flags */ + State->Flags.Cf = (Accumulator < Destination); + State->Flags.Of = ((Accumulator & SIGN_FLAG_LONG) != (Destination & SIGN_FLAG_LONG)) + && ((Accumulator & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = (Accumulator & 0x0F) < (Destination & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + if (State->Flags.Zf) + { + /* Load the source operand into the destination */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Source); + } + else + { + /* Load the destination into EAX */ + State->GeneralRegs[FAST486_REG_EAX].Long = Destination; + } + } + else + { + USHORT Source, Destination, Result; + USHORT Accumulator = State->GeneralRegs[FAST486_REG_EAX].LowWord; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, &Source, &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Compare AX with the destination */ + Result = Accumulator - Destination; + + /* Update the flags */ + State->Flags.Cf = (Accumulator < Destination); + State->Flags.Of = ((Accumulator & SIGN_FLAG_WORD) != (Destination & SIGN_FLAG_WORD)) + && ((Accumulator & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = (Accumulator & 0x0F) < (Destination & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + if (State->Flags.Zf) + { + /* Load the source operand into the destination */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Source); + } + else + { + /* Load the destination into AX */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Destination; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLss) +{ + UCHAR FarPointer[6]; + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xB2); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!ModRegRm.Memory) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) + ? State->SegmentOverride : FAST486_REG_DS, + ModRegRm.MemoryAddress, + FALSE, + FarPointer, + OperandSize ? 6 : 4)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Offset = *((PULONG)FarPointer); + USHORT Segment = *((PUSHORT)&FarPointer[sizeof(ULONG)]); + + /* Set the register to the offset */ + State->GeneralRegs[ModRegRm.Register].Long = Offset; + + /* Load the segment */ + return Fast486LoadSegment(State, + FAST486_REG_SS, + Segment); + } + else + { + USHORT Offset = *((PUSHORT)FarPointer); + USHORT Segment = *((PUSHORT)&FarPointer[sizeof(USHORT)]); + + /* Set the register to the offset */ + State->GeneralRegs[ModRegRm.Register].LowWord = Offset; + + /* Load the segment */ + return Fast486LoadSegment(State, + FAST486_REG_SS, + Segment); + } +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBtr) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + UINT DataSize; + ULONG BitNumber; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Get the bit number */ + BitNumber = OperandSize ? State->GeneralRegs[ModRegRm.Register].Long + : (ULONG)State->GeneralRegs[ModRegRm.Register].LowWord; + + if (ModRegRm.Memory) + { + /* + * For memory operands, add the bit offset divided by + * the data size to the address + */ + ModRegRm.MemoryAddress += BitNumber / DataSize; + } + + /* Normalize the bit number */ + BitNumber %= DataSize; + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + /* Clear the bit */ + Value &= ~(1 << BitNumber); + + /* Write back the result */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT Value; + + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + /* Clear the bit */ + Value &= ~(1 << BitNumber); + + /* Write back the result */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLfsLgs) +{ + UCHAR FarPointer[6]; + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xB4); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!ModRegRm.Memory) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) + ? State->SegmentOverride : FAST486_REG_DS, + ModRegRm.MemoryAddress, + FALSE, + FarPointer, + OperandSize ? 6 : 4)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Offset = *((PULONG)FarPointer); + USHORT Segment = *((PUSHORT)&FarPointer[sizeof(ULONG)]); + + /* Set the register to the offset */ + State->GeneralRegs[ModRegRm.Register].Long = Offset; + + /* Load the segment */ + return Fast486LoadSegment(State, + (Opcode == 0xB4) + ? FAST486_REG_FS : FAST486_REG_GS, + Segment); + } + else + { + USHORT Offset = *((PUSHORT)FarPointer); + USHORT Segment = *((PUSHORT)&FarPointer[sizeof(USHORT)]); + + /* Set the register to the offset */ + State->GeneralRegs[ModRegRm.Register].LowWord = Offset; + + /* Load the segment */ + return Fast486LoadSegment(State, + (Opcode == 0xB4) + ? FAST486_REG_FS : FAST486_REG_GS, + Segment); + } +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovzxByte) +{ + UCHAR Value; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xB6); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write back the zero-extended value */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + (ULONG)Value); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovzxWord) +{ + USHORT Value; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xB7); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write back the zero-extended value */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + (ULONG)Value); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBtc) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + UINT DataSize; + ULONG BitNumber; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Get the bit number */ + BitNumber = OperandSize ? State->GeneralRegs[ModRegRm.Register].Long + : (ULONG)State->GeneralRegs[ModRegRm.Register].LowWord; + + if (ModRegRm.Memory) + { + /* + * For memory operands, add the bit offset divided by + * the data size to the address + */ + ModRegRm.MemoryAddress += BitNumber / DataSize; + } + + /* Normalize the bit number */ + BitNumber %= DataSize; + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + /* Toggle the bit */ + Value ^= 1 << BitNumber; + + /* Write back the result */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT Value; + + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + /* Toggle the bit */ + Value ^= 1 << BitNumber; + + /* Write back the result */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBsf) +{ + INT i; + ULONG Value = 0; + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + ULONG BitNumber; + UINT DataSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xBC); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the value */ + if (OperandSize) + { + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + (PUSHORT)NULL, + (PUSHORT)&Value)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Set ZF */ + State->Flags.Zf = (Value == 0); + if (State->Flags.Zf) return TRUE; + + for (i = 0; i < DataSize; i++) + { + if(Value & (1 << i)) + { + /* Save the bit number */ + BitNumber = i; + + /* Exit the loop */ + break; + } + } + + /* Write back the result */ + if (OperandSize) + { + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, TRUE, BitNumber)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, TRUE, LOWORD(BitNumber))) + { + /* Exception occurred */ + return FALSE; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBsr) +{ + INT i; + ULONG Value = 0; + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + ULONG BitNumber; + UINT DataSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xBD); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the value */ + if (OperandSize) + { + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + (PUSHORT)NULL, + (PUSHORT)&Value)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Set ZF according to the value */ + State->Flags.Zf = (Value == 0); + if (State->Flags.Zf) return TRUE; + + for (i = DataSize - 1; i >= 0; i--) + { + if(Value & (1 << i)) + { + /* Save the bit number */ + BitNumber = i; + + /* Exit the loop */ + break; + } + } + + /* Write back the result */ + if (OperandSize) + { + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, TRUE, BitNumber)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, TRUE, LOWORD(BitNumber))) + { + /* Exception occurred */ + return FALSE; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovsxByte) +{ + CHAR Value; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xBE); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, (PUCHAR)&Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write back the sign-extended value */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + (ULONG)((LONG)Value)); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovsxWord) +{ + SHORT Value; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + TOGGLE_ADSIZE(AddressSize); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xBF); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, (PUSHORT)&Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write back the sign-extended value */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + (ULONG)((LONG)Value)); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeConditionalJmp) +{ + BOOLEAN Jump = FALSE; + LONG Offset = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF0) == 0x80); + + /* Fetch the offset */ + if (Size) + { + if (!Fast486FetchDword(State, (PULONG)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + SHORT Value; + + if (!Fast486FetchWord(State, (PUSHORT)&Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Sign-extend */ + Offset = (LONG)Value; + } + + switch ((Opcode & 0x0F) >> 1) + { + /* JO / JNO */ + case 0: + { + Jump = State->Flags.Of; + break; + } + + /* JC / JNC */ + case 1: + { + Jump = State->Flags.Cf; + break; + } + + /* JZ / JNZ */ + case 2: + { + Jump = State->Flags.Zf; + break; + } + + /* JBE / JNBE */ + case 3: + { + Jump = State->Flags.Cf || State->Flags.Zf; + break; + } + + /* JS / JNS */ + case 4: + { + Jump = State->Flags.Sf; + break; + } + + /* JP / JNP */ + case 5: + { + Jump = State->Flags.Pf; + break; + } + + /* JL / JNL */ + case 6: + { + Jump = State->Flags.Sf != State->Flags.Of; + break; + } + + /* JLE / JNLE */ + case 7: + { + Jump = (State->Flags.Sf != State->Flags.Of) || State->Flags.Zf; + break; + } + } + + if (Opcode & 1) + { + /* Invert the result */ + Jump = !Jump; + } + + if (Jump) + { + /* Move the instruction pointer */ + State->InstPtr.Long += Offset; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeConditionalSet) +{ + BOOLEAN Value = FALSE; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_MOD_REG_RM ModRegRm; + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF0) == 0x90); + + switch ((Opcode & 0x0F) >> 1) + { + /* SETO / SETNO */ + case 0: + { + Value = State->Flags.Of; + break; + } + + /* SETC / SETNC */ + case 1: + { + Value = State->Flags.Cf; + break; + } + + /* SETZ / SETNZ */ + case 2: + { + Value = State->Flags.Zf; + break; + } + + /* SETBE / SETNBE */ + case 3: + { + Value = State->Flags.Cf || State->Flags.Zf; + break; + } + + /* SETS / SETNS */ + case 4: + { + Value = State->Flags.Sf; + break; + } + + /* SETP / SETNP */ + case 5: + { + Value = State->Flags.Pf; + break; + } + + /* SETL / SETNL */ + case 6: + { + Value = State->Flags.Sf != State->Flags.Of; + break; + } + + /* SETLE / SETNLE */ + case 7: + { + Value = (State->Flags.Sf != State->Flags.Of) || State->Flags.Zf; + break; + } + } + + if (Opcode & 1) + { + /* Invert the result */ + Value = !Value; + } + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, &ModRegRm, FALSE, Value); +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeXaddByte) +{ + UCHAR Source, Destination, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xC0); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &Source, + &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = Source + Destination; + + /* Update the flags */ + State->Flags.Cf = (Result < Source) && (Result < Destination); + State->Flags.Of = ((Source & SIGN_FLAG_BYTE) == (Destination & SIGN_FLAG_BYTE)) + && ((Source & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((((Source & 0x0F) + (Destination & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write the sum to the destination */ + if (!Fast486WriteModrmByteOperands(State, &ModRegRm, FALSE, Result)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the old value of the destination to the source */ + if (!Fast486WriteModrmByteOperands(State, &ModRegRm, TRUE, Destination)) + { + /* Exception occurred */ + return FALSE; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeXadd) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xC1); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG Source, Destination, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &Source, + &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = Source + Destination; + + /* Update the flags */ + State->Flags.Cf = (Result < Source) && (Result < Destination); + State->Flags.Of = ((Source & SIGN_FLAG_LONG) == (Destination & SIGN_FLAG_LONG)) + && ((Source & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((((Source & 0x0F) + (Destination & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write the old value of the destination to the source */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, TRUE, Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the sum to the destination */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Result)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT Source, Destination, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &Source, + &Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = Source + Destination; + + /* Update the flags */ + State->Flags.Cf = (Result < Source) && (Result < Destination); + State->Flags.Of = ((Source & SIGN_FLAG_WORD) == (Destination & SIGN_FLAG_WORD)) + && ((Source & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((((Source & 0x0F) + (Destination & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write the old value of the destination to the source */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, TRUE, Destination)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the sum to the destination */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Result)) + { + /* Exception occurred */ + return FALSE; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBswap) +{ + PUCHAR Pointer; + + NO_LOCK_PREFIX(); + + /* Get a pointer to the value */ + Pointer = (PUCHAR)&State->GeneralRegs[Opcode & 0x07].Long; + + /* Swap the byte order */ + SWAP(Pointer[0], Pointer[3]); + SWAP(Pointer[1], Pointer[2]); + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeExtended) +{ + UCHAR SecondOpcode; + + /* Fetch the second operation code */ + if (!Fast486FetchByte(State, &SecondOpcode)) + { + /* Exception occurred */ + return FALSE; + } + + if (Fast486ExtendedHandlers[SecondOpcode] != NULL) + { + /* Call the extended opcode handler */ + return Fast486ExtendedHandlers[SecondOpcode](State, SecondOpcode); + } + else + { + /* This is not a valid opcode */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } +} + diff --git a/reactos/lib/fast486/extraops.h b/reactos/lib/fast486/extraops.h new file mode 100644 index 0000000000000..eab8e28fb9449 --- /dev/null +++ b/reactos/lib/fast486/extraops.h @@ -0,0 +1,67 @@ +/* + * Fast486 386/486 CPU Emulation Library + * extraops.h + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _EXTRAOPS_H_ +#define _EXTRAOPS_H_ + +#pragma once + +/* DEFINES ********************************************************************/ + +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLar); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLsl); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeClts); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeStoreControlReg); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeStoreDebugReg); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLoadControlReg); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLoadDebugReg); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePushFs); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePopFs); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBitTest); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeShld); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePushGs); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodePopGs); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBts); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeShrd); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeImul); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeCmpXchgByte); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeCmpXchg); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLss); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBtr); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeLfsLgs); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovzxByte); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovzxWord); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBtc); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBsf); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBsr); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovsxByte); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeMovsxWord); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeConditionalJmp); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeConditionalSet); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeXaddByte); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeXadd); +FAST486_OPCODE_HANDLER(Fast486ExtOpcodeBswap); +FAST486_OPCODE_HANDLER(Fast486OpcodeExtended); + +#endif // _EXTRAOPS_H_ + +/* EOF */ + diff --git a/reactos/lib/fast486/fast486.c b/reactos/lib/fast486/fast486.c new file mode 100644 index 0000000000000..c47814bb52622 --- /dev/null +++ b/reactos/lib/fast486/fast486.c @@ -0,0 +1,463 @@ +/* + * Fast486 386/486 CPU Emulation Library + * fast486.c + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* INCLUDES *******************************************************************/ + +#include + +// #define NDEBUG +#include + +#include +#include "common.h" +#include "opcodes.h" + +/* DEFINES ********************************************************************/ + +typedef enum +{ + FAST486_STEP_INTO, + FAST486_STEP_OVER, + FAST486_STEP_OUT, + FAST486_CONTINUE +} FAST486_EXEC_CMD; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +inline +VOID +NTAPI +Fast486ExecutionControl(PFAST486_STATE State, FAST486_EXEC_CMD Command) +{ + UCHAR Opcode; + INT ProcedureCallCount = 0; + + /* Main execution loop */ + do + { + /* Check if this is a new instruction */ + if (State->PrefixFlags == 0) State->SavedInstPtr = State->InstPtr; + + /* Perform an instruction fetch */ + if (!Fast486FetchByte(State, &Opcode)) + { + /* Exception occurred */ + State->PrefixFlags = 0; + continue; + } + + // TODO: Check for CALL/RET to update ProcedureCallCount. + + if (Fast486OpcodeHandlers[Opcode] != NULL) + { + /* Call the opcode handler */ + Fast486OpcodeHandlers[Opcode](State, Opcode); + } + else + { + /* This is not a valid opcode */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + } + + if (Fast486OpcodeHandlers[Opcode] == Fast486OpcodePrefix) + { + /* This is a prefix, go to the next instruction immediately */ + continue; + } + + /* A non-prefix opcode has been executed, reset the prefix flags */ + State->PrefixFlags = 0; + + /* + * Check if there is an interrupt to execute, or a hardware interrupt signal + * while interrupts are enabled. + */ + if (State->IntStatus == FAST486_INT_EXECUTE) + { + FAST486_IDT_ENTRY IdtEntry; + + /* Get the interrupt vector */ + if (Fast486GetIntVector(State, State->PendingIntNum, &IdtEntry)) + { + /* Perform the interrupt */ + Fast486InterruptInternal(State, + IdtEntry.Selector, + MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh), + IdtEntry.Type); + + /* Restore the prefix flags, which would be set to OPSIZE for 32-bit real mode */ + State->PrefixFlags = 0; + } + + /* Clear the interrupt status */ + State->IntStatus = FAST486_INT_NONE; + } + else if (State->Flags.If + && (State->IntAckCallback != NULL) + && (State->IntStatus == FAST486_INT_SIGNAL)) + { + /* Acknowledge the interrupt to get the number */ + State->PendingIntNum = State->IntAckCallback(State); + + /* Set the interrupt status to execute on the next instruction */ + State->IntStatus = FAST486_INT_EXECUTE; + } + } + while ((Command == FAST486_CONTINUE) + || (Command == FAST486_STEP_OVER && ProcedureCallCount > 0) + || (Command == FAST486_STEP_OUT && ProcedureCallCount >= 0) + || (Fast486OpcodeHandlers[Opcode] == Fast486OpcodePrefix)); +} + +/* DEFAULT CALLBACKS **********************************************************/ + +static VOID +NTAPI +Fast486MemReadCallback(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size) +{ + UNREFERENCED_PARAMETER(State); + + RtlMoveMemory(Buffer, (PVOID)Address, Size); +} + +static VOID +NTAPI +Fast486MemWriteCallback(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size) +{ + UNREFERENCED_PARAMETER(State); + + RtlMoveMemory((PVOID)Address, Buffer, Size); +} + +static VOID +NTAPI +Fast486IoReadCallback(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG DataCount, UCHAR DataSize) +{ + UNREFERENCED_PARAMETER(State); + UNREFERENCED_PARAMETER(Port); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DataCount); + UNREFERENCED_PARAMETER(DataSize); +} + +static VOID +NTAPI +Fast486IoWriteCallback(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG DataCount, UCHAR DataSize) +{ + UNREFERENCED_PARAMETER(State); + UNREFERENCED_PARAMETER(Port); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DataCount); + UNREFERENCED_PARAMETER(DataSize); +} + +static VOID +NTAPI +Fast486IdleCallback(PFAST486_STATE State) +{ + UNREFERENCED_PARAMETER(State); +} + +static VOID +NTAPI +Fast486BopCallback(PFAST486_STATE State, UCHAR BopCode) +{ + UNREFERENCED_PARAMETER(State); + UNREFERENCED_PARAMETER(BopCode); +} + +static UCHAR +NTAPI +Fast486IntAckCallback(PFAST486_STATE State) +{ + UNREFERENCED_PARAMETER(State); + + /* Return something... */ + return 0; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +Fast486Initialize(PFAST486_STATE State, + FAST486_MEM_READ_PROC MemReadCallback, + FAST486_MEM_WRITE_PROC MemWriteCallback, + FAST486_IO_READ_PROC IoReadCallback, + FAST486_IO_WRITE_PROC IoWriteCallback, + FAST486_IDLE_PROC IdleCallback, + FAST486_BOP_PROC BopCallback, + FAST486_INT_ACK_PROC IntAckCallback, + PULONG Tlb) +{ + /* Set the callbacks (or use default ones if some are NULL) */ + State->MemReadCallback = (MemReadCallback ? MemReadCallback : Fast486MemReadCallback ); + State->MemWriteCallback = (MemWriteCallback ? MemWriteCallback : Fast486MemWriteCallback); + State->IoReadCallback = (IoReadCallback ? IoReadCallback : Fast486IoReadCallback ); + State->IoWriteCallback = (IoWriteCallback ? IoWriteCallback : Fast486IoWriteCallback ); + State->IdleCallback = (IdleCallback ? IdleCallback : Fast486IdleCallback ); + State->BopCallback = (BopCallback ? BopCallback : Fast486BopCallback ); + State->IntAckCallback = (IntAckCallback ? IntAckCallback : Fast486IntAckCallback ); + + /* Set the TLB (if given) */ + State->Tlb = Tlb; + + /* Reset the CPU */ + Fast486Reset(State); +} + +VOID +NTAPI +Fast486Reset(PFAST486_STATE State) +{ + FAST486_SEG_REGS i; + + FAST486_MEM_READ_PROC MemReadCallback = State->MemReadCallback; + FAST486_MEM_WRITE_PROC MemWriteCallback = State->MemWriteCallback; + FAST486_IO_READ_PROC IoReadCallback = State->IoReadCallback; + FAST486_IO_WRITE_PROC IoWriteCallback = State->IoWriteCallback; + FAST486_IDLE_PROC IdleCallback = State->IdleCallback; + FAST486_BOP_PROC BopCallback = State->BopCallback; + FAST486_INT_ACK_PROC IntAckCallback = State->IntAckCallback; + PULONG Tlb = State->Tlb; + + /* Clear the entire structure */ + RtlZeroMemory(State, sizeof(*State)); + + /* Initialize the registers */ + State->Flags.AlwaysSet = 1; + State->InstPtr.LowWord = 0xFFF0; + + /* Set the CPL to 0 */ + State->Cpl = 0; + + /* Initialize segments */ + for (i = 0; i < FAST486_NUM_SEG_REGS; i++) + { + State->SegmentRegs[i].Selector = 0; + State->SegmentRegs[i].Base = 0; + State->SegmentRegs[i].Limit = 0xFFFF; + State->SegmentRegs[i].Present = TRUE; + State->SegmentRegs[i].ReadWrite = TRUE; + State->SegmentRegs[i].Executable = FALSE; + State->SegmentRegs[i].DirConf = FALSE; + State->SegmentRegs[i].SystemType = 1; // Segment descriptor + State->SegmentRegs[i].Dpl = 0; + State->SegmentRegs[i].Size = FALSE; // 16-bit + } + + /* Initialize the code segment */ + State->SegmentRegs[FAST486_REG_CS].Executable = TRUE; + State->SegmentRegs[FAST486_REG_CS].Selector = 0xF000; + State->SegmentRegs[FAST486_REG_CS].Base = 0xFFFF0000; + + /* Initialize the IDT */ + State->Idtr.Size = 0x3FF; + State->Idtr.Address = 0; + +#ifndef FAST486_NO_FPU + /* Initialize CR0 */ + State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_ET; +#endif + + /* Restore the callbacks and TLB */ + State->MemReadCallback = MemReadCallback; + State->MemWriteCallback = MemWriteCallback; + State->IoReadCallback = IoReadCallback; + State->IoWriteCallback = IoWriteCallback; + State->IdleCallback = IdleCallback; + State->BopCallback = BopCallback; + State->IntAckCallback = IntAckCallback; + State->Tlb = Tlb; +} + +VOID +NTAPI +Fast486DumpState(PFAST486_STATE State) +{ + DPRINT1("\nCPU currently executing in %s mode at %04X:%08X\n", + (State->ControlRegisters[0] & FAST486_CR0_PE) ? "protected" : "real", + State->SegmentRegs[FAST486_REG_CS].Selector, + State->InstPtr.Long); + DPRINT1("\nGeneral purpose registers:\n" + "EAX = %08X\tECX = %08X\tEDX = %08X\tEBX = %08X\n" + "ESP = %08X\tEBP = %08X\tESI = %08X\tEDI = %08X\n", + State->GeneralRegs[FAST486_REG_EAX].Long, + State->GeneralRegs[FAST486_REG_ECX].Long, + State->GeneralRegs[FAST486_REG_EDX].Long, + State->GeneralRegs[FAST486_REG_EBX].Long, + State->GeneralRegs[FAST486_REG_ESP].Long, + State->GeneralRegs[FAST486_REG_EBP].Long, + State->GeneralRegs[FAST486_REG_ESI].Long, + State->GeneralRegs[FAST486_REG_EDI].Long); + DPRINT1("\nSegment registers:\n" + "ES = %04X (Base: %08X, Limit: %08X, Dpl: %u)\n" + "CS = %04X (Base: %08X, Limit: %08X, Dpl: %u)\n" + "SS = %04X (Base: %08X, Limit: %08X, Dpl: %u)\n" + "DS = %04X (Base: %08X, Limit: %08X, Dpl: %u)\n" + "FS = %04X (Base: %08X, Limit: %08X, Dpl: %u)\n" + "GS = %04X (Base: %08X, Limit: %08X, Dpl: %u)\n", + State->SegmentRegs[FAST486_REG_ES].Selector, + State->SegmentRegs[FAST486_REG_ES].Base, + State->SegmentRegs[FAST486_REG_ES].Limit, + State->SegmentRegs[FAST486_REG_ES].Dpl, + State->SegmentRegs[FAST486_REG_CS].Selector, + State->SegmentRegs[FAST486_REG_CS].Base, + State->SegmentRegs[FAST486_REG_CS].Limit, + State->SegmentRegs[FAST486_REG_CS].Dpl, + State->SegmentRegs[FAST486_REG_SS].Selector, + State->SegmentRegs[FAST486_REG_SS].Base, + State->SegmentRegs[FAST486_REG_SS].Limit, + State->SegmentRegs[FAST486_REG_SS].Dpl, + State->SegmentRegs[FAST486_REG_DS].Selector, + State->SegmentRegs[FAST486_REG_DS].Base, + State->SegmentRegs[FAST486_REG_DS].Limit, + State->SegmentRegs[FAST486_REG_DS].Dpl, + State->SegmentRegs[FAST486_REG_FS].Selector, + State->SegmentRegs[FAST486_REG_FS].Base, + State->SegmentRegs[FAST486_REG_FS].Limit, + State->SegmentRegs[FAST486_REG_FS].Dpl, + State->SegmentRegs[FAST486_REG_GS].Selector, + State->SegmentRegs[FAST486_REG_GS].Base, + State->SegmentRegs[FAST486_REG_GS].Limit, + State->SegmentRegs[FAST486_REG_GS].Dpl); + DPRINT1("\nFlags: %08X (%s %s %s %s %s %s %s %s %s %s %s %s) Iopl: %u\n", + State->Flags.Long, + State->Flags.Cf ? "CF" : "cf", + State->Flags.Pf ? "PF" : "pf", + State->Flags.Af ? "AF" : "af", + State->Flags.Zf ? "ZF" : "zf", + State->Flags.Sf ? "SF" : "sf", + State->Flags.Tf ? "TF" : "tf", + State->Flags.If ? "IF" : "if", + State->Flags.Df ? "DF" : "df", + State->Flags.Of ? "OF" : "of", + State->Flags.Nt ? "NT" : "nt", + State->Flags.Rf ? "RF" : "rf", + State->Flags.Vm ? "VM" : "vm", + State->Flags.Iopl); + DPRINT1("\nControl Registers:\n" + "CR0 = %08X\tCR2 = %08X\tCR3 = %08X\n", + State->ControlRegisters[FAST486_REG_CR0], + State->ControlRegisters[FAST486_REG_CR2], + State->ControlRegisters[FAST486_REG_CR3]); + DPRINT1("\nDebug Registers:\n" + "DR0 = %08X\tDR1 = %08X\tDR2 = %08X\n" + "DR3 = %08X\tDR4 = %08X\tDR5 = %08X\n", + State->DebugRegisters[FAST486_REG_DR0], + State->DebugRegisters[FAST486_REG_DR1], + State->DebugRegisters[FAST486_REG_DR2], + State->DebugRegisters[FAST486_REG_DR3], + State->DebugRegisters[FAST486_REG_DR4], + State->DebugRegisters[FAST486_REG_DR5]); +} + +VOID +NTAPI +Fast486Continue(PFAST486_STATE State) +{ + /* Call the internal function */ + Fast486ExecutionControl(State, FAST486_CONTINUE); +} + +VOID +NTAPI +Fast486StepInto(PFAST486_STATE State) +{ + /* Call the internal function */ + Fast486ExecutionControl(State, FAST486_STEP_INTO); +} + +VOID +NTAPI +Fast486StepOver(PFAST486_STATE State) +{ + /* Call the internal function */ + Fast486ExecutionControl(State, FAST486_STEP_OVER); +} + +VOID +NTAPI +Fast486StepOut(PFAST486_STATE State) +{ + /* Call the internal function */ + Fast486ExecutionControl(State, FAST486_STEP_OUT); +} + +VOID +NTAPI +Fast486Interrupt(PFAST486_STATE State, UCHAR Number) +{ + /* Set the interrupt status and the number */ + State->IntStatus = FAST486_INT_EXECUTE; + State->PendingIntNum = Number; +} + +VOID +NTAPI +Fast486InterruptSignal(PFAST486_STATE State) +{ + /* Set the interrupt status */ + State->IntStatus = FAST486_INT_SIGNAL; +} + +VOID +NTAPI +Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset) +{ + /* Load the new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment)) + { + /* An exception occurred, let the handler execute instead */ + return; + } + + /* Set the new IP */ + State->InstPtr.Long = Offset; +} + +VOID +NTAPI +Fast486SetStack(PFAST486_STATE State, USHORT Segment, ULONG Offset) +{ + /* Load the new SS */ + if (!Fast486LoadSegment(State, FAST486_REG_SS, Segment)) + { + /* An exception occurred, let the handler execute instead */ + return; + } + + /* Set the new SP */ + State->GeneralRegs[FAST486_REG_ESP].Long = Offset; +} + +VOID +NTAPI +Fast486SetSegment(PFAST486_STATE State, + FAST486_SEG_REGS Segment, + USHORT Selector) +{ + /* Call the internal function */ + Fast486LoadSegment(State, Segment, Selector); +} + +/* EOF */ diff --git a/reactos/lib/fast486/fpu.c b/reactos/lib/fast486/fpu.c new file mode 100644 index 0000000000000..18f4a892afc38 --- /dev/null +++ b/reactos/lib/fast486/fpu.c @@ -0,0 +1,236 @@ +/* + * Fast486 386/486 CPU Emulation Library + * fpu.c + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* INCLUDES *******************************************************************/ + +#include + +// #define NDEBUG +#include + +#include +#include "common.h" +#include "opcodes.h" +#include "fpu.h" + +/* PUBLIC FUNCTIONS ***********************************************************/ + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + FPU_CHECK(); + +#ifndef FAST486_NO_FPU + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +#else + /* Do nothing */ + return TRUE; +#endif +} + +/* EOF */ diff --git a/reactos/lib/fast486/fpu.h b/reactos/lib/fast486/fpu.h new file mode 100644 index 0000000000000..63128a4259950 --- /dev/null +++ b/reactos/lib/fast486/fpu.h @@ -0,0 +1,62 @@ +/* + * Fast486 386/486 CPU Emulation Library + * fpu.h + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _FPU_H_ +#define _FPU_H_ + +#pragma once + +/* DEFINES ********************************************************************/ + +#define FPU_CHECK() if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_EM) \ + { \ + Fast486Exception(State, FAST486_EXCEPTION_NM); \ + return FALSE; \ + } +#define FPU_ST(i) State->FpuRegisters[(State->FpuStatus.Top + (i)) % FAST486_NUM_FPU_REGS] + +enum +{ + FPU_SINGLE_PRECISION = 0, + FPU_DOUBLE_PRECISION = 2, + FPU_DOUBLE_EXT_PRECISION = 3 +}; + +enum +{ + FPU_TAG_VALID = 0, + FPU_TAG_ZERO = 1, + FPU_TAG_SPECIAL = 2, + FPU_TAG_EMPTY = 3 +}; + +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE); +FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF); + +#endif // _FPU_H_ + +/* EOF */ diff --git a/reactos/lib/fast486/opcodes.c b/reactos/lib/fast486/opcodes.c new file mode 100644 index 0000000000000..3dffdaacc182f --- /dev/null +++ b/reactos/lib/fast486/opcodes.c @@ -0,0 +1,6312 @@ +/* + * Fast486 386/486 CPU Emulation Library + * opcodes.c + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* INCLUDES *******************************************************************/ + +#include + +// #define NDEBUG +#include + +#include +#include "opcodes.h" +#include "opgroups.h" +#include "extraops.h" +#include "common.h" +#include "fpu.h" + +/* PUBLIC VARIABLES ***********************************************************/ + +FAST486_OPCODE_HANDLER_PROC +Fast486OpcodeHandlers[FAST486_NUM_OPCODE_HANDLERS] = +{ + Fast486OpcodeAddByteModrm, + Fast486OpcodeAddModrm, + Fast486OpcodeAddByteModrm, + Fast486OpcodeAddModrm, + Fast486OpcodeAddAl, + Fast486OpcodeAddEax, + Fast486OpcodePushEs, + Fast486OpcodePopEs, + Fast486OpcodeOrByteModrm, + Fast486OpcodeOrModrm, + Fast486OpcodeOrByteModrm, + Fast486OpcodeOrModrm, + Fast486OpcodeOrAl, + Fast486OpcodeOrEax, + Fast486OpcodePushCs, + Fast486OpcodeExtended, + Fast486OpcodeAdcByteModrm, + Fast486OpcodeAdcModrm, + Fast486OpcodeAdcByteModrm, + Fast486OpcodeAdcModrm, + Fast486OpcodeAdcAl, + Fast486OpcodeAdcEax, + Fast486OpcodePushSs, + Fast486OpcodePopSs, + Fast486OpcodeSbbByteModrm, + Fast486OpcodeSbbModrm, + Fast486OpcodeSbbByteModrm, + Fast486OpcodeSbbModrm, + Fast486OpcodeSbbAl, + Fast486OpcodeSbbEax, + Fast486OpcodePushDs, + Fast486OpcodePopDs, + Fast486OpcodeAndByteModrm, + Fast486OpcodeAndModrm, + Fast486OpcodeAndByteModrm, + Fast486OpcodeAndModrm, + Fast486OpcodeAndAl, + Fast486OpcodeAndEax, + Fast486OpcodePrefix, + Fast486OpcodeDaa, + Fast486OpcodeCmpSubByteModrm, + Fast486OpcodeCmpSubModrm, + Fast486OpcodeCmpSubByteModrm, + Fast486OpcodeCmpSubModrm, + Fast486OpcodeCmpSubAl, + Fast486OpcodeCmpSubEax, + Fast486OpcodePrefix, + Fast486OpcodeDas, + Fast486OpcodeXorByteModrm, + Fast486OpcodeXorModrm, + Fast486OpcodeXorByteModrm, + Fast486OpcodeXorModrm, + Fast486OpcodeXorAl, + Fast486OpcodeXorEax, + Fast486OpcodePrefix, + Fast486OpcodeAaa, + Fast486OpcodeCmpSubByteModrm, + Fast486OpcodeCmpSubModrm, + Fast486OpcodeCmpSubByteModrm, + Fast486OpcodeCmpSubModrm, + Fast486OpcodeCmpSubAl, + Fast486OpcodeCmpSubEax, + Fast486OpcodePrefix, + Fast486OpcodeAas, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeIncrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodeDecrement, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePushReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePopReg, + Fast486OpcodePushAll, + Fast486OpcodePopAll, + Fast486OpcodeBound, + Fast486OpcodeArpl, + Fast486OpcodePrefix, + Fast486OpcodePrefix, + Fast486OpcodePrefix, + Fast486OpcodePrefix, + Fast486OpcodePushImm, + Fast486OpcodeImulModrmImm, + Fast486OpcodePushByteImm, + Fast486OpcodeImulModrmImm, + Fast486OpcodeIns, + Fast486OpcodeIns, + Fast486OpcodeOuts, + Fast486OpcodeOuts, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeShortConditionalJmp, + Fast486OpcodeGroup8082, + Fast486OpcodeGroup81, + Fast486OpcodeGroup8082, + Fast486OpcodeGroup83, + Fast486OpcodeTestByteModrm, + Fast486OpcodeTestModrm, + Fast486OpcodeXchgByteModrm, + Fast486OpcodeXchgModrm, + Fast486OpcodeMovByteModrm, + Fast486OpcodeMovModrm, + Fast486OpcodeMovByteModrm, + Fast486OpcodeMovModrm, + Fast486OpcodeMovStoreSeg, + Fast486OpcodeLea, + Fast486OpcodeMovLoadSeg, + Fast486OpcodeGroup8F, + Fast486OpcodeNop, + Fast486OpcodeExchangeEax, + Fast486OpcodeExchangeEax, + Fast486OpcodeExchangeEax, + Fast486OpcodeExchangeEax, + Fast486OpcodeExchangeEax, + Fast486OpcodeExchangeEax, + Fast486OpcodeExchangeEax, + Fast486OpcodeCwde, + Fast486OpcodeCdq, + Fast486OpcodeCallAbs, + Fast486OpcodeWait, + Fast486OpcodePushFlags, + Fast486OpcodePopFlags, + Fast486OpcodeSahf, + Fast486OpcodeLahf, + Fast486OpcodeMovAlOffset, + Fast486OpcodeMovEaxOffset, + Fast486OpcodeMovOffsetAl, + Fast486OpcodeMovOffsetEax, + Fast486OpcodeMovs, + Fast486OpcodeMovs, + Fast486OpcodeCmps, + Fast486OpcodeCmps, + Fast486OpcodeTestAl, + Fast486OpcodeTestEax, + Fast486OpcodeStos, + Fast486OpcodeStos, + Fast486OpcodeLods, + Fast486OpcodeLods, + Fast486OpcodeScas, + Fast486OpcodeScas, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovByteRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeMovRegImm, + Fast486OpcodeGroupC0, + Fast486OpcodeGroupC1, + Fast486OpcodeRet, + Fast486OpcodeRet, + Fast486OpcodeLdsLes, + Fast486OpcodeLdsLes, + Fast486OpcodeGroupC6, + Fast486OpcodeGroupC7, + Fast486OpcodeEnter, + Fast486OpcodeLeave, + Fast486OpcodeRetFar, + Fast486OpcodeRetFar, + Fast486OpcodeInt, + Fast486OpcodeInt, + Fast486OpcodeInt, + Fast486OpcodeIret, + Fast486OpcodeGroupD0, + Fast486OpcodeGroupD1, + Fast486OpcodeGroupD2, + Fast486OpcodeGroupD3, + Fast486OpcodeAam, + Fast486OpcodeAad, + Fast486OpcodeSalc, + Fast486OpcodeXlat, + Fast486FpuOpcodeD8, + Fast486FpuOpcodeD9, + Fast486FpuOpcodeDA, + Fast486FpuOpcodeDB, + Fast486FpuOpcodeDC, + Fast486FpuOpcodeDD, + Fast486FpuOpcodeDE, + Fast486FpuOpcodeDF, + Fast486OpcodeLoop, + Fast486OpcodeLoop, + Fast486OpcodeLoop, + Fast486OpcodeJecxz, + Fast486OpcodeInByte, + Fast486OpcodeIn, + Fast486OpcodeOutByte, + Fast486OpcodeOut, + Fast486OpcodeCall, + Fast486OpcodeJmp, + Fast486OpcodeJmpAbs, + Fast486OpcodeShortJump, + Fast486OpcodeInByte, + Fast486OpcodeIn, + Fast486OpcodeOutByte, + Fast486OpcodeOut, + Fast486OpcodePrefix, + NULL, // Invalid + Fast486OpcodePrefix, + Fast486OpcodePrefix, + Fast486OpcodeHalt, + Fast486OpcodeComplCarry, + Fast486OpcodeGroupF6, + Fast486OpcodeGroupF7, + Fast486OpcodeClearCarry, + Fast486OpcodeSetCarry, + Fast486OpcodeClearInt, + Fast486OpcodeSetInt, + Fast486OpcodeClearDir, + Fast486OpcodeSetDir, + Fast486OpcodeGroupFE, + Fast486OpcodeGroupFF, +}; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +FAST486_OPCODE_HANDLER(Fast486OpcodePrefix) +{ + BOOLEAN Valid = FALSE; + + switch (Opcode) + { + /* ES: */ + case 0x26: + { + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_ES; + Valid = TRUE; + } + + break; + } + + /* CS: */ + case 0x2E: + { + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_CS; + Valid = TRUE; + } + + break; + } + + /* SS: */ + case 0x36: + { + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_SS; + Valid = TRUE; + } + + break; + } + + /* DS: */ + case 0x3E: + { + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_DS; + Valid = TRUE; + } + + break; + } + + /* FS: */ + case 0x64: + { + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_FS; + Valid = TRUE; + } + + break; + } + + /* GS: */ + case 0x65: + { + if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) + { + State->PrefixFlags |= FAST486_PREFIX_SEG; + State->SegmentOverride = FAST486_REG_GS; + Valid = TRUE; + } + + break; + } + + /* OPSIZE */ + case 0x66: + { + if (!(State->PrefixFlags & FAST486_PREFIX_OPSIZE)) + { + State->PrefixFlags |= FAST486_PREFIX_OPSIZE; + Valid = TRUE; + } + + break; + } + + /* ADSIZE */ + case 0x67: + { + if (!(State->PrefixFlags & FAST486_PREFIX_ADSIZE)) + { + State->PrefixFlags |= FAST486_PREFIX_ADSIZE; + Valid = TRUE; + } + break; + } + + /* LOCK */ + case 0xF0: + { + if (!(State->PrefixFlags & FAST486_PREFIX_LOCK)) + { + State->PrefixFlags |= FAST486_PREFIX_LOCK; + Valid = TRUE; + } + + break; + } + + /* REPNZ */ + case 0xF2: + { + /* Mutually exclusive with REP */ + if (!(State->PrefixFlags + & (FAST486_PREFIX_REPNZ | FAST486_PREFIX_REP))) + { + State->PrefixFlags |= FAST486_PREFIX_REPNZ; + Valid = TRUE; + } + + break; + } + + /* REP / REPZ */ + case 0xF3: + { + /* Mutually exclusive with REPNZ */ + if (!(State->PrefixFlags + & (FAST486_PREFIX_REPNZ | FAST486_PREFIX_REP))) + { + State->PrefixFlags |= FAST486_PREFIX_REP; + Valid = TRUE; + } + + break; + } + } + + if (!Valid) + { + /* Clear all prefixes */ + State->PrefixFlags = 0; + + /* Throw an exception */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeIncrement) +{ + ULONG Value; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0x40); + + if (Size) + { + Value = ++State->GeneralRegs[Opcode & 0x07].Long; + + State->Flags.Of = (Value == SIGN_FLAG_LONG); + State->Flags.Sf = ((Value & SIGN_FLAG_LONG) != 0); + } + else + { + Value = ++State->GeneralRegs[Opcode & 0x07].LowWord; + + State->Flags.Of = (Value == SIGN_FLAG_WORD); + State->Flags.Sf = ((Value & SIGN_FLAG_WORD) != 0); + } + + State->Flags.Zf = (Value == 0); + State->Flags.Af = ((Value & 0x0F) == 0); + State->Flags.Pf = Fast486CalculateParity(LOBYTE(Value)); + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeDecrement) +{ + ULONG Value; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0x48); + + if (Size) + { + Value = --State->GeneralRegs[Opcode & 0x07].Long; + + State->Flags.Of = (Value == (SIGN_FLAG_LONG - 1)); + State->Flags.Sf = ((Value & SIGN_FLAG_LONG) != 0); + } + else + { + Value = --State->GeneralRegs[Opcode & 0x07].LowWord; + + State->Flags.Of = (Value == (SIGN_FLAG_WORD - 1)); + State->Flags.Sf = ((Value & SIGN_FLAG_WORD) != 0); + } + + State->Flags.Zf = (Value == 0); + State->Flags.Af = ((Value & 0x0F) == 0x0F); + State->Flags.Pf = Fast486CalculateParity(LOBYTE(Value)); + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushReg) +{ + NO_LOCK_PREFIX(); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0x50); + + /* Call the internal function */ + return Fast486StackPush(State, State->GeneralRegs[Opcode & 0x07].Long); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePopReg) +{ + ULONG Value; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0x58); + + /* Call the internal function */ + if (!Fast486StackPop(State, &Value)) return FALSE; + + /* Store the value */ + if (Size) State->GeneralRegs[Opcode & 0x07].Long = Value; + else State->GeneralRegs[Opcode & 0x07].LowWord = Value; + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeNop) +{ + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + /* Idle cycle */ + State->IdleCallback(State); + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeExchangeEax) +{ + INT Reg = Opcode & 0x07; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0x90); + + /* Exchange the values */ + if (Size) + { + ULONG Value; + + Value = State->GeneralRegs[Reg].Long; + State->GeneralRegs[Reg].Long = State->GeneralRegs[FAST486_REG_EAX].Long; + State->GeneralRegs[FAST486_REG_EAX].Long = Value; + } + else + { + USHORT Value; + + Value = State->GeneralRegs[Reg].LowWord; + State->GeneralRegs[Reg].LowWord = State->GeneralRegs[FAST486_REG_EAX].LowWord; + State->GeneralRegs[FAST486_REG_EAX].LowWord = Value; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeShortConditionalJmp) +{ + BOOLEAN Jump = FALSE; + CHAR Offset = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF0) == 0x70); + + TOGGLE_OPSIZE(Size); + + /* Fetch the offset */ + if (!Fast486FetchByte(State, (PUCHAR)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + switch ((Opcode & 0x0F) >> 1) + { + /* JO / JNO */ + case 0: + { + Jump = State->Flags.Of; + break; + } + + /* JC / JNC */ + case 1: + { + Jump = State->Flags.Cf; + break; + } + + /* JZ / JNZ */ + case 2: + { + Jump = State->Flags.Zf; + break; + } + + /* JBE / JNBE */ + case 3: + { + Jump = State->Flags.Cf || State->Flags.Zf; + break; + } + + /* JS / JNS */ + case 4: + { + Jump = State->Flags.Sf; + break; + } + + /* JP / JNP */ + case 5: + { + Jump = State->Flags.Pf; + break; + } + + /* JL / JNL */ + case 6: + { + Jump = State->Flags.Sf != State->Flags.Of; + break; + } + + /* JLE / JNLE */ + case 7: + { + Jump = (State->Flags.Sf != State->Flags.Of) || State->Flags.Zf; + break; + } + } + + if (Opcode & 1) + { + /* Invert the result */ + Jump = !Jump; + } + + if (Jump) + { + /* Move the instruction pointer */ + State->InstPtr.Long += Offset; + + if (!Size) + { + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeClearCarry) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xF8); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Clear CF and return success */ + State->Flags.Cf = FALSE; + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSetCarry) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xF9); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Set CF and return success*/ + State->Flags.Cf = TRUE; + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeComplCarry) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xF5); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Toggle CF and return success */ + State->Flags.Cf = !State->Flags.Cf; + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeClearInt) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xFA); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Check for protected mode */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + { + /* Check IOPL */ + if (State->Flags.Iopl >= State->SegmentRegs[FAST486_REG_CS].Dpl) + { + /* Clear the interrupt flag */ + State->Flags.If = FALSE; + } + else + { + /* General Protection Fault */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + } + else + { + /* Just clear the interrupt flag */ + State->Flags.If = FALSE; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSetInt) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xFB); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Check for protected mode */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + { + /* Check IOPL */ + if (State->Flags.Iopl >= State->SegmentRegs[FAST486_REG_CS].Dpl) + { + /* Set the interrupt flag */ + State->Flags.If = TRUE; + } + else + { + /* General Protection Fault */ + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + } + else + { + /* Just set the interrupt flag */ + State->Flags.If = TRUE; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeClearDir) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xFC); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Clear DF and return success */ + State->Flags.Df = FALSE; + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSetDir) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xFD); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Set DF and return success*/ + State->Flags.Df = TRUE; + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeHalt) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xF4); + + /* No prefixes allowed */ + if (State->PrefixFlags) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Privileged instructions can only be executed under CPL = 0 */ + if (State->SegmentRegs[FAST486_REG_CS].Dpl != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Halt */ + // TODO: Halt the CPU until an interrupt occurs, using IdleCallback if needed. + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeInByte) +{ + UCHAR Data; + ULONG Port; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF7) == 0xE4); + + if (Opcode == 0xE4) + { + /* Fetch the parameter */ + if (!Fast486FetchByte(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the port number to the parameter */ + Port = Data; + } + else + { + /* The port number is in DX */ + Port = State->GeneralRegs[FAST486_REG_EDX].LowWord; + } + + /* Read a byte from the I/O port */ + State->IoReadCallback(State, Port, &Data, 1, sizeof(UCHAR)); + + /* Store the result in AL */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Data; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeIn) +{ + ULONG Port; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF7) == 0xE5); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Opcode == 0xE5) + { + UCHAR Data; + + /* Fetch the parameter */ + if (!Fast486FetchByte(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the port number to the parameter */ + Port = Data; + } + else + { + /* The port number is in DX */ + Port = State->GeneralRegs[FAST486_REG_EDX].LowWord; + } + + if (Size) + { + ULONG Data; + + /* Read a dword from the I/O port */ + State->IoReadCallback(State, Port, &Data, 1, sizeof(ULONG)); + + /* Store the value in EAX */ + State->GeneralRegs[FAST486_REG_EAX].Long = Data; + } + else + { + USHORT Data; + + /* Read a word from the I/O port */ + State->IoReadCallback(State, Port, &Data, 1, sizeof(USHORT)); + + /* Store the value in AX */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Data; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOutByte) +{ + UCHAR Data; + ULONG Port; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF7) == 0xE6); + + if (Opcode == 0xE6) + { + /* Fetch the parameter */ + if (!Fast486FetchByte(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the port number to the parameter */ + Port = Data; + } + else + { + /* The port number is in DX */ + Port = State->GeneralRegs[FAST486_REG_EDX].LowWord; + } + + /* Read the value from AL */ + Data = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + /* Write the byte to the I/O port */ + State->IoWriteCallback(State, Port, &Data, 1, sizeof(UCHAR)); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOut) +{ + ULONG Port; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF7) == 0xE7); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Opcode == 0xE7) + { + UCHAR Data; + + /* Fetch the parameter */ + if (!Fast486FetchByte(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the port number to the parameter */ + Port = Data; + } + else + { + /* The port number is in DX */ + Port = State->GeneralRegs[FAST486_REG_EDX].LowWord; + } + + if (Size) + { + /* Get the value from EAX */ + ULONG Data = State->GeneralRegs[FAST486_REG_EAX].Long; + + /* Write a dword to the I/O port */ + State->IoWriteCallback(State, Port, &Data, 1, sizeof(ULONG)); + } + else + { + /* Get the value from AX */ + USHORT Data = State->GeneralRegs[FAST486_REG_EAX].LowWord; + + /* Write a word to the I/O port */ + State->IoWriteCallback(State, Port, &Data, 1, sizeof(USHORT)); + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeShortJump) +{ + CHAR Offset = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(Size); + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xEB); + + /* Fetch the offset */ + if (!Fast486FetchByte(State, (PUCHAR)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + /* Move the instruction pointer */ + State->InstPtr.Long += Offset; + + if (!Size) + { + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovRegImm) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0xB8); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Size) + { + ULONG Value; + + /* Fetch the dword */ + if (!Fast486FetchDword(State, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Store the value in the register */ + State->GeneralRegs[Opcode & 0x07].Long = Value; + } + else + { + USHORT Value; + + /* Fetch the word */ + if (!Fast486FetchWord(State, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Store the value in the register */ + State->GeneralRegs[Opcode & 0x07].LowWord = Value; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovByteRegImm) +{ + UCHAR Value; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xF8) == 0xB0); + + if (State->PrefixFlags != 0) + { + /* Invalid prefix */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Fetch the byte */ + if (!Fast486FetchByte(State, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode & 0x04) + { + /* AH, CH, DH or BH */ + State->GeneralRegs[Opcode & 0x03].HighByte = Value; + } + else + { + /* AL, CL, DL or BL */ + State->GeneralRegs[Opcode & 0x03].LowByte = Value; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAddByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x00); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue; + + /* Update the flags */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAddModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x01); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue; + + /* Update the flags */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue; + + /* Update the flags */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAddAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x04); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue; + + /* Update the flags */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAddEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x05); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue; + + /* Update the flags */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue; + + /* Update the flags */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOrByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x08); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue | SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOrModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x09); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue | SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue | SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOrAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x0C); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue | SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOrEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x0D); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue | SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue | SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAndByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x20); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAndModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x21); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAndAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x24); + + NO_LOCK_PREFIX(); + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAndEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x25); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXorByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x30); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue ^ SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXorModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x31); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue ^ SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue ^ SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXorAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x34); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue ^ SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXorEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x35); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue ^ SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue ^ SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeTestByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x84); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* The result is discarded */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeTestModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x85); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + } + + /* The result is discarded */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeTestAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xA8); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* The result is discarded */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeTestEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xA9); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue & SecondValue; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + } + + /* The result is discarded */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXchgByteModrm) +{ + UCHAR FirstValue, SecondValue; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x86); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the value from the register to the R/M */ + if (!Fast486WriteModrmByteOperands(State, + &ModRegRm, + FALSE, + FirstValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the value from the R/M to the register */ + if (!Fast486WriteModrmByteOperands(State, + &ModRegRm, + TRUE, + SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXchgModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x87); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the value from the register to the R/M */ + if (!Fast486WriteModrmDwordOperands(State, + &ModRegRm, + FALSE, + FirstValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the value from the R/M to the register */ + if (!Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT FirstValue, SecondValue; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the value from the register to the R/M */ + if (!Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + FirstValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write the value from the R/M to the register */ + if (!Fast486WriteModrmWordOperands(State, + &ModRegRm, + TRUE, + SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* The result is discarded */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushEs) +{ + /* Call the internal API */ + return Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePopEs) +{ + ULONG NewSelector; + + if (!Fast486StackPop(State, &NewSelector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486LoadSegment(State, FAST486_REG_ES, LOWORD(NewSelector)); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushCs) +{ + /* Call the internal API */ + return Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x10); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue + State->Flags.Cf; + + /* Special exception for CF */ + State->Flags.Cf = State->Flags.Cf + && ((FirstValue == 0xFF) || (SecondValue == 0xFF)); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue)); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x11); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue + State->Flags.Cf; + + /* Special exception for CF */ + State->Flags.Cf = State->Flags.Cf + && ((FirstValue == 0xFFFFFFFF) || (SecondValue == 0xFFFFFFFF)); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue)); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue + State->Flags.Cf; + + /* Special exception for CF */ + State->Flags.Cf = State->Flags.Cf + && ((FirstValue == 0xFFFF) || (SecondValue == 0xFFFF)); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue)); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x14); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue + State->Flags.Cf; + + /* Special exception for CF */ + State->Flags.Cf = State->Flags.Cf && + ((FirstValue == 0xFF) || (SecondValue == 0xFF)); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue)); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x15); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue + State->Flags.Cf; + + /* Special exception for CF */ + State->Flags.Cf = State->Flags.Cf && + ((FirstValue == 0xFFFFFFFF) || (SecondValue == 0xFFFFFFFF)); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue)); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue + SecondValue + State->Flags.Cf; + + /* Special exception for CF */ + State->Flags.Cf = State->Flags.Cf && + ((FirstValue == 0xFFFF) || (SecondValue == 0xFFFF)); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue)); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushSs) +{ + /* Call the internal API */ + return Fast486StackPush(State, State->SegmentRegs[FAST486_REG_SS].Selector); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePopSs) +{ + ULONG NewSelector; + + if (!Fast486StackPop(State, &NewSelector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486LoadSegment(State, FAST486_REG_SS, LOWORD(NewSelector)); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + INT Carry = State->Flags.Cf ? 1 : 0; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x18); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if this is the instruction that writes to R/M */ + if (!(Opcode & FAST486_OPCODE_WRITE_REG)) + { + /* Swap the order */ + SWAP(FirstValue, SecondValue); + } + + /* Calculate the result */ + Result = FirstValue - SecondValue - Carry; + + /* Update the flags */ + State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + INT Carry = State->Flags.Cf ? 1 : 0; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x19); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if this is the instruction that writes to R/M */ + if (!(Opcode & FAST486_OPCODE_WRITE_REG)) + { + /* Swap the order */ + SWAP(FirstValue, SecondValue); + } + + /* Calculate the result */ + Result = FirstValue - SecondValue - Carry; + + /* Update the flags */ + State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if this is the instruction that writes to R/M */ + if (!(Opcode & FAST486_OPCODE_WRITE_REG)) + { + /* Swap the order */ + SWAP(FirstValue, SecondValue); + } + + /* Calculate the result */ + Result = FirstValue - SecondValue - Carry; + + /* Update the flags */ + State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + INT Carry = State->Flags.Cf ? 1 : 0; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x1C); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue - SecondValue - Carry; + + /* Update the flags */ + State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + + return TRUE; + +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + INT Carry = State->Flags.Cf ? 1 : 0; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x1D); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue - SecondValue - Carry; + + /* Update the flags */ + State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue - SecondValue - Carry; + + /* Update the flags */ + State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + + return TRUE; + +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushDs) +{ + /* Call the internal API */ + return Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePopDs) +{ + ULONG NewSelector; + + if (!Fast486StackPop(State, &NewSelector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486LoadSegment(State, FAST486_REG_DS, LOWORD(NewSelector)); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeDaa) +{ + UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + BOOLEAN Carry = State->Flags.Cf; + + /* Clear the carry flag */ + State->Flags.Cf = FALSE; + + /* Check if the first BCD digit is invalid or there was a carry from it */ + if (((Value & 0x0F) > 9) || State->Flags.Af) + { + /* Correct it */ + State->GeneralRegs[FAST486_REG_EAX].LowByte += 0x06; + if (State->GeneralRegs[FAST486_REG_EAX].LowByte < 0x06) + { + /* A carry occurred */ + State->Flags.Cf = TRUE; + } + + /* Set the adjust flag */ + State->Flags.Af = TRUE; + } + + /* Check if the second BCD digit is invalid or there was a carry from it */ + if ((Value > 0x99) || Carry) + { + /* Correct it */ + State->GeneralRegs[FAST486_REG_EAX].LowByte += 0x60; + + /* There was a carry */ + State->Flags.Cf = TRUE; + } + + Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + /* Update the flags */ + State->Flags.Sf = (Value & SIGN_FLAG_BYTE) != 0; + State->Flags.Zf = (Value == 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xED) == 0x28); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if this is the instruction that writes to R/M */ + if (!(Opcode & FAST486_OPCODE_WRITE_REG)) + { + /* Swap the order */ + SWAP(FirstValue, SecondValue); + } + + /* Calculate the result */ + Result = FirstValue - SecondValue; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Check if this is not a CMP */ + if (!(Opcode & 0x10)) + { + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + /* Discard the result */ + return TRUE; + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xED) == 0x29); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if this is the instruction that writes to R/M */ + if (!(Opcode & FAST486_OPCODE_WRITE_REG)) + { + /* Swap the order */ + SWAP(FirstValue, SecondValue); + } + + /* Calculate the result */ + Result = FirstValue - SecondValue; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Check if this is not a CMP */ + if (!(Opcode & 0x10)) + { + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + /* Discard the result */ + return TRUE; + } + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if this is the instruction that writes to R/M */ + if (!(Opcode & FAST486_OPCODE_WRITE_REG)) + { + /* Swap the order */ + SWAP(FirstValue, SecondValue); + } + + /* Calculate the result */ + Result = FirstValue - SecondValue; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Check if this is not a CMP */ + if (!(Opcode & 0x10)) + { + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + /* Discard the result */ + return TRUE; + } + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubAl) +{ + UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte; + UCHAR SecondValue, Result; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xEF) == 0x2C); + + if (State->PrefixFlags) + { + /* This opcode doesn't take any prefixes */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486FetchByte(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue - SecondValue; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE)) + && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Check if this is not a CMP */ + if (!(Opcode & 0x10)) + { + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Result; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubEax) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xEF) == 0x2D); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue, Result; + + if (!Fast486FetchDword(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue - SecondValue; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG)) + && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Check if this is not a CMP */ + if (!(Opcode & 0x10)) + { + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result; + } + } + else + { + USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord; + USHORT SecondValue, Result; + + if (!Fast486FetchWord(State, &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = FirstValue - SecondValue; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD)) + && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Check if this is not a CMP */ + if (!(Opcode & 0x10)) + { + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeDas) +{ + UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + BOOLEAN Carry = State->Flags.Cf; + + /* Clear the carry flag */ + State->Flags.Cf = FALSE; + + /* Check if the first BCD digit is invalid or there was a borrow */ + if (((Value & 0x0F) > 9) || State->Flags.Af) + { + /* Correct it */ + State->GeneralRegs[FAST486_REG_EAX].LowByte -= 0x06; + if (State->GeneralRegs[FAST486_REG_EAX].LowByte > 0xFB) + { + /* A borrow occurred */ + State->Flags.Cf = TRUE; + } + + /* Set the adjust flag */ + State->Flags.Af = TRUE; + } + + /* Check if the second BCD digit is invalid or there was a borrow */ + if ((Value > 0x99) || Carry) + { + /* Correct it */ + State->GeneralRegs[FAST486_REG_EAX].LowByte -= 0x60; + + /* There was a borrow */ + State->Flags.Cf = TRUE; + } + + Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + /* Update the flags */ + State->Flags.Sf = (Value & SIGN_FLAG_BYTE) != 0; + State->Flags.Zf = (Value == 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAaa) +{ + UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + /* + * Check if the value in AL is not a valid BCD digit, + * or there was a carry from the lowest 4 bits of AL + */ + if (((Value & 0x0F) > 9) || State->Flags.Af) + { + /* Correct it */ + State->GeneralRegs[FAST486_REG_EAX].LowWord += 0x06; + State->GeneralRegs[FAST486_REG_EAX].HighByte++; + + /* Set CF and AF */ + State->Flags.Cf = State->Flags.Af = TRUE; + } + else + { + /* Clear CF and AF */ + State->Flags.Cf = State->Flags.Af = FALSE; + } + + /* Keep only the lowest 4 bits of AL */ + State->GeneralRegs[FAST486_REG_EAX].LowByte &= 0x0F; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAas) +{ + UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + /* + * Check if the value in AL is not a valid BCD digit, + * or there was a borrow from the lowest 4 bits of AL + */ + if (((Value & 0x0F) > 9) || State->Flags.Af) + { + /* Correct it */ + State->GeneralRegs[FAST486_REG_EAX].LowWord -= 0x06; + State->GeneralRegs[FAST486_REG_EAX].HighByte--; + + /* Set CF and AF */ + State->Flags.Cf = State->Flags.Af = TRUE; + } + else + { + /* Clear CF and AF */ + State->Flags.Cf = State->Flags.Af = FALSE; + } + + /* Keep only the lowest 4 bits of AL */ + State->GeneralRegs[FAST486_REG_EAX].LowByte &= 0x0F; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushAll) +{ + INT i; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_REG SavedEsp = State->GeneralRegs[FAST486_REG_ESP]; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x60); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Push all the registers in order */ + for (i = 0; i < FAST486_NUM_GEN_REGS; i++) + { + if (i == FAST486_REG_ESP) + { + /* Use the saved ESP instead */ + if (!Fast486StackPush(State, Size ? SavedEsp.Long : SavedEsp.LowWord)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Push the register */ + if (!Fast486StackPush(State, Size ? State->GeneralRegs[i].Long + : State->GeneralRegs[i].LowWord)) + { + /* Exception occurred */ + return FALSE; + } + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePopAll) +{ + INT i; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + ULONG Value; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x61); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Pop all the registers in reverse order */ + for (i = FAST486_NUM_GEN_REGS - 1; i >= 0; i--) + { + /* Pop the value */ + if (!Fast486StackPop(State, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Don't modify ESP */ + if (i != FAST486_REG_ESP) + { + if (Size) State->GeneralRegs[i].Long = Value; + else State->GeneralRegs[i].LowWord = LOWORD(Value); + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeBound) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!ModRegRm.Memory) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + if (OperandSize) + { + LONG Index, LowerBound, UpperBound; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + (PULONG)&Index, + (PULONG)&LowerBound)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress + sizeof(ULONG), + FALSE, + &UpperBound, + sizeof(ULONG))) + { + /* Exception occurred */ + return FALSE; + } + + if ((Index < LowerBound) || (Index > UpperBound)) + { + /* Out of bounds */ + Fast486Exception(State, FAST486_EXCEPTION_BR); + return FALSE; + } + } + else + { + SHORT Index, LowerBound, UpperBound; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + (PUSHORT)&Index, + (PUSHORT)&LowerBound)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress + sizeof(USHORT), + FALSE, + &UpperBound, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + + if ((Index < LowerBound) || (Index > UpperBound)) + { + /* Out of bounds */ + Fast486Exception(State, FAST486_EXCEPTION_BR); + return FALSE; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeArpl) +{ + USHORT FirstValue, SecondValue; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm + || (State->PrefixFlags & FAST486_PREFIX_LOCK)) + { + /* Cannot be used in real mode or with a LOCK prefix */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if the RPL needs adjusting */ + if ((SecondValue & 3) < (FirstValue & 3)) + { + /* Adjust the RPL */ + SecondValue &= ~3; + SecondValue |= FirstValue & 3; + + /* Set ZF */ + State->Flags.Zf = TRUE; + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, SecondValue); + } + else + { + /* Clear ZF */ + State->Flags.Zf = FALSE; + return TRUE; + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushImm) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x68); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + ULONG Data; + + if (!Fast486FetchDword(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486StackPush(State, Data); + } + else + { + USHORT Data; + + if (!Fast486FetchWord(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486StackPush(State, Data); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeImulModrmImm) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + LONG Multiplier; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x69); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Fetch the parameters */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode == 0x6B) + { + CHAR Byte; + + /* Fetch the immediate operand */ + if (!Fast486FetchByte(State, (PUCHAR)&Byte)) + { + /* Exception occurred */ + return FALSE; + } + + Multiplier = (LONG)Byte; + } + else + { + if (OperandSize) + { + LONG Dword; + + /* Fetch the immediate operand */ + if (!Fast486FetchDword(State, (PULONG)&Dword)) + { + /* Exception occurred */ + return FALSE; + } + + Multiplier = Dword; + } + else + { + SHORT Word; + + /* Fetch the immediate operand */ + if (!Fast486FetchWord(State, (PUSHORT)&Word)) + { + /* Exception occurred */ + return FALSE; + } + + Multiplier = (LONG)Word; + } + } + + if (OperandSize) + { + LONG RegValue, Multiplicand; + LONGLONG Product; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + (PULONG)&RegValue, + (PULONG)&Multiplicand)) + { + /* Exception occurred */ + return FALSE; + } + + /* Multiply */ + Product = (LONGLONG)Multiplicand * (LONGLONG)Multiplier; + + /* Check for carry/overflow */ + State->Flags.Cf = State->Flags.Of = ((Product < MINLONG) || (Product > MAXLONG)); + + /* Write-back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + (ULONG)((LONG)Product)); + } + else + { + SHORT RegValue, Multiplicand; + LONG Product; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + (PUSHORT)&RegValue, + (PUSHORT)&Multiplicand)) + { + /* Exception occurred */ + return FALSE; + } + + /* Multiply */ + Product = (LONG)Multiplicand * (LONG)Multiplier; + + /* Check for carry/overflow */ + State->Flags.Cf = State->Flags.Of = ((Product < MINSHORT) || (Product > MAXSHORT)); + + /* Write-back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + TRUE, + (USHORT)((SHORT)Product)); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushByteImm) +{ + UCHAR Data; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x6A); + + if (!Fast486FetchByte(State, &Data)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the internal API */ + return Fast486StackPush(State, Data); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovByteModrm) +{ + UCHAR FirstValue, SecondValue, Result; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x88); + + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ReadModrmByteOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode & FAST486_OPCODE_WRITE_REG) Result = SecondValue; + else Result = FirstValue; + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovModrm) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFD) == 0x89); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check the operand size */ + if (OperandSize) + { + ULONG FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmDwordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode & FAST486_OPCODE_WRITE_REG) Result = SecondValue; + else Result = FirstValue; + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } + else + { + USHORT FirstValue, SecondValue, Result; + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + &FirstValue, + &SecondValue)) + { + /* Exception occurred */ + return FALSE; + } + + if (Opcode & FAST486_OPCODE_WRITE_REG) Result = SecondValue; + else Result = FirstValue; + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + Opcode & FAST486_OPCODE_WRITE_REG, + Result); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovStoreSeg) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x8C); + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register >= FAST486_NUM_SEG_REGS) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (OperandSize) + { + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + FALSE, + State->SegmentRegs[ModRegRm.Register].Selector); + } + else + { + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + State->SegmentRegs[ModRegRm.Register].Selector); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeLea) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x8D); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* The second operand must be memory */ + if (!ModRegRm.Memory) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Write the address to the register */ + if (OperandSize) + { + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + TRUE, + ModRegRm.MemoryAddress); + } + else + { + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + TRUE, + ModRegRm.MemoryAddress); + + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovLoadSeg) +{ + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x8E); + + TOGGLE_ADSIZE(AddressSize); + TOGGLE_OPSIZE(OperandSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if ((ModRegRm.Register >= FAST486_NUM_SEG_REGS) + || ((FAST486_SEG_REGS)ModRegRm.Register == FAST486_REG_CS)) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (OperandSize) + { + ULONG Selector; + + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Selector)) + { + /* Exception occurred */ + return FALSE; + } + + return Fast486LoadSegment(State, ModRegRm.Register, LOWORD(Selector)); + } + else + { + USHORT Selector; + + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Selector)) + { + /* Exception occurred */ + return FALSE; + } + + return Fast486LoadSegment(State, ModRegRm.Register, Selector); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCwde) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x98); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Size) + { + /* Sign extend AX to EAX */ + State->GeneralRegs[FAST486_REG_EAX].Long = MAKELONG + ( + State->GeneralRegs[FAST486_REG_EAX].LowWord, + (State->GeneralRegs[FAST486_REG_EAX].LowWord & SIGN_FLAG_WORD) + ? 0xFFFF : 0x0000 + ); + } + else + { + /* Sign extend AL to AX */ + State->GeneralRegs[FAST486_REG_EAX].HighByte = + (State->GeneralRegs[FAST486_REG_EAX].LowByte & SIGN_FLAG_BYTE) + ? 0xFF : 0x00; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCdq) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x99); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Size) + { + /* Sign extend EAX to EDX:EAX */ + State->GeneralRegs[FAST486_REG_EDX].Long = + (State->GeneralRegs[FAST486_REG_EAX].Long & SIGN_FLAG_LONG) + ? 0xFFFFFFFF : 0x00000000; + } + else + { + /* Sign extend AX to DX:AX */ + State->GeneralRegs[FAST486_REG_EDX].LowWord = + (State->GeneralRegs[FAST486_REG_EAX].LowWord & SIGN_FLAG_WORD) + ? 0xFFFF : 0x0000; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCallAbs) +{ + USHORT Segment = 0; + ULONG Offset = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x9A); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Fetch the offset */ + if (Size) + { + if (!Fast486FetchDword(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486FetchWord(State, (PUSHORT)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Fetch the segment */ + if (!Fast486FetchWord(State, &Segment)) + { + /* Exception occurred */ + return FALSE; + } + + /* Push the current code segment selector */ + if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Push the current value of the instruction pointer */ + if (!Fast486StackPush(State, State->InstPtr.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load new (E)IP */ + if (Size) State->InstPtr.Long = Offset; + else State->InstPtr.LowWord = LOWORD(Offset); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeWait) +{ + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return FALSE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePushFlags) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + /* Check for VM86 mode when IOPL is not 3 */ + if (State->Flags.Vm && (State->Flags.Iopl != 3)) + { + /* Call the VM86 monitor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0); + return FALSE; + } + + /* Push the flags */ + if (Size) return Fast486StackPush(State, State->Flags.Long); + else return Fast486StackPush(State, LOWORD(State->Flags.Long)); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodePopFlags) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + INT Cpl = Fast486GetCurrentPrivLevel(State); + FAST486_FLAGS_REG NewFlags; + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + /* Pop the new flags */ + if (!Fast486StackPop(State, &NewFlags.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check for VM86 mode when IOPL is not 3 */ + if (State->Flags.Vm && (State->Flags.Iopl != 3)) + { + /* Call the VM86 monitor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0); + return FALSE; + } + + State->Flags.Cf = NewFlags.Cf; + State->Flags.Pf = NewFlags.Pf; + State->Flags.Af = NewFlags.Af; + State->Flags.Zf = NewFlags.Zf; + State->Flags.Sf = NewFlags.Sf; + State->Flags.Tf = NewFlags.Tf; + State->Flags.Df = NewFlags.Df; + State->Flags.Of = NewFlags.Of; + State->Flags.Nt = NewFlags.Nt; + State->Flags.Ac = NewFlags.Ac; + + if (Cpl == 0) State->Flags.Iopl = NewFlags.Iopl; + if (Cpl <= State->Flags.Iopl) State->Flags.If = NewFlags.If; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSahf) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x9E); + + /* Set the low-order byte of FLAGS to AH */ + State->Flags.Long &= 0xFFFFFF00; + State->Flags.Long |= State->GeneralRegs[FAST486_REG_EAX].HighByte; + + /* Restore the reserved bits of FLAGS */ + State->Flags.AlwaysSet = TRUE; + State->Flags.Reserved0 = State->Flags.Reserved1 = FALSE; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeLahf) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0x9F); + + /* Set AH to the low-order byte of FLAGS */ + State->GeneralRegs[FAST486_REG_EAX].HighByte = LOBYTE(State->Flags.Long); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeRet) +{ + ULONG ReturnAddress; + USHORT BytesToPop = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xC2); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Opcode == 0xC2) + { + /* Fetch the number of bytes to pop after the return */ + if (!Fast486FetchWord(State, &BytesToPop)) return FALSE; + } + + /* Pop the return address */ + if (!Fast486StackPop(State, &ReturnAddress)) return FALSE; + + /* Return to the calling procedure, and if necessary, pop the parameters */ + if (Size) + { + State->InstPtr.Long = ReturnAddress; + State->GeneralRegs[FAST486_REG_ESP].Long += BytesToPop; + } + else + { + State->InstPtr.LowWord = LOWORD(ReturnAddress); + State->GeneralRegs[FAST486_REG_ESP].LowWord += BytesToPop; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeLdsLes) +{ + UCHAR FarPointer[6]; + BOOLEAN OperandSize, AddressSize; + FAST486_MOD_REG_RM ModRegRm; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xC4); + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the operands */ + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (!ModRegRm.Memory) + { + /* Check if this is a BOP and the host supports BOPs */ + if ((Opcode == 0xC4) + && (ModRegRm.Register == FAST486_REG_EAX) + && (ModRegRm.SecondRegister == FAST486_REG_ESP) + && (State->BopCallback != NULL)) + { + UCHAR BopCode; + + /* Fetch the BOP code */ + if (!Fast486FetchByte(State, &BopCode)) + { + /* Exception occurred */ + return FALSE; + } + + /* Call the BOP handler */ + State->BopCallback(State, BopCode); + + /* Return success */ + return TRUE; + } + + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (!Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) + ? State->SegmentOverride : FAST486_REG_DS, + ModRegRm.MemoryAddress, + FALSE, + FarPointer, + OperandSize ? 6 : 4)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Offset = *((PULONG)FarPointer); + USHORT Segment = *((PUSHORT)&FarPointer[sizeof(ULONG)]); + + /* Set the register to the offset */ + State->GeneralRegs[ModRegRm.Register].Long = Offset; + + /* Load the segment */ + return Fast486LoadSegment(State, + (Opcode == 0xC4) + ? FAST486_REG_ES : FAST486_REG_DS, + Segment); + } + else + { + USHORT Offset = *((PUSHORT)FarPointer); + USHORT Segment = *((PUSHORT)&FarPointer[sizeof(USHORT)]); + + /* Set the register to the offset */ + State->GeneralRegs[ModRegRm.Register].LowWord = Offset; + + /* Load the segment */ + return Fast486LoadSegment(State, + (Opcode == 0xC4) + ? FAST486_REG_ES : FAST486_REG_DS, + Segment); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeEnter) +{ + INT i; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + USHORT FrameSize; + UCHAR NestingLevel; + FAST486_REG FramePointer; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xC8); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (!Fast486FetchWord(State, &FrameSize)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486FetchByte(State, &NestingLevel)) + { + /* Exception occurred */ + return FALSE; + } + + /* Push EBP */ + if (!Fast486StackPush(State, State->GeneralRegs[FAST486_REG_EBP].Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Save ESP */ + FramePointer = State->GeneralRegs[FAST486_REG_ESP]; + + /* Set up the nested procedure stacks */ + for (i = 1; i < NestingLevel; i++) + { + if (Size) + { + State->GeneralRegs[FAST486_REG_EBP].Long -= 4; + Fast486StackPush(State, State->GeneralRegs[FAST486_REG_EBP].Long); + } + else + { + State->GeneralRegs[FAST486_REG_EBP].LowWord -= 2; + Fast486StackPush(State, State->GeneralRegs[FAST486_REG_EBP].LowWord); + } + } + + if (NestingLevel > 0) Fast486StackPush(State, FramePointer.Long); + + /* Set EBP to the frame pointer */ + State->GeneralRegs[FAST486_REG_EBP] = FramePointer; + + /* Reserve space for the frame */ + if (Size) State->GeneralRegs[FAST486_REG_ESP].Long -= (ULONG)FrameSize; + else State->GeneralRegs[FAST486_REG_ESP].LowWord -= FrameSize; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeLeave) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xC9); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + if (Size) + { + /* Set the stack pointer (ESP) to the base pointer (EBP) */ + State->GeneralRegs[FAST486_REG_ESP].Long = State->GeneralRegs[FAST486_REG_EBP].Long; + + /* Pop the saved base pointer from the stack */ + return Fast486StackPop(State, &State->GeneralRegs[FAST486_REG_EBP].Long); + } + else + { + ULONG Value; + + /* Set the stack pointer (SP) to the base pointer (BP) */ + State->GeneralRegs[FAST486_REG_ESP].LowWord = State->GeneralRegs[FAST486_REG_EBP].LowWord; + + /* Pop the saved base pointer from the stack */ + if (Fast486StackPop(State, &Value)) + { + State->GeneralRegs[FAST486_REG_EBP].LowWord = LOWORD(Value); + return TRUE; + } + else return FALSE; + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeRetFar) +{ + ULONG Segment = 0; + ULONG Offset = 0; + USHORT BytesToPop = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xCA); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Opcode == 0xCA) + { + /* Fetch the number of bytes to pop after the return */ + if (!Fast486FetchWord(State, &BytesToPop)) return FALSE; + } + + /* Pop the offset */ + if (!Fast486StackPop(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Pop the segment */ + if (!Fast486StackPop(State, &Segment)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load new (E)IP, and if necessary, pop the parameters */ + if (Size) + { + State->InstPtr.Long = Offset; + State->GeneralRegs[FAST486_REG_ESP].Long += BytesToPop; + } + else + { + State->InstPtr.LowWord = LOWORD(Offset); + State->GeneralRegs[FAST486_REG_ESP].LowWord += BytesToPop; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeInt) +{ + UCHAR IntNum; + FAST486_IDT_ENTRY IdtEntry; + + switch (Opcode) + { + case 0xCC: + { + /* This is the INT3 instruction */ + IntNum = 3; + break; + } + + case 0xCD: + { + /* Fetch the interrupt number */ + if (!Fast486FetchByte(State, &IntNum)) + { + /* Exception occurred */ + return FALSE; + } + + break; + } + + case 0xCE: + { + /* Don't do anything if OF is cleared */ + if (!State->Flags.Of) return TRUE; + + /* Exception #OF */ + IntNum = FAST486_EXCEPTION_OF; + + break; + } + + default: + { + /* Should not happen */ + ASSERT(FALSE); + } + } + + /* Get the interrupt vector */ + if (!Fast486GetIntVector(State, IntNum, &IdtEntry)) + { + /* Exception occurred */ + return FALSE; + } + + /* Perform the interrupt */ + if (!Fast486InterruptInternal(State, + IdtEntry.Selector, + MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh), + IdtEntry.Type)) + { + /* Exception occurred */ + return FALSE; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeIret) +{ + FAST486_SEG_REGS i; + ULONG InstPtr, CodeSel, StackPtr, StackSel; + FAST486_FLAGS_REG NewFlags; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xCF); + + NO_LOCK_PREFIX(); + TOGGLE_OPSIZE(Size); + + /* Pop EIP */ + if (!Fast486StackPop(State, &InstPtr)) + { + /* Exception occurred */ + return FALSE; + } + + /* Pop CS */ + if (!Fast486StackPop(State, &CodeSel)) + { + /* Exception occurred */ + return FALSE; + } + + /* Pop EFLAGS */ + if (!Fast486StackPop(State, &NewFlags.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check for protected mode */ + if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + { + INT Cpl = Fast486GetCurrentPrivLevel(State); + + if (State->Flags.Vm) + { + /* Return from VM86 mode */ + + /* Check the IOPL */ + if (State->Flags.Iopl == 3) + { + /* Set new EIP */ + State->InstPtr.Long = LOWORD(InstPtr); + + /* Load new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the new flags */ + if (Size) State->Flags.Long = NewFlags.Long & REAL_MODE_FLAGS_MASK; + else State->Flags.LowWord = NewFlags.LowWord & REAL_MODE_FLAGS_MASK; + State->Flags.AlwaysSet = State->Flags.Vm = TRUE; + State->Flags.Iopl = 3; + } + else + { + /* Call the VM86 monitor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0); + return FALSE; + } + + return TRUE; + } + + if (State->Flags.Nt) + { + /* Nested task return */ + + UNIMPLEMENTED; + return FALSE; + } + + if (NewFlags.Vm) + { + /* Return to VM86 mode */ + ULONG Es, Ds, Fs, Gs; + + /* Pop ESP, SS, ES, FS, GS */ + if (!Fast486StackPop(State, &StackPtr)) return FALSE; + if (!Fast486StackPop(State, &StackSel)) return FALSE; + if (!Fast486StackPop(State, &Es)) return FALSE; + if (!Fast486StackPop(State, &Ds)) return FALSE; + if (!Fast486StackPop(State, &Fs)) return FALSE; + if (!Fast486StackPop(State, &Gs)) return FALSE; + + /* Set the new IP */ + State->InstPtr.Long = LOWORD(InstPtr); + + /* Set the new flags */ + if (Size) State->Flags.Long = NewFlags.Long & REAL_MODE_FLAGS_MASK; + else State->Flags.LowWord = NewFlags.LowWord & REAL_MODE_FLAGS_MASK; + State->Flags.AlwaysSet = State->Flags.Vm = TRUE; + + /* Load the new segments */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel)) return FALSE; + if (!Fast486LoadSegment(State, FAST486_REG_SS, StackSel)) return FALSE; + if (!Fast486LoadSegment(State, FAST486_REG_ES, Es)) return FALSE; + if (!Fast486LoadSegment(State, FAST486_REG_DS, Ds)) return FALSE; + if (!Fast486LoadSegment(State, FAST486_REG_FS, Fs)) return FALSE; + if (!Fast486LoadSegment(State, FAST486_REG_GS, Gs)) return FALSE; + + return TRUE; + } + + /* Load the new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set EIP */ + if (Size) State->InstPtr.Long = InstPtr; + else State->InstPtr.LowWord = LOWORD(InstPtr); + + if (GET_SEGMENT_RPL(CodeSel) > Cpl) + { + /* Pop ESP */ + if (!Fast486StackPop(State, &StackPtr)) + { + /* Exception */ + return FALSE; + } + + /* Pop SS */ + if (!Fast486StackPop(State, &StackSel)) + { + /* Exception */ + return FALSE; + } + + /* Load new SS */ + if (!Fast486LoadSegment(State, FAST486_REG_SS, StackSel)) + { + /* Exception */ + return FALSE; + } + + /* Set ESP */ + if (Size) State->GeneralRegs[FAST486_REG_ESP].Long = StackPtr; + else State->GeneralRegs[FAST486_REG_ESP].LowWord = LOWORD(StackPtr); + } + + /* Set the new flags */ + if (Size) State->Flags.Long = NewFlags.Long & PROT_MODE_FLAGS_MASK; + else State->Flags.LowWord = NewFlags.LowWord & PROT_MODE_FLAGS_MASK; + State->Flags.AlwaysSet = TRUE; + + /* Set additional flags */ + if (Cpl <= State->Flags.Iopl) State->Flags.If = NewFlags.If; + if (Cpl == 0) State->Flags.Iopl = NewFlags.Iopl; + + if (GET_SEGMENT_RPL(CodeSel) > Cpl) + { + /* Update the CPL */ + Cpl = Fast486GetCurrentPrivLevel(State); + + /* Check segment security */ + for (i = 0; i < FAST486_NUM_SEG_REGS; i++) + { + /* Don't check CS or SS */ + if ((i == FAST486_REG_CS) || (i == FAST486_REG_SS)) continue; + + if ((Cpl > State->SegmentRegs[i].Dpl) + && (!State->SegmentRegs[i].Executable + || !State->SegmentRegs[i].DirConf)) + { + /* Load the NULL descriptor in the segment */ + if (!Fast486LoadSegment(State, i, 0)) return FALSE; + } + } + } + } + else + { + if (Size && (InstPtr & 0xFFFF0000)) + { + /* Invalid */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0); + return FALSE; + } + + /* Set new EIP */ + State->InstPtr.Long = InstPtr; + + /* Load new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the new flags */ + if (Size) State->Flags.Long = NewFlags.Long & REAL_MODE_FLAGS_MASK; + else State->Flags.LowWord = NewFlags.LowWord & REAL_MODE_FLAGS_MASK; + State->Flags.AlwaysSet = TRUE; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAam) +{ + UCHAR Base; + UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + NO_LOCK_PREFIX(); + + /* Fetch the base */ + if (!Fast486FetchByte(State, &Base)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check if the base is zero */ + if (Base == 0) + { + /* Divide error */ + Fast486Exception(State, FAST486_EXCEPTION_DE); + return FALSE; + } + + /* Adjust */ + State->GeneralRegs[FAST486_REG_EAX].HighByte = Value / Base; + State->GeneralRegs[FAST486_REG_EAX].LowByte = Value %= Base; + + /* Update flags */ + State->Flags.Af = FALSE; + State->Flags.Zf = (Value == 0); + State->Flags.Sf = ((Value & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeAad) +{ + UCHAR Base; + UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte; + + NO_LOCK_PREFIX(); + + /* Fetch the base */ + if (!Fast486FetchByte(State, &Base)) + { + /* Exception occurred */ + return FALSE; + } + + /* Adjust */ + Value += State->GeneralRegs[FAST486_REG_EAX].HighByte * Base; + State->GeneralRegs[FAST486_REG_EAX].LowWord = Value; + + /* Update flags */ + State->Flags.Af = FALSE; + State->Flags.Zf = (Value == 0); + State->Flags.Sf = ((Value & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeXlat) +{ + UCHAR Value; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + /* Read a byte from DS:[(E)BX + AL] */ + if (!Fast486ReadMemory(State, + FAST486_REG_DS, + (AddressSize ? State->GeneralRegs[FAST486_REG_EBX].Long + : State->GeneralRegs[FAST486_REG_EBX].LowWord) + + State->GeneralRegs[FAST486_REG_EAX].LowByte, + FALSE, + &Value, + sizeof(UCHAR))) + { + /* Exception occurred */ + return FALSE; + } + + /* Set AL to the result */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Value; + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeLoop) +{ + BOOLEAN Condition; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + CHAR Offset = 0; + + /* Make sure this is the right instruction */ + ASSERT((Opcode >= 0xE0) && (Opcode <= 0xE2)); + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(Size); + + if (Size) Condition = ((--State->GeneralRegs[FAST486_REG_ECX].Long) != 0); + else Condition = ((--State->GeneralRegs[FAST486_REG_ECX].LowWord) != 0); + + if (Opcode == 0xE0) + { + /* Additional rule for LOOPNZ */ + if (State->Flags.Zf) Condition = FALSE; + } + + if (Opcode == 0xE1) + { + /* Additional rule for LOOPZ */ + if (!State->Flags.Zf) Condition = FALSE; + } + + /* Fetch the offset */ + if (!Fast486FetchByte(State, (PUCHAR)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + if (Condition) + { + /* Move the instruction pointer */ + if (Size) State->InstPtr.Long += Offset; + else State->InstPtr.LowWord += Offset; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeJecxz) +{ + BOOLEAN Condition; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + CHAR Offset = 0; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xE3); + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(Size); + + if (Size) Condition = (State->GeneralRegs[FAST486_REG_ECX].Long == 0); + else Condition = (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0); + + /* Fetch the offset */ + if (!Fast486FetchByte(State, (PUCHAR)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + if (Condition) + { + /* Move the instruction pointer */ + if (Size) State->InstPtr.Long += Offset; + else State->InstPtr.LowWord += Offset; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCall) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xE8); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Size) + { + LONG Offset = 0; + + /* Fetch the offset */ + if (!Fast486FetchDword(State, (PULONG)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + /* Push the current value of the instruction pointer */ + if (!Fast486StackPush(State, State->InstPtr.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Move the instruction pointer */ + State->InstPtr.Long += Offset; + } + else + { + SHORT Offset = 0; + + /* Fetch the offset */ + if (!Fast486FetchWord(State, (PUSHORT)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + /* Push the current value of the instruction pointer */ + if (!Fast486StackPush(State, State->InstPtr.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Move the instruction pointer */ + State->InstPtr.LowWord += Offset; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeJmp) +{ + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xE9); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + if (Size) + { + LONG Offset = 0; + + /* Fetch the offset */ + if (!Fast486FetchDword(State, (PULONG)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + /* Move the instruction pointer */ + State->InstPtr.Long += Offset; + } + else + { + SHORT Offset = 0; + + /* Fetch the offset */ + if (!Fast486FetchWord(State, (PUSHORT)&Offset)) + { + /* An exception occurred */ + return FALSE; + } + + /* Move the instruction pointer */ + State->InstPtr.Long += Offset; + + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeJmpAbs) +{ + USHORT Segment = 0; + ULONG Offset = 0; + BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xEA); + + TOGGLE_OPSIZE(Size); + NO_LOCK_PREFIX(); + + /* Fetch the offset */ + if (Size) + { + if (!Fast486FetchDword(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + if (!Fast486FetchWord(State, (PUSHORT)&Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Fetch the segment */ + if (!Fast486FetchWord(State, &Segment)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new CS */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load new EIP */ + State->InstPtr.Long = Offset; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovAlOffset) +{ + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + ULONG Offset; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xA0); + + TOGGLE_ADSIZE(AddressSize); + + if (AddressSize) + { + if (!Fast486FetchDword(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT WordOffset; + + if (!Fast486FetchWord(State, &WordOffset)) + { + /* Exception occurred */ + return FALSE; + } + + Offset = (ULONG)WordOffset; + } + + /* Read from memory */ + return Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + FALSE, + &State->GeneralRegs[FAST486_REG_EAX].LowByte, + sizeof(UCHAR)); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovEaxOffset) +{ + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xA1); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (AddressSize) + { + ULONG Offset; + + if (!Fast486FetchDword(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read from memory */ + if (OperandSize) + { + return Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + FALSE, + &State->GeneralRegs[FAST486_REG_EAX].Long, + sizeof(ULONG)); + } + else + { + return Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + FALSE, + &State->GeneralRegs[FAST486_REG_EAX].LowWord, + sizeof(USHORT)); + } + } + else + { + USHORT Offset; + + if (!Fast486FetchWord(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read from memory */ + if (OperandSize) + { + return Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + FALSE, + &State->GeneralRegs[FAST486_REG_EAX].Long, + sizeof(ULONG)); + } + else + { + return Fast486ReadMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + FALSE, + &State->GeneralRegs[FAST486_REG_EAX].LowWord, + sizeof(USHORT)); + } + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovOffsetAl) +{ + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + ULONG Offset; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xA2); + + TOGGLE_ADSIZE(AddressSize); + + if (AddressSize) + { + if (!Fast486FetchDword(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + USHORT WordOffset; + + if (!Fast486FetchWord(State, &WordOffset)) + { + /* Exception occurred */ + return FALSE; + } + + Offset = (ULONG)WordOffset; + } + + /* Write to memory */ + return Fast486WriteMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + &State->GeneralRegs[FAST486_REG_EAX].LowByte, + sizeof(UCHAR)); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovOffsetEax) +{ + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xA3); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (AddressSize) + { + ULONG Offset; + + if (!Fast486FetchDword(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write to memory */ + if (OperandSize) + { + return Fast486WriteMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + &State->GeneralRegs[FAST486_REG_EAX].Long, + sizeof(ULONG)); + } + else + { + return Fast486WriteMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + &State->GeneralRegs[FAST486_REG_EAX].LowWord, + sizeof(USHORT)); + } + } + else + { + USHORT Offset; + + if (!Fast486FetchWord(State, &Offset)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write to memory */ + if (OperandSize) + { + return Fast486WriteMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + &State->GeneralRegs[FAST486_REG_EAX].Long, + sizeof(ULONG)); + } + else + { + return Fast486WriteMemory(State, + (State->PrefixFlags & FAST486_PREFIX_SEG) ? + State->SegmentOverride : FAST486_REG_DS, + Offset, + &State->GeneralRegs[FAST486_REG_EAX].LowWord, + sizeof(USHORT)); + } + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeSalc) +{ + /* Make sure this is the right instruction */ + ASSERT(Opcode == 0xD6); + + NO_LOCK_PREFIX(); + + /* Set all the bits of AL to CF */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = State->Flags.Cf ? 0xFF : 0x00; + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeMovs) +{ + ULONG Data, DataSize; + BOOLEAN OperandSize, AddressSize; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xA4); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead of DS */ + Segment = State->SegmentOverride; + } + + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + if ((AddressSize && (State->GeneralRegs[FAST486_REG_ECX].Long == 0)) + || (!AddressSize && (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0))) + { + /* Do nothing */ + return TRUE; + } + } + + /* Calculate the size */ + if (Opcode == 0xA4) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + /* Read from the source operand */ + if (!Fast486ReadMemory(State, + Segment, + AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long + : State->GeneralRegs[FAST486_REG_ESI].LowWord, + FALSE, + &Data, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write to the destination operand */ + if (!Fast486WriteMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + &Data, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Increment/decrement ESI and EDI */ + if (AddressSize) + { + if (!State->Flags.Df) + { + State->GeneralRegs[FAST486_REG_ESI].Long += DataSize; + State->GeneralRegs[FAST486_REG_EDI].Long += DataSize; + } + else + { + State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize; + State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize; + } + } + else + { + if (!State->Flags.Df) + { + State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize; + State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize; + } + else + { + State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize; + State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize; + } + } + + // FIXME: This method is slow! + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + if (AddressSize) + { + if (--State->GeneralRegs[FAST486_REG_ECX].Long) + { + /* Repeat the instruction */ + State->InstPtr = State->SavedInstPtr; + } + } + else + { + if (--State->GeneralRegs[FAST486_REG_ECX].LowWord) + { + /* Repeat the instruction */ + State->InstPtr = State->SavedInstPtr; + } + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeCmps) +{ + ULONG FirstValue = 0, SecondValue = 0, Result; + ULONG DataSize, DataMask, SignFlag; + BOOLEAN OperandSize, AddressSize; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xA6); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead of DS */ + Segment = State->SegmentOverride; + } + + if ((State->PrefixFlags & FAST486_PREFIX_REP) + || (State->PrefixFlags & FAST486_PREFIX_REPNZ)) + { + if ((AddressSize && (State->GeneralRegs[FAST486_REG_ECX].Long == 0)) + || (!AddressSize && (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0))) + { + /* Do nothing */ + return TRUE; + } + } + + /* Calculate the size */ + if (Opcode == 0xA6) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + /* Calculate the mask and sign flag */ + SignFlag = 1 << ((DataSize * 8) - 1); + DataMask = SignFlag | (SignFlag - 1); + + /* Read from the first source operand */ + if (!Fast486ReadMemory(State, + Segment, + AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long + : State->GeneralRegs[FAST486_REG_ESI].LowWord, + FALSE, + &FirstValue, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read from the second source operand */ + if (!Fast486ReadMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + FALSE, + &SecondValue, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + FirstValue &= DataMask; + SecondValue &= DataMask; + Result = (FirstValue - SecondValue) & DataMask; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SignFlag) != (SecondValue & SignFlag)) + && ((FirstValue & SignFlag) != (Result & SignFlag)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SignFlag) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Increment/decrement ESI and EDI */ + if (AddressSize) + { + if (!State->Flags.Df) + { + State->GeneralRegs[FAST486_REG_ESI].Long += DataSize; + State->GeneralRegs[FAST486_REG_EDI].Long += DataSize; + } + else + { + State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize; + State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize; + } + } + else + { + if (!State->Flags.Df) + { + State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize; + State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize; + } + else + { + State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize; + State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize; + } + } + + // FIXME: This method is slow! + if ((State->PrefixFlags & FAST486_PREFIX_REP) + || (State->PrefixFlags & FAST486_PREFIX_REPNZ)) + { + BOOLEAN Repeat = TRUE; + + if (AddressSize) + { + if ((--State->GeneralRegs[FAST486_REG_ECX].Long) == 0) + { + /* ECX is 0 */ + Repeat = FALSE; + } + } + else + { + if ((--State->GeneralRegs[FAST486_REG_ECX].LowWord) == 0) + { + /* CX is 0 */ + Repeat = FALSE; + } + } + + if (((State->PrefixFlags & FAST486_PREFIX_REP) && !State->Flags.Zf) + || ((State->PrefixFlags & FAST486_PREFIX_REPNZ) && State->Flags.Zf)) + { + /* REPZ with ZF = 0 or REPNZ with ZF = 1 */ + Repeat = FALSE; + } + + if (Repeat) + { + /* Repeat the instruction */ + State->InstPtr = State->SavedInstPtr; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeStos) +{ + ULONG DataSize; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xAA); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Calculate the size */ + if (Opcode == 0xAA) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + UCHAR Block[STRING_BLOCK_SIZE]; + ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long + : State->GeneralRegs[FAST486_REG_ECX].LowWord; + + /* Fill the memory block with the data */ + if (DataSize == sizeof(UCHAR)) + { + RtlFillMemory(Block, sizeof(Block), State->GeneralRegs[FAST486_REG_EAX].LowByte); + } + else + { + ULONG i; + + for (i = 0; i < STRING_BLOCK_SIZE / DataSize; i++) + { + if (DataSize == sizeof(USHORT)) + { + ((PUSHORT)Block)[i] = State->GeneralRegs[FAST486_REG_EAX].LowWord; + } + else + { + ((PULONG)Block)[i] = State->GeneralRegs[FAST486_REG_EAX].Long; + } + } + } + + /* Transfer until finished */ + while (Count) + { + ULONG Processed = min(Count, STRING_BLOCK_SIZE / DataSize); + + /* Simulate the 16-bit wrap-around of DI in 16-bit address mode */ + if (!AddressSize) + { + ULONG MaxBytes = State->Flags.Df + ? (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord + : (0x10000 - (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord); + + Processed = min(Processed, MaxBytes / DataSize); + if (Processed == 0) Processed = 1; + } + + if (State->Flags.Df) + { + /* Set EDI to the starting location */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= (Processed - 1) * DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= (Processed - 1) * DataSize; + } + + /* Write to memory */ + if (!Fast486WriteMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + Block, + Processed * DataSize)) + { + /* Set ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = Count; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = LOWORD(Count); + + /* Exception occurred */ + return FALSE; + } + + if (!State->Flags.Df) + { + /* Increase EDI by the number of bytes transfered */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long += Processed * DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord += Processed * DataSize; + } + else + { + /* Reduce EDI */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize; + } + + /* Reduce the total count by the number processed in this run */ + Count -= Processed; + } + + /* Clear ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0; + } + else + { + /* Write to the destination operand */ + if (!Fast486WriteMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + &State->GeneralRegs[FAST486_REG_EAX].Long, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Increment/decrement EDI */ + if (AddressSize) + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].Long += DataSize; + else State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize; + } + else + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeLods) +{ + ULONG DataSize; + BOOLEAN OperandSize, AddressSize; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xAC); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead of DS */ + Segment = State->SegmentOverride; + } + + /* Calculate the size */ + if (Opcode == 0xAC) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long + : State->GeneralRegs[FAST486_REG_ECX].LowWord; + + /* If the count is 0, do nothing */ + if (Count == 0) return TRUE; + + /* Only the last entry will be loaded */ + if (!State->Flags.Df) + { + if (AddressSize) State->GeneralRegs[FAST486_REG_ESI].Long += (Count - 1) * DataSize; + else State->GeneralRegs[FAST486_REG_ESI].LowWord += (Count - 1) * DataSize; + } + else + { + if (AddressSize) State->GeneralRegs[FAST486_REG_ESI].Long -= (Count - 1) * DataSize; + else State->GeneralRegs[FAST486_REG_ESI].LowWord -= (Count - 1) * DataSize; + } + + /* Clear ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0; + } + + /* Read from the source operand */ + if (!Fast486ReadMemory(State, + Segment, + AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long + : State->GeneralRegs[FAST486_REG_ESI].LowWord, + FALSE, + &State->GeneralRegs[FAST486_REG_EAX].Long, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Increment/decrement ESI */ + if (AddressSize) + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].Long += DataSize; + else State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize; + } + else + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize; + else State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize; + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeScas) +{ + ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long; + ULONG SecondValue = 0; + ULONG Result; + ULONG DataSize, DataMask, SignFlag; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0xAE); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if ((State->PrefixFlags & FAST486_PREFIX_REP) + || (State->PrefixFlags & FAST486_PREFIX_REPNZ)) + { + if ((AddressSize && (State->GeneralRegs[FAST486_REG_ECX].Long == 0)) + || (!AddressSize && (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0))) + { + /* Do nothing */ + return TRUE; + } + } + + /* Calculate the size */ + if (Opcode == 0xAE) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + /* Calculate the mask and sign flag */ + SignFlag = 1 << ((DataSize * 8) - 1); + DataMask = SignFlag | (SignFlag - 1); + + /* Read from the source operand */ + if (!Fast486ReadMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + FALSE, + &SecondValue, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + FirstValue &= DataMask; + SecondValue &= DataMask; + Result = (FirstValue - SecondValue) & DataMask; + + /* Update the flags */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SignFlag) != (SecondValue & SignFlag)) + && ((FirstValue & SignFlag) != (Result & SignFlag)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SignFlag) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Increment/decrement EDI */ + if (AddressSize) + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].Long += DataSize; + else State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize; + } + else + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize; + } + + // FIXME: This method is slow! + if ((State->PrefixFlags & FAST486_PREFIX_REP) + || (State->PrefixFlags & FAST486_PREFIX_REPNZ)) + { + BOOLEAN Repeat = TRUE; + + if (AddressSize) + { + if ((--State->GeneralRegs[FAST486_REG_ECX].Long) == 0) + { + /* ECX is 0 */ + Repeat = FALSE; + } + } + else + { + if ((--State->GeneralRegs[FAST486_REG_ECX].LowWord) == 0) + { + /* CX is 0 */ + Repeat = FALSE; + } + } + + if (((State->PrefixFlags & FAST486_PREFIX_REP) && !State->Flags.Zf) + || ((State->PrefixFlags & FAST486_PREFIX_REPNZ) && State->Flags.Zf)) + { + /* REPZ with ZF = 0 or REPNZ with ZF = 1 */ + Repeat = FALSE; + } + + if (Repeat) + { + /* Repeat the instruction */ + State->InstPtr = State->SavedInstPtr; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeIns) +{ + ULONG DataSize; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0x6C); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Calculate the size */ + if (Opcode == 0x6C) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + UCHAR Block[STRING_BLOCK_SIZE]; + ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long + : State->GeneralRegs[FAST486_REG_ECX].LowWord; + + /* Clear the memory block */ + RtlZeroMemory(Block, sizeof(Block)); + + /* Transfer until finished */ + while (Count) + { + ULONG Processed = min(Count, STRING_BLOCK_SIZE / DataSize); + + /* Simulate the 16-bit wrap-around of DI in 16-bit address mode */ + if (!AddressSize) + { + ULONG MaxBytes = State->Flags.Df + ? (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord + : (0x10000 - (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord); + + Processed = min(Processed, MaxBytes / DataSize); + if (Processed == 0) Processed = 1; + } + + /* Read from the I/O port */ + State->IoReadCallback(State, + State->GeneralRegs[FAST486_REG_EDX].LowWord, + Block, + Processed, + DataSize); + + if (State->Flags.Df) + { + ULONG i, j; + + /* Reduce EDI by the number of bytes to transfer */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= Processed * DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= Processed * DataSize; + + /* Reverse the block data */ + for (i = 0; i < Processed / 2; i++) + { + /* Swap the values */ + for (j = 0; j < DataSize; j++) + { + UCHAR Temp = Block[i * DataSize + j]; + Block[i * DataSize + j] = Block[(Processed - i - 1) * DataSize + j]; + Block[(Processed - i - 1) * DataSize + j] = Temp; + } + } + } + + /* Write to memory */ + if (!Fast486WriteMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + Block, + Processed * DataSize)) + { + /* Set ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = Count; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = LOWORD(Count); + + /* Exception occurred */ + return FALSE; + } + + if (!State->Flags.Df) + { + /* Increase EDI by the number of bytes transfered */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long += Processed * DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord += Processed * DataSize; + } + + /* Reduce the total count by the number processed in this run */ + Count -= Processed; + } + + /* Clear ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0; + } + else + { + ULONG Data = 0; + + /* Read from the I/O port */ + State->IoReadCallback(State, + State->GeneralRegs[FAST486_REG_EDX].LowWord, + &Data, + 1, + DataSize); + + /* Write to the destination operand */ + if (!Fast486WriteMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + &Data, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Increment/decrement EDI */ + if (AddressSize) + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].Long += DataSize; + else State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize; + } + else + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize; + } + } + + /* Return success */ + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeOuts) +{ + ULONG DataSize; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + /* Make sure this is the right instruction */ + ASSERT((Opcode & 0xFE) == 0x6E); + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Calculate the size */ + if (Opcode == 0x6E) DataSize = sizeof(UCHAR); + else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT); + + if (State->PrefixFlags & FAST486_PREFIX_REP) + { + UCHAR Block[STRING_BLOCK_SIZE]; + ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long + : State->GeneralRegs[FAST486_REG_ECX].LowWord; + + /* Clear the memory block */ + RtlZeroMemory(Block, sizeof(Block)); + + /* Transfer until finished */ + while (Count) + { + ULONG Processed = min(Count, STRING_BLOCK_SIZE / DataSize); + + /* Simulate the 16-bit wrap-around of DI in 16-bit address mode */ + if (!AddressSize) + { + ULONG MaxBytes = State->Flags.Df + ? (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord + : (0x10000 - (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord); + + Processed = min(Processed, MaxBytes / DataSize); + if (Processed == 0) Processed = 1; + } + + /* Read from memory */ + if (!Fast486ReadMemory(State, + FAST486_REG_ES, + AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long + : State->GeneralRegs[FAST486_REG_EDI].LowWord, + FALSE, + Block, + Processed * DataSize)) + { + /* Set ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = Count; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = LOWORD(Count); + + /* Exception occurred */ + return FALSE; + } + + if (State->Flags.Df) + { + ULONG i, j; + + /* Reduce EDI by the number of bytes to transfer */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= Processed * DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord -= Processed * DataSize; + + /* Reverse the block data */ + for (i = 0; i < Processed / 2; i++) + { + /* Swap the values */ + for (j = 0; j < DataSize; j++) + { + UCHAR Temp = Block[i * DataSize + j]; + Block[i * DataSize + j] = Block[(Processed - i - 1) * DataSize + j]; + Block[(Processed - i - 1) * DataSize + j] = Temp; + } + } + } + + /* Write to the I/O port */ + State->IoWriteCallback(State, + State->GeneralRegs[FAST486_REG_EDX].LowWord, + Block, + Processed, + DataSize); + + if (!State->Flags.Df) + { + /* Increase EDI by the number of bytes transfered */ + if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long += Processed * DataSize; + else State->GeneralRegs[FAST486_REG_EDI].LowWord += Processed * DataSize; + } + + /* Reduce the total count by the number processed in this run */ + Count -= Processed; + } + + /* Clear ECX */ + if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0; + else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0; + } + else + { + ULONG Data = 0; + + /* Read from the source operand */ + if (!Fast486ReadMemory(State, + FAST486_REG_DS, + AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long + : State->GeneralRegs[FAST486_REG_ESI].LowWord, + FALSE, + &Data, + DataSize)) + { + /* Exception occurred */ + return FALSE; + } + + /* Write to the I/O port */ + State->IoWriteCallback(State, + State->GeneralRegs[FAST486_REG_EDX].LowWord, + &Data, + 1, + DataSize); + + /* Increment/decrement ESI */ + if (AddressSize) + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].Long += DataSize; + else State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize; + } + else + { + if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize; + else State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize; + } + } + + /* Return success */ + return TRUE; +} diff --git a/reactos/lib/fast486/opcodes.h b/reactos/lib/fast486/opcodes.h new file mode 100644 index 0000000000000..3d29e4794c75b --- /dev/null +++ b/reactos/lib/fast486/opcodes.h @@ -0,0 +1,157 @@ +/* + * Fast486 386/486 CPU Emulation Library + * opcodes.h + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _OPCODES_H_ +#define _OPCODES_H_ + +#pragma once + +/* DEFINES ********************************************************************/ + +#define FAST486_NUM_OPCODE_HANDLERS 256 +#define FAST486_OPCODE_WRITE_REG (1 << 1) +#define FAST486_OPCODE_HANDLER(x) \ + BOOLEAN FASTCALL x(PFAST486_STATE State, UCHAR Opcode) + +typedef BOOLEAN (FASTCALL *FAST486_OPCODE_HANDLER_PROC)(PFAST486_STATE, UCHAR); + +extern +FAST486_OPCODE_HANDLER_PROC +Fast486OpcodeHandlers[FAST486_NUM_OPCODE_HANDLERS]; + +FAST486_OPCODE_HANDLER(Fast486OpcodePrefix); +FAST486_OPCODE_HANDLER(Fast486OpcodeIncrement); +FAST486_OPCODE_HANDLER(Fast486OpcodeDecrement); +FAST486_OPCODE_HANDLER(Fast486OpcodePushReg); +FAST486_OPCODE_HANDLER(Fast486OpcodePopReg); +FAST486_OPCODE_HANDLER(Fast486OpcodeNop); +FAST486_OPCODE_HANDLER(Fast486OpcodeExchangeEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeShortConditionalJmp); +FAST486_OPCODE_HANDLER(Fast486OpcodeClearCarry); +FAST486_OPCODE_HANDLER(Fast486OpcodeSetCarry); +FAST486_OPCODE_HANDLER(Fast486OpcodeComplCarry); +FAST486_OPCODE_HANDLER(Fast486OpcodeClearInt); +FAST486_OPCODE_HANDLER(Fast486OpcodeSetInt); +FAST486_OPCODE_HANDLER(Fast486OpcodeClearDir); +FAST486_OPCODE_HANDLER(Fast486OpcodeSetDir); +FAST486_OPCODE_HANDLER(Fast486OpcodeHalt); +FAST486_OPCODE_HANDLER(Fast486OpcodeInByte); +FAST486_OPCODE_HANDLER(Fast486OpcodeIn); +FAST486_OPCODE_HANDLER(Fast486OpcodeOutByte); +FAST486_OPCODE_HANDLER(Fast486OpcodeOut); +FAST486_OPCODE_HANDLER(Fast486OpcodeShortJump); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovRegImm); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovByteRegImm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAddByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAddModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAddAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeAddEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeOrByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeOrModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeOrAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeOrEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeAndByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAndModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAndAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeAndEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeXorByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeXorModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeXorAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeXorEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeTestByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeTestModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeTestAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeTestEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeXchgByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeXchgModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodePushEs); +FAST486_OPCODE_HANDLER(Fast486OpcodePopEs); +FAST486_OPCODE_HANDLER(Fast486OpcodePushCs); +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeAdcEax); +FAST486_OPCODE_HANDLER(Fast486OpcodePushSs); +FAST486_OPCODE_HANDLER(Fast486OpcodePopSs); +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeSbbEax); +FAST486_OPCODE_HANDLER(Fast486OpcodePushDs); +FAST486_OPCODE_HANDLER(Fast486OpcodePopDs); +FAST486_OPCODE_HANDLER(Fast486OpcodeDaa); +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeDas); +FAST486_OPCODE_HANDLER(Fast486OpcodeAaa); +FAST486_OPCODE_HANDLER(Fast486OpcodeAas); +FAST486_OPCODE_HANDLER(Fast486OpcodePushAll); +FAST486_OPCODE_HANDLER(Fast486OpcodePopAll); +FAST486_OPCODE_HANDLER(Fast486OpcodeBound); +FAST486_OPCODE_HANDLER(Fast486OpcodeArpl); +FAST486_OPCODE_HANDLER(Fast486OpcodePushImm); +FAST486_OPCODE_HANDLER(Fast486OpcodeImulModrmImm); +FAST486_OPCODE_HANDLER(Fast486OpcodePushByteImm); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovByteModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovModrm); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovStoreSeg); +FAST486_OPCODE_HANDLER(Fast486OpcodeLea); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovLoadSeg); +FAST486_OPCODE_HANDLER(Fast486OpcodeCwde); +FAST486_OPCODE_HANDLER(Fast486OpcodeCdq); +FAST486_OPCODE_HANDLER(Fast486OpcodeCallAbs); +FAST486_OPCODE_HANDLER(Fast486OpcodeWait); +FAST486_OPCODE_HANDLER(Fast486OpcodePushFlags); +FAST486_OPCODE_HANDLER(Fast486OpcodePopFlags); +FAST486_OPCODE_HANDLER(Fast486OpcodeSahf); +FAST486_OPCODE_HANDLER(Fast486OpcodeLahf); +FAST486_OPCODE_HANDLER(Fast486OpcodeRet); +FAST486_OPCODE_HANDLER(Fast486OpcodeLdsLes); +FAST486_OPCODE_HANDLER(Fast486OpcodeEnter); +FAST486_OPCODE_HANDLER(Fast486OpcodeLeave); +FAST486_OPCODE_HANDLER(Fast486OpcodeRetFarImm); +FAST486_OPCODE_HANDLER(Fast486OpcodeRetFar); +FAST486_OPCODE_HANDLER(Fast486OpcodeInt); +FAST486_OPCODE_HANDLER(Fast486OpcodeIret); +FAST486_OPCODE_HANDLER(Fast486OpcodeAam); +FAST486_OPCODE_HANDLER(Fast486OpcodeAad); +FAST486_OPCODE_HANDLER(Fast486OpcodeXlat); +FAST486_OPCODE_HANDLER(Fast486OpcodeLoop); +FAST486_OPCODE_HANDLER(Fast486OpcodeJecxz); +FAST486_OPCODE_HANDLER(Fast486OpcodeCall); +FAST486_OPCODE_HANDLER(Fast486OpcodeJmp); +FAST486_OPCODE_HANDLER(Fast486OpcodeJmpAbs); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovAlOffset); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovEaxOffset); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovOffsetAl); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovOffsetEax); +FAST486_OPCODE_HANDLER(Fast486OpcodeSalc); +FAST486_OPCODE_HANDLER(Fast486OpcodeMovs); +FAST486_OPCODE_HANDLER(Fast486OpcodeCmps); +FAST486_OPCODE_HANDLER(Fast486OpcodeStos); +FAST486_OPCODE_HANDLER(Fast486OpcodeLods); +FAST486_OPCODE_HANDLER(Fast486OpcodeScas); +FAST486_OPCODE_HANDLER(Fast486OpcodeIns); +FAST486_OPCODE_HANDLER(Fast486OpcodeOuts); + +#endif // _OPCODES_H_ diff --git a/reactos/lib/fast486/opgroups.c b/reactos/lib/fast486/opgroups.c new file mode 100644 index 0000000000000..264fa66ba2cf6 --- /dev/null +++ b/reactos/lib/fast486/opgroups.c @@ -0,0 +1,2370 @@ +/* + * Fast486 386/486 CPU Emulation Library + * opgroups.c + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* INCLUDES *******************************************************************/ + +#include + +// #define NDEBUG +#include + +#include +#include "opcodes.h" +#include "common.h" + +/* PRIVATE FUNCTIONS **********************************************************/ + +inline +static +ULONG +Fast486ArithmeticOperation(PFAST486_STATE State, + INT Operation, + ULONG FirstValue, + ULONG SecondValue, + UCHAR Bits) +{ + ULONG Result; + ULONG SignFlag = 1 << (Bits - 1); + ULONG MaxValue = (SignFlag - 1) | SignFlag; + + /* Make sure the values don't exceed the maximum for their size */ + FirstValue &= MaxValue; + SecondValue &= MaxValue; + + /* Check which operation is this */ + switch (Operation) + { + /* ADD */ + case 0: + { + Result = (FirstValue + SecondValue) & MaxValue; + + /* Update CF, OF and AF */ + State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue); + State->Flags.Of = ((FirstValue & SignFlag) == (SecondValue & SignFlag)) + && ((FirstValue & SignFlag) != (Result & SignFlag)); + State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0); + + break; + } + + /* OR */ + case 1: + { + Result = FirstValue | SecondValue; + break; + } + + /* ADC */ + case 2: + { + INT Carry = State->Flags.Cf ? 1 : 0; + + Result = (FirstValue + SecondValue + Carry) & MaxValue; + + /* Update CF, OF and AF */ + State->Flags.Cf = ((SecondValue == MaxValue) && (Carry == 1)) + || ((Result < FirstValue) && (Result < (SecondValue + Carry))); + State->Flags.Of = ((FirstValue & SignFlag) == (SecondValue & SignFlag)) + && ((FirstValue & SignFlag) != (Result & SignFlag)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + + break; + } + + /* SBB */ + case 3: + { + INT Carry = State->Flags.Cf ? 1 : 0; + + Result = (FirstValue - SecondValue - Carry) & MaxValue; + + /* Update CF, OF and AF */ + State->Flags.Cf = Carry + ? (FirstValue <= SecondValue) + : (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SignFlag) != (SecondValue & SignFlag)) + && ((FirstValue & SignFlag) != (Result & SignFlag)); + State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0; + + break; + } + + /* AND */ + case 4: + { + Result = FirstValue & SecondValue; + break; + } + + /* SUB or CMP */ + case 5: + case 7: + { + Result = (FirstValue - SecondValue) & MaxValue; + + /* Update CF, OF and AF */ + State->Flags.Cf = (FirstValue < SecondValue); + State->Flags.Of = ((FirstValue & SignFlag) != (SecondValue & SignFlag)) + && ((FirstValue & SignFlag) != (Result & SignFlag)); + State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F); + + break; + } + + /* XOR */ + case 6: + { + Result = FirstValue ^ SecondValue; + break; + } + + default: + { + /* Shouldn't happen */ + ASSERT(FALSE); + } + } + + /* Update ZF, SF and PF */ + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SignFlag) != 0); + State->Flags.Pf = Fast486CalculateParity(LOBYTE(Result)); + + /* Return the result */ + return Result; +} + +static +inline +ULONG +Fast486RotateOperation(PFAST486_STATE State, + INT Operation, + ULONG Value, + UCHAR Bits, + UCHAR Count) +{ + ULONG HighestBit = 1 << (Bits - 1); + ULONG MaxValue = HighestBit | (HighestBit - 1); + ULONG Result; + + /* Normalize the count */ + Count &= 0x1F; + + if ((Operation == 2) || (Operation == 3)) Count %= Bits + 1; + + /* If the count is zero, do nothing */ + if (Count == 0) return Value; + + /* Check which operation is this */ + switch (Operation) + { + /* ROL */ + case 0: + { + Count %= Bits; + Result = (Value << Count) | (Value >> (Bits - Count)); + + /* Update CF and OF */ + State->Flags.Cf = Result & 1; + if (Count == 1) State->Flags.Of = State->Flags.Cf + ^ ((Result & HighestBit) != 0); + + break; + } + + /* ROR */ + case 1: + { + Count %= Bits; + Result = (Value >> Count) | (Value << (Bits - Count)); + + /* Update CF and OF */ + State->Flags.Cf = ((Result & HighestBit) != 0); + if (Count == 1) State->Flags.Of = State->Flags.Cf + ^ ((Result & (HighestBit >> 1)) != 0); + + break; + } + + /* RCL */ + case 2: + { + Result = (Value << Count) | (State->Flags.Cf << (Count - 1)); + + /* Complete the calculation, but make sure we don't shift by too much */ + if ((Bits - Count) < 31) Result |= Value >> (Bits - Count + 1); + + /* Update CF and OF */ + State->Flags.Cf = ((Value & (1 << (Bits - Count))) != 0); + if (Count == 1) State->Flags.Of = State->Flags.Cf ^ ((Result & HighestBit) != 0); + + break; + } + + /* RCR */ + case 3: + { + /* Update OF */ + if (Count == 1) State->Flags.Of = State->Flags.Cf ^ ((Value & HighestBit) != 0); + + Result = (Value >> Count) | (State->Flags.Cf << (Bits - Count)); + + /* Complete the calculation, but make sure we don't shift by too much */ + if ((Bits - Count) < 31) Result |= Value << (Bits - Count + 1); + + /* Update CF */ + State->Flags.Cf = ((Value & (1 << (Count - 1))) != 0); + + break; + } + + /* SHL/SAL */ + case 4: + case 6: + { + Result = Value << Count; + + /* Update CF and OF */ + State->Flags.Cf = ((Value & (1 << (Bits - Count))) != 0); + if (Count == 1) State->Flags.Of = State->Flags.Cf + ^ ((Result & HighestBit) != 0); + + break; + } + + /* SHR */ + case 5: + { + Result = Value >> Count; + + /* Update CF and OF */ + State->Flags.Cf = ((Value & (1 << (Count - 1))) != 0); + if (Count == 1) State->Flags.Of = ((Value & HighestBit) != 0); + + break; + } + + /* SAR */ + case 7: + { + Result = Value >> Count; + + /* Fill the top Count bits with the sign bit */ + if (Value & HighestBit) Result |= ((1 << Count) - 1) << (Bits - Count); + + /* Update CF and OF */ + State->Flags.Cf = ((Value & (1 << (Count - 1))) != 0); + if (Count == 1) State->Flags.Of = FALSE; + + break; + } + } + + if (Operation >= 4) + { + /* Update ZF, SF and PF */ + State->Flags.Zf = ((Result & MaxValue) == 0); + State->Flags.Sf = ((Result & HighestBit) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + } + + /* Return the result */ + return Result; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup8082) +{ + UCHAR Immediate, Value; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Fetch the immediate operand */ + if (!Fast486FetchByte(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486ArithmeticOperation(State, ModRegRm.Register, Value, Immediate, 8); + + /* Unless this is CMP, write back the result */ + if (ModRegRm.Register != 7) + { + return Fast486WriteModrmByteOperands(State, &ModRegRm, FALSE, Value); + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup81) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Immediate, Value; + + /* Fetch the immediate operand */ + if (!Fast486FetchDword(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486ArithmeticOperation(State, ModRegRm.Register, Value, Immediate, 32); + + /* Unless this is CMP, write back the result */ + if (ModRegRm.Register != 7) + { + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value); + } + } + else + { + USHORT Immediate, Value; + + /* Fetch the immediate operand */ + if (!Fast486FetchWord(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486ArithmeticOperation(State, ModRegRm.Register, Value, Immediate, 16); + + /* Unless this is CMP, write back the result */ + if (ModRegRm.Register != 7) + { + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value); + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup83) +{ + CHAR ImmByte; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Fetch the immediate operand */ + if (!Fast486FetchByte(State, (PUCHAR)&ImmByte)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Immediate = (ULONG)((LONG)ImmByte); // Sign extend + ULONG Value; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486ArithmeticOperation(State, ModRegRm.Register, Value, Immediate, 32); + + /* Unless this is CMP, write back the result */ + if (ModRegRm.Register != 7) + { + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value); + } + } + else + { + USHORT Immediate = (USHORT)((SHORT)ImmByte); // Sign extend + USHORT Value; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486ArithmeticOperation(State, ModRegRm.Register, Value, Immediate, 16); + + /* Unless this is CMP, write back the result */ + if (ModRegRm.Register != 7) + { + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value); + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup8F) +{ + ULONG Value; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Pop a value from the stack - this must be done first */ + if (!Fast486StackPop(State, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred - restore SP */ + if (OperandSize) State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG); + else State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT); + + return FALSE; + } + + if (ModRegRm.Register != 0) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (OperandSize) + { + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + FALSE, + Value); + } + else + { + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + LOWORD(Value)); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC0) +{ + UCHAR Value, Count; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Fetch the count */ + if (!Fast486FetchByte(State, &Count)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = LOBYTE(Fast486RotateOperation(State, + ModRegRm.Register, + Value, + 8, + Count)); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + FALSE, + Value); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC1) +{ + UCHAR Count; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Fetch the count */ + if (!Fast486FetchByte(State, &Count)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Value; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486RotateOperation(State, + ModRegRm.Register, + Value, + 32, + Count); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value); + } + else + { + USHORT Value; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = LOWORD(Fast486RotateOperation(State, + ModRegRm.Register, + Value, + 16, + Count)); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC6) +{ + UCHAR Immediate; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register != 0) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Get the immediate operand */ + if (!Fast486FetchByte(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + FALSE, + Immediate); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC7) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register != 0) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (OperandSize) + { + ULONG Immediate; + + /* Get the immediate operand */ + if (!Fast486FetchDword(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + FALSE, + Immediate); + } + else + { + USHORT Immediate; + + /* Get the immediate operand */ + if (!Fast486FetchWord(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + Immediate); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD0) +{ + UCHAR Value; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = LOBYTE(Fast486RotateOperation(State, ModRegRm.Register, Value, 8, 1)); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + FALSE, + Value); + +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD1) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Value; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486RotateOperation(State, ModRegRm.Register, Value, 32, 1); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value); + } + else + { + USHORT Value; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = LOWORD(Fast486RotateOperation(State, ModRegRm.Register, Value, 16, 1)); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD2) +{ + UCHAR Value; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = LOBYTE(Fast486RotateOperation(State, + ModRegRm.Register, + Value, + 8, + State->GeneralRegs[FAST486_REG_ECX].LowByte)); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + FALSE, + Value); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD3) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (OperandSize) + { + ULONG Value; + + /* Read the operands */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = Fast486RotateOperation(State, + ModRegRm.Register, + Value, + 32, + State->GeneralRegs[FAST486_REG_ECX].LowByte); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value); + } + else + { + USHORT Value; + + /* Read the operands */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Value = LOWORD(Fast486RotateOperation(State, + ModRegRm.Register, + Value, + 16, + State->GeneralRegs[FAST486_REG_ECX].LowByte)); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value); + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupF6) +{ + UCHAR Value = 0; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + switch (ModRegRm.Register) + { + /* TEST */ + case 0: + case 1: + { + UCHAR Immediate, Result; + + /* Fetch the immediate byte */ + if (!Fast486FetchByte(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + + /* Calculate the result */ + Result = Value & Immediate; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + break; + } + + /* NOT */ + case 2: + { + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, &ModRegRm, FALSE, ~Value); + } + + /* NEG */ + case 3: + { + /* Calculate the result */ + UCHAR Result = -Value; + + /* Update the flags */ + State->Flags.Cf = (Value != 0); + State->Flags.Of = (Value & SIGN_FLAG_BYTE) && (Result & SIGN_FLAG_BYTE); + State->Flags.Af = ((Value & 0x0F) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, &ModRegRm, FALSE, Result); + } + + /* MUL */ + case 4: + { + USHORT Result = (USHORT)Value * (USHORT)State->GeneralRegs[FAST486_REG_EAX].LowByte; + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = (HIBYTE(Result) != 0); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Result; + + break; + } + + /* IMUL */ + case 5: + { + SHORT Result = (SHORT)((CHAR)Value) * (SHORT)((CHAR)State->GeneralRegs[FAST486_REG_EAX].LowByte); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = ((Result < -128) || (Result > 127)); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = (USHORT)Result; + + break; + } + + /* DIV */ + case 6: + { + UCHAR Quotient, Remainder; + + if (Value == 0) + { + /* Divide error */ + Fast486Exception(State, FAST486_EXCEPTION_DE); + return FALSE; + } + + Quotient = State->GeneralRegs[FAST486_REG_EAX].LowWord / Value; + Remainder = State->GeneralRegs[FAST486_REG_EAX].LowWord % Value; + + /* Write back the results */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = Quotient; + State->GeneralRegs[FAST486_REG_EAX].HighByte = Remainder; + + break; + } + + /* IDIV */ + case 7: + { + CHAR Quotient, Remainder; + + if (Value == 0) + { + /* Divide error */ + Fast486Exception(State, FAST486_EXCEPTION_DE); + return FALSE; + } + + Quotient = (SHORT)State->GeneralRegs[FAST486_REG_EAX].LowWord / (CHAR)Value; + Remainder = (SHORT)State->GeneralRegs[FAST486_REG_EAX].LowWord % (CHAR)Value; + + /* Write back the results */ + State->GeneralRegs[FAST486_REG_EAX].LowByte = (UCHAR)Quotient; + State->GeneralRegs[FAST486_REG_EAX].HighByte = (UCHAR)Remainder; + + break; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupF7) +{ + ULONG Value = 0, SignFlag; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the sign flag */ + if (OperandSize) SignFlag = SIGN_FLAG_LONG; + else SignFlag = SIGN_FLAG_WORD; + + /* Read the operand */ + if (OperandSize) + { + /* 32-bit */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* 16-bit */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, (PUSHORT)&Value)) + { + /* Exception occurred */ + return FALSE; + } + } + + switch (ModRegRm.Register) + { + /* TEST */ + case 0: + case 1: + { + ULONG Immediate = 0, Result = 0; + + if (OperandSize) + { + /* Fetch the immediate dword */ + if (!Fast486FetchDword(State, &Immediate)) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Fetch the immediate word */ + if (!Fast486FetchWord(State, (PUSHORT)&Immediate)) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Calculate the result */ + Result = Value & Immediate; + + /* Update the flags */ + State->Flags.Cf = FALSE; + State->Flags.Of = FALSE; + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SignFlag) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + break; + } + + /* NOT */ + case 2: + { + /* Write back the result */ + if (OperandSize) + { + /* 32-bit */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, ~Value); + } + else + { + /* 16-bit */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, LOWORD(~Value)); + } + } + + /* NEG */ + case 3: + { + /* Calculate the result */ + ULONG Result = -Value; + if (!OperandSize) Result &= 0xFFFF; + + /* Update the flags */ + State->Flags.Cf = (Value != 0); + State->Flags.Of = (Value & SignFlag) && (Result & SignFlag); + State->Flags.Af = ((Value & 0x0F) != 0); + State->Flags.Zf = (Result == 0); + State->Flags.Sf = ((Result & SignFlag) != 0); + State->Flags.Pf = Fast486CalculateParity(Result); + + /* Write back the result */ + if (OperandSize) + { + /* 32-bit */ + return Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Result); + } + else + { + /* 16-bit */ + return Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, LOWORD(Result)); + } + } + + /* MUL */ + case 4: + { + if (OperandSize) + { + ULONGLONG Result = (ULONGLONG)Value * (ULONGLONG)State->GeneralRegs[FAST486_REG_EAX].Long; + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = ((Result & 0xFFFFFFFF00000000ULL) != 0); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result & 0xFFFFFFFFULL; + State->GeneralRegs[FAST486_REG_EDX].Long = Result >> 32; + } + else + { + ULONG Result = (ULONG)Value * (ULONG)State->GeneralRegs[FAST486_REG_EAX].LowWord; + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = (HIWORD(Result) != 0); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = LOWORD(Result); + State->GeneralRegs[FAST486_REG_EDX].LowWord = HIWORD(Result); + } + + break; + } + + /* IMUL */ + case 5: + { + if (OperandSize) + { + LONGLONG Result = (LONGLONG)((LONG)Value) * (LONGLONG)((LONG)State->GeneralRegs[FAST486_REG_EAX].Long); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = ((Result < -2147483648LL) || (Result > 2147483647LL)); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].Long = Result & 0xFFFFFFFFULL; + State->GeneralRegs[FAST486_REG_EDX].Long = Result >> 32; + } + else + { + LONG Result = (LONG)((SHORT)Value) * (LONG)((SHORT)State->GeneralRegs[FAST486_REG_EAX].LowWord); + + /* Update the flags */ + State->Flags.Cf = State->Flags.Of = ((Result < -32768) || (Result > 32767)); + + /* Write back the result */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = LOWORD(Result); + State->GeneralRegs[FAST486_REG_EDX].LowWord = HIWORD(Result); + } + + break; + } + + /* DIV */ + case 6: + { + if (Value == 0) + { + /* Divide error */ + Fast486Exception(State, FAST486_EXCEPTION_DE); + return FALSE; + } + + if (OperandSize) + { + ULONGLONG Dividend = (ULONGLONG)State->GeneralRegs[FAST486_REG_EAX].Long + | ((ULONGLONG)State->GeneralRegs[FAST486_REG_EDX].Long << 32); + ULONG Quotient = Dividend / Value; + ULONG Remainder = Dividend % Value; + + /* Write back the results */ + State->GeneralRegs[FAST486_REG_EAX].Long = Quotient; + State->GeneralRegs[FAST486_REG_EDX].Long = Remainder; + } + else + { + ULONG Dividend = (ULONG)State->GeneralRegs[FAST486_REG_EAX].LowWord + | ((ULONG)State->GeneralRegs[FAST486_REG_EDX].LowWord << 16); + USHORT Quotient = Dividend / Value; + USHORT Remainder = Dividend % Value; + + /* Write back the results */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = Quotient; + State->GeneralRegs[FAST486_REG_EDX].LowWord = Remainder; + } + + break; + } + + /* IDIV */ + case 7: + { + if (Value == 0) + { + /* Divide error */ + Fast486Exception(State, FAST486_EXCEPTION_DE); + return FALSE; + } + + if (OperandSize) + { + LONGLONG Dividend = (LONGLONG)State->GeneralRegs[FAST486_REG_EAX].Long + | ((LONGLONG)State->GeneralRegs[FAST486_REG_EDX].Long << 32); + LONG Quotient = Dividend / (LONG)Value; + LONG Remainder = Dividend % (LONG)Value; + + /* Write back the results */ + State->GeneralRegs[FAST486_REG_EAX].Long = (ULONG)Quotient; + State->GeneralRegs[FAST486_REG_EDX].Long = (ULONG)Remainder; + } + else + { + LONG Dividend = (LONG)State->GeneralRegs[FAST486_REG_EAX].LowWord + | ((LONG)State->GeneralRegs[FAST486_REG_EDX].LowWord << 16); + SHORT Quotient = Dividend / (SHORT)LOWORD(Value); + SHORT Remainder = Dividend % (SHORT)LOWORD(Value); + + /* Write back the results */ + State->GeneralRegs[FAST486_REG_EAX].LowWord = (USHORT)Quotient; + State->GeneralRegs[FAST486_REG_EDX].LowWord = (USHORT)Remainder; + } + + break; + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupFE) +{ + UCHAR Value; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register > 1) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Read the operands */ + if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register == 0) + { + /* Increment and update OF and AF */ + Value++; + State->Flags.Of = (Value == SIGN_FLAG_BYTE); + State->Flags.Af = ((Value & 0x0F) == 0); + } + else + { + /* Decrement and update OF and AF */ + State->Flags.Of = (Value == SIGN_FLAG_BYTE); + Value--; + State->Flags.Af = ((Value & 0x0F) == 0x0F); + } + + /* Update flags */ + State->Flags.Zf = (Value == 0); + State->Flags.Sf = ((Value & SIGN_FLAG_BYTE) != 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + /* Write back the result */ + return Fast486WriteModrmByteOperands(State, + &ModRegRm, + FALSE, + Value); +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupFF) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register == 7) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Read the operands */ + if (OperandSize) + { + ULONG Value; + + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register == 0) + { + /* Increment and update OF and AF */ + Value++; + State->Flags.Of = (Value == SIGN_FLAG_LONG); + State->Flags.Af = ((Value & 0x0F) == 0); + } + else if (ModRegRm.Register == 1) + { + /* Decrement and update OF and AF */ + State->Flags.Of = (Value == SIGN_FLAG_LONG); + Value--; + State->Flags.Af = ((Value & 0x0F) == 0x0F); + } + else if (ModRegRm.Register == 2) + { + /* Push the current value of EIP */ + if (!Fast486StackPush(State, State->InstPtr.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the EIP to the address */ + State->InstPtr.Long = Value; + } + else if (ModRegRm.Register == 3) + { + USHORT Selector; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read the selector */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress + sizeof(ULONG), + FALSE, + &Selector, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + + /* Push the current value of CS */ + if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Push the current value of EIP */ + if (!Fast486StackPush(State, State->InstPtr.Long)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new code segment */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the EIP to the address */ + State->InstPtr.Long = Value; + } + else if (ModRegRm.Register == 4) + { + /* Set the EIP to the address */ + State->InstPtr.Long = Value; + } + else if (ModRegRm.Register == 5) + { + USHORT Selector; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read the selector */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress + sizeof(ULONG), + FALSE, + &Selector, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new code segment */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the EIP to the address */ + State->InstPtr.Long = Value; + } + else if (ModRegRm.Register == 6) + { + /* Push the value on to the stack */ + return Fast486StackPush(State, Value); + } + + if (ModRegRm.Register <= 1) + { + /* Update flags */ + State->Flags.Sf = ((Value & SIGN_FLAG_LONG) != 0); + State->Flags.Zf = (Value == 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + /* Write back the result */ + return Fast486WriteModrmDwordOperands(State, + &ModRegRm, + FALSE, + Value); + } + } + else + { + USHORT Value; + + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register == 0) + { + /* Increment and update OF */ + Value++; + State->Flags.Of = (Value == SIGN_FLAG_WORD); + State->Flags.Af = ((Value & 0x0F) == 0); + } + else if (ModRegRm.Register == 1) + { + /* Decrement and update OF */ + State->Flags.Of = (Value == SIGN_FLAG_WORD); + Value--; + State->Flags.Af = ((Value & 0x0F) == 0x0F); + } + else if (ModRegRm.Register == 2) + { + /* Push the current value of IP */ + if (!Fast486StackPush(State, State->InstPtr.LowWord)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the IP to the address */ + State->InstPtr.LowWord = Value; + + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + else if (ModRegRm.Register == 3) + { + USHORT Selector; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read the selector */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress + sizeof(USHORT), + FALSE, + &Selector, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + + /* Push the current value of CS */ + if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Push the current value of IP */ + if (!Fast486StackPush(State, State->InstPtr.LowWord)) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new code segment */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the IP to the address */ + State->InstPtr.LowWord = Value; + + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + else if (ModRegRm.Register == 4) + { + /* Set the IP to the address */ + State->InstPtr.LowWord = Value; + + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + else if (ModRegRm.Register == 5) + { + USHORT Selector; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Read the selector */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress + sizeof(USHORT), + FALSE, + &Selector, + sizeof(USHORT))) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new code segment */ + if (!Fast486LoadSegment(State, FAST486_REG_CS, Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set the IP to the address */ + State->InstPtr.LowWord = Value; + + /* Clear the top half of EIP */ + State->InstPtr.Long &= 0xFFFF; + } + else if (ModRegRm.Register == 6) + { + /* Push the value on to the stack */ + return Fast486StackPush(State, Value); + } + else + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + if (ModRegRm.Register <= 1) + { + /* Update flags */ + State->Flags.Sf = ((Value & SIGN_FLAG_WORD) != 0); + State->Flags.Zf = (Value == 0); + State->Flags.Pf = Fast486CalculateParity(Value); + + /* Write back the result */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + Value); + } + } + + return TRUE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0F00) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check which operation this is */ + switch (ModRegRm.Register) + { + /* SLDT */ + case 0: + { + /* Not recognized in real mode or virtual 8086 mode */ + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + State->Ldtr.Selector); + } + + /* STR */ + case 1: + { + /* Not recognized in real mode or virtual 8086 mode */ + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + State->TaskReg.Selector); + } + + /* LLDT */ + case 2: + { + USHORT Selector; + FAST486_SYSTEM_DESCRIPTOR GdtEntry; + + /* Not recognized in real mode or virtual 8086 mode */ + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* This is a privileged instruction */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + NULL, + &Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Make sure the GDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + /* Read the GDT */ + if (!Fast486ReadLinearMemory(State, + State->Gdtr.Address + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + + if (GET_SEGMENT_INDEX(Selector) == 0) + { + RtlZeroMemory(&State->Ldtr, sizeof(State->Ldtr)); + return TRUE; + } + + if (!GdtEntry.Present) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); + return FALSE; + } + + if (GdtEntry.Signature != FAST486_LDT_SIGNATURE) + { + /* This is not a LDT descriptor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + /* Update the LDTR */ + State->Ldtr.Selector = Selector; + State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24); + State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16); + if (GdtEntry.Granularity) State->Ldtr.Limit <<= 12; + + return TRUE; + } + + /* LTR */ + case 3: + { + USHORT Selector; + FAST486_SYSTEM_DESCRIPTOR GdtEntry; + + /* Not recognized in real mode or virtual 8086 mode */ + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* This is a privileged instruction */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + NULL, + &Selector)) + { + /* Exception occurred */ + return FALSE; + } + + /* Make sure the GDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1)) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + /* Read the GDT */ + if (!Fast486ReadLinearMemory(State, + State->Gdtr.Address + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + + if (GET_SEGMENT_INDEX(Selector) == 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!GdtEntry.Present) + { + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); + return FALSE; + } + + if (GdtEntry.Signature != FAST486_TSS_SIGNATURE) + { + /* This is not a TSS descriptor */ + Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); + return FALSE; + } + + /* Update the TR */ + State->TaskReg.Selector = Selector; + State->TaskReg.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24); + State->TaskReg.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16); + if (GdtEntry.Granularity) State->TaskReg.Limit <<= 12; + State->TaskReg.Busy = TRUE; + + return TRUE; + } + + /* VERR/VERW */ + case 4: + case 5: + { + USHORT Selector; + FAST486_GDT_ENTRY GdtEntry; + + /* Not recognized in real mode or virtual 8086 mode */ + if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + || State->Flags.Vm) + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* This is a privileged instruction */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!Fast486ReadModrmWordOperands(State, + &ModRegRm, + NULL, + &Selector)) + { + /* Exception occurred */ + return FALSE; + } + + if (!(Selector & SEGMENT_TABLE_INDICATOR)) + { + /* Make sure the GDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1)) + { + /* Clear ZF */ + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Read the GDT */ + if (!Fast486ReadLinearMemory(State, + State->Gdtr.Address + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + else + { + /* Make sure the LDT contains the entry */ + if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1)) + { + /* Clear ZF */ + State->Flags.Zf = FALSE; + return TRUE; + } + + /* Read the LDT */ + if (!Fast486ReadLinearMemory(State, + State->Ldtr.Base + + GET_SEGMENT_INDEX(Selector), + &GdtEntry, + sizeof(GdtEntry))) + { + /* Exception occurred */ + return FALSE; + } + } + + /* Set ZF if it is valid and accessible */ + State->Flags.Zf = GdtEntry.Present // must be present + && GdtEntry.SystemType // must be a segment + && (((ModRegRm.Register == 4) + /* code segments are only readable if the RW bit is set */ + && (!GdtEntry.Executable || GdtEntry.ReadWrite)) + || ((ModRegRm.Register == 5) + /* code segments are never writable, data segments are writable when RW is set */ + && (!GdtEntry.Executable && GdtEntry.ReadWrite))) + /* + * for segments other than conforming code segments, + * both RPL and CPL must be less than or equal to DPL + */ + && ((!GdtEntry.Executable || !GdtEntry.DirConf) + && ((GET_SEGMENT_RPL(Selector) <= GdtEntry.Dpl) + && (Fast486GetCurrentPrivLevel(State) <= GdtEntry.Dpl))) + /* for conforming code segments, DPL must be less than or equal to CPL */ + && ((GdtEntry.Executable && GdtEntry.DirConf) + && (GdtEntry.Dpl <= Fast486GetCurrentPrivLevel(State))); + + + return TRUE; + } + + /* Invalid */ + default: + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0F01) +{ + UCHAR TableReg[6]; + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + FAST486_SEG_REGS Segment = FAST486_REG_DS; + + NO_LOCK_PREFIX(); + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* Check for the segment override */ + if (State->PrefixFlags & FAST486_PREFIX_SEG) + { + /* Use the override segment instead */ + Segment = State->SegmentOverride; + } + + /* Check which operation this is */ + switch (ModRegRm.Register) + { + /* SGDT */ + case 0: + { + if (!ModRegRm.Memory) + { + /* The second operand must be a memory location */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Fill the 6-byte table register */ + RtlCopyMemory(TableReg, &State->Gdtr.Size, sizeof(USHORT)); + RtlCopyMemory(&TableReg[sizeof(USHORT)], &State->Gdtr.Address, sizeof(ULONG)); + + /* Store the GDTR */ + return Fast486WriteMemory(State, + Segment, + ModRegRm.MemoryAddress, + TableReg, + sizeof(TableReg)); + } + + /* SIDT */ + case 1: + { + if (!ModRegRm.Memory) + { + /* The second operand must be a memory location */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Fill the 6-byte table register */ + RtlCopyMemory(TableReg, &State->Idtr.Size, sizeof(USHORT)); + RtlCopyMemory(&TableReg[sizeof(USHORT)], &State->Idtr.Address, sizeof(ULONG)); + + /* Store the IDTR */ + return Fast486WriteMemory(State, + Segment, + ModRegRm.MemoryAddress, + TableReg, + sizeof(TableReg)); + } + + /* LGDT */ + case 2: + { + /* This is a privileged instruction */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!ModRegRm.Memory) + { + /* The second operand must be a memory location */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Read the new GDTR */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress, + FALSE, + TableReg, + sizeof(TableReg))) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new GDT */ + State->Gdtr.Size = *((PUSHORT)TableReg); + State->Gdtr.Address = *((PULONG)&TableReg[sizeof(USHORT)]); + + return TRUE; + } + + /* LIDT */ + case 3: + { + /* This is a privileged instruction */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + if (!ModRegRm.Memory) + { + /* The second operand must be a memory location */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Read the new IDTR */ + if (!Fast486ReadMemory(State, + Segment, + ModRegRm.MemoryAddress, + FALSE, + TableReg, + sizeof(TableReg))) + { + /* Exception occurred */ + return FALSE; + } + + /* Load the new IDT */ + State->Idtr.Size = *((PUSHORT)TableReg); + State->Idtr.Address = *((PULONG)&TableReg[sizeof(USHORT)]); + + return TRUE; + } + + /* SMSW */ + case 4: + { + /* Store the lower 16 bits (Machine Status Word) of CR0 */ + return Fast486WriteModrmWordOperands(State, + &ModRegRm, + FALSE, + LOWORD(State->ControlRegisters[FAST486_REG_CR0])); + } + + /* LMSW */ + case 6: + { + USHORT MachineStatusWord; + + /* This is a privileged instruction */ + if (Fast486GetCurrentPrivLevel(State) != 0) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Read the new Machine Status Word */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &MachineStatusWord)) + { + /* Exception occurred */ + return FALSE; + } + + /* This instruction cannot be used to return to real mode */ + if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) + && !(MachineStatusWord & FAST486_CR0_PE)) + { + Fast486Exception(State, FAST486_EXCEPTION_GP); + return FALSE; + } + + /* Set the lowest 4 bits */ + State->ControlRegisters[FAST486_REG_CR0] &= 0xFFFFFFF0; + State->ControlRegisters[FAST486_REG_CR0] |= MachineStatusWord & 0x0F; + + return TRUE; + } + + /* INVLPG */ + case 7: + { + UNIMPLEMENTED; + return FALSE; + } + + /* Invalid */ + default: + { + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + } +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0FB9) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_ADSIZE(AddressSize); + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + /* All of them are reserved (UD2) */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; +} + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0FBA) +{ + FAST486_MOD_REG_RM ModRegRm; + BOOLEAN OperandSize, AddressSize; + UINT DataSize; + UCHAR BitNumber; + + OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size; + + TOGGLE_OPSIZE(OperandSize); + TOGGLE_ADSIZE(AddressSize); + + /* Get the number of bits */ + if (OperandSize) DataSize = 32; + else DataSize = 16; + + if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Register < 4) + { + /* Invalid */ + Fast486Exception(State, FAST486_EXCEPTION_UD); + return FALSE; + } + + /* Get the bit number */ + if (!Fast486FetchByte(State, &BitNumber)) + { + /* Exception occurred */ + return FALSE; + } + + if (ModRegRm.Memory) + { + /* + * For memory operands, add the bit offset divided by + * the data size to the address + */ + ModRegRm.MemoryAddress += BitNumber / DataSize; + } + + /* Normalize the bit number */ + BitNumber %= DataSize; + + if (OperandSize) + { + ULONG Value; + + /* Read the value */ + if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + if (ModRegRm.Register == 5) + { + /* BTS */ + Value |= 1 << BitNumber; + } + else if (ModRegRm.Register == 6) + { + /* BTR */ + Value &= ~(1 << BitNumber); + } + else if (ModRegRm.Register == 7) + { + /* BTC */ + Value ^= 1 << BitNumber; + } + + if (ModRegRm.Register >= 5) + { + /* Write back the result */ + if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + } + else + { + USHORT Value; + + /* Read the value */ + if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Value)) + { + /* Exception occurred */ + return FALSE; + } + + /* Set CF to the bit value */ + State->Flags.Cf = (Value >> BitNumber) & 1; + + if (ModRegRm.Register == 5) + { + /* BTS */ + Value |= 1 << BitNumber; + } + else if (ModRegRm.Register == 6) + { + /* BTR */ + Value &= ~(1 << BitNumber); + } + else if (ModRegRm.Register == 7) + { + /* BTC */ + Value ^= 1 << BitNumber; + } + + if (ModRegRm.Register >= 5) + { + /* Write back the result */ + if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, Value)) + { + /* Exception occurred */ + return FALSE; + } + } + } + + /* Return success */ + return TRUE; +} + +/* EOF */ diff --git a/reactos/lib/fast486/opgroups.h b/reactos/lib/fast486/opgroups.h new file mode 100644 index 0000000000000..bf55cce9c3f46 --- /dev/null +++ b/reactos/lib/fast486/opgroups.h @@ -0,0 +1,53 @@ +/* + * Fast486 386/486 CPU Emulation Library + * opgroups.h + * + * Copyright (C) 2013 Aleksandar Andrejevic + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _OPGROUPS_H_ +#define _OPGROUPS_H_ + +#pragma once + +/* DEFINES ********************************************************************/ + +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup8082); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup81); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup83); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup8F); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC0); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC1); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC6); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupC7); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD0); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD1); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD2); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupD3); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupF6); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupF7); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupFE); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroupFF); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0F00); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0F01); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0FB9); +FAST486_OPCODE_HANDLER(Fast486OpcodeGroup0FBA); + +#endif // _OPGROUPS_H_ + +/* EOF */ + diff --git a/reactos/ntoskrnl/mm/section.c b/reactos/ntoskrnl/mm/section.c index ec3b094767822..af8719277e5c4 100644 --- a/reactos/ntoskrnl/mm/section.c +++ b/reactos/ntoskrnl/mm/section.c @@ -280,7 +280,14 @@ NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader, nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize); if(!NT_SUCCESS(nStatus)) - DIE(("ReadFile failed, status %08X\n", nStatus)); + { + NTSTATUS ReturnedStatus = nStatus; + + /* If it attempted to read past the end of the file, it means e_lfanew is invalid */ + if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT; + + DIE(("ReadFile failed, status %08X\n", ReturnedStatus)); + } ASSERT(pData); ASSERT(pBuffer); diff --git a/reactos/subsystems/CMakeLists.txt b/reactos/subsystems/CMakeLists.txt index 52d10bc85fcfa..230e58c9aceec 100644 --- a/reactos/subsystems/CMakeLists.txt +++ b/reactos/subsystems/CMakeLists.txt @@ -1,6 +1,4 @@ -if(ARCH STREQUAL "i386") - add_subdirectory(ntvdm) -endif() +add_subdirectory(ntvdm) add_subdirectory(win) add_subdirectory(win32) diff --git a/reactos/subsystems/ntvdm/CMakeLists.txt b/reactos/subsystems/ntvdm/CMakeLists.txt index 7a1c6aa155dda..80ab3e92e41c8 100644 --- a/reactos/subsystems/ntvdm/CMakeLists.txt +++ b/reactos/subsystems/ntvdm/CMakeLists.txt @@ -1,11 +1,42 @@ -include_directories(.) +include_directories( + ${REACTOS_SOURCE_DIR}/include/reactos/libs/fast486 + ntvdm) -add_executable(ntvdm +spec2def(ntvdm.exe ntvdm.spec) + +list(APPEND SOURCE + bios/bios32/bios32.c + bios/bios32/kbdbios32.c + bios/bios32/vidbios32.c + bios/bios.c + bios/kbdbios.c + bios/rom.c + bios/vidbios.c + hardware/cmos.c + hardware/pic.c + hardware/ps2.c + hardware/speaker.c + hardware/timer.c + hardware/vga.c + dos/dos32krnl/bios.c + dos/dos32krnl/dos.c + dos/dem.c + bop.c + callback.c + clock.c + emulator.c + io.c + registers.c + utils.c + vddsup.c ntvdm.c - ntvdm.rc) + ntvdm.rc + ${CMAKE_CURRENT_BINARY_DIR}/ntvdm.def) -set_module_type(ntvdm win32cui) -add_importlibs(ntvdm ntdll user32 gdi32 advapi32 msvcrt kernel32) -add_dependencies(ntvdm ndk bugcodes) +add_executable(ntvdm ${SOURCE}) +set_module_type(ntvdm win32cui UNICODE) +set_image_base(ntvdm 0x0F000000) +target_link_libraries(ntvdm fast486) +add_importlibs(ntvdm user32 gdi32 advapi32 msvcrt kernel32 ntdll) add_cd_file(TARGET ntvdm DESTINATION reactos/system32 FOR all) diff --git a/reactos/subsystems/ntvdm/bios/bios.c b/reactos/subsystems/ntvdm/bios/bios.c new file mode 100644 index 0000000000000..12066b53dd3d8 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios.c @@ -0,0 +1,211 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bios.c + * PURPOSE: VDM BIOS Support Library + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" +#include "bop.h" + +#include "bios.h" +#include "bios32/bios32.h" + +#include "rom.h" + +#include "io.h" +#include "hardware/cmos.h" + +/* DEFINES ********************************************************************/ + +/* BOP Identifiers */ +#define BOP_BIOSINIT 0x00 // Windows NTVDM (SoftPC) BIOS calls BOP 0x00 + // to let the virtual machine initialize itself + // the IVT and its hardware. +#define BOP_EQUIPLIST 0x11 +#define BOP_GETMEMSIZE 0x12 + +/* PRIVATE VARIABLES **********************************************************/ + +static BOOLEAN Bios32Loaded = FALSE; + +static CALLBACK16 __BiosContext; +PBIOS_DATA_AREA Bda; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID WINAPI BiosInitBop(LPWORD Stack) +{ + BOOLEAN Success; + + /* Load the second part of the Windows NTVDM BIOS image */ + LPCSTR BiosFileName = "bios1.rom"; + PVOID BiosLocation = (PVOID)TO_LINEAR(BIOS_SEGMENT, 0x0000); + DWORD BiosSize = 0; + + /* Disable interrupts */ + setIF(0); + + DisplayMessage(L"You are loading Windows NTVDM BIOS!\n"); + + /* Initialize a private callback context */ + InitializeContext(&__BiosContext, BIOS_SEGMENT, 0x0000); + + Success = LoadRom(BiosFileName, BiosLocation, &BiosSize); + DPRINT1("BIOS loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + if (Success == FALSE) + { + /* Stop the VDM */ + EmulatorTerminate(); + return; + } + + // DisplayMessage(L"First bytes at 0x%p: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n" + // L"3 last bytes at 0x%p: 0x%02x 0x%02x 0x%02x", + // BiosLocation, + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 0), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 1), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 2), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 3), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 4), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 5), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 6), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 7), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 8), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 9), + + // (PVOID)((ULONG_PTR)BiosLocation + BiosSize - 2), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 2), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 1), + // *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 0)); + + /* Initialize IVT and hardware */ + + /* Initialize the Keyboard and Video BIOS */ + if (!KbdBiosInitialize() || !VidBiosInitialize()) + { + /* Stop the VDM */ + EmulatorTerminate(); + return; + } + + /* Load VGA BIOS */ + // Success = LoadRom("v7vga.rom", (PVOID)0xC0000, &BiosSize); + // DPRINT1("VGA BIOS loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + /* Enable interrupts */ + setIF(1); + + ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! ///////////////////// + + /* Load some ROMs */ + // Success = LoadRom("boot.bin", (PVOID)0xE0000, &BiosSize); + // DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + SearchAndInitRoms(&__BiosContext); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID WINAPI BiosEquipmentService(LPWORD Stack) +{ + /* Return the equipment list */ + setAX(Bda->EquipmentList); +} + +VOID WINAPI BiosGetMemorySize(LPWORD Stack) +{ + /* Return the conventional memory size in kB, typically 640 kB */ + setAX(Bda->MemorySize); +} + +BOOLEAN +BiosInitialize(IN LPCSTR BiosFileName) +{ + BOOLEAN Success = FALSE; + + /* Disable interrupts */ + setIF(0); + + /* Initialize the BDA pointer */ + Bda = (PBIOS_DATA_AREA)SEG_OFF_TO_PTR(BDA_SEGMENT, 0); + + /* Register the BIOS support BOPs */ + RegisterBop(BOP_BIOSINIT , BiosInitBop); + RegisterBop(BOP_EQUIPLIST , BiosEquipmentService); + RegisterBop(BOP_GETMEMSIZE, BiosGetMemorySize); + + if (BiosFileName) + { + PVOID BiosLocation = NULL; + DWORD BiosSize = 0; + + Success = LoadBios(BiosFileName, &BiosLocation, &BiosSize); + DPRINT1("BIOS loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + if (!Success) return FALSE; + + DisplayMessage(L"First bytes at 0x%p: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n" + L"3 last bytes at 0x%p: 0x%02x 0x%02x 0x%02x", + BiosLocation, + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 0), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 1), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 2), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 3), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 4), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 5), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 6), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 7), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 8), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 9), + + (PVOID)((ULONG_PTR)BiosLocation + BiosSize - 2), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 2), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 1), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 0)); + + DisplayMessage(L"POST at 0x%p: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + TO_LINEAR(getCS(), getIP()), + *(PCHAR)((ULONG_PTR)SEG_OFF_TO_PTR(getCS(), getIP()) + 0), + *(PCHAR)((ULONG_PTR)SEG_OFF_TO_PTR(getCS(), getIP()) + 1), + *(PCHAR)((ULONG_PTR)SEG_OFF_TO_PTR(getCS(), getIP()) + 2), + *(PCHAR)((ULONG_PTR)SEG_OFF_TO_PTR(getCS(), getIP()) + 3), + *(PCHAR)((ULONG_PTR)SEG_OFF_TO_PTR(getCS(), getIP()) + 4)); + + /* Boot it up */ + + /* + * The CPU is already in reset-mode so that + * CS:IP points to F000:FFF0 as required. + */ + DisplayMessage(L"CS=0x%p ; IP=0x%p", getCS(), getIP()); + // setCS(0xF000); + // setIP(0xFFF0); + + Success = TRUE; + } + else + { + Success = Bios32Loaded = Bios32Initialize(); + } + + /* Enable interrupts */ + setIF(1); + + return Success; +} + +VOID +BiosCleanup(VOID) +{ + if (Bios32Loaded) Bios32Cleanup(); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios.h b/reactos/subsystems/ntvdm/bios/bios.h new file mode 100644 index 0000000000000..cc1bb94b803d1 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios.h @@ -0,0 +1,118 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bios.h + * PURPOSE: VDM BIOS Support Library + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _BIOS_H_ +#define _BIOS_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" +#include "kbdbios.h" +#include "vidbios.h" + +/* DEFINES ********************************************************************/ + +#define BDA_SEGMENT 0x40 +#define BIOS_SEGMENT 0xF000 + +#define BIOS_EQUIPMENT_LIST 0x2C // HACK: Disable FPU for now + +/* + * BIOS Data Area at 0040:XXXX + * + * See: http://webpages.charter.net/danrollins/techhelp/0093.HTM + * and: http://www.bioscentral.com/misc/bda.htm + * for more information. + */ +#pragma pack(push, 1) +typedef struct +{ + WORD SerialPorts[4]; // 0x00 + WORD ParallelPorts[3]; // 0x08 + WORD EbdaSegment; // 0x0e - ParallelPort in PC/XT + WORD EquipmentList; // 0x10 + BYTE Reserved0; // 0x12 - Errors in PCjr infrared keyboard link + WORD MemorySize; // 0x13 + WORD Reserved1; // 0x15 - Scratch pad for manufacturing error tests + WORD KeybdShiftFlags; // 0x17 + BYTE AlternateKeypad; // 0x19 + WORD KeybdBufferHead; // 0x1a + WORD KeybdBufferTail; // 0x1c + WORD KeybdBuffer[BIOS_KBD_BUFFER_SIZE]; // 0x1e + BYTE DriveRecalibrate; // 0x3e + BYTE DriveMotorStatus; // 0x3f + BYTE MotorShutdownCounter; // 0x40 + BYTE LastDisketteOperation; // 0x41 + BYTE Reserved2[7]; // 0x42 + BYTE VideoMode; // 0x49 + WORD ScreenColumns; // 0x4a + WORD VideoPageSize; // 0x4c + WORD VideoPageOffset; // 0x4e + WORD CursorPosition[BIOS_MAX_PAGES]; // 0x50 + BYTE CursorEndLine; // 0x60 + BYTE CursorStartLine; // 0x61 + BYTE VideoPage; // 0x62 + WORD CrtBasePort; // 0x63 + BYTE CrtModeControl; // 0x65 + BYTE CrtColorPaletteMask; // 0x66 + BYTE CassetteData[5]; // 0x67 + DWORD TickCounter; // 0x6c + BYTE MidnightPassed; // 0x70 + BYTE BreakFlag; // 0x71 + WORD SoftReset; // 0x72 + BYTE LastDiskOperation; // 0x74 + BYTE NumDisks; // 0x75 + BYTE DriveControlByte; // 0x76 + BYTE DiskPortOffset; // 0x77 + BYTE LptTimeOut[4]; // 0x78 + BYTE ComTimeOut[4]; // 0x7c + WORD KeybdBufferStart; // 0x80 + WORD KeybdBufferEnd; // 0x82 + BYTE ScreenRows; // 0x84 + WORD CharacterHeight; // 0x85 + BYTE EGAFlags[2]; // 0x87 + BYTE VGAFlags[2]; // 0x89 + DWORD Reserved3; // 0x8b + BYTE Reserved4; // 0x8f + BYTE Reserved5[2]; // 0x90 + BYTE Reserved6[2]; // 0x92 + BYTE Reserved7[2]; // 0x94 + WORD Reserved8; // 0x96 + DWORD Reserved9; // 0x98 + DWORD Reserved10; // 0x9c + DWORD Reserved11[2]; // 0xa0 + DWORD EGAPtr; // 0xa8 + BYTE Reserved12[68]; // 0xac + BYTE Reserved13[16]; // 0xf0 + + DWORD Reserved14; // 0x100 + BYTE Reserved15[12]; // 0x104 + BYTE Reserved16[17]; // 0x110 + BYTE Reserved17[15]; // 0x121 + BYTE Reserved18[3]; // 0x130 +} BIOS_DATA_AREA, *PBIOS_DATA_AREA; +#pragma pack(pop) + +C_ASSERT(sizeof(BIOS_DATA_AREA) == 0x133); + +/* FUNCTIONS ******************************************************************/ + +extern PBIOS_DATA_AREA Bda; + +VOID WINAPI BiosEquipmentService(LPWORD Stack); +VOID WINAPI BiosGetMemorySize(LPWORD Stack); + +BOOLEAN +BiosInitialize(IN LPCSTR BiosFileName); + +VOID +BiosCleanup(VOID); + +#endif // _BIOS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/bios32.c b/reactos/subsystems/ntvdm/bios/bios32/bios32.c new file mode 100644 index 0000000000000..fc2728262424d --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/bios32.c @@ -0,0 +1,403 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bios32.c + * PURPOSE: VDM 32-bit BIOS + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" +#include "bop.h" + +#include "../bios.h" +#include "../rom.h" +#include "bios32.h" +#include "bios32p.h" +#include "kbdbios32.h" +#include "vidbios32.h" + +#include "io.h" +#include "hardware/cmos.h" +#include "hardware/pic.h" +#include "hardware/timer.h" + +/* PRIVATE VARIABLES **********************************************************/ + +CALLBACK16 BiosContext; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID WINAPI BiosException(LPWORD Stack) +{ + /* Get the exception number and call the emulator API */ + BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]); + EmulatorException(ExceptionNumber, Stack); +} + +static VOID WINAPI BiosMiscService(LPWORD Stack) +{ + switch (getAH()) + { + /* Wait */ + case 0x86: + { + /* + * Interval in microseconds in CX:DX + * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm + * for more information. + */ + Sleep(MAKELONG(getDX(), getCX())); + + /* Clear CF */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + + break; + } + + /* Copy Extended Memory */ + case 0x87: + { + DWORD Count = (DWORD)getCX() * 2; + PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI()); + DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24); + DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16); + DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24); + DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16); + + /* Check for flags */ + if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF; + if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF; + + if ((Count > SourceLimit) || (Count > DestLimit)) + { + setAX(0x80); + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + + break; + } + + /* Copy */ + RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase), + (PVOID)((ULONG_PTR)BaseAddress + SourceBase), + Count); + + setAX(ERROR_SUCCESS); + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + break; + } + + /* Get Extended Memory Size */ + case 0x88: + { + UCHAR Low, High; + + /* + * Return the (usable) extended memory (after 1 MB) + * size in kB from CMOS. + */ + IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW); + Low = IOReadB(CMOS_DATA_PORT); + IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH); + High = IOReadB(CMOS_DATA_PORT); + setAX(MAKEWORD(Low, High)); + + /* Clear CF */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + + break; + } + + default: + { + DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n", + getAH()); + } + } +} + +static VOID WINAPI BiosTimeService(LPWORD Stack) +{ + switch (getAH()) + { + case 0x00: + { + /* Set AL to 1 if midnight had passed, 0 otherwise */ + setAL(Bda->MidnightPassed ? 0x01 : 0x00); + + /* Return the tick count in CX:DX */ + setCX(HIWORD(Bda->TickCounter)); + setDX(LOWORD(Bda->TickCounter)); + + /* Reset the midnight flag */ + Bda->MidnightPassed = FALSE; + + break; + } + + case 0x01: + { + /* Set the tick count to CX:DX */ + Bda->TickCounter = MAKELONG(getDX(), getCX()); + + /* Reset the midnight flag */ + Bda->MidnightPassed = FALSE; + + break; + } + + default: + { + DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n", + getAH()); + } + } +} + +static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack) +{ + /* Increase the system tick count */ + Bda->TickCounter++; +} + + +// From SeaBIOS +static VOID PicSetIRQMask(USHORT off, USHORT on) +{ + UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8; + IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on); + IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on); +} + +// From SeaBIOS +VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func) +{ + UCHAR vector; + + PicSetIRQMask(1 << hwirq, 0); + if (hwirq < 8) + vector = BIOS_PIC_MASTER_INT + hwirq; + else + vector = BIOS_PIC_SLAVE_INT + hwirq - 8; + + RegisterBiosInt32(vector, func); +} + + +VOID PicIRQComplete(LPWORD Stack) +{ + /* Get the interrupt number */ + BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]); + + /* + * If this was a PIC IRQ, send an End-of-Interrupt to the PIC. + */ + + if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8) + { + /* It was an IRQ from the master PIC */ + IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI); + } + else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8) + { + /* It was an IRQ from the slave PIC */ + IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI); + IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI); + } +} + +static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack) +{ + BYTE IrqNumber; + + IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */); + IrqNumber = IOReadB(PIC_MASTER_CMD); + + DPRINT("Master - IrqNumber = 0x%x\n", IrqNumber); + + PicIRQComplete(Stack); +} + +static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack) +{ + BYTE IrqNumber; + + IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */); + IrqNumber = IOReadB(PIC_SLAVE_CMD); + + DPRINT("Slave - IrqNumber = 0x%x\n", IrqNumber); + + PicIRQComplete(Stack); +} + +// Timer IRQ 0 +static VOID WINAPI BiosTimerIrq(LPWORD Stack) +{ + /* + * Perform the system timer interrupt. + * + * Do not call directly BiosSystemTimerInterrupt(Stack); + * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT + * for their purpose... + */ + /** EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT); **/ + Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT); + PicIRQComplete(Stack); +} + + +static VOID BiosHwSetup(VOID) +{ + /* Initialize the master and the slave PICs (cascade mode) */ + IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4); + IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4); + + /* + * Set the interrupt vector offsets for each PIC + * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15) + */ + IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT); + IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT ); + + /* Tell the master PIC that there is a slave PIC at IRQ 2 */ + IOWriteB(PIC_MASTER_DATA, 1 << 2); + /* Tell the slave PIC its cascade identity */ + IOWriteB(PIC_SLAVE_DATA , 2); + + /* Make sure both PICs are in 8086 mode */ + IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086); + IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086); + + /* Clear the masks for both PICs */ + // IOWriteB(PIC_MASTER_DATA, 0x00); + // IOWriteB(PIC_SLAVE_DATA , 0x00); + /* Disable all IRQs */ + IOWriteB(PIC_MASTER_DATA, 0xFF); + IOWriteB(PIC_SLAVE_DATA , 0xFF); + + + /* Initialize PIT Counter 0 */ + IOWriteB(PIT_COMMAND_PORT, 0x34); + IOWriteB(PIT_DATA_PORT(0), 0x00); + IOWriteB(PIT_DATA_PORT(0), 0x00); + + /* Initialize PIT Counter 1 */ + IOWriteB(PIT_COMMAND_PORT, 0x74); + IOWriteB(PIT_DATA_PORT(1), 0x00); + IOWriteB(PIT_DATA_PORT(1), 0x00); + + /* Initialize PIT Counter 2 */ + IOWriteB(PIT_COMMAND_PORT, 0xB4); + IOWriteB(PIT_DATA_PORT(2), 0x00); + IOWriteB(PIT_DATA_PORT(2), 0x00); + + EnableHwIRQ(0, BiosTimerIrq); +} + +static VOID InitializeBiosInt32(VOID) +{ + USHORT i; + // USHORT Offset = 0; + + /* Initialize the callback context */ + InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000); + + /* Register the BIOS 32-bit Interrupts */ + for (i = 0x00; i <= 0xFF; i++) + { + // Offset += RegisterInt32(MAKELONG(Offset, BIOS_SEGMENT), i, NULL, NULL); + BiosContext.NextOffset += RegisterInt32(MAKELONG(BiosContext.NextOffset, + BiosContext.Segment), + i, NULL, NULL); + } + + /* Initialize the exception vector interrupts to a default Exception handler */ + for (i = 0; i < 8; i++) + RegisterBiosInt32(i, BiosException); + + /* Initialize HW vector interrupts to a default HW handler */ + for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) + RegisterBiosInt32(i, BiosHandleMasterPicIRQ); + for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++) + RegisterBiosInt32(i, BiosHandleSlavePicIRQ); + + /* Initialize software vector handlers */ + RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService ); + RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize ); + RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService ); + RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService ); + RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt); + + /* Some interrupts are in fact addresses to tables */ + ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x41] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x46] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x48] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x49] = (ULONG)NULL; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* + * The BIOS POST (Power On-Self Test) + */ +BOOLEAN Bios32Initialize(VOID) +{ + BOOLEAN Success; + UCHAR Low, High; + + /* Initialize the stack */ + // That's what says IBM... (stack at 30:00FF going downwards) + // setSS(0x0000); + // setSP(0x0400); + setSS(0x0050); // Stack at 50:0400, going downwards + setSP(0x0400); + + /* Set data segment */ + setDS(BDA_SEGMENT); + + /* Initialize the BDA contents */ + Bda->EquipmentList = BIOS_EQUIPMENT_LIST; + + /* + * Retrieve the conventional memory size + * in kB from CMOS, typically 640 kB. + */ + IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW); + Low = IOReadB(CMOS_DATA_PORT); + IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH); + High = IOReadB(CMOS_DATA_PORT); + Bda->MemorySize = MAKEWORD(Low, High); + + /* Register the BIOS 32-bit Interrupts */ + InitializeBiosInt32(); + + /* Initialize platform hardware (PIC/PIT chips, ...) */ + BiosHwSetup(); + + /* Initialize the Keyboard and Video BIOS */ + if (!KbdBios32Initialize() || !VidBios32Initialize()) return FALSE; + + ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! ///////////////////// + + /* Load some ROMs */ + Success = LoadRom("boot.bin", (PVOID)0xE0000, NULL); + DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + SearchAndInitRoms(&BiosContext); + + /* We are done */ + return TRUE; +} + +VOID Bios32Cleanup(VOID) +{ + VidBios32Cleanup(); + KbdBios32Cleanup(); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/bios32.h b/reactos/subsystems/ntvdm/bios/bios32/bios32.h new file mode 100644 index 0000000000000..4bd5d30b15c21 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/bios32.h @@ -0,0 +1,32 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bios32.h + * PURPOSE: VDM 32-bit BIOS + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _BIOS32_H_ +#define _BIOS32_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" +// #include "../bios.h" + +/* DEFINES ********************************************************************/ + +// #define BIOS_EQUIPMENT_INTERRUPT 0x11 +// #define BIOS_MEMORY_SIZE 0x12 +// #define BIOS_MISC_INTERRUPT 0x15 +// #define BIOS_TIME_INTERRUPT 0x1A +// #define BIOS_SYS_TIMER_INTERRUPT 0x1C + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN Bios32Initialize(VOID); +VOID Bios32Cleanup(VOID); + +#endif // _BIOS32_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/bios32p.h b/reactos/subsystems/ntvdm/bios/bios32/bios32p.h new file mode 100644 index 0000000000000..604fda30cc78a --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/bios32p.h @@ -0,0 +1,45 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bios32.h + * PURPOSE: VDM 32-bit BIOS + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _BIOS32P_H_ +#define _BIOS32P_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" +#include "../bios.h" + +/**/ #include "callback.h" /**/ + +/* DEFINES ********************************************************************/ + +#define BIOS_PIC_MASTER_INT 0x08 +#define BIOS_PIC_SLAVE_INT 0x70 + +#define BIOS_EQUIPMENT_INTERRUPT 0x11 +#define BIOS_MEMORY_SIZE 0x12 +#define BIOS_MISC_INTERRUPT 0x15 +#define BIOS_TIME_INTERRUPT 0x1A +#define BIOS_SYS_TIMER_INTERRUPT 0x1C + +/* FUNCTIONS ******************************************************************/ + +extern CALLBACK16 BiosContext; +#define RegisterBiosInt32(IntNumber, IntHandler) \ +do { \ + BiosContext.NextOffset += RegisterInt32(MAKELONG(BiosContext.NextOffset, \ + BiosContext.Segment), \ + (IntNumber), (IntHandler), NULL); \ +} while(0); + +VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func); +VOID PicIRQComplete(LPWORD Stack); + +#endif // _BIOS32P_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/kbdbios32.c b/reactos/subsystems/ntvdm/bios/bios32/kbdbios32.c new file mode 100644 index 0000000000000..9d95ccd368b96 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/kbdbios32.c @@ -0,0 +1,293 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: kbdbios32.c + * PURPOSE: VDM Keyboard 32-bit BIOS + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" + +#include "kbdbios32.h" +#include "../kbdbios.h" +#include "bios32p.h" + +#include "io.h" +#include "hardware/ps2.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static BYTE BiosKeyboardMap[256]; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static BOOLEAN BiosKbdBufferPush(WORD Data) +{ + /* Get the location of the element after the tail */ + WORD NextElement = Bda->KeybdBufferTail + sizeof(WORD); + + /* Wrap it around if it's at or beyond the end */ + if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart; + + /* If it's full, fail */ + if (NextElement == Bda->KeybdBufferHead) return FALSE; + + /* Put the value in the queue */ + *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data; + Bda->KeybdBufferTail += sizeof(WORD); + + /* Check if we are at, or have passed, the end of the buffer */ + if (Bda->KeybdBufferTail >= Bda->KeybdBufferEnd) + { + /* Return it to the beginning */ + Bda->KeybdBufferTail = Bda->KeybdBufferStart; + } + + /* Return success */ + return TRUE; +} + +static BOOLEAN BiosKbdBufferTop(LPWORD Data) +{ + /* If it's empty, fail */ + if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE; + + /* Otherwise, get the value and return success */ + *Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead)); + + return TRUE; +} + +static BOOLEAN BiosKbdBufferPop(VOID) +{ + /* If it's empty, fail */ + if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE; + + /* Remove the value from the queue */ + Bda->KeybdBufferHead += sizeof(WORD); + + /* Check if we are at, or have passed, the end of the buffer */ + if (Bda->KeybdBufferHead >= Bda->KeybdBufferEnd) + { + /* Return it to the beginning */ + Bda->KeybdBufferHead = Bda->KeybdBufferStart; + } + + /* Return success */ + return TRUE; +} + +static WORD BiosPeekCharacter(VOID) +{ + WORD CharacterData = 0; + + /* Get the key from the queue, but don't remove it */ + if (BiosKbdBufferTop(&CharacterData)) return CharacterData; + else return 0xFFFF; +} + +WORD BiosGetCharacter(VOID) +{ + WORD CharacterData = 0; + + /* Check if there is a key available */ + if (BiosKbdBufferTop(&CharacterData)) + { + /* A key was available, remove it from the queue */ + BiosKbdBufferPop(); + } + else + { + /* No key available. Set the handler CF to repeat the BOP */ + setCF(1); + // CharacterData = 0xFFFF; + } + + return CharacterData; +} + +static VOID WINAPI BiosKeyboardService(LPWORD Stack) +{ + switch (getAH()) + { + /* Wait for keystroke and read */ + case 0x00: + /* Wait for extended keystroke and read */ + case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h + { + /* Read the character (and wait if necessary) */ + setAX(BiosGetCharacter()); + break; + } + + /* Get keystroke status */ + case 0x01: + /* Get extended keystroke status */ + case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h + { + WORD Data = BiosPeekCharacter(); + + if (Data != 0xFFFF) + { + /* There is a character, clear ZF and return it */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF; + setAX(Data); + } + else + { + /* No character, set ZF */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF; + } + + break; + } + + /* Get shift status */ + case 0x02: + { + /* Return the lower byte of the keyboard shift status word */ + setAL(LOBYTE(Bda->KeybdShiftFlags)); + break; + } + + /* Reserved */ + case 0x04: + { + DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n"); + break; + } + + /* Push keystroke */ + case 0x05: + { + /* Return 0 if success, 1 if failure */ + setAL(BiosKbdBufferPush(getCX()) == FALSE); + break; + } + + /* Get extended shift status */ + case 0x12: + { + /* + * Be careful! The returned word is similar to Bda->KeybdShiftFlags + * but the high byte is organized differently: + * the bytes 2 and 3 of the high byte are not the same... + */ + WORD KeybdShiftFlags = (Bda->KeybdShiftFlags & 0xF3FF); + + /* Return the extended keyboard shift status word */ + setAX(KeybdShiftFlags); + break; + } + + default: + { + DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n", + getAH()); + } + } +} + +// Keyboard IRQ 1 +static VOID WINAPI BiosKeyboardIrq(LPWORD Stack) +{ + BYTE ScanCode, VirtualKey; + WORD Character; + + /* Get the scan code and virtual key code */ + ScanCode = IOReadB(PS2_DATA_PORT); + VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK); + + /* Check if this is a key press or release */ + if (!(ScanCode & (1 << 7))) + { + /* Key press */ + if (VirtualKey == VK_NUMLOCK || + VirtualKey == VK_CAPITAL || + VirtualKey == VK_SCROLL || + VirtualKey == VK_INSERT) + { + /* For toggle keys, toggle the lowest bit in the keyboard map */ + BiosKeyboardMap[VirtualKey] ^= ~(1 << 0); + } + + /* Set the highest bit */ + BiosKeyboardMap[VirtualKey] |= (1 << 7); + + /* Find out which character this is */ + Character = 0; + if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0) + { + /* Not ASCII */ + Character = 0; + } + + /* Push it onto the BIOS keyboard queue */ + BiosKbdBufferPush(MAKEWORD(Character, ScanCode)); + } + else + { + /* Key release, unset the highest bit */ + BiosKeyboardMap[VirtualKey] &= ~(1 << 7); + } + + /* Clear the keyboard flags */ + Bda->KeybdShiftFlags = 0; + + /* Set the appropriate flags based on the state */ + if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT; + if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT; + if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL; + if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT; + if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON; + if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON; + if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON; + if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON; + if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT; + if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT; + if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ; + if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE; + if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL; + if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK; + if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK; + if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT; + + PicIRQComplete(Stack); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN KbdBios32Initialize(VOID) +{ + /* Initialize the common Keyboard BIOS Support Library */ + if (!KbdBiosInitialize()) return FALSE; + + /* Initialize the BDA */ + Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer); + Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD); + Bda->KeybdBufferHead = Bda->KeybdBufferTail = 0; + + /* Register the BIOS 32-bit Interrupts */ + + /* Initialize software vector handlers */ + RegisterBiosInt32(BIOS_KBD_INTERRUPT, BiosKeyboardService); + + /* Set up the HW vector interrupts */ + EnableHwIRQ(1, BiosKeyboardIrq); + // EnableHwIRQ(12, BiosMouseIrq); + + return TRUE; +} + +VOID KbdBios32Cleanup(VOID) +{ + /* Cleanup the common Keyboard BIOS Support Library */ + KbdBiosCleanup(); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/kbdbios32.h b/reactos/subsystems/ntvdm/bios/bios32/kbdbios32.h new file mode 100644 index 0000000000000..b5f9c145a1244 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/kbdbios32.h @@ -0,0 +1,46 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: kbdbios32.h + * PURPOSE: VDM Keyboard 32-bit BIOS + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _KBDBIOS32_H_ +#define _KBDBIOS32_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +// #define BIOS_KBD_INTERRUPT 0x16 + +#define BIOS_KBD_BUFFER_SIZE 16 + +#define BDA_KBDFLAG_RSHIFT (1 << 0) +#define BDA_KBDFLAG_LSHIFT (1 << 1) +#define BDA_KBDFLAG_CTRL (1 << 2) +#define BDA_KBDFLAG_ALT (1 << 3) +#define BDA_KBDFLAG_SCROLL_ON (1 << 4) +#define BDA_KBDFLAG_NUMLOCK_ON (1 << 5) +#define BDA_KBDFLAG_CAPSLOCK_ON (1 << 6) +#define BDA_KBDFLAG_INSERT_ON (1 << 7) +#define BDA_KBDFLAG_RALT (1 << 8) +#define BDA_KBDFLAG_LALT (1 << 9) +#define BDA_KBDFLAG_SYSRQ (1 << 10) +#define BDA_KBDFLAG_PAUSE (1 << 11) +#define BDA_KBDFLAG_SCROLL (1 << 12) +#define BDA_KBDFLAG_NUMLOCK (1 << 13) +#define BDA_KBDFLAG_CAPSLOCK (1 << 14) +#define BDA_KBDFLAG_INSERT (1 << 15) + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN KbdBios32Initialize(VOID); +VOID KbdBios32Cleanup(VOID); + +#endif // _KBDBIOS32_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/vidbios32.c b/reactos/subsystems/ntvdm/bios/bios32/vidbios32.c new file mode 100644 index 0000000000000..732776c2db6db --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/vidbios32.c @@ -0,0 +1,41 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vidbios32.c + * PURPOSE: VDM Video 32-bit BIOS + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * NOTE: All of the real code is in bios/vidbios.c + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" + +#include "vidbios32.h" +#include "../vidbios.h" +#include "bios32p.h" + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN VidBios32Initialize(VOID) +{ + /* Initialize the common Video BIOS Support Library */ + if (!VidBiosInitialize()) return FALSE; + + /* Register the BIOS 32-bit Interrupts */ + RegisterBiosInt32(BIOS_VIDEO_INTERRUPT, VidBiosVideoService); + + return TRUE; +} + +VOID VidBios32Cleanup(VOID) +{ + /* Cleanup the common Video BIOS Support Library */ + VidBiosCleanup(); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/bios32/vidbios32.h b/reactos/subsystems/ntvdm/bios/bios32/vidbios32.h new file mode 100644 index 0000000000000..46b853bb61eee --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/bios32/vidbios32.h @@ -0,0 +1,29 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vidbios32.h + * PURPOSE: VDM Video 32-bit BIOS + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * NOTE: All of the real code is in bios/vidbios.c + */ + +#ifndef _VIDBIOS32_H_ +#define _VIDBIOS32_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +// #define BIOS_VIDEO_INTERRUPT 0x10 + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN VidBios32Initialize(VOID); +VOID VidBios32Cleanup(VOID); + +#endif // _VIDBIOS32_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/kbdbios.c b/reactos/subsystems/ntvdm/bios/kbdbios.c new file mode 100644 index 0000000000000..72550869f9d61 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/kbdbios.c @@ -0,0 +1,51 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: kbdbios.c + * PURPOSE: VDM Keyboard BIOS Support Library + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" +#include "bop.h" + +#include "bios.h" +// #include "kbdbios.h" + +/* DEFINES ********************************************************************/ + +/* BOP Identifiers */ +#define BOP_KBD_IRQ 0x09 +#define BOP_KBD_INT 0x16 + +/* PUBLIC FUNCTIONS ***********************************************************/ + +static VOID WINAPI KbdBiosIRQ(LPWORD Stack) +{ + DPRINT1("KbdBiosIRQ is UNIMPLEMENTED\n"); +} + +static VOID WINAPI KbdBiosINT(LPWORD Stack) +{ + DPRINT1("KbdBiosINT is UNIMPLEMENTED\n"); +} + +BOOLEAN KbdBiosInitialize(VOID) +{ + /* Register the BIOS support BOPs */ + RegisterBop(BOP_KBD_IRQ, KbdBiosIRQ); + RegisterBop(BOP_KBD_INT, KbdBiosINT); + + return TRUE; +} + +VOID KbdBiosCleanup(VOID) +{ +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/kbdbios.h b/reactos/subsystems/ntvdm/bios/kbdbios.h new file mode 100644 index 0000000000000..46486785b2ba9 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/kbdbios.h @@ -0,0 +1,31 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: kbdbios.h + * PURPOSE: VDM Keyboard BIOS Support Library + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _KBDBIOS_H_ +#define _KBDBIOS_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define BIOS_KBD_INTERRUPT 0x16 + +#define BIOS_KBD_BUFFER_SIZE 16 + +/* FUNCTIONS ******************************************************************/ + +WORD BiosGetCharacter(VOID); + +BOOLEAN KbdBiosInitialize(VOID); +VOID KbdBiosCleanup(VOID); + +#endif // _KBDBIOS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/rom.c b/reactos/subsystems/ntvdm/bios/rom.c new file mode 100644 index 0000000000000..e4dbc6bf5f692 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/rom.c @@ -0,0 +1,227 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: rom.c + * PURPOSE: ROM Support Functions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" +#include "utils.h" + +#include "rom.h" + +/* PRIVATE FUNCTIONS **********************************************************/ + +static HANDLE +OpenRomFile(IN PCSTR RomFileName, + OUT PULONG RomSize OPTIONAL) +{ + HANDLE hRomFile; + ULONG ulRomSize = 0; + + /* Open the ROM image file */ + hRomFile = FileOpen(RomFileName, &ulRomSize); + + /* If we failed, bail out */ + if (hRomFile == NULL) return NULL; + + /* + * The size of the ROM image file is at most 256kB. For instance, + * the SeaBIOS image, which includes also expansion ROMs inside it, + * covers the range C000:0000 to F000:FFFF . + */ + if (ulRomSize > 0x40000) + { + /* We failed, bail out */ + DPRINT1("ROM image size 0x%lx too large, expected at most 0x40000 (256kB)", ulRomSize); + FileClose(hRomFile); + return NULL; + } + + /* Success, return file handle and size if needed */ + if (RomSize) *RomSize = ulRomSize; + return hRomFile; +} + +static BOOLEAN +LoadRomFileByHandle(IN HANDLE RomFileHandle, + IN PVOID RomLocation, + IN ULONG RomSize, + OUT PULONG BytesRead) +{ + /* + * The size of the ROM image file is at most 256kB. For instance, + * the SeaBIOS image, which includes also expansion ROMs inside it, + * covers the range C000:0000 to F000:FFFF . + */ + if (RomSize > 0x40000) + { + DPRINT1("ROM image size 0x%lx too large, expected at most 0x40000 (256kB)", RomSize); + return FALSE; + } + + /* Attempt to load the ROM image file into memory */ + return FileLoadByHandle(RomFileHandle, + REAL_TO_PHYS(RomLocation), + RomSize, + BytesRead); +} + +static UCHAR +ComputeChecksum(IN ULONG RomLocation, + IN ULONG RomSize) +{ + ULONG RomLastAddress = RomLocation + RomSize; + UCHAR Sum = 0x00; // Using a UCHAR guarantees that we wrap at 0xFF i.e. we do a sum modulo 0x100. + + while (RomLocation < RomLastAddress) + { + Sum += *(PUCHAR)REAL_TO_PHYS(RomLocation); + ++RomLocation; + } + + return Sum; +} + +static VOID +InitRomRange(IN PCALLBACK16 Context, + IN ULONG Start, + IN ULONG End, + IN ULONG Increment) +{ + ULONG Address, AddressBoot; + ULONG RomSize; + UCHAR Checksum; + + for (Address = Start; Address < End; Address += Increment) + { + /* Does the ROM have a valid signature? */ + if (*(PUSHORT)REAL_TO_PHYS(Address) == OPTION_ROM_SIGNATURE) + { + /* Check the control sum of the ROM */ + + /* + * If this is an adapter ROM (Start: C8000, End: E0000), its + * reported size is stored in byte 2 of the ROM. + * + * If this is an expansion ROM (Start: E0000, End: F0000), + * its real length is 64kB. + */ + RomSize = *(PUCHAR)REAL_TO_PHYS(Address + 2) * 512; + if (Address >= 0xE0000) RomSize = 0x10000; + + Checksum = ComputeChecksum(Address, RomSize); + if (Checksum == 0x00) + { + AddressBoot = Address + 3; + DPRINT1("Going to run @ address 0x%p\n", AddressBoot); + + AddressBoot = MAKELONG((AddressBoot & 0xFFFF), (AddressBoot & 0xF0000) >> 4); + // setDS((Address & 0xF0000) >> 4); + setDS((Address & 0xFF000) >> 4); + RunCallback16(Context, AddressBoot); + // Call16((AddressBoot & 0xF0000) >> 4, (AddressBoot & 0xFFFF)); + + DPRINT1("Rom @ address 0x%p initialized\n", Address); + } + else + { + DPRINT1("Rom @ address 0x%p has invalid checksum of 0x%02x\n", Address, Checksum); + } + } + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN +LoadBios(IN PCSTR BiosFileName, + OUT PVOID* BiosLocation OPTIONAL, + OUT PULONG BiosSize OPTIONAL) +{ + BOOLEAN Success; + HANDLE hBiosFile; + ULONG ulBiosSize = 0; + PVOID pBiosLocation; + + /* Open the BIOS image file */ + hBiosFile = OpenRomFile(BiosFileName, &ulBiosSize); + + /* If we failed, bail out */ + if (hBiosFile == NULL) return FALSE; + + /* BIOS location needs to be aligned on 32-bit boundary */ + // (PVOID)((ULONG_PTR)BaseAddress + ROM_AREA_END + 1 - ulBiosSize) + pBiosLocation = MEM_ALIGN_DOWN(TO_LINEAR(0xF000, 0xFFFF) + 1 - ulBiosSize, sizeof(ULONG)); + + /* Attempt to load the BIOS image file into memory */ + Success = LoadRomFileByHandle(hBiosFile, + pBiosLocation, + ulBiosSize, + &ulBiosSize); + DPRINT1("BIOS loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + /* Close the BIOS image file */ + FileClose(hBiosFile); + + /* In case of success, return BIOS location and size if needed */ + if (Success) + { + if (BiosLocation) *BiosLocation = pBiosLocation; + if (BiosSize) *BiosSize = ulBiosSize; + } + + return Success; +} + +BOOLEAN +LoadRom(IN PCSTR RomFileName, + IN PVOID RomLocation, + OUT PULONG RomSize OPTIONAL) +{ + BOOLEAN Success; + HANDLE hRomFile; + ULONG ulRomSize = 0; + + /* Open the ROM image file */ + hRomFile = OpenRomFile(RomFileName, &ulRomSize); + + /* If we failed, bail out */ + if (hRomFile == NULL) return FALSE; + + /* Attempt to load the ROM image file into memory */ + Success = LoadRomFileByHandle(hRomFile, + RomLocation, + ulRomSize, + &ulRomSize); + DPRINT1("ROM loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + /* Close the ROM image file and return */ + FileClose(hRomFile); + + /* In case of success, return ROM size if needed */ + if (Success) + { + if (RomSize) *RomSize = ulRomSize; + } + + return Success; +} + +VOID +SearchAndInitRoms(IN PCALLBACK16 Context) +{ + /* Adapters ROMs -- Start: C8000, End: E0000, 2kB blocks */ + InitRomRange(Context, 0xC8000, 0xE0000, 0x0800); + + /* Expansion ROM -- Start: E0000, End: F0000, 64kB block */ + InitRomRange(Context, 0xE0000, 0xEFFFF, 0x10000); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/rom.h b/reactos/subsystems/ntvdm/bios/rom.h new file mode 100644 index 0000000000000..01f95e85dc802 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/rom.h @@ -0,0 +1,40 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: rom.h + * PURPOSE: ROM Support Functions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _ROM_H_ +#define _ROM_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define ROM_AREA_START 0xE0000 +#define ROM_AREA_END 0xFFFFF + +#define OPTION_ROM_SIGNATURE 0xAA55 + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN +LoadBios(IN PCSTR BiosFileName, + OUT PVOID* BiosLocation OPTIONAL, + OUT PULONG BiosSize OPTIONAL); + +BOOLEAN +LoadRom(IN PCSTR RomFileName, + IN PVOID RomLocation, + OUT PULONG RomSize OPTIONAL); + +VOID +SearchAndInitRoms(IN PCALLBACK16 Context); + +#endif // _ROM_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/vidbios.c b/reactos/subsystems/ntvdm/bios/vidbios.c new file mode 100644 index 0000000000000..cec5a7060e209 --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/vidbios.c @@ -0,0 +1,1560 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vidbios.c + * PURPOSE: VDM Video BIOS Support Library + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" +#include "bop.h" + +#include "bios.h" +// #include "vidbios.h" + +#include "io.h" +#include "hardware/vga.h" + +/* DEFINES ********************************************************************/ + +/* BOP Identifiers */ +#define BOP_VIDEO_INT 0x10 + +/* MACROS *********************************************************************/ + +// +// These macros are defined for ease-of-use of some VGA I/O ports +// whose addresses depend whether we are in Monochrome or Colour mode. +// +#define VGA_INSTAT1_READ Bda->CrtBasePort + 6 // VGA_INSTAT1_READ_MONO or VGA_INSTAT1_READ_COLOR +#define VGA_CRTC_INDEX Bda->CrtBasePort // VGA_CRTC_INDEX_MONO or VGA_CRTC_INDEX_COLOR +#define VGA_CRTC_DATA Bda->CrtBasePort + 1 // VGA_CRTC_DATA_MONO or VGA_CRTC_DATA_COLOR + +/* PRIVATE VARIABLES **********************************************************/ + +/* + * VGA Register Configurations for BIOS Video Modes + * The configurations come from DOSBox. + */ +static VGA_REGISTERS VideoMode_40x25_text = +{ + /* Miscellaneous Register */ + 0x67, + + /* Sequencer Registers */ + {0x00, 0x08, 0x03, 0x00, 0x07}, + + /* CRTC Registers */ + {0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00} +}; + +static VGA_REGISTERS VideoMode_80x25_text = +{ + /* Miscellaneous Register */ + 0x67, + + /* Sequencer Registers */ + {0x00, 0x00, 0x03, 0x00, 0x07}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00} +}; + +static VGA_REGISTERS VideoMode_320x200_4color = +{ + /* Miscellaneous Register */ + 0x63, + + /* Sequencer Registers */ + {0x00, 0x09, 0x03, 0x00, 0x02}, + + /* CRTC Registers */ + {0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_640x200_2color = +{ + /* Miscellaneous Register */ + 0x63, + + /* Sequencer Registers */ + {0x00, 0x09, 0x0F, 0x00, 0x02}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xC2, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_320x200_16color = +{ + /* Miscellaneous Register */ + 0x63, + + /* Sequencer Registers */ + {0x00, 0x09, 0x0F, 0x00, 0x02}, + + /* CRTC Registers */ + {0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xE3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF}, + + /* AC Registers */ +// {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, +// 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00} + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_640x200_16color = +{ + /* Miscellaneous Register */ + 0x63, + + /* Sequencer Registers */ + {0x00, 0x01, 0x0F, 0x00, 0x02}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x00, 0x96, 0xB9, 0xE3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF}, + + /* AC Registers */ +// {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, +// 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00} + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_640x350_16color = +{ + /* Miscellaneous Register */ + 0xA3, + + /* Sequencer Registers */ + {0x00, 0x01, 0x0F, 0x00, 0x02}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5D, 0x28, 0x0F, 0x63, 0xBA, 0xE3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_640x480_2color = +{ + /* Miscellaneous Register */ + 0xE3, + + /* Sequencer Registers */ + {0x00, 0x01, 0x0F, 0x00, 0x02}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xC3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_640x480_16color = +{ + /* Miscellaneous Register */ + 0xE3, + + /* Sequencer Registers */ + {0x00, 0x01, 0x0F, 0x00, 0x02}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00} +}; + +static VGA_REGISTERS VideoMode_320x200_256color = +{ + /* Miscellaneous Register */ + 0x63, + + /* Sequencer Registers */ + {0x00, 0x01, 0x0F, 0x00, 0x0E}, + + /* CRTC Registers */ + {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, + 0xFF}, + + /* GC Registers */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF}, + + /* AC Registers */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00} +}; + +/* See http://wiki.osdev.org/Drawing_In_Protected_Mode#Locating_Video_Memory */ +static PVGA_REGISTERS VideoModes[BIOS_MAX_VIDEO_MODE + 1] = +{ + &VideoMode_40x25_text, /* Mode 00h */ // 16 color (mono) + &VideoMode_40x25_text, /* Mode 01h */ // 16 color + &VideoMode_80x25_text, /* Mode 02h */ // 16 color (mono) + &VideoMode_80x25_text, /* Mode 03h */ // 16 color + &VideoMode_320x200_4color, /* Mode 04h */ // CGA 4 color + &VideoMode_320x200_4color, /* Mode 05h */ // CGA same (m) + &VideoMode_640x200_2color, /* Mode 06h */ // CGA 640*200 2 color + NULL, /* Mode 07h */ // MDA monochrome text 80*25 + NULL, /* Mode 08h */ // PCjr + NULL, /* Mode 09h */ // PCjr + NULL, /* Mode 0Ah */ // PCjr + NULL, /* Mode 0Bh */ // Reserved + NULL, /* Mode 0Ch */ // Reserved + &VideoMode_320x200_16color, /* Mode 0Dh */ // EGA 320*200 16 color + &VideoMode_640x200_16color, /* Mode 0Eh */ // EGA 640*200 16 color + NULL, /* Mode 0Fh */ // EGA 640*350 mono + &VideoMode_640x350_16color, /* Mode 10h */ // EGA 640*350 HiRes 16 color + &VideoMode_640x480_2color, /* Mode 11h */ // VGA 640*480 mono + &VideoMode_640x480_16color, /* Mode 12h */ // VGA + &VideoMode_320x200_256color, /* Mode 13h */ // VGA +}; + +// FIXME: Are they computable with the previous data ?? +// Values taken from DOSBox. +static WORD VideoModePageSize[BIOS_MAX_VIDEO_MODE + 1] = +{ + 0x0800, 0x0800, 0x1000, 0x1000, + 0x4000, 0x4000, 0x4000, 0x1000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2000, 0x4000, 0x8000, + 0x8000, 0xA000, 0xA000, 0x2000 +}; + +/* + * BIOS Mode Palettes + * + * Many people have different versions of those palettes + * (e.g. DOSBox, http://www.brokenthorn.com/Resources/OSDevVid2.html , + * etc...) A choice should be made at some point. + */ + +// This is the same as EgaPalette__HiRes +static CONST COLORREF TextPalette[VGA_MAX_COLORS / 4] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0xAA, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x00, 0x00, 0x55), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xAA, 0x55), RGB(0x00, 0xAA, 0xFF), + RGB(0xAA, 0x00, 0x55), RGB(0xAA, 0x00, 0xFF), RGB(0xAA, 0xAA, 0x55), RGB(0xAA, 0xAA, 0xFF), + + RGB(0x00, 0x55, 0x00), RGB(0x00, 0x55, 0xAA), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xAA), + RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0x55, 0xAA), RGB(0xAA, 0xFF, 0x00), RGB(0xAA, 0xFF, 0xAA), + RGB(0x00, 0x55, 0x55), RGB(0x00, 0x55, 0xFF), RGB(0x00, 0xFF, 0x55), RGB(0x00, 0xFF, 0xFF), + RGB(0xAA, 0x55, 0x55), RGB(0xAA, 0x55, 0xFF), RGB(0xAA, 0xFF, 0x55), RGB(0xAA, 0xFF, 0xFF), + + + RGB(0x55, 0x00, 0x00), RGB(0x55, 0x00, 0xAA), RGB(0x55, 0xAA, 0x00), RGB(0x55, 0xAA, 0xAA), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xAA), RGB(0xFF, 0xAA, 0x00), RGB(0xFF, 0xAA, 0xAA), + RGB(0x55, 0x00, 0x55), RGB(0x55, 0x00, 0xFF), RGB(0x55, 0xAA, 0x55), RGB(0x55, 0xAA, 0xFF), + RGB(0xFF, 0x00, 0x55), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xAA, 0x55), RGB(0xFF, 0xAA, 0xFF), + + RGB(0x55, 0x55, 0x00), RGB(0x55, 0x55, 0xAA), RGB(0x55, 0xFF, 0x00), RGB(0x55, 0xFF, 0xAA), + RGB(0xFF, 0x55, 0x00), RGB(0xFF, 0x55, 0xAA), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF) +}; + +// Unused at the moment +static CONST COLORREF mtext_palette[64] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), + + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF) +}; + +// Unused at the moment +static CONST COLORREF mtext_s3_palette[64] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), + + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), RGB(0xAA, 0xAA, 0xAA), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), + RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF) +}; + +// Unused at the moment +static CONST COLORREF CgaPalette[16] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF) +}; + +// Unused at the moment +static CONST COLORREF CgaPalette2[VGA_MAX_COLORS / 4] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF) +}; + +static CONST COLORREF EgaPalette___16ColorFixed_DOSBox[VGA_MAX_COLORS / 4] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + + + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + + + + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + + + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF) +}; + +// This is the same as TextPalette +static CONST COLORREF EgaPalette__HiRes[VGA_MAX_COLORS / 4] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0xAA, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x00, 0x00, 0x55), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xAA, 0x55), RGB(0x00, 0xAA, 0xFF), + RGB(0xAA, 0x00, 0x55), RGB(0xAA, 0x00, 0xFF), RGB(0xAA, 0xAA, 0x55), RGB(0xAA, 0xAA, 0xFF), + + RGB(0x00, 0x55, 0x00), RGB(0x00, 0x55, 0xAA), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xAA), + RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0x55, 0xAA), RGB(0xAA, 0xFF, 0x00), RGB(0xAA, 0xFF, 0xAA), + RGB(0x00, 0x55, 0x55), RGB(0x00, 0x55, 0xFF), RGB(0x00, 0xFF, 0x55), RGB(0x00, 0xFF, 0xFF), + RGB(0xAA, 0x55, 0x55), RGB(0xAA, 0x55, 0xFF), RGB(0xAA, 0xFF, 0x55), RGB(0xAA, 0xFF, 0xFF), + + + RGB(0x55, 0x00, 0x00), RGB(0x55, 0x00, 0xAA), RGB(0x55, 0xAA, 0x00), RGB(0x55, 0xAA, 0xAA), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xAA), RGB(0xFF, 0xAA, 0x00), RGB(0xFF, 0xAA, 0xAA), + RGB(0x55, 0x00, 0x55), RGB(0x55, 0x00, 0xFF), RGB(0x55, 0xAA, 0x55), RGB(0x55, 0xAA, 0xFF), + RGB(0xFF, 0x00, 0x55), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xAA, 0x55), RGB(0xFF, 0xAA, 0xFF), + + RGB(0x55, 0x55, 0x00), RGB(0x55, 0x55, 0xAA), RGB(0x55, 0xFF, 0x00), RGB(0x55, 0xFF, 0xAA), + RGB(0xFF, 0x55, 0x00), RGB(0xFF, 0x55, 0xAA), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF) +}; + +#define USE_REACTOS_COLORS +// #define USE_DOSBOX_COLORS + +/* + * Same palette as the default one 'VgaDefaultPalette' in vga.c + */ +#if defined(USE_REACTOS_COLORS) + +// ReactOS colors +static CONST COLORREF VgaPalette[VGA_MAX_COLORS] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35), + RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75), + RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA), + RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF), + RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00), + RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00), + RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE), + RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF), + RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF), + RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E), + RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82), + RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82), + RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF), + RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF), + RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF), + RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA), + RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA), + RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA), + RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF), + RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF), + RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71), + RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C), + RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00), + RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00), + RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55), + RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71), + RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71), + RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45), + RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39), + RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39), + RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61), + RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71), + RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71), + RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59), + RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51), + RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51), + RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69), + RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71), + RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41), + RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10), + RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00), + RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00), + RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31), + RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41), + RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41), + RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28), + RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20), + RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20), + RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39), + RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41), + RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41), + RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31), + RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D), + RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D), + RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D), + RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00) +}; + +#elif defined(USE_DOSBOX_COLORS) + +// DOSBox colors +static CONST COLORREF VgaPalette[VGA_MAX_COLORS] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0x00), RGB(0x14, 0x14, 0x14), RGB(0x20, 0x20, 0x20), RGB(0x2C, 0x2C, 0x2C), + RGB(0x38, 0x38, 0x38), RGB(0x45, 0x45, 0x45), RGB(0x51, 0x51, 0x51), RGB(0x61, 0x61, 0x61), + RGB(0x71, 0x71, 0x71), RGB(0x82, 0x82, 0x82), RGB(0x92, 0x92, 0x92), RGB(0xA2, 0xA2, 0xA2), + RGB(0xB6, 0xB6, 0xB6), RGB(0xCB, 0xCB, 0xCB), RGB(0xE3, 0xE3, 0xE3), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x7D, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF), + RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x7D), RGB(0xFF, 0x00, 0x41), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x7D, 0x00), RGB(0xFF, 0xBE, 0x00), + RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x7D, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00), + RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x7D), RGB(0x00, 0xFF, 0xBE), + RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x7D, 0xFF), RGB(0x00, 0x41, 0xFF), + RGB(0x7D, 0x7D, 0xFF), RGB(0x9E, 0x7D, 0xFF), RGB(0xBE, 0x7D, 0xFF), RGB(0xDF, 0x7D, 0xFF), + RGB(0xFF, 0x7D, 0xFF), RGB(0xFF, 0x7D, 0xDF), RGB(0xFF, 0x7D, 0xBE), RGB(0xFF, 0x7D, 0x9E), + + RGB(0xFF, 0x7D, 0x7D), RGB(0xFF, 0x9E, 0x7D), RGB(0xFF, 0xBE, 0x7D), RGB(0xFF, 0xDF, 0x7D), + RGB(0xFF, 0xFF, 0x7D), RGB(0xDF, 0xFF, 0x7D), RGB(0xBE, 0xFF, 0x7D), RGB(0x9E, 0xFF, 0x7D), + RGB(0x7D, 0xFF, 0x7D), RGB(0x7D, 0xFF, 0x9E), RGB(0x7D, 0xFF, 0xBE), RGB(0x7D, 0xFF, 0xDF), + RGB(0x7D, 0xFF, 0xFF), RGB(0x7D, 0xDF, 0xFF), RGB(0x7D, 0xBE, 0xFF), RGB(0x7D, 0x9E, 0xFF), + RGB(0xB6, 0xB6, 0xFF), RGB(0xC7, 0xB6, 0xFF), RGB(0xDB, 0xB6, 0xFF), RGB(0xEB, 0xB6, 0xFF), + RGB(0xFF, 0xB6, 0xFF), RGB(0xFF, 0xB6, 0xEB), RGB(0xFF, 0xB6, 0xDB), RGB(0xFF, 0xB6, 0xC7), + RGB(0xFF, 0xB6, 0xB6), RGB(0xFF, 0xC7, 0xB6), RGB(0xFF, 0xDB, 0xB6), RGB(0xFF, 0xEB, 0xB6), + RGB(0xFF, 0xFF, 0xB6), RGB(0xEB, 0xFF, 0xB6), RGB(0xDB, 0xFF, 0xB6), RGB(0xC7, 0xFF, 0xB6), + RGB(0xB6, 0xFF, 0xB6), RGB(0xB6, 0xFF, 0xC7), RGB(0xB6, 0xFF, 0xDB), RGB(0xB6, 0xFF, 0xEB), + RGB(0xB6, 0xFF, 0xFF), RGB(0xB6, 0xEB, 0xFF), RGB(0xB6, 0xDB, 0xFF), RGB(0xB6, 0xC7, 0xFF), + RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x38, 0x00, 0x71), RGB(0x55, 0x00, 0x71), + RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x38), RGB(0x71, 0x00, 0x1C), + RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x38, 0x00), RGB(0x71, 0x55, 0x00), + RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x38, 0x71, 0x00), RGB(0x1C, 0x71, 0x00), + RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x38), RGB(0x00, 0x71, 0x55), + RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x38, 0x71), RGB(0x00, 0x1C, 0x71), + + RGB(0x38, 0x38, 0x71), RGB(0x45, 0x38, 0x71), RGB(0x55, 0x38, 0x71), RGB(0x61, 0x38, 0x71), + RGB(0x71, 0x38, 0x71), RGB(0x71, 0x38, 0x61), RGB(0x71, 0x38, 0x55), RGB(0x71, 0x38, 0x45), + RGB(0x71, 0x38, 0x38), RGB(0x71, 0x45, 0x38), RGB(0x71, 0x55, 0x38), RGB(0x71, 0x61, 0x38), + RGB(0x71, 0x71, 0x38), RGB(0x61, 0x71, 0x38), RGB(0x55, 0x71, 0x38), RGB(0x45, 0x71, 0x38), + RGB(0x38, 0x71, 0x38), RGB(0x38, 0x71, 0x45), RGB(0x38, 0x71, 0x55), RGB(0x38, 0x71, 0x61), + RGB(0x38, 0x71, 0x71), RGB(0x38, 0x61, 0x71), RGB(0x38, 0x55, 0x71), RGB(0x38, 0x45, 0x71), + RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71), + RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59), + RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51), + RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51), + RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69), + RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71), + RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x30, 0x00, 0x41), + RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x30), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10), + RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x30, 0x00), + RGB(0x41, 0x41, 0x00), RGB(0x30, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00), + + RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x30), + RGB(0x00, 0x41, 0x41), RGB(0x00, 0x30, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41), + RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x30, 0x20, 0x41), RGB(0x38, 0x20, 0x41), + RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x38), RGB(0x41, 0x20, 0x30), RGB(0x41, 0x20, 0x28), + RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x30, 0x20), RGB(0x41, 0x38, 0x20), + RGB(0x41, 0x41, 0x20), RGB(0x38, 0x41, 0x20), RGB(0x30, 0x41, 0x20), RGB(0x28, 0x41, 0x20), + RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x30), RGB(0x20, 0x41, 0x38), + RGB(0x20, 0x41, 0x41), RGB(0x20, 0x38, 0x41), RGB(0x20, 0x30, 0x41), RGB(0x20, 0x28, 0x41), + RGB(0x2C, 0x2C, 0x41), RGB(0x30, 0x2C, 0x41), RGB(0x34, 0x2C, 0x41), RGB(0x3C, 0x2C, 0x41), + RGB(0x41, 0x2C, 0x41), RGB(0x41, 0x2C, 0x3C), RGB(0x41, 0x2C, 0x34), RGB(0x41, 0x2C, 0x30), + RGB(0x41, 0x2C, 0x2C), RGB(0x41, 0x30, 0x2C), RGB(0x41, 0x34, 0x2C), RGB(0x41, 0x3C, 0x2C), + RGB(0x41, 0x41, 0x2C), RGB(0x3C, 0x41, 0x2C), RGB(0x34, 0x41, 0x2C), RGB(0x30, 0x41, 0x2C), + RGB(0x2C, 0x41, 0x2C), RGB(0x2C, 0x41, 0x30), RGB(0x2C, 0x41, 0x34), RGB(0x2C, 0x41, 0x3C), + RGB(0x2C, 0x41, 0x41), RGB(0x2C, 0x3C, 0x41), RGB(0x2C, 0x34, 0x41), RGB(0x2C, 0x30, 0x41), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00) +}; + +#endif + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID VidBiosReadWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page) +{ + INT i, j; + INT Counter = 0; + WORD Character; + DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize); + + for (i = Rectangle.Top; i <= Rectangle.Bottom; i++) + { + for (j = Rectangle.Left; j <= Rectangle.Right; j++) + { + /* Read from video memory */ + EmulatorReadMemory(&EmulatorContext, + VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD), + (LPVOID)&Character, + sizeof(WORD)); + + /* Write the data to the buffer in row order */ + Buffer[Counter++] = Character; + } + } +} + +static VOID VidBiosWriteWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page) +{ + INT i, j; + INT Counter = 0; + WORD Character; + DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize); + + for (i = Rectangle.Top; i <= Rectangle.Bottom; i++) + { + for (j = Rectangle.Left; j <= Rectangle.Right; j++) + { + Character = Buffer[Counter++]; + + /* Write to video memory */ + EmulatorWriteMemory(&EmulatorContext, + VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD), + (LPVOID)&Character, + sizeof(WORD)); + } + } +} + +static BOOLEAN VidBiosScrollWindow(INT Direction, + DWORD Amount, + SMALL_RECT Rectangle, + BYTE Page, + BYTE FillAttribute) +{ + DWORD i; + LPWORD WindowData; + WORD WindowWidth = Rectangle.Right - Rectangle.Left + 1; + WORD WindowHeight = Rectangle.Bottom - Rectangle.Top + 1; + DWORD WindowSize = WindowWidth * WindowHeight; + + /* Allocate a buffer for the window */ + WindowData = (LPWORD)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + WindowSize * sizeof(WORD)); + if (WindowData == NULL) return FALSE; + + /* Read the window data */ + VidBiosReadWindow(WindowData, Rectangle, Page); + + if ((Amount == 0) + || (((Direction == SCROLL_DIRECTION_UP) + || (Direction == SCROLL_DIRECTION_DOWN)) + && (Amount >= WindowHeight)) + || (((Direction == SCROLL_DIRECTION_LEFT) + || (Direction == SCROLL_DIRECTION_RIGHT)) + && (Amount >= WindowWidth))) + { + /* Fill the window */ + for (i = 0; i < WindowSize; i++) + { + WindowData[i] = MAKEWORD(' ', FillAttribute); + } + + goto Done; + } + + switch (Direction) + { + case SCROLL_DIRECTION_UP: + { + RtlMoveMemory(WindowData, + &WindowData[WindowWidth * Amount], + (WindowSize - WindowWidth * Amount) * sizeof(WORD)); + + for (i = 0; i < Amount * WindowWidth; i++) + { + WindowData[WindowSize - i - 1] = MAKEWORD(' ', FillAttribute); + } + + break; + } + + case SCROLL_DIRECTION_DOWN: + { + RtlMoveMemory(&WindowData[WindowWidth * Amount], + WindowData, + (WindowSize - WindowWidth * Amount) * sizeof(WORD)); + + for (i = 0; i < Amount * WindowWidth; i++) + { + WindowData[i] = MAKEWORD(' ', FillAttribute); + } + + break; + } + + default: + { + // TODO: NOT IMPLEMENTED! + UNIMPLEMENTED; + } + } + +Done: + /* Write back the window data */ + VidBiosWriteWindow(WindowData, Rectangle, Page); + + /* Free the window buffer */ + HeapFree(GetProcessHeap(), 0, WindowData); + + return TRUE; +} + +static BOOLEAN VgaSetRegisters(PVGA_REGISTERS Registers) +{ + INT i; + + if (Registers == NULL) return FALSE; + + /* Disable interrupts */ + setIF(0); + + /* + * Set the CRT base address according to the selected mode, + * monochrome or color. The following macros: + * VGA_INSTAT1_READ, VGA_CRTC_INDEX and VGA_CRTC_DATA are then + * used to access the correct VGA I/O ports. + */ + Bda->CrtBasePort = (Registers->Misc & 0x01) ? VGA_CRTC_INDEX_COLOR + : VGA_CRTC_INDEX_MONO; + + /* Write the misc register */ + IOWriteB(VGA_MISC_WRITE, Registers->Misc); + + /* Synchronous reset on */ + IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_RESET_REG); + IOWriteB(VGA_SEQ_DATA , VGA_SEQ_RESET_AR); + + /* Write the sequencer registers */ + for (i = 1; i < VGA_SEQ_MAX_REG; i++) + { + IOWriteB(VGA_SEQ_INDEX, i); + IOWriteB(VGA_SEQ_DATA, Registers->Sequencer[i]); + } + + /* Synchronous reset off */ + IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_RESET_REG); + IOWriteB(VGA_SEQ_DATA , VGA_SEQ_RESET_SR | VGA_SEQ_RESET_AR); + + /* Unlock CRTC registers 0-7 */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_END_HORZ_BLANKING_REG); + IOWriteB(VGA_CRTC_DATA, IOReadB(VGA_CRTC_DATA) | 0x80); + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_VERT_RETRACE_END_REG); + IOWriteB(VGA_CRTC_DATA, IOReadB(VGA_CRTC_DATA) & ~0x80); + // Make sure they remain unlocked + Registers->CRT[VGA_CRTC_END_HORZ_BLANKING_REG] |= 0x80; + Registers->CRT[VGA_CRTC_VERT_RETRACE_END_REG] &= ~0x80; + + /* Write the CRTC registers */ + for (i = 0; i < VGA_CRTC_MAX_REG; i++) + { + IOWriteB(VGA_CRTC_INDEX, i); + IOWriteB(VGA_CRTC_DATA, Registers->CRT[i]); + } + + /* Write the GC registers */ + for (i = 0; i < VGA_GC_MAX_REG; i++) + { + IOWriteB(VGA_GC_INDEX, i); + IOWriteB(VGA_GC_DATA, Registers->Graphics[i]); + } + + /* Write the AC registers */ + // DbgPrint("\n"); + for (i = 0; i < VGA_AC_MAX_REG; i++) + { + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, i); + IOWriteB(VGA_AC_WRITE, Registers->Attribute[i]); + // DbgPrint("Registers->Attribute[%d] = %d\n", i, Registers->Attribute[i]); + } + // DbgPrint("\n"); + + /* Set the PEL mask */ + IOWriteB(VGA_DAC_MASK, 0xFF); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + + /* Enable interrupts */ + setIF(1); + + return TRUE; +} + +static VOID VgaSetPalette(const COLORREF* Palette, ULONG Size) +{ + ULONG i; + + // /* Disable screen and enable palette access */ + // IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + // IOWriteB(VGA_AC_INDEX, 0x00); + + for (i = 0; i < Size; i++) + { + IOWriteB(VGA_DAC_WRITE_INDEX, i); + IOWriteB(VGA_DAC_DATA, VGA_COLOR_TO_DAC(GetRValue(Palette[i]))); + IOWriteB(VGA_DAC_DATA, VGA_COLOR_TO_DAC(GetGValue(Palette[i]))); + IOWriteB(VGA_DAC_DATA, VGA_COLOR_TO_DAC(GetBValue(Palette[i]))); + } + + /* The following step might be optional */ + for (i = Size; i < VGA_MAX_COLORS; i++) + { + IOWriteB(VGA_DAC_WRITE_INDEX, i); + IOWriteB(VGA_DAC_DATA, VGA_COLOR_TO_DAC(0x00)); + IOWriteB(VGA_DAC_DATA, VGA_COLOR_TO_DAC(0x00)); + IOWriteB(VGA_DAC_DATA, VGA_COLOR_TO_DAC(0x00)); + } + + /* Enable screen and disable palette access */ + // IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + // IOWriteB(VGA_AC_INDEX, 0x20); +} + +static VOID VgaChangePalette(BYTE ModeNumber) +{ + const COLORREF* Palette; + ULONG Size; + + if (ModeNumber >= 0x13) + { + /* VGA modes */ + Palette = VgaPalette; + Size = sizeof(VgaPalette)/sizeof(VgaPalette[0]); + } + else if (ModeNumber == 0x10) + { + /* EGA HiRes mode */ + Palette = EgaPalette__HiRes; + Size = sizeof(EgaPalette__HiRes)/sizeof(EgaPalette__HiRes[0]); + } + else // if ((ModeNumber == 0x0D) || (ModeNumber == 0x0E)) + { + /* EGA modes */ + Palette = EgaPalette___16ColorFixed_DOSBox; + Size = sizeof(EgaPalette___16ColorFixed_DOSBox)/sizeof(EgaPalette___16ColorFixed_DOSBox[0]); + } + + VgaSetPalette(Palette, Size); +} + +static VOID VidBiosGetCursorPosition(PBYTE Row, PBYTE Column, BYTE Page) +{ + /* Make sure the selected video page is valid */ + if (Page >= BIOS_MAX_PAGES) return; + + /* Get the cursor location */ + *Row = HIBYTE(Bda->CursorPosition[Page]); + *Column = LOBYTE(Bda->CursorPosition[Page]); +} + +static VOID VidBiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page) +{ + /* Make sure the selected video page is valid */ + if (Page >= BIOS_MAX_PAGES) return; + + /* Update the position in the BDA */ + Bda->CursorPosition[Page] = MAKEWORD(Column, Row); + + /* Check if this is the current video page */ + if (Page == Bda->VideoPage) + { + WORD Offset = Row * Bda->ScreenColumns + Column; + + /* Modify the CRTC registers */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG); + IOWriteB(VGA_CRTC_DATA , LOBYTE(Offset)); + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG); + IOWriteB(VGA_CRTC_DATA , HIBYTE(Offset)); + } +} + +VOID VidBiosSyncCursorPosition(VOID) +{ + BYTE Row, Column; + BYTE Low, High; + SHORT ScreenColumns = VgaGetDisplayResolution().X; + WORD Offset; + + /* Get the cursor location */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG); + Low = IOReadB(VGA_CRTC_DATA); + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG); + High = IOReadB(VGA_CRTC_DATA); + + Offset = MAKEWORD(Low, High); + + Row = (BYTE)(Offset / ScreenColumns); + Column = (BYTE)(Offset % ScreenColumns); + + /* Synchronize our cursor position with VGA */ + VidBiosSetCursorPosition(Row, Column, Bda->VideoPage); +} + +BYTE VidBiosGetVideoMode(VOID) +{ + return Bda->VideoMode; +} + +static BOOLEAN VidBiosSetVideoMode(BYTE ModeNumber) +{ + BYTE Page; + + COORD Resolution; + PVGA_REGISTERS VgaMode = VideoModes[ModeNumber]; + + DPRINT1("Switching to mode %Xh; VgaMode = 0x%p\n", ModeNumber, VgaMode); + + if (!VgaSetRegisters(VgaMode)) return FALSE; + + VgaChangePalette(ModeNumber); + + /* + * IBM standard modes do not clear the screen if the + * high bit of AL is set (EGA or higher only). + * See Ralf Brown: http://www.ctyme.com/intr/rb-0069.htm + * for more information. + */ + // if ((ModeNumber & 0x08) == 0) VgaClearMemory(); + VgaClearMemory(); + + // Bda->CrtModeControl; + // Bda->CrtColorPaletteMask; + // Bda->EGAFlags; + // Bda->VGAFlags; + + /* Update the values in the BDA */ + Bda->VideoMode = ModeNumber; + Bda->VideoPageSize = VideoModePageSize[ModeNumber]; + Bda->VideoPage = 0; + Bda->VideoPageOffset = Bda->VideoPage * Bda->VideoPageSize; + + /* Set the start address in the CRTC */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_LOW_REG); + IOWriteB(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset)); + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_HIGH_REG); + IOWriteB(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset)); + + /* Update the character height */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_MAX_SCAN_LINE_REG); + Bda->CharacterHeight = 1 + (IOReadB(VGA_CRTC_DATA) & 0x1F); + + /* Update the screen size */ + Resolution = VgaGetDisplayResolution(); + Bda->ScreenColumns = Resolution.X; + Bda->ScreenRows = Resolution.Y - 1; + + /* Set the cursor position for each page */ + for (Page = 0; Page < BIOS_MAX_PAGES; ++Page) + VidBiosSetCursorPosition(0, 0, Page); + + /* Refresh display */ + VgaRefreshDisplay(); + + return TRUE; +} + +static BOOLEAN VidBiosSetVideoPage(BYTE PageNumber) +{ + BYTE Row, Column; + + /* Check if the page exists */ + if (PageNumber >= BIOS_MAX_PAGES) return FALSE; + + /* Check if this is the same page */ + if (PageNumber == Bda->VideoPage) return TRUE; + + /* Update the values in the BDA */ + Bda->VideoPage = PageNumber; + Bda->VideoPageOffset = Bda->VideoPage * Bda->VideoPageSize; + + /* Set the start address in the CRTC */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_LOW_REG); + IOWriteB(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset)); + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_HIGH_REG); + IOWriteB(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset)); + + /* + * Get the cursor location (we don't update anything on the BIOS side + * but we update the cursor location on the VGA side). + */ + VidBiosGetCursorPosition(&Row, &Column, PageNumber); + VidBiosSetCursorPosition(Row, Column, PageNumber); + + return TRUE; +} + +static VOID VidBiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page) +{ + WORD CharData = MAKEWORD(Character, Attribute); + BYTE Row, Column; + + /* Make sure the page exists */ + if (Page >= BIOS_MAX_PAGES) return; + + /* Get the cursor location */ + VidBiosGetCursorPosition(&Row, &Column, Page); + + if (Character == '\a') + { + /* Bell control character */ + // NOTE: We may use what the terminal emulator offers to us... + Beep(800, 200); + return; + } + else if (Character == '\b') + { + /* Backspace control character */ + if (Column > 0) + { + Column--; + } + else if (Row > 0) + { + Column = Bda->ScreenColumns - 1; + Row--; + } + + /* Erase the existing character */ + CharData = MAKEWORD(' ', Attribute); + EmulatorWriteMemory(&EmulatorContext, + TO_LINEAR(TEXT_VIDEO_SEG, + Page * Bda->VideoPageSize + + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)), + (LPVOID)&CharData, + sizeof(WORD)); + } + else if (Character == '\t') + { + /* Horizontal Tabulation control character */ + do + { + // Taken from DOSBox + VidBiosPrintCharacter(' ', Attribute, Page); + VidBiosGetCursorPosition(&Row, &Column, Page); + } while (Column % 8); + } + else if (Character == '\n') + { + /* Line Feed control character */ + Row++; + } + else if (Character == '\r') + { + /* Carriage Return control character */ + Column = 0; + } + else + { + /* Default character */ + + /* Write the character */ + EmulatorWriteMemory(&EmulatorContext, + TO_LINEAR(TEXT_VIDEO_SEG, + Page * Bda->VideoPageSize + + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)), + (LPVOID)&CharData, + sizeof(WORD)); + + /* Advance the cursor */ + Column++; + } + + /* Check if it passed the end of the row */ + if (Column >= Bda->ScreenColumns) + { + /* Return to the first column and go to the next line */ + Column = 0; + Row++; + } + + /* Scroll the screen up if needed */ + if (Row > Bda->ScreenRows) + { + /* The screen must be scrolled up */ + SMALL_RECT Rectangle = { 0, 0, Bda->ScreenColumns - 1, Bda->ScreenRows }; + + VidBiosScrollWindow(SCROLL_DIRECTION_UP, + 1, + Rectangle, + Page, + DEFAULT_ATTRIBUTE); + + Row--; + } + + /* Set the cursor position */ + VidBiosSetCursorPosition(Row, Column, Page); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID WINAPI VidBiosVideoService(LPWORD Stack) +{ + switch (getAH()) + { + /* Set Video Mode */ + case 0x00: + { + VidBiosSetVideoMode(getAL()); + break; + } + + /* Set Text-Mode Cursor Shape */ + case 0x01: + { + /* Update the BDA */ + Bda->CursorStartLine = getCH(); + Bda->CursorEndLine = getCL(); + + /* Modify the CRTC registers */ + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_START_REG); + IOWriteB(VGA_CRTC_DATA , Bda->CursorStartLine); + IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_END_REG); + IOWriteB(VGA_CRTC_DATA , Bda->CursorEndLine); + + break; + } + + /* Set Cursor Position */ + case 0x02: + { + VidBiosSetCursorPosition(getDH(), getDL(), getBH()); + break; + } + + /* Get Cursor Position and Shape */ + case 0x03: + { + /* Make sure the selected video page exists */ + if (getBH() >= BIOS_MAX_PAGES) break; + + /* Return the result */ + setAX(0); + setCX(MAKEWORD(Bda->CursorEndLine, Bda->CursorStartLine)); + setDX(Bda->CursorPosition[getBH()]); + break; + } + + /* Query Light Pen */ + case 0x04: + { + /* + * On modern BIOSes, this function returns 0 + * so that we can ignore the other registers. + */ + setAX(0); + break; + } + + /* Select Active Display Page */ + case 0x05: + { + VidBiosSetVideoPage(getAL()); + break; + } + + /* Scroll Window Up/Down */ + case 0x06: + case 0x07: + { + SMALL_RECT Rectangle = { getCL(), getCH(), getDL(), getDH() }; + + /* Call the internal function */ + VidBiosScrollWindow((getAH() == 0x06) ? SCROLL_DIRECTION_UP + : SCROLL_DIRECTION_DOWN, + getAL(), + Rectangle, + Bda->VideoPage, + getBH()); + + break; + } + + /* Read Character and Attribute at Cursor Position */ + case 0x08: + { + WORD CharacterData; + BYTE Page = getBH(); + DWORD Offset; + + /* Check if the page exists */ + if (Page >= BIOS_MAX_PAGES) break; + + /* Find the offset of the character */ + Offset = Page * Bda->VideoPageSize + + (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns + + LOBYTE(Bda->CursorPosition[Page])) * 2; + + /* Read from the video memory */ + EmulatorReadMemory(&EmulatorContext, + TO_LINEAR(TEXT_VIDEO_SEG, Offset), + (LPVOID)&CharacterData, + sizeof(WORD)); + + /* Return the character data in AX */ + setAX(CharacterData); + + break; + } + + /* Write Character and Attribute at Cursor Position */ + case 0x09: + /* Write Character only (PCjr: + Attribute) at Cursor Position */ + case 0x0A: + { + WORD CharacterData = MAKEWORD(getAL(), getBL()); + BYTE Page = getBH(); + DWORD Offset, Counter = getCX(); + + /* Check if the page exists */ + if (Page >= BIOS_MAX_PAGES) break; + + /* Find the offset of the character */ + Offset = Page * Bda->VideoPageSize + + (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns + + LOBYTE(Bda->CursorPosition[Page])) * 2; + + /* Write to video memory a certain number of times */ + while (Counter > 0) + { + EmulatorWriteMemory(&EmulatorContext, + TO_LINEAR(TEXT_VIDEO_SEG, Offset), + (LPVOID)&CharacterData, + (getAH() == 0x09) ? sizeof(WORD) : sizeof(BYTE)); + Offset += 2; + Counter--; + } + + break; + } + + /* Teletype Output */ + case 0x0E: + { + VidBiosPrintCharacter(getAL(), getBL(), getBH()); + break; + } + + /* Get Current Video Mode */ + case 0x0F: + { + setAX(MAKEWORD(Bda->VideoMode, Bda->ScreenColumns)); + setBX(MAKEWORD(getBL(), Bda->VideoPage)); + break; + } + + /* Palette Control */ + case 0x10: + { + switch (getAL()) + { + /* Set Single Palette Register */ + case 0x00: + { + /* Write the index */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, getBL()); + + /* Write the data */ + IOWriteB(VGA_AC_WRITE, getBH()); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + break; + } + + /* Set Overscan Color */ + case 0x01: + { + /* Write the index */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG); + + /* Write the data */ + IOWriteB(VGA_AC_WRITE, getBH()); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + break; + } + + /* Set All Palette Registers */ + case 0x02: + { + INT i; + LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX()); + + /* Set the palette registers */ + for (i = 0; i <= VGA_AC_PAL_F_REG; i++) + { + /* Write the index */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, i); + + /* Write the data */ + IOWriteB(VGA_AC_WRITE, Buffer[i]); + } + + /* Set the overscan register */ + IOWriteB(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG); + IOWriteB(VGA_AC_WRITE, Buffer[VGA_AC_PAL_F_REG + 1]); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + break; + } + + /* Get Single Palette Register */ + case 0x07: + { + /* Write the index */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, getBL()); + + /* Read the data */ + setBH(IOReadB(VGA_AC_READ)); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + break; + } + + /* Get Overscan Color */ + case 0x08: + { + /* Write the index */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG); + + /* Read the data */ + setBH(IOReadB(VGA_AC_READ)); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + break; + } + + /* Get All Palette Registers */ + case 0x09: + { + INT i; + LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX()); + + /* Get the palette registers */ + for (i = 0; i <= VGA_AC_PAL_F_REG; i++) + { + /* Write the index */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, i); + + /* Read the data */ + Buffer[i] = IOReadB(VGA_AC_READ); + } + + /* Get the overscan register */ + IOWriteB(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG); + Buffer[VGA_AC_PAL_F_REG + 1] = IOReadB(VGA_AC_READ); + + /* Enable screen and disable palette access */ + IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state + IOWriteB(VGA_AC_INDEX, 0x20); + break; + } + + /* Set Individual DAC Register */ + case 0x10: + { + /* Write the index */ + // Certainly in BL and not in BX as said by Ralf Brown... + IOWriteB(VGA_DAC_WRITE_INDEX, getBL()); + + /* Write the data in this order: Red, Green, Blue */ + IOWriteB(VGA_DAC_DATA, getDH()); + IOWriteB(VGA_DAC_DATA, getCH()); + IOWriteB(VGA_DAC_DATA, getCL()); + + break; + } + + /* Set Block of DAC Registers */ + case 0x12: + { + INT i; + LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX()); + + /* Write the index */ + // Certainly in BL and not in BX as said by Ralf Brown... + IOWriteB(VGA_DAC_WRITE_INDEX, getBL()); + + for (i = 0; i < getCX(); i++) + { + /* Write the data in this order: Red, Green, Blue */ + IOWriteB(VGA_DAC_DATA, *Buffer++); + IOWriteB(VGA_DAC_DATA, *Buffer++); + IOWriteB(VGA_DAC_DATA, *Buffer++); + } + + break; + } + + /* Get Individual DAC Register */ + case 0x15: + { + /* Write the index */ + IOWriteB(VGA_DAC_READ_INDEX, getBL()); + + /* Read the data in this order: Red, Green, Blue */ + setDH(IOReadB(VGA_DAC_DATA)); + setCH(IOReadB(VGA_DAC_DATA)); + setCL(IOReadB(VGA_DAC_DATA)); + + break; + } + + /* Get Block of DAC Registers */ + case 0x17: + { + INT i; + LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX()); + + /* Write the index */ + // Certainly in BL and not in BX as said by Ralf Brown... + IOWriteB(VGA_DAC_READ_INDEX, getBL()); + + for (i = 0; i < getCX(); i++) + { + /* Write the data in this order: Red, Green, Blue */ + *Buffer++ = IOReadB(VGA_DAC_DATA); + *Buffer++ = IOReadB(VGA_DAC_DATA); + *Buffer++ = IOReadB(VGA_DAC_DATA); + } + + break; + } + + default: + { + DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n", + getAL()); + break; + } + } + + break; + } + + /* Alternate Function Select */ + case 0x12: + { + DPRINT1("BIOS Function INT 12h (Alternate Function Select), BX = 0x%04X NOT IMPLEMENTED\n", + getBX()); + break; + } + + /* Write String */ + case 0x13: + { + DPRINT1("BIOS Function INT 13h (Write String) is UNIMPLEMENTED\n"); + break; + } + + /* Display combination code */ + case 0x1A: + { + switch(getAL()) + { + case 0x00: /* Get Display combiantion code */ + setAX(MAKEWORD(0x1A, 0x1A)); + setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */ + break; + case 0x01: /* Set Display combination code */ + DPRINT1("Set Display combination code - Unsupported\n"); + break; + default: + break; + } + break; + } + + default: + { + DPRINT1("BIOS Function INT 10h, AH = 0x%02X NOT IMPLEMENTED\n", + getAH()); + } + } +} + +BOOLEAN VidBiosInitialize(VOID) +{ + /* Some interrupts are in fact addresses to tables */ + ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x1F] = (ULONG)NULL; + // ((PULONG)BaseAddress)[0x42] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x43] = (ULONG)NULL; + ((PULONG)BaseAddress)[0x44] = (ULONG)NULL; + + /* Set the default video mode */ + VidBiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE); + + /* Synchronize our cursor position with VGA */ + VidBiosSyncCursorPosition(); + + /* Register the BIOS support BOPs */ + RegisterBop(BOP_VIDEO_INT, VidBiosVideoService); + + return TRUE; +} + +VOID VidBiosCleanup(VOID) +{ +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bios/vidbios.h b/reactos/subsystems/ntvdm/bios/vidbios.h new file mode 100644 index 0000000000000..70b3e54606e4a --- /dev/null +++ b/reactos/subsystems/ntvdm/bios/vidbios.h @@ -0,0 +1,49 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vidbios.h + * PURPOSE: VDM Video BIOS Support Library + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _VIDBIOS_H_ +#define _VIDBIOS_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define BIOS_VIDEO_INTERRUPT 0x10 + +#define CONSOLE_FONT_HEIGHT 8 +#define BIOS_DEFAULT_VIDEO_MODE 0x03 +#define BIOS_MAX_PAGES 8 +#define BIOS_MAX_VIDEO_MODE 0x13 +#define DEFAULT_ATTRIBUTE 0x07 + +#define GRAPHICS_VIDEO_SEG 0xA000 +#define TEXT_VIDEO_SEG 0xB800 + +enum +{ + SCROLL_DIRECTION_UP, + SCROLL_DIRECTION_DOWN, + SCROLL_DIRECTION_LEFT, + SCROLL_DIRECTION_RIGHT +}; + +/* FUNCTIONS ******************************************************************/ + +VOID WINAPI VidBiosVideoService(LPWORD Stack); + +VOID VidBiosSyncCursorPosition(VOID); + +BOOLEAN VidBiosInitialize(VOID); +VOID VidBiosCleanup(VOID); + +#endif // _VIDBIOS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bop.c b/reactos/subsystems/ntvdm/bop.c new file mode 100644 index 0000000000000..407004290b7a3 --- /dev/null +++ b/reactos/subsystems/ntvdm/bop.c @@ -0,0 +1,50 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bop.c + * PURPOSE: BIOS Operation Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +// #define NDEBUG + +#include "emulator.h" +#include "bop.h" + +/* PRIVATE VARIABLES **********************************************************/ + +/* + * This is the list of registered BOP handlers. + */ +EMULATOR_BOP_PROC BopProc[EMULATOR_MAX_BOP_NUM] = { NULL }; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID RegisterBop(BYTE BopCode, EMULATOR_BOP_PROC BopHandler) +{ + BopProc[BopCode] = BopHandler; +} + +VOID WINAPI EmulatorBiosOperation(PFAST486_STATE State, UCHAR BopCode) +{ + WORD StackSegment, StackPointer; + LPWORD Stack; + + /* Get the SS:SP */ + StackSegment = State->SegmentRegs[FAST486_REG_SS].Selector; + StackPointer = State->GeneralRegs[FAST486_REG_ESP].LowWord; + + /* Get the stack */ + Stack = (LPWORD)SEG_OFF_TO_PTR(StackSegment, StackPointer); + + /* Call the BOP handler */ + if (BopProc[BopCode] != NULL) + BopProc[BopCode](Stack); + else + DPRINT("Invalid BOP code: 0x%02X\n", BopCode); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/bop.h b/reactos/subsystems/ntvdm/bop.h new file mode 100644 index 0000000000000..a936d1f25cb29 --- /dev/null +++ b/reactos/subsystems/ntvdm/bop.h @@ -0,0 +1,28 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: bop.h + * PURPOSE: BIOS Operation Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _BOP_H_ +#define _BOP_H_ + +/* DEFINES ********************************************************************/ + +/* BOP Identifiers */ +#define EMULATOR_BOP 0xC4C4 +#define EMULATOR_MAX_BOP_NUM 0xFF + 1 + +/* FUNCTIONS ******************************************************************/ + +typedef VOID (WINAPI *EMULATOR_BOP_PROC)(LPWORD Stack); + +VOID RegisterBop(BYTE BopCode, EMULATOR_BOP_PROC BopHandler); +VOID WINAPI EmulatorBiosOperation(PFAST486_STATE State, UCHAR BopCode); + +#endif // _BOP_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/callback.c b/reactos/subsystems/ntvdm/callback.c new file mode 100644 index 0000000000000..34c0bcc782430 --- /dev/null +++ b/reactos/subsystems/ntvdm/callback.c @@ -0,0 +1,293 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: callback.c + * PURPOSE: 16 and 32-bit Callbacks Support + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" + +#include "bop.h" +#include + +/* PRIVATE VARIABLES **********************************************************/ + +/* + * This is the list of registered 32-bit Interrupt handlers. + */ +EMULATOR_INT32_PROC Int32Proc[EMULATOR_MAX_INT32_NUM] = { NULL }; + +/* BOP Identifiers */ +#define BOP_CONTROL 0xFF // Control BOP Handler + #define BOP_CONTROL_DEFFUNC 0x00 // Default Control BOP Function + +/* 32-bit Interrupt dispatcher function code for the Control BOP Handler */ +#define BOP_CONTROL_INT32 0xFF + + +#define BOP(num) LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), (num) +#define UnSimulate16(trap) \ +do { \ + *(PUSHORT)(trap) = EMULATOR_BOP; \ + (trap) += sizeof(USHORT); \ + *(trap) = BOP_UNSIMULATE; \ +} while(0) +// #define UnSimulate16 MAKELONG(EMULATOR_BOP, BOP_UNSIMULATE) // BOP(BOP_UNSIMULATE) + +#define CALL16_TRAMPOLINE_SIZE (1 * sizeof(ULONGLONG)) +#define INT16_TRAMPOLINE_SIZE (1 * sizeof(ULONGLONG)) + +/* 16-bit generic interrupt code for calling a 32-bit interrupt handler */ +BYTE Int16To32[] = +{ + 0xFA, // cli + + /* Push the value of the interrupt to be called */ + 0x6A, 0xFF, // push i (patchable to 0x6A, 0xIntNum) + + /* The counter variable (initialized to 0) */ + 0x6A, 0x00, // push 0 + + /* Stack variables */ + 0x83, 0xEC, 0x04, // sub sp, 4 + + /* The BOP Sequence */ +// BOP_SEQ: + 0xF8, // clc + BOP(BOP_CONTROL), // Control BOP + BOP_CONTROL_INT32, // 32-bit Interrupt dispatcher + + 0x73, 0x04, // jnc EXIT (offset +4) + + 0xFB, // sti + + // HACK: The following instruction should be HLT! + 0x90, // nop + + 0xEB, 0xF5, // jmp BOP_SEQ (offset -11) + +// EXIT: + 0x83, 0xC4, 0x08, // add sp, 8 + 0xCF, // iret +}; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +InitializeContext(IN PCALLBACK16 Context, + IN USHORT Segment, + IN USHORT Offset) +{ + Context->TrampolineFarPtr = MAKELONG(Offset, Segment); + Context->Segment = Segment; + Context->NextOffset = Offset + max(CALL16_TRAMPOLINE_SIZE, + INT16_TRAMPOLINE_SIZE); +} + +VOID +Call16(IN USHORT Segment, + IN USHORT Offset) +{ + /* Save CS:IP */ + USHORT OrgCS = getCS(); + USHORT OrgIP = getIP(); + + /* Set the new CS:IP */ + setCS(Segment); + setIP(Offset); + + DPRINT("Call16(%04X:%04X)\n", Segment, Offset); + + /* Start CPU simulation */ + EmulatorSimulate(); + + /* Restore CS:IP */ + setCS(OrgCS); + setIP(OrgIP); +} + + + +ULONG +RegisterCallback16(IN ULONG FarPtr, + IN LPBYTE CallbackCode, + IN SIZE_T CallbackSize, + OUT PSIZE_T CodeSize OPTIONAL) +{ + LPBYTE CodeStart = (LPBYTE)FAR_POINTER(FarPtr); + LPBYTE Code = CodeStart; + + SIZE_T OurCodeSize = CallbackSize; + + if (CallbackCode == NULL) CallbackSize = 0; + + if (CallbackCode) + { + /* 16-bit interrupt code */ + RtlCopyMemory(Code, CallbackCode, CallbackSize); + Code += CallbackSize; + } + + /* Return the real size of the code if needed */ + if (CodeSize) *CodeSize = OurCodeSize; // == (ULONG_PTR)Code - (ULONG_PTR)CodeStart; + + // /* Return the entry-point address for 32-bit calls */ + // return (ULONG_PTR)(CodeStart + CallbackSize); + return OurCodeSize; +} + +VOID +RunCallback16(IN PCALLBACK16 Context, + IN ULONG FarPtr) +{ + PUCHAR TrampolineBase = (PUCHAR)FAR_POINTER(Context->TrampolineFarPtr); + PUCHAR Trampoline = TrampolineBase; + UCHAR OldTrampoline[CALL16_TRAMPOLINE_SIZE]; + + /* Save the old trampoline */ + ((PULONGLONG)&OldTrampoline)[0] = ((PULONGLONG)TrampolineBase)[0]; + + DPRINT1("RunCallback16(0x%p)\n", FarPtr); + + /* Build the generic entry-point for 16-bit far calls */ + *Trampoline++ = 0x9A; // Call far seg:off + *(PULONG)Trampoline = FarPtr; + Trampoline += sizeof(ULONG); + UnSimulate16(Trampoline); + + /* Perform the call */ + Call16(HIWORD(Context->TrampolineFarPtr), + LOWORD(Context->TrampolineFarPtr)); + + /* Restore the old trampoline */ + ((PULONGLONG)TrampolineBase)[0] = ((PULONGLONG)&OldTrampoline)[0]; +} + + + +ULONG +RegisterInt16(IN ULONG FarPtr, + IN BYTE IntNumber, + IN LPBYTE CallbackCode, + IN SIZE_T CallbackSize, + OUT PSIZE_T CodeSize OPTIONAL) +{ + /* Get a pointer to the IVT and set the corresponding entry (far pointer) */ + LPDWORD IntVecTable = (LPDWORD)SEG_OFF_TO_PTR(0x0000, 0x0000); + IntVecTable[IntNumber] = FarPtr; + + /* Register the 16-bit callback */ + return RegisterCallback16(FarPtr, + CallbackCode, + CallbackSize, + CodeSize); +} + +ULONG +RegisterInt32(IN ULONG FarPtr, + IN BYTE IntNumber, + IN EMULATOR_INT32_PROC IntHandler, + OUT PSIZE_T CodeSize OPTIONAL) +{ + /* Array for holding our copy of the 16-bit interrupt callback */ + BYTE IntCallback[sizeof(Int16To32)/sizeof(BYTE)]; + + /* Check whether the 32-bit interrupt was already registered */ + // if (Int32Proc[IntNumber] != NULL) + // { + // DPRINT1("RegisterInt32: Interrupt 0x%X already registered!\n", IntNumber); + // return 0; + // } + + /* Register the 32-bit interrupt handler */ + Int32Proc[IntNumber] = IntHandler; + + /* Copy the generic 16-bit interrupt callback and patch it */ + RtlCopyMemory(IntCallback, Int16To32, sizeof(Int16To32)); + IntCallback[2] = IntNumber; + + /* Register the 16-bit interrupt callback */ + return RegisterInt16(FarPtr, + IntNumber, + IntCallback, + sizeof(IntCallback), + CodeSize); +} + +VOID +Int32Call(IN PCALLBACK16 Context, + IN BYTE IntNumber) +{ + PUCHAR TrampolineBase = (PUCHAR)FAR_POINTER(Context->TrampolineFarPtr); + PUCHAR Trampoline = TrampolineBase; + UCHAR OldTrampoline[INT16_TRAMPOLINE_SIZE]; + + DPRINT("Int32Call(0x%X)\n", IntNumber); + + /* Save the old trampoline */ + ((PULONGLONG)&OldTrampoline)[0] = ((PULONGLONG)TrampolineBase)[0]; + + /* Build the generic entry-point for 16-bit calls */ + if (IntNumber == 0x03) + { + /* We are redefining for INT 03h */ + *Trampoline++ = 0xCC; // Call INT 03h + /** *Trampoline++ = 0x90; // nop **/ + } + else + { + /* Normal interrupt */ + *Trampoline++ = 0xCD; // Call INT XXh + *Trampoline++ = IntNumber; + } + UnSimulate16(Trampoline); + + /* Perform the call */ + Call16(HIWORD(Context->TrampolineFarPtr), + LOWORD(Context->TrampolineFarPtr)); + + /* Restore the old trampoline */ + ((PULONGLONG)TrampolineBase)[0] = ((PULONGLONG)&OldTrampoline)[0]; +} + + + +VOID WINAPI Int32Dispatch(LPWORD Stack) +{ + /* Get the interrupt number */ + BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]); + + /* Call the 32-bit Interrupt handler */ + if (Int32Proc[IntNum] != NULL) + Int32Proc[IntNum](Stack); + else + DPRINT1("Unhandled 32-bit interrupt: 0x%02X, AX = 0x%04X\n", IntNum, getAX()); +} + +static VOID WINAPI ControlBop(LPWORD Stack) +{ + /* Get the Function Number and skip it */ + BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); + setIP(getIP() + 1); + + if (FuncNum == BOP_CONTROL_INT32) + Int32Dispatch(Stack); + else + // DPRINT1("Unassigned Control BOP Function: 0x%02X\n", FuncNum); + DisplayMessage(L"Unassigned Control BOP Function: 0x%02X\n", FuncNum); +} + +VOID InitializeCallbacks(VOID) +{ + /* Register the Control BOP */ + RegisterBop(BOP_CONTROL, ControlBop); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/callback.h b/reactos/subsystems/ntvdm/callback.h new file mode 100644 index 0000000000000..c29ef9cb687f1 --- /dev/null +++ b/reactos/subsystems/ntvdm/callback.h @@ -0,0 +1,75 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: callback.h + * PURPOSE: 32-bit Interrupt Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _CALLBACK_H_ +#define _CALLBACK_H_ + +/* DEFINES ********************************************************************/ + +/* 32-bit Interrupt Identifiers */ +#define EMULATOR_MAX_INT32_NUM 0xFF + 1 + +#define INT_HANDLER_OFFSET 0x1000 +#define COMMON_STUB_OFFSET 0x2000 + + +typedef struct _CALLBACK16 +{ + ULONG TrampolineFarPtr; // Where the trampoline zone is placed + USHORT Segment; + USHORT NextOffset; +} CALLBACK16, *PCALLBACK16; + + +/* FUNCTIONS ******************************************************************/ + +typedef VOID (WINAPI *EMULATOR_INT32_PROC)(LPWORD Stack); + +VOID +InitializeContext(IN PCALLBACK16 Context, + IN USHORT Segment, + IN USHORT Offset); + +VOID +Call16(IN USHORT Segment, + IN USHORT Offset); + +ULONG +RegisterCallback16(IN ULONG FarPtr, + IN LPBYTE CallbackCode, + IN SIZE_T CallbackSize, + OUT PSIZE_T CodeSize OPTIONAL); + +VOID +RunCallback16(IN PCALLBACK16 Context, + IN ULONG FarPtr); + +ULONG +RegisterInt16(IN ULONG FarPtr, + IN BYTE IntNumber, + IN LPBYTE CallbackCode, + IN SIZE_T CallbackSize, + OUT PSIZE_T CodeSize OPTIONAL); + +ULONG +RegisterInt32(IN ULONG FarPtr, + IN BYTE IntNumber, + IN EMULATOR_INT32_PROC IntHandler, + OUT PSIZE_T CodeSize OPTIONAL); + +VOID +Int32Call(IN PCALLBACK16 Context, + IN BYTE IntNumber); + +VOID WINAPI Int32Dispatch(LPWORD Stack); +VOID InitializeCallbacks(VOID); + +#endif // _CALLBACK_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/clock.c b/reactos/subsystems/ntvdm/clock.c new file mode 100644 index 0000000000000..74e1fe4b4f5b7 --- /dev/null +++ b/reactos/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) +{ + extern BOOLEAN CpuSimulate; + 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 */ + for (i = 0; VdmRunning && CpuSimulate && (i < STEPS_PER_CYCLE); 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/reactos/subsystems/ntvdm/clock.h b/reactos/subsystems/ntvdm/clock.h new file mode 100644 index 0000000000000..53927d2b257e8 --- /dev/null +++ b/reactos/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/reactos/subsystems/ntvdm/dos/dem.c b/reactos/subsystems/ntvdm/dos/dem.c new file mode 100644 index 0000000000000..f78dab86a3a96 --- /dev/null +++ b/reactos/subsystems/ntvdm/dos/dem.c @@ -0,0 +1,375 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dem.c + * PURPOSE: DOS 32-bit Emulation Support Library - + * This library is used by the built-in NTVDM DOS32 and by + * the NT 16-bit DOS in Windows (via BOPs). It also exposes + * exported functions that can be used by VDDs. + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "utils.h" + +#include "dem.h" +#include "bop.h" + +#include "bios/bios.h" +#include "hardware/vga.h" + +/* Extra PSDK/NDK Headers */ +#include + +/* PRIVATE VARIABLES **********************************************************/ + +/**/extern BYTE CurrentDrive;/**/ + +/* DEFINES ********************************************************************/ + +/* BOP Identifiers */ +#define BOP_DOS 0x50 // DOS System BOP (for NTIO.SYS and NTDOS.SYS) +#define BOP_CMD 0x54 // DOS Command Interpreter BOP (for COMMAND.COM) + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID WINAPI DosSystemBop(LPWORD Stack) +{ + /* Get the Function Number and skip it */ + BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); + setIP(getIP() + 1); + + switch (FuncNum) + { + case 0x11: // Load the DOS kernel + { + BOOLEAN Success; + HANDLE hDosKernel; + ULONG ulDosKernelSize = 0; + + DPRINT1("You are loading Windows NT DOS!\n"); + + /* Open the DOS kernel file */ + hDosKernel = FileOpen("ntdos.sys", &ulDosKernelSize); + + /* If we failed, bail out */ + if (hDosKernel == NULL) goto Quit; + + /* + * Attempt to load the DOS kernel into memory. + * The segment where to load the DOS kernel is defined + * by the DOS BIOS and is found in DI:0000 . + */ + Success = FileLoadByHandle(hDosKernel, + REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)), + ulDosKernelSize, + &ulDosKernelSize); + + DPRINT1("Windows NT DOS loading %s at 0x%04X:0x%04X, size 0x%x ; GetLastError() = %u\n", + (Success ? "succeeded" : "failed"), + getDI(), 0x0000, + ulDosKernelSize, + GetLastError()); + + /* Close the DOS kernel file */ + FileClose(hDosKernel); + +Quit: + if (!Success) + { + /* We failed everything, stop the VDM */ + EmulatorTerminate(); + } + + break; + } + + default: + { + + DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum); + // setCF(1); // Disable, otherwise we enter an infinite loop + break; + } + } +} + +static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack) +{ + /* Get the Function Number and skip it */ + BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); + setIP(getIP() + 1); + + switch (FuncNum) + { + case 0x08: // Launch external command + { +#define CMDLINE_LENGTH 1024 + + BOOL Result; + DWORD dwExitCode; + + LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI()); + LPSTR CmdPtr = Command; + CHAR CommandLine[CMDLINE_LENGTH] = ""; + STARTUPINFOA StartupInfo; + PROCESS_INFORMATION ProcessInformation; + + /* NULL-terminate the command by removing the return carriage character */ + while (*CmdPtr != '\r') CmdPtr++; + *CmdPtr = '\0'; + + DPRINT1("CMD Run Command '%s'\n", Command); + + /* Spawn a user-defined 32-bit command preprocessor */ + + /* Build the command line */ + // FIXME: Use COMSPEC env var!! + strcpy(CommandLine, "cmd.exe /c "); + strcat(CommandLine, Command); + + ZeroMemory(&StartupInfo, sizeof(StartupInfo)); + ZeroMemory(&ProcessInformation, sizeof(ProcessInformation)); + + StartupInfo.cb = sizeof(StartupInfo); + + VgaRefreshDisplay(); + VgaDetachFromConsole(FALSE); + + Result = CreateProcessA(NULL, + CommandLine, + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &StartupInfo, + &ProcessInformation); + if (Result) + { + DPRINT1("Command '%s' launched successfully\n", Command); + + /* Wait for process termination */ + WaitForSingleObject(ProcessInformation.hProcess, INFINITE); + + /* Get the exit code */ + GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode); + + /* Close handles */ + CloseHandle(ProcessInformation.hThread); + CloseHandle(ProcessInformation.hProcess); + } + else + { + DPRINT1("Failed when launched command '%s'\n"); + dwExitCode = GetLastError(); + } + + VgaAttachToConsole(); + VgaRefreshDisplay(); + VidBiosSyncCursorPosition(); + + setAL((UCHAR)dwExitCode); + + break; + } + + default: + { + DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum); + // setCF(1); // Disable, otherwise we enter an infinite loop + break; + } + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN DosInitialize(IN LPCSTR DosKernelFileName) +{ + /* Register the DOS BOPs */ + RegisterBop(BOP_DOS, DosSystemBop ); + RegisterBop(BOP_CMD, DosCmdInterpreterBop); + + if (DosKernelFileName) + { + BOOLEAN Success; + HANDLE hDosBios; + ULONG ulDosBiosSize = 0; + + /* Open the DOS BIOS file */ + hDosBios = FileOpen(DosKernelFileName, &ulDosBiosSize); + + /* If we failed, bail out */ + if (hDosBios == NULL) return FALSE; + + /* Attempt to load the DOS BIOS into memory */ + Success = FileLoadByHandle(hDosBios, + REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)), + ulDosBiosSize, + &ulDosBiosSize); + + DPRINT1("DOS BIOS loading %s at 0x%04X:0x%04X, size 0x%x ; GetLastError() = %u\n", + (Success ? "succeeded" : "failed"), + 0x0070, 0x0000, + ulDosBiosSize, + GetLastError()); + + /* Close the DOS BIOS file */ + FileClose(hDosBios); + + if (Success) + { + /* Position execution pointers and return */ + setCS(0x0070); + setIP(0x0000); + } + + return Success; + } + else + { + BOOLEAN Result; + + Result = DosBIOSInitialize(); + // Result &= DosKRNLInitialize(); + + return Result; + } +} + +/* PUBLIC EXPORTED APIS *******************************************************/ + +// demLFNCleanup +// demLFNGetCurrentDirectory + +// demGetFileTimeByHandle_WOW +// demWOWLFNAllocateSearchHandle +// demWOWLFNCloseSearchHandle +// demWOWLFNEntry +// demWOWLFNGetSearchHandle +// demWOWLFNInit + +DWORD +WINAPI +demClientErrorEx(IN HANDLE FileHandle, + IN CHAR Unknown, + IN BOOL Flag) +{ + UNIMPLEMENTED; + return GetLastError(); +} + +DWORD +WINAPI +demFileDelete(IN LPCSTR FileName) +{ + if (DeleteFileA(FileName)) SetLastError(ERROR_SUCCESS); + + return GetLastError(); +} + +DWORD +WINAPI +demFileFindFirst(OUT PVOID lpFindFileData, + IN LPCSTR FileName, + IN WORD AttribMask) +{ + BOOLEAN Success = TRUE; + WIN32_FIND_DATAA FindData; + PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData; + + /* Fill the block */ + FindFileBlock->DriveLetter = CurrentDrive + 'A'; + FindFileBlock->AttribMask = AttribMask; + FindFileBlock->SearchHandle = FindFirstFileA(FileName, &FindData); + if (FindFileBlock->SearchHandle == INVALID_HANDLE_VALUE) return GetLastError(); + + do + { + /* Check the attributes */ + if (!((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_DIRECTORY)) + & ~AttribMask)) + { + break; + } + } + while ((Success = FindNextFileA(FindFileBlock->SearchHandle, &FindData))); + + if (!Success) return GetLastError(); + + FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes); + FileTimeToDosDateTime(&FindData.ftLastWriteTime, + &FindFileBlock->FileDate, + &FindFileBlock->FileTime); + FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF + : FindData.nFileSizeLow; + strcpy(FindFileBlock->FileName, FindData.cAlternateFileName); + + return ERROR_SUCCESS; +} + +DWORD +WINAPI +demFileFindNext(OUT PVOID lpFindFileData) +{ + WIN32_FIND_DATAA FindData; + PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData; + + do + { + if (!FindNextFileA(FindFileBlock->SearchHandle, &FindData)) + return GetLastError(); + + /* Update the block */ + FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes); + FileTimeToDosDateTime(&FindData.ftLastWriteTime, + &FindFileBlock->FileDate, + &FindFileBlock->FileTime); + FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF + : FindData.nFileSizeLow; + strcpy(FindFileBlock->FileName, FindData.cAlternateFileName); + } + while((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_DIRECTORY)) + & ~FindFileBlock->AttribMask); + + return ERROR_SUCCESS; +} + +UCHAR +WINAPI +demGetPhysicalDriveType(IN UCHAR DriveNumber) +{ + UNIMPLEMENTED; + return DOSDEVICE_DRIVE_UNKNOWN; +} + +BOOL +WINAPI +demIsShortPathName(IN LPCSTR Path, + IN BOOL Unknown) +{ + UNIMPLEMENTED; + return FALSE; +} + +DWORD +WINAPI +demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory, + OUT PUCHAR DriveNumber) +{ + UNIMPLEMENTED; + return ERROR_SUCCESS; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/dos/dem.h b/reactos/subsystems/ntvdm/dos/dem.h new file mode 100644 index 0000000000000..1e5f2de1b0524 --- /dev/null +++ b/reactos/subsystems/ntvdm/dos/dem.h @@ -0,0 +1,82 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dem.h + * PURPOSE: DOS 32-bit Emulation Support Library - + * This library is used by the built-in NTVDM DOS32 and by + * the NT 16-bit DOS in Windows (via BOPs). It also exposes + * exported functions that can be used by VDDs. + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _DEM_H_ +#define _DEM_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" +#include "dos32krnl/dos.h" + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN DosInitialize(IN LPCSTR DosKernelFileNames); + +DWORD +WINAPI +demClientErrorEx +( + IN HANDLE FileHandle, + IN CHAR Unknown, + IN BOOL Flag +); + +DWORD +WINAPI +demFileDelete +( + IN LPCSTR FileName +); + +DWORD +WINAPI +demFileFindFirst +( + OUT PVOID lpFindFileData, + IN LPCSTR FileName, + IN WORD AttribMask +); + +DWORD +WINAPI +demFileFindNext +( + OUT PVOID lpFindFileData +); + +UCHAR +WINAPI +demGetPhysicalDriveType +( + IN UCHAR DriveNumber +); + +BOOL +WINAPI +demIsShortPathName +( + IN LPCSTR Path, + IN BOOL Unknown +); + +DWORD +WINAPI +demSetCurrentDirectoryGetDrive +( + IN LPCSTR CurrentDirectory, + OUT PUCHAR DriveNumber +); + +#endif // _DEM_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/dos/dos32krnl/bios.c b/reactos/subsystems/ntvdm/dos/dos32krnl/bios.c new file mode 100644 index 0000000000000..b7ce9d313e549 --- /dev/null +++ b/reactos/subsystems/ntvdm/dos/dos32krnl/bios.c @@ -0,0 +1,273 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dos.c + * PURPOSE: VDM DOS Kernel + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" + +#include "dos.h" + +#include "bios/bios.h" + +/* PRIVATE VARIABLES **********************************************************/ + +// static BYTE CurrentDrive; +// static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH]; + +/* PRIVATE FUNCTIONS **********************************************************/ + +#if 0 +static WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten) +{ + WORD Result = ERROR_SUCCESS; + DWORD BytesWritten32 = 0; + HANDLE Handle = DosGetRealHandle(FileHandle); + WORD i; + + DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n", + FileHandle, + Count); + + /* Make sure the handle is valid */ + if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; + + if (IsConsoleHandle(Handle)) + { + for (i = 0; i < Count; i++) + { + /* Call the BIOS to print the character */ + VidBiosPrintCharacter(((LPBYTE)Buffer)[i], DOS_CHAR_ATTRIBUTE, Bda->VideoPage); + BytesWritten32++; + } + } + else + { + /* Write the file */ + if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL)) + { + /* Store the error code */ + Result = (WORD)GetLastError(); + } + } + + /* The number of bytes written is always 16-bit */ + *BytesWritten = LOWORD(BytesWritten32); + + /* Return the error code */ + return Result; +} +#endif + +/* PUBLIC FUNCTIONS ***********************************************************/ + +CHAR DosReadCharacter(VOID) +{ + CHAR Character = '\0'; + WORD BytesRead; + + if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE))) + { + /* Call the BIOS */ + Character = LOBYTE(BiosGetCharacter()); + } + else + { + /* Use the file reading function */ + DosReadFile(DOS_INPUT_HANDLE, &Character, sizeof(CHAR), &BytesRead); + } + + return Character; +} + +BOOLEAN DosCheckInput(VOID) +{ + HANDLE Handle = DosGetRealHandle(DOS_INPUT_HANDLE); + + if (IsConsoleHandle(Handle)) + { + /* Save AX */ + USHORT AX = getAX(); + + /* Call the BIOS */ + setAH(0x01); // or 0x11 for enhanced, but what to choose? + Int32Call(&DosContext, BIOS_KBD_INTERRUPT); + + /* Restore AX */ + setAX(AX); + + /* Return keyboard status */ + return (getZF() == 0); + } + else + { + DWORD FileSizeHigh; + DWORD FileSize = GetFileSize(Handle, &FileSizeHigh); + LONG LocationHigh = 0; + DWORD Location = SetFilePointer(Handle, 0, &LocationHigh, FILE_CURRENT); + + return ((Location != FileSize) || (LocationHigh != FileSizeHigh)); + } +} + +VOID DosPrintCharacter(CHAR Character) +{ + WORD BytesWritten; + + /* Use the file writing function */ + DosWriteFile(DOS_OUTPUT_HANDLE, &Character, sizeof(CHAR), &BytesWritten); +} + +BOOLEAN DosBIOSInitialize(VOID) +{ + PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT); + + LPWSTR SourcePtr, Environment; + LPSTR AsciiString; + DWORD AsciiSize; + LPSTR DestPtr = (LPSTR)SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0); + +#if 0 + UCHAR i; + CHAR CurrentDirectory[MAX_PATH]; + CHAR DosDirectory[DOS_DIR_LENGTH]; + LPSTR Path; + + FILE *Stream; + WCHAR Buffer[256]; +#endif + + /* Initialize the MCB */ + Mcb->BlockType = 'Z'; + Mcb->Size = USER_MEMORY_SIZE; + Mcb->OwnerPsp = 0; + + /* Initialize the link MCB to the UMB area */ + Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT + USER_MEMORY_SIZE + 1); + Mcb->BlockType = 'M'; + Mcb->Size = UMB_START_SEGMENT - FIRST_MCB_SEGMENT - USER_MEMORY_SIZE - 2; + Mcb->OwnerPsp = SYSTEM_PSP; + + /* Initialize the UMB area */ + Mcb = SEGMENT_TO_MCB(UMB_START_SEGMENT); + Mcb->BlockType = 'Z'; + Mcb->Size = UMB_END_SEGMENT - UMB_START_SEGMENT; + Mcb->OwnerPsp = 0; + + /* Get the environment strings */ + SourcePtr = Environment = GetEnvironmentStringsW(); + if (Environment == NULL) return FALSE; + + /* Fill the DOS system environment block */ + while (*SourcePtr) + { + /* Get the size of the ASCII string */ + AsciiSize = WideCharToMultiByte(CP_ACP, + 0, + SourcePtr, + -1, + NULL, + 0, + NULL, + NULL); + + /* Allocate memory for the ASCII string */ + AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize); + if (AsciiString == NULL) + { + FreeEnvironmentStringsW(Environment); + return FALSE; + } + + /* Convert to ASCII */ + WideCharToMultiByte(CP_ACP, + 0, + SourcePtr, + -1, + AsciiString, + AsciiSize, + NULL, + NULL); + + /* Copy the string into DOS memory */ + strcpy(DestPtr, AsciiString); + + /* Move to the next string */ + SourcePtr += wcslen(SourcePtr) + 1; + DestPtr += strlen(AsciiString); + *(DestPtr++) = 0; + + /* Free the memory */ + HeapFree(GetProcessHeap(), 0, AsciiString); + } + *DestPtr = 0; + + /* Free the memory allocated for environment strings */ + FreeEnvironmentStringsW(Environment); + + +#if 0 + + /* Clear the current directory buffer */ + ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories)); + + /* Get the current directory */ + if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory)) + { + // TODO: Use some kind of default path? + return FALSE; + } + + /* Convert that to a DOS path */ + if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH)) + { + // TODO: Use some kind of default path? + return FALSE; + } + + /* Set the drive */ + CurrentDrive = DosDirectory[0] - 'A'; + + /* Get the directory part of the path */ + Path = strchr(DosDirectory, '\\'); + if (Path != NULL) + { + /* Skip the backslash */ + Path++; + } + + /* Set the directory */ + if (Path != NULL) + { + strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH); + } + + /* Read CONFIG.SYS */ + Stream = _wfopen(DOS_CONFIG_PATH, L"r"); + if (Stream != NULL) + { + while (fgetws(Buffer, sizeof(Buffer)/sizeof(Buffer[0]), Stream)) + { + // TODO: Parse the line + } + fclose(Stream); + } + +#endif + + + /* Register the DOS 32-bit Interrupts */ + // RegisterDosInt32(0x20, DosInt20h); + + /* Initialize the DOS kernel */ + return DosKRNLInitialize(); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/dos/dos32krnl/dos.c b/reactos/subsystems/ntvdm/dos/dos32krnl/dos.c new file mode 100644 index 0000000000000..f1f9fffd70bcb --- /dev/null +++ b/reactos/subsystems/ntvdm/dos/dos32krnl/dos.c @@ -0,0 +1,2810 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dos.c + * PURPOSE: VDM DOS Kernel + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" + +#include "dos.h" +#include "dos/dem.h" + +#include "bios/bios.h" +#include "registers.h" + +/* PRIVATE VARIABLES **********************************************************/ + +CALLBACK16 DosContext; + +static WORD CurrentPsp = SYSTEM_PSP; +static WORD DosLastError = 0; +static DWORD DiskTransferArea; +/*static*/ BYTE CurrentDrive; +static CHAR LastDrive = 'E'; +static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH]; +static HANDLE DosSystemFileTable[DOS_SFT_SIZE]; +static WORD DosSftRefCount[DOS_SFT_SIZE]; +static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT; +static BOOLEAN DosUmbLinked = FALSE; +static WORD DosErrorLevel = 0x0000; + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* + * Memory management functions + */ +static VOID DosCombineFreeBlocks(WORD StartBlock) +{ + PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb; + + /* If this is the last block or it's not free, quit */ + if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return; + + while (TRUE) + { + /* Get a pointer to the next MCB */ + NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1); + + /* Check if the next MCB is free */ + if (NextMcb->OwnerPsp == 0) + { + /* Combine them */ + CurrentMcb->Size += NextMcb->Size + 1; + CurrentMcb->BlockType = NextMcb->BlockType; + NextMcb->BlockType = 'I'; + } + else + { + /* No more adjoining free blocks */ + break; + } + } +} + +static WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable) +{ + WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0; + PDOS_MCB CurrentMcb, NextMcb; + BOOLEAN SearchUmb = FALSE; + + DPRINT("DosAllocateMemory: Size 0x%04X\n", Size); + + if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))) + { + /* Search UMB first */ + Segment = UMB_START_SEGMENT; + SearchUmb = TRUE; + } + + while (TRUE) + { + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(Segment); + + /* Make sure it's valid */ + if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') + { + DPRINT("The DOS memory arena is corrupted!\n"); + DosLastError = ERROR_ARENA_TRASHED; + return 0; + } + + /* Only check free blocks */ + if (CurrentMcb->OwnerPsp != 0) goto Next; + + /* Combine this free block with adjoining free blocks */ + DosCombineFreeBlocks(Segment); + + /* Update the maximum block size */ + if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size; + + /* Check if this block is big enough */ + if (CurrentMcb->Size < Size) goto Next; + + switch (DosAllocStrategy & 0x3F) + { + case DOS_ALLOC_FIRST_FIT: + { + /* For first fit, stop immediately */ + Result = Segment; + goto Done; + } + + case DOS_ALLOC_BEST_FIT: + { + /* For best fit, update the smallest block found so far */ + if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size)) + { + Result = Segment; + } + + break; + } + + case DOS_ALLOC_LAST_FIT: + { + /* For last fit, make the current block the result, but keep searching */ + Result = Segment; + break; + } + } + +Next: + /* If this was the last MCB in the chain, quit */ + if (CurrentMcb->BlockType == 'Z') + { + /* Check if nothing was found while searching through UMBs */ + if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW)) + { + /* Search low memory */ + Segment = FIRST_MCB_SEGMENT; + continue; + } + + break; + } + + /* Otherwise, update the segment and continue */ + Segment += CurrentMcb->Size + 1; + } + +Done: + + /* If we didn't find a free block, return 0 */ + if (Result == 0) + { + DosLastError = ERROR_NOT_ENOUGH_MEMORY; + if (MaxAvailable) *MaxAvailable = MaxSize; + return 0; + } + + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(Result); + + /* Check if the block is larger than requested */ + if (CurrentMcb->Size > Size) + { + /* It is, split it into two blocks */ + NextMcb = SEGMENT_TO_MCB(Result + Size + 1); + + /* Initialize the new MCB structure */ + NextMcb->BlockType = CurrentMcb->BlockType; + NextMcb->Size = CurrentMcb->Size - Size - 1; + NextMcb->OwnerPsp = 0; + + /* Update the current block */ + CurrentMcb->BlockType = 'M'; + CurrentMcb->Size = Size; + } + + /* Take ownership of the block */ + CurrentMcb->OwnerPsp = CurrentPsp; + + /* Return the segment of the data portion of the block */ + return Result + 1; +} + +static BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) +{ + BOOLEAN Success = TRUE; + WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment; + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb; + + DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n", + BlockData, + NewSize); + + /* Make sure this is a valid, allocated block */ + if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0) + { + Success = FALSE; + DosLastError = ERROR_INVALID_HANDLE; + goto Done; + } + + ReturnSize = Mcb->Size; + + /* Check if we need to expand or contract the block */ + if (NewSize > Mcb->Size) + { + /* We can't expand the last block */ + if (Mcb->BlockType != 'M') + { + Success = FALSE; + goto Done; + } + + /* Get the pointer and segment of the next MCB */ + NextSegment = Segment + Mcb->Size + 1; + NextMcb = SEGMENT_TO_MCB(NextSegment); + + /* Make sure the next segment is free */ + if (NextMcb->OwnerPsp != 0) + { + DPRINT("Cannot expand memory block: next segment is not free!\n"); + DosLastError = ERROR_NOT_ENOUGH_MEMORY; + Success = FALSE; + goto Done; + } + + /* Combine this free block with adjoining free blocks */ + DosCombineFreeBlocks(NextSegment); + + /* Set the maximum possible size of the block */ + ReturnSize += NextMcb->Size + 1; + + /* Maximize the current block */ + Mcb->Size = ReturnSize; + Mcb->BlockType = NextMcb->BlockType; + + /* Invalidate the next block */ + NextMcb->BlockType = 'I'; + + /* Check if the block is larger than requested */ + if (Mcb->Size > NewSize) + { + DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n", + Mcb->Size, + NewSize); + + /* It is, split it into two blocks */ + NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); + + /* Initialize the new MCB structure */ + NextMcb->BlockType = Mcb->BlockType; + NextMcb->Size = Mcb->Size - NewSize - 1; + NextMcb->OwnerPsp = 0; + + /* Update the current block */ + Mcb->BlockType = 'M'; + Mcb->Size = NewSize; + } + } + else if (NewSize < Mcb->Size) + { + DPRINT("Shrinking block from 0x%04X to 0x%04X\n", + Mcb->Size, + NewSize); + + /* Just split the block */ + NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); + NextMcb->BlockType = Mcb->BlockType; + NextMcb->Size = Mcb->Size - NewSize - 1; + NextMcb->OwnerPsp = 0; + + /* Update the MCB */ + Mcb->BlockType = 'M'; + Mcb->Size = NewSize; + } + +Done: + /* Check if the operation failed */ + if (!Success) + { + DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n", + ReturnSize); + + /* Return the maximum possible size */ + if (MaxAvailable) *MaxAvailable = ReturnSize; + } + + return Success; +} + +static BOOLEAN DosFreeMemory(WORD BlockData) +{ + PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1); + + DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData); + + /* Make sure the MCB is valid */ + if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') + { + DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType); + return FALSE; + } + + /* Mark the block as free */ + Mcb->OwnerPsp = 0; + + return TRUE; +} + +static BOOLEAN DosLinkUmb(VOID) +{ + DWORD Segment = FIRST_MCB_SEGMENT; + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); + + DPRINT("Linking UMB\n"); + + /* Check if UMBs are already linked */ + if (DosUmbLinked) return FALSE; + + /* Find the last block */ + while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF)) + { + Segment += Mcb->Size + 1; + Mcb = SEGMENT_TO_MCB(Segment); + } + + /* Make sure it's valid */ + if (Mcb->BlockType != 'Z') return FALSE; + + /* Connect the MCB with the UMB chain */ + Mcb->BlockType = 'M'; + + DosUmbLinked = TRUE; + return TRUE; +} + +static BOOLEAN DosUnlinkUmb(VOID) +{ + DWORD Segment = FIRST_MCB_SEGMENT; + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); + + DPRINT("Unlinking UMB\n"); + + /* Check if UMBs are already unlinked */ + if (!DosUmbLinked) return FALSE; + + /* Find the block preceding the MCB that links it with the UMB chain */ + while (Segment <= 0xFFFF) + { + if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE)) + { + /* This is the last non-UMB segment */ + break; + } + + /* Advance to the next MCB */ + Segment += Mcb->Size + 1; + Mcb = SEGMENT_TO_MCB(Segment); + } + + /* Mark the MCB as the last MCB */ + Mcb->BlockType = 'Z'; + + DosUmbLinked = FALSE; + return TRUE; +} + +static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner) +{ + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1); + + /* Just set the owner */ + Mcb->OwnerPsp = NewOwner; +} + +static WORD DosCopyEnvironmentBlock(LPCVOID Environment, LPCSTR ProgramName) +{ + PCHAR Ptr, DestBuffer = NULL; + ULONG TotalSize = 0; + WORD DestSegment; + + Ptr = (PCHAR)Environment; + + /* Calculate the size of the environment block */ + while (*Ptr) + { + TotalSize += strlen(Ptr) + 1; + Ptr += strlen(Ptr) + 1; + } + TotalSize++; + + /* Add the string buffer size */ + TotalSize += strlen(ProgramName) + 1; + + /* Allocate the memory for the environment block */ + DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL); + if (!DestSegment) return 0; + + Ptr = (PCHAR)Environment; + + DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0); + while (*Ptr) + { + /* Copy the string */ + strcpy(DestBuffer, Ptr); + + /* Advance to the next string */ + DestBuffer += strlen(Ptr); + Ptr += strlen(Ptr) + 1; + + /* Put a zero after the string */ + *(DestBuffer++) = 0; + } + + /* Set the final zero */ + *(DestBuffer++) = 0; + + /* Copy the program name after the environment block */ + strcpy(DestBuffer, ProgramName); + + return DestSegment; +} + +/* Taken from base/shell/cmd/console.c */ +BOOL IsConsoleHandle(HANDLE hHandle) +{ + DWORD dwMode; + + /* Check whether the handle may be that of a console... */ + if ((GetFileType(hHandle) & FILE_TYPE_CHAR) == 0) return FALSE; + + /* + * It may be. Perform another test... The idea comes from the + * MSDN description of the WriteConsole API: + * + * "WriteConsole fails if it is used with a standard handle + * that is redirected to a file. If an application processes + * multilingual output that can be redirected, determine whether + * the output handle is a console handle (one method is to call + * the GetConsoleMode function and check whether it succeeds). + * If the handle is a console handle, call WriteConsole. If the + * handle is not a console handle, the output is redirected and + * you should call WriteFile to perform the I/O." + */ + return GetConsoleMode(hHandle, &dwMode); +} + +static WORD DosOpenHandle(HANDLE Handle) +{ + BYTE i; + WORD DosHandle; + PDOS_PSP PspBlock; + LPBYTE HandleTable; + + /* The system PSP has no handle table */ + if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE; + + /* Get a pointer to the handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Find a free entry in the JFT */ + for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++) + { + if (HandleTable[DosHandle] == 0xFF) break; + } + + /* If there are no free entries, fail */ + if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE; + + /* Check if the handle is already in the SFT */ + for (i = 0; i < DOS_SFT_SIZE; i++) + { + /* Check if this is the same handle */ + if (DosSystemFileTable[i] != Handle) continue; + + /* Already in the table, reference it */ + DosSftRefCount[i]++; + + /* Set the JFT entry to that SFT index */ + HandleTable[DosHandle] = i; + + /* Return the new handle */ + return DosHandle; + } + + /* Add the handle to the SFT */ + for (i = 0; i < DOS_SFT_SIZE; i++) + { + /* Make sure this is an empty table entry */ + if (DosSystemFileTable[i] != INVALID_HANDLE_VALUE) continue; + + /* Initialize the empty table entry */ + DosSystemFileTable[i] = Handle; + DosSftRefCount[i] = 1; + + /* Set the JFT entry to that SFT index */ + HandleTable[DosHandle] = i; + + /* Return the new handle */ + return DosHandle; + } + + /* The SFT is full */ + return INVALID_DOS_HANDLE; +} + +HANDLE DosGetRealHandle(WORD DosHandle) +{ + PDOS_PSP PspBlock; + LPBYTE HandleTable; + + /* The system PSP has no handle table */ + if (CurrentPsp == SYSTEM_PSP) return INVALID_HANDLE_VALUE; + + /* Get a pointer to the handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Make sure the handle is open */ + if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE; + + /* Return the Win32 handle */ + return DosSystemFileTable[HandleTable[DosHandle]]; +} + +static VOID DosCopyHandleTable(LPBYTE DestinationTable) +{ + INT i; + PDOS_PSP PspBlock; + LPBYTE SourceTable; + + /* Clear the table first */ + for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF; + + /* Check if this is the initial process */ + if (CurrentPsp == SYSTEM_PSP) + { + /* Set up the standard I/O devices */ + for (i = 0; i <= 2; i++) + { + /* Set the index in the SFT */ + DestinationTable[i] = (BYTE)i; + + /* Increase the reference count */ + DosSftRefCount[i]++; + } + + /* Done */ + return; + } + + /* Get the parent PSP block and handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Copy the first 20 handles into the new table */ + for (i = 0; i < 20; i++) + { + DestinationTable[i] = SourceTable[i]; + + /* Increase the reference count */ + DosSftRefCount[SourceTable[i]]++; + } +} + +static BOOLEAN DosCloseHandle(WORD DosHandle) +{ + BYTE SftIndex; + PDOS_PSP PspBlock; + LPBYTE HandleTable; + + DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle); + + /* The system PSP has no handle table */ + if (CurrentPsp == SYSTEM_PSP) return FALSE; + + /* Get a pointer to the handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Make sure the handle is open */ + if (HandleTable[DosHandle] == 0xFF) return FALSE; + + /* Decrement the reference count of the SFT entry */ + SftIndex = HandleTable[DosHandle]; + DosSftRefCount[SftIndex]--; + + /* Check if the reference count fell to zero */ + if (!DosSftRefCount[SftIndex]) + { + /* Close the file, it's no longer needed */ + CloseHandle(DosSystemFileTable[SftIndex]); + + /* Clear the handle */ + DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE; + } + + /* Clear the entry in the JFT */ + HandleTable[DosHandle] = 0xFF; + + return TRUE; +} + +static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle) +{ + BYTE SftIndex; + PDOS_PSP PspBlock; + LPBYTE HandleTable; + + DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n", + OldHandle, + NewHandle); + + /* The system PSP has no handle table */ + if (CurrentPsp == SYSTEM_PSP) return FALSE; + + /* Get a pointer to the handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Make sure the old handle is open */ + if (HandleTable[OldHandle] == 0xFF) return FALSE; + + /* Check if the new handle is open */ + if (HandleTable[NewHandle] != 0xFF) + { + /* Close it */ + DosCloseHandle(NewHandle); + } + + /* Increment the reference count of the SFT entry */ + SftIndex = HandleTable[OldHandle]; + DosSftRefCount[SftIndex]++; + + /* Make the new handle point to that SFT entry */ + HandleTable[NewHandle] = SftIndex; + + /* Return success */ + return TRUE; +} + +static WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes) +{ + HANDLE FileHandle; + WORD DosHandle; + + DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n", + FilePath, + Attributes); + + /* Create the file */ + FileHandle = CreateFileA(FilePath, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_ALWAYS, + Attributes, + NULL); + + if (FileHandle == INVALID_HANDLE_VALUE) + { + /* Return the error code */ + return (WORD)GetLastError(); + } + + /* Open the DOS handle */ + DosHandle = DosOpenHandle(FileHandle); + + if (DosHandle == INVALID_DOS_HANDLE) + { + /* Close the handle */ + CloseHandle(FileHandle); + + /* Return the error code */ + return ERROR_TOO_MANY_OPEN_FILES; + } + + /* It was successful */ + *Handle = DosHandle; + return ERROR_SUCCESS; +} + +static WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode) +{ + HANDLE FileHandle; + ACCESS_MASK Access = 0; + WORD DosHandle; + + DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n", + FilePath, + AccessMode); + + /* Parse the access mode */ + switch (AccessMode & 3) + { + case 0: + { + /* Read-only */ + Access = GENERIC_READ; + break; + } + + case 1: + { + /* Write only */ + Access = GENERIC_WRITE; + break; + } + + case 2: + { + /* Read and write */ + Access = GENERIC_READ | GENERIC_WRITE; + break; + } + + default: + { + /* Invalid */ + return ERROR_INVALID_PARAMETER; + } + } + + /* Open the file */ + FileHandle = CreateFileA(FilePath, + Access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (FileHandle == INVALID_HANDLE_VALUE) + { + /* Return the error code */ + return (WORD)GetLastError(); + } + + /* Open the DOS handle */ + DosHandle = DosOpenHandle(FileHandle); + + if (DosHandle == INVALID_DOS_HANDLE) + { + /* Close the handle */ + CloseHandle(FileHandle); + + /* Return the error code */ + return ERROR_TOO_MANY_OPEN_FILES; + } + + /* It was successful */ + *Handle = DosHandle; + return ERROR_SUCCESS; +} + +WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead) +{ + WORD Result = ERROR_SUCCESS; + DWORD BytesRead32 = 0; + HANDLE Handle = DosGetRealHandle(FileHandle); + + DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count); + + /* Make sure the handle is valid */ + if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; + + /* Read the file */ + if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL)) + { + /* Store the error code */ + Result = (WORD)GetLastError(); + } + + /* The number of bytes read is always 16-bit */ + *BytesRead = LOWORD(BytesRead32); + + /* Return the error code */ + return Result; +} + +WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten) +{ + WORD Result = ERROR_SUCCESS; + DWORD BytesWritten32 = 0; + HANDLE Handle = DosGetRealHandle(FileHandle); + WORD i; + + DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n", + FileHandle, + Count); + + /* Make sure the handle is valid */ + if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; + + if (IsConsoleHandle(Handle)) + { + for (i = 0; i < Count; i++) + { + /* Save AX and BX */ + USHORT AX = getAX(); + USHORT BX = getBX(); + + /* Set the parameters */ + setAL(((PCHAR)Buffer)[i]); + setBL(DOS_CHAR_ATTRIBUTE); + setBH(Bda->VideoPage); + + /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ + setAH(0x0E); + Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT); + + /* Restore AX and BX */ + setBX(BX); + setAX(AX); + + BytesWritten32++; + } + } + else + { + /* Write the file */ + if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL)) + { + /* Store the error code */ + Result = (WORD)GetLastError(); + } + } + + /* The number of bytes written is always 16-bit */ + *BytesWritten = LOWORD(BytesWritten32); + + /* Return the error code */ + return Result; +} + +static WORD DosSeekFile(WORD FileHandle, LONG Offset, BYTE Origin, LPDWORD NewOffset) +{ + WORD Result = ERROR_SUCCESS; + DWORD FilePointer; + HANDLE Handle = DosGetRealHandle(FileHandle); + + DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n", + FileHandle, + Offset, + Origin); + + /* Make sure the handle is valid */ + if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; + + /* Check if the origin is valid */ + if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END) + { + return ERROR_INVALID_FUNCTION; + } + + /* Move the file pointer */ + FilePointer = SetFilePointer(Handle, Offset, NULL, Origin); + + /* Check if there's a possibility the operation failed */ + if (FilePointer == INVALID_SET_FILE_POINTER) + { + /* Get the real error code */ + Result = (WORD)GetLastError(); + } + + if (Result != ERROR_SUCCESS) + { + /* The operation did fail */ + return Result; + } + + /* Return the file pointer, if requested */ + if (NewOffset) *NewOffset = FilePointer; + + /* Return success */ + return ERROR_SUCCESS; +} + +static BOOLEAN DosFlushFileBuffers(WORD FileHandle) +{ + HANDLE Handle = DosGetRealHandle(FileHandle); + + /* Make sure the handle is valid */ + if (Handle == INVALID_HANDLE_VALUE) return FALSE; + + /* + * No need to check whether the handle is a console handle since + * FlushFileBuffers() automatically does this check and calls + * FlushConsoleInputBuffer() for us. + */ + // if (IsConsoleHandle(Handle)) + // return (BOOLEAN)FlushConsoleInputBuffer(Handle); + // else + return (BOOLEAN)FlushFileBuffers(Handle); +} + +static BOOLEAN DosChangeDrive(BYTE Drive) +{ + WCHAR DirectoryPath[DOS_CMDLINE_LENGTH]; + + /* Make sure the drive exists */ + if (Drive > (LastDrive - 'A')) return FALSE; + + /* Find the path to the new current directory */ + swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]); + + /* Change the current directory of the process */ + if (!SetCurrentDirectory(DirectoryPath)) return FALSE; + + /* Set the current drive */ + CurrentDrive = Drive; + + /* Return success */ + return TRUE; +} + +static BOOLEAN DosChangeDirectory(LPSTR Directory) +{ + BYTE DriveNumber; + DWORD Attributes; + LPSTR Path; + + /* Make sure the directory path is not too long */ + if (strlen(Directory) >= DOS_DIR_LENGTH) + { + DosLastError = ERROR_PATH_NOT_FOUND; + return FALSE; + } + + /* Get the drive number */ + DriveNumber = Directory[0] - 'A'; + + /* Make sure the drive exists */ + if (DriveNumber > (LastDrive - 'A')) + { + DosLastError = ERROR_PATH_NOT_FOUND; + return FALSE; + } + + /* Get the file attributes */ + Attributes = GetFileAttributesA(Directory); + + /* Make sure the path exists and is a directory */ + if ((Attributes == INVALID_FILE_ATTRIBUTES) + || !(Attributes & FILE_ATTRIBUTE_DIRECTORY)) + { + DosLastError = ERROR_PATH_NOT_FOUND; + return FALSE; + } + + /* Check if this is the current drive */ + if (DriveNumber == CurrentDrive) + { + /* Change the directory */ + if (!SetCurrentDirectoryA(Directory)) + { + DosLastError = LOWORD(GetLastError()); + return FALSE; + } + } + + /* Get the directory part of the path */ + Path = strchr(Directory, '\\'); + if (Path != NULL) + { + /* Skip the backslash */ + Path++; + } + + /* Set the directory for the drive */ + if (Path != NULL) + { + strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH); + } + else + { + CurrentDirectories[DriveNumber][0] = '\0'; + } + + /* Return success */ + return TRUE; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment) +{ + PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment); + LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress); + + ZeroMemory(PspBlock, sizeof(DOS_PSP)); + + /* Set the exit interrupt */ + PspBlock->Exit[0] = 0xCD; // int 0x20 + PspBlock->Exit[1] = 0x20; + + /* Set the number of the last paragraph */ + PspBlock->LastParagraph = PspSegment + ProgramSize - 1; + + /* Save the interrupt vectors */ + PspBlock->TerminateAddress = IntVecTable[0x22]; + PspBlock->BreakAddress = IntVecTable[0x23]; + PspBlock->CriticalAddress = IntVecTable[0x24]; + + /* Set the parent PSP */ + PspBlock->ParentPsp = CurrentPsp; + + /* Copy the parent handle table */ + DosCopyHandleTable(PspBlock->HandleTable); + + /* Set the environment block */ + PspBlock->EnvBlock = Environment; + + /* Set the handle table pointers to the internal handle table */ + PspBlock->HandleTableSize = 20; + PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment); + + /* Set the DOS version */ + PspBlock->DosVersion = DOS_VERSION; + + /* Set the far call opcodes */ + PspBlock->FarCall[0] = 0xCD; // int 0x21 + PspBlock->FarCall[1] = 0x21; + PspBlock->FarCall[2] = 0xCB; // retf + + /* Set the command line */ + PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1); + RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize); + PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r'; +} + +DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType, + IN LPCSTR ExecutablePath, + IN LPCSTR CommandLine, + IN PVOID Environment, + OUT PDWORD StackLocation OPTIONAL, + OUT PDWORD EntryPoint OPTIONAL) +{ + DWORD Result = ERROR_SUCCESS; + HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL; + LPBYTE Address = NULL; + WORD Segment = 0; + WORD EnvBlock = 0; + WORD MaxAllocSize; + DWORD i, FileSize, ExeSize; + PIMAGE_DOS_HEADER Header; + PDWORD RelocationTable; + PWORD RelocWord; + + DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n", + LoadType, + ExecutablePath, + CommandLine, + Environment, + StackLocation, + EntryPoint); + + if (LoadType == DOS_LOAD_OVERLAY) + { + DPRINT1("Overlay loading is not supported yet.\n"); + return ERROR_NOT_SUPPORTED; + } + + /* Open a handle to the executable */ + FileHandle = CreateFileA(ExecutablePath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (FileHandle == INVALID_HANDLE_VALUE) + { + Result = GetLastError(); + goto Cleanup; + } + + /* Get the file size */ + FileSize = GetFileSize(FileHandle, NULL); + + /* Create a mapping object for the file */ + FileMapping = CreateFileMapping(FileHandle, + NULL, + PAGE_READONLY, + 0, + 0, + NULL); + if (FileMapping == NULL) + { + Result = GetLastError(); + goto Cleanup; + } + + /* Map the file into memory */ + Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0); + if (Address == NULL) + { + Result = GetLastError(); + goto Cleanup; + } + + /* Copy the environment block to DOS memory */ + EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath); + if (EnvBlock == 0) + { + Result = ERROR_NOT_ENOUGH_MEMORY; + goto Cleanup; + } + + /* Check if this is an EXE file or a COM file */ + if (Address[0] == 'M' && Address[1] == 'Z') + { + /* EXE file */ + + /* Get the MZ header */ + Header = (PIMAGE_DOS_HEADER)Address; + + /* Get the base size of the file, in paragraphs (rounded up) */ + ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4; + + /* Add the PSP size, in paragraphs */ + ExeSize += sizeof(DOS_PSP) >> 4; + + /* Add the maximum size that should be allocated */ + ExeSize += Header->e_maxalloc; + + /* Make sure it does not pass 0xFFFF */ + if (ExeSize > 0xFFFF) ExeSize = 0xFFFF; + + /* Reduce the size one by one until the allocation is successful */ + for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--) + { + /* Try to allocate that much memory */ + Segment = DosAllocateMemory((WORD)ExeSize, NULL); + if (Segment != 0) break; + } + + /* Check if at least the lowest allocation was successful */ + if (Segment == 0) + { + Result = ERROR_NOT_ENOUGH_MEMORY; + goto Cleanup; + } + + /* Initialize the PSP */ + DosInitializePsp(Segment, + CommandLine, + (WORD)ExeSize, + EnvBlock); + + /* The process owns its own memory */ + DosChangeMemoryOwner(Segment, Segment); + DosChangeMemoryOwner(EnvBlock, Segment); + + /* Copy the program to Segment:0100 */ + RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100), + Address + (Header->e_cparhdr << 4), + min(FileSize - (Header->e_cparhdr << 4), + (ExeSize << 4) - sizeof(DOS_PSP))); + + /* Get the relocation table */ + RelocationTable = (PDWORD)(Address + Header->e_lfarlc); + + /* Perform relocations */ + for (i = 0; i < Header->e_crlc; i++) + { + /* Get a pointer to the word that needs to be patched */ + RelocWord = (PWORD)SEG_OFF_TO_PTR(Segment + HIWORD(RelocationTable[i]), + 0x100 + LOWORD(RelocationTable[i])); + + /* Add the number of the EXE segment to it */ + *RelocWord += Segment + (sizeof(DOS_PSP) >> 4); + } + + if (LoadType == DOS_LOAD_AND_EXECUTE) + { + /* Set the initial segment registers */ + setDS(Segment); + setES(Segment); + + /* Set the stack to the location from the header */ + EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss, + Header->e_sp); + + /* Execute */ + CurrentPsp = Segment; + DiskTransferArea = MAKELONG(0x80, Segment); + EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4), + Header->e_ip); + } + } + else + { + /* COM file */ + + /* Find the maximum amount of memory that can be allocated */ + DosAllocateMemory(0xFFFF, &MaxAllocSize); + + /* Make sure it's enough for the whole program and the PSP */ + if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP))) + { + Result = ERROR_NOT_ENOUGH_MEMORY; + goto Cleanup; + } + + /* Allocate all of it */ + Segment = DosAllocateMemory(MaxAllocSize, NULL); + if (Segment == 0) + { + Result = ERROR_ARENA_TRASHED; + goto Cleanup; + } + + /* The process owns its own memory */ + DosChangeMemoryOwner(Segment, Segment); + DosChangeMemoryOwner(EnvBlock, Segment); + + /* Copy the program to Segment:0100 */ + RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100), + Address, + FileSize); + + /* Initialize the PSP */ + DosInitializePsp(Segment, + CommandLine, + MaxAllocSize, + EnvBlock); + + if (LoadType == DOS_LOAD_AND_EXECUTE) + { + /* Set the initial segment registers */ + setDS(Segment); + setES(Segment); + + /* Set the stack to the last word of the segment */ + EmulatorSetStack(Segment, 0xFFFE); + + /* + * Set the value on the stack to 0, so that a near return + * jumps to PSP:0000 which has the exit code. + */ + *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0; + + /* Execute */ + CurrentPsp = Segment; + DiskTransferArea = MAKELONG(0x80, Segment); + EmulatorExecute(Segment, 0x100); + } + } + +Cleanup: + if (Result != ERROR_SUCCESS) + { + /* It was not successful, cleanup the DOS memory */ + if (EnvBlock) DosFreeMemory(EnvBlock); + if (Segment) DosFreeMemory(Segment); + } + + /* Unmap the file*/ + if (Address != NULL) UnmapViewOfFile(Address); + + /* Close the file mapping object */ + if (FileMapping != NULL) CloseHandle(FileMapping); + + /* Close the file handle */ + if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle); + + return Result; +} + +WORD DosCreateProcess(DOS_EXEC_TYPE LoadType, + LPCSTR ProgramName, + PDOS_EXEC_PARAM_BLOCK Parameters) +{ + DWORD Result; + DWORD BinaryType; + LPVOID Environment = NULL; + VDM_COMMAND_INFO CommandInfo; + CHAR CmdLine[MAX_PATH]; + CHAR AppName[MAX_PATH]; + CHAR PifFile[MAX_PATH]; + CHAR Desktop[MAX_PATH]; + CHAR Title[MAX_PATH]; + CHAR Env[MAX_PATH]; + STARTUPINFOA StartupInfo; + PROCESS_INFORMATION ProcessInfo; + + /* Get the binary type */ + if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError(); + + /* Did the caller specify an environment segment? */ + if (Parameters->Environment) + { + /* Yes, use it instead of the parent one */ + Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0); + } + + /* Set up the startup info structure */ + ZeroMemory(&StartupInfo, sizeof(STARTUPINFOA)); + StartupInfo.cb = sizeof(STARTUPINFOA); + + /* Create the process */ + if (!CreateProcessA(ProgramName, + FAR_POINTER(Parameters->CommandLine), + NULL, + NULL, + FALSE, + 0, + Environment, + NULL, + &StartupInfo, + &ProcessInfo)) + { + return GetLastError(); + } + + /* Check the type of the program */ + switch (BinaryType) + { + /* These are handled by NTVDM */ + case SCS_DOS_BINARY: + case SCS_WOW_BINARY: + { + /* Clear the structure */ + ZeroMemory(&CommandInfo, sizeof(CommandInfo)); + + /* Initialize the structure members */ + CommandInfo.TaskId = SessionId; + CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT; + CommandInfo.CmdLine = CmdLine; + CommandInfo.CmdLen = sizeof(CmdLine); + CommandInfo.AppName = AppName; + CommandInfo.AppLen = sizeof(AppName); + CommandInfo.PifFile = PifFile; + CommandInfo.PifLen = sizeof(PifFile); + CommandInfo.Desktop = Desktop; + CommandInfo.DesktopLen = sizeof(Desktop); + CommandInfo.Title = Title; + CommandInfo.TitleLen = sizeof(Title); + CommandInfo.Env = Env; + CommandInfo.EnvLen = sizeof(Env); + + /* Get the VDM command information */ + if (!GetNextVDMCommand(&CommandInfo)) + { + /* Shouldn't happen */ + ASSERT(FALSE); + } + + /* Increment the re-entry count */ + CommandInfo.VDMState = VDM_INC_REENTER_COUNT; + GetNextVDMCommand(&CommandInfo); + + /* Load the executable */ + Result= DosLoadExecutable(LoadType, + AppName, + CmdLine, + Env, + &Parameters->StackLocation, + &Parameters->EntryPoint); + if (Result != ERROR_SUCCESS) + { + DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result); + break; + } + + break; + } + + /* Not handled by NTVDM */ + default: + { + /* Wait for the process to finish executing */ + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + } + } + + /* Close the handles */ + CloseHandle(ProcessInfo.hProcess); + CloseHandle(ProcessInfo.hThread); + + return ERROR_SUCCESS; +} + +VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode) +{ + WORD i; + WORD McbSegment = FIRST_MCB_SEGMENT; + PDOS_MCB CurrentMcb; + LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress); + PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp); + VDM_COMMAND_INFO CommandInfo; + + DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n", + Psp, + ReturnCode); + + /* Check if this PSP is it's own parent */ + if (PspBlock->ParentPsp == Psp) goto Done; + + for (i = 0; i < PspBlock->HandleTableSize; i++) + { + /* Close the handle */ + DosCloseHandle(i); + } + + /* Free the memory used by the process */ + while (TRUE) + { + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(McbSegment); + + /* Make sure the MCB is valid */ + if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break; + + /* If this block was allocated by the process, free it */ + if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1); + + /* If this was the last block, quit */ + if (CurrentMcb->BlockType == 'Z') break; + + /* Update the segment and continue */ + McbSegment += CurrentMcb->Size + 1; + } + +Done: + /* Restore the interrupt vectors */ + IntVecTable[0x22] = PspBlock->TerminateAddress; + IntVecTable[0x23] = PspBlock->BreakAddress; + IntVecTable[0x24] = PspBlock->CriticalAddress; + + /* Update the current PSP */ + if (Psp == CurrentPsp) + { + CurrentPsp = PspBlock->ParentPsp; + if (CurrentPsp == SYSTEM_PSP) + { + ResetEvent(VdmTaskEvent); + EmulatorUnsimulate(); + } + } + + // FIXME: This is probably not the best way to do it + /* Check if this was a nested DOS task */ + if (CurrentPsp != SYSTEM_PSP) + { + /* Decrement the re-entry count */ + CommandInfo.TaskId = SessionId; + CommandInfo.VDMState = VDM_DEC_REENTER_COUNT; + GetNextVDMCommand(&CommandInfo); + + /* Clear the structure */ + ZeroMemory(&CommandInfo, sizeof(CommandInfo)); + + /* Update the VDM state of the task */ + CommandInfo.TaskId = SessionId; + CommandInfo.VDMState = VDM_FLAG_DONT_WAIT; + GetNextVDMCommand(&CommandInfo); + } + + /* Save the return code - Normal termination */ + DosErrorLevel = MAKEWORD(ReturnCode, 0x00); + + /* Return control to the parent process */ + EmulatorExecute(HIWORD(PspBlock->TerminateAddress), + LOWORD(PspBlock->TerminateAddress)); +} + +BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle) +{ + HANDLE Handle = DosGetRealHandle(FileHandle); + + if (Handle == INVALID_HANDLE_VALUE) + { + /* Doesn't exist */ + DosLastError = ERROR_FILE_NOT_FOUND; + return FALSE; + } + + switch (ControlCode) + { + /* Get Device Information */ + case 0x00: + { + WORD InfoWord = 0; + + /* + * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm + * for a list of possible flags. + */ + + if (Handle == DosSystemFileTable[0]) + { + /* Console input */ + InfoWord |= 1 << 0; + } + else if (Handle == DosSystemFileTable[1]) + { + /* Console output */ + InfoWord |= 1 << 1; + } + + /* It is a device */ + InfoWord |= 1 << 7; + + /* Return the device information word */ + setDX(InfoWord); + return TRUE; + } + + /* Unsupported control code */ + default: + { + DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode); + + DosLastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + } +} + +VOID WINAPI DosInt20h(LPWORD Stack) +{ + /* This is the exit interrupt */ + DosTerminateProcess(Stack[STACK_CS], 0); +} + +VOID WINAPI DosInt21h(LPWORD Stack) +{ + BYTE Character; + SYSTEMTIME SystemTime; + PCHAR String; + PDOS_INPUT_BUFFER InputBuffer; + + /* Check the value in the AH register */ + switch (getAH()) + { + /* Terminate Program */ + case 0x00: + { + DosTerminateProcess(Stack[STACK_CS], 0); + break; + } + + /* Read Character from STDIN with Echo */ + case 0x01: + { + Character = DosReadCharacter(); + DosPrintCharacter(Character); + + /* Let the BOP repeat if needed */ + if (getCF()) break; + + setAL(Character); + break; + } + + /* Write Character to STDOUT */ + case 0x02: + { + Character = getDL(); + DosPrintCharacter(Character); + + /* + * We return the output character (DOS 2.1+). + * Also, if we're going to output a TAB, then + * don't return a TAB but a SPACE instead. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm + * for more information. + */ + setAL(Character == '\t' ? ' ' : Character); + break; + } + + /* Read Character from STDAUX */ + case 0x03: + { + // FIXME: Really read it from STDAUX! + DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n"); + setAL(DosReadCharacter()); + break; + } + + /* Write Character to STDAUX */ + case 0x04: + { + // FIXME: Really write it to STDAUX! + DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n"); + DosPrintCharacter(getDL()); + break; + } + + /* Write Character to Printer */ + case 0x05: + { + // FIXME: Really write it to printer! + DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n"); + DPRINT1("0x%p\n", getDL()); + DPRINT1("\n\n-----------\n\n"); + break; + } + + /* Direct Console I/O */ + case 0x06: + { + Character = getDL(); + + if (Character != 0xFF) + { + /* Output */ + DosPrintCharacter(Character); + + /* + * We return the output character (DOS 2.1+). + * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm + * for more information. + */ + setAL(Character); + } + else + { + /* Input */ + if (DosCheckInput()) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF; + setAL(DosReadCharacter()); + } + else + { + /* No character available */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF; + setAL(0x00); + } + } + + break; + } + + /* Character Input without Echo */ + case 0x07: + case 0x08: + { + Character = DosReadCharacter(); + + /* Let the BOP repeat if needed */ + if (getCF()) break; + + setAL(Character); + break; + } + + /* Write string to STDOUT */ + case 0x09: + { + String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); + + while (*String != '$') + { + DosPrintCharacter(*String); + String++; + } + + /* + * We return the terminating character (DOS 2.1+). + * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm + * for more information. + */ + setAL('$'); + break; + } + + /* Read Buffered Input */ + case 0x0A: + { + InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX()); + + while (Stack[STACK_COUNTER] < InputBuffer->MaxLength) + { + /* Try to read a character */ + Character = DosReadCharacter(); + + /* If it's not ready yet, let the BOP repeat */ + if (getCF()) break; + + /* Echo the character and append it to the buffer */ + DosPrintCharacter(Character); + InputBuffer->Buffer[Stack[STACK_COUNTER]] = Character; + + if (Character == '\r') break; + Stack[STACK_COUNTER]++; + } + + /* Update the length */ + InputBuffer->Length = Stack[STACK_COUNTER]; + break; + } + + /* Get STDIN Status */ + case 0x0B: + { + setAL(DosCheckInput() ? 0xFF : 0x00); + break; + } + + /* Flush Buffer and Read STDIN */ + case 0x0C: + { + BYTE InputFunction = getAL(); + + /* Flush STDIN buffer */ + DosFlushFileBuffers(DOS_INPUT_HANDLE); // Maybe just create a DosFlushInputBuffer... + + /* + * If the input function number contained in AL is valid, i.e. + * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves + * recursively with AL == AH. + */ + if (InputFunction == 0x01 || InputFunction == 0x06 || + InputFunction == 0x07 || InputFunction == 0x08 || + InputFunction == 0x0A) + { + setAH(InputFunction); + /* + * Instead of calling ourselves really recursively as in: + * DosInt21h(Stack); + * prefer resetting the CF flag to let the BOP repeat. + */ + setCF(1); + } + break; + } + + /* Disk Reset */ + case 0x0D: + { + PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp); + + // TODO: Flush what's needed. + DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n"); + + /* Clear CF in DOS 6 only */ + if (PspBlock->DosVersion == 0x0006) + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + + break; + } + + /* Set Default Drive */ + case 0x0E: + { + DosChangeDrive(getDL()); + setAL(LastDrive - 'A' + 1); + break; + } + + /* NULL Function for CP/M Compatibility */ + case 0x18: + { + /* + * This function corresponds to the CP/M BDOS function + * "get bit map of logged drives", which is meaningless + * under MS-DOS. + * + * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB + * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm + * for more information. + */ + setAL(0x00); + break; + } + + /* Get Default Drive */ + case 0x19: + { + setAL(CurrentDrive); + break; + } + + /* Set Disk Transfer Area */ + case 0x1A: + { + DiskTransferArea = MAKELONG(getDX(), getDS()); + break; + } + + /* NULL Function for CP/M Compatibility */ + case 0x1D: + case 0x1E: + { + /* + * Function 0x1D corresponds to the CP/M BDOS function + * "get bit map of read-only drives", which is meaningless + * under MS-DOS. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm + * for more information. + * + * Function 0x1E corresponds to the CP/M BDOS function + * "set file attributes", which was meaningless under MS-DOS 1.x. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm + * for more information. + */ + setAL(0x00); + break; + } + + /* NULL Function for CP/M Compatibility */ + case 0x20: + { + /* + * This function corresponds to the CP/M BDOS function + * "get/set default user (sublibrary) number", which is meaningless + * under MS-DOS. + * + * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION + * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm + * for more information. + */ + setAL(0x00); + break; + } + + /* Set Interrupt Vector */ + case 0x25: + { + ULONG FarPointer = MAKELONG(getDX(), getDS()); + DPRINT1("Setting interrupt 0x%x ...\n", getAL()); + + /* Write the new far pointer to the IDT */ + ((PULONG)BaseAddress)[getAL()] = FarPointer; + break; + } + + /* Create New PSP */ + case 0x26: + { + DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n"); + break; + } + + /* Get System Date */ + case 0x2A: + { + GetLocalTime(&SystemTime); + setCX(SystemTime.wYear); + setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth)); + setAL(SystemTime.wDayOfWeek); + break; + } + + /* Set System Date */ + case 0x2B: + { + GetLocalTime(&SystemTime); + SystemTime.wYear = getCX(); + SystemTime.wMonth = getDH(); + SystemTime.wDay = getDL(); + + /* Return success or failure */ + setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF); + break; + } + + /* Get System Time */ + case 0x2C: + { + GetLocalTime(&SystemTime); + setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour)); + setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond)); + break; + } + + /* Set System Time */ + case 0x2D: + { + GetLocalTime(&SystemTime); + SystemTime.wHour = getCH(); + SystemTime.wMinute = getCL(); + SystemTime.wSecond = getDH(); + SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds + + /* Return success or failure */ + setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF); + break; + } + + /* Get Disk Transfer Area */ + case 0x2F: + { + setES(HIWORD(DiskTransferArea)); + setBX(LOWORD(DiskTransferArea)); + break; + } + + /* Get DOS Version */ + case 0x30: + { + PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp); + + /* + * DOS 2+ - GET DOS VERSION + * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm + * for more information. + */ + + if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00) + { + /* + * Return DOS OEM number: + * 0x00 for IBM PC-DOS + * 0x02 for packaged MS-DOS + */ + setBH(0x02); + } + + if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01) + { + /* + * Return version flag: + * 1 << 3 if DOS is in ROM, + * 0 (reserved) if not. + */ + setBH(0x00); + } + + /* Return DOS 24-bit user serial number in BL:CX */ + setBL(0x00); + setCX(0x0000); + + /* + * Return DOS version: Minor:Major in AH:AL + * The Windows NT DOS box returns version 5.00, subject to SETVER. + */ + setAX(PspBlock->DosVersion); + + break; + } + + /* Extended functionalities */ + case 0x33: + { + if (getAL() == 0x06) + { + /* + * DOS 5+ - GET TRUE VERSION NUMBER + * This function always returns the true version number, unlike + * AH=30h, whose return value may be changed with SETVER. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm + * for more information. + */ + + /* + * Return the true DOS version: Minor:Major in BH:BL + * The Windows NT DOS box returns BX=3205h (version 5.50). + */ + setBX(NTDOS_VERSION); + + /* DOS revision 0 */ + setDL(0x00); + + /* Unpatched DOS */ + setDH(0x00); + } + // else + // { + // /* Invalid subfunction */ + // setAL(0xFF); + // } + + break; + } + + /* Get Interrupt Vector */ + case 0x35: + { + DWORD FarPointer = ((PDWORD)BaseAddress)[getAL()]; + + /* Read the address from the IDT into ES:BX */ + setES(HIWORD(FarPointer)); + setBX(LOWORD(FarPointer)); + break; + } + + /* SWITCH character - AVAILDEV */ + case 0x37: + { + if (getAL() == 0x00) + { + /* + * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER + * This setting is ignored by MS-DOS 4.0+. + * MS-DOS 5+ always return AL=00h/DL=2Fh. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm + * for more information. + */ + setDL('/'); + setAL(0x00); + } + else if (getAL() == 0x01) + { + /* + * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER + * This setting is ignored by MS-DOS 5+. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm + * for more information. + */ + // getDL(); + setAL(0xFF); + } + else if (getAL() == 0x02) + { + /* + * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE + * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm + * for more information. + */ + // setDL(); + setAL(0xFF); + } + else if (getAL() == 0x03) + { + /* + * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE + * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm + * for more information. + */ + // getDL(); + setAL(0xFF); + } + else + { + /* Invalid subfunction */ + setAL(0xFF); + } + + break; + } + + /* Create Directory */ + case 0x39: + { + String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); + + if (CreateDirectoryA(String, NULL)) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(LOWORD(GetLastError())); + } + + break; + } + + /* Remove Directory */ + case 0x3A: + { + String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); + + if (RemoveDirectoryA(String)) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(LOWORD(GetLastError())); + } + + break; + } + + /* Set Current Directory */ + case 0x3B: + { + String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); + + if (DosChangeDirectory(String)) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(DosLastError); + } + + break; + } + + /* Create File */ + case 0x3C: + { + WORD FileHandle; + WORD ErrorCode = DosCreateFile(&FileHandle, + (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()), + getCX()); + + if (ErrorCode == 0) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(FileHandle); + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ErrorCode); + } + + break; + } + + /* Open File */ + case 0x3D: + { + WORD FileHandle; + WORD ErrorCode = DosOpenFile(&FileHandle, + (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()), + getAL()); + + if (ErrorCode == 0) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(FileHandle); + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ErrorCode); + } + + break; + } + + /* Close File */ + case 0x3E: + { + if (DosCloseHandle(getBX())) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_HANDLE); + } + + break; + } + + /* Read from File or Device */ + case 0x3F: + { + WORD Handle = getBX(); + LPBYTE Buffer = (LPBYTE)SEG_OFF_TO_PTR(getDS(), getDX()); + WORD Count = getCX(); + WORD BytesRead = 0; + WORD ErrorCode = ERROR_SUCCESS; + CHAR Character; + + if (IsConsoleHandle(DosGetRealHandle(Handle))) + { + while (Stack[STACK_COUNTER] < Count) + { + /* Read a character from the BIOS */ + Character = LOBYTE(BiosGetCharacter()); + + /* Stop if the BOP needs to be repeated */ + if (getCF()) break; + + // FIXME: Security checks! + DosPrintCharacter(Character); + Buffer[Stack[STACK_COUNTER]++] = Character; + + if (Character == '\r') + { + /* Stop on first carriage return */ + DosPrintCharacter('\n'); + break; + } + } + + if (Character != '\r') + { + if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY; + else BytesRead = Count; + } + else BytesRead = Stack[STACK_COUNTER]; + } + else + { + /* Use the file reading function */ + ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead); + } + + if (ErrorCode == ERROR_SUCCESS) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(BytesRead); + } + else if (ErrorCode != ERROR_NOT_READY) + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ErrorCode); + } + break; + } + + /* Write to File or Device */ + case 0x40: + { + WORD BytesWritten = 0; + WORD ErrorCode = DosWriteFile(getBX(), + SEG_OFF_TO_PTR(getDS(), getDX()), + getCX(), + &BytesWritten); + + if (ErrorCode == ERROR_SUCCESS) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(BytesWritten); + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ErrorCode); + } + + break; + } + + /* Delete File */ + case 0x41: + { + LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); + + if (demFileDelete(FileName) == ERROR_SUCCESS) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + /* + * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm + * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file." + */ + setAL(FileName[0] - 'A'); + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(GetLastError()); + } + + break; + } + + /* Seek File */ + case 0x42: + { + DWORD NewLocation; + WORD ErrorCode = DosSeekFile(getBX(), + MAKELONG(getDX(), getCX()), + getAL(), + &NewLocation); + + if (ErrorCode == ERROR_SUCCESS) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + + /* Return the new offset in DX:AX */ + setDX(HIWORD(NewLocation)); + setAX(LOWORD(NewLocation)); + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ErrorCode); + } + + break; + } + + /* Get/Set File Attributes */ + case 0x43: + { + DWORD Attributes; + LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); + + if (getAL() == 0x00) + { + /* Get the attributes */ + Attributes = GetFileAttributesA(FileName); + + /* Check if it failed */ + if (Attributes == INVALID_FILE_ATTRIBUTES) + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(GetLastError()); + } + else + { + /* Return the attributes that DOS can understand */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setCX(Attributes & 0x00FF); + } + } + else if (getAL() == 0x01) + { + /* Try to set the attributes */ + if (SetFileAttributesA(FileName, getCL())) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(GetLastError()); + } + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_FUNCTION); + } + + break; + } + + /* IOCTL */ + case 0x44: + { + if (DosHandleIoctl(getAL(), getBX())) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(DosLastError); + } + + break; + } + + /* Duplicate Handle */ + case 0x45: + { + WORD NewHandle; + HANDLE Handle = DosGetRealHandle(getBX()); + + if (Handle != INVALID_HANDLE_VALUE) + { + /* The handle is invalid */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_HANDLE); + break; + } + + /* Open a new handle to the same entry */ + NewHandle = DosOpenHandle(Handle); + + if (NewHandle == INVALID_DOS_HANDLE) + { + /* Too many files open */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_TOO_MANY_OPEN_FILES); + break; + } + + /* Return the result */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(NewHandle); + break; + } + + /* Force Duplicate Handle */ + case 0x46: + { + if (DosDuplicateHandle(getBX(), getCX())) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_HANDLE); + } + + break; + } + + /* Get Current Directory */ + case 0x47: + { + BYTE DriveNumber = getDL(); + String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI()); + + /* Get the real drive number */ + if (DriveNumber == 0) + { + DriveNumber = CurrentDrive; + } + else + { + /* Decrement DriveNumber since it was 1-based */ + DriveNumber--; + } + + if (DriveNumber <= LastDrive - 'A') + { + /* + * Copy the current directory into the target buffer. + * It doesn't contain the drive letter and the backslash. + */ + strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH); + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_DRIVE); + } + + break; + } + + /* Allocate Memory */ + case 0x48: + { + WORD MaxAvailable = 0; + WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable); + + if (Segment != 0) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(Segment); + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(DosLastError); + setBX(MaxAvailable); + } + + break; + } + + /* Free Memory */ + case 0x49: + { + if (DosFreeMemory(getES())) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_ARENA_TRASHED); + } + + break; + } + + /* Resize Memory Block */ + case 0x4A: + { + WORD Size; + + if (DosResizeMemory(getES(), getBX(), &Size)) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(DosLastError); + setBX(Size); + } + + break; + } + + /* Execute */ + case 0x4B: + { + DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL(); + LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX()); + PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX()); + WORD ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock); + + if (ErrorCode == ERROR_SUCCESS) + { + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ErrorCode); + } + + break; + } + + /* Terminate With Return Code */ + case 0x4C: + { + DosTerminateProcess(CurrentPsp, getAL()); + break; + } + + /* Get Return Code (ERRORLEVEL) */ + case 0x4D: + { + /* + * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm + * DosErrorLevel is cleared after being read by this function. + */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(DosErrorLevel); + DosErrorLevel = 0x0000; // Clear it + break; + } + + /* Find First File */ + case 0x4E: + { + WORD Result = (WORD)demFileFindFirst(FAR_POINTER(DiskTransferArea), + SEG_OFF_TO_PTR(getDS(), getDX()), + getCX()); + + setAX(Result); + if (Result == ERROR_SUCCESS) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + + break; + } + + /* Find Next File */ + case 0x4F: + { + WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea)); + + setAX(Result); + if (Result == ERROR_SUCCESS) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + + break; + } + + /* Internal - Set Current Process ID (Set PSP Address) */ + case 0x50: + { + // FIXME: Is it really what it's done ?? + CurrentPsp = getBX(); + break; + } + + /* Internal - Get Current Process ID (Get PSP Address) */ + case 0x51: + /* Get Current PSP Address */ + case 0x62: + { + /* + * Undocumented AH=51h is identical to the documented AH=62h. + * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm + * and http://www.ctyme.com/intr/rb-3140.htm + * for more information. + */ + setBX(CurrentPsp); + break; + } + + /* Get/Set Memory Management Options */ + case 0x58: + { + if (getAL() == 0x00) + { + /* Get allocation strategy */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAX(DosAllocStrategy); + } + else if (getAL() == 0x01) + { + /* Set allocation strategy */ + + if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) + == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) + { + /* Can't set both */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_PARAMETER); + break; + } + + if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT) + { + /* Invalid allocation strategy */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_PARAMETER); + break; + } + + DosAllocStrategy = getBL(); + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else if (getAL() == 0x02) + { + /* Get UMB link state */ + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + setAL(DosUmbLinked ? 0x01 : 0x00); + } + else if (getAL() == 0x03) + { + /* Set UMB link state */ + if (getBX()) DosLinkUmb(); + else DosUnlinkUmb(); + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + } + else + { + /* Invalid or unsupported function */ + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + setAX(ERROR_INVALID_FUNCTION); + } + + break; + } + + /* Unsupported */ + default: + { + DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n", + getAH(), getAL()); + + setAL(0); // Some functions expect AL to be 0 when it's not supported. + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; + } + } +} + +VOID WINAPI DosBreakInterrupt(LPWORD Stack) +{ + UNREFERENCED_PARAMETER(Stack); + + /* Stop the VDM task */ + ResetEvent(VdmTaskEvent); + EmulatorUnsimulate(); +} + +VOID WINAPI DosFastConOut(LPWORD Stack) +{ + /* + * This is the DOS 2+ Fast Console Output Interrupt. + * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm + * for more information. + */ + +#if 0 + if (Stack[STACK_COUNTER] == 0) + { + Stack[STACK_COUNTER]++; + + /* Save AX and BX */ + Stack[STACK_VAR_A] = getAX(); + Stack[STACK_VAR_B] = getBX(); + + /* Rewind the BOP manually, we can't use CF because the interrupt could modify it */ + EmulatorExecute(getCS(), getIP() - 4); + + /* Call INT 0x10, AH = 0x0E */ + setAH(0x0E); + setBL(DOS_CHAR_ATTRIBUTE); + setBH(Bda->VideoPage); + + EmulatorInterrupt(0x10); + } + else + { + /* Restore AX and BX */ + setAX(Stack[STACK_VAR_A]); + setBX(Stack[STACK_VAR_B]); + } +#else + /* Save AX and BX */ + USHORT AX = getAX(); + USHORT BX = getBX(); + + /* Set the parameters (AL = character, already set) */ + setBL(DOS_CHAR_ATTRIBUTE); + setBH(Bda->VideoPage); + + /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ + setAH(0x0E); + Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT); + + /* Restore AX and BX */ + setBX(BX); + setAX(AX); +#endif +} + +VOID WINAPI DosInt2Fh(LPWORD Stack) +{ + DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n", + getAH(), getAL()); + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; +} + +BOOLEAN DosKRNLInitialize(VOID) +{ + +#if 1 + + UCHAR i; + CHAR CurrentDirectory[MAX_PATH]; + CHAR DosDirectory[DOS_DIR_LENGTH]; + LPSTR Path; + + FILE *Stream; + WCHAR Buffer[256]; + + /* Clear the current directory buffer */ + ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories)); + + /* Get the current directory */ + if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory)) + { + // TODO: Use some kind of default path? + return FALSE; + } + + /* Convert that to a DOS path */ + if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH)) + { + // TODO: Use some kind of default path? + return FALSE; + } + + /* Set the drive */ + CurrentDrive = DosDirectory[0] - 'A'; + + /* Get the directory part of the path */ + Path = strchr(DosDirectory, '\\'); + if (Path != NULL) + { + /* Skip the backslash */ + Path++; + } + + /* Set the directory */ + if (Path != NULL) + { + strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH); + } + + /* Read CONFIG.SYS */ + Stream = _wfopen(DOS_CONFIG_PATH, L"r"); + if (Stream != NULL) + { + while (fgetws(Buffer, 256, Stream)) + { + // TODO: Parse the line + } + fclose(Stream); + } + + /* Initialize the SFT */ + for (i = 0; i < DOS_SFT_SIZE; i++) + { + DosSystemFileTable[i] = INVALID_HANDLE_VALUE; + DosSftRefCount[i] = 0; + } + + /* Get handles to standard I/O devices */ + DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE); + DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE); + DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE); + + /* Initialize the reference counts */ + DosSftRefCount[0] = DosSftRefCount[1] = DosSftRefCount[2] = 1; + +#endif + + /* Initialize the callback context */ + InitializeContext(&DosContext, 0x0070, 0x0000); + + /* Register the DOS 32-bit Interrupts */ + RegisterDosInt32(0x20, DosInt20h ); + RegisterDosInt32(0x21, DosInt21h ); +// RegisterDosInt32(0x22, DosInt22h ); // Termination + RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break +// RegisterDosInt32(0x24, DosInt24h ); // Critical Error + RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output + RegisterDosInt32(0x2F, DosInt2Fh ); + + return TRUE; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h b/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h new file mode 100644 index 0000000000000..18e0b14cd070c --- /dev/null +++ b/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h @@ -0,0 +1,217 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dos.h + * PURPOSE: VDM DOS Kernel + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _DOS_H_ +#define _DOS_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/**/ #include "callback.h" /**/ + +/* DEFINES ********************************************************************/ + +// +// We are DOS 5.00 (reported by INT 21h, AH=30h) +// and DOS 5.50 (reported by INT 21h, AX=3306h) for Windows NT Compatibility +// +#define DOS_VERSION MAKEWORD(5, 00) +#define NTDOS_VERSION MAKEWORD(5, 50) + +#define DOS_CONFIG_PATH L"%SystemRoot%\\system32\\CONFIG.NT" +#define DOS_COMMAND_INTERPRETER L"%SystemRoot%\\system32\\COMMAND.COM /k %SystemRoot%\\system32\\AUTOEXEC.NT" +#define FIRST_MCB_SEGMENT 0x1000 +#define USER_MEMORY_SIZE 0x8FFE +#define SYSTEM_PSP 0x08 +#define SYSTEM_ENV_BLOCK 0x800 +#define INVALID_DOS_HANDLE 0xFFFF +#define DOS_INPUT_HANDLE 0 +#define DOS_OUTPUT_HANDLE 1 +#define DOS_ERROR_HANDLE 2 +#define DOS_SFT_SIZE 255 +#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0))) +#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0))) +#define UMB_START_SEGMENT 0xC000 +#define UMB_END_SEGMENT 0xDFFF +#define DOS_ALLOC_HIGH 0x40 +#define DOS_ALLOC_HIGH_LOW 0x80 +#define DOS_CMDLINE_LENGTH 127 +#define DOS_DIR_LENGTH 64 +#define NUM_DRIVES ('Z' - 'A' + 1) +#define DOS_CHAR_ATTRIBUTE 0x07 + +enum DOS_ALLOC_STRATEGY +{ + DOS_ALLOC_FIRST_FIT, + DOS_ALLOC_BEST_FIT, + DOS_ALLOC_LAST_FIT +}; + +typedef enum +{ + DOS_LOAD_AND_EXECUTE = 0x00, + DOS_LOAD_ONLY = 0x01, + DOS_LOAD_OVERLAY = 0x03 +} DOS_EXEC_TYPE; + +#pragma pack(push, 1) + +typedef struct _DOS_MCB +{ + CHAR BlockType; + WORD OwnerPsp; + WORD Size; + BYTE Unused[3]; + CHAR Name[8]; +} DOS_MCB, *PDOS_MCB; + +typedef struct _DOS_FCB +{ + BYTE DriveNumber; + CHAR FileName[8]; + CHAR FileExt[3]; + WORD BlockNumber; + WORD RecordSize; + DWORD FileSize; + WORD LastWriteDate; + WORD LastWriteTime; + BYTE Reserved[8]; + BYTE BlockRecord; + BYTE RecordNumber[3]; +} DOS_FCB, *PDOS_FCB; + +typedef struct _DOS_PSP +{ + BYTE Exit[2]; + WORD LastParagraph; + BYTE Reserved0[6]; + DWORD TerminateAddress; + DWORD BreakAddress; + DWORD CriticalAddress; + WORD ParentPsp; + BYTE HandleTable[20]; + WORD EnvBlock; + DWORD LastStack; + WORD HandleTableSize; + DWORD HandleTablePtr; + DWORD PreviousPsp; + DWORD Reserved1; + WORD DosVersion; + BYTE Reserved2[14]; + BYTE FarCall[3]; + BYTE Reserved3[9]; + DOS_FCB Fcb; + BYTE CommandLineSize; + CHAR CommandLine[DOS_CMDLINE_LENGTH]; +} DOS_PSP, *PDOS_PSP; + +typedef struct _DOS_INPUT_BUFFER +{ + BYTE MaxLength; + BYTE Length; + CHAR Buffer[ANYSIZE_ARRAY]; +} DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER; + +typedef struct _DOS_DRIVER_HEADER +{ + DWORD NextDriver; + WORD Attributes; + WORD StrategyEntry; + WORD InterruptEntry; + CHAR DeviceName[8]; +} DOS_DRIVER_HEADER, *PDOS_DRIVER_HEADER; + +typedef struct _DOS_FIND_FILE_BLOCK +{ + CHAR DriveLetter; + CHAR Pattern[11]; + UCHAR AttribMask; + DWORD Unused; + HANDLE SearchHandle; + + /* The following part of the structure is documented */ + UCHAR Attributes; + WORD FileTime; + WORD FileDate; + DWORD FileSize; + CHAR FileName[13]; +} DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK; + +typedef struct _DOS_EXEC_PARAM_BLOCK +{ + /* Input variables */ + WORD Environment; + DWORD CommandLine; + DWORD FirstFcb; + DWORD SecondFcb; + + /* Output variables */ + DWORD StackLocation; + DWORD EntryPoint; +} DOS_EXEC_PARAM_BLOCK, *PDOS_EXEC_PARAM_BLOCK; + +#pragma pack(pop) + +/* FUNCTIONS ******************************************************************/ + +extern CALLBACK16 DosContext; +#define RegisterDosInt32(IntNumber, IntHandler) \ +do { \ + DosContext.NextOffset += RegisterInt32(MAKELONG(DosContext.NextOffset, \ + DosContext.Segment), \ + (IntNumber), (IntHandler), NULL); \ +} while(0); + +/* + * DOS BIOS Functions + * See bios.c + */ +CHAR DosReadCharacter(VOID); +BOOLEAN DosCheckInput(VOID); +VOID DosPrintCharacter(CHAR Character); + +BOOLEAN DosBIOSInitialize(VOID); + + +/* + * DOS Kernel Functions + * See dos.c + */ +BOOL IsConsoleHandle(HANDLE hHandle); +HANDLE DosGetRealHandle(WORD DosHandle); +WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead); +WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten); + +VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment); +DWORD DosLoadExecutable( + IN DOS_EXEC_TYPE LoadType, + IN LPCSTR ExecutablePath, + IN LPCSTR CommandLine, + IN PVOID Environment, + OUT PDWORD StackLocation OPTIONAL, + OUT PDWORD EntryPoint OPTIONAL +); +WORD DosCreateProcess( + DOS_EXEC_TYPE LoadType, + LPCSTR ProgramName, + PDOS_EXEC_PARAM_BLOCK Parameters +); +VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode); +BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle); + +VOID WINAPI DosInt20h(LPWORD Stack); +VOID WINAPI DosInt21h(LPWORD Stack); +VOID WINAPI DosBreakInterrupt(LPWORD Stack); +VOID WINAPI DosInt2Fh(LPWORD Stack); + +BOOLEAN DosKRNLInitialize(VOID); + +#endif // _DOS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/emulator.c b/reactos/subsystems/ntvdm/emulator.c new file mode 100644 index 0000000000000..fe4bdfd4a133c --- /dev/null +++ b/reactos/subsystems/ntvdm/emulator.c @@ -0,0 +1,517 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: emulator.c + * PURPOSE: Minimal x86 machine emulator for the VDM + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "callback.h" + +#include "clock.h" +#include "bios/rom.h" +#include "hardware/cmos.h" +#include "hardware/pic.h" +#include "hardware/ps2.h" +#include "hardware/speaker.h" +#include "hardware/timer.h" +#include "hardware/vga.h" + +#include "bop.h" +#include "vddsup.h" +#include "io.h" + +#include + +/* PRIVATE VARIABLES **********************************************************/ + +FAST486_STATE EmulatorContext; +BOOLEAN CpuSimulate = FALSE; + +/* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */ +const static INT MaxCpuCallLevel = 32; +static INT CpuCallLevel = 0; + +LPVOID BaseAddress = NULL; +BOOLEAN VdmRunning = TRUE; + +static BOOLEAN A20Line = FALSE; +static BYTE Port61hState = 0x00; + +static HANDLE InputThread = NULL; + +LPCWSTR ExceptionName[] = +{ + L"Division By Zero", + L"Debug", + L"Unexpected Error", + L"Breakpoint", + L"Integer Overflow", + L"Bound Range Exceeded", + L"Invalid Opcode", + L"FPU Not Available" +}; + +/* BOP Identifiers */ +#define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app + +/* PRIVATE FUNCTIONS **********************************************************/ + +VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size) +{ + UNREFERENCED_PARAMETER(State); + + // BIG HACK!!!! To make BIOS images working correctly, + // until Aleksander rewrites memory management!! + if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000; + + /* If the A20 line is disabled, mask bit 20 */ + if (!A20Line) Address &= ~(1 << 20); + + /* Make sure the requested address is valid */ + if ((Address + Size) >= MAX_ADDRESS) return; + + /* + * Check if we are going to read the VGA memory and + * copy it into the virtual address space if needed. + */ + if (((Address + Size) >= VgaGetVideoBaseAddress()) + && (Address < VgaGetVideoLimitAddress())) + { + DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress()); + DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress()) + - VgaAddress + 1; + LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress); + + /* Read from the VGA memory */ + VgaReadMemory(VgaAddress, DestBuffer, ActualSize); + } + + /* Read the data from the virtual address space and store it in the buffer */ + RtlCopyMemory(Buffer, REAL_TO_PHYS(Address), Size); +} + +VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size) +{ + UNREFERENCED_PARAMETER(State); + + // BIG HACK!!!! To make BIOS images working correctly, + // until Aleksander rewrites memory management!! + if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000; + + /* If the A20 line is disabled, mask bit 20 */ + if (!A20Line) Address &= ~(1 << 20); + + /* Make sure the requested address is valid */ + if ((Address + Size) >= MAX_ADDRESS) return; + + /* Make sure we don't write to the ROM area */ + if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return; + + /* Read the data from the buffer and store it in the virtual address space */ + RtlCopyMemory(REAL_TO_PHYS(Address), Buffer, Size); + + /* + * Check if we modified the VGA memory. + */ + if (((Address + Size) >= VgaGetVideoBaseAddress()) + && (Address < VgaGetVideoLimitAddress())) + { + DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress()); + DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress()) + - VgaAddress + 1; + LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress); + + /* Write to the VGA memory */ + VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize); + } +} + +UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State) +{ + UNREFERENCED_PARAMETER(State); + + /* Get the interrupt number from the PIC */ + return PicGetInterrupt(); +} + +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 */ + EmulatorTerminate(); + 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) +{ + if (CpuCallLevel > MaxCpuCallLevel) + { + DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)", + CpuCallLevel, MaxCpuCallLevel); + + /* Stop the VDM */ + EmulatorTerminate(); + return; + } + CpuCallLevel++; + + CpuSimulate = TRUE; + while (VdmRunning && CpuSimulate) ClockUpdate(); + + CpuCallLevel--; + if (CpuCallLevel < 0) CpuCallLevel = 0; + + /* This takes into account for reentrance */ + CpuSimulate = TRUE; +} + +VOID EmulatorUnsimulate(VOID) +{ + /* Stop simulation */ + CpuSimulate = FALSE; +} + +VOID EmulatorTerminate(VOID) +{ + /* Stop the VDM */ + VdmRunning = FALSE; +} + +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; +} + +static VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack) +{ + DPRINT1("NTVDM: BOP_DEBUGGER\n"); + DebugBreak(); +} + +static VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack) +{ + EmulatorUnsimulate(); +} + +static BYTE WINAPI Port61hRead(ULONG Port) +{ + return Port61hState; +} + +static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data) +{ + // BOOLEAN SpeakerChange = FALSE; + BYTE OldPort61hState = Port61hState; + + /* Only the four lowest bytes can be written */ + Port61hState = (Port61hState & 0xF0) | (Data & 0x0F); + + if ((OldPort61hState ^ Port61hState) & 0x01) + { + DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off"); + // SpeakerChange = TRUE; + } + + PitSetGate(2, !!(Port61hState & 0x01)); + + if ((OldPort61hState ^ Port61hState) & 0x02) + { + /* There were some change for the speaker... */ + DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off"); + // SpeakerChange = TRUE; + } + // if (SpeakerChange) SpeakerChange(); + SpeakerChange(); +} + +static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State) +{ + if (State) + { + DPRINT("PicInterruptRequest\n"); + PicInterruptRequest(0); // Raise IRQ 0 + } + // else < Lower IRQ 0 > +} + +static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State) +{ +#if 0 + if (State) + { + /* Set bit 4 of Port 61h */ + Port61hState |= 1 << 4; + } + else + { + /* Clear bit 4 of Port 61h */ + Port61hState &= ~(1 << 4); + } +#else + Port61hState = (Port61hState & 0xEF) | (State << 4); +#endif +} + +static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State) +{ + // BYTE OldPort61hState = Port61hState; + +#if 0 + if (State) + { + /* Set bit 5 of Port 61h */ + Port61hState |= 1 << 5; + } + else + { + /* Clear bit 5 of Port 61h */ + Port61hState &= ~(1 << 5); + } +#else + Port61hState = (Port61hState & 0xDF) | (State << 5); +#endif + DPRINT("Speaker PIT out\n"); + // if ((OldPort61hState ^ Port61hState) & 0x20) + // SpeakerChange(); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +DWORD WINAPI PumpConsoleInput(LPVOID Parameter); + +BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) +{ + /* Allocate memory for the 16-bit address space */ + BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS); + if (BaseAddress == NULL) + { + wprintf(L"FATAL: Failed to allocate VDM memory.\n"); + return FALSE; + } + + /* 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, + EmulatorWriteMemory, + EmulatorReadIo, + EmulatorWriteIo, + NULL, + EmulatorBiosOperation, + EmulatorIntAcknowledge, + NULL /* TODO: Use a TLB */); + + /* Initialize DMA */ + + /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */ + PicInitialize(); + PitInitialize(); + CmosInitialize(); + SpeakerInitialize(); + + /* Set output functions */ + PitSetOutFunction(0, NULL, PitChan0Out); + PitSetOutFunction(1, NULL, PitChan1Out); + PitSetOutFunction(2, NULL, PitChan2Out); + + /* Register the I/O Ports */ + RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite); + + /* Set the console input mode */ + // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions + // upon console window events (screen buffer resize, ...). + SetConsoleMode(ConsoleInput, ENABLE_PROCESSED_INPUT /* | ENABLE_WINDOW_INPUT */); + // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); + + /* Initialize the PS2 port */ + PS2Initialize(ConsoleInput); + + /* 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); + + /* Initialize the software callback system and register the emulator BOPs */ + InitializeCallbacks(); + RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop); + RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop); + + /* Initialize VDD support */ + VDDSupInitialize(); + + return TRUE; +} + +VOID EmulatorCleanup(VOID) +{ + VgaCleanup(); + + /* Close the input thread handle */ + if (InputThread != NULL) CloseHandle(InputThread); + InputThread = NULL; + + PS2Cleanup(); + + SpeakerCleanup(); + CmosCleanup(); + // PitCleanup(); + // PicCleanup(); + + // Fast486Cleanup(); + + /* Free the memory allocated for the 16-bit address space */ + if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress); +} + + + +VOID +WINAPI +VDDSimulate16(VOID) +{ + EmulatorSimulate(); +} + +VOID +WINAPI +VDDTerminateVDM(VOID) +{ + /* Stop the VDM */ + EmulatorTerminate(); +} + +PBYTE +WINAPI +Sim32pGetVDMPointer(IN ULONG Address, + IN BOOLEAN ProtectedMode) +{ + // FIXME + UNREFERENCED_PARAMETER(ProtectedMode); + + /* + * HIWORD(Address) == Segment (if ProtectedMode == FALSE) + * or Selector (if ProtectedMode == TRUE ) + * LOWORD(Address) == Offset + */ + return (PBYTE)FAR_POINTER(Address); +} + +PBYTE +WINAPI +MGetVdmPointer(IN ULONG Address, + IN ULONG Size, + IN BOOLEAN ProtectedMode) +{ + UNREFERENCED_PARAMETER(Size); + return Sim32pGetVDMPointer(Address, ProtectedMode); +} + +PVOID +WINAPI +VdmMapFlat(IN USHORT Segment, + IN ULONG Offset, + IN VDM_MODE Mode) +{ + // FIXME + UNREFERENCED_PARAMETER(Mode); + + return SEG_OFF_TO_PTR(Segment, Offset); +} + +BOOL +WINAPI +VdmFlushCache(IN USHORT Segment, + IN ULONG Offset, + IN ULONG Size, + IN VDM_MODE Mode) +{ + // FIXME + UNIMPLEMENTED; + return TRUE; +} + +BOOL +WINAPI +VdmUnmapFlat(IN USHORT Segment, + IN ULONG Offset, + IN PVOID Buffer, + IN VDM_MODE Mode) +{ + // FIXME + UNIMPLEMENTED; + return TRUE; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/emulator.h b/reactos/subsystems/ntvdm/emulator.h new file mode 100644 index 0000000000000..20e9a07a98c7b --- /dev/null +++ b/reactos/subsystems/ntvdm/emulator.h @@ -0,0 +1,137 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: emulator.h + * PURPOSE: Minimal x86 machine emulator for the VDM + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _EMULATOR_H_ +#define _EMULATOR_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" +#include + +/* DEFINES ********************************************************************/ + +/* FLAGS */ +#define EMULATOR_FLAG_CF (1 << 0) +#define EMULATOR_FLAG_PF (1 << 2) +#define EMULATOR_FLAG_AF (1 << 4) +#define EMULATOR_FLAG_ZF (1 << 6) +#define EMULATOR_FLAG_SF (1 << 7) +#define EMULATOR_FLAG_TF (1 << 8) +#define EMULATOR_FLAG_IF (1 << 9) +#define EMULATOR_FLAG_DF (1 << 10) +#define EMULATOR_FLAG_OF (1 << 11) +#define EMULATOR_FLAG_NT (1 << 14) +#define EMULATOR_FLAG_RF (1 << 16) +#define EMULATOR_FLAG_VM (1 << 17) +#define EMULATOR_FLAG_AC (1 << 18) +#define EMULATOR_FLAG_VIF (1 << 19) +#define EMULATOR_FLAG_VIP (1 << 20) +#define EMULATOR_FLAG_ID (1 << 21) + +#define STACK_VAR_B 0 +#define STACK_VAR_A 1 +#define STACK_COUNTER 2 +#define STACK_INT_NUM 3 +#define STACK_IP 4 +#define STACK_CS 5 +#define STACK_FLAGS 6 + + +/* Basic Memory Management */ +#define MEM_ALIGN_UP(ptr, align) MEM_ALIGN_DOWN((ULONG_PTR)(ptr) + (align) - 1l, (align)) +#define MEM_ALIGN_DOWN(ptr, align) (PVOID)((ULONG_PTR)(ptr) & ~((align) - 1l)) + +#define TO_LINEAR(seg, off) (((seg) << 4) + (off)) +#define MAX_SEGMENT 0xFFFF +#define MAX_OFFSET 0xFFFF +#define MAX_ADDRESS 0x1000000 // 16 MB of RAM + +#define FAR_POINTER(x) \ + (PVOID)((ULONG_PTR)BaseAddress + TO_LINEAR(HIWORD(x), LOWORD(x))) + +#define SEG_OFF_TO_PTR(seg, off) \ + (PVOID)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), (off))) + +#define REAL_TO_PHYS(ptr) (PVOID)((ULONG_PTR)(ptr) + (ULONG_PTR)BaseAddress) +#define PHYS_TO_REAL(ptr) (PVOID)((ULONG_PTR)(ptr) - (ULONG_PTR)BaseAddress) + + +/* BCD-Binary conversion */ +#define BINARY_TO_BCD(x) ((((x) / 1000) << 12) + (((x) / 100) << 8) + (((x) / 10) << 4) + ((x) % 10)) +#define BCD_TO_BINARY(x) (((x) >> 12) * 1000 + ((x) >> 8) * 100 + ((x) >> 4) * 10 + ((x) & 0x0F)) + + +/* System I/O ports */ +#define CONTROL_SYSTEM_PORT61H 0x61 + + +enum +{ + EMULATOR_EXCEPTION_DIVISION_BY_ZERO, + EMULATOR_EXCEPTION_DEBUG, + EMULATOR_EXCEPTION_NMI, + EMULATOR_EXCEPTION_BREAKPOINT, + EMULATOR_EXCEPTION_OVERFLOW, + EMULATOR_EXCEPTION_BOUND, + EMULATOR_EXCEPTION_INVALID_OPCODE, + EMULATOR_EXCEPTION_NO_FPU, + EMULATOR_EXCEPTION_DOUBLE_FAULT, + EMULATOR_EXCEPTION_FPU_SEGMENT, + EMULATOR_EXCEPTION_INVALID_TSS, + EMULATOR_EXCEPTION_NO_SEGMENT, + EMULATOR_EXCEPTION_STACK_SEGMENT, + EMULATOR_EXCEPTION_GPF, + EMULATOR_EXCEPTION_PAGE_FAULT +}; + +extern FAST486_STATE EmulatorContext; +extern LPVOID BaseAddress; +extern BOOLEAN VdmRunning; + +/* FUNCTIONS ******************************************************************/ + +VOID WINAPI EmulatorReadMemory +( + PFAST486_STATE State, + ULONG Address, + PVOID Buffer, + ULONG Size +); + +VOID WINAPI EmulatorWriteMemory +( + PFAST486_STATE State, + ULONG Address, + PVOID Buffer, + ULONG Size +); + +UCHAR WINAPI EmulatorIntAcknowledge +( + PFAST486_STATE State +); + +VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack); + +VOID EmulatorExecute(WORD Segment, WORD Offset); +VOID EmulatorStep(VOID); +VOID EmulatorSimulate(VOID); +VOID EmulatorUnsimulate(VOID); +VOID EmulatorTerminate(VOID); + +VOID EmulatorInterrupt(BYTE Number); +VOID EmulatorInterruptSignal(VOID); +VOID EmulatorSetA20(BOOLEAN Enabled); + +BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput); +VOID EmulatorCleanup(VOID); + +#endif // _EMULATOR_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/cmos.c b/reactos/subsystems/ntvdm/hardware/cmos.c new file mode 100644 index 0000000000000..4f86467249b8a --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/cmos.c @@ -0,0 +1,478 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: cmos.c + * PURPOSE: CMOS Real Time Clock emulation + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "cmos.h" + +#include "io.h" +#include "pic.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static HANDLE hCmosRam = INVALID_HANDLE_VALUE; +static CMOS_MEMORY CmosMemory; + +static BOOLEAN NmiEnabled = TRUE; +static CMOS_REGISTERS SelectedRegister = CMOS_REG_STATUS_D; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN IsNmiEnabled(VOID) +{ + return NmiEnabled; +} + +VOID CmosWriteAddress(BYTE Value) +{ + /* Update the NMI enabled flag */ + NmiEnabled = !(Value & CMOS_DISABLE_NMI); + + /* Get the register number */ + Value &= ~CMOS_DISABLE_NMI; + + if (Value < CMOS_REG_MAX) + { + /* Select the new register */ + SelectedRegister = Value; + } + else + { + /* Default to Status Register D */ + SelectedRegister = CMOS_REG_STATUS_D; + } +} + +BYTE CmosReadData(VOID) +{ + SYSTEMTIME CurrentTime; + + /* Get the current time */ + GetLocalTime(&CurrentTime); + + switch (SelectedRegister) + { + case CMOS_REG_SECONDS: + return READ_CMOS_DATA(CmosMemory, CurrentTime.wSecond); + + case CMOS_REG_ALARM_SEC: + return READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmSecond); + + case CMOS_REG_MINUTES: + return READ_CMOS_DATA(CmosMemory, CurrentTime.wMinute); + + case CMOS_REG_ALARM_MIN: + return READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmMinute); + + case CMOS_REG_HOURS: + { + BOOLEAN Afternoon = FALSE; + BYTE Value = CurrentTime.wHour; + + if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12)) + { + Value -= 12; + Afternoon = TRUE; + } + + Value = READ_CMOS_DATA(CmosMemory, Value); + + /* Convert to 12-hour */ + if (Afternoon) Value |= 0x80; + + return Value; + } + + case CMOS_REG_ALARM_HRS: + { + BOOLEAN Afternoon = FALSE; + BYTE Value = CmosMemory.AlarmHour; + + if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12)) + { + Value -= 12; + Afternoon = TRUE; + } + + Value = READ_CMOS_DATA(CmosMemory, Value); + + /* Convert to 12-hour */ + if (Afternoon) Value |= 0x80; + + return Value; + } + + case CMOS_REG_DAY_OF_WEEK: + /* + * The CMOS value is 1-based but the + * GetLocalTime API value is 0-based. + * Correct it. + */ + return READ_CMOS_DATA(CmosMemory, CurrentTime.wDayOfWeek + 1); + + case CMOS_REG_DAY: + return READ_CMOS_DATA(CmosMemory, CurrentTime.wDay); + + case CMOS_REG_MONTH: + return READ_CMOS_DATA(CmosMemory, CurrentTime.wMonth); + + case CMOS_REG_YEAR: + return READ_CMOS_DATA(CmosMemory, CurrentTime.wYear % 100); + + case CMOS_REG_STATUS_C: + { + BYTE Value = CmosMemory.StatusRegC; + + /* Clear status register C */ + CmosMemory.StatusRegC = 0x00; + + /* Return the old value */ + return Value; + } + + case CMOS_REG_STATUS_A: + case CMOS_REG_STATUS_B: + case CMOS_REG_STATUS_D: + case CMOS_REG_DIAGNOSTICS: + case CMOS_REG_SHUTDOWN_STATUS: + default: + { + // ASSERT(SelectedRegister < CMOS_REG_MAX); + return CmosMemory.Regs[SelectedRegister]; + } + } + + /* Return to Status Register D */ + SelectedRegister = CMOS_REG_STATUS_D; +} + +VOID CmosWriteData(BYTE Value) +{ + BOOLEAN ChangeTime = FALSE; + SYSTEMTIME CurrentTime; + + /* Get the current time */ + GetLocalTime(&CurrentTime); + + switch (SelectedRegister) + { + case CMOS_REG_SECONDS: + { + ChangeTime = TRUE; + CurrentTime.wSecond = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_ALARM_SEC: + { + CmosMemory.AlarmSecond = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_MINUTES: + { + ChangeTime = TRUE; + CurrentTime.wMinute = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_ALARM_MIN: + { + CmosMemory.AlarmMinute = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_HOURS: + { + BOOLEAN Afternoon = FALSE; + + ChangeTime = TRUE; + + if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80)) + { + Value &= ~0x80; + Afternoon = TRUE; + } + + CurrentTime.wHour = WRITE_CMOS_DATA(CmosMemory, Value); + + /* Convert to 24-hour format */ + if (Afternoon) CurrentTime.wHour += 12; + + break; + } + + case CMOS_REG_ALARM_HRS: + { + BOOLEAN Afternoon = FALSE; + + if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80)) + { + Value &= ~0x80; + Afternoon = TRUE; + } + + CmosMemory.AlarmHour = WRITE_CMOS_DATA(CmosMemory, Value); + + /* Convert to 24-hour format */ + if (Afternoon) CmosMemory.AlarmHour += 12; + + break; + } + + case CMOS_REG_DAY_OF_WEEK: + { + ChangeTime = TRUE; + /* + * The CMOS value is 1-based but the + * SetLocalTime API value is 0-based. + * Correct it. + */ + Value -= 1; + CurrentTime.wDayOfWeek = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_DAY: + { + ChangeTime = TRUE; + CurrentTime.wDay = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_MONTH: + { + ChangeTime = TRUE; + CurrentTime.wMonth = WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_YEAR: + { + ChangeTime = TRUE; + + /* Clear everything except the century */ + CurrentTime.wYear = (CurrentTime.wYear / 100) * 100; + + CurrentTime.wYear += WRITE_CMOS_DATA(CmosMemory, Value); + break; + } + + case CMOS_REG_STATUS_A: + { + CmosMemory.StatusRegA = Value & 0x7F; // Bit 7 is read-only + break; + } + + case CMOS_REG_STATUS_B: + { + CmosMemory.StatusRegB = Value; + break; + } + + case CMOS_REG_STATUS_C: + case CMOS_REG_STATUS_D: + // Status registers C and D are read-only + break; + + /* Is the following correct? */ + case CMOS_REG_EXT_MEMORY_LOW: + case CMOS_REG_ACTUAL_EXT_MEMORY_LOW: + { + /* Sync EMS and UMS */ + CmosMemory.Regs[CMOS_REG_EXT_MEMORY_LOW] = + CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_LOW] = Value; + break; + } + + /* Is the following correct? */ + case CMOS_REG_EXT_MEMORY_HIGH: + case CMOS_REG_ACTUAL_EXT_MEMORY_HIGH: + { + /* Sync EMS and UMS */ + CmosMemory.Regs[CMOS_REG_EXT_MEMORY_HIGH] = + CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_HIGH] = Value; + break; + } + + default: + { + CmosMemory.Regs[SelectedRegister] = Value; + } + } + + if (ChangeTime) SetLocalTime(&CurrentTime); + + /* Return to Status Register D */ + SelectedRegister = CMOS_REG_STATUS_D; +} + +BYTE WINAPI CmosReadPort(ULONG Port) +{ + ASSERT(Port == CMOS_DATA_PORT); + return CmosReadData(); +} + +VOID WINAPI CmosWritePort(ULONG Port, BYTE Data) +{ + if (Port == CMOS_ADDRESS_PORT) + CmosWriteAddress(Data); + else if (Port == CMOS_DATA_PORT) + CmosWriteData(Data); +} + +DWORD RtcGetTicksPerSecond(VOID) +{ + BYTE RateSelect = CmosMemory.StatusRegB & 0x0F; + + if (RateSelect == 0) + { + /* No periodic interrupt */ + return 0; + } + + /* 1 and 2 act like 8 and 9 */ + if (RateSelect <= 2) RateSelect += 7; + + return 1 << (16 - RateSelect); +} + +VOID RtcPeriodicTick(VOID) +{ + /* Set PF */ + CmosMemory.StatusRegC |= CMOS_STC_PF; + + /* Check if there should be an interrupt on a periodic timer tick */ + if (CmosMemory.StatusRegB & CMOS_STB_INT_PERIODIC) + { + CmosMemory.StatusRegC |= CMOS_STC_IRQF; + + /* Interrupt! */ + PicInterruptRequest(RTC_IRQ_NUMBER); + } +} + +/* Should be called every second */ +VOID RtcTimeUpdate(VOID) +{ + SYSTEMTIME CurrentTime; + + /* Get the current time */ + GetLocalTime(&CurrentTime); + + /* Set UF */ + CmosMemory.StatusRegC |= CMOS_STC_UF; + + /* Check if the time matches the alarm time */ + if ((CurrentTime.wHour == CmosMemory.AlarmHour ) && + (CurrentTime.wMinute == CmosMemory.AlarmMinute) && + (CurrentTime.wSecond == CmosMemory.AlarmSecond)) + { + /* Set the alarm flag */ + CmosMemory.StatusRegC |= CMOS_STC_AF; + + /* Set IRQF if there should be an interrupt */ + if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_ALARM) CmosMemory.StatusRegC |= CMOS_STC_IRQF; + } + + /* Check if there should be an interrupt on update */ + if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_UPDATE) CmosMemory.StatusRegC |= CMOS_STC_IRQF; + + if (CmosMemory.StatusRegC & CMOS_STC_IRQF) + { + /* Interrupt! */ + PicInterruptRequest(RTC_IRQ_NUMBER); + } +} + +VOID CmosInitialize(VOID) +{ + DWORD CmosSize = sizeof(CmosMemory); + + /* File must not be opened before */ + ASSERT(hCmosRam == INVALID_HANDLE_VALUE); + + /* Clear the CMOS memory */ + ZeroMemory(&CmosMemory, sizeof(CmosMemory)); + + /* Always open (and if needed, create) a RAM file with shared access */ + SetLastError(0); // For debugging purposes + hCmosRam = CreateFileW(L"cmos.ram", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + DPRINT1("CMOS opening %s ; GetLastError() = %u\n", hCmosRam != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError()); + + if (hCmosRam != INVALID_HANDLE_VALUE) + { + BOOL Success; + + /* Attempt to fill the CMOS memory with the RAM file */ + SetLastError(0); // For debugging purposes + Success = ReadFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL); + if (CmosSize != sizeof(CmosMemory)) + { + /* Bad CMOS Ram file. Reinitialize the CMOS memory. */ + DPRINT1("Invalid CMOS file, read bytes %u, expected bytes %u\n", CmosSize, sizeof(CmosMemory)); + ZeroMemory(&CmosMemory, sizeof(CmosMemory)); + } + DPRINT1("CMOS loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + SetFilePointer(hCmosRam, 0, NULL, FILE_BEGIN); + } + + /* Overwrite some registers with default values */ + CmosMemory.StatusRegA = CMOS_DEFAULT_STA; + CmosMemory.StatusRegB = CMOS_DEFAULT_STB; + CmosMemory.StatusRegC = 0x00; + CmosMemory.StatusRegD = CMOS_BATTERY_OK; // Our CMOS battery works perfectly forever. + CmosMemory.Diagnostics = 0x00; // Diagnostics must not find any errors. + CmosMemory.ShutdownStatus = 0x00; + + /* Memory settings */ + + /* + * Conventional memory size is 640 kB, + * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM + * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm + * for more information. + */ + CmosMemory.Regs[CMOS_REG_BASE_MEMORY_LOW ] = LOBYTE(0x0280); + CmosMemory.Regs[CMOS_REG_BASE_MEMORY_HIGH] = HIBYTE(0x0280); + + CmosMemory.Regs[CMOS_REG_EXT_MEMORY_LOW] = + CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_LOW] = LOBYTE((MAX_ADDRESS - 0x100000) / 1024); + + CmosMemory.Regs[CMOS_REG_EXT_MEMORY_HIGH] = + CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_HIGH] = HIBYTE((MAX_ADDRESS - 0x100000) / 1024); + + /* Register the I/O Ports */ + RegisterIoPort(CMOS_ADDRESS_PORT, NULL , CmosWritePort); + RegisterIoPort(CMOS_DATA_PORT , CmosReadPort, CmosWritePort); +} + +VOID CmosCleanup(VOID) +{ + DWORD CmosSize = sizeof(CmosMemory); + + if (hCmosRam == INVALID_HANDLE_VALUE) return; + + /* Flush the CMOS memory back to the RAM file and close it */ + SetFilePointer(hCmosRam, 0, NULL, FILE_BEGIN); + WriteFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL); + + CloseHandle(hCmosRam); + hCmosRam = INVALID_HANDLE_VALUE; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/cmos.h b/reactos/subsystems/ntvdm/hardware/cmos.h new file mode 100644 index 0000000000000..f84d27ccc28b3 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/cmos.h @@ -0,0 +1,139 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: cmos.h + * PURPOSE: CMOS Real Time Clock emulation + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _CMOS_H_ +#define _CMOS_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define RTC_IRQ_NUMBER 8 +#define CMOS_ADDRESS_PORT 0x70 +#define CMOS_DATA_PORT 0x71 +#define CMOS_DISABLE_NMI (1 << 7) +#define CMOS_BATTERY_OK 0x80 + +/* Status Register B flags */ +#define CMOS_STB_24HOUR (1 << 1) +#define CMOS_STB_BINARY (1 << 2) +#define CMOS_STB_SQUARE_WAVE (1 << 3) +#define CMOS_STB_INT_ON_UPDATE (1 << 4) +#define CMOS_STB_INT_ON_ALARM (1 << 5) +#define CMOS_STB_INT_PERIODIC (1 << 6) + +/* Status Register C flags */ +#define CMOS_STC_UF CMOS_STB_INT_ON_UPDATE +#define CMOS_STC_AF CMOS_STB_INT_ON_ALARM +#define CMOS_STC_PF CMOS_STB_INT_PERIODIC +#define CMOS_STC_IRQF (1 << 7) + +/* Default register values */ +#define CMOS_DEFAULT_STA 0x26 +#define CMOS_DEFAULT_STB CMOS_STB_24HOUR + +#define WRITE_CMOS_DATA(Cmos, Value) \ + ((Cmos).StatusRegB & CMOS_STB_BINARY) ? (Value) : BCD_TO_BINARY(Value) + +#define READ_CMOS_DATA(Cmos, Value) \ + ((Cmos).StatusRegB & CMOS_STB_BINARY) ? (Value) : BINARY_TO_BCD(Value) + +typedef enum _CMOS_REGISTERS +{ + CMOS_REG_SECONDS, + CMOS_REG_ALARM_SEC, + CMOS_REG_MINUTES, + CMOS_REG_ALARM_MIN, + CMOS_REG_HOURS, + CMOS_REG_ALARM_HRS, + CMOS_REG_DAY_OF_WEEK, + CMOS_REG_DAY, + CMOS_REG_MONTH, + CMOS_REG_YEAR, + CMOS_REG_STATUS_A, + CMOS_REG_STATUS_B, + CMOS_REG_STATUS_C, + CMOS_REG_STATUS_D, + CMOS_REG_DIAGNOSTICS, + CMOS_REG_SHUTDOWN_STATUS, + CMOS_REG_BASE_MEMORY_LOW = 0x15, + CMOS_REG_BASE_MEMORY_HIGH = 0x16, + CMOS_REG_EXT_MEMORY_LOW = 0x17, + CMOS_REG_EXT_MEMORY_HIGH = 0x18, + CMOS_REG_ACTUAL_EXT_MEMORY_LOW = 0x30, + CMOS_REG_ACTUAL_EXT_MEMORY_HIGH = 0x31, + CMOS_REG_MAX = 0x40 +} CMOS_REGISTERS, *PCMOS_REGISTERS; + +/* + * CMOS Memory Map + * + * See the following documentation for more information: + * http://www.intel-assembler.it/portale/5/cmos-memory-map-123/cmos-memory-map-123.asp + * http://wiki.osdev.org/CMOS + * http://www.walshcomptech.com/ohlandl/config/cmos_registers.html + * http://www.fysnet.net/cmosinfo.htm + * http://www.bioscentral.com/misc/cmosmap.htm + */ +#pragma pack(push, 1) +typedef struct +{ + BYTE Second; // 0x00 + BYTE AlarmSecond; // 0x01 + BYTE Minute; // 0x02 + BYTE AlarmMinute; // 0x03 + BYTE Hour; // 0x04 + BYTE AlarmHour; // 0x05 + BYTE DayOfWeek; // 0x06 + BYTE Day; // 0x07 + BYTE Month; // 0x08 + BYTE Year; // 0x09 + + BYTE StatusRegA; // 0x0a + BYTE StatusRegB; // 0x0b +} CMOS_CLOCK, *PCMOS_CLOCK; + +typedef struct +{ + union + { + struct + { + CMOS_CLOCK; // 0x00 - 0x0b + BYTE StatusRegC; // 0x0c + BYTE StatusRegD; // 0x0d + BYTE Diagnostics; // 0x0e + BYTE ShutdownStatus; // 0x0f + }; + BYTE Regs1[0x10]; // 0x00 - 0x0f + BYTE Regs [0x40]; // 0x00 - 0x3f + }; + + /* + * Extended information 0x40 - 0x7f + */ +} CMOS_MEMORY, *PCMOS_MEMORY; +#pragma pack(pop) + +C_ASSERT(sizeof(CMOS_MEMORY) == 0x40); + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN IsNmiEnabled(VOID); +DWORD RtcGetTicksPerSecond(VOID); +VOID RtcPeriodicTick(VOID); +VOID RtcTimeUpdate(VOID); + +VOID CmosInitialize(VOID); +VOID CmosCleanup(VOID); + +#endif // _CMOS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/pic.c b/reactos/subsystems/ntvdm/hardware/pic.c new file mode 100644 index 0000000000000..aa40821f4493a --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/pic.c @@ -0,0 +1,342 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: pic.c + * PURPOSE: Programmable Interrupt Controller emulation + * (Interrupt Controller Adapter (ICA) in Windows terminology) + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "io.h" +#include "pic.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static PIC MasterPic, SlavePic; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static BYTE PicReadCommand(BYTE Port) +{ + PPIC Pic; + + /* Which PIC are we accessing? */ + if (Port == PIC_MASTER_CMD) Pic = &MasterPic; + else Pic = &SlavePic; + + if (Pic->ReadIsr) + { + /* Read the in-service register */ + Pic->ReadIsr = FALSE; + return Pic->InServiceRegister; + } + else + { + /* Read the interrupt request register */ + return Pic->IntRequestRegister; + } +} + +static VOID PicWriteCommand(BYTE Port, BYTE Value) +{ + PPIC Pic; + + /* Which PIC are we accessing? */ + if (Port == PIC_MASTER_CMD) Pic = &MasterPic; + else Pic = &SlavePic; + + if (Value & PIC_ICW1) + { + /* Start initialization */ + Pic->Initialization = TRUE; + Pic->IntOffset = 0xFF; + Pic->CascadeRegisterSet = FALSE; + Pic->ConfigRegister = Value; + return; + } + + if (Value & PIC_OCW3) + { + /* This is an OCR3 */ + if (Value == PIC_OCW3_READ_ISR) + { + /* Return the ISR on next read from command port */ + Pic->ReadIsr = TRUE; + } + + return; + } + + /* This is an OCW2 */ + if (Value & PIC_OCW2_EOI) + { + if (Value & PIC_OCW2_SL) + { + /* If the SL bit is set, clear a specific IRQ */ + Pic->InServiceRegister &= ~(1 << (Value & PIC_OCW2_NUM_MASK)); + } + else + { + /* Otherwise, clear all of them */ + Pic->InServiceRegister = 0; + } + } +} + +static BYTE PicReadData(BYTE Port) +{ + /* Read the mask register */ + if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister; + else return SlavePic.MaskRegister; +} + +static VOID PicWriteData(BYTE Port, BYTE Value) +{ + PPIC Pic; + + /* Which PIC are we accessing? */ + if (Port == PIC_MASTER_DATA) Pic = &MasterPic; + else Pic = &SlavePic; + + /* Is the PIC ready? */ + if (!Pic->Initialization) + { + /* Yes, this is an OCW1 */ + Pic->MaskRegister = Value; + return; + } + + /* Has the interrupt offset been set? */ + if (Pic->IntOffset == 0xFF) + { + /* This is an ICW2, set the offset (last three bits always zero) */ + Pic->IntOffset = Value & 0xF8; + + /* Check if we are in single mode and don't need an ICW4 */ + if ((Pic->ConfigRegister & PIC_ICW1_SINGLE) + && !(Pic->ConfigRegister & PIC_ICW1_ICW4)) + { + /* Yes, done initializing */ + Pic->Initialization = FALSE; + } + return; + } + + /* Check if we are in cascade mode and the cascade register was not set */ + if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet) + { + /* This is an ICW3 */ + Pic->CascadeRegister = Value; + Pic->CascadeRegisterSet = TRUE; + + /* Check if we need an ICW4 */ + if (!(Pic->ConfigRegister & PIC_ICW1_ICW4)) + { + /* No, done initializing */ + Pic->Initialization = FALSE; + } + return; + } + + /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */ + if (Value & PIC_ICW4_AEOI) + { + /* Use automatic end-of-interrupt */ + Pic->AutoEoi = TRUE; + } + + /* Done initializing */ + Pic->Initialization = FALSE; +} + +static BYTE WINAPI PicReadPort(ULONG Port) +{ + switch (Port) + { + case PIC_MASTER_CMD: + case PIC_SLAVE_CMD: + { + return PicReadCommand(Port); + } + + case PIC_MASTER_DATA: + case PIC_SLAVE_DATA: + { + return PicReadData(Port); + } + } + + return 0; +} + +static VOID WINAPI PicWritePort(ULONG Port, BYTE Data) +{ + switch (Port) + { + case PIC_MASTER_CMD: + case PIC_SLAVE_CMD: + { + PicWriteCommand(Port, Data); + break; + } + + case PIC_MASTER_DATA: + case PIC_SLAVE_DATA: + { + PicWriteData(Port, Data); + break; + } + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID PicInterruptRequest(BYTE Number) +{ + BYTE i; + + if (/* Number >= 0 && */ Number < 8) + { + /* Check if any of the higher-priority interrupts are busy */ + for (i = 0; i <= Number; i++) + { + if (MasterPic.InServiceRegister & (1 << Number)) return; + } + + /* Check if the interrupt is masked */ + if (MasterPic.MaskRegister & (1 << Number)) return; + + /* Set the appropriate bit in the IRR and interrupt the CPU */ + MasterPic.IntRequestRegister |= 1 << Number; + EmulatorInterruptSignal(); + } + else if (Number >= 8 && Number < 16) + { + Number -= 8; + + /* + * The slave PIC is connected to IRQ 2, always! If the master PIC + * was misconfigured, don't do anything. + */ + if (!(MasterPic.CascadeRegister & (1 << 2)) + || SlavePic.CascadeRegister != 2) + { + return; + } + + /* Check if any of the higher-priority interrupts are busy */ + if (MasterPic.InServiceRegister != 0) return; + for (i = 0; i <= Number; i++) + { + if (SlavePic.InServiceRegister & (1 << Number)) return; + } + + /* Check if the interrupt is masked */ + if (SlavePic.MaskRegister & (1 << Number)) return; + + /* Set the IRQ 2 bit in the master ISR */ + if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << 2); + + /* Set the appropriate bit in the IRR and interrupt the CPU */ + SlavePic.IntRequestRegister |= 1 << Number; + EmulatorInterruptSignal(); + } +} + +BYTE PicGetInterrupt(VOID) +{ + INT i, j; + + /* Search interrupts by priority */ + for (i = 0; i < 8; i++) + { + /* Check if this line is cascaded to the slave PIC */ + if ((i == 2) + && MasterPic.CascadeRegister & (1 << 2) + && SlavePic.Slave + && (SlavePic.CascadeRegister == 2)) + { + /* Search the slave PIC interrupts by priority */ + for (j = 0; j < 8; j++) if ((j != 1) && SlavePic.IntRequestRegister & (1 << j)) + { + /* Clear the IRR flag */ + SlavePic.IntRequestRegister &= ~(1 << j); + + /* Set the ISR flag, unless AEOI is enabled */ + if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << j); + + /* Return the interrupt number */ + return SlavePic.IntOffset + j; + } + } + + if (MasterPic.IntRequestRegister & (1 << i)) + { + /* Clear the IRR flag */ + MasterPic.IntRequestRegister &= ~(1 << i); + + /* Set the ISR flag, unless AEOI is enabled */ + if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i); + + /* Return the interrupt number */ + return MasterPic.IntOffset + i; + } + } + + /* Spurious interrupt */ + if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7; + else return MasterPic.IntOffset + 7; +} + +VOID PicInitialize(VOID) +{ + /* Register the I/O Ports */ + RegisterIoPort(PIC_MASTER_CMD , PicReadPort, PicWritePort); + RegisterIoPort(PIC_SLAVE_CMD , PicReadPort, PicWritePort); + RegisterIoPort(PIC_MASTER_DATA, PicReadPort, PicWritePort); + RegisterIoPort(PIC_SLAVE_DATA , PicReadPort, PicWritePort); +} + + + +VOID +WINAPI +call_ica_hw_interrupt(INT ms, + BYTE line, + INT count) +{ + BYTE InterruptNumber = line; + + /* Check for PIC validity */ + if (ms != ICA_MASTER || ms != ICA_SLAVE) return; + + /* + * Adjust the interrupt request number according to the parameters, + * by adding an offset == 8 to the interrupt number. + * + * Indeed VDDs calling this function usually subtracts 8 so that they give: + * + * ms | line | corresponding interrupt number + * ------------+--------+-------------------------------- + * ICA_MASTER | 0 -- 7 | 0 -- 7 + * ICA_SLAVE | 0 -- 7 | 8 -- 15 + * + * and PicInterruptRequest subtracts again 8 to the interrupt number + * if it is greater or equal than 8 (so that it determines which PIC + * to use via the interrupt number). + */ + if (ms == ICA_SLAVE) InterruptNumber += 8; + + /* Send the specified number of interrupt requests */ + while (count-- > 0) + { + PicInterruptRequest(InterruptNumber); + } +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/pic.h b/reactos/subsystems/ntvdm/hardware/pic.h new file mode 100644 index 0000000000000..03d7dd1738e57 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/pic.h @@ -0,0 +1,61 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: pic.h + * PURPOSE: Programmable Interrupt Controller emulation + * (Interrupt Controller Adapter (ICA) in Windows terminology) + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _PIC_H_ +#define _PIC_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define PIC_MASTER_CMD 0x20 +#define PIC_MASTER_DATA 0x21 +#define PIC_SLAVE_CMD 0xA0 +#define PIC_SLAVE_DATA 0xA1 + +#define PIC_ICW1 0x10 +#define PIC_ICW1_ICW4 (1 << 0) +#define PIC_ICW1_SINGLE (1 << 1) +#define PIC_ICW4_8086 (1 << 0) +#define PIC_ICW4_AEOI (1 << 1) + +#define PIC_OCW2_NUM_MASK 0x07 +#define PIC_OCW2_EOI (1 << 5) +#define PIC_OCW2_SL (1 << 6) + +#define PIC_OCW3 (1 << 3) +#define PIC_OCW3_READ_ISR 0x0B + +typedef struct _PIC +{ + BOOLEAN Initialization; + BYTE MaskRegister; + BYTE IntRequestRegister; + BYTE InServiceRegister; + BYTE IntOffset; + BYTE ConfigRegister; + BYTE CascadeRegister; + BOOLEAN CascadeRegisterSet; + BOOLEAN AutoEoi; + BOOLEAN Slave; + BOOLEAN ReadIsr; +} PIC, *PPIC; + +/* FUNCTIONS ******************************************************************/ + +VOID PicInterruptRequest(BYTE Number); +BYTE PicGetInterrupt(VOID); + +VOID PicInitialize(VOID); + +#endif // _PIC_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/ps2.c b/reactos/subsystems/ntvdm/hardware/ps2.c new file mode 100644 index 0000000000000..c98d32e76df2f --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/ps2.c @@ -0,0 +1,365 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: ps2.c + * PURPOSE: PS/2 controller emulation + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "io.h" +#include "ps2.h" +#include "pic.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static BYTE KeyboardQueue[KEYBOARD_BUFFER_SIZE]; +static BOOLEAN KeyboardQueueEmpty = TRUE; +static UINT KeyboardQueueStart = 0; +static UINT KeyboardQueueEnd = 0; +static BYTE KeyboardData = 0, KeyboardResponse = 0; +static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE; +static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG; +static HANDLE QueueMutex = NULL; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static BOOLEAN KeyboardQueuePush(BYTE ScanCode) +{ + BOOLEAN Result = TRUE; + + WaitForSingleObject(QueueMutex, INFINITE); + + /* Check if the keyboard queue is full */ + if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd)) + { + Result = FALSE; + goto Done; + } + + /* Insert the value in the queue */ + KeyboardQueue[KeyboardQueueEnd] = ScanCode; + KeyboardQueueEnd++; + KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE; + + /* Since we inserted a value, it's not empty anymore */ + KeyboardQueueEmpty = FALSE; + +Done: + ReleaseMutex(QueueMutex); + return Result; +} + +static BOOLEAN KeyboardQueuePop(BYTE *ScanCode) +{ + BOOLEAN Result = TRUE; + + /* Make sure the keyboard queue is not empty (fast check) */ + if (KeyboardQueueEmpty) return FALSE; + + WaitForSingleObject(QueueMutex, INFINITE); + + /* + * Recheck whether keyboard queue is not empty (it + * may have changed after having grabbed the mutex). + */ + if (KeyboardQueueEmpty) + { + Result = FALSE; + goto Done; + } + + /* Get the scan code */ + *ScanCode = KeyboardQueue[KeyboardQueueStart]; + + /* Remove the value from the queue */ + KeyboardQueueStart++; + KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE; + + /* Check if the queue is now empty */ + if (KeyboardQueueStart == KeyboardQueueEnd) + { + KeyboardQueueEmpty = TRUE; + } + +Done: + ReleaseMutex(QueueMutex); + return Result; +} + +static BYTE WINAPI PS2ReadPort(ULONG Port) +{ + if (Port == PS2_CONTROL_PORT) + { + BYTE Status = 0; + + /* Set the first bit if the data can be read */ + if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0; + + /* Always set bit 2 */ + Status |= 1 << 2; + + /* Set bit 3 if the next byte goes to the controller */ + if (KeyboardWriteResponse) Status |= 1 << 3; + + return Status; + } + else if (Port == PS2_DATA_PORT) + { + /* If there was a response byte from the controller, return it */ + if (KeyboardReadResponse) + { + KeyboardReadResponse = FALSE; + KeyboardData = KeyboardResponse; + } + + return KeyboardData; + } + else return 0; +} + +static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data) +{ + if (Port == PS2_CONTROL_PORT) + { + switch (Data) + { + /* Read configuration byte */ + case 0x20: + { + KeyboardResponse = KeyboardConfig; + KeyboardReadResponse = TRUE; + break; + } + + /* Write configuration byte */ + case 0x60: + /* Write controller output port */ + case 0xD1: + /* Write keyboard output buffer */ + case 0xD2: + /* Write mouse output buffer */ + case 0xD3: + /* Write mouse input buffer */ + case 0xD4: + { + /* These commands require a response */ + KeyboardResponse = Data; + KeyboardWriteResponse = TRUE; + break; + } + + /* Disable mouse */ + case 0xA7: + { + // TODO: Mouse support + break; + } + + /* Enable mouse */ + case 0xA8: + { + // TODO: Mouse support + break; + } + + /* Test mouse port */ + case 0xA9: + { + KeyboardResponse = 0; + KeyboardReadResponse = TRUE; + break; + } + + /* Test PS/2 controller */ + case 0xAA: + { + KeyboardResponse = 0x55; + KeyboardReadResponse = TRUE; + break; + } + + /* Disable keyboard */ + case 0xAD: + { + // TODO: Not implemented + break; + } + + /* Enable keyboard */ + case 0xAE: + { + // TODO: Not implemented + break; + } + + /* Read controller output port */ + case 0xD0: + { + // TODO: Not implemented + break; + } + + /* CPU Reset */ + case 0xF0: + case 0xF2: + case 0xF4: + case 0xF6: + case 0xF8: + case 0xFA: + case 0xFC: + case 0xFE: + { + /* Stop the VDM */ + EmulatorTerminate(); + break; + } + } + } + else if (Port == PS2_DATA_PORT) + { + /* Check if the controller is waiting for a response */ + if (KeyboardWriteResponse) + { + KeyboardWriteResponse = FALSE; + + /* Check which command it was */ + switch (KeyboardResponse) + { + /* Write configuration byte */ + case 0x60: + { + KeyboardConfig = Data; + break; + } + + /* Write controller output */ + case 0xD1: + { + /* Check if bit 0 is unset */ + if (!(Data & (1 << 0))) + { + /* CPU disabled - Stop the VDM */ + EmulatorTerminate(); + } + + /* Update the A20 line setting */ + EmulatorSetA20(Data & (1 << 1)); + + break; + } + + case 0xD2: + { + /* Push the data byte to the keyboard queue */ + KeyboardQueuePush(Data); + break; + } + + case 0xD3: + { + // TODO: Mouse support + break; + } + + case 0xD4: + { + // TODO: Mouse support + break; + } + } + + return; + } + + // TODO: Implement PS/2 device commands + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID PS2Dispatch(PINPUT_RECORD InputRecord) +{ + /* Check the event type */ + switch (InputRecord->EventType) + { + case KEY_EVENT: + { + WORD 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; + } + + case MOUSE_EVENT: + { + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + break; + } + + /* We ignore all the rest */ + default: + break; + } +} + +VOID GenerateKeyboardInterrupts(VOID) +{ + /* Generate an IRQ 1 if there is a key ready in the queue */ + if (KeyboardQueuePop(&KeyboardData)) PicInterruptRequest(1); +} + +BOOLEAN PS2Initialize(HANDLE ConsoleInput) +{ +#if 0 + DWORD ConInMode; +#endif + + /* Create the mutex */ + QueueMutex = CreateMutex(NULL, FALSE, NULL); + + /* Register the I/O Ports */ + RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort); + RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort); + +#if 0 + if (GetConsoleMode(ConsoleInput, &ConInMode)) + { + if (MousePresent) + { + /* Support mouse input events if there is a mouse on the system */ + ConInMode |= ENABLE_MOUSE_INPUT; + } + else + { + /* Do not support mouse input events if there is no mouse on the system */ + ConInMode &= ~ENABLE_MOUSE_INPUT; + } + + SetConsoleMode(ConsoleInput, ConInMode); + } +#endif + + return TRUE; +} + +VOID PS2Cleanup(VOID) +{ + CloseHandle(QueueMutex); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/ps2.h b/reactos/subsystems/ntvdm/hardware/ps2.h new file mode 100644 index 0000000000000..119e9bdc4714b --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/ps2.h @@ -0,0 +1,35 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: ps2.h + * PURPOSE: PS/2 controller emulation + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _PS2_H_ +#define _PS2_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define KEYBOARD_BUFFER_SIZE 32 +#define PS2_DATA_PORT 0x60 +#define PS2_CONTROL_PORT 0x64 +#define PS2_DEFAULT_CONFIG 0x05 +#define KEYBOARD_ACK 0xFA +#define KEYBOARD_RESEND 0xFE + +/* FUNCTIONS ******************************************************************/ + +VOID PS2Dispatch(PINPUT_RECORD InputRecord); +VOID GenerateKeyboardInterrupts(VOID); + +BOOLEAN PS2Initialize(HANDLE ConsoleInput); +VOID PS2Cleanup(VOID); + +#endif // _PS2_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/speaker.c b/reactos/subsystems/ntvdm/hardware/speaker.c new file mode 100644 index 0000000000000..03bf25cc84a41 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/speaker.c @@ -0,0 +1,145 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: speaker.c + * PURPOSE: PC Speaker emulation + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "speaker.h" +#include "io.h" +#include "timer.h" + +/* Extra PSDK/NDK Headers */ +#include +#include +#include + +/* DDK Driver Headers */ +#include + +/* PRIVATE VARIABLES **********************************************************/ + +static HANDLE hBeep = NULL; + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID SpeakerChange(VOID) +{ + BYTE Port61hState = IOReadB(CONTROL_SYSTEM_PORT61H); + BOOLEAN IsConnectedToPITChannel2 = !!(Port61hState & 0x01); + BOOLEAN SpeakerDataOn = !!(Port61hState & 0x02); + + if (PitChannel2 && IsConnectedToPITChannel2 && SpeakerDataOn) + { + /* Start beeping - Adapted from kernel32:Beep() */ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + BEEP_SET_PARAMETERS BeepSetParameters; + + DWORD PitChannel2ReloadValue = PitChannel2->ReloadValue; + if (PitChannel2ReloadValue == 0) PitChannel2ReloadValue = 65536; + + /* Set beep data */ + BeepSetParameters.Frequency = (PIT_BASE_FREQUENCY / PitChannel2ReloadValue) * + (PitChannel2->Mode == PIT_MODE_SQUARE_WAVE ? 2 : 1); + BeepSetParameters.Duration = INFINITE; + + /* Send the beep */ + Status = NtDeviceIoControlFile(hBeep, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BEEP_SET, + &BeepSetParameters, + sizeof(BeepSetParameters), + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Beep (%lu, %lu) failed, Status 0x%08lx\n", + BeepSetParameters.Frequency, + BeepSetParameters.Duration, + Status); + } + } + else + { + /* Stop beeping */ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + BEEP_SET_PARAMETERS BeepSetParameters; + + /* Set beep data */ + BeepSetParameters.Frequency = 0x00; + BeepSetParameters.Duration = 0x00; + + /* Send the beep */ + Status = NtDeviceIoControlFile(hBeep, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BEEP_SET, + &BeepSetParameters, + sizeof(BeepSetParameters), + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Beep (%lu, %lu) failed, Status 0x%08lx\n", + BeepSetParameters.Frequency, + BeepSetParameters.Duration, + Status); + } + } +} + +VOID SpeakerInitialize(VOID) +{ + NTSTATUS Status; + UNICODE_STRING BeepDevice; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + + /* Adapted from kernel32:Beep() */ + + // + // On TS systems, we need to Load Winsta.dll and call WinstationBeepOpen + // after doing a GetProcAddress for it + // + + /* Open the device */ + RtlInitUnicodeString(&BeepDevice, L"\\Device\\Beep"); + InitializeObjectAttributes(&ObjectAttributes, &BeepDevice, 0, NULL, NULL); + Status = NtCreateFile(&hBeep, + FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + 0, + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open Beep driver, Status 0x%08lx\n", Status); + } +} + +VOID SpeakerCleanup(VOID) +{ + NtClose(hBeep); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/speaker.h b/reactos/subsystems/ntvdm/hardware/speaker.h new file mode 100644 index 0000000000000..94c9fc07db58c --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/speaker.h @@ -0,0 +1,27 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: speaker.h + * PURPOSE: PC Speaker emulation + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _SPEAKER_H_ +#define _SPEAKER_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +/* FUNCTIONS ******************************************************************/ + +VOID SpeakerChange(VOID); + +VOID SpeakerInitialize(VOID); +VOID SpeakerCleanup(VOID); + +#endif // _SPEAKER_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/timer.c b/reactos/subsystems/ntvdm/hardware/timer.c new file mode 100644 index 0000000000000..87f6863447947 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/timer.c @@ -0,0 +1,522 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: timer.c + * PURPOSE: Programmable Interval Timer emulation - + * i82C54/8254 compatible + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "io.h" +#include "timer.h" +#include "pic.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static PIT_CHANNEL PitChannels[PIT_CHANNELS]; +PPIT_CHANNEL PitChannel2 = &PitChannels[2]; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID PitLatchChannelStatus(BYTE Channel) +{ + if (Channel >= PIT_CHANNELS) return; + + /* + * A given counter can be latched only one time until it gets unlatched. + * If the counter is latched and then is latched again later before the + * value is read, then this last latch command is ignored and the value + * will be the value at the time the first command was issued. + */ + if (PitChannels[Channel].LatchStatusSet == FALSE) + { + BYTE StatusLatch = 0; + /** HACK!! **/BYTE NullCount = 0;/** HACK!! **/ + + StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6; + StatusLatch |= (PitChannels[Channel].ReadWriteMode & 0x03) << 4; + StatusLatch |= (PitChannels[Channel].Mode & 0x07) << 1; + StatusLatch |= (PitChannels[Channel].Bcd & 0x01); + + /* Latch the counter's status */ + PitChannels[Channel].LatchStatusSet = TRUE; + PitChannels[Channel].StatusLatch = StatusLatch; + } +} + +static VOID PitLatchChannelCount(BYTE Channel) +{ + if (Channel >= PIT_CHANNELS) return; + + /* + * A given counter can be latched only one time until it gets unlatched. + * If the counter is latched and then is latched again later before the + * value is read, then this last latch command is ignored and the value + * will be the value at the time the first command was issued. + */ + if (PitChannels[Channel].ReadStatus == 0x00) + { + /* Latch the counter's value */ + PitChannels[Channel].ReadStatus = PitChannels[Channel].ReadWriteMode; + + /* Convert the current value to BCD if needed */ + PitChannels[Channel].OutputLatch = + READ_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CurrentValue); + } +} + +static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State) +{ + /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/ + + /* Set the new state of the OUT pin */ + Channel->Out = State; + + /* Call the callback */ + if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State); +} + +static VOID PitInitCounter(PPIT_CHANNEL Channel) +{ + switch (Channel->Mode) + { + case PIT_MODE_INT_ON_TERMINAL_COUNT: + PitSetOut(Channel, FALSE); + break; + + case PIT_MODE_HARDWARE_ONE_SHOT: + case PIT_MODE_RATE_GENERATOR: + case PIT_MODE_SQUARE_WAVE: + case PIT_MODE_SOFTWARE_STROBE: + case PIT_MODE_HARDWARE_STROBE: + PitSetOut(Channel, TRUE); + break; + } +} + +static VOID PitWriteCommand(BYTE Value) +{ + BYTE Channel = (Value >> 6) & 0x03; + BYTE ReadWriteMode = (Value >> 4) & 0x03; + BYTE Mode = (Value >> 1) & 0x07; + BOOLEAN IsBcd = Value & 0x01; + + /* + * Check for valid PIT channel - Possible values: 0, 1, 2. + * A value of 3 is for Read-Back Command. + */ + if (Channel > PIT_CHANNELS) return; + + /* Read-Back Command */ + if (Channel == PIT_CHANNELS) + { + if ((Value & 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts + { + if (Value & 0x02) PitLatchChannelCount(0); + if (Value & 0x04) PitLatchChannelCount(1); + if (Value & 0x08) PitLatchChannelCount(2); + } + if ((Value & 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses + { + if (Value & 0x02) PitLatchChannelStatus(0); + if (Value & 0x04) PitLatchChannelStatus(1); + if (Value & 0x08) PitLatchChannelStatus(2); + } + return; + } + + /* Check if this is a counter latch command... */ + if (ReadWriteMode == 0) + { + PitLatchChannelCount(Channel); + return; + } + + /* ... otherwise, set the modes and reset flip-flops */ + PitChannels[Channel].ReadWriteMode = ReadWriteMode; + PitChannels[Channel].ReadStatus = 0x00; + PitChannels[Channel].WriteStatus = 0x00; + + PitChannels[Channel].LatchStatusSet = FALSE; + PitChannels[Channel].StatusLatch = 0x00; + + PitChannels[Channel].CountRegister = 0x00; + PitChannels[Channel].OutputLatch = 0x00; + + /** HACK!! **/PitChannels[Channel].FlipFlop = FALSE;/** HACK!! **/ + + /* Fix the current value if we switch to BCD counting */ + PitChannels[Channel].Bcd = IsBcd; + if (IsBcd && PitChannels[Channel].CurrentValue > 9999) + PitChannels[Channel].CurrentValue = 9999; + + switch (Mode) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + { + PitChannels[Channel].Mode = Mode; + break; + } + + case 6: + case 7: + { + /* + * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR + * and PIT_MODE_SQUARE_WAVE respectively. + */ + PitChannels[Channel].Mode = Mode - 4; + break; + } + } + + PitInitCounter(&PitChannels[Channel]); +} + +static BYTE PitReadData(BYTE Channel) +{ + LPBYTE ReadWriteMode = NULL; + LPWORD CurrentValue = NULL; + + /* + * If the status was latched, the first read operation will return the + * latched status, whichever value (count or status) was latched first. + */ + if (PitChannels[Channel].LatchStatusSet) + { + PitChannels[Channel].LatchStatusSet = FALSE; + return PitChannels[Channel].StatusLatch; + } + + /* To be able to read the count asynchronously, latch it first if needed */ + if (PitChannels[Channel].ReadStatus == 0) PitLatchChannelCount(Channel); + + /* The count is now latched */ + ASSERT(PitChannels[Channel].ReadStatus != 0); + + ReadWriteMode = &PitChannels[Channel].ReadStatus ; + CurrentValue = &PitChannels[Channel].OutputLatch; + + if (*ReadWriteMode & 1) + { + /* Read LSB */ + *ReadWriteMode &= ~1; + return LOBYTE(*CurrentValue); + } + + if (*ReadWriteMode & 2) + { + /* Read MSB */ + *ReadWriteMode &= ~2; + return HIBYTE(*CurrentValue); + } + + /* Shouldn't get here */ + ASSERT(FALSE); + return 0; +} + +static VOID PitWriteData(BYTE Channel, BYTE Value) +{ + LPBYTE ReadWriteMode = NULL; + + if (PitChannels[Channel].WriteStatus == 0x00) + { + PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode; + } + + ASSERT(PitChannels[Channel].WriteStatus != 0); + + ReadWriteMode = &PitChannels[Channel].WriteStatus; + + if (*ReadWriteMode & 1) + { + /* Write LSB */ + *ReadWriteMode &= ~1; + PitChannels[Channel].CountRegister &= 0xFF00; + PitChannels[Channel].CountRegister |= Value; + } + else if (*ReadWriteMode & 2) + { + /* Write MSB */ + *ReadWriteMode &= ~2; + PitChannels[Channel].CountRegister &= 0x00FF; + PitChannels[Channel].CountRegister |= Value << 8; + } + + /* ReadWriteMode went to zero: we are going to load the new count */ + if (*ReadWriteMode == 0x00) + { + if (PitChannels[Channel].CountRegister == 0x0000) + { + /* Wrap around to the highest count */ + if (PitChannels[Channel].Bcd) + PitChannels[Channel].CountRegister = 9999; + else + PitChannels[Channel].CountRegister = 0xFFFF; // 0x10000; // 65536 + } + + /* Convert the current value from BCD if needed */ + PitChannels[Channel].CountRegister = + WRITE_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CountRegister); + PitChannels[Channel].ReloadValue = PitChannels[Channel].CountRegister; + } +} + +static BYTE WINAPI PitReadPort(ULONG Port) +{ + switch (Port) + { + case PIT_DATA_PORT(0): + case PIT_DATA_PORT(1): + case PIT_DATA_PORT(2): + { + return PitReadData(Port - PIT_DATA_PORT(0)); + } + } + + return 0; +} + +static VOID WINAPI PitWritePort(ULONG Port, BYTE Data) +{ + switch (Port) + { + case PIT_COMMAND_PORT: + { + PitWriteCommand(Data); + break; + } + + case PIT_DATA_PORT(0): + case PIT_DATA_PORT(1): + case PIT_DATA_PORT(2): + { + PitWriteData(Port - PIT_DATA_PORT(0), Data); + break; + } + } +} + +static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) +{ + if (Count == 0) return; + + switch (Channel->Mode) + { + case PIT_MODE_INT_ON_TERMINAL_COUNT: + { + /* Decrement the value */ + if (Count > Channel->CurrentValue) + { + /* The value does not reload in this case */ + Channel->CurrentValue = 0; + } + else Channel->CurrentValue -= Count; + + /* Did it fall to the terminal count? */ + if (Channel->CurrentValue == 0 && !Channel->Out) + { + /* Yes, raise the output line */ + PitSetOut(Channel, TRUE); + } + break; + } + + case PIT_MODE_RATE_GENERATOR: + { + BOOLEAN Reloaded = FALSE; + + while (Count) + { + if ((Count > Channel->CurrentValue) + && (Channel->CurrentValue != 0)) + { + /* Decrement the count */ + Count -= Channel->CurrentValue; + + /* Reload the value */ + Channel->CurrentValue = Channel->ReloadValue; + + /* Set the flag */ + Reloaded = TRUE; + } + else + { + /* Decrement the value */ + Channel->CurrentValue -= Count; + + /* Clear the count */ + Count = 0; + + /* Did it fall to zero? */ + if (Channel->CurrentValue == 0) + { + Channel->CurrentValue = Channel->ReloadValue; + Reloaded = TRUE; + } + } + } + + /* If there was a reload, raise the output line */ + if (Reloaded) PitSetOut(Channel, TRUE); + + break; + } + + case PIT_MODE_SQUARE_WAVE: + { + INT ReloadCount = 0; + WORD ReloadValue = Channel->ReloadValue; + + /* The reload value must be even */ + ReloadValue &= ~1; + + while (Count) + { + if (((Count * 2) > Channel->CurrentValue) + && (Channel->CurrentValue != 0)) + { + /* Decrement the count */ + Count -= Channel->CurrentValue / 2; + + /* Reload the value */ + Channel->CurrentValue = ReloadValue; + + /* Increment the reload count */ + ReloadCount++; + } + else + { + /* Decrement the value */ + Channel->CurrentValue -= Count * 2; + + /* Clear the count */ + Count = 0; + + /* Did it fall to zero? */ + if (Channel->CurrentValue == 0) + { + /* Reload the value */ + Channel->CurrentValue = ReloadValue; + + /* Increment the reload count */ + ReloadCount++; + } + } + } + + if (ReloadCount == 0) break; + + /* Toggle the flip-flop if the number of reloads was odd */ + if (ReloadCount & 1) + { + Channel->FlipFlop = !Channel->FlipFlop; + // PitSetOut(Channel, !Channel->Out); + } + + /* Was there any rising edge? */ + if ((Channel->FlipFlop && (ReloadCount == 1)) || (ReloadCount > 1)) + { + /* Yes, raise the output line */ + PitSetOut(Channel, TRUE); + } + + break; + } + + case PIT_MODE_SOFTWARE_STROBE: + { + // TODO: NOT IMPLEMENTED + break; + } + + case PIT_MODE_HARDWARE_ONE_SHOT: + case PIT_MODE_HARDWARE_STROBE: + { + /* These modes do not work on x86 PCs */ + break; + } + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction) +{ + if (Channel >= PIT_CHANNELS) return; + + PitChannels[Channel].OutParam = Param; + PitChannels[Channel].OutFunction = OutFunction; +} + +VOID PitSetGate(BYTE Channel, BOOLEAN State) +{ + if (Channel >= PIT_CHANNELS) return; + if (State == PitChannels[Channel].Gate) return; + + /* UNIMPLEMENTED */ + PitChannels[Channel].Gate = State; +} + +VOID PitClock(DWORD Count) +{ + UINT i; + + if (Count == 0) return; + + for (i = 0; i < PIT_CHANNELS; i++) + { + // if (!PitChannels[i].Counting) continue; + PitDecrementCount(&PitChannels[i], Count); + } +} + +DWORD PitGetResolution(VOID) +{ + INT i; + DWORD MinReloadValue = 65536; + + for (i = 0; i < PIT_CHANNELS; i++) + { + DWORD ReloadValue = PitChannels[i].ReloadValue; + + /* 0 means 65536 */ + if (ReloadValue == 0) ReloadValue = 65536; + + if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue; + } + + /* Return the frequency resolution */ + return PIT_BASE_FREQUENCY / MinReloadValue; +} + +VOID PitInitialize(VOID) +{ + /* Set up the timers to their default value */ + PitSetOutFunction(0, NULL, NULL); + PitSetGate(0, TRUE); + PitSetOutFunction(1, NULL, NULL); + PitSetGate(1, TRUE); + PitSetOutFunction(2, NULL, NULL); + PitSetGate(2, FALSE); + + /* Register the I/O Ports */ + RegisterIoPort(PIT_COMMAND_PORT, NULL , PitWritePort); + RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort); + RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort); + RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/timer.h b/reactos/subsystems/ntvdm/hardware/timer.h new file mode 100644 index 0000000000000..21f7f8ee37971 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/timer.h @@ -0,0 +1,90 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: timer.h + * PURPOSE: Programmable Interval Timer emulation - + * i82C54/8254 compatible + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define PIT_CHANNELS 3 +#define PIT_BASE_FREQUENCY 1193182LL +#define PIT_DATA_PORT(x) (0x40 + (x)) +#define PIT_COMMAND_PORT 0x43 + +#define WRITE_PIT_VALUE(PitChannel, Value) \ + (PitChannel).Bcd ? BCD_TO_BINARY(Value) : (Value) + +#define READ_PIT_VALUE(PitChannel, Value) \ + (PitChannel).Bcd ? BINARY_TO_BCD(Value) : (Value) + +typedef enum _PIT_MODE +{ + PIT_MODE_INT_ON_TERMINAL_COUNT, + PIT_MODE_HARDWARE_ONE_SHOT, + PIT_MODE_RATE_GENERATOR, + PIT_MODE_SQUARE_WAVE, + PIT_MODE_SOFTWARE_STROBE, + PIT_MODE_HARDWARE_STROBE +} PIT_MODE, *PPIT_MODE; + +typedef VOID (WINAPI *PIT_OUT_FUNCTION)(LPVOID Param, BOOLEAN State); + +typedef struct _PIT_CHANNEL +{ + /* PIT Status fields */ + PIT_MODE Mode; + BOOLEAN Bcd; + BYTE ReadWriteMode; // 0 --> Counter Latch ; 1 --> LSB R/W ; 2 --> MSB R/W ; 3 --> LSB then MSB R/W + + /* For interleaved reading and writing in 2-byte RW mode */ + BYTE ReadStatus; // Same convention as ReadWriteMode + BYTE WriteStatus; // Same convention as ReadWriteMode + + /* For reading the PIT status byte */ + BOOLEAN LatchStatusSet; + BYTE StatusLatch; + + /* Counting */ + BOOLEAN Gate; + + /**/WORD CountRegister;/**/ // Our ReloadValue ??? + WORD OutputLatch; + /*******************************/ + + WORD ReloadValue; // Max value of the counter + WORD CurrentValue; // Real value of the counter + + /* PIT Output */ + BOOLEAN Out; // 0: Low ; 1: High + /** HACK!! **/BOOLEAN FlipFlop;/** HACK!! **/ + LPVOID OutParam; + PIT_OUT_FUNCTION OutFunction; + +} PIT_CHANNEL, *PPIT_CHANNEL; + +extern PPIT_CHANNEL PitChannel2; // Needed for PC Speaker + +/* FUNCTIONS ******************************************************************/ + +VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction); +VOID PitSetGate(BYTE Channel, BOOLEAN State); + +VOID PitClock(DWORD Count); +DWORD PitGetResolution(VOID); + +VOID PitInitialize(VOID); + +#endif // _TIMER_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/vga.c b/reactos/subsystems/ntvdm/hardware/vga.c new file mode 100644 index 0000000000000..4a4dc1b92378f --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/vga.c @@ -0,0 +1,1935 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vga.c + * PURPOSE: VGA hardware emulation + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "vga.h" +#include "../bios/vidbios.h" + +#include "io.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 }; +static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF }; + +#define USE_REACTOS_COLORS +// #define USE_DOSBOX_COLORS + +#if defined(USE_REACTOS_COLORS) + +// ReactOS colors +static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35), + RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75), + RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA), + RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF), + RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00), + RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00), + RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE), + RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF), + RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF), + RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E), + RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82), + RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82), + RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF), + RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF), + RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF), + RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA), + RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA), + RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA), + RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF), + RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF), + RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71), + RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C), + RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00), + RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00), + RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55), + RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71), + RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71), + RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45), + RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39), + RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39), + RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61), + RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71), + RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71), + RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59), + RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51), + RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51), + RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69), + RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71), + RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41), + RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10), + RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00), + RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00), + RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31), + RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41), + RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41), + RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28), + RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20), + RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20), + RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39), + RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41), + RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41), + RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31), + RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D), + RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D), + RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D), + RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00) +}; + +#elif defined(USE_DOSBOX_COLORS) + +// DOSBox colors +static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] = +{ + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA), + RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA), + RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF), + RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0x00), RGB(0x14, 0x14, 0x14), RGB(0x20, 0x20, 0x20), RGB(0x2C, 0x2C, 0x2C), + RGB(0x38, 0x38, 0x38), RGB(0x45, 0x45, 0x45), RGB(0x51, 0x51, 0x51), RGB(0x61, 0x61, 0x61), + RGB(0x71, 0x71, 0x71), RGB(0x82, 0x82, 0x82), RGB(0x92, 0x92, 0x92), RGB(0xA2, 0xA2, 0xA2), + RGB(0xB6, 0xB6, 0xB6), RGB(0xCB, 0xCB, 0xCB), RGB(0xE3, 0xE3, 0xE3), RGB(0xFF, 0xFF, 0xFF), + RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x7D, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF), + RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x7D), RGB(0xFF, 0x00, 0x41), + RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x7D, 0x00), RGB(0xFF, 0xBE, 0x00), + RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x7D, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00), + RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x7D), RGB(0x00, 0xFF, 0xBE), + RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x7D, 0xFF), RGB(0x00, 0x41, 0xFF), + RGB(0x7D, 0x7D, 0xFF), RGB(0x9E, 0x7D, 0xFF), RGB(0xBE, 0x7D, 0xFF), RGB(0xDF, 0x7D, 0xFF), + RGB(0xFF, 0x7D, 0xFF), RGB(0xFF, 0x7D, 0xDF), RGB(0xFF, 0x7D, 0xBE), RGB(0xFF, 0x7D, 0x9E), + + RGB(0xFF, 0x7D, 0x7D), RGB(0xFF, 0x9E, 0x7D), RGB(0xFF, 0xBE, 0x7D), RGB(0xFF, 0xDF, 0x7D), + RGB(0xFF, 0xFF, 0x7D), RGB(0xDF, 0xFF, 0x7D), RGB(0xBE, 0xFF, 0x7D), RGB(0x9E, 0xFF, 0x7D), + RGB(0x7D, 0xFF, 0x7D), RGB(0x7D, 0xFF, 0x9E), RGB(0x7D, 0xFF, 0xBE), RGB(0x7D, 0xFF, 0xDF), + RGB(0x7D, 0xFF, 0xFF), RGB(0x7D, 0xDF, 0xFF), RGB(0x7D, 0xBE, 0xFF), RGB(0x7D, 0x9E, 0xFF), + RGB(0xB6, 0xB6, 0xFF), RGB(0xC7, 0xB6, 0xFF), RGB(0xDB, 0xB6, 0xFF), RGB(0xEB, 0xB6, 0xFF), + RGB(0xFF, 0xB6, 0xFF), RGB(0xFF, 0xB6, 0xEB), RGB(0xFF, 0xB6, 0xDB), RGB(0xFF, 0xB6, 0xC7), + RGB(0xFF, 0xB6, 0xB6), RGB(0xFF, 0xC7, 0xB6), RGB(0xFF, 0xDB, 0xB6), RGB(0xFF, 0xEB, 0xB6), + RGB(0xFF, 0xFF, 0xB6), RGB(0xEB, 0xFF, 0xB6), RGB(0xDB, 0xFF, 0xB6), RGB(0xC7, 0xFF, 0xB6), + RGB(0xB6, 0xFF, 0xB6), RGB(0xB6, 0xFF, 0xC7), RGB(0xB6, 0xFF, 0xDB), RGB(0xB6, 0xFF, 0xEB), + RGB(0xB6, 0xFF, 0xFF), RGB(0xB6, 0xEB, 0xFF), RGB(0xB6, 0xDB, 0xFF), RGB(0xB6, 0xC7, 0xFF), + RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x38, 0x00, 0x71), RGB(0x55, 0x00, 0x71), + RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x38), RGB(0x71, 0x00, 0x1C), + RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x38, 0x00), RGB(0x71, 0x55, 0x00), + RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x38, 0x71, 0x00), RGB(0x1C, 0x71, 0x00), + RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x38), RGB(0x00, 0x71, 0x55), + RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x38, 0x71), RGB(0x00, 0x1C, 0x71), + + RGB(0x38, 0x38, 0x71), RGB(0x45, 0x38, 0x71), RGB(0x55, 0x38, 0x71), RGB(0x61, 0x38, 0x71), + RGB(0x71, 0x38, 0x71), RGB(0x71, 0x38, 0x61), RGB(0x71, 0x38, 0x55), RGB(0x71, 0x38, 0x45), + RGB(0x71, 0x38, 0x38), RGB(0x71, 0x45, 0x38), RGB(0x71, 0x55, 0x38), RGB(0x71, 0x61, 0x38), + RGB(0x71, 0x71, 0x38), RGB(0x61, 0x71, 0x38), RGB(0x55, 0x71, 0x38), RGB(0x45, 0x71, 0x38), + RGB(0x38, 0x71, 0x38), RGB(0x38, 0x71, 0x45), RGB(0x38, 0x71, 0x55), RGB(0x38, 0x71, 0x61), + RGB(0x38, 0x71, 0x71), RGB(0x38, 0x61, 0x71), RGB(0x38, 0x55, 0x71), RGB(0x38, 0x45, 0x71), + RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71), + RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59), + RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51), + RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51), + RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69), + RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71), + RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x30, 0x00, 0x41), + RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x30), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10), + RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x30, 0x00), + RGB(0x41, 0x41, 0x00), RGB(0x30, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00), + + RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x30), + RGB(0x00, 0x41, 0x41), RGB(0x00, 0x30, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41), + RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x30, 0x20, 0x41), RGB(0x38, 0x20, 0x41), + RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x38), RGB(0x41, 0x20, 0x30), RGB(0x41, 0x20, 0x28), + RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x30, 0x20), RGB(0x41, 0x38, 0x20), + RGB(0x41, 0x41, 0x20), RGB(0x38, 0x41, 0x20), RGB(0x30, 0x41, 0x20), RGB(0x28, 0x41, 0x20), + RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x30), RGB(0x20, 0x41, 0x38), + RGB(0x20, 0x41, 0x41), RGB(0x20, 0x38, 0x41), RGB(0x20, 0x30, 0x41), RGB(0x20, 0x28, 0x41), + RGB(0x2C, 0x2C, 0x41), RGB(0x30, 0x2C, 0x41), RGB(0x34, 0x2C, 0x41), RGB(0x3C, 0x2C, 0x41), + RGB(0x41, 0x2C, 0x41), RGB(0x41, 0x2C, 0x3C), RGB(0x41, 0x2C, 0x34), RGB(0x41, 0x2C, 0x30), + RGB(0x41, 0x2C, 0x2C), RGB(0x41, 0x30, 0x2C), RGB(0x41, 0x34, 0x2C), RGB(0x41, 0x3C, 0x2C), + RGB(0x41, 0x41, 0x2C), RGB(0x3C, 0x41, 0x2C), RGB(0x34, 0x41, 0x2C), RGB(0x30, 0x41, 0x2C), + RGB(0x2C, 0x41, 0x2C), RGB(0x2C, 0x41, 0x30), RGB(0x2C, 0x41, 0x34), RGB(0x2C, 0x41, 0x3C), + RGB(0x2C, 0x41, 0x41), RGB(0x2C, 0x3C, 0x41), RGB(0x2C, 0x34, 0x41), RGB(0x2C, 0x30, 0x41), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), + RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00) +}; + +#endif + +/* + * Console interface -- VGA-mode-agnostic + */ +typedef struct _CHAR_CELL +{ + CHAR Char; + BYTE Attributes; +} CHAR_CELL, *PCHAR_CELL; +C_ASSERT(sizeof(CHAR_CELL) == 2); + +static LPVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to + // either TextFramebuffer or a valid + // graphics framebuffer. +static HPALETTE PaletteHandle = NULL; + +static HANDLE StartEvent = NULL; +static HANDLE EndEvent = NULL; +static HANDLE AnotherEvent = NULL; + +static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo; +static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo; + + +/* + * Text mode -- we always keep a valid text mode framebuffer + * even if we are in graphics mode. This is needed in order to + * keep a consistent VGA state. + */ +static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; +static COORD TextResolution = {0}; +static PCHAR_CELL TextFramebuffer = NULL; +static HANDLE TextConsoleBuffer = NULL; + +/* Graphics mode */ +static HANDLE GraphicsConsoleBuffer = NULL; +static HANDLE ConsoleMutex = NULL; +static BOOLEAN DoubleVision = FALSE; + +/* + * VGA Hardware + */ +static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE]; + +static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0}; + +static BYTE VgaMiscRegister; +static BYTE VgaFeatureRegister; + +static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG; +static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG]; + +static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG; +static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG]; + +static BYTE VgaGcIndex = VGA_GC_RESET_REG; +static BYTE VgaGcRegisters[VGA_GC_MAX_REG]; + +static BOOLEAN VgaAcLatch = FALSE; +static BOOLEAN VgaAcPalDisable = TRUE; +static BYTE VgaAcIndex = VGA_AC_PAL_0_REG; +static BYTE VgaAcRegisters[VGA_AC_MAX_REG]; + +// static VGA_REGISTERS VgaRegisters; + +static BYTE VgaDacMask = 0xFF; +static WORD VgaDacIndex = 0; +static BOOLEAN VgaDacReadWrite = FALSE; +static BYTE VgaDacRegisters[VGA_PALETTE_SIZE]; + +static BOOLEAN InVerticalRetrace = FALSE; +static BOOLEAN InHorizontalRetrace = FALSE; + +static BOOLEAN NeedsUpdate = FALSE; +static BOOLEAN ModeChanged = FALSE; +static BOOLEAN CursorMoved = FALSE; +static BOOLEAN PaletteChanged = FALSE; + +static +enum SCREEN_MODE +{ + TEXT_MODE, + GRAPHICS_MODE +} ScreenMode = TEXT_MODE; + +static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 }; + +/* RegisterConsoleVDM EMULATION ***********************************************/ + +#include + +typedef +BOOL +(WINAPI *pRegisterConsoleVDM) +( + BOOL IsDosVDM_flag, + HANDLE EventHandle_1, + HANDLE EventHandle_2, + HANDLE EventHandle_3, + int Unused1, + PVOID returned_val_1, + PVOID *returned_val_2, + PVOID lpUnknownBuffer, + DWORD theUnknownBufferLength, + COORD theVDMBufferSize, + PCHAR *lpVDMBuffer +); + +#if 0 +BOOL +WINAPI +RegisterConsoleVDM +( + BOOL IsDosVDM_flag, + HANDLE EventHandle_1, + HANDLE EventHandle_2, + HANDLE EventHandle_3, + int Unused1, + PVOID returned_val_1, + PVOID *returned_val_2, + PVOID lpUnknownBuffer, + DWORD theUnknownBufferLength, + COORD theVDMBufferSize, + PVOID *lpVDMBuffer +); + +HMODULE hKernel32 = NULL; +pRegisterConsoleVDM RegisterConsoleVDM = NULL; +#endif + +/* + * This private buffer, per-console, is used by + * RegisterConsoleVDM and InvalidateConsoleDIBits. + */ +static COORD VDMBufferSize = {0}; +static PCHAR_CELL VDMBuffer = NULL; + +static PCHAR_INFO CharBuff = NULL; // This is a hack, which is unneeded + // for the real RegisterConsoleVDM and + // InvalidateConsoleDIBits + +BOOL +WINAPI +__RegisterConsoleVDM(BOOL IsDosVDM_flag, + HANDLE EventHandle_1, + HANDLE EventHandle_2, + HANDLE EventHandle_3, + int Unused1, + PVOID returned_val_1, + PVOID *returned_val_2, + PVOID lpUnknownBuffer, + DWORD theUnknownBufferLength, + COORD theVDMBufferSize, + PCHAR *lpVDMBuffer) +{ + UNREFERENCED_PARAMETER(EventHandle_3); + UNREFERENCED_PARAMETER(Unused1); + UNREFERENCED_PARAMETER(returned_val_1); + UNREFERENCED_PARAMETER(returned_val_2); + UNREFERENCED_PARAMETER(lpUnknownBuffer); + UNREFERENCED_PARAMETER(theUnknownBufferLength); + + SetLastError(0); + DPRINT1("__RegisterConsoleVDM(%d)\n", IsDosVDM_flag); + + if (lpVDMBuffer == NULL) return FALSE; + + if (IsDosVDM_flag) + { + // if (EventHandle_1 == NULL || EventHandle_2 == NULL) return FALSE; + if (VDMBuffer != NULL) return FALSE; + + VDMBufferSize = theVDMBufferSize; + + /* HACK: Cache -- to be removed in the real implementation */ + CharBuff = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + theVDMBufferSize.X * theVDMBufferSize.Y + * sizeof(CHAR_INFO)); + ASSERT(CharBuff); + + VDMBuffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + theVDMBufferSize.X * theVDMBufferSize.Y + * sizeof(CHAR_CELL)); + *lpVDMBuffer = (PCHAR)VDMBuffer; + return (VDMBuffer != NULL); + } + else + { + /* HACK: Cache -- to be removed in the real implementation */ + if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff); + CharBuff = NULL; + + if (VDMBuffer) HeapFree(GetProcessHeap(), 0, VDMBuffer); + VDMBuffer = NULL; + + VDMBufferSize.X = VDMBufferSize.Y = 0; + + return TRUE; + } +} + +BOOL +__InvalidateConsoleDIBits(IN HANDLE hConsoleOutput, + IN PSMALL_RECT lpRect) +{ + if ((hConsoleOutput == TextConsoleBuffer) && (VDMBuffer != NULL)) + { + /* HACK: Write the cached data to the console */ + + COORD Origin = { lpRect->Left, lpRect->Top }; + SHORT i, j; + + ASSERT(CharBuff); + + for (i = 0; i < VDMBufferSize.Y; i++) + { + for (j = 0; j < VDMBufferSize.X; j++) + { + CharBuff[i * VDMBufferSize.X + j].Char.AsciiChar = VDMBuffer[i * VDMBufferSize.X + j].Char; + CharBuff[i * VDMBufferSize.X + j].Attributes = VDMBuffer[i * VDMBufferSize.X + j].Attributes; + } + } + + WriteConsoleOutputA(hConsoleOutput, + CharBuff, + VDMBufferSize, + Origin, + lpRect); + } + + return InvalidateConsoleDIBits(hConsoleOutput, lpRect); +} + +/* PRIVATE FUNCTIONS **********************************************************/ + +static inline DWORD VgaGetAddressSize(VOID); +static VOID VgaUpdateTextCursor(VOID); + +static VOID VgaUpdateCursorPosition(VOID) +{ + /* + * Update the cursor position in the VGA registers. + */ + WORD Offset = ConsoleInfo.dwCursorPosition.Y * TextResolution.X + + ConsoleInfo.dwCursorPosition.X; + + VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG] = LOBYTE(Offset); + VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG] = HIBYTE(Offset); + + VidBiosSyncCursorPosition(); + VgaUpdateTextCursor(); +} + +static BOOL VgaAttachToConsoleInternal(PCOORD Resolution) +{ + BOOL Success; + ULONG Length = 0; + PVIDEO_HARDWARE_STATE_HEADER State; + + SHORT i, j; + DWORD AddressSize, ScanlineSize; + DWORD Address = 0; + DWORD CurrentAddr; + SMALL_RECT ConRect; + COORD Origin = { 0, 0 }; + + ASSERT(TextFramebuffer == NULL); + + TextResolution = *Resolution; + + /* + * Windows 2k3 winsrv.dll calls NtVdmControl(VdmQueryVdmProcess == 14, &ConsoleHandle); + * in the two following APIs: + * SrvRegisterConsoleVDM (corresponding win32 API: RegisterConsoleVDM) + * SrvVDMConsoleOperation (corresponding Win32 API: ) + * to check whether the current process is a VDM process, and fails otherwise with the + * error 0xC0000022 (). + * + * It is worth it to notice that also basesrv.dll does the same only for the + * BaseSrvIsFirstVDM API... + */ + + Success = + __RegisterConsoleVDM(1, + StartEvent, + EndEvent, + AnotherEvent, // NULL, + 0, + &Length, // NULL, <-- putting this (and null in the next var) makes the API returning error 12 "ERROR_INVALID_ACCESS" + (PVOID*)&State, // NULL, + NULL, + 0, + TextResolution, + (PCHAR*)&TextFramebuffer); + if (!Success) + { + DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError()); + EmulatorTerminate(); + return FALSE; + } + + /* + * Resize the console + */ + ConRect.Left = 0; + ConRect.Top = ConsoleInfo.srWindow.Top; + ConRect.Right = ConRect.Left + Resolution->X - 1; + ConRect.Bottom = ConRect.Top + Resolution->Y - 1; + /* + * Use this trick to effectively resize the console buffer and window, + * because: + * - SetConsoleScreenBufferSize fails if the new console screen buffer size + * is smaller than the current console window size, and: + * - SetConsoleWindowInfo fails if the new console window size is larger + * than the current console screen buffer size. + */ + SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution); + SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect); + SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution); + /* Update the saved console information */ + GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo); + + /* + * Copy console data into VGA memory + */ + + /* Get the data */ + AddressSize = VgaGetAddressSize(); + ConRect.Left = ConRect.Top = 0; + ConRect.Right = TextResolution.X; + ConRect.Bottom = TextResolution.Y; + ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2; + + /* Read the data from the console into the framebuffer... */ + ReadConsoleOutputA(TextConsoleBuffer, + CharBuff, + TextResolution, + Origin, + &ConRect); + + /* ... and copy the framebuffer into the VGA memory */ + + /* Loop through the scanlines */ + for (i = 0; i < TextResolution.Y; i++) + { + /* Loop through the characters */ + for (j = 0; j < TextResolution.X; j++) + { + CurrentAddr = LOWORD((Address + j) * AddressSize); + + /* Store the character in plane 0 */ + VgaMemory[CurrentAddr] = CharBuff[i * TextResolution.X + j].Char.AsciiChar; + + /* Store the attribute in plane 1 */ + VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuff[i * TextResolution.X + j].Attributes; + } + + /* Move to the next scanline */ + Address += ScanlineSize; + } + + VgaUpdateCursorPosition(); + + return TRUE; +} + +BOOL VgaAttachToConsole(VOID) +{ + if (TextResolution.X == 0 || TextResolution.Y == 0) + DPRINT1("VgaAttachToConsole -- TextResolution uninitialized\n"); + + if (TextResolution.X == 0) TextResolution.X = 80; + if (TextResolution.Y == 0) TextResolution.Y = 25; + + return VgaAttachToConsoleInternal(&TextResolution); +} + +VOID VgaDetachFromConsole(BOOL ChangingMode) +{ + ULONG dummyLength; + PVOID dummyPtr; + COORD dummySize = {0}; + + __RegisterConsoleVDM(0, + NULL, + NULL, + NULL, + 0, + &dummyLength, + &dummyPtr, + NULL, + 0, + dummySize, + (PCHAR*)&dummyPtr); + + TextFramebuffer = NULL; + + if (!ChangingMode) + { + SMALL_RECT ConRect; + + /* Restore the old screen buffer */ + SetConsoleActiveScreenBuffer(TextConsoleBuffer); + + /* Restore the original console size */ + ConRect.Left = 0; + ConRect.Top = 0; + ConRect.Right = ConRect.Left + OrgConsoleBufferInfo.srWindow.Right - OrgConsoleBufferInfo.srWindow.Left; + ConRect.Bottom = ConRect.Top + OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top ; + /* + * See the following trick explanation in VgaAttachToConsoleInternal. + */ + SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize); + SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect); + SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize); + + /* Restore the original cursor shape */ + SetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo); + } +} + +static BOOL IsConsoleHandle(HANDLE hHandle) +{ + DWORD dwMode; + + /* Check whether the handle may be that of a console... */ + if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR) + return FALSE; + + /* + * It may be. Perform another test... The idea comes from the + * MSDN description of the WriteConsole API: + * + * "WriteConsole fails if it is used with a standard handle + * that is redirected to a file. If an application processes + * multilingual output that can be redirected, determine whether + * the output handle is a console handle (one method is to call + * the GetConsoleMode function and check whether it succeeds). + * If the handle is a console handle, call WriteConsole. If the + * handle is not a console handle, the output is redirected and + * you should call WriteFile to perform the I/O." + */ + return GetConsoleMode(hHandle, &dwMode); +} + +static inline DWORD VgaGetAddressSize(VOID) +{ + if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD) + { + /* Double-word addressing */ + return 4; // sizeof(DWORD) + } + else if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE) + { + /* Byte addressing */ + return 1; // sizeof(BYTE) + } + else + { + /* Word addressing */ + return 2; // sizeof(WORD) + } +} + +static inline DWORD VgaTranslateReadAddress(DWORD Address) +{ + DWORD Offset = Address - VgaGetVideoBaseAddress(); + BYTE Plane; + + /* Check for chain-4 and odd-even mode */ + if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4) + { + /* The lowest two bits are the plane number */ + Plane = Offset & 3; + Offset >>= 2; + } + else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE) + { + /* The LSB is the plane number */ + Plane = Offset & 1; + Offset >>= 1; + } + else + { + /* Use the read mode */ + Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03; + } + + /* Multiply the offset by the address size */ + Offset *= VgaGetAddressSize(); + + return Offset + Plane * VGA_BANK_SIZE; +} + +static inline DWORD VgaTranslateWriteAddress(DWORD Address) +{ + DWORD Offset = Address - VgaGetVideoBaseAddress(); + + /* Check for chain-4 and odd-even mode */ + if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4) + { + /* Shift the offset to the right by 2 */ + Offset >>= 2; + } + else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE) + { + /* Shift the offset to the right by 1 */ + Offset >>= 1; + } + + /* Multiply the offset by the address size */ + Offset *= VgaGetAddressSize(); + + /* Return the offset on plane 0 */ + return Offset; +} + +static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane) +{ + BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 3; + BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG]; + + if (WriteMode == 1) + { + /* In write mode 1 just return the latch register */ + return VgaLatchRegisters[Plane]; + } + + if (WriteMode != 2) + { + /* Write modes 0 and 3 rotate the data to the right first */ + BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 7; + Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount))); + } + else + { + /* Write mode 2 expands the appropriate bit to all 8 bits */ + Data = (Data & (1 << Plane)) ? 0xFF : 0x00; + } + + if (WriteMode == 0) + { + /* + * In write mode 0, the enable set/reset register decides if the + * set/reset bit should be expanded to all 8 bits. + */ + if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane)) + { + /* Copy the bit from the set/reset register to all 8 bits */ + Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00; + } + } + + if (WriteMode != 3) + { + /* Write modes 0 and 2 then perform a logical operation on the data and latch */ + BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 3; + + if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane]; + else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane]; + else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane]; + } + else + { + /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */ + BitMask &= Data; + + /* Then we expand the bit in the set/reset field */ + Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00; + } + + /* Bits cleared in the bitmask are replaced with latch register bits */ + Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask)); + + /* Return the byte */ + return Data; +} + +static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column) +{ + /* Check if this is the first time the rectangle is updated */ + if (!NeedsUpdate) + { + UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT; + UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT; + } + + /* Expand the rectangle to include the point */ + UpdateRectangle.Left = min(UpdateRectangle.Left, Column); + UpdateRectangle.Right = max(UpdateRectangle.Right, Column); + UpdateRectangle.Top = min(UpdateRectangle.Top, Row); + UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row); + + /* Set the update request flag */ + NeedsUpdate = TRUE; +} + +static VOID VgaWriteSequencer(BYTE Data) +{ + ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG); + + /* Save the value */ + VgaSeqRegisters[VgaSeqIndex] = Data; +} + +static VOID VgaWriteGc(BYTE Data) +{ + ASSERT(VgaGcIndex < VGA_GC_MAX_REG); + + /* Save the value */ + VgaGcRegisters[VgaGcIndex] = Data; + + /* Check the index */ + switch (VgaGcIndex) + { + case VGA_GC_MISC_REG: + { + /* The GC misc register decides if it's text or graphics mode */ + ModeChanged = TRUE; + break; + } + } +} + +static VOID VgaWriteCrtc(BYTE Data) +{ + ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG); + + /* Save the value */ + VgaCrtcRegisters[VgaCrtcIndex] = Data; + + /* Check the index */ + switch (VgaCrtcIndex) + { + case VGA_CRTC_END_HORZ_DISP_REG: + case VGA_CRTC_VERT_DISP_END_REG: + case VGA_CRTC_OVERFLOW_REG: + { + /* The video mode has changed */ + ModeChanged = TRUE; + break; + } + + case VGA_CRTC_CURSOR_LOC_LOW_REG: + case VGA_CRTC_CURSOR_LOC_HIGH_REG: + case VGA_CRTC_CURSOR_START_REG: + case VGA_CRTC_CURSOR_END_REG: + { + /* Set the cursor moved flag */ + CursorMoved = TRUE; + break; + } + } +} + +static VOID VgaWriteDac(BYTE Data) +{ + INT PaletteIndex; + PALETTEENTRY Entry; + + /* Set the value */ + VgaDacRegisters[VgaDacIndex] = Data; + + /* Find the palette index */ + PaletteIndex = VgaDacIndex / 3; + + /* Fill the entry structure */ + Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3]); + Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 1]); + Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]); + Entry.peFlags = 0; + + /* Update the palette entry and set the palette change flag */ + SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry); + PaletteChanged = TRUE; + + /* Update the index */ + VgaDacIndex++; + VgaDacIndex %= VGA_PALETTE_SIZE; +} + +static VOID VgaWriteAc(BYTE Data) +{ + ASSERT(VgaAcIndex < VGA_AC_MAX_REG); + + /* Save the value */ + if (VgaAcIndex <= VGA_AC_PAL_F_REG) + { + if (VgaAcPalDisable) return; + + // DbgPrint(" AC Palette Writing %d to index %d\n", Data, VgaAcIndex); + if (VgaAcRegisters[VgaAcIndex] != Data) + { + /* Update the AC register and set the palette change flag */ + VgaAcRegisters[VgaAcIndex] = Data; + PaletteChanged = TRUE; + } + } + else + { + VgaAcRegisters[VgaAcIndex] = Data; + } +} + +static VOID VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries) +{ + USHORT i; + + /* Copy the colors of the default palette to the DAC and console palette */ + for (i = 0; i < NumOfEntries; i++) + { + /* Set the palette entries */ + Entries[i].peRed = GetRValue(VgaDefaultPalette[i]); + Entries[i].peGreen = GetGValue(VgaDefaultPalette[i]); + Entries[i].peBlue = GetBValue(VgaDefaultPalette[i]); + Entries[i].peFlags = 0; + + /* Set the DAC registers */ + VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i])); + VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i])); + VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i])); + } +} + +static BOOLEAN VgaInitializePalette(VOID) +{ + LPLOGPALETTE Palette; + + /* Allocate storage space for the palette */ + Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(LOGPALETTE) + + VGA_MAX_COLORS * sizeof(PALETTEENTRY)); + if (Palette == NULL) return FALSE; + + /* Initialize the palette */ + Palette->palVersion = 0x0300; + Palette->palNumEntries = VGA_MAX_COLORS; + + /* Restore the default palette */ + VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries); + + /* Create the palette */ + PaletteHandle = CreatePalette(Palette); + + /* Free the palette */ + HeapFree(GetProcessHeap(), 0, Palette); + + /* Fail if the palette wasn't successfully created... */ + if (PaletteHandle == NULL) return FALSE; + + /* ... otherwise return success */ + return TRUE; +} + +static BOOL VgaEnterGraphicsMode(PCOORD Resolution) +{ + DWORD i; + CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo; + BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE]; + LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer; + LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors); + + LONG Width = Resolution->X; + LONG Height = Resolution->Y; + + /* Use DoubleVision mode if the resolution is too small */ + if (Width < VGA_MINIMUM_WIDTH && Height < VGA_MINIMUM_HEIGHT) + { + DoubleVision = TRUE; + Width *= 2; + Height *= 2; + } + else + { + DoubleVision = FALSE; + } + + /* Fill the bitmap info header */ + ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER)); + BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + BitmapInfo->bmiHeader.biWidth = Width; + BitmapInfo->bmiHeader.biHeight = Height; + BitmapInfo->bmiHeader.biBitCount = 8; + BitmapInfo->bmiHeader.biPlanes = 1; + BitmapInfo->bmiHeader.biCompression = BI_RGB; + BitmapInfo->bmiHeader.biSizeImage = Width * Height /* * 1 == biBitCount / 8 */; + + /* Fill the palette data */ + for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i; + + /* Fill the console graphics buffer info */ + GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE; + GraphicsBufferInfo.lpBitMapInfo = BitmapInfo; + GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS; + + /* Create the buffer */ + GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CONSOLE_GRAPHICS_BUFFER, + &GraphicsBufferInfo); + if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE; + + /* Save the framebuffer address and mutex */ + ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap; + ConsoleMutex = GraphicsBufferInfo.hMutex; + + /* Clear the framebuffer */ + ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage); + + /* Set the active buffer */ + SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer); + + /* Set the graphics mode palette */ + SetConsolePalette(GraphicsConsoleBuffer, + PaletteHandle, + SYSPAL_NOSTATIC256); + + /* Set the screen mode flag */ + ScreenMode = GRAPHICS_MODE; + + return TRUE; +} + +static VOID VgaLeaveGraphicsMode(VOID) +{ + /* Release the console framebuffer mutex */ + ReleaseMutex(ConsoleMutex); + + /* Switch back to the default console text buffer */ + // SetConsoleActiveScreenBuffer(TextConsoleBuffer); + + /* Cleanup the video data */ + CloseHandle(ConsoleMutex); + ConsoleMutex = NULL; + ConsoleFramebuffer = NULL; + CloseHandle(GraphicsConsoleBuffer); + GraphicsConsoleBuffer = NULL; + DoubleVision = FALSE; +} + +static BOOL VgaEnterTextMode(PCOORD Resolution) +{ + DPRINT1("VgaEnterTextMode\n"); + + /* Switch to the text buffer */ + SetConsoleActiveScreenBuffer(TextConsoleBuffer); + + /* Adjust the text framebuffer if we changed the resolution */ + if (TextResolution.X != Resolution->X || + TextResolution.Y != Resolution->Y) + { + VgaDetachFromConsole(TRUE); + + /* + * VgaAttachToConsoleInternal sets TextResolution to the + * new resolution and updates ConsoleInfo. + */ + if (!VgaAttachToConsoleInternal(Resolution)) + { + DisplayMessage(L"An unexpected error occurred!\n"); + EmulatorTerminate(); + return FALSE; + } + } + else VgaUpdateCursorPosition(); + + /* The active framebuffer is now the text framebuffer */ + ConsoleFramebuffer = TextFramebuffer; + + /* + * Set the text mode palette. + * + * WARNING: This call should fail on Windows (and therefore + * we get the default palette and our external behaviour is + * just like Windows' one), but it should success on ReactOS + * (so that we get console palette changes even for text-mode + * screen-buffers, which is a new feature on ReactOS). + */ + SetConsolePalette(TextConsoleBuffer, + PaletteHandle, + SYSPAL_NOSTATIC256); + + /* Set the screen mode flag */ + ScreenMode = TEXT_MODE; + + return TRUE; +} + +static VOID VgaLeaveTextMode(VOID) +{ + /* Reset the active framebuffer */ + ConsoleFramebuffer = NULL; +} + +static VOID VgaChangeMode(VOID) +{ + COORD Resolution = VgaGetDisplayResolution(); + + if (ScreenMode == GRAPHICS_MODE) + { + /* Leave the current graphics mode */ + VgaLeaveGraphicsMode(); + } + else + { + /* Leave the current text mode */ + VgaLeaveTextMode(); + } + + /* Check if the new mode is alphanumeric */ + if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)) + { + /* Enter new text mode */ + if (!VgaEnterTextMode(&Resolution)) + { + DisplayMessage(L"An unexpected VGA error occurred while switching into text mode."); + EmulatorTerminate(); + return; + } + } + else + { + /* Enter graphics mode */ + if (!VgaEnterGraphicsMode(&Resolution)) + { + DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode."); + EmulatorTerminate(); + return; + } + } + + /* Trigger a full update of the screen */ + NeedsUpdate = TRUE; + UpdateRectangle.Left = 0; + UpdateRectangle.Top = 0; + UpdateRectangle.Right = Resolution.X; + UpdateRectangle.Bottom = Resolution.Y; + + /* Reset the mode change flag */ + ModeChanged = FALSE; +} + +static VOID VgaUpdateFramebuffer(VOID) +{ + SHORT i, j, k; + COORD Resolution = VgaGetDisplayResolution(); + DWORD AddressSize = VgaGetAddressSize(); + DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG], + VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG]); + DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2; + + /* + * If console framebuffer is NULL, that means something went wrong + * earlier and this is the final display refresh. + */ + if (ConsoleFramebuffer == NULL) return; + + /* Check if this is text mode or graphics mode */ + if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) + { + /* Graphics mode */ + PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer; + + /* + * Synchronize access to the graphics framebuffer + * with the console framebuffer mutex. + */ + WaitForSingleObject(ConsoleMutex, INFINITE); + + /* Loop through the scanlines */ + for (i = 0; i < Resolution.Y; i++) + { + /* Loop through the pixels */ + for (j = 0; j < Resolution.X; j++) + { + BYTE PixelData = 0; + + /* Check the shifting mode */ + if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256) + { + /* 4 bits shifted from each plane */ + + /* Check if this is 16 or 256 color mode */ + if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) + { + /* One byte per pixel */ + PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE + + (Address + (j / VGA_NUM_BANKS)) + * AddressSize]; + } + else + { + /* 4-bits per pixel */ + + PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE + + (Address + (j / (VGA_NUM_BANKS * 2))) + * AddressSize]; + + /* Check if we should use the highest 4 bits or lowest 4 */ + if (((j / VGA_NUM_BANKS) % 2) == 0) + { + /* Highest 4 */ + PixelData >>= 4; + } + else + { + /* Lowest 4 */ + PixelData &= 0x0F; + } + } + } + else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG) + { + /* Check if this is 16 or 256 color mode */ + if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) + { + // TODO: NOT IMPLEMENTED + DPRINT1("8-bit interleaved mode is not implemented!\n"); + } + else + { + /* + * 2 bits shifted from plane 0 and 2 for the first 4 pixels, + * then 2 bits shifted from plane 1 and 3 for the next 4 + */ + BYTE LowPlaneData = VgaMemory[((j / 4) % 2) * VGA_BANK_SIZE + + (Address + (j / 8)) * AddressSize]; + BYTE HighPlaneData = VgaMemory[(((j / 4) % 2) + 2) * VGA_BANK_SIZE + + (Address + (j / 8)) * AddressSize]; + + /* Extract the two bits from each plane */ + LowPlaneData = (LowPlaneData >> (6 - ((j % 4) * 2))) & 3; + HighPlaneData = (HighPlaneData >> (6 - ((j % 4) * 2))) & 3; + + /* Combine them into the pixel */ + PixelData = LowPlaneData | (HighPlaneData << 2); + } + } + else + { + /* 1 bit shifted from each plane */ + + /* Check if this is 16 or 256 color mode */ + if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) + { + /* 8 bits per pixel, 2 on each plane */ + + for (k = 0; k < VGA_NUM_BANKS; k++) + { + /* The data is on plane k, 4 pixels per byte */ + BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE + + (Address + (j / VGA_NUM_BANKS)) + * AddressSize]; + + /* The mask of the first bit in the pair */ + BYTE BitMask = 1 << (((3 - (j % VGA_NUM_BANKS)) * 2) + 1); + + /* Bits 0, 1, 2 and 3 come from the first bit of the pair */ + if (PlaneData & BitMask) PixelData |= 1 << k; + + /* Bits 4, 5, 6 and 7 come from the second bit of the pair */ + if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4); + } + } + else + { + /* 4 bits per pixel, 1 on each plane */ + + for (k = 0; k < VGA_NUM_BANKS; k++) + { + BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE + + (Address + (j / (VGA_NUM_BANKS * 2))) + * AddressSize]; + + /* If the bit on that plane is set, set it */ + if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k; + } + } + } + + if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)) + { + /* + * In 16 color mode, the value is an index to the AC registers + * if external palette access is disabled, otherwise (in case + * of palette loading) it is a blank pixel. + */ + PixelData = (VgaAcPalDisable ? VgaAcRegisters[PixelData & 0x0F] + : 0); + } + + /* Take into account DoubleVision mode when checking for pixel updates */ + if (DoubleVision) + { + /* Now check if the resulting pixel data has changed */ + if (GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] != PixelData) + { + /* Yes, write the new value */ + GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] = PixelData; + GraphicsBuffer[(i * Resolution.X * 4) + (j * 2 + 1)] = PixelData; + GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2)] = PixelData; + GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2 + 1)] = PixelData; + + /* Mark the specified pixel as changed */ + VgaMarkForUpdate(i, j); + } + } + else + { + /* Now check if the resulting pixel data has changed */ + if (GraphicsBuffer[i * Resolution.X + j] != PixelData) + { + /* Yes, write the new value */ + GraphicsBuffer[i * Resolution.X + j] = PixelData; + + /* Mark the specified pixel as changed */ + VgaMarkForUpdate(i, j); + } + } + } + + /* Move to the next scanline */ + Address += ScanlineSize; + } + + /* + * Release the console framebuffer mutex + * so that we allow for repainting. + */ + ReleaseMutex(ConsoleMutex); + } + else + { + /* Text mode */ + DWORD CurrentAddr; + PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer; + CHAR_CELL CharInfo; + + /* Loop through the scanlines */ + for (i = 0; i < Resolution.Y; i++) + { + /* Loop through the characters */ + for (j = 0; j < Resolution.X; j++) + { + CurrentAddr = LOWORD((Address + j) * AddressSize); + + /* Plane 0 holds the character itself */ + CharInfo.Char = VgaMemory[CurrentAddr]; + + /* Plane 1 holds the attribute */ + CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE]; + + /* Now check if the resulting character data has changed */ + if ((CharBuffer[i * Resolution.X + j].Char != CharInfo.Char) || + (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes)) + { + /* Yes, write the new value */ + CharBuffer[i * Resolution.X + j] = CharInfo; + + /* Mark the specified cell as changed */ + VgaMarkForUpdate(i, j); + } + } + + /* Move to the next scanline */ + Address += ScanlineSize; + } + } +} + +static VOID VgaUpdateTextCursor(VOID) +{ + COORD Position; + CONSOLE_CURSOR_INFO CursorInfo; + BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F; + BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F; + DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2; + BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F); + WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG], + VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]); + + /* Just return if we are not in text mode */ + if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) return; + + if (CursorStart < CursorEnd) + { + /* Visible cursor */ + CursorInfo.bVisible = TRUE; + CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize; + } + else + { + /* No cursor */ + CursorInfo.bVisible = FALSE; + CursorInfo.dwSize = 0; + } + + /* Add the cursor skew to the location */ + Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3; + + /* Find the coordinates of the new position */ + Position.X = (SHORT)(Location % ScanlineSize); + Position.Y = (SHORT)(Location / ScanlineSize); + + DPRINT1("VgaUpdateTextCursor: X = %d ; Y = %d\n", Position.X, Position.Y); + + /* Update the physical cursor */ + SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo); + SetConsoleCursorPosition(TextConsoleBuffer, Position); + + /* Reset the cursor move flag */ + CursorMoved = FALSE; +} + +static BYTE WINAPI VgaReadPort(ULONG Port) +{ + DPRINT("VgaReadPort: Port 0x%X\n", Port); + + switch (Port) + { + case VGA_MISC_READ: + return VgaMiscRegister; + + case VGA_INSTAT0_READ: + return 0; // Not implemented + + case VGA_INSTAT1_READ_MONO: + case VGA_INSTAT1_READ_COLOR: + { + BYTE Result = 0; + + /* Reset the AC latch */ + VgaAcLatch = FALSE; + + /* Set a flag if there is a vertical or horizontal retrace */ + if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD; + + /* Set an additional flag if there was a vertical retrace */ + if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE; + + /* Clear the flags */ + InHorizontalRetrace = InVerticalRetrace = FALSE; + + return Result; + } + + case VGA_FEATURE_READ: + return VgaFeatureRegister; + + case VGA_AC_INDEX: + return VgaAcIndex; + + case VGA_AC_READ: + return VgaAcRegisters[VgaAcIndex]; + + case VGA_SEQ_INDEX: + return VgaSeqIndex; + + case VGA_SEQ_DATA: + return VgaSeqRegisters[VgaSeqIndex]; + + case VGA_DAC_MASK: + return VgaDacMask; + + case VGA_DAC_READ_INDEX: + /* This returns the read/write state */ + return (VgaDacReadWrite ? 0 : 3); + + case VGA_DAC_WRITE_INDEX: + return (VgaDacIndex / 3); + + case VGA_DAC_DATA: + { + /* Ignore reads in write mode */ + if (!VgaDacReadWrite) + { + BYTE Data = VgaDacRegisters[VgaDacIndex++]; + VgaDacIndex %= VGA_PALETTE_SIZE; + return Data; + } + + break; + } + + case VGA_CRTC_INDEX_MONO: + case VGA_CRTC_INDEX_COLOR: + return VgaCrtcIndex; + + case VGA_CRTC_DATA_MONO: + case VGA_CRTC_DATA_COLOR: + return VgaCrtcRegisters[VgaCrtcIndex]; + + case VGA_GC_INDEX: + return VgaGcIndex; + + case VGA_GC_DATA: + return VgaGcRegisters[VgaGcIndex]; + + default: + DPRINT1("VgaReadPort: Unknown port 0x%X\n", Port); + break; + } + + return 0; +} + +static VOID WINAPI VgaWritePort(ULONG Port, BYTE Data) +{ + DPRINT("VgaWritePort: Port 0x%X, Data 0x%02X\n", Port, Data); + + switch (Port) + { + case VGA_MISC_WRITE: + { + VgaMiscRegister = Data; + + if (VgaMiscRegister & 0x01) + { + /* Color emulation */ + DPRINT1("Color emulation\n"); + + /* Register the new I/O Ports */ + RegisterIoPort(0x3D4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_COLOR + RegisterIoPort(0x3D5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_COLOR + RegisterIoPort(0x3DA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR + + /* Unregister the old ones */ + UnregisterIoPort(0x3B4); // VGA_CRTC_INDEX_MONO + UnregisterIoPort(0x3B5); // VGA_CRTC_DATA_MONO + UnregisterIoPort(0x3BA); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO + } + else + { + /* Monochrome emulation */ + DPRINT1("Monochrome emulation\n"); + + /* Register the new I/O Ports */ + RegisterIoPort(0x3B4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_MONO + RegisterIoPort(0x3B5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_MONO + RegisterIoPort(0x3BA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO + + /* Unregister the old ones */ + UnregisterIoPort(0x3D4); // VGA_CRTC_INDEX_COLOR + UnregisterIoPort(0x3D5); // VGA_CRTC_DATA_COLOR + UnregisterIoPort(0x3DA); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR + } + + // if (VgaMiscRegister & 0x02) { /* Enable RAM access */ } else { /* Disable RAM access */ } + break; + } + + case VGA_FEATURE_WRITE_MONO: + case VGA_FEATURE_WRITE_COLOR: + { + VgaFeatureRegister = Data; + break; + } + + case VGA_AC_INDEX: + // case VGA_AC_WRITE: + { + if (!VgaAcLatch) + { + /* Change the index */ + BYTE Index = Data & 0x1F; + if (Index < VGA_AC_MAX_REG) VgaAcIndex = Index; + + /* + * Change palette protection by checking for + * the Palette Address Source bit. + */ + VgaAcPalDisable = (Data & 0x20) ? TRUE : FALSE; + } + else + { + /* Write the data */ + VgaWriteAc(Data); + } + + /* Toggle the latch */ + VgaAcLatch = !VgaAcLatch; + break; + } + + case VGA_SEQ_INDEX: + { + /* Set the sequencer index register */ + if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data; + break; + } + + case VGA_SEQ_DATA: + { + /* Call the sequencer function */ + VgaWriteSequencer(Data); + break; + } + + case VGA_DAC_MASK: + { + VgaDacMask = Data; + break; + } + + case VGA_DAC_READ_INDEX: + { + VgaDacReadWrite = FALSE; + VgaDacIndex = Data * 3; + break; + } + + case VGA_DAC_WRITE_INDEX: + { + VgaDacReadWrite = TRUE; + VgaDacIndex = Data * 3; + break; + } + + case VGA_DAC_DATA: + { + /* Ignore writes in read mode */ + if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F); + break; + } + + case VGA_CRTC_INDEX_MONO: + case VGA_CRTC_INDEX_COLOR: + { + /* Set the CRTC index register */ + if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data; + break; + } + + case VGA_CRTC_DATA_MONO: + case VGA_CRTC_DATA_COLOR: + { + /* Call the CRTC function */ + VgaWriteCrtc(Data); + break; + } + + case VGA_GC_INDEX: + { + /* Set the GC index register */ + if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data; + break; + } + + case VGA_GC_DATA: + { + /* Call the GC function */ + VgaWriteGc(Data); + break; + } + + default: + DPRINT1("VgaWritePort: Unknown port 0x%X\n", Port); + break; + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +DWORD VgaGetVideoBaseAddress(VOID) +{ + return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03]; +} + +DWORD VgaGetVideoLimitAddress(VOID) +{ + return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03]; +} + +COORD VgaGetDisplayResolution(VOID) +{ + COORD Resolution; + BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F); + + /* The low 8 bits are in the display registers */ + Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG]; + Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG]; + + /* Set the top bits from the overflow register */ + if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8) + { + Resolution.Y |= 1 << 8; + } + if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9) + { + Resolution.Y |= 1 << 9; + } + + /* Increase the values by 1 */ + Resolution.X++; + Resolution.Y++; + + if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) + { + /* Multiply the horizontal resolution by the 9/8 dot mode */ + Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM) + ? 8 : 9; + + /* The horizontal resolution is halved in 8-bit mode */ + if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2; + } + + if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE) + { + /* Halve the vertical resolution */ + Resolution.Y >>= 1; + } + else + { + /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */ + Resolution.Y /= MaximumScanLine; + } + + /* Return the resolution */ + return Resolution; +} + +VOID VgaRefreshDisplay(VOID) +{ + HANDLE ConsoleBufferHandle = NULL; + COORD Resolution; + + /* Set the vertical retrace flag */ + InVerticalRetrace = TRUE; + + /* If nothing has changed, just return */ + // if (!ModeChanged && !CursorMoved && !PaletteChanged && !NeedsUpdate) + // return; + + /* Change the display mode */ + if (ModeChanged) VgaChangeMode(); + + /* Change the text cursor location */ + if (CursorMoved) VgaUpdateTextCursor(); + + /* Retrieve the current resolution */ + Resolution = VgaGetDisplayResolution(); + + if (PaletteChanged) + { + /* Trigger a full update of the screen */ + NeedsUpdate = TRUE; + UpdateRectangle.Left = 0; + UpdateRectangle.Top = 0; + UpdateRectangle.Right = Resolution.X; + UpdateRectangle.Bottom = Resolution.Y; + + PaletteChanged = FALSE; + } + + /* Update the contents of the framebuffer */ + VgaUpdateFramebuffer(); + + /* Ignore if there's nothing to update */ + if (!NeedsUpdate) return; + + DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n", + UpdateRectangle.Left, + UpdateRectangle.Top, + UpdateRectangle.Right, + UpdateRectangle.Bottom); + + /* Check if this is text mode or graphics mode */ + if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) + { + /* Graphics mode */ + ConsoleBufferHandle = GraphicsConsoleBuffer; + + /* In DoubleVision mode, scale the update rectangle */ + if (DoubleVision) + { + UpdateRectangle.Left *= 2; + UpdateRectangle.Top *= 2; + UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1; + UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1; + } + } + else + { + /* Text mode */ + ConsoleBufferHandle = TextConsoleBuffer; + } + + /* Redraw the screen */ + __InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle); + + /* Clear the update flag */ + NeedsUpdate = FALSE; +} + +VOID VgaHorizontalRetrace(VOID) +{ + /* Set the flag */ + InHorizontalRetrace = TRUE; +} + +VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size) +{ + DWORD i; + DWORD VideoAddress; + + DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size); + + /* Ignore if video RAM access is disabled */ + if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return; + + /* Loop through each byte */ + for (i = 0; i < Size; i++) + { + VideoAddress = VgaTranslateReadAddress(Address + i); + + /* Load the latch registers */ + VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)]; + VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)]; + VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)]; + VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)]; + + /* Copy the value to the buffer */ + Buffer[i] = VgaMemory[VideoAddress]; + } +} + +VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size) +{ + DWORD i, j; + DWORD VideoAddress; + + DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n", Address, Size); + + /* Ignore if video RAM access is disabled */ + if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return; + + /* Also ignore if write access to all planes is disabled */ + if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return; + + /* Loop through each byte */ + for (i = 0; i < Size; i++) + { + VideoAddress = VgaTranslateWriteAddress(Address + i); + + for (j = 0; j < VGA_NUM_BANKS; j++) + { + /* Make sure the page is writeable */ + if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue; + + /* Check if this is chain-4 mode */ + if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4) + { + if (((Address + i) & 3) != j) + { + /* This plane will not be accessed */ + continue; + } + } + + /* Check if this is odd-even mode */ + if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE) + { + if (((Address + i) & 1) != (j & 1)) + { + /* This plane will not be accessed */ + continue; + } + } + + /* Copy the value to the VGA memory */ + VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j); + } + } +} + +VOID VgaClearMemory(VOID) +{ + ZeroMemory(VgaMemory, sizeof(VgaMemory)); +} + +VOID VgaResetPalette(VOID) +{ + PALETTEENTRY Entries[VGA_MAX_COLORS]; + + /* Restore the default palette */ + VgaRestoreDefaultPalette(Entries, VGA_MAX_COLORS); + SetPaletteEntries(PaletteHandle, 0, VGA_MAX_COLORS, Entries); + PaletteChanged = TRUE; +} + +BOOLEAN VgaInitialize(HANDLE TextHandle) +{ + /* Save the default text-mode console output handle */ + if (!IsConsoleHandle(TextHandle)) return FALSE; + TextConsoleBuffer = TextHandle; + + /* Save the original cursor and console screen buffer information */ + if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) || + !GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo)) + { + return FALSE; + } + ConsoleInfo = OrgConsoleBufferInfo; + + /* Initialize the VGA palette and fail if it isn't successfully created */ + if (!VgaInitializePalette()) return FALSE; + /***/ VgaResetPalette(); /***/ + + /* Switch to the text buffer */ + SetConsoleActiveScreenBuffer(TextConsoleBuffer); + + /* Clear the VGA memory */ + VgaClearMemory(); + + /* Register the I/O Ports */ + RegisterIoPort(0x3CC, VgaReadPort, NULL); // VGA_MISC_READ + RegisterIoPort(0x3C2, VgaReadPort, VgaWritePort); // VGA_MISC_WRITE, VGA_INSTAT0_READ + RegisterIoPort(0x3CA, VgaReadPort, NULL); // VGA_FEATURE_READ + RegisterIoPort(0x3C0, VgaReadPort, VgaWritePort); // VGA_AC_INDEX, VGA_AC_WRITE + RegisterIoPort(0x3C1, VgaReadPort, NULL); // VGA_AC_READ + RegisterIoPort(0x3C4, VgaReadPort, VgaWritePort); // VGA_SEQ_INDEX + RegisterIoPort(0x3C5, VgaReadPort, VgaWritePort); // VGA_SEQ_DATA + RegisterIoPort(0x3C6, VgaReadPort, VgaWritePort); // VGA_DAC_MASK + RegisterIoPort(0x3C7, VgaReadPort, VgaWritePort); // VGA_DAC_READ_INDEX + RegisterIoPort(0x3C8, VgaReadPort, VgaWritePort); // VGA_DAC_WRITE_INDEX + RegisterIoPort(0x3C9, VgaReadPort, VgaWritePort); // VGA_DAC_DATA + RegisterIoPort(0x3CE, VgaReadPort, VgaWritePort); // VGA_GC_INDEX + RegisterIoPort(0x3CF, VgaReadPort, VgaWritePort); // VGA_GC_DATA + + /* Return success */ + return TRUE; +} + +VOID VgaCleanup(VOID) +{ + if (ScreenMode == GRAPHICS_MODE) + { + /* Leave the current graphics mode */ + VgaLeaveGraphicsMode(); + } + else + { + /* Leave the current text mode */ + VgaLeaveTextMode(); + } + + VgaDetachFromConsole(FALSE); + + CloseHandle(AnotherEvent); + CloseHandle(EndEvent); + CloseHandle(StartEvent); + +#if 0 + RegisterConsoleVDM = NULL; + FreeLibrary(hKernel32); +#endif +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/hardware/vga.h b/reactos/subsystems/ntvdm/hardware/vga.h new file mode 100644 index 0000000000000..1f142c175a8d5 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/vga.h @@ -0,0 +1,270 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vga.h + * PURPOSE: VGA hardware emulation + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _VGA_H_ +#define _VGA_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define VGA_NUM_BANKS 4 +#define VGA_BANK_SIZE 0x10000 +#define VGA_MAX_COLORS 256 +#define VGA_PALETTE_SIZE (VGA_MAX_COLORS * 3) +#define VGA_BITMAP_INFO_SIZE (sizeof(BITMAPINFOHEADER) + 2 * (VGA_PALETTE_SIZE / 3)) +#define VGA_MINIMUM_WIDTH 400 +#define VGA_MINIMUM_HEIGHT 300 +#define VGA_DAC_TO_COLOR(x) (((x) << 2) | ((x) >> 4)) +#define VGA_COLOR_TO_DAC(x) ((x) >> 2) + + + +/* Register I/O ports */ + +#define VGA_MISC_READ 0x3CC +#define VGA_MISC_WRITE 0x3C2 + +#define VGA_INSTAT0_READ 0x3C2 + +#define VGA_INSTAT1_READ_MONO 0x3BA +#define VGA_INSTAT1_READ_COLOR 0x3DA + +#define VGA_FEATURE_READ 0x3CA +#define VGA_FEATURE_WRITE_MONO 0x3BA +#define VGA_FEATURE_WRITE_COLOR 0x3DA + +#define VGA_AC_INDEX 0x3C0 +#define VGA_AC_WRITE 0x3C0 +#define VGA_AC_READ 0x3C1 + +#define VGA_SEQ_INDEX 0x3C4 +#define VGA_SEQ_DATA 0x3C5 + +#define VGA_DAC_MASK 0x3C6 +#define VGA_DAC_READ_INDEX 0x3C7 +#define VGA_DAC_WRITE_INDEX 0x3C8 +#define VGA_DAC_DATA 0x3C9 + +#define VGA_CRTC_INDEX_MONO 0x3B4 +#define VGA_CRTC_DATA_MONO 0x3B5 +#define VGA_CRTC_INDEX_COLOR 0x3D4 +#define VGA_CRTC_DATA_COLOR 0x3D5 + +#define VGA_GC_INDEX 0x3CE +#define VGA_GC_DATA 0x3CF + + + +// +// Miscellaneous and Status Registers +// + +/* Miscellaneous register bits */ +#define VGA_MISC_COLOR (1 << 0) +#define VGA_MISC_RAM_ENABLED (1 << 1) +// #define VGA_MISC_CSEL1 (1 << 2) +// #define VGA_MISC_CSEL2 (1 << 3) +#define VGA_MISC_OE_INVERT (1 << 5) +#define VGA_MISC_HSYNCP (1 << 6) +#define VGA_MISC_VSYNCP (1 << 7) + +/* Status register flags */ +#define VGA_STAT_DD (1 << 0) +#define VGA_STAT_VRETRACE (1 << 3) + + +// +// Sequencer Registers +// + +/* Sequencer reset register bits */ +#define VGA_SEQ_RESET_AR (1 << 0) +#define VGA_SEQ_RESET_SR (1 << 1) + +/* Sequencer clock register bits */ +#define VGA_SEQ_CLOCK_98DM (1 << 0) +#define VGA_SEQ_CLOCK_SLR (1 << 2) +#define VGA_SEQ_CLOCK_DCR (1 << 3) +#define VGA_SEQ_CLOCK_S4 (1 << 4) +#define VGA_SEQ_CLOCK_SD (1 << 5) + +/* Sequencer memory register bits */ +#define VGA_SEQ_MEM_EXT (1 << 1) +#define VGA_SEQ_MEM_OE (1 << 2) +#define VGA_SEQ_MEM_C4 (1 << 3) + +enum +{ + VGA_SEQ_RESET_REG, + VGA_SEQ_CLOCK_REG, + VGA_SEQ_MASK_REG, + VGA_SEQ_CHAR_REG, + VGA_SEQ_MEM_REG, + VGA_SEQ_MAX_REG +}; + + +// +// CRT Controller Registers +// + +/* CRTC overflow register bits */ +#define VGA_CRTC_OVERFLOW_VT8 (1 << 0) +#define VGA_CRTC_OVERFLOW_VDE8 (1 << 1) +#define VGA_CRTC_OVERFLOW_VRS8 (1 << 2) +#define VGA_CRTC_OVERFLOW_SVB8 (1 << 3) +#define VGA_CRTC_OVERFLOW_LC8 (1 << 4) +#define VGA_CRTC_OVERFLOW_VT9 (1 << 5) +#define VGA_CRTC_OVERFLOW_VDE9 (1 << 6) +#define VGA_CRTC_OVERFLOW_VRS9 (1 << 7) + +/* CRTC underline register bits */ +#define VGA_CRTC_UNDERLINE_DWORD (1 << 6) + +/* CRTC max scanline register bits */ +#define VGA_CRTC_MAXSCANLINE_DOUBLE (1 << 7) + +/* CRTC mode control register bits */ +#define VGA_CRTC_MODE_CONTROL_WRAP (1 << 5) +#define VGA_CRTC_MODE_CONTROL_BYTE (1 << 6) +#define VGA_CRTC_MODE_CONTROL_SYNC (1 << 7) + +enum +{ + VGA_CRTC_HORZ_TOTAL_REG, + VGA_CRTC_END_HORZ_DISP_REG, + VGA_CRTC_START_HORZ_BLANKING_REG, + VGA_CRTC_END_HORZ_BLANKING_REG, + VGA_CRTC_START_HORZ_RETRACE_REG, + VGA_CRTC_END_HORZ_RETRACE_REG, + VGA_CRTC_VERT_TOTAL_REG, + VGA_CRTC_OVERFLOW_REG, + VGA_CRTC_PRESET_ROW_SCAN_REG, + VGA_CRTC_MAX_SCAN_LINE_REG, + VGA_CRTC_CURSOR_START_REG, + VGA_CRTC_CURSOR_END_REG, + VGA_CRTC_START_ADDR_HIGH_REG, + VGA_CRTC_START_ADDR_LOW_REG, + VGA_CRTC_CURSOR_LOC_HIGH_REG, + VGA_CRTC_CURSOR_LOC_LOW_REG, + VGA_CRTC_VERT_RETRACE_START_REG, + VGA_CRTC_VERT_RETRACE_END_REG, + VGA_CRTC_VERT_DISP_END_REG, + VGA_CRTC_OFFSET_REG, + VGA_CRTC_UNDERLINE_REG, + VGA_CRTC_START_VERT_BLANKING_REG, + VGA_CRTC_END_VERT_BLANKING, + VGA_CRTC_MODE_CONTROL_REG, + VGA_CRTC_LINE_COMPARE_REG, + VGA_CRTC_MAX_REG +}; + + +// +// Graphics Controller Registers +// + +/* Graphics controller mode register bits */ +#define VGA_GC_MODE_READ (1 << 3) +#define VGA_GC_MODE_OE (1 << 4) +#define VGA_GC_MODE_SHIFTREG (1 << 5) +#define VGA_GC_MODE_SHIFT256 (1 << 6) + +/* Graphics controller miscellaneous register bits */ +#define VGA_GC_MISC_NOALPHA (1 << 0) +#define VGA_GC_MISC_OE (1 << 1) + +enum +{ + VGA_GC_RESET_REG, + VGA_GC_ENABLE_RESET_REG, + VGA_GC_COLOR_COMPARE_REG, + VGA_GC_ROTATE_REG, + VGA_GC_READ_MAP_SEL_REG, + VGA_GC_MODE_REG, + VGA_GC_MISC_REG, + VGA_GC_COLOR_IGNORE_REG, + VGA_GC_BITMASK_REG, + VGA_GC_MAX_REG +}; + + +// +// Attribute Controller Registers +// They are a relinquish of the CGA/EGA era. +// + +/* AC mode control register bits */ +#define VGA_AC_CONTROL_ATGE (1 << 0) +#define VGA_AC_CONTROL_MONO (1 << 1) +#define VGA_AC_CONTROL_LGE (1 << 2) +#define VGA_AC_CONTROL_BLINK (1 << 3) +#define VGA_AC_CONTROL_PPM (1 << 5) +#define VGA_AC_CONTROL_8BIT (1 << 6) +#define VGA_AC_CONTROL_P54S (1 << 7) + +enum +{ + VGA_AC_PAL_0_REG, + VGA_AC_PAL_1_REG, + VGA_AC_PAL_2_REG, + VGA_AC_PAL_3_REG, + VGA_AC_PAL_4_REG, + VGA_AC_PAL_5_REG, + VGA_AC_PAL_6_REG, + VGA_AC_PAL_7_REG, + VGA_AC_PAL_8_REG, + VGA_AC_PAL_9_REG, + VGA_AC_PAL_A_REG, + VGA_AC_PAL_B_REG, + VGA_AC_PAL_C_REG, + VGA_AC_PAL_D_REG, + VGA_AC_PAL_E_REG, + VGA_AC_PAL_F_REG, + VGA_AC_CONTROL_REG, + VGA_AC_OVERSCAN_REG, + VGA_AC_COLOR_PLANE_REG, + VGA_AC_HORZ_PANNING_REG, + VGA_AC_COLOR_SEL_REG, + VGA_AC_MAX_REG +}; + + +typedef struct _VGA_REGISTERS +{ + UCHAR Misc; + UCHAR Sequencer[VGA_SEQ_MAX_REG]; + UCHAR CRT[VGA_CRTC_MAX_REG]; + UCHAR Graphics[VGA_GC_MAX_REG]; + UCHAR Attribute[VGA_AC_MAX_REG]; +} VGA_REGISTERS, *PVGA_REGISTERS; + + +/* FUNCTIONS ******************************************************************/ + +BOOL VgaAttachToConsole(VOID); +VOID VgaDetachFromConsole(BOOL ChangeMode); + +DWORD VgaGetVideoBaseAddress(VOID); +DWORD VgaGetVideoLimitAddress(VOID); +COORD VgaGetDisplayResolution(VOID); +VOID VgaRefreshDisplay(VOID); +VOID VgaHorizontalRetrace(VOID); +VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size); +VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size); +VOID VgaClearMemory(VOID); + +BOOLEAN VgaInitialize(HANDLE TextHandle); +VOID VgaCleanup(VOID); + +#endif // _VGA_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/int32.c b/reactos/subsystems/ntvdm/int32.c new file mode 100644 index 0000000000000..b89eb137e03c5 --- /dev/null +++ b/reactos/subsystems/ntvdm/int32.c @@ -0,0 +1,131 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: int32.c + * PURPOSE: 32-bit Interrupt Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +// #define NDEBUG + +#include "emulator.h" +#include "int32.h" + +#include "bop.h" + +/* PRIVATE VARIABLES **********************************************************/ + +/* + * This is the list of registered 32-bit Interrupt handlers. + */ +EMULATOR_INT32_PROC Int32Proc[EMULATOR_MAX_INT32_NUM] = { NULL }; + +/* BOP Identifiers */ +#define BOP_CONTROL 0xFF // Control BOP Handler + #define BOP_CONTROL_DEFFUNC 0x00 // Default Control BOP Function + +/* 32-bit Interrupt dispatcher function code for the Control BOP Handler */ +#define BOP_CONTROL_INT32 0xFF + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID WINAPI Int32Dispatch(LPWORD Stack) +{ + /* Get the interrupt number */ + BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]); + + /* Call the 32-bit Interrupt handler */ + if (Int32Proc[IntNum] != NULL) + Int32Proc[IntNum](Stack); + else + DPRINT("Unhandled 32-bit interrupt: 0x%02X, AX = 0x%04X\n", IntNum, getAX()); +} + +VOID WINAPI ControlBop(LPWORD Stack) +{ + /* Get the Function Number and skip it */ + BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); + setIP(getIP() + 1); + + if (FuncNum == BOP_CONTROL_INT32) + Int32Dispatch(Stack); + else + DPRINT("Unassigned Control BOP Function: 0x%02X\n", FuncNum); +} + +VOID InitializeInt32(WORD BiosSegment) +{ + LPDWORD IntVecTable = (LPDWORD)BaseAddress; + LPBYTE BiosCode = (LPBYTE)SEG_OFF_TO_PTR(BiosSegment, 0); + USHORT i; + WORD BopSeqOffset, Offset = 0; + + /* Generate ISR stubs and fill the IVT */ + for (i = 0x00; i <= 0xFF; i++) + { + Offset = INT_HANDLER_OFFSET + (i << 4); + IntVecTable[i] = MAKELONG(Offset, BiosSegment); + + BiosCode[Offset++] = 0xFA; // cli + + BiosCode[Offset++] = 0x6A; // push i + BiosCode[Offset++] = (UCHAR)i; + + /* The counter variable (initialized to 0) */ + BiosCode[Offset++] = 0x6A; // push 0 + BiosCode[Offset++] = 0x00; + + /* Stack variables */ + BiosCode[Offset++] = 0x83; // sub sp, 4 + BiosCode[Offset++] = 0xEC; + BiosCode[Offset++] = 0x04; + + BopSeqOffset = COMMON_STUB_OFFSET - (Offset + 3); + + BiosCode[Offset++] = 0xE9; // jmp near BOP_SEQ + BiosCode[Offset++] = LOBYTE(BopSeqOffset); + BiosCode[Offset++] = HIBYTE(BopSeqOffset); + } + + /* Write the common stub code */ + Offset = COMMON_STUB_OFFSET; + +// BOP_SEQ: + BiosCode[Offset++] = 0xF8; // clc + + BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence + BiosCode[Offset++] = HIBYTE(EMULATOR_BOP); + BiosCode[Offset++] = BOP_CONTROL; // Control BOP + BiosCode[Offset++] = BOP_CONTROL_INT32; // 32-bit Interrupt dispatcher + + BiosCode[Offset++] = 0x73; // jnc EXIT (offset +4) + BiosCode[Offset++] = 0x04; + + BiosCode[Offset++] = 0xFB; // sti + + // HACK: The following instruction should be HLT! + BiosCode[Offset++] = 0x90; // nop + + BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -11) + BiosCode[Offset++] = 0xF5; + +// EXIT: + BiosCode[Offset++] = 0x83; // add sp, 8 + BiosCode[Offset++] = 0xC4; + BiosCode[Offset++] = 0x08; + + BiosCode[Offset++] = 0xCF; // iret + + /* Register the Control BOP */ + RegisterBop(BOP_CONTROL, ControlBop); +} + +VOID RegisterInt32(BYTE IntNumber, EMULATOR_INT32_PROC IntHandler) +{ + Int32Proc[IntNumber] = IntHandler; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/int32.h b/reactos/subsystems/ntvdm/int32.h new file mode 100644 index 0000000000000..d6cced23348c8 --- /dev/null +++ b/reactos/subsystems/ntvdm/int32.h @@ -0,0 +1,31 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: int32.h + * PURPOSE: 32-bit Interrupt Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _INT32_H_ +#define _INT32_H_ + +/* DEFINES ********************************************************************/ + +/* 32-bit Interrupt Identifiers */ +#define EMULATOR_MAX_INT32_NUM 0xFF + 1 + +#define INT_HANDLER_OFFSET 0x1000 +#define COMMON_STUB_OFFSET 0x2000 + +/* FUNCTIONS ******************************************************************/ + +typedef VOID (WINAPI *EMULATOR_INT32_PROC)(LPWORD Stack); + +VOID WINAPI Int32Dispatch(LPWORD Stack); +VOID InitializeInt32(WORD BiosSegment); +VOID RegisterInt32(BYTE IntNumber, EMULATOR_INT32_PROC IntHandler); + +#endif // _INT32_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/io.c b/reactos/subsystems/ntvdm/io.c new file mode 100644 index 0000000000000..9a85a114cf563 --- /dev/null +++ b/reactos/subsystems/ntvdm/io.c @@ -0,0 +1,605 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: io.c + * PURPOSE: I/O Port Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "io.h" + +/* PRIVATE VARIABLES **********************************************************/ + +typedef struct _EMULATOR_IO_HANDLERS +{ + EMULATOR_INB_PROC InB; + EMULATOR_INW_PROC InW; + EMULATOR_IND_PROC InD; + + EMULATOR_INSB_PROC InsB; + EMULATOR_INSW_PROC InsW; + EMULATOR_INSD_PROC InsD; + + EMULATOR_OUTB_PROC OutB; + EMULATOR_OUTW_PROC OutW; + EMULATOR_OUTD_PROC OutD; + + EMULATOR_OUTSB_PROC OutsB; + EMULATOR_OUTSW_PROC OutsW; + EMULATOR_OUTSD_PROC OutsD; +} EMULATOR_IO_HANDLERS, *PEMULATOR_IO_HANDLERS; + +typedef struct _EMULATOR_IOPORT_HANDLERS +{ + HANDLE hVdd; // == 0 if unused, + // INVALID_HANDLE_VALUE if handled internally, + // a valid VDD handle if handled externally. + union + { + /* For Windows compatibility only, not used internally... */ + VDD_IO_HANDLERS VddIoHandlers; + + /* ... we use these members internally */ + EMULATOR_IO_HANDLERS IoHandlers; + }; +} EMULATOR_IOPORT_HANDLERS, *PEMULATOR_IOPORT_HANDLERS; + +/* + * This is the list of registered I/O Port handlers. + */ +EMULATOR_IOPORT_HANDLERS IoPortProc[EMULATOR_MAX_IOPORTS_NUM] = {{NULL}}; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +UCHAR +IOReadB(ULONG Port) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.InB) + { + return IoPortProc[Port].IoHandlers.InB(Port); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.inb_handler) + { + UCHAR Data; + ASSERT(Port <= MAXWORD); + IoPortProc[Port].VddIoHandlers.inb_handler((WORD)Port, &Data); + return Data; + } + else + { + /* Return an empty port byte value */ + DPRINT("Read from unknown port: 0x%X\n", Port); + return 0xFF; + } +} + +VOID +IOReadStrB(ULONG Port, + PUCHAR Buffer, + ULONG Count) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.InsB) + { + IoPortProc[Port].IoHandlers.InsB(Port, Buffer, Count); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.insb_handler) + { + ASSERT(Port <= MAXWORD); + ASSERT(Count <= MAXWORD); + IoPortProc[Port].VddIoHandlers.insb_handler((WORD)Port, Buffer, (WORD)Count); + } + else + { + while (Count--) + *Buffer++ = IOReadB(Port); + } +} + +VOID +IOWriteB(ULONG Port, + UCHAR Buffer) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.OutB) + { + IoPortProc[Port].IoHandlers.OutB(Port, Buffer); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.outb_handler) + { + ASSERT(Port <= MAXWORD); + IoPortProc[Port].VddIoHandlers.outb_handler((WORD)Port, Buffer); + } + else + { + /* Do nothing */ + DPRINT("Write to unknown port: 0x%X\n", Port); + } +} + +VOID +IOWriteStrB(ULONG Port, + PUCHAR Buffer, + ULONG Count) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.OutsB) + { + IoPortProc[Port].IoHandlers.OutsB(Port, Buffer, Count); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.outsb_handler) + { + ASSERT(Port <= MAXWORD); + ASSERT(Count <= MAXWORD); + IoPortProc[Port].VddIoHandlers.outsb_handler((WORD)Port, Buffer, (WORD)Count); + } + else + { + while (Count--) IOWriteB(Port, *Buffer++); + } +} + +USHORT +IOReadW(ULONG Port) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.InW) + { + return IoPortProc[Port].IoHandlers.InW(Port); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.inw_handler) + { + USHORT Data; + ASSERT(Port <= MAXWORD); + IoPortProc[Port].VddIoHandlers.inw_handler((WORD)Port, &Data); + return Data; + } + else + { + UCHAR Low, High; + + // FIXME: Is it ok on Little endian and Big endian ?? + Low = IOReadB(Port); + High = IOReadB(Port + sizeof(UCHAR)); + return MAKEWORD(Low, High); + } +} + +VOID +IOReadStrW(ULONG Port, + PUSHORT Buffer, + ULONG Count) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.InsW) + { + IoPortProc[Port].IoHandlers.InsW(Port, Buffer, Count); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.insw_handler) + { + ASSERT(Port <= MAXWORD); + ASSERT(Count <= MAXWORD); + IoPortProc[Port].VddIoHandlers.insw_handler((WORD)Port, Buffer, (WORD)Count); + } + else + { + while (Count--) + *Buffer++ = IOReadW(Port); + } +} + +VOID +IOWriteW(ULONG Port, + USHORT Buffer) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.OutW) + { + IoPortProc[Port].IoHandlers.OutW(Port, Buffer); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.outw_handler) + { + ASSERT(Port <= MAXWORD); + IoPortProc[Port].VddIoHandlers.outw_handler((WORD)Port, Buffer); + } + else + { + // FIXME: Is it ok on Little endian and Big endian ?? + IOWriteB(Port, LOBYTE(Buffer)); + IOWriteB(Port + sizeof(UCHAR), HIBYTE(Buffer)); + } +} + +VOID +IOWriteStrW(ULONG Port, + PUSHORT Buffer, + ULONG Count) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.OutsW) + { + IoPortProc[Port].IoHandlers.OutsW(Port, Buffer, Count); + } + else if (IoPortProc[Port].hVdd > 0 && + IoPortProc[Port].VddIoHandlers.outsw_handler) + { + ASSERT(Port <= MAXWORD); + ASSERT(Count <= MAXWORD); + IoPortProc[Port].VddIoHandlers.outsw_handler((WORD)Port, Buffer, (WORD)Count); + } + else + { + while (Count--) IOWriteW(Port, *Buffer++); + } +} + +ULONG +IOReadD(ULONG Port) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.InD) + { + return IoPortProc[Port].IoHandlers.InD(Port); + } + else + { + USHORT Low, High; + + // FIXME: Is it ok on Little endian and Big endian ?? + Low = IOReadW(Port); + High = IOReadW(Port + sizeof(USHORT)); + return MAKELONG(Low, High); + } +} + +VOID +IOReadStrD(ULONG Port, + PULONG Buffer, + ULONG Count) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.InsD) + { + IoPortProc[Port].IoHandlers.InsD(Port, Buffer, Count); + } + else + { + while (Count--) + *Buffer++ = IOReadD(Port); + } +} + +VOID +IOWriteD(ULONG Port, + ULONG Buffer) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.OutD) + { + IoPortProc[Port].IoHandlers.OutD(Port, Buffer); + } + else + { + // FIXME: Is it ok on Little endian and Big endian ?? + IOWriteW(Port, LOWORD(Buffer)); + IOWriteW(Port + sizeof(USHORT), HIWORD(Buffer)); + } +} + +VOID +IOWriteStrD(ULONG Port, + PULONG Buffer, + ULONG Count) +{ + if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE && + IoPortProc[Port].IoHandlers.OutsD) + { + IoPortProc[Port].IoHandlers.OutsD(Port, Buffer, Count); + } + else + { + while (Count--) IOWriteD(Port, *Buffer++); + } +} + + +VOID RegisterIoPort(ULONG Port, + EMULATOR_INB_PROC InHandler, + EMULATOR_OUTB_PROC OutHandler) +{ + if (IoPortProc[Port].IoHandlers.InB == NULL) + IoPortProc[Port].IoHandlers.InB = InHandler; + else + DPRINT1("IoPortProc[0x%X].IoHandlers.InB already registered\n", Port); + + if (IoPortProc[Port].IoHandlers.OutB == NULL) + IoPortProc[Port].IoHandlers.OutB = OutHandler; + else + DPRINT1("IoPortProc[0x%X].IoHandlers.OutB already registered\n", Port); + + /* We hold the I/O port internally */ + IoPortProc[Port].hVdd = INVALID_HANDLE_VALUE; +} + +VOID UnregisterIoPort(ULONG Port) +{ + /* + * Put automagically all the fields to zero: + * the hVdd gets unregistered as well as all the handlers. + */ + // IoPortProc[Port] = {NULL}; + ZeroMemory(&IoPortProc[Port], sizeof(IoPortProc[Port])); +} + +VOID WINAPI +EmulatorReadIo(PFAST486_STATE State, + ULONG Port, + PVOID Buffer, + ULONG DataCount, + UCHAR DataSize) +{ + UNREFERENCED_PARAMETER(State); + + if (DataSize == 0 || DataCount == 0) return; + + if (DataSize == sizeof(UCHAR)) + { + if (DataCount == 1) + *(PUCHAR)Buffer = IOReadB(Port); + else + IOReadStrB(Port, Buffer, DataCount); + } + else if (DataSize == sizeof(USHORT)) + { + if (DataCount == 1) + *(PUSHORT)Buffer = IOReadW(Port); + else + IOReadStrW(Port, Buffer, DataCount); + } + else if (DataSize == sizeof(ULONG)) + { + if (DataCount == 1) + *(PULONG)Buffer = IOReadD(Port); + else + IOReadStrD(Port, Buffer, DataCount); + } + else + { + PBYTE Address = (PBYTE)Buffer; + + while (DataCount--) + { + ULONG CurrentPort = Port; + ULONG Count; + UCHAR NewDataSize = DataSize; + + /* Read dword */ + Count = NewDataSize / sizeof(ULONG); + NewDataSize = NewDataSize % sizeof(ULONG); + while (Count--) + { + *(PULONG)Address = IOReadD(CurrentPort); + CurrentPort += sizeof(ULONG); + Address += sizeof(ULONG); + } + + /* Read word */ + Count = NewDataSize / sizeof(USHORT); + NewDataSize = NewDataSize % sizeof(USHORT); + while (Count--) + { + *(PUSHORT)Address = IOReadW(CurrentPort); + CurrentPort += sizeof(USHORT); + Address += sizeof(USHORT); + } + + /* Read byte */ + Count = NewDataSize / sizeof(UCHAR); + NewDataSize = NewDataSize % sizeof(UCHAR); + while (Count--) + { + *(PUCHAR)Address = IOReadB(CurrentPort); + CurrentPort += sizeof(UCHAR); + Address += sizeof(UCHAR); + } + + ASSERT(Count == 0); + ASSERT(NewDataSize == 0); + } + } +} + +VOID WINAPI +EmulatorWriteIo(PFAST486_STATE State, + ULONG Port, + PVOID Buffer, + ULONG DataCount, + UCHAR DataSize) +{ + UNREFERENCED_PARAMETER(State); + + if (DataSize == 0 || DataCount == 0) return; + + if (DataSize == sizeof(UCHAR)) + { + if (DataCount == 1) + IOWriteB(Port, *(PUCHAR)Buffer); + else + IOWriteStrB(Port, Buffer, DataCount); + } + else if (DataSize == sizeof(USHORT)) + { + if (DataCount == 1) + IOWriteW(Port, *(PUSHORT)Buffer); + else + IOWriteStrW(Port, Buffer, DataCount); + } + else if (DataSize == sizeof(ULONG)) + { + if (DataCount == 1) + IOWriteD(Port, *(PULONG)Buffer); + else + IOWriteStrD(Port, Buffer, DataCount); + } + else + { + PBYTE Address = (PBYTE)Buffer; + + while (DataCount--) + { + ULONG CurrentPort = Port; + ULONG Count; + UCHAR NewDataSize = DataSize; + + /* Write dword */ + Count = NewDataSize / sizeof(ULONG); + NewDataSize = NewDataSize % sizeof(ULONG); + while (Count--) + { + IOWriteD(CurrentPort, *(PULONG)Address); + CurrentPort += sizeof(ULONG); + Address += sizeof(ULONG); + } + + /* Write word */ + Count = NewDataSize / sizeof(USHORT); + NewDataSize = NewDataSize % sizeof(USHORT); + while (Count--) + { + IOWriteW(CurrentPort, *(PUSHORT)Address); + CurrentPort += sizeof(USHORT); + Address += sizeof(USHORT); + } + + /* Write byte */ + Count = NewDataSize / sizeof(UCHAR); + NewDataSize = NewDataSize % sizeof(UCHAR); + while (Count--) + { + IOWriteB(CurrentPort, *(PUCHAR)Address); + CurrentPort += sizeof(UCHAR); + Address += sizeof(UCHAR); + } + + ASSERT(Count == 0); + ASSERT(NewDataSize == 0); + } + } +} + + + +BOOL +WINAPI +VDDInstallIOHook(HANDLE hVdd, + WORD cPortRange, + PVDD_IO_PORTRANGE pPortRange, + PVDD_IO_HANDLERS IOhandler) +{ + /* Check possible validity of the VDD handle */ + if (hVdd == 0 || hVdd == INVALID_HANDLE_VALUE) return FALSE; + + /* Loop for each range of I/O ports */ + while (cPortRange--) + { + WORD i; + + /* Register the range of I/O ports */ + for (i = pPortRange->First; i <= pPortRange->Last; ++i) + { + /* + * Don't do anything if the I/O port is already + * handled internally or externally. + */ + if (IoPortProc[i].hVdd != 0) + { + DPRINT1("IoPortProc[0x%X] already registered\n", i); + continue; + } + + /* Register wrt. the VDD */ + IoPortProc[i].hVdd = hVdd; + + /* Disable the internal handlers */ + IoPortProc[i].IoHandlers.InB = NULL; + IoPortProc[i].IoHandlers.InW = NULL; + IoPortProc[i].IoHandlers.InD = NULL; + + IoPortProc[i].IoHandlers.InsB = NULL; + IoPortProc[i].IoHandlers.InsW = NULL; + IoPortProc[i].IoHandlers.InsD = NULL; + + IoPortProc[i].IoHandlers.OutB = NULL; + IoPortProc[i].IoHandlers.OutW = NULL; + IoPortProc[i].IoHandlers.OutD = NULL; + + IoPortProc[i].IoHandlers.OutsB = NULL; + IoPortProc[i].IoHandlers.OutsW = NULL; + IoPortProc[i].IoHandlers.OutsD = NULL; + + /* Save our handlers */ + IoPortProc[i].VddIoHandlers = *IOhandler; + } + + /* Go to the next range */ + ++pPortRange; + ++IOhandler; + } + + return TRUE; +} + +VOID +WINAPI +VDDDeInstallIOHook(HANDLE hVdd, + WORD cPortRange, + PVDD_IO_PORTRANGE pPortRange) +{ + /* Check possible validity of the VDD handle */ + if (hVdd == 0 || hVdd == INVALID_HANDLE_VALUE) return; + + /* Loop for each range of I/O ports */ + while (cPortRange--) + { + WORD i; + + /* Unregister the range of I/O ports */ + for (i = pPortRange->First; i <= pPortRange->Last; ++i) + { + /* + * Don't do anything if we don't own the I/O port. + */ + if (IoPortProc[i].hVdd != hVdd) + { + DPRINT1("IoPortProc[0x%X] owned by somebody else\n", i); + continue; + } + + /* + * Put automagically all the fields to zero: + * the hVdd gets unregistered as well as all the handlers. + */ + // IoPortProc[i] = {NULL}; + ZeroMemory(&IoPortProc[i], sizeof(IoPortProc[i])); + } + + /* Go to the next range */ + ++pPortRange; + } +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/io.h b/reactos/subsystems/ntvdm/io.h new file mode 100644 index 0000000000000..9ed80ec794b8b --- /dev/null +++ b/reactos/subsystems/ntvdm/io.h @@ -0,0 +1,108 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: io.c + * PURPOSE: I/O Port Handlers + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _IO_H_ +#define _IO_H_ + +/* DEFINES ********************************************************************/ + +#define EMULATOR_MAX_IOPORTS_NUM 0x10000 + +/* FUNCTIONS ******************************************************************/ + +typedef UCHAR (WINAPI *EMULATOR_INB_PROC)(ULONG Port); +typedef USHORT (WINAPI *EMULATOR_INW_PROC)(ULONG Port); +typedef ULONG (WINAPI *EMULATOR_IND_PROC)(ULONG Port); + +typedef VOID (WINAPI *EMULATOR_INSB_PROC)(ULONG Port, PUCHAR Buffer, ULONG Count); +typedef VOID (WINAPI *EMULATOR_INSW_PROC)(ULONG Port, PUSHORT Buffer, ULONG Count); +typedef VOID (WINAPI *EMULATOR_INSD_PROC)(ULONG Port, PULONG Buffer, ULONG Count); + +typedef VOID (WINAPI *EMULATOR_OUTB_PROC)(ULONG Port, UCHAR Data); +typedef VOID (WINAPI *EMULATOR_OUTW_PROC)(ULONG Port, USHORT Data); +typedef VOID (WINAPI *EMULATOR_OUTD_PROC)(ULONG Port, ULONG Data); + +typedef VOID (WINAPI *EMULATOR_OUTSB_PROC)(ULONG Port, PUCHAR Buffer, ULONG Count); +typedef VOID (WINAPI *EMULATOR_OUTSW_PROC)(ULONG Port, PUSHORT Buffer, ULONG Count); +typedef VOID (WINAPI *EMULATOR_OUTSD_PROC)(ULONG Port, PULONG Buffer, ULONG Count); + + +UCHAR +IOReadB(ULONG Port); +VOID +IOReadStrB(ULONG Port, + PUCHAR Buffer, + ULONG Count); + +VOID +IOWriteB(ULONG Port, + UCHAR Buffer); +VOID +IOWriteStrB(ULONG Port, + PUCHAR Buffer, + ULONG Count); + +USHORT +IOReadW(ULONG Port); +VOID +IOReadStrW(ULONG Port, + PUSHORT Buffer, + ULONG Count); + +VOID +IOWriteW(ULONG Port, + USHORT Buffer); +VOID +IOWriteStrW(ULONG Port, + PUSHORT Buffer, + ULONG Count); + +ULONG +IOReadD(ULONG Port); +VOID +IOReadStrD(ULONG Port, + PULONG Buffer, + ULONG Count); + +VOID +IOWriteD(ULONG Port, + ULONG Buffer); +VOID +IOWriteStrD(ULONG Port, + PULONG Buffer, + ULONG Count); + + +VOID RegisterIoPort(ULONG Port, + EMULATOR_INB_PROC InHandler, + EMULATOR_OUTB_PROC OutHandler); + +VOID UnregisterIoPort(ULONG Port); + +VOID WINAPI EmulatorReadIo +( + PFAST486_STATE State, + ULONG Port, + PVOID Buffer, + ULONG DataCount, + UCHAR DataSize +); + +VOID WINAPI EmulatorWriteIo +( + PFAST486_STATE State, + ULONG Port, + PVOID Buffer, + ULONG DataCount, + UCHAR DataSize +); + +#endif // _IO_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/lang/bg-BG.rc b/reactos/subsystems/ntvdm/lang/bg-BG.rc deleted file mode 100644 index aea51c6a350d3..0000000000000 --- a/reactos/subsystems/ntvdm/lang/bg-BG.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "Поддръжка на РеактОС за привидна ДОС машина.\n" - STRING_PromptMsg "Напишете r за пускане, s за спиране или q за изход." -END diff --git a/reactos/subsystems/ntvdm/lang/cs-CZ.rc b/reactos/subsystems/ntvdm/lang/cs-CZ.rc index e4dd3a52b1b6b..b620634924c0c 100644 --- a/reactos/subsystems/ntvdm/lang/cs-CZ.rc +++ b/reactos/subsystems/ntvdm/lang/cs-CZ.rc @@ -1,12 +1,19 @@ -/* FILE: subsystems/ntvdm/lang/cs-CZ.rc +/* + * FILE: subsystems/ntvdm/lang/cs-CZ.rc * TRANSLATOR: Radek Liska aka Black_Fox (radekliska at gmail dot com) - * UPDATED: 2008-06-24 + * UPDATED: 2014-02-01 */ - + LANGUAGE LANG_CZECH, SUBLANG_DEFAULT STRINGTABLE BEGIN - STRING_WelcomeMsg "ReactOS podpora virtuálního DOS stroje.\n" - STRING_PromptMsg "Napište r pro spuštìní, s pro vypnutí nebo q pro ukonèení." + IDS_HIDE_MOUSE, "&Skrýt ukazatel myši" + IDS_SHOW_MOUSE, "&Zobrazit ukazatel myši" + IDS_VDM_MENU , "ReactOS &VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "&Ukončit ReactOS VDM" END diff --git a/reactos/subsystems/ntvdm/lang/de-DE.rc b/reactos/subsystems/ntvdm/lang/de-DE.rc index e8007f8efe142..1f56de3559c95 100644 --- a/reactos/subsystems/ntvdm/lang/de-DE.rc +++ b/reactos/subsystems/ntvdm/lang/de-DE.rc @@ -1,13 +1,13 @@ -/* - * German translate - * By Rouven Wessling 2005 pentiumforever@gmail.com - * 2008 dark_shadow@gmx.at - */ - LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL STRINGTABLE BEGIN - STRING_WelcomeMsg "ReactOS virtuelle DOS-Unterstützung.\n" - STRING_PromptMsg "Drücken Sie r zum Starten, s zum Herunterfahren oder q zum Beenden." + IDS_HIDE_MOUSE, "Mauszeiger &verstecken" + IDS_SHOW_MOUSE, "Mauszeiger &anzeigen" + IDS_VDM_MENU , "&ReactOS VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "ReactOS VDM b&eenden" END diff --git a/reactos/subsystems/ntvdm/lang/en-US.rc b/reactos/subsystems/ntvdm/lang/en-US.rc index cdb7f06e72698..4ec9d710cbbbc 100644 --- a/reactos/subsystems/ntvdm/lang/en-US.rc +++ b/reactos/subsystems/ntvdm/lang/en-US.rc @@ -2,6 +2,12 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US STRINGTABLE BEGIN - STRING_WelcomeMsg "ReactOS Virtual DOS Machine support.\n" - STRING_PromptMsg "Type r to run, s to shutdown or q to quit now." + 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/reactos/subsystems/ntvdm/lang/es-ES.rc b/reactos/subsystems/ntvdm/lang/es-ES.rc index d5b7d15291cfa..19d0591e7b8d3 100644 --- a/reactos/subsystems/ntvdm/lang/es-ES.rc +++ b/reactos/subsystems/ntvdm/lang/es-ES.rc @@ -1,9 +1,13 @@ -/* Spanish translation by Samuel Serapion */ - LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL STRINGTABLE BEGIN - STRING_WelcomeMsg "Maquina virtual de DOS en ReactOS.\n" - STRING_PromptMsg "Escriba r para correr, s para desactivar or q para salir ahora." + IDS_HIDE_MOUSE, "&Ocultar puntero del ratón" + IDS_SHOW_MOUSE, "&Mostrar puntero del ratón" + IDS_VDM_MENU , "ReactOS &VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "&Salir de ReactOS VDM" END diff --git a/reactos/subsystems/ntvdm/lang/fr-FR.rc b/reactos/subsystems/ntvdm/lang/fr-FR.rc index 702f95a75066d..853aa5271a9ab 100644 --- a/reactos/subsystems/ntvdm/lang/fr-FR.rc +++ b/reactos/subsystems/ntvdm/lang/fr-FR.rc @@ -2,6 +2,12 @@ LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL STRINGTABLE BEGIN - STRING_WelcomeMsg "Aide de la Machine DOS Virtuel de ReactOS.\n" - STRING_PromptMsg "Taper r pour démarrer, s pour éteindre ou q pour quitter maintenant." + 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/reactos/subsystems/ntvdm/lang/hu-HU.rc b/reactos/subsystems/ntvdm/lang/hu-HU.rc deleted file mode 100644 index c25b7fbbe6f44..0000000000000 --- a/reactos/subsystems/ntvdm/lang/hu-HU.rc +++ /dev/null @@ -1,9 +0,0 @@ -/* Hungarian translation by Robert Horvath 2005 - talley at cubeclub.hu */ - -LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "ReactOS Virtuális DOS Gép támogatás.\n" - STRING_PromptMsg "Futtatáshoz nyomd meg a r, leállításhoz a s vagy kilépéshez a q gombot." -END diff --git a/reactos/subsystems/ntvdm/lang/id-ID.rc b/reactos/subsystems/ntvdm/lang/id-ID.rc deleted file mode 100644 index be7b06c9b8341..0000000000000 --- a/reactos/subsystems/ntvdm/lang/id-ID.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_INDONESIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "Dukungan ReactOS Virtual DOS Machine.\n" - STRING_PromptMsg "Ketik r untuk menjalankan, s untuk mematikan atau q untuk keluar sekarang." -END diff --git a/reactos/subsystems/ntvdm/lang/it-IT.rc b/reactos/subsystems/ntvdm/lang/it-IT.rc index 305c91418cc46..ff3a0960de5cc 100644 --- a/reactos/subsystems/ntvdm/lang/it-IT.rc +++ b/reactos/subsystems/ntvdm/lang/it-IT.rc @@ -1,15 +1,13 @@ -/* -* PROJECT: ReactOS Virtual DOS Machine -* LICENSE: GPL - See COPYING in the top level directory -* FILE: subsystems/ntvdm/it-IT.rc -* PURPOSE: Italian Translation of subsystems/ntvdm/en-US.rc -* TRANSLATOR: Daniele Forsi (dforsi at gmail.com) -*/ - LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL STRINGTABLE BEGIN - STRING_WelcomeMsg "Supporto di ReactOS per la macchina virtuale DOS.\n" - STRING_PromptMsg "Digitare r per avviare, s per arrestare o q per abbandonare ora." + IDS_HIDE_MOUSE, "&Nascondi il mouse" + IDS_SHOW_MOUSE, "&Mostra il mouse" + IDS_VDM_MENU , "ReactOS &VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "&Esci da ReactOS VDM" END diff --git a/reactos/subsystems/ntvdm/lang/ja-JP.rc b/reactos/subsystems/ntvdm/lang/ja-JP.rc deleted file mode 100644 index 7a5bfcf49435f..0000000000000 --- a/reactos/subsystems/ntvdm/lang/ja-JP.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "ReactOS Virtual DOS Machine support.\n" - STRING_PromptMsg "起動するには r を、シャットダウンするには s を、今すぐ終了させるには q を入力してください。" -END diff --git a/reactos/subsystems/ntvdm/lang/no-NO.rc b/reactos/subsystems/ntvdm/lang/no-NO.rc deleted file mode 100644 index 421dcd515aa45..0000000000000 --- a/reactos/subsystems/ntvdm/lang/no-NO.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_NORWEGIAN, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "ReactOS Vituell DOS Maskin støtte.\n" - STRING_PromptMsg "Skriv r for å kjøre, s å avslutte eller q for å slutte nå." -END diff --git a/reactos/subsystems/ntvdm/lang/pl-PL.rc b/reactos/subsystems/ntvdm/lang/pl-PL.rc index 689cf0dcb6e6b..e23ee40e88719 100644 --- a/reactos/subsystems/ntvdm/lang/pl-PL.rc +++ b/reactos/subsystems/ntvdm/lang/pl-PL.rc @@ -1,14 +1,15 @@ -/* - * Translated by xrogers - * xxrogers@users.sourceforge.net - * https://sourceforge.net/projects/reactospl - * UTF-8 conversion by Caemyr (May, 2011) - */ - +// Created by wojo664 - Saved in UTF-8 encoding + LANGUAGE LANG_POLISH, SUBLANG_DEFAULT STRINGTABLE BEGIN - STRING_WelcomeMsg "Wirtualna maszyna DOS dla ReactOS.\n" - STRING_PromptMsg "Wciśnij r aby uruchomić, s aby wyłączyć lub q, aby zakończyć." + IDS_HIDE_MOUSE, "&Ukryj Wskaźnik Myszki" + IDS_SHOW_MOUSE, "&Pokaż Wskaźnik Myszki" + IDS_VDM_MENU , "ReactOS &VDM" +END + +STRINGTABLE +BEGIN + IDS_VDM_QUIT, "&Wyjdź z ReactOS VDM" END diff --git a/reactos/subsystems/ntvdm/lang/pt-BR.rc b/reactos/subsystems/ntvdm/lang/pt-BR.rc deleted file mode 100644 index 5f68fc7842c84..0000000000000 --- a/reactos/subsystems/ntvdm/lang/pt-BR.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "ReactOS subsistema para suporte DOS de 16 bits.\n" - STRING_PromptMsg "Digite r para executar, s para desligar ou q para sair." -END diff --git a/reactos/subsystems/ntvdm/lang/ro-RO.rc b/reactos/subsystems/ntvdm/lang/ro-RO.rc deleted file mode 100644 index d8c65c6b75efa..0000000000000 --- a/reactos/subsystems/ntvdm/lang/ro-RO.rc +++ /dev/null @@ -1,14 +0,0 @@ -/* - * FILE: subsystems/ntvdm/lang/ro-RO.rc - * ReactOS Project (http://www.reactos.org) - * TRANSLATOR: Fulea Ștefan (PM on ReactOS Forum at fulea.stefan) - * CHANGE LOG: 2011-10-16 initial translation - */ - -LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "Asistență pentru mașina virtuală DOS.\n" - STRING_PromptMsg "Tastați r pentru a executa, s pentru a închide sau q pentru a ieși imediat." -END diff --git a/reactos/subsystems/ntvdm/lang/ru-RU.rc b/reactos/subsystems/ntvdm/lang/ru-RU.rc deleted file mode 100644 index 5a2f3aa122d5c..0000000000000 --- a/reactos/subsystems/ntvdm/lang/ru-RU.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "Виртуальная машина поддержки DOS для ReactOS.\n" - STRING_PromptMsg "Введите r для запуска, s для выключения или q выхода." -END diff --git a/reactos/subsystems/ntvdm/lang/sk-SK.rc b/reactos/subsystems/ntvdm/lang/sk-SK.rc deleted file mode 100644 index cf72455ad0e22..0000000000000 --- a/reactos/subsystems/ntvdm/lang/sk-SK.rc +++ /dev/null @@ -1,11 +0,0 @@ -/* TRANSLATOR: Mário Kaèmár /Mario Kacmar/ aka Kario (kario@szm.sk) - * DATE OF TR: 12-02-2008 - */ - -LANGUAGE LANG_SLOVAK, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "Podpora virtuálneho DOSového stroja v systéme ReactOS.\n" - STRING_PromptMsg "Napíšte r pre spustenie, s pre vypnutie alebo q pre okamžité skonèenie." -END diff --git a/reactos/subsystems/ntvdm/lang/th-TH.rc b/reactos/subsystems/ntvdm/lang/th-TH.rc deleted file mode 100644 index acaa68723383d..0000000000000 --- a/reactos/subsystems/ntvdm/lang/th-TH.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_THAI, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "รองรับการทำงานระบบดอสเสมือนของ ReactOS\n" - STRING_PromptMsg "แบบ r เพื่อทำงาน, s เพื่อปิดระบบหรือ q เพื่อออกทันที" -END diff --git a/reactos/subsystems/ntvdm/lang/uk-UA.rc b/reactos/subsystems/ntvdm/lang/uk-UA.rc deleted file mode 100644 index ed8fc7307c19a..0000000000000 --- a/reactos/subsystems/ntvdm/lang/uk-UA.rc +++ /dev/null @@ -1,15 +0,0 @@ -/* - * PROJECT: Virtual DOS Machine - * LICENSE: GPL - See COPYING in the top level directory - * FILE: subsystems/ntvdm/Uk.rc - * PURPOSE: Ukraianian Language File for Virtual DOS Machine - * TRANSLATOR: Artem Reznikov - */ - -LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "Підтримка віртуальної машини DOS у ReactOS.\n" - STRING_PromptMsg "Введіть r для запуску, s для закриття або q, щоб вийти зараз." -END diff --git a/reactos/subsystems/ntvdm/lang/zh-CN.rc b/reactos/subsystems/ntvdm/lang/zh-CN.rc deleted file mode 100644 index 89dbafa96fb80..0000000000000 --- a/reactos/subsystems/ntvdm/lang/zh-CN.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "ReactOS 虚拟 DOS 机支持。\n" - STRING_PromptMsg "输入 r 以便运行,s 以便关闭或者 q 以便立即退出。" -END diff --git a/reactos/subsystems/ntvdm/lang/zh-TW.rc b/reactos/subsystems/ntvdm/lang/zh-TW.rc deleted file mode 100644 index 9a7790b4f3c04..0000000000000 --- a/reactos/subsystems/ntvdm/lang/zh-TW.rc +++ /dev/null @@ -1,7 +0,0 @@ -LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL - -STRINGTABLE -BEGIN - STRING_WelcomeMsg "ReactOS 虛擬 DOS 機支援。\n" - STRING_PromptMsg "鍵入 r 以便運行, s 以便關閉或者 q 以便立即退出。" -END diff --git a/reactos/subsystems/ntvdm/ntvdm.c b/reactos/subsystems/ntvdm/ntvdm.c index 29cf5734bd280..813a8858f0aa9 100644 --- a/reactos/subsystems/ntvdm/ntvdm.c +++ b/reactos/subsystems/ntvdm/ntvdm.c @@ -1,375 +1,571 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: subsys/ntvdm/ntvdm->c + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: ntvdm.c * PURPOSE: Virtual DOS Machine - * PROGRAMMER: Robert Dickenson (robd@mok.lvcm.com) - * UPDATE HISTORY: - * Created 23/10/2002 + * PROGRAMMERS: Aleksandar Andrejevic */ -/* INCLUDES *****************************************************************/ +/* INCLUDES *******************************************************************/ -#include -#define WIN32_NO_STATUS -#include -#include -#include -#include -#include +#define NDEBUG + +#include "ntvdm.h" +#include "emulator.h" + +#include "clock.h" +#include "hardware/ps2.h" +#include "hardware/vga.h" +#include "bios/bios.h" +#include "dos/dem.h" #include "resource.h" -#define NDEBUG -#include +/* + * Activate this line if you want to run NTVDM in standalone mode with: + * ntvdm.exe + */ +// #define STANDALONE -/* GLOBALS ******************************************************************/ +/* VARIABLES ******************************************************************/ +static HANDLE ConsoleInput = INVALID_HANDLE_VALUE; +static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE; +static DWORD OrgConsoleInputMode, OrgConsoleOutputMode; +static BOOLEAN AcceptCommands = TRUE; +static HANDLE CommandThread = NULL; -/* FUNCTIONS *****************************************************************/ +static HMENU hConsoleMenu = NULL; +static INT VdmMenuPos = -1; +static BOOLEAN ShowPointer = FALSE; -void PrintString(char* fmt,...) +ULONG SessionId = 0; +HANDLE VdmTaskEvent = NULL; + +/* + * 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[] = { - char buffer[512]; - va_list ap; + { IDS_VDM_QUIT, NULL, ID_VDM_QUIT }, - va_start(ap, fmt); - vsprintf(buffer, fmt, ap); - va_end(ap); + { 0, NULL, 0 } /* End of list */ +}; - OutputDebugStringA(buffer); +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)); } -/* -GetVersion -GetVolumeInformationW -GetWindowsDirectoryA -GlobalMemoryStatus -HeapAlloc -HeapCreate -HeapDestroy -HeapFree -HeapReAlloc - -GetNextVDMCommand -ExitVDM -RegisterConsoleVDM -SetVDMCurrentDirectories -VDMConsoleOperation -WriteConsoleInputVDMW - -NtSetLdtEntries -NtTerminateProcess - -NtMapViewOfSection -NtUnmapViewOfSection - -NtVdmControl - */ -typedef struct tag_VDM_CONFIG { - int dos_options; - int files; - int buffers; - WCHAR** device_list; -//dos=high, umb -//device=%SystemRoot%\system32\himem.sys -//files=40 -} VDM_CONFIG, *PVDM_CONFIG; - -typedef struct tag_VDM_AUTOEXEC { - WCHAR** load_list; -//lh %SystemRoot%\system32\mscdexnt.exe -//lh %SystemRoot%\system32\redir -//lh %SystemRoot%\system32\dosx -} VDM_AUTOEXEC, *PVDM_AUTOEXEC; - -typedef struct tag_VDM_CONTROL_BLOCK { - HANDLE hHeap; - PVOID ImageMem; - VDM_CONFIG vdmConfig; - VDM_AUTOEXEC vdmAutoexec; - PROCESS_INFORMATION ProcessInformation; - CHAR CommandLine[MAX_PATH]; - CHAR CurrentDirectory[MAX_PATH]; - -} VDM_CONTROL_BLOCK, *PVDM_CONTROL_BLOCK; - - -BOOL -StartVDM(PVDM_CONTROL_BLOCK vdm) +static VOID +CreateVdmMenu(HANDLE ConOutHandle) { - BOOL Result; - STARTUPINFOA StartupInfo; - - StartupInfo.cb = sizeof(StartupInfo); - StartupInfo.lpReserved = NULL; - StartupInfo.lpDesktop = NULL; - StartupInfo.lpTitle = NULL; - StartupInfo.dwFlags = 0; - StartupInfo.cbReserved2 = 0; - StartupInfo.lpReserved2 = 0; - - Result = CreateProcessA(vdm->CommandLine, - NULL, - NULL, - NULL, - FALSE, - DETACHED_PROCESS, - NULL, - NULL, - &StartupInfo, - &vdm->ProcessInformation); - if (!Result) { - PrintString("VDM: Failed to execute target process\n"); - return FALSE; - } - WaitForSingleObject(vdm->ProcessInformation.hProcess, INFINITE); - CloseHandle(vdm->ProcessInformation.hProcess); - CloseHandle(vdm->ProcessInformation.hThread); - return TRUE; + hConsoleMenu = ConsoleMenuControl(ConsoleOutput, + ID_SHOWHIDE_MOUSE, + ID_VDM_QUIT); + if (hConsoleMenu == NULL) return; + + VdmMenuPos = GetMenuItemCount(hConsoleMenu); + AppendMenuItems(hConsoleMenu, VdmMainMenuItems); + DrawMenuBar(GetConsoleWindow()); } -BOOL -ShutdownVDM(PVDM_CONTROL_BLOCK vdm) +static VOID +DestroyVdmMenu(VOID) { - BOOL result = TRUE; + 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)); - return result; + DrawMenuBar(GetConsoleWindow()); } -BOOL ReadConfigForVDM(PVDM_CONTROL_BLOCK vdm) +static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr) { - BOOL result = TRUE; - DWORD dwError; - HANDLE hFile; - - hFile = CreateFileW(L"\\system32\\config.nt", - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_ALWAYS /*OPEN_EXISTING*/, - FILE_ATTRIBUTE_NORMAL, - 0); - dwError = GetLastError(); - if (hFile == INVALID_HANDLE_VALUE) { - // error with file path or system problem? - } else { - if (dwError == 0L) { - // we just created a new file, perhaps we should set/write some defaults? - } - if (dwError == ERROR_ALREADY_EXISTS) { - // read the line entries and cache in some struct... - } - CloseHandle(hFile); + WCHAR szMenuString[255] = L""; + + if (ShowPtr) + { + /* Be sure the cursor will be shown */ + while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ; + } + else + { + /* Be sure the cursor will be hidden */ + while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ; } - hFile = CreateFileW(L"\\system32\\autoexec.nt", - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - 0); - dwError = GetLastError(); - if (hFile == INVALID_HANDLE_VALUE) { - // error with file path or system problem? - } else { - if (dwError == 0L) { - // we just created a new file, perhaps we should set/write some defaults? - } - if (dwError == ERROR_ALREADY_EXISTS) { - // read the line entries and cache in some struct... - } - CloseHandle(hFile); + 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); } +} - return result; +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID DisplayMessage(LPCWSTR Format, ...) +{ + WCHAR Buffer[256]; + va_list Parameters; + + va_start(Parameters, Format); + _vsnwprintf(Buffer, 256, Format, Parameters); + DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer); + MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK); + va_end(Parameters); } -BOOL -LoadConfigDriversForVDM(PVDM_CONFIG vdmConfig) +BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType) { - BOOL result = TRUE; + switch (ControlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + { + /* Call INT 23h */ + EmulatorInterrupt(0x23); + break; + } + case CTRL_LAST_CLOSE_EVENT: + { + if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT) + { + /* Exit immediately */ + if (CommandThread) TerminateThread(CommandThread, 0); + EmulatorTerminate(); + } + else + { + /* Stop accepting new commands */ + AcceptCommands = FALSE; + } + + break; + } + default: + { + /* Stop the VDM if the user logs out or closes the console */ + EmulatorTerminate(); + } + } + return TRUE; +} - return result; +VOID ConsoleInitUI(VOID) +{ + CreateVdmMenu(ConsoleOutput); } -BOOL -SetConfigOptionsForVDM(PVDM_AUTOEXEC vdmAutoexec) +VOID ConsoleCleanupUI(VOID) { - BOOL result = TRUE; + /* Display again properly the mouse pointer */ + if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer); - return result; + DestroyVdmMenu(); } -BOOL -CreateVDM(PVDM_CONTROL_BLOCK vdm) +DWORD WINAPI PumpConsoleInput(LPVOID Parameter) { -// BOOL result = TRUE; - SYSTEM_INFO inf; - MEMORYSTATUS stat; - - - GlobalMemoryStatus(&stat); - if (stat.dwLength != sizeof(MEMORYSTATUS)) { - printf("WARNING: GlobalMemoryStatus() returned unknown structure version, size %ld, expected %d.\n", stat.dwLength, sizeof(stat)); - } else { - printf("Memory Load: %ld percent in use.\n", stat.dwMemoryLoad); - printf("\t%ld total bytes physical memory.\n", stat.dwTotalPhys); - printf("\t%ld available physical memory.\n", stat.dwAvailPhys); - printf("\t%ld total bytes paging file.\n", stat.dwTotalPageFile); - printf("\t%ld available paging file.\n", stat.dwAvailPageFile); - printf("\t%lx total bytes virtual memory.\n", stat.dwTotalVirtual); - printf("\t%lx available bytes virtual memory.\n", stat.dwAvailVirtual); - -#define OUT_OF_HEADROOM 90 - if (stat.dwMemoryLoad > OUT_OF_HEADROOM) { - DPRINT("VDM: system resources deemed to low to start VDM.\n"); - //SetLastError(); - return FALSE; + HANDLE ConsoleInput = (HANDLE)Parameter; + INPUT_RECORD InputRecord; + DWORD Count; + + while (VdmRunning) + { + /* Make sure the task event is signaled */ + WaitForSingleObject(VdmTaskEvent, INFINITE); + + /* 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: + /* Stop the VDM */ + EmulatorTerminate(); + break; + + default: + break; + } + + break; + } + + default: + break; + } } - GetSystemInfo(&inf); - vdm->hHeap = HeapCreate(0, inf.dwAllocationGranularity, 0); - if (vdm->hHeap == NULL) { - DPRINT("VDM: failed to create heap.\n"); + return 0; +} + +BOOL ConsoleInit(VOID) +{ + /* Set the handler routine */ + SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); + + /* Enable the CTRL_LAST_CLOSE_EVENT */ + SetLastConsoleEventActive(); + + /* Get the input handle to the real console, and check for success */ + ConsoleInput = CreateFileW(L"CONIN$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (ConsoleInput == INVALID_HANDLE_VALUE) + { + wprintf(L"FATAL: Cannot retrieve a handle to the console input\n"); + return FALSE; + } + + /* Get the output handle to the real console, and check for success */ + ConsoleOutput = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (ConsoleOutput == INVALID_HANDLE_VALUE) + { + CloseHandle(ConsoleInput); + wprintf(L"FATAL: Cannot retrieve a handle to the console output\n"); return FALSE; } -#define DEFAULT_VDM_IMAGE_SIZE 2000000 - vdm->ImageMem = HeapAlloc(vdm->hHeap, 0, DEFAULT_VDM_IMAGE_SIZE); - if (vdm->ImageMem == NULL) { - DPRINT("VDM: failed to allocate image memory from heap %x.\n", vdm->hHeap); - HeapDestroy(vdm->hHeap); - vdm->hHeap = NULL; + /* Save the original input and output console modes */ + if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) || + !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode)) + { + CloseHandle(ConsoleOutput); + CloseHandle(ConsoleInput); + wprintf(L"FATAL: Cannot save console in/out modes\n"); return FALSE; } + + /* Initialize the UI */ + ConsoleInitUI(); + return TRUE; } -BOOL -DestroyVDM(PVDM_CONTROL_BLOCK vdm) +VOID ConsoleCleanup(VOID) { - BOOL result = TRUE; + /* Restore the original input and output console modes */ + SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode); + SetConsoleMode(ConsoleInput , OrgConsoleInputMode ); - if (vdm->ImageMem != NULL) { - if (HeapFree(vdm->hHeap, 0, vdm->ImageMem) != FALSE) { - DPRINT("VDM: failed to free memory from heap %x.\n", vdm->hHeap); - result = FALSE; - } - vdm->ImageMem = NULL; - } - if (vdm->hHeap != NULL) { - if (!HeapDestroy(vdm->hHeap)) { - DPRINT("VDM: failed to destroy heap %x.\n", vdm->hHeap); - result = FALSE; + /* Cleanup the UI */ + ConsoleCleanupUI(); + + /* Close the console handles */ + if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput); + if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput); +} + +DWORD WINAPI CommandThreadProc(LPVOID Parameter) +{ + BOOLEAN First = TRUE; + DWORD Result; + VDM_COMMAND_INFO CommandInfo; + CHAR CmdLine[MAX_PATH]; + CHAR AppName[MAX_PATH]; + CHAR PifFile[MAX_PATH]; + CHAR Desktop[MAX_PATH]; + CHAR Title[MAX_PATH]; + CHAR Env[MAX_PATH]; + + UNREFERENCED_PARAMETER(Parameter); + + do + { + /* Clear the structure */ + ZeroMemory(&CommandInfo, sizeof(CommandInfo)); + + /* Initialize the structure members */ + CommandInfo.TaskId = SessionId; + CommandInfo.VDMState = VDM_FLAG_DOS; + CommandInfo.CmdLine = CmdLine; + CommandInfo.CmdLen = sizeof(CmdLine); + CommandInfo.AppName = AppName; + CommandInfo.AppLen = sizeof(AppName); + CommandInfo.PifFile = PifFile; + CommandInfo.PifLen = sizeof(PifFile); + CommandInfo.Desktop = Desktop; + CommandInfo.DesktopLen = sizeof(Desktop); + CommandInfo.Title = Title; + CommandInfo.TitleLen = sizeof(Title); + CommandInfo.Env = Env; + CommandInfo.EnvLen = sizeof(Env); + + if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK; + + /* Wait for the next available VDM */ + if (!GetNextVDMCommand(&CommandInfo)) break; + + /* Start the process from the command line */ + DPRINT1("Starting '%s'...\n", AppName); + + Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE, AppName, CmdLine, Env, NULL, NULL); + if (Result != ERROR_SUCCESS) + { + DisplayMessage(L"Could not start '%S'. Error: %u", AppName, Result); + break; } - vdm->hHeap = NULL; + + /* Attach to the console */ + if (!First) VgaAttachToConsole(); + + /* Perform a screen refresh */ + VgaRefreshDisplay(); + + /* Start simulation */ + SetEvent(VdmTaskEvent); + EmulatorSimulate(); + + /* Perform another screen refresh */ + VgaRefreshDisplay(); + + /* Detach from the console */ + VgaDetachFromConsole(FALSE); + + First = FALSE; } - return result; + while (AcceptCommands); + + return 0; } -int WINAPI -WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +INT wmain(INT argc, WCHAR *argv[]) { - VDM_CONTROL_BLOCK VdmCB; +#ifdef STANDALONE + DWORD Result; - ULONG i; - BOOL vdmStarted = FALSE; + CHAR ApplicationName[MAX_PATH]; + CHAR CommandLine[DOS_CMDLINE_LENGTH]; - WCHAR WelcomeMsg[RC_STRING_MAX_SIZE]; - WCHAR PromptMsg[RC_STRING_MAX_SIZE]; - CHAR InputBuffer[255]; + if (argc >= 2) + { + WideCharToMultiByte(CP_ACP, 0, argv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL); - LoadStringW( GetModuleHandle(NULL), STRING_WelcomeMsg, WelcomeMsg,sizeof(WelcomeMsg) / sizeof(WelcomeMsg[0])); - LoadStringW( GetModuleHandle(NULL), STRING_PromptMsg, PromptMsg ,sizeof(PromptMsg) / sizeof(PromptMsg[0])); + if (argc >= 3) WideCharToMultiByte(CP_ACP, 0, argv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL); + else strcpy(CommandLine, ""); + } + else + { + wprintf(L"\nReactOS Virtual DOS Machine\n\n" + L"Usage: NTVDM []\n"); + return 0; + } - AllocConsole(); - SetConsoleTitleW(L"ntvdm"); +#else + INT i; + WCHAR *endptr; - WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), - WelcomeMsg, lstrlenW(WelcomeMsg), // wcslen(WelcomeMsg), - &Result, NULL); + /* Parse the command line arguments */ + for (i = 1; i < argc; i++) + { + if (wcsncmp(argv[i], L"-i", 2) == 0) + { + /* This is the session ID */ + SessionId = wcstoul(argv[i] + 2, &endptr, 10); - if (!CreateVDM(&VdmCB)) { - DPRINT("VDM: failed to create VDM.\n"); - //SetLastError(); - return 2; + /* The VDM hasn't been started from a console, so quit when the task is done */ + AcceptCommands = FALSE; + } } - ReadConfigForVDM(&VdmCB); +#endif + + DPRINT1("\n\n\nNTVDM - Starting...\n\n\n"); - if (!LoadConfigDriversForVDM(&(VdmCB.vdmConfig))) { - DPRINT("VDM: failed to load configuration drivers.\n"); - //SetLastError(); - return 2; + /* Create the task event */ + VdmTaskEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + ASSERT(VdmTaskEvent != NULL); + + /* Initialize the console */ + if (!ConsoleInit()) + { + wprintf(L"FATAL: A problem occurred when trying to initialize the console\n"); + goto Cleanup; } - if (!SetConfigOptionsForVDM(&(VdmCB.vdmAutoexec))) { - DPRINT("VDM: failed to set configuration options.\n"); - //SetLastError(); - return 3; + + /* Initialize the emulator */ + if (!EmulatorInitialize(ConsoleInput, ConsoleOutput)) + { + wprintf(L"FATAL: Failed to initialize the emulator\n"); + goto Cleanup; } - GetSystemDirectoryA(VdmCB.CommandLine, MAX_PATH); - strcat(VdmCB.CommandLine, "\\hello.exe"); - GetWindowsDirectoryA(VdmCB.CurrentDirectory, MAX_PATH); - - for (;;) { - WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), - PromptMsg, lstrlenW(PromptMsg), // wcslen(PromptMsg), - &Result, NULL); - i = 0; - do { - ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE), - &InputBuffer[i], 1, - &Result, NULL); - if (++i >= (sizeof(InputBuffer) - 1)) { - break; - } - } while (InputBuffer[i - 1] != '\n'); - InputBuffer[i - 1] = '\0'; - - if (InputBuffer[0] == 'r' || InputBuffer[0] == 'R') { - if (!vdmStarted) { - if (StartVDM(&VdmCB)) { - vdmStarted = TRUE; - } else { - DPRINT("VDM: failed to start.\n"); - } - } else { - DPRINT("VDM: already started.\n"); - } - } - if (InputBuffer[0] == 's' || InputBuffer[0] == 'S') { - if (vdmStarted) { - if (ShutdownVDM(&VdmCB)) { - vdmStarted = FALSE; - } else { - DPRINT("VDM: failed to shutdown.\n"); - } - } else { - DPRINT("VDM: not started.\n"); - } - } - if (InputBuffer[0] == 'q' || InputBuffer[0] == 'Q') { - break; - } + /* Initialize the system BIOS */ + if (!BiosInitialize(NULL)) + { + wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n"); + goto Cleanup; } - if (!ShutdownVDM(&VdmCB)) { - DPRINT("VDM: failed to cleanly shutdown VDM.\n"); - //SetLastError(); - return 5; + /* Initialize the VDM DOS kernel */ + if (!DosInitialize(NULL)) + { + wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n"); + goto Cleanup; } - if (!DestroyVDM(&VdmCB)) { - DPRINT("VDM: failed to cleanly destroy VDM.\n"); - //SetLastError(); - return 6; +#ifndef STANDALONE + + /* Create the GetNextVDMCommand thread */ + CommandThread = CreateThread(NULL, 0, &CommandThreadProc, NULL, 0, NULL); + if (CommandThread == NULL) + { + wprintf(L"FATAL: Failed to create the command processing thread: %d\n", GetLastError()); + goto Cleanup; + } + + /* Wait for the command thread to exit */ + WaitForSingleObject(CommandThread, INFINITE); + + /* Close the thread handle */ + CloseHandle(CommandThread); + +#else + + /* Start the process from the command line */ + DPRINT1("Starting '%s'...\n", ApplicationName); + + Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE, + ApplicationName, + CommandLine, + GetEnvironmentStrings(), + NULL, + NULL); + if (Result != ERROR_SUCCESS) + { + DisplayMessage(L"Could not start '%S'. Error: %u", ApplicationName, Result); + goto Cleanup; } - ExitProcess(0); + /* Start simulation */ + SetEvent(VdmTaskEvent); + EmulatorSimulate(); + + /* Perform another screen refresh */ + VgaRefreshDisplay(); + +#endif + +Cleanup: + BiosCleanup(); + EmulatorCleanup(); + ConsoleCleanup(); + +#ifndef STANDALONE + ExitVDM(FALSE, 0); +#endif + + /* Quit the VDM */ + DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n"); + return 0; } + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/ntvdm.h b/reactos/subsystems/ntvdm/ntvdm.h new file mode 100644 index 0000000000000..3555ce41d6b48 --- /dev/null +++ b/reactos/subsystems/ntvdm/ntvdm.h @@ -0,0 +1,44 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: ntvdm.h + * PURPOSE: Header file to define commonly used stuff + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _NTVDM_H_ +#define _NTVDM_H_ + +/* INCLUDES *******************************************************************/ + +#include +#include +#include +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +DWORD WINAPI SetLastConsoleEventActive(VOID); + +/* FUNCTIONS ******************************************************************/ + +extern ULONG SessionId; +extern HANDLE VdmTaskEvent; + +VOID DisplayMessage(LPCWSTR Format, ...); + +#endif // _NTVDM_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/ntvdm.rc b/reactos/subsystems/ntvdm/ntvdm.rc index 6542cc225a9e5..bb78cedd6a366 100644 --- a/reactos/subsystems/ntvdm/ntvdm.rc +++ b/reactos/subsystems/ntvdm/ntvdm.rc @@ -1,32 +1,39 @@ #include +#include +// #include #include "resource.h" -#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Virtual DOS Machine" -#define REACTOS_STR_INTERNAL_NAME "ntvdm" -#define REACTOS_STR_ORIGINAL_FILENAME "ntvdm.exe" +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Virtual DOS Machine" +#define REACTOS_STR_INTERNAL_NAME "ntvdm" +#define REACTOS_STR_ORIGINAL_FILENAME "ntvdm.exe" #include +IDI_APPICON ICON "res/ntvdm.ico" + /* UTF-8 */ #pragma code_page(65001) -#include "lang/bg-BG.rc" -#include "lang/cs-CZ.rc" -#include "lang/de-DE.rc" -#include "lang/en-US.rc" -#include "lang/es-ES.rc" -#include "lang/fr-FR.rc" -#include "lang/hu-HU.rc" -#include "lang/id-ID.rc" -#include "lang/it-IT.rc" -#include "lang/ja-JP.rc" -#include "lang/no-NO.rc" -#include "lang/pl-PL.rc" -#include "lang/pt-BR.rc" -#include "lang/ro-RO.rc" -#include "lang/ru-RU.rc" -#include "lang/sk-SK.rc" -#include "lang/th-TH.rc" -#include "lang/uk-UA.rc" -#include "lang/zh-CN.rc" -#include "lang/zh-TW.rc" +#ifdef LANGUAGE_CS_CZ + #include "lang/cs-CZ.rc" +#endif +#ifdef LANGUAGE_DE_DE + #include "lang/de-DE.rc" +#endif +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif +#ifdef LANGUAGE_ES_ES + #include "lang/es-ES.rc" +#endif +#ifdef LANGUAGE_FR_FR + #include "lang/fr-FR.rc" +#endif +#ifdef LANGUAGE_IT_IT + #include "lang/it-IT.rc" +#endif +#ifdef LANGUAGE_PL_PL + #include "lang/pl-PL.rc" +#endif diff --git a/reactos/subsystems/ntvdm/ntvdm.spec b/reactos/subsystems/ntvdm/ntvdm.spec new file mode 100644 index 0000000000000..244bda9200f58 --- /dev/null +++ b/reactos/subsystems/ntvdm/ntvdm.spec @@ -0,0 +1,220 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM Registers exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +@ stdcall getAF() +@ stdcall getAH() +@ stdcall getAL() +@ stdcall getAX() +@ stdcall getBH() +@ stdcall getBL() +@ stdcall getBP() +@ stdcall getBX() +@ stdcall getCF() +@ stdcall getCH() +@ stdcall getCL() +@ stdcall getCS() +@ stdcall getCX() +@ stdcall getDF() +@ stdcall getDH() +@ stdcall getDI() +@ stdcall getDL() +@ stdcall getDS() +@ stdcall getDX() +@ stdcall getEAX() +@ stdcall getEBP() +@ stdcall getEBX() +@ stdcall getECX() +@ stdcall getEDI() +@ stdcall getEDX() +@ stdcall getEFLAGS() +@ stdcall getEIP() +@ stdcall getES() +@ stdcall getESI() +@ stdcall getESP() +@ stdcall getFS() +@ stdcall getGS() +@ stdcall getIF() +@ stdcall getIntelRegistersPointer() +@ stdcall getIP() +@ stdcall getMSW() +@ stdcall getOF() +@ stdcall getPF() +@ stdcall getSF() +@ stdcall getSI() +@ stdcall getSP() +@ stdcall getSS() +@ stdcall getZF() + +@ stdcall setAF(long) +@ stdcall setAH(long) +@ stdcall setAL(long) +@ stdcall setAX(long) +@ stdcall setBH(long) +@ stdcall setBL(long) +@ stdcall setBP(long) +@ stdcall setBX(long) +@ stdcall setCF(long) +@ stdcall setCH(long) +@ stdcall setCL(long) +@ stdcall setCS(long) +@ stdcall setCX(long) +@ stdcall setDF(long) +@ stdcall setDH(long) +@ stdcall setDI(long) +@ stdcall setDL(long) +@ stdcall setDS(long) +@ stdcall setDX(long) +@ stdcall setEAX(long) +@ stdcall setEBP(long) +@ stdcall setEBX(long) +@ stdcall setECX(long) +@ stdcall setEDI(long) +@ stdcall setEDX(long) +@ stdcall setEFLAGS(long) +@ stdcall setEIP(long) +@ stdcall setES(long) +@ stdcall setESI(long) +@ stdcall setESP(long) +@ stdcall setFS(long) +@ stdcall setGS(long) +@ stdcall setIF(long) +@ stdcall setIP(long) +@ stdcall setMSW(long) +@ stdcall setOF(long) +@ stdcall setPF(long) +@ stdcall setSF(long) +@ stdcall setSI(long) +@ stdcall setSP(long) +@ stdcall setSS(long) +@ stdcall setZF(long) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM CCPU MIPS exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +@ stdcall c_getAF() getAF +@ stdcall c_getAH() getAH +@ stdcall c_getAL() getAL +@ stdcall c_getAX() getAX +@ stdcall c_getBH() getBH +@ stdcall c_getBL() getBL +@ stdcall c_getBP() getBP +@ stdcall c_getBX() getBX +@ stdcall c_getCF() getCF +@ stdcall c_getCH() getCH +@ stdcall c_getCL() getCL +@ stdcall c_getCS() getCS +@ stdcall c_getCX() getCX +@ stdcall c_getDF() getDF +@ stdcall c_getDH() getDH +@ stdcall c_getDI() getDI +@ stdcall c_getDL() getDL +@ stdcall c_getDS() getDS +@ stdcall c_getDX() getDX +@ stdcall c_getEAX() getEAX +@ stdcall c_getEBP() getEBP +@ stdcall c_getEBX() getEBX +@ stdcall c_getECX() getECX +@ stdcall c_getEDI() getEDI +@ stdcall c_getEDX() getEDX +@ stdcall c_getEIP() getEIP +@ stdcall c_getES() getES +@ stdcall c_getESI() getESI +@ stdcall c_getESP() getESP +@ stdcall c_getFS() getFS +@ stdcall c_getGS() getGS +@ stdcall c_getIF() getIF +@ stdcall c_getIP() getIP +@ stdcall c_getMSW() getMSW +@ stdcall c_getOF() getOF +@ stdcall c_getPF() getPF +@ stdcall c_getSF() getSF +@ stdcall c_getSI() getSI +@ stdcall c_getSP() getSP +@ stdcall c_getSS() getSS +@ stdcall c_getZF() getZF + +@ stdcall c_setAF(long) setAF +@ stdcall c_setAH(long) setAH +@ stdcall c_setAL(long) setAL +@ stdcall c_setAX(long) setAX +@ stdcall c_setBH(long) setBH +@ stdcall c_setBL(long) setBL +@ stdcall c_setBP(long) setBP +@ stdcall c_setBX(long) setBX +@ stdcall c_setCF(long) setCF +@ stdcall c_setCH(long) setCH +@ stdcall c_setCL(long) setCL +@ stdcall c_setCS(long) setCS +@ stdcall c_setCX(long) setCX +@ stdcall c_setDF(long) setDF +@ stdcall c_setDH(long) setDH +@ stdcall c_setDI(long) setDI +@ stdcall c_setDL(long) setDL +@ stdcall c_setDS(long) setDS +@ stdcall c_setDX(long) setDX +@ stdcall c_setEAX(long) setEAX +@ stdcall c_setEBP(long) setEBP +@ stdcall c_setEBX(long) setEBX +@ stdcall c_setECX(long) setECX +@ stdcall c_setEDI(long) setEDI +@ stdcall c_setEDX(long) setEDX +@ stdcall c_setEIP(long) setEIP +@ stdcall c_setES(long) setES +@ stdcall c_setESI(long) setESI +@ stdcall c_setESP(long) setESP +@ stdcall c_setFS(long) setFS +@ stdcall c_setGS(long) setGS +@ stdcall c_setIF(long) setIF +@ stdcall c_setIP(long) setIP +@ stdcall c_setMSW(long) setMSW +@ stdcall c_setOF(long) setOF +@ stdcall c_setPF(long) setPF +@ stdcall c_setSF(long) setSF +@ stdcall c_setSI(long) setSI +@ stdcall c_setSP(long) setSP +@ stdcall c_setSS(long) setSS +@ stdcall c_setZF(long) setZF + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM DOS-32 Emulation exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +@ stdcall demClientErrorEx(long long long) +@ stdcall demFileDelete(ptr) +@ stdcall demFileFindFirst(ptr ptr long) +@ stdcall demFileFindNext(ptr) +;@ stdcall demGetFileTimeByHandle_WOW +@ stdcall demGetPhysicalDriveType(long) +@ stdcall demIsShortPathName(ptr long) +;@ stdcall demLFNCleanup +;@ stdcall demLFNGetCurrentDirectory +@ stdcall demSetCurrentDirectoryGetDrive(ptr ptr) +;@ stdcall demWOWLFNAllocateSearchHandle +;@ stdcall demWOWLFNCloseSearchHandle +;@ stdcall demWOWLFNEntry +;@ stdcall demWOWLFNGetSearchHandle +;@ stdcall demWOWLFNInit + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NTVDM Miscellaneous exports ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +@ stdcall MGetVdmPointer(long long long) +@ stdcall Sim32pGetVDMPointer(long long) + +;@ stdcall VdmFlushCache(long long long long) ; Not exported on x86 +@ stdcall VdmMapFlat(long long long) +;@ stdcall VdmUnmapFlat(long long ptr long) ; Not exported on x86 + +@ stdcall call_ica_hw_interrupt(long long long) +@ stdcall VDDInstallIOHook(long long ptr ptr) +@ stdcall VDDDeInstallIOHook(long long ptr) + +@ stdcall VDDSimulate16() +@ stdcall host_simulate() VDDSimulate16 +@ stdcall VDDTerminateVDM() diff --git a/reactos/subsystems/ntvdm/registers.c b/reactos/subsystems/ntvdm/registers.c new file mode 100644 index 0000000000000..d3d5ce166759f --- /dev/null +++ b/reactos/subsystems/ntvdm/registers.c @@ -0,0 +1,683 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: registers.c + * PURPOSE: Exported functions for manipulating registers + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" + +/* PUBLIC FUNCTIONS ***********************************************************/ + +static inline BOOLEAN EmulatorGetFlag(ULONG Flag) +{ + return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE; +} + +static inline VOID EmulatorSetFlag(ULONG Flag) +{ + EmulatorContext.Flags.Long |= Flag; +} + +static inline VOID EmulatorClearFlag(ULONG Flag) +{ + EmulatorContext.Flags.Long &= ~Flag; +} + +VOID EmulatorSetStack(WORD Segment, DWORD Offset) +{ + Fast486SetStack(&EmulatorContext, Segment, Offset); +} + + + +PVOID +WINAPI +getIntelRegistersPointer(VOID) +{ + UNIMPLEMENTED; + return NULL; +} + +ULONG +WINAPI +getEAX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long; +} + +VOID +WINAPI +setEAX(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long = Value; +} + +USHORT +WINAPI +getAX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EAX].LowWord; +} + +VOID +WINAPI +setAX(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EAX].LowWord = Value; +} + +UCHAR +WINAPI +getAH(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EAX].HighByte; +} + +VOID +WINAPI +setAH(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EAX].HighByte = Value; +} + +UCHAR +WINAPI +getAL(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EAX].LowByte; +} + +VOID +WINAPI +setAL(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EAX].LowByte = Value; +} + +ULONG +WINAPI +getEBX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long; +} + +VOID +WINAPI +setEBX(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long = Value; +} + +USHORT +WINAPI +getBX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EBX].LowWord; +} + +VOID +WINAPI +setBX(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EBX].LowWord = Value; +} + +UCHAR +WINAPI +getBH(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EBX].HighByte; +} + +VOID +WINAPI +setBH(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EBX].HighByte = Value; +} + +UCHAR +WINAPI +getBL(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EBX].LowByte; +} + +VOID +WINAPI +setBL(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EBX].LowByte = Value; +} + + + +ULONG +WINAPI +getECX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long; +} + +VOID +WINAPI +setECX(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long = Value; +} + +USHORT +WINAPI +getCX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ECX].LowWord; +} + +VOID +WINAPI +setCX(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_ECX].LowWord = Value; +} + +UCHAR +WINAPI +getCH(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ECX].HighByte; +} + +VOID +WINAPI +setCH(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_ECX].HighByte = Value; +} + +UCHAR +WINAPI +getCL(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ECX].LowByte; +} + +VOID +WINAPI +setCL(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_ECX].LowByte = Value; +} + + + +ULONG +WINAPI +getEDX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long; +} + +VOID +WINAPI +setEDX(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long = Value; +} + +USHORT +WINAPI +getDX(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EDX].LowWord; +} + +VOID +WINAPI +setDX(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EDX].LowWord = Value; +} + +UCHAR +WINAPI +getDH(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EDX].HighByte; +} + +VOID +WINAPI +setDH(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EDX].HighByte = Value; +} + +UCHAR +WINAPI +getDL(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EDX].LowByte; +} + +VOID +WINAPI +setDL(UCHAR Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EDX].LowByte = Value; +} + + + +ULONG +WINAPI +getESP(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ESP].Long; +} + +VOID +WINAPI +setESP(ULONG Value) +{ + EmulatorSetStack(getSS(), Value); +} + +USHORT +WINAPI +getSP(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ESP].LowWord; +} + +VOID +WINAPI +setSP(USHORT Value) +{ + EmulatorSetStack(getSS(), Value); +} + + + +ULONG +WINAPI +getEBP(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EBP].Long; +} + +VOID +WINAPI +setEBP(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EBP].Long = Value; +} + +USHORT +WINAPI +getBP(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EBP].LowWord; +} + +VOID +WINAPI +setBP(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EBP].LowWord = Value; +} + + + +ULONG +WINAPI +getESI(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long; +} + +VOID +WINAPI +setESI(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long = Value; +} + +USHORT +WINAPI +getSI(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_ESI].LowWord; +} + +VOID +WINAPI +setSI(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_ESI].LowWord = Value; +} + + + +ULONG +WINAPI +getEDI(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long; +} + +VOID +WINAPI +setEDI(ULONG Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long = Value; +} + +USHORT +WINAPI +getDI(VOID) +{ + return EmulatorContext.GeneralRegs[FAST486_REG_EDI].LowWord; +} + +VOID +WINAPI +setDI(USHORT Value) +{ + EmulatorContext.GeneralRegs[FAST486_REG_EDI].LowWord = Value; +} + + + +ULONG +WINAPI +getEIP(VOID) +{ + return EmulatorContext.InstPtr.Long; +} + +VOID +WINAPI +setEIP(ULONG Value) +{ + EmulatorExecute(getCS(), Value); +} + +USHORT +WINAPI +getIP(VOID) +{ + return EmulatorContext.InstPtr.LowWord; +} + +VOID +WINAPI +setIP(USHORT Value) +{ + EmulatorExecute(getCS(), Value); +} + + + +USHORT +WINAPI +getCS(VOID) +{ + return EmulatorContext.SegmentRegs[FAST486_REG_CS].Selector; +} + +VOID +WINAPI +setCS(USHORT Value) +{ + Fast486SetSegment(&EmulatorContext, FAST486_REG_CS, Value); +} + +USHORT +WINAPI +getSS(VOID) +{ + return EmulatorContext.SegmentRegs[FAST486_REG_SS].Selector; +} + +VOID +WINAPI +setSS(USHORT Value) +{ + Fast486SetSegment(&EmulatorContext, FAST486_REG_SS, Value); +} + +USHORT +WINAPI +getDS(VOID) +{ + return EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector; +} + +VOID +WINAPI +setDS(USHORT Value) +{ + Fast486SetSegment(&EmulatorContext, FAST486_REG_DS, Value); +} + +USHORT +WINAPI +getES(VOID) +{ + return EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector; +} + +VOID +WINAPI +setES(USHORT Value) +{ + Fast486SetSegment(&EmulatorContext, FAST486_REG_ES, Value); +} + +USHORT +WINAPI +getFS(VOID) +{ + return EmulatorContext.SegmentRegs[FAST486_REG_FS].Selector; +} + +VOID +WINAPI +setFS(USHORT Value) +{ + Fast486SetSegment(&EmulatorContext, FAST486_REG_FS, Value); +} + +USHORT +WINAPI +getGS(VOID) +{ + return EmulatorContext.SegmentRegs[FAST486_REG_GS].Selector; +} + +VOID +WINAPI +setGS(USHORT Value) +{ + Fast486SetSegment(&EmulatorContext, FAST486_REG_GS, Value); +} + + + +ULONG +WINAPI +getCF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_CF); +} + +VOID +WINAPI +setCF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_CF); + else + EmulatorClearFlag(EMULATOR_FLAG_CF); +} + +ULONG +WINAPI +getPF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_PF); +} + +VOID +WINAPI +setPF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_PF); + else + EmulatorClearFlag(EMULATOR_FLAG_PF); +} + +ULONG +WINAPI +getAF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_AF); +} + +VOID +WINAPI +setAF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_AF); + else + EmulatorClearFlag(EMULATOR_FLAG_AF); +} + +ULONG +WINAPI +getZF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_ZF); +} + +VOID +WINAPI +setZF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_ZF); + else + EmulatorClearFlag(EMULATOR_FLAG_ZF); +} + +ULONG +WINAPI +getSF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_SF); +} + +VOID +WINAPI +setSF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_SF); + else + EmulatorClearFlag(EMULATOR_FLAG_SF); +} + +ULONG +WINAPI +getIF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_IF); +} + +VOID +WINAPI +setIF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_IF); + else + EmulatorClearFlag(EMULATOR_FLAG_IF); +} + +ULONG +WINAPI +getDF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_DF); +} + +VOID +WINAPI +setDF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_DF); + else + EmulatorClearFlag(EMULATOR_FLAG_DF); +} + +ULONG +WINAPI +getOF(VOID) +{ + return EmulatorGetFlag(EMULATOR_FLAG_OF); +} + +VOID +WINAPI +setOF(ULONG Flag) +{ + if (Flag & 1) + EmulatorSetFlag(EMULATOR_FLAG_OF); + else + EmulatorClearFlag(EMULATOR_FLAG_OF); +} + + + +ULONG +WINAPI +getEFLAGS(VOID) +{ + return EmulatorContext.Flags.Long; +} + +VOID +WINAPI +setEFLAGS(ULONG Flags) +{ + EmulatorContext.Flags.Long = Flags; +} + + + +USHORT +WINAPI +getMSW(VOID) +{ + return LOWORD(EmulatorContext.ControlRegisters[FAST486_REG_CR0]); +} + +VOID +WINAPI +setMSW(USHORT Value) +{ + /* Set the lower 16 bits (Machine Status Word) of CR0 */ + EmulatorContext.ControlRegisters[FAST486_REG_CR0] &= 0xFFFF0000; + EmulatorContext.ControlRegisters[FAST486_REG_CR0] |= Value & 0xFFFF; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/registers.h b/reactos/subsystems/ntvdm/registers.h new file mode 100644 index 0000000000000..09568f3b7a843 --- /dev/null +++ b/reactos/subsystems/ntvdm/registers.h @@ -0,0 +1,123 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: registers.c + * PURPOSE: Exported functions for manipulating registers + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _REGISTERS_H_ +#define _REGISTERS_H_ + +/* INCLUDES *******************************************************************/ + +VOID EmulatorSetStack(WORD Segment, DWORD Offset); + +#if 0 // Those function prototypes are already included via ddk/vddsvc.h + +PVOID WINAPI getIntelRegistersPointer(VOID); + +ULONG WINAPI getEAX(VOID); +VOID WINAPI setEAX(ULONG); +USHORT WINAPI getAX(VOID); +VOID WINAPI setAX(USHORT); +UCHAR WINAPI getAH(VOID); +VOID WINAPI setAH(UCHAR); +UCHAR WINAPI getAL(VOID); +VOID WINAPI setAL(UCHAR); + +ULONG WINAPI getEBX(VOID); +VOID WINAPI setEBX(ULONG); +USHORT WINAPI getBX(VOID); +VOID WINAPI setBX(USHORT); +UCHAR WINAPI getBH(VOID); +VOID WINAPI setBH(UCHAR); +UCHAR WINAPI getBL(VOID); +VOID WINAPI setBL(UCHAR); + +ULONG WINAPI getECX(VOID); +VOID WINAPI setECX(ULONG); +USHORT WINAPI getCX(VOID); +VOID WINAPI setCX(USHORT); +UCHAR WINAPI getCH(VOID); +VOID WINAPI setCH(UCHAR); +UCHAR WINAPI getCL(VOID); +VOID WINAPI setCL(UCHAR); + +ULONG WINAPI getEDX(VOID); +VOID WINAPI setEDX(ULONG); +USHORT WINAPI getDX(VOID); +VOID WINAPI setDX(USHORT); +UCHAR WINAPI getDH(VOID); +VOID WINAPI setDH(UCHAR); +UCHAR WINAPI getDL(VOID); +VOID WINAPI setDL(UCHAR); + + + +ULONG WINAPI getESP(VOID); +VOID WINAPI setESP(ULONG); +USHORT WINAPI getSP(VOID); +VOID WINAPI setSP(USHORT); + +ULONG WINAPI getEBP(VOID); +VOID WINAPI setEBP(ULONG); +USHORT WINAPI getBP(VOID); +VOID WINAPI setBP(USHORT); + +ULONG WINAPI getESI(VOID); +VOID WINAPI setESI(ULONG); +USHORT WINAPI getSI(VOID); +VOID WINAPI setSI(USHORT); + +ULONG WINAPI getEDI(VOID); +VOID WINAPI setEDI(ULONG); +USHORT WINAPI getDI(VOID); +VOID WINAPI setDI(USHORT); + +ULONG WINAPI getEIP(VOID); +VOID WINAPI setEIP(ULONG); +USHORT WINAPI getIP(VOID); +VOID WINAPI setIP(USHORT); + +USHORT WINAPI getCS(VOID); +VOID WINAPI setCS(USHORT); +USHORT WINAPI getSS(VOID); +VOID WINAPI setSS(USHORT); +USHORT WINAPI getDS(VOID); +VOID WINAPI setDS(USHORT); +USHORT WINAPI getES(VOID); +VOID WINAPI setES(USHORT); +USHORT WINAPI getFS(VOID); +VOID WINAPI setFS(USHORT); +USHORT WINAPI getGS(VOID); +VOID WINAPI setGS(USHORT); + +ULONG WINAPI getCF(VOID); +VOID WINAPI setCF(ULONG); +ULONG WINAPI getPF(VOID); +VOID WINAPI setPF(ULONG); +ULONG WINAPI getAF(VOID); +VOID WINAPI setAF(ULONG); +ULONG WINAPI getZF(VOID); +VOID WINAPI setZF(ULONG); +ULONG WINAPI getSF(VOID); +VOID WINAPI setSF(ULONG); +ULONG WINAPI getIF(VOID); +VOID WINAPI setIF(ULONG); +ULONG WINAPI getDF(VOID); +VOID WINAPI setDF(ULONG); +ULONG WINAPI getOF(VOID); +VOID WINAPI setOF(ULONG); + +ULONG WINAPI getEFLAGS(VOID); +VOID WINAPI setEFLAGS(ULONG); + +USHORT WINAPI getMSW(VOID); +VOID WINAPI setMSW(USHORT); + +#endif + +#endif // _REGISTERS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/res/ntvdm.ico b/reactos/subsystems/ntvdm/res/ntvdm.ico new file mode 100644 index 0000000000000..161c1b07a9a02 Binary files /dev/null and b/reactos/subsystems/ntvdm/res/ntvdm.ico differ diff --git a/reactos/subsystems/ntvdm/resource.h b/reactos/subsystems/ntvdm/resource.h index e7572adbb7bee..f7a6f3c2a1b0a 100644 --- a/reactos/subsystems/ntvdm/resource.h +++ b/reactos/subsystems/ntvdm/resource.h @@ -1,5 +1,14 @@ #pragma once -#define RC_STRING_MAX_SIZE 2048 -#define STRING_WelcomeMsg 100 -#define STRING_PromptMsg 101 +#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 */ diff --git a/reactos/subsystems/ntvdm/utils.c b/reactos/subsystems/ntvdm/utils.c new file mode 100644 index 0000000000000..0619376ee9850 --- /dev/null +++ b/reactos/subsystems/ntvdm/utils.c @@ -0,0 +1,87 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: utils.c + * PURPOSE: Utility Functions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +FileClose(IN HANDLE FileHandle) +{ + CloseHandle(FileHandle); +} + +HANDLE +FileOpen(IN PCSTR FileName, + OUT PULONG FileSize OPTIONAL) +{ + HANDLE hFile; + ULONG ulFileSize; + + /* Open the file */ + SetLastError(0); // For debugging purposes + hFile = CreateFileA(FileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + DPRINT1("File '%s' opening %s ; GetLastError() = %u\n", + FileName, hFile != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError()); + + /* If we failed, bail out */ + if (hFile == INVALID_HANDLE_VALUE) return NULL; + + /* OK, we have a handle to the file */ + + /* + * Retrieve the size of the file. In NTVDM we will handle files + * of maximum 1Mb so we can largely use GetFileSize only. + */ + ulFileSize = GetFileSize(hFile, NULL); + if (ulFileSize == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS) + { + /* We failed, bail out */ + DPRINT1("Error when retrieving file size, or size too large (%d)\n", ulFileSize); + FileClose(hFile); + return NULL; + } + + /* Success, return file handle and size if needed */ + if (FileSize) *FileSize = ulFileSize; + return hFile; +} + +BOOLEAN +FileLoadByHandle(IN HANDLE FileHandle, + IN PVOID Location, + IN ULONG FileSize, + OUT PULONG BytesRead) +{ + BOOLEAN Success; + + /* Attempt to load the file into memory */ + SetLastError(0); // For debugging purposes + Success = !!ReadFile(FileHandle, + Location, // REAL_TO_PHYS(LocationRealPtr), + FileSize, + BytesRead, + NULL); + DPRINT1("File loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError()); + + return Success; +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/utils.h b/reactos/subsystems/ntvdm/utils.h new file mode 100644 index 0000000000000..98ce0936a963d --- /dev/null +++ b/reactos/subsystems/ntvdm/utils.h @@ -0,0 +1,33 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: utils.h + * PURPOSE: Utility Functions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* FUNCTIONS ******************************************************************/ + +VOID +FileClose(IN HANDLE FileHandle); + +HANDLE +FileOpen(IN PCSTR FileName, + OUT PULONG FileSize OPTIONAL); + +BOOLEAN +FileLoadByHandle(IN HANDLE FileHandle, + IN PVOID Location, + IN ULONG FileSize, + OUT PULONG BytesRead); + +#endif // _UTILS_H_ + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/vddsup.c b/reactos/subsystems/ntvdm/vddsup.c new file mode 100644 index 0000000000000..92a0ff1ac3b19 --- /dev/null +++ b/reactos/subsystems/ntvdm/vddsup.c @@ -0,0 +1,358 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vddsup.c + * PURPOSE: Virtual Device Drivers (VDD) Support + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "vddsup.h" + +#include "bop.h" + +#include + +typedef VOID (WINAPI *VDD_PROC)(VOID); + +typedef struct _VDD_MODULE +{ + HMODULE hDll; + VDD_PROC DispatchRoutine; +} VDD_MODULE, *PVDD_MODULE; + +/* PRIVATE VARIABLES **********************************************************/ + +// TODO: Maybe use a linked list. +// But the number of elements must be <= MAXUSHORT (MAXWORD) +#define MAX_VDD_MODULES 0xFF + 1 +VDD_MODULE VDDList[MAX_VDD_MODULES] = {{NULL}}; + +// Valid handles of VDD DLLs start at 1 and finish at MAX_VDD_MODULES +#define ENTRY_TO_HANDLE(Entry) ((Entry) + 1) +#define HANDLE_TO_ENTRY(Handle) ((Handle) - 1) +#define IS_VALID_HANDLE(Handle) ((Handle) > 0 && (Handle) <= MAX_VDD_MODULES) + +/* PRIVATE FUNCTIONS **********************************************************/ + +USHORT GetNextFreeVDDEntry(VOID) +{ + USHORT Entry = MAX_VDD_MODULES; + for (Entry = 0; Entry < sizeof(VDDList)/sizeof(VDDList[0]); ++Entry) + { + if (VDDList[Entry].hDll == NULL) break; + } + return Entry; +} + +VOID WINAPI ThirdPartyVDDBop(LPWORD Stack) +{ + /* Get the Function Number and skip it */ + BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); + setIP(getIP() + 1); + + switch (FuncNum) + { + /* RegisterModule */ + case 0: + { + BOOL Success = TRUE; + WORD RetVal = 0; + WORD Entry = 0; + LPCSTR DllName = NULL, + InitRoutineName = NULL, + DispatchRoutineName = NULL; + HMODULE hDll = NULL; + VDD_PROC InitRoutine = NULL, + DispatchRoutine = NULL; + + DPRINT("RegisterModule() called\n"); + + /* Clear the Carry Flag (no error happened so far) */ + setCF(0); + + /* Retrieve the next free entry in the table (used later on) */ + Entry = GetNextFreeVDDEntry(); + if (Entry >= MAX_VDD_MODULES) + { + DPRINT1("Failed to create a new VDD module entry\n"); + Success = FALSE; + RetVal = 4; + goto Quit; + } + + /* Retrieve the VDD name in DS:SI */ + DllName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()); + + /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */ + if (TO_LINEAR(getES(), getDI()) != 0) + InitRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getES(), getDI()); + + /* Retrieve the dispatch routine API name in DS:BX */ + DispatchRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getBX()); + + DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n", + (DllName ? DllName : "n/a"), + (InitRoutineName ? InitRoutineName : "n/a"), + (DispatchRoutineName ? DispatchRoutineName : "n/a")); + + /* Load the VDD DLL */ + hDll = LoadLibraryA(DllName); + if (hDll == NULL) + { + DWORD LastError = GetLastError(); + Success = FALSE; + + if (LastError == ERROR_NOT_ENOUGH_MEMORY) + { + DPRINT1("Not enough memory to load DLL '%s'\n", DllName); + RetVal = 4; + goto Quit; + } + else + { + DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName, LastError); + RetVal = 1; + goto Quit; + } + } + + /* Load the initialization routine if needed */ + if (InitRoutineName) + { + InitRoutine = (VDD_PROC)GetProcAddress(hDll, InitRoutineName); + if (InitRoutine == NULL) + { + DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName); + Success = FALSE; + RetVal = 3; + goto Quit; + } + } + + /* Load the dispatch routine */ + DispatchRoutine = (VDD_PROC)GetProcAddress(hDll, DispatchRoutineName); + if (DispatchRoutine == NULL) + { + DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName); + Success = FALSE; + RetVal = 2; + goto Quit; + } + + /* If we arrived there, that means everything is OK */ + + /* Register the VDD DLL */ + VDDList[Entry].hDll = hDll; + VDDList[Entry].DispatchRoutine = DispatchRoutine; + + /* Call the initialization routine if needed */ + if (InitRoutine) InitRoutine(); + + /* We succeeded. RetVal will contain a valid VDD DLL handle */ + Success = TRUE; + RetVal = ENTRY_TO_HANDLE(Entry); // Convert the entry to a valid handle + +Quit: + if (!Success) + { + /* Unload the VDD DLL */ + if (hDll) FreeLibrary(hDll); + + /* Set the Carry Flag to indicate that an error happened */ + setCF(1); + } + // else + // { + // /* Clear the Carry Flag (success) */ + // setCF(0); + // } + setAX(RetVal); + break; + } + + /* UnRegisterModule */ + case 1: + { + WORD Handle = getAX(); + WORD Entry = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry + + DPRINT("UnRegisterModule() called\n"); + + /* Sanity checks */ + if (!IS_VALID_HANDLE(Handle) || VDDList[Entry].hDll == NULL) + { + DPRINT1("Invalid VDD DLL Handle: %d\n", Entry); + /* Stop the VDM */ + EmulatorTerminate(); + return; + } + + /* Unregister the VDD DLL */ + FreeLibrary(VDDList[Entry].hDll); + VDDList[Entry].hDll = NULL; + VDDList[Entry].DispatchRoutine = NULL; + break; + } + + /* DispatchCall */ + case 2: + { + WORD Handle = getAX(); + WORD Entry = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry + + DPRINT("DispatchCall() called\n"); + + /* Sanity checks */ + if (!IS_VALID_HANDLE(Handle) || + VDDList[Entry].hDll == NULL || + VDDList[Entry].DispatchRoutine == NULL) + { + DPRINT1("Invalid VDD DLL Handle: %d\n", Entry); + /* Stop the VDM */ + EmulatorTerminate(); + return; + } + + /* Call the dispatch routine */ + VDDList[Entry].DispatchRoutine(); + break; + } + + default: + { + DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum); + setCF(1); + break; + } + } +} + +BOOL LoadInstallableVDD(VOID) +{ +#define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers." +#define ERROR_REGVDD L"Virtual Device Driver format in the registry is invalid." +#define ERROR_LOADVDD L"An installable Virtual Device Driver failed Dll initialization." + + BOOL Success = TRUE; + LONG Error = 0; + DWORD Type = 0; + DWORD BufSize = 0; + + HKEY hVDDKey; + LPCWSTR VDDKeyName = L"SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers"; + LPWSTR VDDValueName = L"VDD"; + LPWSTR VDDList = NULL; + + HANDLE hVDD; + + /* Try to open the VDD registry key */ + Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + VDDKeyName, + 0, + KEY_QUERY_VALUE, + &hVDDKey); + if (Error == ERROR_FILE_NOT_FOUND) + { + /* If the key just doesn't exist, don't do anything else */ + return TRUE; + } + else if (Error != ERROR_SUCCESS) + { + /* The key exists but there was an access error: display an error and quit */ + DisplayMessage(ERROR_REGVDD); + return FALSE; + } + + /* + * Retrieve the size of the VDD registry value + * and check that it's of REG_MULTI_SZ type. + */ + Error = RegQueryValueExW(hVDDKey, + VDDValueName, + NULL, + &Type, + NULL, + &BufSize); + if (Error == ERROR_FILE_NOT_FOUND) + { + /* If the value just doesn't exist, don't do anything else */ + Success = TRUE; + goto Quit; + } + else if (Error != ERROR_SUCCESS || Type != REG_MULTI_SZ) + { + /* + * The value exists but there was an access error or + * is of the wrong type: display an error and quit. + */ + DisplayMessage(ERROR_REGVDD); + Success = FALSE; + goto Quit; + } + + /* Allocate the buffer */ + BufSize = (BufSize < 2*sizeof(WCHAR) ? 2*sizeof(WCHAR) : BufSize); + VDDList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BufSize); + if (VDDList == NULL) + { + DisplayMessage(ERROR_MEMORYVDD); + Success = FALSE; + goto Quit; + } + + /* Retrieve the list of VDDs to load */ + if (RegQueryValueExW(hVDDKey, + VDDValueName, + NULL, + NULL, + (LPBYTE)VDDList, + &BufSize) != ERROR_SUCCESS) + { + DisplayMessage(ERROR_REGVDD); + Success = FALSE; + goto Quit; + } + + /* Load the VDDs */ + VDDValueName = VDDList; + while (*VDDList) + { + DPRINT1("Loading VDD '%S'...", VDDList); + hVDD = LoadLibraryW(VDDList); + if (hVDD == NULL) + { + DbgPrint("Failed\n"); + DisplayMessage(ERROR_LOADVDD); + } + else + { + DbgPrint("Succeeded\n"); + } + /* Go to next string */ + VDDList += wcslen(VDDList) + 1; + } + VDDList = VDDValueName; + +Quit: + if (VDDList) HeapFree(GetProcessHeap(), 0, VDDList); + RegCloseKey(hVDDKey); + return Success; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID VDDSupInitialize(VOID) +{ + /* Register the 3rd-party VDD BOP Handler */ + RegisterBop(BOP_3RDPARTY, ThirdPartyVDDBop); + + /* Load the installable VDDs from the registry */ + LoadInstallableVDD(); +} + +/* EOF */ diff --git a/reactos/subsystems/ntvdm/vddsup.h b/reactos/subsystems/ntvdm/vddsup.h new file mode 100644 index 0000000000000..4de9178a3eace --- /dev/null +++ b/reactos/subsystems/ntvdm/vddsup.h @@ -0,0 +1,20 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: vddsup.h + * PURPOSE: Virtual Device Drivers (VDD) Support + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _VDDSUP_H_ +#define _VDDSUP_H_ + +/* DEFINES ********************************************************************/ + +/* FUNCTIONS ******************************************************************/ + +VOID VDDSupInitialize(VOID); + +#endif // _VDDSUP_H_ + +/* EOF */ diff --git a/reactos/subsystems/win/basesrv/basesrv.h b/reactos/subsystems/win/basesrv/basesrv.h index 66e4efcdcf468..881156d2a309b 100644 --- a/reactos/subsystems/win/basesrv/basesrv.h +++ b/reactos/subsystems/win/basesrv/basesrv.h @@ -22,6 +22,7 @@ #include #include #include +#include /* PSEH for SEH Support */ #include diff --git a/reactos/subsystems/win/basesrv/init.c b/reactos/subsystems/win/basesrv/init.c index 49e9485304c7b..ef98ed3f6ffa7 100644 --- a/reactos/subsystems/win/basesrv/init.c +++ b/reactos/subsystems/win/basesrv/init.c @@ -9,6 +9,7 @@ /* INCLUDES *******************************************************************/ #include "basesrv.h" +#include "vdm.h" #include @@ -569,6 +570,14 @@ BaseInitializeStaticServerData(IN PCSR_SERVER_DLL LoadedServerDll) LoadedServerDll->SharedSection = BaseStaticServerData; } +VOID +NTAPI +BaseSrvDisconnect(PCSR_PROCESS Process) +{ + /* Cleanup the VDM console records */ + BaseSrvCleanupVdmRecords(HandleToUlong(Process->ClientId.UniqueProcess)); +} + CSR_SERVER_DLL_INIT(ServerDllInitialization) { /* Setup the DLL Object */ @@ -581,7 +590,7 @@ CSR_SERVER_DLL_INIT(ServerDllInitialization) #endif LoadedServerDll->SizeOfProcessData = 0; LoadedServerDll->ConnectCallback = NULL; - LoadedServerDll->DisconnectCallback = NULL; + LoadedServerDll->DisconnectCallback = BaseSrvDisconnect; LoadedServerDll->ShutdownProcessCallback = NULL; BaseSrvDllInstance = LoadedServerDll->ServerHandle; @@ -591,6 +600,9 @@ CSR_SERVER_DLL_INIT(ServerDllInitialization) /* Initialize DOS devices management */ BaseInitDefineDosDevice(); + /* Initialize VDM support */ + BaseInitializeVDM(); + /* All done */ return STATUS_SUCCESS; } diff --git a/reactos/subsystems/win/basesrv/proc.c b/reactos/subsystems/win/basesrv/proc.c index e61405c0f0e31..53113f7edda40 100644 --- a/reactos/subsystems/win/basesrv/proc.c +++ b/reactos/subsystems/win/basesrv/proc.c @@ -9,6 +9,7 @@ /* INCLUDES *******************************************************************/ #include "basesrv.h" +#include "vdm.h" #define NDEBUG #include @@ -167,7 +168,30 @@ CSR_API(BaseSrvCreateProcess) /* FIXME: Should notify user32 */ - /* FIXME: VDM vodoo */ + /* Check if this is a VDM process */ + if (CreateProcessRequest->VdmBinaryType) + { + PVDM_CONSOLE_RECORD ConsoleRecord; + + if (CreateProcessRequest->VdmTask != 0) + { + /* Get the console record using the task ID */ + Status = GetConsoleRecordBySessionId(CreateProcessRequest->VdmTask, + &ConsoleRecord); + } + else + { + /* Get the console record using the console handle */ + Status = BaseSrvGetConsoleRecord(CreateProcessRequest->hVDM, + &ConsoleRecord); + } + + /* Check if it failed */ + if (!NT_SUCCESS(Status)) return Status; + + /* Store the process ID of the VDM in the console record */ + ConsoleRecord->ProcessId = HandleToUlong(CreateProcessRequest->ClientId.UniqueProcess); + } /* Return the result of this operation */ return Status; diff --git a/reactos/subsystems/win/basesrv/vdm.c b/reactos/subsystems/win/basesrv/vdm.c index 2ac45655e8e02..415662046935e 100644 --- a/reactos/subsystems/win/basesrv/vdm.c +++ b/reactos/subsystems/win/basesrv/vdm.c @@ -4,69 +4,1326 @@ * FILE: subsystems/win/basesrv/vdm.c * PURPOSE: Virtual DOS Machines (VDM) Support * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * Aleksandar Andrejevic */ /* INCLUDES *******************************************************************/ #include "basesrv.h" +#include "vdm.h" #define NDEBUG #include +/* GLOBALS ********************************************************************/ + +BOOLEAN FirstVDM = TRUE; +LIST_ENTRY VDMConsoleListHead; +RTL_CRITICAL_SECTION DosCriticalSection; +RTL_CRITICAL_SECTION WowCriticalSection; + +/* FUNCTIONS ******************************************************************/ + +NTSTATUS NTAPI BaseSrvGetConsoleRecord(HANDLE ConsoleHandle, PVDM_CONSOLE_RECORD *Record) +{ + PLIST_ENTRY i; + PVDM_CONSOLE_RECORD CurrentRecord = NULL; + + /* NULL is not a valid console handle */ + if (ConsoleHandle == NULL) return STATUS_INVALID_PARAMETER; + + /* Search for a record that has the same console handle */ + for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink) + { + CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry); + if (CurrentRecord->ConsoleHandle == ConsoleHandle) break; + } + + /* Check if nothing was found */ + if (i == &VDMConsoleListHead) CurrentRecord = NULL; + + *Record = CurrentRecord; + return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND; +} + +NTSTATUS NTAPI GetConsoleRecordBySessionId(ULONG TaskId, PVDM_CONSOLE_RECORD *Record) +{ + PLIST_ENTRY i; + PVDM_CONSOLE_RECORD CurrentRecord = NULL; + + /* Search for a record that has the same console handle */ + for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink) + { + CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry); + if (CurrentRecord->SessionId == TaskId) break; + } + + /* Check if nothing was found */ + if (i == &VDMConsoleListHead) CurrentRecord = NULL; + + *Record = CurrentRecord; + return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND; +} + +ULONG NTAPI GetNextDosSesId(VOID) +{ + ULONG SessionId; + PLIST_ENTRY i; + PVDM_CONSOLE_RECORD CurrentRecord = NULL; + BOOLEAN Found; + + /* Search for an available session ID */ + for (SessionId = 1; SessionId != 0; SessionId++) + { + Found = FALSE; + + /* Check if the ID is already in use */ + for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink) + { + CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry); + if (CurrentRecord->SessionId == SessionId) Found = TRUE; + } + + /* If not, we found one */ + if (!Found) break; + } + + ASSERT(SessionId != 0); + + /* Return the session ID */ + return SessionId; +} + +BOOLEAN NTAPI BaseSrvIsVdmAllowed(VOID) +{ + NTSTATUS Status; + BOOLEAN VdmAllowed = TRUE; + HANDLE RootKey, KeyHandle; + UNICODE_STRING KeyName, ValueName, MachineKeyName; + OBJECT_ATTRIBUTES Attributes; + UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; + ULONG ActualSize; + + /* Initialize the unicode strings */ + RtlInitUnicodeString(&MachineKeyName, L"\\Registry\\Machine"); + RtlInitUnicodeString(&KeyName, VDM_POLICY_KEY_NAME); + RtlInitUnicodeString(&ValueName, VDM_DISALLOWED_VALUE_NAME); + + InitializeObjectAttributes(&Attributes, + &MachineKeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + /* Open the local machine key */ + Status = NtOpenKey(&RootKey, KEY_READ, &Attributes); + if (!NT_SUCCESS(Status)) return FALSE; + + InitializeObjectAttributes(&Attributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + RootKey, + NULL); + + /* Open the policy key in the local machine hive, if it exists */ + if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes))) + { + /* Read the value, if it's set */ + if (NT_SUCCESS(NtQueryValueKey(KeyHandle, + &ValueName, + KeyValuePartialInformation, + ValueInfo, + sizeof(ValueBuffer), + &ActualSize))) + { + if (*((PULONG)ValueInfo->Data)) + { + /* The VDM has been disabled in the registry */ + VdmAllowed = FALSE; + } + } + + NtClose(KeyHandle); + } + + /* Close the local machine key */ + NtClose(RootKey); + + /* If it's disabled system-wide, there's no need to check the user key */ + if (!VdmAllowed) return FALSE; + + /* Open the current user key of the client */ + if (!CsrImpersonateClient(NULL)) return VdmAllowed; + Status = RtlOpenCurrentUser(KEY_READ, &RootKey); + CsrRevertToSelf(); + + /* If that fails, return the system-wide setting */ + if (!NT_SUCCESS(Status)) return VdmAllowed; + + InitializeObjectAttributes(&Attributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + RootKey, + NULL); + + /* Open the policy key in the current user hive, if it exists */ + if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes))) + { + /* Read the value, if it's set */ + if (NT_SUCCESS(NtQueryValueKey(KeyHandle, + &ValueName, + KeyValuePartialInformation, + ValueInfo, + sizeof(ValueBuffer), + &ActualSize))) + { + if (*((PULONG)ValueInfo->Data)) + { + /* The VDM has been disabled in the registry */ + VdmAllowed = FALSE; + } + } + + NtClose(KeyHandle); + } + + return VdmAllowed; +} + +NTSTATUS NTAPI BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent) +{ + NTSTATUS Status; + + /* Create the event */ + Status = NtCreateEvent(ServerEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); + if (!NT_SUCCESS(Status)) return Status; + + /* Duplicate the event into the client process */ + Status = NtDuplicateObject(NtCurrentProcess(), + *ServerEvent, + CsrGetClientThread()->Process->ProcessHandle, + ClientEvent, + 0, + 0, + DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS); + + if (!NT_SUCCESS(Status)) NtClose(*ServerEvent); + return Status; +} + +VOID NTAPI BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo) +{ + /* Free the allocated structure members */ + if (CommandInfo->CmdLine != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CmdLine); + if (CommandInfo->AppName != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->AppName); + if (CommandInfo->PifFile != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->PifFile); + if (CommandInfo->CurDirectory != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CurDirectory); + if (CommandInfo->Env != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Env); + if (CommandInfo->Desktop != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Desktop); + if (CommandInfo->Title != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Title); + if (CommandInfo->Reserved != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Reserved); + + /* Free the structure itself */ + RtlFreeHeap(BaseSrvHeap, 0, CommandInfo); +} + +VOID NTAPI BaseSrvCleanupVdmRecords(ULONG ProcessId) +{ + PLIST_ENTRY i; + PVDM_CONSOLE_RECORD ConsoleRecord = NULL; + PVDM_DOS_RECORD DosRecord; + + /* Enter the critical section */ + RtlEnterCriticalSection(&DosCriticalSection); + + /* Search for a record that has the same process handle */ + for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink) + { + ConsoleRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry); + + if (ConsoleRecord->ProcessId == ProcessId) + { + /* Cleanup the DOS records */ + while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead) + { + DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, + VDM_DOS_RECORD, + Entry); + + /* Set the event and close it */ + NtSetEvent(DosRecord->ServerEvent, NULL); + NtClose(DosRecord->ServerEvent); + + /* Remove the DOS entry */ + if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo); + RemoveEntryList(&DosRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, DosRecord); + } + + if (ConsoleRecord->CurrentDirs != NULL) + { + /* Free the current directories */ + RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs); + ConsoleRecord->CurrentDirs = NULL; + ConsoleRecord->CurDirsLength = 0; + } + + /* Close the event handle */ + if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent); + + /* Remove the console record */ + i = i->Blink; + RemoveEntryList(&ConsoleRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord); + } + } + + /* Leave the critical section */ + RtlLeaveCriticalSection(&DosCriticalSection); +} + +BOOLEAN NTAPI BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord) +{ + BOOLEAN Success = FALSE; + PVDM_COMMAND_INFO CommandInfo = NULL; + + /* Allocate the command information structure */ + CommandInfo = (PVDM_COMMAND_INFO)RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + sizeof(VDM_COMMAND_INFO)); + if (CommandInfo == NULL) return FALSE; + + /* Fill the structure */ + CommandInfo->TaskId = CheckVdmRequest->iTask; + CommandInfo->ExitCode = DosRecord->ExitCode; + CommandInfo->CodePage = CheckVdmRequest->CodePage; + CommandInfo->StdIn = CheckVdmRequest->StdIn; + CommandInfo->StdOut = CheckVdmRequest->StdOut; + CommandInfo->StdErr = CheckVdmRequest->StdErr; + + /* Allocate memory for the command line */ + CommandInfo->CmdLine = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->CmdLen); + if (CommandInfo->CmdLine == NULL) goto Cleanup; + + /* Copy the command line */ + RtlMoveMemory(CommandInfo->CmdLine, CheckVdmRequest->CmdLine, CheckVdmRequest->CmdLen); + + /* Allocate memory for the application name */ + CommandInfo->AppName = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->AppLen); + if (CommandInfo->AppName == NULL) goto Cleanup; + + /* Copy the application name */ + RtlMoveMemory(CommandInfo->AppName, CheckVdmRequest->AppName, CheckVdmRequest->AppLen); + + /* Allocate memory for the PIF file name */ + if (CheckVdmRequest->PifLen != 0) + { + CommandInfo->PifFile = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->PifLen); + if (CommandInfo->PifFile == NULL) goto Cleanup; + + /* Copy the PIF file name */ + RtlMoveMemory(CommandInfo->PifFile, CheckVdmRequest->PifFile, CheckVdmRequest->PifLen); + } + else CommandInfo->PifFile = NULL; + + /* Allocate memory for the current directory */ + if (CheckVdmRequest->CurDirectoryLen != 0) + { + CommandInfo->CurDirectory = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->CurDirectoryLen); + if (CommandInfo->CurDirectory == NULL) goto Cleanup; + + /* Copy the current directory */ + RtlMoveMemory(CommandInfo->CurDirectory, + CheckVdmRequest->CurDirectory, + CheckVdmRequest->CurDirectoryLen); + } + else CommandInfo->CurDirectory = NULL; + + /* Allocate memory for the environment block */ + CommandInfo->Env = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->EnvLen); + if (CommandInfo->Env == NULL) goto Cleanup; + + /* Copy the environment block */ + RtlMoveMemory(CommandInfo->Env, CheckVdmRequest->Env, CheckVdmRequest->EnvLen); + + CommandInfo->EnvLen = CheckVdmRequest->EnvLen; + RtlMoveMemory(&CommandInfo->StartupInfo, + CheckVdmRequest->StartupInfo, + sizeof(STARTUPINFOA)); + + /* Allocate memory for the desktop */ + if (CheckVdmRequest->DesktopLen != 0) + { + CommandInfo->Desktop = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->DesktopLen); + if (CommandInfo->Desktop == NULL) goto Cleanup; + + /* Copy the desktop name */ + RtlMoveMemory(CommandInfo->Desktop, CheckVdmRequest->Desktop, CheckVdmRequest->DesktopLen); + } + else CommandInfo->Desktop = NULL; + + CommandInfo->DesktopLen = CheckVdmRequest->DesktopLen; + + /* Allocate memory for the title */ + if (CheckVdmRequest->TitleLen != 0) + { + CommandInfo->Title = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->TitleLen); + if (CommandInfo->Title == NULL) goto Cleanup; + + /* Copy the title */ + RtlMoveMemory(CommandInfo->Title, CheckVdmRequest->Title, CheckVdmRequest->TitleLen); + } + else CommandInfo->Title = NULL; + + CommandInfo->TitleLen = CheckVdmRequest->TitleLen; + + /* Allocate memory for the reserved field */ + if (CheckVdmRequest->ReservedLen != 0) + { + CommandInfo->Reserved = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + CheckVdmRequest->ReservedLen); + if (CommandInfo->Reserved == NULL) goto Cleanup; + + /* Copy the reserved field */ + RtlMoveMemory(CommandInfo->Reserved, + CheckVdmRequest->Reserved, + CheckVdmRequest->ReservedLen); + } + else CommandInfo->Reserved = NULL; + + CommandInfo->ReservedLen = CheckVdmRequest->ReservedLen; + + CommandInfo->CmdLen = CheckVdmRequest->CmdLen; + CommandInfo->AppLen = CheckVdmRequest->AppLen; + CommandInfo->PifLen = CheckVdmRequest->PifLen; + CommandInfo->CurDirectoryLen = CheckVdmRequest->CurDirectoryLen; + CommandInfo->VDMState = DosRecord->State; + // TODO: Set CommandInfo->CurrentDrive + // TODO: Set CommandInfo->ComingFromBat + + /* Set the DOS record's command structure */ + DosRecord->CommandInfo = CommandInfo; + + /* The operation was successful */ + Success = TRUE; + +Cleanup: + /* If it wasn't successful, free the memory */ + if (!Success) BaseSrvFreeVDMInfo(CommandInfo); + + return Success; +} + +NTSTATUS NTAPI BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo, + PBASE_GET_NEXT_VDM_COMMAND Message) +{ + /* Copy the data */ + Message->iTask = CommandInfo->TaskId; + Message->StdIn = CommandInfo->StdIn; + Message->StdOut = CommandInfo->StdOut; + Message->StdErr = CommandInfo->StdErr; + Message->CodePage = CommandInfo->CodePage; + Message->dwCreationFlags = CommandInfo->CreationFlags; + Message->ExitCode = CommandInfo->ExitCode; + Message->CurrentDrive = CommandInfo->CurrentDrive; + Message->VDMState = CommandInfo->VDMState; + Message->fComingFromBat = CommandInfo->ComingFromBat; + + if (CommandInfo->CmdLen && Message->CmdLen) + { + if (Message->CmdLen < CommandInfo->CmdLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the command line */ + RtlMoveMemory(Message->CmdLine, CommandInfo->CmdLine, CommandInfo->CmdLen); + Message->CmdLen = CommandInfo->CmdLen; + } + + if (CommandInfo->AppLen && Message->AppLen) + { + if (Message->AppLen < CommandInfo->CmdLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the application name */ + RtlMoveMemory(Message->AppName, CommandInfo->AppName, CommandInfo->AppLen); + Message->AppLen = CommandInfo->AppLen; + } + + if (CommandInfo->PifLen && Message->PifLen) + { + if (Message->PifLen < CommandInfo->PifLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the PIF file name */ + RtlMoveMemory(Message->PifFile, CommandInfo->PifFile, CommandInfo->PifLen); + Message->PifLen = CommandInfo->PifLen; + } + + if (CommandInfo->CurDirectoryLen && Message->CurDirectoryLen) + { + if (Message->CurDirectoryLen < CommandInfo->CurDirectoryLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the current directory */ + RtlMoveMemory(Message->CurDirectory, CommandInfo->CurDirectory, CommandInfo->CurDirectoryLen); + Message->CurDirectoryLen = CommandInfo->CurDirectoryLen; + } + + if (CommandInfo->EnvLen && Message->EnvLen) + { + if (Message->EnvLen < CommandInfo->EnvLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the environment */ + RtlMoveMemory(Message->Env, CommandInfo->Env, CommandInfo->EnvLen); + Message->EnvLen = CommandInfo->EnvLen; + } + + /* Copy the startup info */ + RtlMoveMemory(Message->StartupInfo, + &CommandInfo->StartupInfo, + sizeof(STARTUPINFOA)); + + if (CommandInfo->DesktopLen && Message->DesktopLen) + { + if (Message->DesktopLen < CommandInfo->DesktopLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the desktop name */ + RtlMoveMemory(Message->Desktop, CommandInfo->Desktop, CommandInfo->DesktopLen); + Message->DesktopLen = CommandInfo->DesktopLen; + } + + if (CommandInfo->TitleLen && Message->TitleLen) + { + if (Message->TitleLen < CommandInfo->TitleLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the title */ + RtlMoveMemory(Message->Title, CommandInfo->Title, CommandInfo->TitleLen); + Message->TitleLen = CommandInfo->TitleLen; + } + + if (CommandInfo->ReservedLen && Message->ReservedLen) + { + if (Message->ReservedLen < CommandInfo->ReservedLen) return STATUS_BUFFER_TOO_SMALL; + + /* Copy the reserved parameter */ + RtlMoveMemory(Message->Reserved, CommandInfo->Reserved, CommandInfo->ReservedLen); + Message->ReservedLen = CommandInfo->ReservedLen; + } + + return STATUS_SUCCESS; +} + +VOID NTAPI BaseInitializeVDM(VOID) +{ + /* Initialize the list head */ + InitializeListHead(&VDMConsoleListHead); + + /* Initialize the critical section */ + RtlInitializeCriticalSection(&DosCriticalSection); + RtlInitializeCriticalSection(&WowCriticalSection); +} + /* PUBLIC SERVER APIS *********************************************************/ CSR_API(BaseSrvCheckVDM) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest; + PRTL_CRITICAL_SECTION CriticalSection = NULL; + PVDM_CONSOLE_RECORD ConsoleRecord = NULL; + PVDM_DOS_RECORD DosRecord = NULL; + BOOLEAN NewConsoleRecord = FALSE; + + /* Don't do anything if the VDM has been disabled in the registry */ + if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED; + + /* Validate the message buffers */ + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->CmdLine, + CheckVdmRequest->CmdLen, + sizeof(*CheckVdmRequest->CmdLine)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->AppName, + CheckVdmRequest->AppLen, + sizeof(*CheckVdmRequest->AppName)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->PifFile, + CheckVdmRequest->PifLen, + sizeof(*CheckVdmRequest->PifFile)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->CurDirectory, + CheckVdmRequest->CurDirectoryLen, + sizeof(*CheckVdmRequest->CurDirectory)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->Desktop, + CheckVdmRequest->DesktopLen, + sizeof(*CheckVdmRequest->Desktop)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->Title, + CheckVdmRequest->TitleLen, + sizeof(*CheckVdmRequest->Title)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CheckVdmRequest->Reserved, + CheckVdmRequest->ReservedLen, + sizeof(*CheckVdmRequest->Reserved))) + { + return STATUS_INVALID_PARAMETER; + } + + CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW) + ? &DosCriticalSection + : &WowCriticalSection; + + /* Enter the critical section */ + RtlEnterCriticalSection(CriticalSection); + + /* Check if this is a DOS or WOW VDM */ + if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW) + { + /* Get the console record */ + Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle, + &ConsoleRecord); + + if (!NT_SUCCESS(Status)) + { + /* Allocate a new console record */ + ConsoleRecord = (PVDM_CONSOLE_RECORD)RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + sizeof(VDM_CONSOLE_RECORD)); + if (ConsoleRecord == NULL) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Remember that the console record was allocated here */ + NewConsoleRecord = TRUE; + + /* Initialize the console record */ + ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle; + ConsoleRecord->ProcessHandle = CsrGetClientThread()->Process->ProcessHandle; + ConsoleRecord->ServerEvent = ConsoleRecord->ClientEvent = NULL; + ConsoleRecord->ReenterCount = 0; + ConsoleRecord->CurrentDirs = NULL; + ConsoleRecord->CurDirsLength = 0; + ConsoleRecord->SessionId = GetNextDosSesId(); + InitializeListHead(&ConsoleRecord->DosListHead); + } + + /* Allocate a new DOS record */ + DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + sizeof(VDM_DOS_RECORD)); + if (DosRecord == NULL) + { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Initialize the DOS record */ + DosRecord->State = VDM_NOT_LOADED; + DosRecord->ExitCode = 0; + + Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent); + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Return the client event handle */ + CheckVdmRequest->WaitObjectForParent = DosRecord->ClientEvent; + + /* Translate the input structure into a VDM command structure and set it in the DOS record */ + if (!BaseSrvCopyCommand(CheckVdmRequest, DosRecord)) + { + /* The only possibility is that an allocation failure occurred */ + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Add the DOS record */ + InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry); + + if (ConsoleRecord->ServerEvent) + { + /* Signal the session event */ + NtSetEvent(ConsoleRecord->ServerEvent, NULL); + } + + if (NewConsoleRecord) + { + /* Add the console record */ + InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry); + } + + if (ConsoleRecord->ConsoleHandle == NULL) + { + /* The parent doesn't have a console, so return the session ID */ + CheckVdmRequest->iTask = ConsoleRecord->SessionId; + } + else CheckVdmRequest->iTask = 0; + + CheckVdmRequest->VDMState = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY; + Status = STATUS_SUCCESS; + } + else + { + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; + } + +Cleanup: + /* Check if it failed */ + if (!NT_SUCCESS(Status)) + { + /* Free the DOS record */ + if (DosRecord != NULL) + { + if (DosRecord->ServerEvent) NtClose(DosRecord->ServerEvent); + if (DosRecord->ClientEvent) + { + /* Close the remote handle */ + NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle, + DosRecord->ClientEvent, + NULL, + NULL, + 0, + 0, + DUPLICATE_CLOSE_SOURCE); + } + + RtlFreeHeap(BaseSrvHeap, 0, DosRecord); + DosRecord = NULL; + } + + /* Free the console record if it was allocated here */ + if (NewConsoleRecord) + { + RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord); + ConsoleRecord = NULL; + } + } + + /* Leave the critical section */ + RtlLeaveCriticalSection(CriticalSection); + + return Status; } CSR_API(BaseSrvUpdateVDMEntry) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.UpdateVDMEntryRequest; + PRTL_CRITICAL_SECTION CriticalSection = NULL; + PVDM_CONSOLE_RECORD ConsoleRecord = NULL; + PVDM_DOS_RECORD DosRecord = NULL; + + CriticalSection = (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW) + ? &DosCriticalSection + : &WowCriticalSection; + + /* Enter the critical section */ + RtlEnterCriticalSection(CriticalSection); + + /* Check if this is a DOS or WOW VDM */ + if (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW) + { + if (UpdateVdmEntryRequest->iTask != 0) + { + /* Get the console record using the task ID */ + Status = GetConsoleRecordBySessionId(UpdateVdmEntryRequest->iTask, + &ConsoleRecord); + } + else + { + /* Get the console record using the console handle */ + Status = BaseSrvGetConsoleRecord(UpdateVdmEntryRequest->ConsoleHandle, + &ConsoleRecord); + } + + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Get the primary DOS record */ + DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, + VDM_DOS_RECORD, + Entry); + + switch (UpdateVdmEntryRequest->EntryIndex) + { + case VdmEntryUndo: + { + /* Close the server event handle, the client will close the client handle */ + NtClose(DosRecord->ServerEvent); + DosRecord->ServerEvent = DosRecord->ClientEvent = NULL; + + if (UpdateVdmEntryRequest->VDMCreationState & (VDM_UNDO_PARTIAL | VDM_UNDO_FULL)) + { + /* Remove the DOS record */ + if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo); + RemoveEntryList(&DosRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, DosRecord); + + /* + * Since this is an undo, if that was the only DOS record the VDM + * won't even start, so the console record should be removed too. + */ + if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead) + { + if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent); + RemoveEntryList(&ConsoleRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord); + } + } + + /* It was successful */ + Status = STATUS_SUCCESS; + + break; + } + + case VdmEntryUpdateProcess: + { + /* Duplicate the VDM process handle */ + Status = NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle, + UpdateVdmEntryRequest->VDMProcessHandle, + NtCurrentProcess(), + &ConsoleRecord->ProcessHandle, + 0, + 0, + DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS); + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Create a pair of handles to one event object */ + Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, + &DosRecord->ClientEvent); + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Return the client event handle */ + UpdateVdmEntryRequest->WaitObjectForParent = DosRecord->ClientEvent; + + break; + } + + case VdmEntryUpdateControlCHandler: + { + // TODO: NOT IMPLEMENTED + DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented!"); + Status = STATUS_NOT_IMPLEMENTED; + + break; + } + + default: + { + /* Invalid */ + Status = STATUS_INVALID_PARAMETER; + } + } + } + else + { + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; + } + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(CriticalSection); + + return Status; } CSR_API(BaseSrvGetNextVDMCommand) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommandRequest = + &((PBASE_API_MESSAGE)ApiMessage)->Data.GetNextVDMCommandRequest; + PRTL_CRITICAL_SECTION CriticalSection; + PLIST_ENTRY i = NULL; + PVDM_CONSOLE_RECORD ConsoleRecord = NULL; + PVDM_DOS_RECORD DosRecord = NULL; + + /* Validate the message buffers */ + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->CmdLine, + GetNextVdmCommandRequest->CmdLen, + sizeof(*GetNextVdmCommandRequest->CmdLine)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->AppName, + GetNextVdmCommandRequest->AppLen, + sizeof(*GetNextVdmCommandRequest->AppName)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->PifFile, + GetNextVdmCommandRequest->PifLen, + sizeof(*GetNextVdmCommandRequest->PifFile)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->CurDirectory, + GetNextVdmCommandRequest->CurDirectoryLen, + sizeof(*GetNextVdmCommandRequest->CurDirectory)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->Env, + GetNextVdmCommandRequest->EnvLen, + sizeof(*GetNextVdmCommandRequest->Env)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->Desktop, + GetNextVdmCommandRequest->DesktopLen, + sizeof(*GetNextVdmCommandRequest->Desktop)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->Title, + GetNextVdmCommandRequest->TitleLen, + sizeof(*GetNextVdmCommandRequest->Title)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->Reserved, + GetNextVdmCommandRequest->ReservedLen, + sizeof(*GetNextVdmCommandRequest->Reserved)) + || !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetNextVdmCommandRequest->StartupInfo, + 1, + sizeof(STARTUPINFOA))) + { + return STATUS_INVALID_PARAMETER; + } + + CriticalSection = (GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW) + ? &WowCriticalSection + : &DosCriticalSection; + + /* Enter the critical section */ + RtlEnterCriticalSection(CriticalSection); + + if (!(GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW)) + { + if (GetNextVdmCommandRequest->iTask != 0) + { + /* Get the console record using the task ID */ + Status = GetConsoleRecordBySessionId(GetNextVdmCommandRequest->iTask, + &ConsoleRecord); + } + else + { + /* Get the console record using the console handle */ + Status = BaseSrvGetConsoleRecord(GetNextVdmCommandRequest->ConsoleHandle, + &ConsoleRecord); + } + + /* Make sure we found the console record */ + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Return the session ID */ + GetNextVdmCommandRequest->iTask = ConsoleRecord->SessionId; + GetNextVdmCommandRequest->WaitObjectForVDM = NULL; + + if (GetNextVdmCommandRequest->VDMState & VDM_GET_FIRST_COMMAND) + { + /* Check if the DOS record list is empty */ + if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead) + { + Status = STATUS_INVALID_PARAMETER; + goto Cleanup; + } + + /* Get the first DOS record */ + DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, VDM_DOS_RECORD, Entry); + + /* Make sure its command information is still there */ + if (DosRecord->CommandInfo == NULL) + { + Status = STATUS_INVALID_PARAMETER; + goto Cleanup; + } + + /* Fill the command information */ + Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest); + goto Cleanup; + } + + /* Check if we should set the state of a running DOS record to ready */ + if (!(GetNextVdmCommandRequest->VDMState + & (VDM_FLAG_FIRST_TASK | VDM_FLAG_RETRY | VDM_FLAG_NESTED_TASK))) + { + /* Search for a DOS record that is currently running */ + for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink) + { + DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry); + if (DosRecord->State == VDM_NOT_READY) break; + } + + /* Check if we found any */ + if (i == &ConsoleRecord->DosListHead) + { + Status = STATUS_INVALID_PARAMETER; + goto Cleanup; + } + + /* Set the exit code */ + DosRecord->ExitCode = GetNextVdmCommandRequest->ExitCode; + + /* Update the VDM state */ + DosRecord->State = VDM_READY; + + /* Notify all waiting threads that the task is finished */ + NtSetEvent(DosRecord->ServerEvent, NULL); + NtClose(DosRecord->ServerEvent); + DosRecord->ServerEvent = NULL; + } + + /* Search for a DOS record that isn't loaded yet */ + for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink) + { + DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry); + if (DosRecord->State == VDM_NOT_LOADED) break; + } + + if (i != &ConsoleRecord->DosListHead) + { + /* DOS tasks which haven't been loaded yet should have a command info structure */ + ASSERT(DosRecord->CommandInfo != NULL); + + /* Check if the caller only wants environment data */ + if (GetNextVdmCommandRequest->VDMState & VDM_GET_ENVIRONMENT) + { + if (GetNextVdmCommandRequest->EnvLen < DosRecord->CommandInfo->EnvLen) + { + /* Not enough space was reserved */ + GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen; + Status = STATUS_BUFFER_OVERFLOW; + goto Cleanup; + } + + /* Copy the environment data */ + RtlMoveMemory(GetNextVdmCommandRequest->Env, + DosRecord->CommandInfo->Env, + DosRecord->CommandInfo->EnvLen); + + /* Return the actual size to the caller */ + GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen; + } + else + { + /* Fill the command information */ + Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest); + + /* Free the command information, it's no longer needed */ + BaseSrvFreeVDMInfo(DosRecord->CommandInfo); + DosRecord->CommandInfo = NULL; + + /* Update the VDM state */ + GetNextVdmCommandRequest->VDMState = DosRecord->State = VDM_NOT_READY; + } + + Status = STATUS_SUCCESS; + goto Cleanup; + } + } + else + { + // TODO: WOW SUPPORT NOT IMPLEMENTED + Status = STATUS_NOT_IMPLEMENTED; + goto Cleanup; + } + + /* There is no command yet */ + if ((GetNextVdmCommandRequest->VDMState & (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY)) + != (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY)) + { + if (ConsoleRecord->ServerEvent) + { + /* Reset the event */ + NtResetEvent(ConsoleRecord->ServerEvent, NULL); + } + else + { + /* Create a pair of wait handles */ + Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent, + &ConsoleRecord->ClientEvent); + if (!NT_SUCCESS(Status)) goto Cleanup; + } + + /* Return the client event handle */ + GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent; + } + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(CriticalSection); + + return Status; } CSR_API(BaseSrvExitVDM) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest; + PRTL_CRITICAL_SECTION CriticalSection = NULL; + PVDM_CONSOLE_RECORD ConsoleRecord = NULL; + PVDM_DOS_RECORD DosRecord; + + CriticalSection = (ExitVdmRequest->iWowTask == 0) + ? &DosCriticalSection + : &WowCriticalSection; + + /* Enter the critical section */ + RtlEnterCriticalSection(CriticalSection); + + if (ExitVdmRequest->iWowTask == 0) + { + /* Get the console record */ + Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord); + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Cleanup the DOS records */ + while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead) + { + DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, + VDM_DOS_RECORD, + Entry); + + /* Set the event and close it */ + NtSetEvent(DosRecord->ServerEvent, NULL); + NtClose(DosRecord->ServerEvent); + + /* Remove the DOS entry */ + if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo); + RemoveEntryList(&DosRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, DosRecord); + } + + if (ConsoleRecord->CurrentDirs != NULL) + { + /* Free the current directories */ + RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs); + ConsoleRecord->CurrentDirs = NULL; + ConsoleRecord->CurDirsLength = 0; + } + + /* Close the event handle */ + if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent); + + /* Remove the console record */ + RemoveEntryList(&ConsoleRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord); + } + else + { + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; + } + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(CriticalSection); + + return Status; } CSR_API(BaseSrvIsFirstVDM) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest; + + /* Return the result */ + IsFirstVDMRequest->FirstVDM = FirstVDM; + + /* Clear the first VDM flag */ + FirstVDM = FALSE; + + return STATUS_SUCCESS; } CSR_API(BaseSrvGetVDMExitCode) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest; + PLIST_ENTRY i = NULL; + PVDM_CONSOLE_RECORD ConsoleRecord = NULL; + PVDM_DOS_RECORD DosRecord = NULL; + + /* Enter the critical section */ + RtlEnterCriticalSection(&DosCriticalSection); + + /* Get the console record */ + Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord); + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Search for a DOS record that has the same parent process handle */ + for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink) + { + DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry); + if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break; + } + + /* Check if no DOS record was found */ + if (i == &ConsoleRecord->DosListHead) + { + Status = STATUS_NOT_FOUND; + goto Cleanup; + } + + /* Check if this task is still running */ + if (DosRecord->State == VDM_READY) + { + GetVDMExitCodeRequest->ExitCode = STATUS_PENDING; + goto Cleanup; + } + + /* Return the exit code */ + GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode; + + /* Since this is a zombie task record, remove it */ + if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo); + RemoveEntryList(&DosRecord->Entry); + RtlFreeHeap(BaseSrvHeap, 0, DosRecord); + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(&DosCriticalSection); + + return Status; } CSR_API(BaseSrvSetReenterCount) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status = STATUS_SUCCESS; + PBASE_SET_REENTER_COUNT SetReenterCountRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.SetReenterCountRequest; + PVDM_CONSOLE_RECORD ConsoleRecord; + + /* Enter the critical section */ + RtlEnterCriticalSection(&DosCriticalSection); + + /* Get the console record */ + Status = BaseSrvGetConsoleRecord(SetReenterCountRequest->ConsoleHandle, &ConsoleRecord); + if (!NT_SUCCESS(Status)) goto Cleanup; + + if (SetReenterCountRequest->fIncDec == VDM_INC_REENTER_COUNT) ConsoleRecord->ReenterCount++; + else if (SetReenterCountRequest->fIncDec == VDM_DEC_REENTER_COUNT) + { + ConsoleRecord->ReenterCount--; + if (ConsoleRecord->ServerEvent != NULL) NtSetEvent(ConsoleRecord->ServerEvent, NULL); + } + else Status = STATUS_INVALID_PARAMETER; + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(&DosCriticalSection); + + return Status; } CSR_API(BaseSrvSetVDMCurDirs) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest; + PVDM_CONSOLE_RECORD ConsoleRecord; + PCHAR Buffer = NULL; + + /* Validate the input buffer */ + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs, + VDMCurrentDirsRequest->cchCurDirs, + sizeof(*VDMCurrentDirsRequest->lpszzCurDirs))) + { + return STATUS_INVALID_PARAMETER; + } + + /* Enter the critical section */ + RtlEnterCriticalSection(&DosCriticalSection); + + /* Find the console record */ + Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord); + if (!NT_SUCCESS(Status)) goto Cleanup; + + if (ConsoleRecord->CurrentDirs == NULL) + { + /* Allocate memory for the current directory information */ + Buffer = RtlAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + VDMCurrentDirsRequest->cchCurDirs); + } + else + { + /* Resize the amount of allocated memory */ + Buffer = RtlReAllocateHeap(BaseSrvHeap, + HEAP_ZERO_MEMORY, + ConsoleRecord->CurrentDirs, + VDMCurrentDirsRequest->cchCurDirs); + } + + if (Buffer == NULL) + { + /* Allocation failed */ + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + /* Update the console record */ + ConsoleRecord->CurrentDirs = Buffer; + ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs; + + /* Copy the data */ + RtlMoveMemory(ConsoleRecord->CurrentDirs, + VDMCurrentDirsRequest->lpszzCurDirs, + VDMCurrentDirsRequest->cchCurDirs); + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(&DosCriticalSection); + + return Status; } CSR_API(BaseSrvGetVDMCurDirs) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest; + PVDM_CONSOLE_RECORD ConsoleRecord; + + /* Validate the output buffer */ + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs, + VDMCurrentDirsRequest->cchCurDirs, + sizeof(*VDMCurrentDirsRequest->lpszzCurDirs))) + { + return STATUS_INVALID_PARAMETER; + } + + /* Enter the critical section */ + RtlEnterCriticalSection(&DosCriticalSection); + + /* Find the console record */ + Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord); + if (!NT_SUCCESS(Status)) goto Cleanup; + + /* Return the actual size of the current directory information */ + VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength; + + /* Check if the buffer is large enough */ + if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + goto Cleanup; + } + + /* Copy the data */ + RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs, + ConsoleRecord->CurrentDirs, + ConsoleRecord->CurDirsLength); + +Cleanup: + /* Leave the critical section */ + RtlLeaveCriticalSection(&DosCriticalSection); + + return Status; } CSR_API(BaseSrvBatNotification) diff --git a/reactos/subsystems/win/basesrv/vdm.h b/reactos/subsystems/win/basesrv/vdm.h new file mode 100644 index 0000000000000..3da59d9044291 --- /dev/null +++ b/reactos/subsystems/win/basesrv/vdm.h @@ -0,0 +1,60 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Base API Server DLL + * FILE: subsystems/win/basesrv/vdm.h + * PURPOSE: VDM Definitions + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef __VDM_H__ +#define __VDM_H__ + +#include + +/* DEFINITIONS ****************************************************************/ + +#define VDM_POLICY_KEY_NAME L"Software\\Policies\\Microsoft\\Windows\\AppCompat" +#define VDM_DISALLOWED_VALUE_NAME L"VDMDisallowed" + +typedef struct _VDM_CONSOLE_RECORD +{ + LIST_ENTRY Entry; + HANDLE ConsoleHandle; + HANDLE ProcessHandle; + HANDLE ServerEvent; + HANDLE ClientEvent; + ULONG ProcessId; + ULONG ReenterCount; + PCHAR CurrentDirs; + ULONG CurDirsLength; + ULONG SessionId; + LIST_ENTRY DosListHead; +} VDM_CONSOLE_RECORD, *PVDM_CONSOLE_RECORD; + +typedef struct _VDM_DOS_RECORD +{ + LIST_ENTRY Entry; + USHORT State; + ULONG ExitCode; + HANDLE ServerEvent; + HANDLE ClientEvent; + PVDM_COMMAND_INFO CommandInfo; +} VDM_DOS_RECORD, *PVDM_DOS_RECORD; + +/* FUNCTIONS ******************************************************************/ + +NTSTATUS NTAPI BaseSrvGetConsoleRecord(HANDLE ConsoleHandle, PVDM_CONSOLE_RECORD *Record); +NTSTATUS NTAPI GetConsoleRecordBySessionId(ULONG TaskId, PVDM_CONSOLE_RECORD *Record); +ULONG NTAPI GetNextDosSesId(VOID); +BOOLEAN NTAPI BaseSrvIsVdmAllowed(VOID); +NTSTATUS NTAPI BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent); +VOID NTAPI BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo); +VOID NTAPI BaseSrvCleanupVdmRecords(ULONG ProcessId); +BOOLEAN NTAPI BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord); +NTSTATUS NTAPI BaseSrvFillCommandInfo( + PVDM_COMMAND_INFO CommandInfo, + PBASE_GET_NEXT_VDM_COMMAND Message +); +VOID NTAPI BaseInitializeVDM(VOID); + +#endif // __VDM_H__ diff --git a/reactos/subsystems/win32/csrsrv/procsup.c b/reactos/subsystems/win32/csrsrv/procsup.c index d682c379388f1..451f2697d45ab 100644 --- a/reactos/subsystems/win32/csrsrv/procsup.c +++ b/reactos/subsystems/win32/csrsrv/procsup.c @@ -605,15 +605,18 @@ CsrCreateProcess(IN HANDLE hProcess, } /* Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */ - if ((Flags & CsrProcessCreateNewGroup) == 0) + if (Flags & CsrProcessCreateNewGroup) { - /* Create new data */ + /* + * We create the process group leader of a new process group, therefore + * its process group ID and sequence number are its own ones. + */ CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess); CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber; } else { - /* Copy it from the current process */ + /* Inherit the process group ID and sequence number from the current process */ CsrProcess->ProcessGroupId = CurrentProcess->ProcessGroupId; CsrProcess->ProcessGroupSequence = CurrentProcess->ProcessGroupSequence; }