mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
[NTVDM]
Start of an implementation of a software DOS emulator. Brought to you by Aleksandar Andrejevic. Good luck ;) Remove the old language files. They will be recreated when the time comes. svn path=/branches/ntvdm/; revision=59249
This commit is contained in:
parent
dfd4fed945
commit
f1e0b70d39
29 changed files with 1498 additions and 616 deletions
|
@ -1,11 +1,16 @@
|
|||
|
||||
include_directories(.)
|
||||
include_directories(${REACTOS_SOURCE_DIR}/include/reactos/libs/softx86)
|
||||
|
||||
add_executable(ntvdm
|
||||
list(APPEND SOURCE
|
||||
bios.c
|
||||
dos.c
|
||||
emulator.c
|
||||
ntvdm.c
|
||||
ntvdm.rc)
|
||||
|
||||
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)
|
||||
target_link_libraries(ntvdm softx86 softx87)
|
||||
add_importlibs(ntvdm msvcrt user32 kernel32)
|
||||
add_dependencies(ntvdm softx86 softx87)
|
||||
add_cd_file(TARGET ntvdm DESTINATION reactos/system32 FOR all)
|
||||
|
|
225
subsystems/ntvdm/bios.c
Normal file
225
subsystems/ntvdm/bios.c
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Virtual DOS Machine
|
||||
* FILE: bios.c
|
||||
* PURPOSE: VDM BIOS
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
#include "ntvdm.h"
|
||||
|
||||
BYTE CursorRow, CursorCol;
|
||||
WORD ConsoleWidth, ConsoleHeight;
|
||||
|
||||
BOOLEAN BiosInitialize()
|
||||
{
|
||||
INT i;
|
||||
WORD Offset = 0;
|
||||
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
|
||||
LPBYTE BiosCode = (LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(BIOS_SEGMENT, 0));
|
||||
|
||||
/* Generate ISR stubs and fill the IVT */
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
IntVecTable[i * 2] = Offset;
|
||||
IntVecTable[i * 2 + 1] = BIOS_SEGMENT;
|
||||
|
||||
if (i != SPECIAL_INT_NUM)
|
||||
{
|
||||
BiosCode[Offset++] = 0xFA; // cli
|
||||
|
||||
BiosCode[Offset++] = 0x6A; // push i
|
||||
BiosCode[Offset++] = (BYTE)i;
|
||||
|
||||
BiosCode[Offset++] = 0xCD; // int SPECIAL_INT_NUM
|
||||
BiosCode[Offset++] = SPECIAL_INT_NUM;
|
||||
|
||||
BiosCode[Offset++] = 0x83; // add sp, 2
|
||||
BiosCode[Offset++] = 0xC4;
|
||||
BiosCode[Offset++] = 0x02;
|
||||
}
|
||||
|
||||
BiosCode[Offset++] = 0xCF; // iret
|
||||
}
|
||||
|
||||
/* Get the console buffer info */
|
||||
if (!GetConsoleScreenBufferInfo(ConsoleOutput, &ConsoleInfo))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Set the initial cursor position and console size */
|
||||
CursorCol = ConsoleInfo.dwCursorPosition.X;
|
||||
CursorRow = ConsoleInfo.dwCursorPosition.Y;
|
||||
ConsoleWidth = ConsoleInfo.dwSize.X;
|
||||
ConsoleHeight = ConsoleInfo.dwSize.Y;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static COORD BiosVideoAddressToCoord(ULONG Address)
|
||||
{
|
||||
COORD Result = {0, 0};
|
||||
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
||||
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(ConsoleOutput, &ConsoleInfo))
|
||||
{
|
||||
assert(0);
|
||||
return Result;
|
||||
}
|
||||
|
||||
Result.X = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) % ConsoleInfo.dwSize.X;
|
||||
Result.Y = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) / ConsoleInfo.dwSize.X;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
|
||||
{
|
||||
ULONG i;
|
||||
COORD Coordinates;
|
||||
DWORD CharsWritten;
|
||||
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
/* Loop through all the addresses */
|
||||
for (i = StartAddress; i < EndAddress; i++)
|
||||
{
|
||||
/* Get the coordinates */
|
||||
Coordinates = BiosVideoAddressToCoord(i);
|
||||
|
||||
/* Check if this is a character byte or an attribute byte */
|
||||
if ((i - CONSOLE_VIDEO_MEM_START) % 2 == 0)
|
||||
{
|
||||
/* This is a regular character */
|
||||
FillConsoleOutputCharacterA(ConsoleOutput,
|
||||
*(PCHAR)((ULONG_PTR)BaseAddress + i),
|
||||
sizeof(CHAR),
|
||||
Coordinates,
|
||||
&CharsWritten);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an attribute */
|
||||
FillConsoleOutputAttribute(ConsoleOutput,
|
||||
*(PCHAR)((ULONG_PTR)BaseAddress + i),
|
||||
sizeof(CHAR),
|
||||
Coordinates,
|
||||
&CharsWritten);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress)
|
||||
{
|
||||
ULONG i;
|
||||
COORD Coordinates;
|
||||
WORD Attribute;
|
||||
DWORD CharsWritten;
|
||||
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
/* Loop through all the addresses */
|
||||
for (i = StartAddress; i < EndAddress; i++)
|
||||
{
|
||||
/* Get the coordinates */
|
||||
Coordinates = BiosVideoAddressToCoord(i);
|
||||
|
||||
/* Check if this is a character byte or an attribute byte */
|
||||
if ((i - CONSOLE_VIDEO_MEM_START) % 2 == 0)
|
||||
{
|
||||
/* This is a regular character */
|
||||
ReadConsoleOutputCharacterA(ConsoleOutput,
|
||||
(LPSTR)((ULONG_PTR)BaseAddress + i),
|
||||
sizeof(CHAR),
|
||||
Coordinates,
|
||||
&CharsWritten);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an attribute */
|
||||
ReadConsoleOutputAttribute(ConsoleOutput,
|
||||
&Attribute,
|
||||
sizeof(CHAR),
|
||||
Coordinates,
|
||||
&CharsWritten);
|
||||
|
||||
*(PCHAR)((ULONG_PTR)BaseAddress + i) = LOBYTE(Attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID BiosVideoService()
|
||||
{
|
||||
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
INT CursorHeight;
|
||||
BOOLEAN Invisible = FALSE;
|
||||
COORD CursorPosition;
|
||||
CONSOLE_CURSOR_INFO CursorInfo;
|
||||
DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
|
||||
DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
|
||||
DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
|
||||
|
||||
switch (LOBYTE(Eax))
|
||||
{
|
||||
/* Set Text-Mode Cursor Shape */
|
||||
case 0x01:
|
||||
{
|
||||
/* Retrieve and validate the input */
|
||||
Invisible = ((HIBYTE(Ecx) >> 5) & 0x03) ? TRUE : FALSE;
|
||||
CursorHeight = (HIBYTE(Ecx) & 0x1F) - (LOBYTE(Ecx) & 0x1F);
|
||||
if (CursorHeight < 1) CursorHeight = 1;
|
||||
if (CursorHeight > 100) CursorHeight = 100;
|
||||
|
||||
/* Set the cursor */
|
||||
CursorInfo.dwSize = (CursorHeight * 100) / CONSOLE_FONT_HEIGHT;
|
||||
CursorInfo.bVisible = !Invisible;
|
||||
SetConsoleCursorInfo(ConsoleOutput, &CursorInfo);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set Cursor Position */
|
||||
case 0x02:
|
||||
{
|
||||
CursorPosition.X = LOBYTE(Edx);
|
||||
CursorPosition.Y = HIBYTE(Edx);
|
||||
|
||||
SetConsoleCursorPosition(ConsoleOutput, CursorPosition);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Scroll Up Window */
|
||||
case 0x06:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Scroll Down Window */
|
||||
case 0x07:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read Character And Attribute At Cursor Position */
|
||||
case 0x08:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write Character And Attribute At Cursor Position */
|
||||
case 0x09:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write Character Only At Cursor Position */
|
||||
case 0x0A:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF */
|
707
subsystems/ntvdm/dos.c
Normal file
707
subsystems/ntvdm/dos.c
Normal file
|
@ -0,0 +1,707 @@
|
|||
/*
|
||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Virtual DOS Machine
|
||||
* FILE: dos.c
|
||||
* PURPOSE: VDM DOS Kernel
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
#include "ntvdm.h"
|
||||
|
||||
WORD CurrentPsp = SYSTEM_PSP, LastError = 0;
|
||||
|
||||
static VOID DosCombineFreeBlocks()
|
||||
{
|
||||
WORD Segment = FIRST_MCB_SEGMENT;
|
||||
PDOS_MCB CurrentMcb, NextMcb;
|
||||
|
||||
/* Loop through all the blocks */
|
||||
while (TRUE)
|
||||
{
|
||||
/* Get a pointer to the MCB */
|
||||
CurrentMcb = SEGMENT_TO_MCB(Segment);
|
||||
|
||||
/* Ignore the last block */
|
||||
if (CurrentMcb->BlockType == 'Z') break;
|
||||
|
||||
/* Get a pointer to the next MCB */
|
||||
NextMcb = SEGMENT_TO_MCB(Segment + CurrentMcb->Size + 1);
|
||||
|
||||
/* If both this block and the next one are free, combine them */
|
||||
if ((CurrentMcb->OwnerPsp == 0) && (NextMcb->OwnerPsp == 0))
|
||||
{
|
||||
CurrentMcb->Size += NextMcb->Size + 1;
|
||||
CurrentMcb->BlockType = NextMcb->BlockType;
|
||||
|
||||
/* Invalidate the next MCB */
|
||||
NextMcb->BlockType = 'I';
|
||||
|
||||
/* Try to combine the current block again with the next one */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Update the segment and continue */
|
||||
Segment += CurrentMcb->Size + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
|
||||
{
|
||||
PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
|
||||
ULONG TotalSize = 0;
|
||||
WORD DestSegment;
|
||||
|
||||
Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
|
||||
|
||||
/* Calculate the size of the environment block */
|
||||
while (*Ptr)
|
||||
{
|
||||
TotalSize += strlen(Ptr) + 1;
|
||||
Ptr += strlen(Ptr) + 1;
|
||||
}
|
||||
TotalSize++;
|
||||
|
||||
/* Allocate the memory for the environment block */
|
||||
DestSegment = DosAllocateMemory((TotalSize + 0x0F) >> 4);
|
||||
if (!DestSegment) return 0;
|
||||
|
||||
Ptr = SourceBuffer;
|
||||
|
||||
DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
|
||||
while (*Ptr)
|
||||
{
|
||||
/* Copy the string */
|
||||
strcpy(DestBuffer, Ptr);
|
||||
|
||||
/* Advance to the next string */
|
||||
Ptr += strlen(Ptr) + 1;
|
||||
DestBuffer += strlen(Ptr) + 1;
|
||||
}
|
||||
|
||||
/* Set the final zero */
|
||||
*DestBuffer = 0;
|
||||
|
||||
return DestSegment;
|
||||
}
|
||||
|
||||
WORD DosAllocateMemory(WORD Size)
|
||||
{
|
||||
WORD Result = 0, Segment = FIRST_MCB_SEGMENT;
|
||||
PDOS_MCB CurrentMcb, NextMcb;
|
||||
|
||||
/* Find an unallocated block */
|
||||
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')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only check free blocks */
|
||||
if (CurrentMcb->OwnerPsp != 0) goto Next;
|
||||
|
||||
/* Check if the block is big enough */
|
||||
if (CurrentMcb->Size < Size) goto Next;
|
||||
|
||||
/* It is, update the smallest found so far */
|
||||
if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
|
||||
{
|
||||
Result = Segment;
|
||||
}
|
||||
|
||||
Next:
|
||||
/* If this was the last MCB in the chain, quit. */
|
||||
if (CurrentMcb->BlockType == 'Z') break;
|
||||
|
||||
/* Otherwise, update the segment and continue */
|
||||
Segment += CurrentMcb->Size + 1;
|
||||
}
|
||||
|
||||
/* If we didn't find a free block, return zero */
|
||||
if (Result == 0) 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 = Size - CurrentMcb->Size - 1;
|
||||
NextMcb->OwnerPsp = 0;
|
||||
|
||||
/* Update the current block */
|
||||
CurrentMcb->BlockType = 'M';
|
||||
CurrentMcb->Size = Size;
|
||||
|
||||
/* Combine consecutive free blocks into larger blocks */
|
||||
DosCombineFreeBlocks();
|
||||
}
|
||||
|
||||
/* Take ownership of the block */
|
||||
CurrentMcb->OwnerPsp = CurrentPsp;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
WORD DosResizeMemory(WORD Segment, WORD NewSize)
|
||||
{
|
||||
WORD ReturnSize = 0, CurrentSeg;
|
||||
PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), CurrentMcb;
|
||||
BOOLEAN FinalBlockUsed = FALSE;
|
||||
|
||||
/* We can't expand the last block */
|
||||
if (Mcb->BlockType != 'M') return 0;
|
||||
|
||||
/* Check if need to expand or contract the block */
|
||||
if (NewSize > Mcb->Size)
|
||||
{
|
||||
ReturnSize = Mcb->Size;
|
||||
|
||||
/* Get the segment of the next MCB */
|
||||
CurrentSeg = Segment + Mcb->Size + 1;
|
||||
|
||||
/* Calculate the maximum amount of memory this block could expand to */
|
||||
while (ReturnSize < NewSize)
|
||||
{
|
||||
/* Get the MCB */
|
||||
CurrentMcb = SEGMENT_TO_MCB(CurrentSeg);
|
||||
|
||||
/* We can't expand the block over an allocated block */
|
||||
if (CurrentMcb->OwnerPsp != 0) break;
|
||||
|
||||
ReturnSize += CurrentMcb->Size + 1;
|
||||
|
||||
/* Check if this is the last block */
|
||||
if (CurrentMcb->BlockType == 'Z')
|
||||
{
|
||||
FinalBlockUsed = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update the segment and continue */
|
||||
CurrentSeg += CurrentMcb->Size + 1;
|
||||
}
|
||||
|
||||
/* Check if we need to split the last block */
|
||||
if (ReturnSize > NewSize)
|
||||
{
|
||||
/* Initialize the new MCB structure */
|
||||
CurrentMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
|
||||
CurrentMcb->BlockType = (FinalBlockUsed) ? 'Z' : 'M';
|
||||
CurrentMcb->Size = ReturnSize - NewSize - 1;
|
||||
CurrentMcb->OwnerPsp = 0;
|
||||
}
|
||||
|
||||
/* Calculate the new size of the block */
|
||||
ReturnSize = min(ReturnSize, NewSize);
|
||||
|
||||
/* Update the MCB */
|
||||
if (FinalBlockUsed) Mcb->BlockType = 'Z';
|
||||
Mcb->Size = ReturnSize;
|
||||
}
|
||||
else if (NewSize < Mcb->Size)
|
||||
{
|
||||
/* Just split the block */
|
||||
CurrentMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
|
||||
CurrentMcb->BlockType = Mcb->BlockType;
|
||||
CurrentMcb->Size = Mcb->Size - NewSize - 1;
|
||||
CurrentMcb->OwnerPsp = 0;
|
||||
|
||||
/* Update the MCB */
|
||||
Mcb->BlockType = 'M';
|
||||
Mcb->Size = NewSize;
|
||||
|
||||
ReturnSize = NewSize;
|
||||
}
|
||||
|
||||
/* Combine consecutive free blocks into larger blocks */
|
||||
DosCombineFreeBlocks();
|
||||
|
||||
return ReturnSize;
|
||||
}
|
||||
|
||||
BOOLEAN DosFreeMemory(WORD Segment)
|
||||
{
|
||||
PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
|
||||
|
||||
/* Make sure the MCB is valid */
|
||||
if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') return FALSE;
|
||||
|
||||
/* Mark the block as free */
|
||||
Mcb->OwnerPsp = 0;
|
||||
|
||||
/* Combine consecutive free blocks into larger blocks */
|
||||
DosCombineFreeBlocks();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WORD DosCreateFile(LPCSTR FilePath)
|
||||
{
|
||||
// TODO: NOT IMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
WORD DosOpenFile(LPCSTR FilePath)
|
||||
{
|
||||
// TODO: NOT IMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
|
||||
{
|
||||
INT i;
|
||||
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 program size */
|
||||
PspBlock->MemSize = ProgramSize;
|
||||
|
||||
/* Save the interrupt vectors */
|
||||
PspBlock->TerminateAddress = IntVecTable[0x22];
|
||||
PspBlock->BreakAddress = IntVecTable[0x23];
|
||||
PspBlock->CriticalAddress = IntVecTable[0x24];
|
||||
|
||||
/* Set the parent PSP */
|
||||
PspBlock->ParentPsp = CurrentPsp;
|
||||
|
||||
/* Initialize the handle table */
|
||||
for (i = 0; i < 20; i++) PspBlock->HandleTable[i] = 0xFF;
|
||||
|
||||
/* Did we get an environment segment? */
|
||||
if (!Environment)
|
||||
{
|
||||
/* No, copy the one from the parent */
|
||||
Environment = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
|
||||
? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
|
||||
: SYSTEM_ENV_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 = strlen(CommandLine);
|
||||
RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
|
||||
PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
|
||||
}
|
||||
|
||||
BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
|
||||
{
|
||||
BOOLEAN Success = FALSE;
|
||||
HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
|
||||
LPBYTE Address = NULL;
|
||||
LPSTR ProgramFilePath, Parameters[128];
|
||||
CHAR CommandLineCopy[128];
|
||||
INT ParamCount = 0;
|
||||
WORD Segment, FileSize;
|
||||
|
||||
/* Save a copy of the command line */
|
||||
strcpy(CommandLineCopy, CommandLine);
|
||||
|
||||
/* Get the file name of the executable */
|
||||
ProgramFilePath = strtok(CommandLineCopy, " \t");
|
||||
|
||||
/* Load the parameters in the local array */
|
||||
while ((ParamCount < 256)
|
||||
&& ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
|
||||
{
|
||||
ParamCount++;
|
||||
}
|
||||
|
||||
/* Open a handle to the executable */
|
||||
FileHandle = CreateFileA(ProgramFilePath,
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (FileHandle == INVALID_HANDLE_VALUE) 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) goto Cleanup;
|
||||
|
||||
/* Map the file into memory */
|
||||
Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (Address == NULL) goto Cleanup;
|
||||
|
||||
/* Check if this is an EXE file or a COM file */
|
||||
if (Address[0] == 'M' && Address[1] == 'Z')
|
||||
{
|
||||
/* EXE file */
|
||||
|
||||
// TODO: NOT IMPLEMENTED
|
||||
DisplayMessage(L"EXE files are not yet supported!");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* COM file */
|
||||
|
||||
/* Allocate memory for the whole program and the PSP */
|
||||
Segment = DosAllocateMemory((FileSize + sizeof(DOS_PSP)) >> 4);
|
||||
if (Segment == 0) goto Cleanup;
|
||||
|
||||
/* Copy the program to Segment:0100 */
|
||||
RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
|
||||
+ TO_LINEAR(Segment, 0x100)),
|
||||
Address,
|
||||
FileSize);
|
||||
|
||||
/* Initialize the PSP */
|
||||
DosInitializePsp(Segment,
|
||||
CommandLine,
|
||||
(FileSize + sizeof(DOS_PSP)) >> 4,
|
||||
EnvBlock);
|
||||
|
||||
/* Set the initial segment registers */
|
||||
EmulatorSetRegister(EMULATOR_REG_DS, Segment);
|
||||
EmulatorSetRegister(EMULATOR_REG_ES, Segment);
|
||||
|
||||
/* Set the stack to the last word of the segment */
|
||||
EmulatorSetStack(Segment, 0xFFFE);
|
||||
|
||||
/* Execute */
|
||||
CurrentPsp = Segment;
|
||||
EmulatorExecute(Segment, 0x100);
|
||||
|
||||
Success = TRUE;
|
||||
}
|
||||
|
||||
Cleanup:
|
||||
/* 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 Success;
|
||||
}
|
||||
|
||||
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
|
||||
{
|
||||
WORD McbSegment = FIRST_MCB_SEGMENT;
|
||||
PDOS_MCB CurrentMcb;
|
||||
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
|
||||
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
|
||||
|
||||
/* Check if this PSP is it's own parent */
|
||||
if (PspBlock->ParentPsp == Psp) goto Done;
|
||||
|
||||
// TODO: Close all handles opened by the process
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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) VdmRunning = FALSE;
|
||||
}
|
||||
|
||||
/* Return control to the parent process */
|
||||
EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
|
||||
LOWORD(PspBlock->TerminateAddress));
|
||||
}
|
||||
|
||||
CHAR DosReadCharacter()
|
||||
{
|
||||
// TODO: STDIN can be redirected under DOS 2.0+
|
||||
return _getch();
|
||||
}
|
||||
|
||||
VOID DosPrintCharacter(CHAR Character)
|
||||
{
|
||||
// TODO: STDOUT can be redirected under DOS 2.0+
|
||||
if (Character == '\r') Character = '\n';
|
||||
putchar(Character);
|
||||
}
|
||||
|
||||
VOID DosInt20h(WORD CodeSegment)
|
||||
{
|
||||
/* This is the exit interrupt */
|
||||
DosTerminateProcess(CodeSegment, 0);
|
||||
}
|
||||
|
||||
VOID DosInt21h(WORD CodeSegment)
|
||||
{
|
||||
INT i;
|
||||
CHAR Character;
|
||||
PCHAR String;
|
||||
PDOS_INPUT_BUFFER InputBuffer;
|
||||
DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
|
||||
DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
|
||||
DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
|
||||
WORD DataSegment = EmulatorGetRegister(EMULATOR_REG_DS);
|
||||
WORD ExtSegment = EmulatorGetRegister(EMULATOR_REG_ES);
|
||||
|
||||
/* Check the value in the AH register */
|
||||
switch (HIBYTE(Eax))
|
||||
{
|
||||
/* Terminate Program */
|
||||
case 0x00:
|
||||
{
|
||||
DosTerminateProcess(CodeSegment, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read Character And Echo */
|
||||
case 0x01:
|
||||
{
|
||||
Character = DosReadCharacter();
|
||||
DosPrintCharacter(Character);
|
||||
EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Print Character */
|
||||
case 0x02:
|
||||
{
|
||||
DosPrintCharacter(LOBYTE(Edx));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read Character Without Echo */
|
||||
case 0x08:
|
||||
{
|
||||
EmulatorSetRegister(EMULATOR_REG_AX,
|
||||
(Eax & 0xFFFFFF00) | DosReadCharacter());
|
||||
break;
|
||||
}
|
||||
|
||||
/* Print String */
|
||||
case 0x09:
|
||||
{
|
||||
String = (PCHAR)((ULONG_PTR)BaseAddress
|
||||
+ TO_LINEAR(DataSegment, LOWORD(Edx)));
|
||||
|
||||
while ((*String) != '$')
|
||||
{
|
||||
DosPrintCharacter(*String);
|
||||
String++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read Buffered Input */
|
||||
case 0x0A:
|
||||
{
|
||||
InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
|
||||
+ TO_LINEAR(DataSegment,
|
||||
LOWORD(Edx)));
|
||||
|
||||
InputBuffer->Length = 0;
|
||||
for (i = 0; i < InputBuffer->MaxLength; i ++)
|
||||
{
|
||||
Character = DosReadCharacter();
|
||||
DosPrintCharacter(Character);
|
||||
InputBuffer->Buffer[InputBuffer->Length] = Character;
|
||||
if (Character == '\r') break;
|
||||
InputBuffer->Length++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Allocate Memory */
|
||||
case 0x48:
|
||||
{
|
||||
WORD Segment = DosAllocateMemory(LOWORD(Ebx));
|
||||
if (Segment != 0)
|
||||
{
|
||||
EmulatorSetRegister(EMULATOR_REG_AX, Segment);
|
||||
EmulatorClearFlag(EMULATOR_FLAG_CF);
|
||||
}
|
||||
else EmulatorSetFlag(EMULATOR_FLAG_CF);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Free Memory */
|
||||
case 0x49:
|
||||
{
|
||||
if (DosFreeMemory(ExtSegment))
|
||||
{
|
||||
EmulatorClearFlag(EMULATOR_FLAG_CF);
|
||||
}
|
||||
else EmulatorSetFlag(EMULATOR_FLAG_CF);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Resize Memory Block */
|
||||
case 0x4A:
|
||||
{
|
||||
WORD Size = DosResizeMemory(ExtSegment, LOWORD(Ebx));
|
||||
|
||||
if (Size != 0)
|
||||
{
|
||||
EmulatorSetRegister(EMULATOR_REG_BX, Size);
|
||||
EmulatorClearFlag(EMULATOR_FLAG_CF);
|
||||
}
|
||||
else EmulatorSetFlag(EMULATOR_FLAG_CF);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Terminate With Return Code */
|
||||
case 0x4C:
|
||||
{
|
||||
DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Unsupported */
|
||||
default:
|
||||
{
|
||||
EmulatorSetFlag(EMULATOR_FLAG_CF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID DosBreakInterrupt()
|
||||
{
|
||||
VdmRunning = FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN DosInitialize()
|
||||
{
|
||||
PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
|
||||
FILE *Stream;
|
||||
WCHAR Buffer[256];
|
||||
LPWSTR SourcePtr, Environment;
|
||||
LPSTR AsciiString;
|
||||
LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
|
||||
DWORD AsciiSize;
|
||||
|
||||
/* Initialize the MCB */
|
||||
Mcb->BlockType = 'Z';
|
||||
Mcb->Size = (WORD)USER_MEMORY_SIZE;
|
||||
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);
|
||||
|
||||
/* Free the memory */
|
||||
HeapFree(GetProcessHeap(), 0, AsciiString);
|
||||
|
||||
/* Move to the next string */
|
||||
SourcePtr += wcslen(SourcePtr) + 1;
|
||||
DestPtr += strlen(AsciiString) + 1;
|
||||
}
|
||||
|
||||
/* Free the memory allocated for environment strings */
|
||||
FreeEnvironmentStringsW(Environment);
|
||||
|
||||
/* Read CONFIG.SYS */
|
||||
Stream = _wfopen(DOS_CONFIG_PATH, L"r");
|
||||
if (Stream != NULL)
|
||||
{
|
||||
while (fgetws(Buffer, 256, Stream))
|
||||
{
|
||||
// TODO: Parse the line
|
||||
}
|
||||
fclose(Stream);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* EOF */
|
252
subsystems/ntvdm/emulator.c
Normal file
252
subsystems/ntvdm/emulator.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* 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 <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "ntvdm.h"
|
||||
#include <softx86/softx86.h>
|
||||
#include <softx86/softx87.h>
|
||||
|
||||
softx86_ctx EmulatorContext;
|
||||
softx87_ctx FpuEmulatorContext;
|
||||
|
||||
static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||
{
|
||||
/* Make sure the requested address is valid */
|
||||
if ((Address + Size) >= MAX_ADDRESS) return;
|
||||
|
||||
/* Are we reading some of the console video memory? */
|
||||
if (((Address + Size) >= CONSOLE_VIDEO_MEM_START)
|
||||
&& (Address < CONSOLE_VIDEO_MEM_END))
|
||||
{
|
||||
/* Call the VDM BIOS to update the video memory */
|
||||
BiosUpdateConsole(max(Address, CONSOLE_VIDEO_MEM_START),
|
||||
min(Address + Size, CONSOLE_VIDEO_MEM_END));
|
||||
}
|
||||
|
||||
/* Read the data from the virtual address space and store it in the buffer */
|
||||
RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
|
||||
}
|
||||
|
||||
static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||
{
|
||||
/* 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((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
|
||||
|
||||
/* Check if we modified the console video memory */
|
||||
if (((Address + Size) >= CONSOLE_VIDEO_MEM_START)
|
||||
&& (Address < CONSOLE_VIDEO_MEM_END))
|
||||
{
|
||||
/* Call the VDM BIOS to update the screen */
|
||||
BiosUpdateConsole(max(Address, CONSOLE_VIDEO_MEM_START),
|
||||
min(Address + Size, CONSOLE_VIDEO_MEM_END));
|
||||
}
|
||||
}
|
||||
|
||||
static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||
{
|
||||
// TODO: NOT IMPLEMENTED!
|
||||
}
|
||||
|
||||
static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||
{
|
||||
// TODO: NOT IMPLEMENTED!
|
||||
}
|
||||
|
||||
static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
|
||||
{
|
||||
WORD StackSegment, StackPointer, CodeSegment, InstructionPointer;
|
||||
BYTE IntNum;
|
||||
|
||||
/* Check if this is the special interrupt */
|
||||
if (Number == SPECIAL_INT_NUM)
|
||||
{
|
||||
/* Get the SS:SP */
|
||||
StackSegment = EmulatorContext.state->segment_reg[SX86_SREG_SS].val;
|
||||
StackPointer = EmulatorContext.state->general_reg[SX86_REG_SP].val;
|
||||
|
||||
/* Get the interrupt number */
|
||||
IntNum = *(LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(StackSegment, StackPointer));
|
||||
|
||||
/* Move the stack pointer forward one word to skip the interrupt number */
|
||||
StackPointer += sizeof(WORD);
|
||||
|
||||
/* Get the CS:IP */
|
||||
InstructionPointer = *(LPWORD)((ULONG_PTR)BaseAddress
|
||||
+ TO_LINEAR(StackSegment, StackPointer));
|
||||
CodeSegment = *(LPWORD)((ULONG_PTR)BaseAddress
|
||||
+ TO_LINEAR(StackSegment, StackPointer + sizeof(WORD)));
|
||||
|
||||
/* Check if this was an exception */
|
||||
if (IntNum < 8)
|
||||
{
|
||||
/* Display a message to the user */
|
||||
DisplayMessage(L"Exception: %s occured at %04X:%04X",
|
||||
ExceptionName[IntNum],
|
||||
CodeSegment,
|
||||
InstructionPointer);
|
||||
|
||||
/* Stop the VDM */
|
||||
VdmRunning = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (IntNum)
|
||||
{
|
||||
case VIDEO_BIOS_INTERRUPT:
|
||||
{
|
||||
/* This is the video BIOS interrupt, call the BIOS */
|
||||
BiosVideoService();
|
||||
break;
|
||||
}
|
||||
case 0x20:
|
||||
{
|
||||
DosInt20h(CodeSegment);
|
||||
break;
|
||||
}
|
||||
case 0x21:
|
||||
{
|
||||
DosInt21h(CodeSegment);
|
||||
break;
|
||||
}
|
||||
case 0x23:
|
||||
{
|
||||
DosBreakInterrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||
|
||||
BOOLEAN EmulatorInitialize()
|
||||
{
|
||||
/* Allocate memory for the 16-bit address space */
|
||||
BaseAddress = HeapAlloc(GetProcessHeap(), 0, MAX_ADDRESS);
|
||||
if (BaseAddress == NULL) return FALSE;
|
||||
|
||||
/* Initialize the softx86 CPU emulator */
|
||||
if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80186))
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, BaseAddress);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Initialize the softx87 FPU emulator*/
|
||||
if(!softx87_init(&FpuEmulatorContext, SX87_FPULEVEL_8087))
|
||||
{
|
||||
softx86_free(&EmulatorContext);
|
||||
HeapFree(GetProcessHeap(), 0, BaseAddress);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Set memory read/write callbacks */
|
||||
EmulatorContext.callbacks->on_read_memory = EmulatorReadMemory;
|
||||
EmulatorContext.callbacks->on_write_memory = EmulatorWriteMemory;
|
||||
|
||||
/* Set MMIO read/write callbacks */
|
||||
EmulatorContext.callbacks->on_read_io = EmulatorReadIo;
|
||||
EmulatorContext.callbacks->on_write_io = EmulatorWriteIo;
|
||||
|
||||
/* Set interrupt callbacks */
|
||||
EmulatorContext.callbacks->on_sw_int = EmulatorSoftwareInt;
|
||||
|
||||
/* Connect the emulated FPU to the emulated CPU */
|
||||
softx87_connect_to_CPU(&EmulatorContext, &FpuEmulatorContext);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID EmulatorSetStack(WORD Segment, WORD Offset)
|
||||
{
|
||||
/* Call the softx86 API */
|
||||
softx86_set_stack_ptr(&EmulatorContext, Segment, Offset);
|
||||
}
|
||||
|
||||
VOID EmulatorExecute(WORD Segment, WORD Offset)
|
||||
{
|
||||
/* Call the softx86 API */
|
||||
softx86_set_instruction_ptr(&EmulatorContext, Segment, Offset);
|
||||
}
|
||||
|
||||
VOID EmulatorInterrupt(BYTE Number)
|
||||
{
|
||||
LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
|
||||
UINT Segment, Offset;
|
||||
|
||||
/* Get the segment and offset */
|
||||
Segment = HIWORD(IntVecTable[Number]);
|
||||
Offset = LOWORD(IntVecTable[Number]);
|
||||
|
||||
/* Call the softx86 API */
|
||||
softx86_make_simple_interrupt_call(&EmulatorContext, &Segment, &Offset);
|
||||
}
|
||||
|
||||
ULONG EmulatorGetRegister(ULONG Register)
|
||||
{
|
||||
if (Register < EMULATOR_REG_CS)
|
||||
{
|
||||
return EmulatorContext.state->general_reg[Register].val;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EmulatorContext.state->segment_reg[(Register >> 3) - 1].val;
|
||||
}
|
||||
}
|
||||
|
||||
VOID EmulatorSetRegister(ULONG Register, ULONG Value)
|
||||
{
|
||||
if (Register < EMULATOR_REG_CS)
|
||||
{
|
||||
EmulatorContext.state->general_reg[Register].val = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
EmulatorContext.state->segment_reg[(Register >> 3) - 1].val = Value;
|
||||
}
|
||||
}
|
||||
|
||||
BOOLEAN EmulatorGetFlag(ULONG Flag)
|
||||
{
|
||||
return (EmulatorContext.state->reg_flags.val & Flag);
|
||||
}
|
||||
|
||||
VOID EmulatorSetFlag(ULONG Flag)
|
||||
{
|
||||
EmulatorContext.state->reg_flags.val |= Flag;
|
||||
}
|
||||
|
||||
VOID EmulatorClearFlag(ULONG Flag)
|
||||
{
|
||||
EmulatorContext.state->reg_flags.val &= ~Flag;
|
||||
}
|
||||
|
||||
VOID EmulatorStep()
|
||||
{
|
||||
/* Call the softx86 API */
|
||||
softx86_step(&EmulatorContext);
|
||||
}
|
||||
|
||||
VOID EmulatorCleanup()
|
||||
{
|
||||
/* Free the memory allocated for the 16-bit address space */
|
||||
if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
|
||||
|
||||
/* Free the softx86 CPU and FPU emulator */
|
||||
softx86_free(&EmulatorContext);
|
||||
softx87_free(&FpuEmulatorContext);
|
||||
}
|
||||
|
||||
/* EOF */
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Ïîääðúæêà íà ÐåàêòÎÑ çà ïðèâèäíà ÄÎÑ ìàøèíà.\n"
|
||||
STRING_PromptMsg, "Íàïèøåòå r<cr> çà ïóñêàíå, s<cr> çà ñïèðàíå èëè q<cr> çà èçõîä."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/* FILE: subsystems/ntvdm/lang/cs-CZ.rc
|
||||
* TRANSLATOR: Radek Liska aka Black_Fox (radekliska at gmail dot com)
|
||||
* UPDATED: 2008-06-24
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_CZECH, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS podpora virtuálního DOS stroje.\n"
|
||||
STRING_PromptMsg, "Napište r<cr> pro spuštìní, s<cr> pro vypnutí nebo q<cr> pro ukonèení."
|
||||
END
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Translate into German.
|
||||
* By Rouven Wessling 2005 pentiumforever@gmail.com
|
||||
* 2008 dark_shadow@gmx.at
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS virtuelle DOS-Unterstützung.\n"
|
||||
STRING_PromptMsg, "Drücken Sie r<cr> zum Starten, s<cr> zum Herunterfahren oder q<cr> zum Beenden."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS Virtual DOS Machine support.\n"
|
||||
STRING_PromptMsg, "Type r<cr> to run, s<cr> to shutdown or q<cr> to quit now."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Spanish Resource File
|
||||
* Reactos(c)2006 Samuel Serapion
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Maquina virtual de DOS en ReactOS.\n"
|
||||
STRING_PromptMsg, "Escriba r<cr> para correr, s<cr> para desactivar or q<cr> para salir ahora."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Aide de la Machine DOS Virtuel de ReactOS.\n"
|
||||
STRING_PromptMsg, "Taper r<cr> pour démarrer, s<cr> pour éteindre ou q<cr> pour quitter maintenant."
|
||||
END
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* Hungarian resource file for ntvdm
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
* Translation by Robert Horvath 2005 - talley at cubeclub.hu
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS Virtuális DOS Gép támogatás.\n"
|
||||
STRING_PromptMsg, "Futtatáshoz nyomd meg a r<cr>, leállításhoz a s<cr> vagy kilépéshez a q<cr> gombot."
|
||||
END
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
LANGUAGE LANG_INDONESIAN, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Dukungan ReactOS Virtual DOS Machine.\n"
|
||||
STRING_PromptMsg, "Ketik r<cr> untuk menjalankan, s<cr> untuk mematikan atau q<cr> untuk keluar sekarang."
|
||||
END
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
* PROGRAMMERS: Copyright (C) 2005 Magnus Olsen
|
||||
* Copyright (C) 2007 Daniele Forsi (dforsi at gmail.com) Italian Translation
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Supporto di ReactOS per la macchina virtuale DOS.\n"
|
||||
STRING_PromptMsg, "Digitare r<invio> per avviare, s<invio> per arrestare o q<invio> per abbandonare ora."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS Virtual DOS Machine support.\n"
|
||||
STRING_PromptMsg, "起動するには r<cr> を、シャットダウンするには s<cr> を、今すぐ終了させるには q<cr> を入力してください。"
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_NORWEGIAN, SUBLANG_NEUTRAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS Vituell DOS Maskin støtte.\n"
|
||||
STRING_PromptMsg, "Skriv r<cr> for å kjøre, s<cr> å avslutte eller q<cr> for å slutte nå."
|
||||
END
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* translated by xrogers
|
||||
* xxrogers@users.sourceforge.net
|
||||
* https://sourceforge.net/projects/reactospl
|
||||
* UTF-8 conversion by Caemyr (May, 2011)
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Wirtualna maszyna DOS dla ReactOS.\n"
|
||||
STRING_PromptMsg, "Wciśnij r<cr> aby uruchomić, s<cr> aby wyłączyć lub q<cr>, aby zakończyć."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS subsistema para suporte DOS de 16 bits.\n"
|
||||
STRING_PromptMsg, "Digite r<cr> para executar, s<cr> para desligar ou q<cr> para sair."
|
||||
END
|
|
@ -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 DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Asistență pentru mașina virtuală DOS.\n"
|
||||
STRING_PromptMsg, "Tastați r<cr> pentru a executa, s<cr> pentru a închide sau q<cr> pentru a ieși imediat."
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Moved all hardcoded strings to En.rc.
|
||||
* By Magnus Olsen 2005
|
||||
*/
|
||||
|
||||
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Виртуальная машина поддержки DOS для ReactOS.\n"
|
||||
STRING_PromptMsg, "Введите r<cr> для запуска, s<cr> для выключения или q<cr> выхода."
|
||||
END
|
|
@ -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 DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Podpora virtuálneho DOSového stroja v systéme ReactOS.\n"
|
||||
STRING_PromptMsg, "Napíšte r<cr> pre spustenie, s<cr> pre vypnutie alebo q<cr> pre okamžité skonèenie."
|
||||
END
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
LANGUAGE LANG_THAI, SUBLANG_DEFAULT
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ÃͧÃѺ¡Ò÷ӧҹÃкº´ÍÊàÊÁ×͹¢Í§ ReactOS\n"
|
||||
STRING_PromptMsg, "Ẻ r<cr> à¾×èÍ·Ó§Ò¹, s<cr> à¾×èͻԴÃкºËÃ×Í q<cr> à¾×èÍÍÍ¡·Ñ¹·Õ"
|
||||
END
|
|
@ -1,16 +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 DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "Підтримка віртуальної машини DOS у ReactOS.\n"
|
||||
STRING_PromptMsg, "Введіть r<cr> для запуску, s<cr> для закриття або q<cr>, щоб вийти зараз."
|
||||
END
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS 虚拟 DOS 机支持。\n"
|
||||
STRING_PromptMsg, "输入 r<cr> 以便运行,s<cr> 以便关闭或者 q<cr> 以便立即退出。"
|
||||
END
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
|
||||
STRINGTABLE DISCARDABLE
|
||||
BEGIN
|
||||
|
||||
STRING_WelcomeMsg, "ReactOS 虛擬 DOS 機支援。\n"
|
||||
STRING_PromptMsg, "鍵入 r<cr> 以便運行, s<cr> 以便關閉或者 q<cr> 以便立即退出。"
|
||||
END
|
|
@ -1,375 +1,130 @@
|
|||
/*
|
||||
* 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 <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *****************************************************************/
|
||||
#include "ntvdm.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <wincon.h>
|
||||
#include <winuser.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* GLOBALS ******************************************************************/
|
||||
|
||||
|
||||
/* FUNCTIONS *****************************************************************/
|
||||
|
||||
void PrintString(char* fmt,...)
|
||||
BOOLEAN VdmRunning = TRUE;
|
||||
LPVOID BaseAddress = NULL;
|
||||
LPCWSTR ExceptionName[] =
|
||||
{
|
||||
char buffer[512];
|
||||
va_list ap;
|
||||
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"
|
||||
};
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf(buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
OutputDebugStringA(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
VOID DisplayMessage(LPCWSTR Format, ...)
|
||||
{
|
||||
BOOL Result;
|
||||
STARTUPINFOA StartupInfo;
|
||||
WCHAR Buffer[256];
|
||||
va_list Parameters;
|
||||
|
||||
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;
|
||||
va_start(Parameters, Format);
|
||||
_vsnwprintf(Buffer, 256, Format, Parameters);
|
||||
MessageBox(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
|
||||
va_end(Parameters);
|
||||
}
|
||||
|
||||
BOOL
|
||||
ShutdownVDM(PVDM_CONTROL_BLOCK vdm)
|
||||
BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL ReadConfigForVDM(PVDM_CONTROL_BLOCK vdm)
|
||||
switch (ControlType)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL
|
||||
LoadConfigDriversForVDM(PVDM_CONFIG vdmConfig)
|
||||
case CTRL_C_EVENT:
|
||||
case CTRL_BREAK_EVENT:
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
|
||||
return result;
|
||||
/* Perform interrupt 0x23 */
|
||||
EmulatorInterrupt(0x23);
|
||||
}
|
||||
|
||||
BOOL
|
||||
SetConfigOptionsForVDM(PVDM_AUTOEXEC vdmAutoexec)
|
||||
default:
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
|
||||
return result;
|
||||
/* Stop the VDM if the user logs out or closes the console */
|
||||
VdmRunning = FALSE;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CreateVDM(PVDM_CONTROL_BLOCK vdm)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GetSystemInfo(&inf);
|
||||
vdm->hHeap = HeapCreate(0, inf.dwAllocationGranularity, 0);
|
||||
if (vdm->hHeap == NULL) {
|
||||
DPRINT("VDM: failed to create heap.\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;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL
|
||||
DestroyVDM(PVDM_CONTROL_BLOCK vdm)
|
||||
INT wmain(INT argc, WCHAR *argv[])
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
INT i;
|
||||
BOOLEAN PrintUsage = TRUE;
|
||||
CHAR CommandLine[128];
|
||||
|
||||
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;
|
||||
}
|
||||
vdm->hHeap = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/* Set the handler routine */
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
||||
|
||||
int WINAPI
|
||||
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||
/* Parse the command line arguments */
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
VDM_CONTROL_BLOCK VdmCB;
|
||||
DWORD Result;
|
||||
ULONG i;
|
||||
BOOL vdmStarted = FALSE;
|
||||
if (argv[i][0] != L'-' && argv[i][0] != L'/') continue;
|
||||
|
||||
WCHAR WelcomeMsg[RC_STRING_MAX_SIZE];
|
||||
WCHAR PromptMsg[RC_STRING_MAX_SIZE];
|
||||
CHAR InputBuffer[255];
|
||||
switch (argv[i][1])
|
||||
{
|
||||
case L'f':
|
||||
case L'F':
|
||||
{
|
||||
if (argv[i+1] != NULL)
|
||||
{
|
||||
/* The DOS command line must be ASCII */
|
||||
WideCharToMultiByte(CP_ACP, 0, argv[i+1], -1, CommandLine, 128, NULL, NULL);
|
||||
|
||||
LoadStringW( GetModuleHandle(NULL), STRING_WelcomeMsg, WelcomeMsg,sizeof(WelcomeMsg) / sizeof(WelcomeMsg[0]));
|
||||
LoadStringW( GetModuleHandle(NULL), STRING_PromptMsg, PromptMsg ,sizeof(PromptMsg) / sizeof(PromptMsg[0]));
|
||||
|
||||
AllocConsole();
|
||||
SetConsoleTitleW(L"ntvdm");
|
||||
|
||||
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
WelcomeMsg, lstrlenW(WelcomeMsg), // wcslen(WelcomeMsg),
|
||||
&Result, NULL);
|
||||
|
||||
if (!CreateVDM(&VdmCB)) {
|
||||
DPRINT("VDM: failed to create VDM.\n");
|
||||
//SetLastError();
|
||||
return 2;
|
||||
/* This is the only mandatory parameter */
|
||||
PrintUsage = FALSE;
|
||||
}
|
||||
|
||||
ReadConfigForVDM(&VdmCB);
|
||||
|
||||
if (!LoadConfigDriversForVDM(&(VdmCB.vdmConfig))) {
|
||||
DPRINT("VDM: failed to load configuration drivers.\n");
|
||||
//SetLastError();
|
||||
return 2;
|
||||
}
|
||||
if (!SetConfigOptionsForVDM(&(VdmCB.vdmAutoexec))) {
|
||||
DPRINT("VDM: failed to set configuration options.\n");
|
||||
//SetLastError();
|
||||
return 3;
|
||||
}
|
||||
|
||||
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");
|
||||
default:
|
||||
{
|
||||
wprintf(L"Unknown option: %s", argv[i]);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShutdownVDM(&VdmCB)) {
|
||||
DPRINT("VDM: failed to cleanly shutdown VDM.\n");
|
||||
//SetLastError();
|
||||
return 5;
|
||||
}
|
||||
|
||||
if (!DestroyVDM(&VdmCB)) {
|
||||
DPRINT("VDM: failed to cleanly destroy VDM.\n");
|
||||
//SetLastError();
|
||||
return 6;
|
||||
}
|
||||
|
||||
ExitProcess(0);
|
||||
if (PrintUsage)
|
||||
{
|
||||
wprintf(L"ReactOS Virtual DOS Machine\n\n");
|
||||
wprintf(L"Usage: NTVDM /F <PROGRAM>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!EmulatorInitialize()) return 1;
|
||||
|
||||
/* Initialize the system BIOS */
|
||||
if (!BiosInitialize())
|
||||
{
|
||||
wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Initialize the VDM DOS kernel */
|
||||
if (!DosInitialize())
|
||||
{
|
||||
wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Start the process from the command line */
|
||||
if (!DosCreateProcess(CommandLine, 0))
|
||||
{
|
||||
DisplayMessage(L"Could not start program: %S", CommandLine);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Main loop */
|
||||
while (VdmRunning) EmulatorStep();
|
||||
|
||||
Cleanup:
|
||||
EmulatorCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
199
subsystems/ntvdm/ntvdm.h
Normal file
199
subsystems/ntvdm/ntvdm.h
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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 <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* DEFINES ********************************************************************/
|
||||
|
||||
#define TO_LINEAR(seg, off) (((seg) << 4) + (off))
|
||||
#define MAX_SEGMENT 0xFFFF
|
||||
#define MAX_OFFSET 0xFFFF
|
||||
#define MAX_ADDRESS TO_LINEAR(MAX_SEGMENT, MAX_OFFSET)
|
||||
#define ROM_AREA_START 0xC0000
|
||||
#define ROM_AREA_END 0xFFFFF
|
||||
#define BIOS_SEGMENT 0xF000
|
||||
#define VIDEO_BIOS_INTERRUPT 0x10
|
||||
#define SPECIAL_INT_NUM 0xFF
|
||||
#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)))
|
||||
|
||||
/* DOS constants */
|
||||
#define DOS_VERSION 0x0600
|
||||
#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 0x8FFFF
|
||||
#define SYSTEM_PSP 0x08
|
||||
#define SYSTEM_ENV_BLOCK 0x800
|
||||
|
||||
/* System console constants */
|
||||
#define CONSOLE_FONT_HEIGHT 8
|
||||
#define CONSOLE_VIDEO_MEM_START 0xB8000
|
||||
#define CONSOLE_VIDEO_MEM_END 0xBFFFF
|
||||
|
||||
#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)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EMULATOR_REG_AX,
|
||||
EMULATOR_REG_CX,
|
||||
EMULATOR_REG_DX,
|
||||
EMULATOR_REG_BX,
|
||||
EMULATOR_REG_SI,
|
||||
EMULATOR_REG_DI,
|
||||
EMULATOR_REG_SP,
|
||||
EMULATOR_REG_BP,
|
||||
EMULATOR_REG_CS,
|
||||
EMULATOR_REG_SS,
|
||||
EMULATOR_REG_DS,
|
||||
EMULATOR_REG_ES,
|
||||
EMULATOR_REG_FS,
|
||||
EMULATOR_REG_GS
|
||||
} EMULATOR_REGISTER;
|
||||
|
||||
#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 MemSize;
|
||||
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[127];
|
||||
} DOS_PSP, *PDOS_PSP;
|
||||
|
||||
typedef struct _DOS_SFT_ENTRY
|
||||
{
|
||||
WORD ReferenceCount;
|
||||
WORD Mode;
|
||||
BYTE Attribute;
|
||||
WORD DeviceInfo;
|
||||
DWORD DriveParamBlock;
|
||||
WORD FirstCluster;
|
||||
WORD FileTime;
|
||||
WORD FileDate;
|
||||
DWORD FileSize;
|
||||
DWORD CurrentOffset;
|
||||
WORD LastClusterAccessed;
|
||||
DWORD DirEntSector;
|
||||
BYTE DirEntryIndex;
|
||||
CHAR FileName[11];
|
||||
BYTE Reserved0[6];
|
||||
WORD OwnerPsp;
|
||||
BYTE Reserved1[8];
|
||||
} DOS_SFT_ENTRY, *PDOS_SFT_ENTRY;
|
||||
|
||||
typedef struct _DOS_SFT
|
||||
{
|
||||
DWORD NextTablePtr;
|
||||
WORD FileCount;
|
||||
DOS_SFT_ENTRY Entry[ANYSIZE_ARRAY];
|
||||
} DOS_SFT, *PDOS_SFT;
|
||||
|
||||
typedef struct _DOS_INPUT_BUFFER
|
||||
{
|
||||
BYTE MaxLength, Length;
|
||||
CHAR Buffer[ANYSIZE_ARRAY];
|
||||
} DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
extern LPVOID BaseAddress;
|
||||
extern BOOLEAN VdmRunning;
|
||||
extern LPCWSTR ExceptionName[];
|
||||
|
||||
VOID DisplayMessage(LPCWSTR Format, ...);
|
||||
BOOLEAN BiosInitialize();
|
||||
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
|
||||
VOID BiosPrintCharacter(CHAR Character, BYTE Attribute);
|
||||
BOOLEAN DosInitialize();
|
||||
WORD DosAllocateMemory(WORD Size);
|
||||
BOOLEAN DosFreeMemory(WORD Segment);
|
||||
WORD DosResizeMemory(WORD Segment, WORD NewSize);
|
||||
BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock);
|
||||
VOID DosInt20h(WORD CodeSegment);
|
||||
VOID DosInt21h(WORD CodeSegment);
|
||||
VOID DosBreakInterrupt();
|
||||
VOID BiosVideoService();
|
||||
VOID EmulatorSetStack(WORD Segment, WORD Offset);
|
||||
VOID EmulatorExecute(WORD Segment, WORD Offset);
|
||||
VOID EmulatorInterrupt(BYTE Number);
|
||||
ULONG EmulatorGetRegister(ULONG Register);
|
||||
VOID EmulatorSetRegister(ULONG Register, ULONG Value);
|
||||
VOID EmulatorSetFlag(ULONG Flag);
|
||||
VOID EmulatorClearFlag(ULONG Flag);
|
||||
BOOLEAN EmulatorGetFlag(ULONG Flag);
|
||||
BOOLEAN EmulatorInitialize();
|
||||
VOID EmulatorStep();
|
||||
VOID EmulatorCleanup();
|
||||
|
||||
/* EOF */
|
|
@ -1,8 +1,12 @@
|
|||
#include <windef.h>
|
||||
#include <winuser.h>
|
||||
#include "resource.h"
|
||||
|
||||
#include <windows.h>
|
||||
#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Virtual DOS Machine\0"
|
||||
#define REACTOS_STR_INTERNAL_NAME "ntvdm\0"
|
||||
#define REACTOS_STR_ORIGINAL_FILENAME "ntvdm.exe\0"
|
||||
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 <reactos/version.rc>
|
||||
|
||||
#include "rsrc.rc"
|
||||
|
|
|
@ -1,6 +1,2 @@
|
|||
#define RC_STRING_MAX_SIZE 2048
|
||||
#define STRING_WelcomeMsg 100
|
||||
#define STRING_PromptMsg 101
|
||||
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -1,27 +1,4 @@
|
|||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
|
||||
/* Language-specific resources */
|
||||
#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/th-TH.rc"
|
||||
#include "lang/pt-BR.rc"
|
||||
#include "lang/sk-SK.rc"
|
||||
#include "lang/zh-CN.rc"
|
||||
#include "lang/zh-TW.rc"
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
|
||||
// UTF-8
|
||||
#pragma code_page(65001)
|
||||
#include "lang/pl-PL.rc"
|
||||
#include "lang/ro-RO.rc"
|
||||
#include "lang/ru-RU.rc"
|
||||
#include "lang/uk-UA.rc"
|
||||
|
|
Loading…
Reference in a new issue