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 */ EmulatorMoveMemory(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); } }
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; } } }
static VOID DumpMemoryRaw(HANDLE hFile) { PVOID Buffer; SIZE_T Size; /* Dump the VM memory */ SetFilePointer(hFile, 0, NULL, FILE_BEGIN); Buffer = REAL_TO_PHYS(NULL); Size = MAX_ADDRESS - (ULONG_PTR)(NULL); WriteFile(hFile, Buffer, Size, &Size, NULL); }
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; } }
static VOID DumpMemoryTxt(HANDLE hFile) { #define LINE_SIZE 75 + 2 ULONG i; PBYTE Ptr1, Ptr2; CHAR LineBuffer[LINE_SIZE]; PCHAR Line; SIZE_T LineSize; /* Dump the VM memory */ SetFilePointer(hFile, 0, NULL, FILE_BEGIN); Ptr1 = Ptr2 = REAL_TO_PHYS(NULL); while (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0) { Ptr1 = Ptr2; Line = LineBuffer; /* Print the address */ Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, "%08x ", PHYS_TO_REAL(Ptr1)); /* Print up to 16 bytes... */ /* ... in hexadecimal form first... */ i = 0; while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0)) { Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, " %02x", *Ptr1); ++Ptr1; } /* ... align with spaces if needed... */ RtlFillMemory(Line, 0x0F + 4 - i, ' '); Line += 0x0F + 4 - i; /* ... then in character form. */ i = 0; while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr2) > 0)) { *Line++ = ((*Ptr2 >= 0x20 && *Ptr2 <= 0x7E) || (*Ptr2 >= 0x80 && *Ptr2 < 0xFF) ? *Ptr2 : '.'); ++Ptr2; } /* Newline */ *Line++ = '\r'; *Line++ = '\n'; /* Finally write the line to the file */ LineSize = Line - LineBuffer; WriteFile(hFile, LineBuffer, LineSize, &LineSize, NULL); } }
static VOID DemLoadNTDOSKernel(VOID) { BOOLEAN Success = FALSE; LPCSTR DosKernelFileName = "ntdos.sys"; HANDLE hDosKernel; ULONG ulDosKernelSize = 0; DPRINT1("You are loading Windows NT DOS!\n"); /* Open the DOS kernel file */ hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize); 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 file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n", DosKernelFileName, (Success ? "succeeded" : "failed"), getDI(), 0x0000, ulDosKernelSize, GetLastError()); /* Close the DOS kernel file */ FileClose(hDosKernel); Quit: if (!Success) { /* We failed everything, stop the VDM */ BiosDisplayMessage("Windows NT DOS kernel file '%s' loading failed (Error: %u). The VDM will shut down.\n", DosKernelFileName, GetLastError()); EmulatorTerminate(); return; } }
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 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); } } }
static UCHAR XmsRealloc(WORD Handle, WORD NewSize) { DWORD BlockNumber; PXMS_HANDLE HandleEntry = GetHandleRecord(Handle); DWORD CurrentIndex = 0; ULONG RunStart; ULONG RunSize; if (!ValidateHandle(HandleEntry)) return XMS_STATUS_INVALID_HANDLE; if (HandleEntry->LockCount) return XMS_STATUS_LOCKED; /* Get the block number */ BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE; if (NewSize < HandleEntry->Size) { /* Just reduce the size of this block */ RtlClearBits(&AllocBitmap, BlockNumber + NewSize, HandleEntry->Size - NewSize); FreeBlocks += HandleEntry->Size - NewSize; HandleEntry->Size = NewSize; } else if (NewSize > HandleEntry->Size) { /* Check if we can expand in-place */ if (RtlAreBitsClear(&AllocBitmap, BlockNumber + HandleEntry->Size, NewSize - HandleEntry->Size)) { /* Just increase the size of this block */ RtlSetBits(&AllocBitmap, BlockNumber + HandleEntry->Size, NewSize - HandleEntry->Size); FreeBlocks -= NewSize - HandleEntry->Size; HandleEntry->Size = NewSize; /* We're done */ return XMS_STATUS_SUCCESS; } /* Deallocate the current block range */ RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size); /* Find a new place for this block */ while (CurrentIndex < XMS_BLOCKS) { RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart); if (RunSize == 0) break; if (RunSize >= NewSize) { /* Allocate the new range */ RtlSetBits(&AllocBitmap, RunStart, NewSize); /* Move the data to the new location */ RtlMoveMemory((PVOID)REAL_TO_PHYS(XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE), (PVOID)REAL_TO_PHYS(HandleEntry->Address), HandleEntry->Size * XMS_BLOCK_SIZE); /* Update the handle entry */ HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE; HandleEntry->Size = NewSize; /* Update the free block counter */ FreeBlocks -= NewSize - HandleEntry->Size; return XMS_STATUS_SUCCESS; } /* Keep searching */ CurrentIndex = RunStart + RunSize; } /* Restore the old block range */ RtlSetBits(&AllocBitmap, BlockNumber, HandleEntry->Size); return XMS_STATUS_OUT_OF_MEMORY; } return XMS_STATUS_SUCCESS; }
static UCHAR XmsAlloc(WORD Size, PWORD Handle) { BYTE i; PXMS_HANDLE HandleEntry; DWORD CurrentIndex = 0; ULONG RunStart; ULONG RunSize; if (Size > FreeBlocks) return XMS_STATUS_OUT_OF_MEMORY; for (i = 0; i < XMS_MAX_HANDLES; i++) { HandleEntry = &HandleTable[i]; if (HandleEntry->Handle == 0) { *Handle = i + 1; break; } } if (i == XMS_MAX_HANDLES) return XMS_STATUS_OUT_OF_HANDLES; /* Optimize blocks */ for (i = 0; i < XMS_MAX_HANDLES; i++) { /* Skip free and locked blocks */ if (HandleEntry->Handle == 0 || HandleEntry->LockCount > 0) continue; CurrentIndex = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE; /* Check if there is any free space before this block */ RunSize = RtlFindLastBackwardRunClear(&AllocBitmap, CurrentIndex, &RunStart); if (RunSize == 0) break; /* Move this block back */ RtlMoveMemory((PVOID)REAL_TO_PHYS(HandleEntry->Address - RunSize * XMS_BLOCK_SIZE), (PVOID)REAL_TO_PHYS(HandleEntry->Address), RunSize * XMS_BLOCK_SIZE); /* Update the address */ HandleEntry->Address -= RunSize * XMS_BLOCK_SIZE; } while (CurrentIndex < XMS_BLOCKS) { RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart); if (RunSize == 0) break; if (RunSize >= HandleEntry->Size) { /* Allocate it here */ HandleEntry->Handle = i + 1; HandleEntry->LockCount = 0; HandleEntry->Size = Size; HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE; FreeBlocks -= Size; RtlSetBits(&AllocBitmap, RunStart, HandleEntry->Size); return XMS_STATUS_SUCCESS; } /* Keep searching */ CurrentIndex = RunStart + RunSize; } return XMS_STATUS_OUT_OF_MEMORY; }
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); } }