- Add a basic boot sequence functionality (read from CMOS); will be improved in the future.
- Print a "FATAL BOOT FAILURE" error message when INT 18h is called.
- Fail startup if we cannot mount the available hard disk images.
- Improve some diagnostic error messages.

svn path=/trunk/; revision=69421
This commit is contained in:
Hermès Bélusca-Maïto 2015-10-01 00:09:24 +00:00
parent b1b7020854
commit 59136514da
8 changed files with 216 additions and 154 deletions

View file

@ -161,6 +161,31 @@ static const BYTE PostCode[] =
/* PRIVATE FUNCTIONS **********************************************************/ /* PRIVATE FUNCTIONS **********************************************************/
static VOID BiosCharPrint(CHAR Character)
{
/* Save AX and BX */
USHORT AX = getAX();
USHORT BX = getBX();
/*
* Set the parameters:
* AL contains the character to print,
* BL contains the character attribute,
* BH contains the video page to use.
*/
setAL(Character);
setBL(DEFAULT_ATTRIBUTE);
setBH(Bda->VideoPage);
/* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
setAH(0x0E);
Int32Call(&BiosContext, BIOS_VIDEO_INTERRUPT);
/* Restore AX and BX */
setBX(BX);
setAX(AX);
}
static VOID WINAPI BiosException(LPWORD Stack) static VOID WINAPI BiosException(LPWORD Stack)
{ {
/* Get the exception number and call the emulator API */ /* Get the exception number and call the emulator API */
@ -505,6 +530,8 @@ static VOID WINAPI BiosMiscService(LPWORD Stack)
static VOID WINAPI BiosRomBasic(LPWORD Stack) static VOID WINAPI BiosRomBasic(LPWORD Stack)
{ {
PrintMessageAnsi(BiosCharPrint, "FATAL: INT18: BOOT FAILURE.");
/* ROM Basic is unsupported, display a message to the user */ /* ROM Basic is unsupported, display a message to the user */
DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing."); DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing.");
@ -518,79 +545,110 @@ extern VOID WINAPI BiosDiskService(LPWORD Stack);
static VOID WINAPI BiosBootstrapLoader(LPWORD Stack) static VOID WINAPI BiosBootstrapLoader(LPWORD Stack)
{ {
USHORT BootOrder;
USHORT AX, BX, CX, DX, ES;
AX = getAX();
BX = getBX();
CX = getCX();
DX = getDX();
ES = getES();
/* /*
* In real BIOSes one loads the bootsector read from a diskette * Read the boot sequence order from the CMOS, old behaviour AMI-style.
* or from a disk, copy it to 0000:7C00 and then boot it. *
* Since we are 32-bit VM and we hardcode our DOS at the moment, * For more information, see:
* just call the DOS 32-bit initialization code. * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/orgs.asm
* http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/boot.c
* http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/cmos.cc
* https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html
* http://www.bioscentral.com/misc/cmosmap.htm
*/
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SYSOP);
BootOrder = (IOReadB(CMOS_DATA_PORT) & 0x20) >> 5;
/*
* BootOrder =
* 0: Hard Disk, then Floppy Disk
* 1: Floppy Disk, then Hard Disk
* In all cases, if booting from those devices failed,
* ROM DOS-32 is started. If it fails, INT 18h is called.
*/ */
DPRINT("BiosBootstrapLoader -->\n"); DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder);
/*
* Format of the BootOrder command:
* 2 bytes. Each half-byte contains the ID of the drive to boot.
* Currently defined:
* 0x0: 1st Floppy disk
* 0x1: 1st Hard disk
* Other, or 0xF: Stop boot sequence.
*/
BootOrder = 0xFF00 | ((1 << (4 * BootOrder)) & 0xFF);
Retry:
switch (BootOrder & 0x0F)
{ {
USHORT i; /* Boot from 1st floppy drive */
case 0:
USHORT AX, BX, CX, DX, ES;
AX = getAX();
BX = getBX();
CX = getCX();
DX = getDX();
ES = getES();
// i = 0;
i = 1;
do
{ {
if (i == 0) setAH(0x02); // Read sectors
{ setAL(0x01); // Number of sectors
/* Boot from 1st floppy drive */ setDH(0x00); // Head 0
setCH(0x00); // Cylinder 0
setCL(0x01); // Sector 1
setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
setES(0x0000); // Write data in 0000:7C00
setBX(0x7C00);
BiosDiskService(Stack);
if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
setAH(0x02); // Read sectors break;
setAL(0x01); // Number of sectors }
setDH(0x00); // First head
setCH(0x00); // First cylinder
setCL(0x01); // First sector
setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
setES(0x0000); // Place data in 0000:7C00
setBX(0x7C00);
BiosDiskService(Stack);
if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
}
else if (i == 1)
{
/* Boot from 1st HDD drive */
setAH(0x02); // Read sectors /* Boot from 1st HDD drive */
setAL(0x01); // Number of sectors case 1:
setDH(0x00); // First head {
setCH(0x00); // First cylinder setAH(0x02); // Read sectors
setCL(0x01); // First sector setAL(0x01); // Number of sectors
setDL(0x80); // First HDD drive (used by loader code, so should not be cleared) setDH(0x00); // Head 0
setES(0x0000); // Place data in 0000:7C00 setCH(0x00); // Cylinder 0
setBX(0x7C00); setCL(0x01); // Sector 1
BiosDiskService(Stack); setDL(0x80); // First HDD drive (used by loader code, so should not be cleared)
if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit; setES(0x0000); // Write data in 0000:7C00
DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH()); setBX(0x7C00);
} BiosDiskService(Stack);
// } while (i++ < 1); if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
} while (i-- > 0); DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
/* Clear everything, we are going to load DOS32 */ break;
setAX(AX); }
setBX(BX);
setCX(CX); default:
setDX(DX); goto StartDos;
setES(ES);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
} }
/* Go to next drive and invalidate the last half-byte. */
BootOrder = (BootOrder >> 4) | 0xF000;
goto Retry;
StartDos:
/* Clear everything, we are going to load DOS32 */
setAX(AX);
setBX(BX);
setCX(CX);
setDX(DX);
setES(ES);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
/* Load our DOS */ /* Load our DOS */
DosBootsectorInitialize(); DosBootsectorInitialize();
Quit: Quit:
/* /*
* Position CPU to 0000:7C00 to boot the OS. * Jump to 0000:7C00 to boot the OS.
* *
* Since we are called via the INT32 mechanism, we need to correctly set * Since we are called via the INT32 mechanism, we need to correctly set
* CS:IP, not by changing the current one (otherwise the interrupt could * CS:IP, not by changing the current one (otherwise the interrupt could

View file

@ -82,83 +82,6 @@ VOID DosCharPrint(CHAR Character)
DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
} }
/*
* This function, derived from ntvdm.c!DisplayMessage, is used by the BIOS and
* the DOS to display messages to an output device. A printer function is given
* for printing the characters.
*/
static VOID
DisplayMessageAnsiV(IN CHAR_PRINT CharPrint,
IN LPCSTR Format,
IN va_list args)
{
static CHAR CurChar = 0;
LPSTR str;
#ifndef WIN2K_COMPLIANT
CHAR StaticBuffer[256];
LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
#else
CHAR Buffer[2048]; // Large enough. If not, increase it by hand.
#endif
size_t MsgLen;
#ifndef WIN2K_COMPLIANT
/*
* Retrieve the message length and if it is too long, allocate
* an auxiliary buffer; otherwise use the static buffer.
* The string is built to be NULL-terminated.
*/
MsgLen = _vscprintf(Format, args);
if (MsgLen >= ARRAYSIZE(StaticBuffer))
{
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR));
if (Buffer == NULL)
{
/* Allocation failed, use the static buffer and display a suitable error message */
Buffer = StaticBuffer;
Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed.";
MsgLen = strlen(Format);
}
}
#else
MsgLen = ARRAYSIZE(Buffer) - 1;
#endif
RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR));
_vsnprintf(Buffer, MsgLen, Format, args);
/* Display the message */
DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
MsgLen = strlen(Buffer);
str = Buffer;
while (MsgLen--)
{
if (*str == '\n' && CurChar != '\r')
CharPrint('\r');
CurChar = *str++;
CharPrint(CurChar);
}
#ifndef WIN2K_COMPLIANT
/* Free the buffer if needed */
if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
#endif
}
VOID
DemDisplayMessage(IN CHAR_PRINT CharPrint,
IN LPCSTR Format, ...)
{
va_list Parameters;
va_start(Parameters, Format);
DisplayMessageAnsiV(CharPrint, Format, Parameters);
va_end(Parameters);
}
static VOID DemLoadNTDOSKernel(VOID) static VOID DemLoadNTDOSKernel(VOID)
{ {

View file

@ -29,18 +29,13 @@
/* FUNCTIONS ******************************************************************/ /* FUNCTIONS ******************************************************************/
typedef VOID (*CHAR_PRINT)(IN CHAR Character);
VOID BiosCharPrint(CHAR Character); VOID BiosCharPrint(CHAR Character);
VOID DosCharPrint(CHAR Character);
VOID DemDisplayMessage(IN CHAR_PRINT CharPrint,
IN LPCSTR Format, ...);
#define BiosDisplayMessage(Format, ...) \ #define BiosDisplayMessage(Format, ...) \
DemDisplayMessage(BiosCharPrint, (Format), ##__VA_ARGS__) PrintMessageAnsi(BiosCharPrint, (Format), ##__VA_ARGS__)
VOID DosCharPrint(CHAR Character);
#define DosDisplayMessage(Format, ...) \ #define DosDisplayMessage(Format, ...) \
DemDisplayMessage(DosCharPrint, (Format), ##__VA_ARGS__) PrintMessageAnsi(DosCharPrint, (Format), ##__VA_ARGS__)
BOOLEAN DosShutdown(BOOLEAN Immediate); BOOLEAN DosShutdown(BOOLEAN Immediate);

View file

@ -564,14 +564,22 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
} }
} }
/* Mount the available hard disks */ /*
* Mount the available hard disks. Contrary to floppies, failing
* mounting a hard disk is considered as an unrecoverable error.
*/
for (i = 0; i < ARRAYSIZE(GlobalSettings.HardDisks); ++i) for (i = 0; i < ARRAYSIZE(GlobalSettings.HardDisks); ++i)
{ {
if (GlobalSettings.HardDisks[i].Length != 0 && if (GlobalSettings.HardDisks[i].Length != 0 &&
GlobalSettings.HardDisks[i].Buffer && GlobalSettings.HardDisks[i].Buffer &&
GlobalSettings.HardDisks[i].Buffer != '\0') GlobalSettings.HardDisks[i].Buffer != '\0')
{ {
MountDisk(HARD_DISK, i, GlobalSettings.HardDisks[i].Buffer, FALSE); if (!MountDisk(HARD_DISK, i, GlobalSettings.HardDisks[i].Buffer, FALSE))
{
wprintf(L"FATAL: Failed to mount hard disk file '%Z'.\n", &GlobalSettings);
EmulatorCleanup();
return FALSE;
}
} }
} }

View file

@ -70,6 +70,7 @@ typedef enum _CMOS_REGISTERS
CMOS_REG_BASE_MEMORY_HIGH = 0x16, CMOS_REG_BASE_MEMORY_HIGH = 0x16,
CMOS_REG_EXT_MEMORY_LOW = 0x17, CMOS_REG_EXT_MEMORY_LOW = 0x17,
CMOS_REG_EXT_MEMORY_HIGH = 0x18, CMOS_REG_EXT_MEMORY_HIGH = 0x18,
CMOS_REG_SYSOP = 0x2D,
CMOS_REG_ACTUAL_EXT_MEMORY_LOW = 0x30, CMOS_REG_ACTUAL_EXT_MEMORY_LOW = 0x30,
CMOS_REG_ACTUAL_EXT_MEMORY_HIGH = 0x31, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH = 0x31,
CMOS_REG_CENTURY = 0x32, CMOS_REG_CENTURY = 0x32,

View file

@ -199,7 +199,7 @@ MountFDI(IN PDISK_IMAGE DiskImage,
if (FileSize == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS) if (FileSize == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS)
{ {
/* We failed, bail out */ /* We failed, bail out */
DisplayMessage(L"Error when retrieving file size, or size too large (%d).", FileSize); DisplayMessage(L"MountFDI: Error when retrieving file size, or size too large (%d).", FileSize);
return FALSE; return FALSE;
} }
@ -547,7 +547,11 @@ MountDisk(IN DISK_TYPE DiskType,
FileName, hFile != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError()); FileName, hFile != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError());
/* If we failed, bail out */ /* If we failed, bail out */
if (hFile == INVALID_HANDLE_VALUE) return FALSE; if (hFile == INVALID_HANDLE_VALUE)
{
DisplayMessage(L"MountDisk: Error when opening disk file '%S' (Error: %u).", FileName, GetLastError());
return FALSE;
}
/* OK, we have a handle to the file */ /* OK, we have a handle to the file */
@ -563,7 +567,7 @@ MountDisk(IN DISK_TYPE DiskType,
GetLastError() == ERROR_INVALID_FUNCTION) GetLastError() == ERROR_INVALID_FUNCTION)
{ {
/* Objects other than real files are not supported */ /* Objects other than real files are not supported */
DisplayMessage(L"'%S' is not a valid disk file.", FileName); DisplayMessage(L"MountDisk: '%S' is not a valid disk file.", FileName);
goto Quit; goto Quit;
} }
SetLastError(0); SetLastError(0);
@ -571,7 +575,7 @@ MountDisk(IN DISK_TYPE DiskType,
GetLastError() == ERROR_INVALID_FUNCTION) GetLastError() == ERROR_INVALID_FUNCTION)
{ {
/* Objects other than real files are not supported */ /* Objects other than real files are not supported */
DisplayMessage(L"'%S' is not a valid disk file.", FileName); DisplayMessage(L"MountDisk: '%S' is not a valid disk file.", FileName);
goto Quit; goto Quit;
} }

View file

@ -562,9 +562,9 @@ DisplayMessage(IN LPCWSTR Format, ...)
WCHAR Buffer[2048]; // Large enough. If not, increase it by hand. WCHAR Buffer[2048]; // Large enough. If not, increase it by hand.
#endif #endif
size_t MsgLen; size_t MsgLen;
va_list Parameters; va_list args;
va_start(Parameters, Format); va_start(args, Format);
#ifndef WIN2K_COMPLIANT #ifndef WIN2K_COMPLIANT
/* /*
@ -572,7 +572,7 @@ DisplayMessage(IN LPCWSTR Format, ...)
* an auxiliary buffer; otherwise use the static buffer. * an auxiliary buffer; otherwise use the static buffer.
* The string is built to be NULL-terminated. * The string is built to be NULL-terminated.
*/ */
MsgLen = _vscwprintf(Format, Parameters); MsgLen = _vscwprintf(Format, args);
if (MsgLen >= ARRAYSIZE(StaticBuffer)) if (MsgLen >= ARRAYSIZE(StaticBuffer))
{ {
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR)); Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
@ -589,9 +589,9 @@ DisplayMessage(IN LPCWSTR Format, ...)
#endif #endif
RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR)); RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR));
_vsnwprintf(Buffer, MsgLen, Format, Parameters); _vsnwprintf(Buffer, MsgLen, Format, args);
va_end(Parameters); va_end(args);
/* Display the message */ /* Display the message */
DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer); DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
@ -603,6 +603,76 @@ DisplayMessage(IN LPCWSTR Format, ...)
#endif #endif
} }
/*
* This function, derived from DisplayMessage, is used by the BIOS and
* the DOS to display messages to an output device. A printer function
* is given for printing the characters.
*/
VOID
PrintMessageAnsi(IN CHAR_PRINT CharPrint,
IN LPCSTR Format, ...)
{
static CHAR CurChar = 0;
LPSTR str;
#ifndef WIN2K_COMPLIANT
CHAR StaticBuffer[256];
LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
#else
CHAR Buffer[2048]; // Large enough. If not, increase it by hand.
#endif
size_t MsgLen;
va_list args;
va_start(args, Format);
#ifndef WIN2K_COMPLIANT
/*
* Retrieve the message length and if it is too long, allocate
* an auxiliary buffer; otherwise use the static buffer.
* The string is built to be NULL-terminated.
*/
MsgLen = _vscprintf(Format, args);
if (MsgLen >= ARRAYSIZE(StaticBuffer))
{
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR));
if (Buffer == NULL)
{
/* Allocation failed, use the static buffer and display a suitable error message */
Buffer = StaticBuffer;
Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed.";
MsgLen = strlen(Format);
}
}
#else
MsgLen = ARRAYSIZE(Buffer) - 1;
#endif
RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR));
_vsnprintf(Buffer, MsgLen, Format, args);
va_end(args);
/* Display the message */
// DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
MsgLen = strlen(Buffer);
str = Buffer;
while (MsgLen--)
{
if (*str == '\n' && CurChar != '\r')
CharPrint('\r');
CurChar = *str++;
CharPrint(CurChar);
}
#ifndef WIN2K_COMPLIANT
/* Free the buffer if needed */
if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
#endif
}
static VOID static VOID
ConsoleCleanup(VOID); ConsoleCleanup(VOID);
@ -877,7 +947,7 @@ wmain(INT argc, WCHAR *argv[])
} }
#endif #endif
/* Load global VDM settings */ /* Load the global VDM settings */
LoadGlobalSettings(&GlobalSettings); LoadGlobalSettings(&GlobalSettings);
DPRINT1("\n\n\nNTVDM - Starting...\n\n\n"); DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");

View file

@ -94,7 +94,10 @@ extern HWND hConsoleWnd;
/* /*
* Interface functions * Interface functions
*/ */
typedef VOID (*CHAR_PRINT)(IN CHAR Character);
VOID DisplayMessage(IN LPCWSTR Format, ...); VOID DisplayMessage(IN LPCWSTR Format, ...);
VOID PrintMessageAnsi(IN CHAR_PRINT CharPrint,
IN LPCSTR Format, ...);
/*static*/ VOID /*static*/ VOID
CreateVdmMenu(HANDLE ConOutHandle); CreateVdmMenu(HANDLE ConOutHandle);