static VOID CmdStartExternalCommand(VOID) { DWORD Result; // TODO: improve: this code has strong similarities // with the 'default' case of DosCreateProcess. LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI()); CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = ""; LPSTR CmdLinePtr; ULONG CmdLineLen; /* Spawn a user-defined 32-bit command preprocessor */ // FIXME: Use COMSPEC env var!! CmdLinePtr = CmdLine; strcpy(CmdLinePtr, "cmd.exe /c "); CmdLinePtr += strlen(CmdLinePtr); /* Build a Win32-compatible command-line */ CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1); RtlCopyMemory(CmdLinePtr, Command, CmdLineLen); CmdLinePtr[CmdLineLen] = '\0'; /* Remove any trailing return carriage character and NULL-terminate the command line */ while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++; *CmdLinePtr = '\0'; DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine); /* * No need to prepare the stack for DosStartComSpec since we won't start it. */ Result = DosStartProcess32(Command, CmdLine, SEG_OFF_TO_PTR(getES(), 0) /*Environment*/, MAKELONG(getIP(), getCS()) /*ReturnAddress*/, FALSE); if (Result != ERROR_SUCCESS) { DosDisplayMessage("Failed to start command '%s' ('%s'). Error: %u\n", Command, CmdLine, Result); setCF(0); setAL((UCHAR)Result); } else { DosDisplayMessage("Command '%s' ('%s') started successfully.\n", Command, CmdLine); #ifndef STANDALONE setCF(Repeat); // Set CF if we need to start a 16-bit process #else setCF(0); #endif } }
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; }
BOOLEAN DiskBios32Initialize(VOID) { /* * Initialize BIOS Disk ROM static data */ /* Floppy Parameter Table */ RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xEFC7), &FloppyParamTable.FloppyParamTable, sizeof(FloppyParamTable.FloppyParamTable)); // // FIXME: Must be done by HW floppy controller! // /* Detect and initialize the supported disks */ // TODO: the "Detect" part is missing. FloppyDrive[0] = RetrieveDisk(FLOPPY_DISK, 0); FloppyDrive[1] = RetrieveDisk(FLOPPY_DISK, 1); HardDrive[0] = RetrieveDisk(HARD_DISK, 0); HardDrive[1] = RetrieveDisk(HARD_DISK, 1); HardDrive[2] = RetrieveDisk(HARD_DISK, 2); HardDrive[3] = RetrieveDisk(HARD_DISK, 3); return TRUE; }
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 = FALSE; 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; } } }
BOOLEAN DosCountryInitialize(VOID) { UINT i; /* Initialize some memory to store country information */ // FIXME: Can we use instead some static area from the DOS data structure?? CountryDataSegment = DosAllocateMemory(sizeof(COUNTRY_DATA), NULL); if (CountryDataSegment == 0) return FALSE; CountryData = (PCOUNTRY_DATA)SEG_OFF_TO_PTR(CountryDataSegment, 0x0000); RtlMoveMemory(CountryData->CaseMapRoutine, CaseMapRoutine, sizeof(CaseMapRoutine)); CountryData->UpCaseTbl.Size = ARRAYSIZE(CountryData->UpCaseTbl.Data); for (i = 0; i < CountryData->UpCaseTbl.Size; ++i) CountryData->UpCaseTbl.Data[i] = 0x80 + i; CountryData->LoCaseTbl.Size = ARRAYSIZE(CountryData->LoCaseTbl.Data); for (i = 0; i < CountryData->LoCaseTbl.Size; ++i) CountryData->LoCaseTbl.Data[i] = i; CountryData->FNameTermTbl.Size = ARRAYSIZE(CountryData->FNameTermTbl.Data); CountryData->FNameTermTbl.Data[ 0] = 0x01; // Dummy Byte CountryData->FNameTermTbl.Data[ 1] = 0x00; // Lowest permissible Character Value for Filename CountryData->FNameTermTbl.Data[ 2] = 0xFF; // Highest permissible Character Value for Filename CountryData->FNameTermTbl.Data[ 3] = 0x00; // Dummy Byte CountryData->FNameTermTbl.Data[ 4] = 0x00; // First excluded Character in Range \ all characters in this CountryData->FNameTermTbl.Data[ 5] = 0x20; // Last excluded Character in Range / range are illegal CountryData->FNameTermTbl.Data[ 6] = 0x02; // Dummy Byte CountryData->FNameTermTbl.Data[ 7] = 14; // Number of illegal (terminator) Characters // CountryData->FNameTermTbl.Data[ 8] = ".\"/\\[]:|<>+=;,"; // Characters which terminate a Filename CountryData->FNameTermTbl.Data[ 8] = '.'; CountryData->FNameTermTbl.Data[ 9] = '\"'; CountryData->FNameTermTbl.Data[10] = '/'; CountryData->FNameTermTbl.Data[11] = '\\'; CountryData->FNameTermTbl.Data[12] = '['; CountryData->FNameTermTbl.Data[13] = ']'; CountryData->FNameTermTbl.Data[14] = ':'; CountryData->FNameTermTbl.Data[15] = '|'; CountryData->FNameTermTbl.Data[16] = '<'; CountryData->FNameTermTbl.Data[17] = '>'; CountryData->FNameTermTbl.Data[18] = '+'; CountryData->FNameTermTbl.Data[19] = '='; CountryData->FNameTermTbl.Data[20] = ';'; CountryData->FNameTermTbl.Data[21] = ','; CountryData->CollateTbl.Size = ARRAYSIZE(CountryData->CollateTbl.Data); for (i = 0; i < CountryData->CollateTbl.Size; ++i) CountryData->CollateTbl.Data[i] = i; CountryData->DBCSLeadTbl.Size = 0; // Empty DBCS table CountryData->DBCSLeadTbl.Data[0] = 0x0000; return TRUE; }
PVOID WINAPI VdmMapFlat(IN USHORT Segment, IN ULONG Offset, IN VDM_MODE Mode) { // FIXME UNREFERENCED_PARAMETER(Mode); return SEG_OFF_TO_PTR(Segment, Offset); }
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); }
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); switch (FuncNum) { case BOP_CONTROL_INT32: Int32Dispatch(Stack); break; default: // DPRINT1("Unassigned Control BOP Function: 0x%02X\n", FuncNum); DisplayMessage(L"Unassigned Control BOP Function: 0x%02X", FuncNum); break; } }
VOID DosBootsectorInitialize(VOID) { /* We write the bootsector at 0000:7C00 */ ULONG_PTR Address = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00); CHAR DosKernelFileName[] = ""; // No DOS file name, therefore we'll load DOS32 DPRINT("DosBootsectorInitialize\n"); /* Write the "bootsector" */ RtlCopyMemory((PVOID)Address, Bootsector1, sizeof(Bootsector1)); Address += sizeof(Bootsector1); RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName)); Address += sizeof(DosKernelFileName); RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2)); /* Register the DOS Loading BOP */ RegisterBop(BOP_LOAD_DOS, DosInitialize); }
static VOID CmdStartComSpec32(VOID) { DWORD Result; // TODO: improve: this code has strong similarities with the // 'default' case of DosCreateProcess and with the 'case 0x08'. CHAR CmdLine[sizeof("cmd.exe") + 1] = ""; /* Spawn a user-defined 32-bit command preprocessor */ // FIXME: Use COMSPEC env var!! strcpy(CmdLine, "cmd.exe"); DPRINT1("CMD Run 32-bit Command Interpreter '%s'\n", CmdLine); /* * No need to prepare the stack for DosStartComSpec since we won't start it. */ Result = DosStartProcess32(CmdLine, CmdLine, SEG_OFF_TO_PTR(getES(), 0) /*Environment*/, MAKELONG(getIP(), getCS()) /*ReturnAddress*/, FALSE); if (Result != ERROR_SUCCESS) { DosDisplayMessage("Failed to start 32-bit Command Interpreter '%s'. Error: %u\n", CmdLine, Result); setCF(0); setAL((UCHAR)Result); } else { DosDisplayMessage("32-bit Command Interpreter '%s' started successfully.\n", CmdLine); #ifndef STANDALONE setCF(Repeat); // Set CF if we need to start a 16-bit process #else setCF(0); #endif } }
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) { /* Load the DOS kernel */ case 0x11: { DemLoadNTDOSKernel(); break; } /* Call 32-bit Driver Strategy Routine */ case BOP_DRV_STRATEGY: { DeviceStrategyBop(); break; } /* Call 32-bit Driver Interrupt Routine */ case BOP_DRV_INTERRUPT: { DeviceInterruptBop(); break; } default: { DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum); // setCF(1); // Disable, otherwise we enter an infinite loop break; } } }
VOID DosBootsectorInitialize(VOID) { /* We write the bootsector at 0000:7C00 */ ULONG_PTR StartAddress = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00); ULONG_PTR Address = StartAddress; CHAR DosKernelFileName[] = ""; // No DOS BIOS file name, therefore we will load DOS32 DPRINT("DosBootsectorInitialize\n"); /* Write the "bootsector" */ RtlCopyMemory((PVOID)Address, Bootsector1, sizeof(Bootsector1)); Address += sizeof(Bootsector1); RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName)); Address += sizeof(DosKernelFileName); RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2)); Address += sizeof(Bootsector2); /* Initialize the callback context */ InitializeContext(&DosContext, 0x0000, (ULONG_PTR)MEM_ALIGN_UP(0x7C00 + Address - StartAddress, sizeof(WORD))); /* Register the DOS Loading BOP */ RegisterBop(BOP_LOAD_DOS, DosInitialize); }
static VOID WINAPI DosInitialize(LPWORD Stack) { /* Get the DOS BIOS file name (NULL-terminated) */ // FIXME: Isn't it possible to use some DS:SI instead?? LPCSTR DosBiosFileName = (LPCSTR)SEG_OFF_TO_PTR(getCS(), getIP()); setIP(getIP() + strlen(DosBiosFileName) + 1); // Skip it DPRINT("DosInitialize('%s')\n", DosBiosFileName); /* * We succeeded, deregister the DOS Loading BOP * so that no app will be able to call us back. */ RegisterBop(BOP_LOAD_DOS, NULL); /* Register the DOS BOPs */ RegisterBop(BOP_DOS, DosSystemBop ); RegisterBop(BOP_CMD, DosCmdInterpreterBop); if (DosBiosFileName[0] != '\0') { BOOLEAN Success = FALSE; HANDLE hDosBios; ULONG ulDosBiosSize = 0; /* Open the DOS BIOS file */ hDosBios = FileOpen(DosBiosFileName, &ulDosBiosSize); if (hDosBios == NULL) goto Quit; /* Attempt to load the DOS BIOS into memory */ Success = FileLoadByHandle(hDosBios, REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)), ulDosBiosSize, &ulDosBiosSize); DPRINT1("DOS BIOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n", DosBiosFileName, (Success ? "succeeded" : "failed"), 0x0070, 0x0000, ulDosBiosSize, GetLastError()); /* Close the DOS BIOS file */ FileClose(hDosBios); Quit: if (!Success) { BiosDisplayMessage("DOS BIOS file '%s' loading failed (Error: %u). The VDM will shut down.\n", DosBiosFileName, GetLastError()); return; } } else { /* Load the 16-bit startup code for DOS32 and register its Starting BOP */ RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup, sizeof(Startup)); // This is the equivalent of BOP_LOAD_DOS, function 0x11 "Load the DOS kernel" // for the Windows NT DOS. RegisterBop(BOP_START_DOS, DosStart); } /* Position execution pointers for DOS startup and return */ setCS(0x0070); setIP(0x0000); }
static VOID WINAPI DosStart(LPWORD Stack) { #ifdef STANDALONE DWORD Result; CHAR ApplicationName[MAX_PATH]; CHAR CommandLine[DOS_CMDLINE_LENGTH]; #endif DPRINT("DosStart\n"); /* * We succeeded, deregister the DOS Starting BOP * so that no app will be able to call us back. */ RegisterBop(BOP_START_DOS, NULL); /* Load the mouse driver */ DosMouseInitialize(); #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 Quit; } /* Wait for the command thread to exit */ WaitForSingleObject(CommandThread, INFINITE); /* Close the thread handle */ CloseHandle(CommandThread); #else if (NtVdmArgc >= 2) { WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL); if (NtVdmArgc >= 3) WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL); else strcpy(CommandLine, ""); } else { DisplayMessage(L"Invalid DOS command line\n"); goto Quit; } /* Start the process from the command line */ DPRINT1("Starting '%s' ('%s')...\n", ApplicationName, CommandLine); Result = DosStartProcess(ApplicationName, CommandLine, SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0)); if (Result != ERROR_SUCCESS) { DisplayMessage(L"Could not start '%S'. Error: %u", ApplicationName, Result); goto Quit; } #endif Quit: /* Stop the VDM */ EmulatorTerminate(); }
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 line by removing the return carriage character */ while (*CmdPtr && *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); VidBiosDetachFromConsole(); 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(); } VidBiosAttachToConsole(); 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; } } }
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) { /* Kill the VDM */ case 0x00: { /* Stop the VDM */ EmulatorTerminate(); return; } /* * Get a new app to start * * Input * DS:DX : Data block. * * Output * CF : 0: Success; 1: Failure. */ case 0x01: { CmdStartProcess(); break; } /* * Check binary format * * Input * DS:DX : Program to check. * * Output * CF : 0: Success; 1: Failure. * AX : Error code. */ case 0x07: { DWORD BinaryType; LPSTR ProgramName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); if (!GetBinaryTypeA(ProgramName, &BinaryType)) { /* An error happened, bail out */ setCF(1); setAX(LOWORD(GetLastError())); break; } // FIXME: We only support DOS binaries for now... ASSERT(BinaryType == SCS_DOS_BINARY); if (BinaryType != SCS_DOS_BINARY) { /* An error happened, bail out */ setCF(1); setAX(LOWORD(ERROR_BAD_EXE_FORMAT)); break; } /* Return success: DOS application */ setCF(0); break; } /* * Start an external command * * Input * DS:SI : Command to start. * ES : Environment block segment. * AL : Current drive number. * AH : 0: Directly start the command; * 1: Use "cmd.exe /c" to start the command. * * Output * CF : 0: Shell-out; 1: Continue. * AL : Error/Exit code. */ case 0x08: { CmdStartExternalCommand(); break; } /* * Start the default 32-bit command interpreter (COMSPEC) * * Input * ES : Environment block segment. * AL : Current drive number. * * Output * CF : 0: Shell-out; 1: Continue. * AL : Error/Exit code. */ case 0x0A: { CmdStartComSpec32(); break; } /* * Set exit code * * Input * DX : Exit code * * Output * CF : 0: Shell-out; 1: Continue. */ case 0x0B: { CmdSetExitCode(); break; } /* * Get start information * * Output * AL : 0 (resp. 1): Started from (resp. without) an existing console. */ case 0x10: { #ifndef STANDALONE /* * When a new instance of our (internal) COMMAND.COM is started, * we check whether we need to run a 32-bit COMSPEC. This goes by * checking whether we were started in a new console (no parent * console process) or from an existing one. * * However COMMAND.COM can also be started in the case where a * 32-bit process (started by a 16-bit parent) wants to start a new * 16-bit process: to ensure DOS reentry we need to start a new * instance of COMMAND.COM. On Windows the COMMAND.COM is started * just before the 32-bit process (in fact, it is this COMMAND.COM * which starts the 32-bit process via an undocumented command-line * switch '/z', which syntax is: * COMMAND.COM /z\bAPPNAME.EXE * notice the '\b' character inserted in-between. Then COMMAND.COM * issues a BOP_CMD 08h with AH=00h to start the process). * * Instead, we do the reverse, i.e. we start the 32-bit process, * and *only* if needed, i.e. if this process wants to start a * new 16-bit process, we start our COMMAND.COM. * * The problem we then face is that our COMMAND.COM will possibly * want to start a new COMSPEC, however we do not want this. * The chosen solution is to flag this case -- done with the 'Reentry' * boolean -- so that COMMAND.COM will not attempt to start COMSPEC * but instead will directly try to start the 16-bit process. */ // setAL(SessionId != 0); setAL((SessionId != 0) && !Reentry); /* Reset 'Reentry' */ Reentry = FALSE; #else setAL(0); #endif break; } default: { DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum); // setCF(1); // Disable, otherwise we enter an infinite loop break; } } }
static VOID WINAPI EmsIntHandler(LPWORD Stack) { switch (getAH()) { /* Get Manager Status */ case 0x40: { setAH(EMS_STATUS_SUCCESS); break; } /* Get Page Frame Segment */ case 0x41: { setAH(EMS_STATUS_SUCCESS); setBX(EmsSegment); break; } /* Get Number of Unallocated Pages */ case 0x42: { setAH(EMS_STATUS_SUCCESS); setBX(RtlNumberOfClearBits(&AllocBitmap)); setDX(EmsTotalPages); break; } /* Get Handle and Allocate Memory */ case 0x43: { USHORT Handle; UCHAR Status = EmsAlloc(getBX(), &Handle); if (Status == EMS_STATUS_SUCCESS) setDX(Handle); setAH(Status); break; } /* Map Memory */ case 0x44: { setAH(EmsMap(getDX(), getAL(), getBX())); break; } /* Release Handle and Memory */ case 0x45: { setAH(EmsFree(getDX())); break; } /* Get EMM Version */ case 0x46: { setAH(EMS_STATUS_SUCCESS); setAL(EMS_VERSION_NUM); break; } /* Save Page Map */ case 0x47: { // FIXME: This depends on an EMS handle given in DX RtlCopyMemory(MappingBackup, Mapping, sizeof(Mapping)); setAH(EMS_STATUS_SUCCESS); break; } /* Restore Page Map */ case 0x48: { // FIXME: This depends on an EMS handle given in DX RtlCopyMemory(Mapping, MappingBackup, sizeof(Mapping)); setAH(EMS_STATUS_SUCCESS); break; } /* Get Number of Opened Handles */ case 0x4B: { USHORT NumOpenHandles = 0; USHORT i; for (i = 0; i < ARRAYSIZE(HandleTable); i++) { if (HandleTable[i].Allocated) ++NumOpenHandles; } setAH(EMS_STATUS_SUCCESS); setBX(NumOpenHandles); break; } /* Get Handle Number of Pages */ case 0x4C: { PEMS_HANDLE HandleEntry = GetHandleRecord(getDX()); if (!ValidateHandle(HandleEntry)) { setAH(EMS_STATUS_INVALID_HANDLE); break; } setAH(EMS_STATUS_SUCCESS); setBX(HandleEntry->PageCount); break; } /* Get All Handles Number of Pages */ case 0x4D: { PEMS_HANDLE_PAGE_INFO HandlePageInfo = (PEMS_HANDLE_PAGE_INFO)SEG_OFF_TO_PTR(getES(), getDI()); USHORT NumOpenHandles = 0; USHORT i; for (i = 0; i < ARRAYSIZE(HandleTable); i++) { if (HandleTable[i].Allocated) { HandlePageInfo->Handle = i; HandlePageInfo->PageCount = HandleTable[i].PageCount; ++HandlePageInfo; ++NumOpenHandles; } } setAH(EMS_STATUS_SUCCESS); setBX(NumOpenHandles); break; } /* Get or Set Page Map */ case 0x4E: { switch (getAL()) { /* Get Mapping Registers */ // case 0x00: // TODO: NOT IMPLEMENTED /* Set Mapping Registers */ // case 0x01: // TODO: NOT IMPLEMENTED /* Get and Set Mapping Registers At Once */ // case 0x02: // TODO: NOT IMPLEMENTED /* Get Size of Page-Mapping Array */ case 0x03: { setAH(EMS_STATUS_SUCCESS); setAL(sizeof(Mapping)); break; } default: { DPRINT1("EMS function AH = 0x4E, subfunction AL = %02X NOT IMPLEMENTED\n", getAL()); setAH(EMS_STATUS_UNKNOWN_FUNCTION); break; } } break; } /* Get/Set Handle Name */ case 0x53: { PEMS_HANDLE HandleEntry = GetHandleRecord(getDX()); if (!ValidateHandle(HandleEntry)) { setAH(EMS_STATUS_INVALID_HANDLE); break; } if (getAL() == 0x00) { /* Retrieve the name */ RtlCopyMemory(SEG_OFF_TO_PTR(getES(), getDI()), HandleEntry->Name, sizeof(HandleEntry->Name)); setAH(EMS_STATUS_SUCCESS); } else if (getAL() == 0x01) { /* Store the name */ RtlCopyMemory(HandleEntry->Name, SEG_OFF_TO_PTR(getDS(), getSI()), sizeof(HandleEntry->Name)); setAH(EMS_STATUS_SUCCESS); } else { DPRINT1("Invalid subfunction %02X for EMS function AH = 53h\n", getAL()); setAH(EMS_STATUS_INVALID_SUBFUNCTION); } break; } /* Handle Directory functions */ case 0x54: { if (getAL() == 0x00) { /* Get Handle Directory */ PEMS_HANDLE_DIR_ENTRY HandleDir = (PEMS_HANDLE_DIR_ENTRY)SEG_OFF_TO_PTR(getES(), getDI()); USHORT NumOpenHandles = 0; USHORT i; for (i = 0; i < ARRAYSIZE(HandleTable); i++) { if (HandleTable[i].Allocated) { HandleDir->Handle = i; RtlCopyMemory(HandleDir->Name, HandleTable[i].Name, sizeof(HandleDir->Name)); ++HandleDir; ++NumOpenHandles; } } setAH(EMS_STATUS_SUCCESS); setAL((UCHAR)NumOpenHandles); } else if (getAL() == 0x01) { /* Search for Named Handle */ PUCHAR HandleName = (PUCHAR)SEG_OFF_TO_PTR(getDS(), getSI()); PEMS_HANDLE HandleFound = NULL; USHORT i; for (i = 0; i < ARRAYSIZE(HandleTable); i++) { if (HandleTable[i].Allocated && RtlCompareMemory(HandleName, HandleTable[i].Name, sizeof(HandleTable[i].Name)) == sizeof(HandleTable[i].Name)) { HandleFound = &HandleTable[i]; break; } } /* Bail out if no handle was found */ if (i >= ARRAYSIZE(HandleTable)) // HandleFound == NULL { setAH(EMS_STATUS_HANDLE_NOT_FOUND); break; } /* Return the handle number */ setDX(i); /* Sanity check: Check whether the handle was unnamed */ i = 0; while ((i < sizeof(HandleFound->Name)) && (HandleFound->Name[i] == '\0')) ++i; if (i >= sizeof(HandleFound->Name)) { setAH(EMS_STATUS_UNNAMED_HANDLE); } else { setAH(EMS_STATUS_SUCCESS); } } else if (getAL() == 0x02) { /* * Get Total Number of Handles * * This function retrieves the maximum number of handles * (allocated or not) the memory manager supports, which * a program may request. */ setAH(EMS_STATUS_SUCCESS); setBX(ARRAYSIZE(HandleTable)); } else { DPRINT1("Invalid subfunction %02X for EMS function AH = 54h\n", getAL()); setAH(EMS_STATUS_INVALID_SUBFUNCTION); } break; } /* Move/Exchange Memory */ case 0x57: { PUCHAR SourcePtr, DestPtr; PEMS_HANDLE HandleEntry; PEMS_PAGE PageEntry; BOOLEAN Exchange = getAL(); PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI()); if (Data->SourceType) { /* Expanded memory */ HandleEntry = GetHandleRecord(Data->SourceHandle); if (!ValidateHandle(HandleEntry)) { setAH(EMS_STATUS_INVALID_HANDLE); break; } PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment); if (!PageEntry) { setAH(EMS_STATUS_INV_LOGICAL_PAGE); break; } SourcePtr = (PUCHAR)((ULONG_PTR)EmsMemory + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE + Data->SourceOffset); } else { /* Conventional memory */ SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset); } if (Data->DestType) { /* Expanded memory */ HandleEntry = GetHandleRecord(Data->DestHandle); if (!ValidateHandle(HandleEntry)) { setAH(EMS_STATUS_INVALID_HANDLE); break; } PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment); if (!PageEntry) { setAH(EMS_STATUS_INV_LOGICAL_PAGE); break; } DestPtr = (PUCHAR)((ULONG_PTR)EmsMemory + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE + Data->DestOffset); } else { /* Conventional memory */ DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset); } if (Exchange) { ULONG i; /* Exchange */ for (i = 0; i < Data->RegionLength; i++) { UCHAR Temp = DestPtr[i]; DestPtr[i] = SourcePtr[i]; SourcePtr[i] = Temp; } } else { /* Move */ RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength); } setAH(EMS_STATUS_SUCCESS); break; } /* Get Mappable Physical Address Array */ case 0x58: { if (getAL() == 0x00) { PEMS_MAPPABLE_PHYS_PAGE PageArray = (PEMS_MAPPABLE_PHYS_PAGE)SEG_OFF_TO_PTR(getES(), getDI()); ULONG i; for (i = 0; i < EMS_PHYSICAL_PAGES; i++) { PageArray->PageSegment = EMS_SEGMENT + i * (EMS_PAGE_SIZE >> 4); PageArray->PageNumber = i; ++PageArray; } setAH(EMS_STATUS_SUCCESS); setCX(EMS_PHYSICAL_PAGES); } else if (getAL() == 0x01) { setAH(EMS_STATUS_SUCCESS); setCX(EMS_PHYSICAL_PAGES); } else { DPRINT1("Invalid subfunction %02X for EMS function AH = 58h\n", getAL()); setAH(EMS_STATUS_INVALID_SUBFUNCTION); } break; }
static VOID CmdStartProcess(VOID) { #ifndef STANDALONE PCOMSPEC_INFO ComSpecInfo; #endif SIZE_T CmdLen; PNEXT_CMD DataStruct = (PNEXT_CMD)SEG_OFF_TO_PTR(getDS(), getDX()); DPRINT1("CmdStartProcess -- DS:DX = %04X:%04X (DataStruct = 0x%p)\n", getDS(), getDX(), DataStruct); /* Pause the VM */ EmulatorPause(); #ifndef STANDALONE /* Check whether we need to shell out now in case we were started by a 32-bit app */ ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp); if (ComSpecInfo && ComSpecInfo->Terminated) { RemoveComSpecInfo(ComSpecInfo); DPRINT1("Exit DOS from start-app BOP\n"); setCF(1); goto Quit; } /* Clear the structure */ RtlZeroMemory(&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.CurDirectory = CurDirectory; CommandInfo.CurDirectoryLen = sizeof(CurDirectory); CommandInfo.Desktop = Desktop; CommandInfo.DesktopLen = sizeof(Desktop); CommandInfo.Title = Title; CommandInfo.TitleLen = sizeof(Title); CommandInfo.Env = Env; CommandInfo.EnvLen = EnvSize; if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK; Command: if (Repeat) CommandInfo.VDMState |= VDM_FLAG_RETRY; Repeat = FALSE; /* Get the VDM command information */ DPRINT1("Calling GetNextVDMCommand in CmdStartProcess: wait for new VDM task...\n"); if (!GetNextVDMCommand(&CommandInfo)) { DPRINT1("CmdStartProcess - GetNextVDMCommand failed, retrying... last error = %d\n", GetLastError()); if (CommandInfo.EnvLen > EnvSize) { /* Expand the environment size */ EnvSize = CommandInfo.EnvLen; CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize); /* Repeat the request */ Repeat = TRUE; goto Command; } /* Shouldn't happen */ DisplayMessage(L"An unrecoverable failure happened from start-app BOP; exiting DOS."); setCF(1); goto Quit; } // FIXME: What happens if some other 32-bit app is killed while we are waiting there?? DPRINT1("CmdStartProcess - GetNextVDMCommand succeeded, start app...\n"); #else if (!First) { DPRINT1("Exit DOS from start-app BOP\n"); setCF(1); goto Quit; } #endif /* Compute the command line length, not counting the terminating "\r\n" */ CmdLen = strlen(CmdLine); if (CmdLen >= 2 && CmdLine[CmdLen - 2] == '\r') CmdLen -= 2; DPRINT1("Starting '%s' ('%.*s')...\n", AppName, CmdLen, CmdLine); /* Start the process */ // FIXME: Merge 'Env' with the master environment SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0) // FIXME: Environment RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->AppNameSeg, DataStruct->AppNameOff), AppName, MAX_PATH); *(PBYTE)(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff)) = (BYTE)CmdLen; RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff + 1), CmdLine, DOS_CMDLINE_LENGTH); #ifndef STANDALONE /* Update console title if we run in a separate console */ if (SessionId != 0) SetConsoleTitleA(AppName); #endif First = FALSE; setCF(0); DPRINT1("App started!\n"); Quit: /* Resume the VM */ EmulatorResume(); }
static VOID WINAPI DosStart(LPWORD Stack) { BOOLEAN Success; DWORD Result; #ifndef STANDALONE INT i; #endif DPRINT("DosStart\n"); /* * We succeeded, deregister the DOS Starting BOP * so that no app will be able to call us back. */ RegisterBop(BOP_START_DOS, NULL); /* Initialize the callback context */ InitializeContext(&DosContext, BIOS_CODE_SEGMENT, 0x0010); Success = DosBIOSInitialize(); // Success &= DosKRNLInitialize(); if (!Success) { BiosDisplayMessage("DOS32 loading failed (Error: %u). The VDM will shut down.\n", GetLastError()); EmulatorTerminate(); return; } /* Load the mouse driver */ DosMouseInitialize(); #ifndef STANDALONE /* Parse the command line arguments */ for (i = 1; i < NtVdmArgc; i++) { if (wcsncmp(NtVdmArgv[i], L"-i", 2) == 0) { /* This is the session ID (hex format) */ SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 16); } } /* Initialize Win32-VDM environment */ Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize); if (Env == NULL) { DosDisplayMessage("Failed to initialize the global environment (Error: %u). The VDM will shut down.\n", GetLastError()); EmulatorTerminate(); return; } /* Clear the structure */ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo)); /* Get the initial information */ CommandInfo.TaskId = SessionId; CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS; GetNextVDMCommand(&CommandInfo); #else /* Retrieve the command to start */ if (NtVdmArgc >= 2) { WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, AppName, sizeof(AppName), NULL, NULL); if (NtVdmArgc >= 3) WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CmdLine, sizeof(CmdLine), NULL, NULL); else strcpy(CmdLine, ""); } else { DosDisplayMessage("Invalid DOS command line\n"); EmulatorTerminate(); return; } #endif /* * At this point, CS:IP points to the DOS BIOS exit code. If the * root command interpreter fails to start (or if it exits), DOS * exits and the VDM terminates. */ /* Start the root command interpreter */ // TODO: Really interpret the 'SHELL=' line of CONFIG.NT, and use it! /* * Prepare the stack for DosStartComSpec: * push Flags, CS and IP, and an extra WORD. */ setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = (WORD)getEFLAGS(); setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getCS(); setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getIP(); setSP(getSP() - sizeof(WORD)); Result = DosStartComSpec(TRUE, SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0), MAKELONG(getIP(), getCS()), #ifndef STANDALONE &RootCmd.ComSpecPsp #else NULL #endif ); if (Result != ERROR_SUCCESS) { /* Unprepare the stack for DosStartComSpec */ setSP(getSP() + 4*sizeof(WORD)); DosDisplayMessage("Failed to start the Command Interpreter (Error: %u). The VDM will shut down.\n", Result); EmulatorTerminate(); return; } #ifndef STANDALONE RootCmd.Terminated = FALSE; InsertComSpecInfo(&RootCmd); #endif /**/ /* Attach to the console and resume the VM */ DosProcessConsoleAttach(); EmulatorResume(); /**/ return; }
static VOID WINAPI DosInitialize(LPWORD Stack) { BOOLEAN Success = FALSE; /* Get the DOS kernel file name (NULL-terminated) */ // FIXME: Isn't it possible to use some DS:SI instead?? LPCSTR DosKernelFileName = (LPCSTR)SEG_OFF_TO_PTR(getCS(), getIP()); setIP(getIP() + strlen(DosKernelFileName) + 1); // Skip it DPRINT("DosInitialize('%s')\n", DosKernelFileName); /* Register the DOS BOPs */ RegisterBop(BOP_DOS, DosSystemBop ); RegisterBop(BOP_CMD, DosCmdInterpreterBop); if (DosKernelFileName && DosKernelFileName[0] != '\0') { HANDLE hDosBios; ULONG ulDosBiosSize = 0; /* Open the DOS BIOS file */ hDosBios = FileOpen(DosKernelFileName, &ulDosBiosSize); /* If we failed, bail out */ if (hDosBios == NULL) goto QuitCustom; /* 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 %04X:%04X, size 0x%X ; GetLastError() = %u\n", (Success ? "succeeded" : "failed"), 0x0070, 0x0000, ulDosBiosSize, GetLastError()); /* Close the DOS BIOS file */ FileClose(hDosBios); if (!Success) goto QuitCustom; /* Position execution pointers and return */ setCS(0x0070); setIP(0x0000); /* Return control */ QuitCustom: if (!Success) DisplayMessage(L"Custom DOS '%S' loading failed, what to do??", DosKernelFileName); } else { Success = DosBIOSInitialize(); // Success &= DosKRNLInitialize(); if (!Success) goto Quit32; /* Write the "bootsector" */ RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup, sizeof(Startup)); /* Register the DOS Starting BOP */ RegisterBop(BOP_START_DOS, DosStart); /* Position execution pointers and return */ setCS(0x0070); setIP(0x0000); /* Return control */ Quit32: if (!Success) DisplayMessage(L"DOS32 loading failed, what to do??"); } if (Success) { /* * We succeeded, deregister the DOS Loading BOP * so that no app will be able to call us back. */ RegisterBop(BOP_LOAD_DOS, NULL); } }
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; } } }
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(); }
static VOID WINAPI XmsBopProcedure(LPWORD Stack) { switch (getAH()) { /* Get XMS Version */ case 0x00: { setAX(0x0300); /* XMS version 3.00 */ setBX(0x0301); /* Driver version 3.01 */ setDX(0x0001); /* HMA present */ break; } /* Request HMA */ case 0x01: { /* Check whether HMA is already reserved */ if (IsHmaReserved) { /* It is, bail out */ setAX(0x0000); setBL(XMS_STATUS_HMA_IN_USE); break; } // NOTE: We implicitely suppose that we always have HMA. // If not, we should fail there with the XMS_STATUS_HMA_DOES_NOT_EXIST // error code. /* Check whether the requested size is above the minimal allowed one */ if (getDX() < HmaMinSize) { /* It is not, bail out */ setAX(0x0000); setBL(XMS_STATUS_HMA_MIN_SIZE); break; } /* Reserve it */ IsHmaReserved = TRUE; setAX(0x0001); setBL(XMS_STATUS_SUCCESS); break; } /* Release HMA */ case 0x02: { /* Check whether HMA was reserved */ if (!IsHmaReserved) { /* It was not, bail out */ setAX(0x0000); setBL(XMS_STATUS_HMA_NOT_ALLOCATED); break; } /* Release it */ IsHmaReserved = FALSE; setAX(0x0001); setBL(XMS_STATUS_SUCCESS); break; } /* Global Enable A20 */ case 0x03: { /* Enable A20 if needed */ if (!IsA20Enabled) { XmsLocalEnableA20(); if (getAX() != 0x0001) { /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */ break; } IsA20Enabled = TRUE; } setAX(0x0001); /* Line successfully enabled */ setBL(XMS_STATUS_SUCCESS); break; } /* Global Disable A20 */ case 0x04: { UCHAR Result = XMS_STATUS_SUCCESS; /* Disable A20 if needed */ if (IsA20Enabled) { XmsLocalDisableA20(); if (getAX() != 0x0001) { /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */ break; } IsA20Enabled = FALSE; Result = getBL(); } setAX(0x0001); /* Line successfully disabled */ setBL(Result); break; } /* Local Enable A20 */ case 0x05: { /* This call sets AX and BL to their correct values */ XmsLocalEnableA20(); break; } /* Local Disable A20 */ case 0x06: { /* This call sets AX and BL to their correct values */ XmsLocalDisableA20(); break; } /* Query A20 State */ case 0x07: { setAX(EmulatorGetA20()); setBL(XMS_STATUS_SUCCESS); break; } /* Query Free Extended Memory */ case 0x08: { setAX(XmsGetLargestFreeBlock()); setDX(FreeBlocks); if (FreeBlocks > 0) setBL(XMS_STATUS_SUCCESS); else setBL(XMS_STATUS_OUT_OF_MEMORY); break; } /* Allocate Extended Memory Block */ case 0x09: { WORD Handle; UCHAR Result = XmsAlloc(getDX(), &Handle); if (Result == XMS_STATUS_SUCCESS) { setAX(1); setDX(Handle); } else { setAX(0); setBL(Result); } break; } /* Free Extended Memory Block */ case 0x0A: { UCHAR Result = XmsFree(getDX()); setAX(Result == XMS_STATUS_SUCCESS); setBL(Result); break; } /* Move Extended Memory Block */ case 0x0B: { PVOID SourceAddress, DestAddress; PXMS_COPY_DATA CopyData = (PXMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI()); PXMS_HANDLE HandleEntry; if (CopyData->SourceHandle) { HandleEntry = GetHandleRecord(CopyData->SourceHandle); if (!ValidateHandle(HandleEntry)) { setAX(0); setBL(XMS_STATUS_BAD_SRC_HANDLE); break; } if (CopyData->SourceOffset >= HandleEntry->Size * XMS_BLOCK_SIZE) { setAX(0); setBL(XMS_STATUS_BAD_SRC_OFFSET); } SourceAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->SourceOffset); } else { /* The offset is actually a 16-bit segment:offset pointer */ SourceAddress = FAR_POINTER(CopyData->SourceOffset); } if (CopyData->DestHandle) { HandleEntry = GetHandleRecord(CopyData->DestHandle); if (!ValidateHandle(HandleEntry)) { setAX(0); setBL(XMS_STATUS_BAD_DEST_HANDLE); break; } if (CopyData->DestOffset >= HandleEntry->Size * XMS_BLOCK_SIZE) { setAX(0); setBL(XMS_STATUS_BAD_DEST_OFFSET); } DestAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->DestOffset); } else { /* The offset is actually a 16-bit segment:offset pointer */ DestAddress = FAR_POINTER(CopyData->DestOffset); } /* Perform the move */ RtlMoveMemory(DestAddress, SourceAddress, CopyData->Count); setAX(1); setBL(XMS_STATUS_SUCCESS); break; } /* Lock Extended Memory Block */ case 0x0C: { DWORD Address; UCHAR Result = XmsLock(getDX(), &Address); if (Result == XMS_STATUS_SUCCESS) { setAX(1); /* Store the LINEAR address in DX:BX */ setDX(HIWORD(Address)); setBX(LOWORD(Address)); } else { setAX(0); setBL(Result); } break; } /* Unlock Extended Memory Block */ case 0x0D: { UCHAR Result = XmsUnlock(getDX()); setAX(Result == XMS_STATUS_SUCCESS); setBL(Result); break; } /* Get Handle Information */ case 0x0E: { PXMS_HANDLE HandleEntry = GetHandleRecord(getDX()); UINT i; UCHAR Handles = 0; if (!ValidateHandle(HandleEntry)) { setAX(0); setBL(XMS_STATUS_INVALID_HANDLE); break; } for (i = 0; i < XMS_MAX_HANDLES; i++) { if (HandleTable[i].Handle == 0) Handles++; } setAX(1); setBH(HandleEntry->LockCount); setBL(Handles); setDX(HandleEntry->Size); break; } /* Reallocate Extended Memory Block */ case 0x0F: { UCHAR Result = XmsRealloc(getDX(), getBX()); setAX(Result == XMS_STATUS_SUCCESS); setBL(Result); break; } /* Request UMB */ case 0x10: { BOOLEAN Result; USHORT Segment = 0x0000; /* No preferred segment */ USHORT Size = getDX(); /* Size is in paragraphs */ Result = UmaDescReserve(&Segment, &Size); if (Result) setBX(Segment); else setBL(Size > 0 ? XMS_STATUS_SMALLER_UMB : XMS_STATUS_OUT_OF_UMBS); setDX(Size); setAX(Result); break; } /* Release UMB */ case 0x11: { BOOLEAN Result; USHORT Segment = getDX(); Result = UmaDescRelease(Segment); if (!Result) setBL(XMS_STATUS_INVALID_UMB); setAX(Result); break; } /* Reallocate UMB */ case 0x12: { BOOLEAN Result; USHORT Segment = getDX(); USHORT Size = getBX(); /* Size is in paragraphs */ Result = UmaDescReallocate(Segment, &Size); if (!Result) { if (Size > 0) { setBL(XMS_STATUS_SMALLER_UMB); setDX(Size); } else { setBL(XMS_STATUS_INVALID_UMB); } } setAX(Result); break; } default: { DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH()); setBL(XMS_STATUS_NOT_IMPLEMENTED); } } }