mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
2ea03b5b22
Fixing typos in code comments enhances code readability and comprehension for developers and collaborators.
1298 lines
39 KiB
C
1298 lines
39 KiB
C
/*
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
* FILE: subsystems/mvdm/ntvdm/bios/bios32/bios32.c
|
|
* PURPOSE: VDM 32-bit BIOS
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "ntvdm.h"
|
|
|
|
/* BIOS Version number and Copyright */
|
|
#include <reactos/buildno.h>
|
|
#include <reactos/version.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "emulator.h"
|
|
#include "cpu/cpu.h" // for EMULATOR_FLAG_CF
|
|
#include "cpu/bop.h"
|
|
#include "int32.h"
|
|
#include <isvbop.h>
|
|
|
|
#include <bios/bios.h>
|
|
#include <bios/rom.h>
|
|
#include "bios32.h"
|
|
#include "bios32p.h"
|
|
#include "dskbios32.h"
|
|
#include "kbdbios32.h"
|
|
#include "vidbios32.h"
|
|
#include "moubios32.h"
|
|
|
|
#include "memory.h"
|
|
#include "io.h"
|
|
#include "hardware/cmos.h"
|
|
#include "hardware/pic.h"
|
|
#include "hardware/pit.h"
|
|
#include "hardware/ps2.h"
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
CALLBACK16 BiosContext;
|
|
|
|
/*
|
|
|
|
Bochs BIOS, see rombios.h
|
|
=========================
|
|
|
|
// model byte 0xFC = AT
|
|
#define SYS_MODEL_ID 0xFC
|
|
#define SYS_SUBMODEL_ID 0x00
|
|
#define BIOS_REVISION 1
|
|
#define BIOS_CONFIG_TABLE 0xe6f5
|
|
|
|
#ifndef BIOS_BUILD_DATE
|
|
# define BIOS_BUILD_DATE "06/23/99"
|
|
#endif
|
|
|
|
// 1K of base memory used for Extended Bios Data Area (EBDA)
|
|
// EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
|
|
#define EBDA_SEG 0x9FC0
|
|
#define EBDA_SIZE 1 // In KiB
|
|
#define BASE_MEM_IN_K (640 - EBDA_SIZE)
|
|
|
|
|
|
See rombios.c
|
|
=============
|
|
|
|
ROM BIOS compatibility entry points:
|
|
===================================
|
|
$e05b ; POST Entry Point
|
|
$e2c3 ; NMI Handler Entry Point
|
|
$e3fe ; INT 13h Fixed Disk Services Entry Point
|
|
$e401 ; Fixed Disk Parameter Table
|
|
$e6f2 ; INT 19h Boot Load Service Entry Point
|
|
$e6f5 ; Configuration Data Table
|
|
$e729 ; Baud Rate Generator Table
|
|
$e739 ; INT 14h Serial Communications Service Entry Point
|
|
$e82e ; INT 16h Keyboard Service Entry Point
|
|
$e987 ; INT 09h Keyboard Service Entry Point
|
|
$ec59 ; INT 13h Diskette Service Entry Point
|
|
$ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
|
|
$efc7 ; Diskette Controller Parameter Table
|
|
$efd2 ; INT 17h Printer Service Entry Point
|
|
$f045 ; INT 10 Functions 0-Fh Entry Point
|
|
$f065 ; INT 10h Video Support Service Entry Point
|
|
$f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
|
|
$f841 ; INT 12h Memory Size Service Entry Point
|
|
$f84d ; INT 11h Equipment List Service Entry Point
|
|
$f859 ; INT 15h System Services Entry Point
|
|
$fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
|
|
$fe6e ; INT 1Ah Time-of-day Service Entry Point
|
|
$fea5 ; INT 08h System Timer ISR Entry Point
|
|
$fef3 ; Initial Interrupt Vector Offsets Loaded by POST
|
|
$ff53 ; IRET Instruction for Dummy Interrupt Handler
|
|
$ff54 ; INT 05h Print Screen Service Entry Point
|
|
$fff0 ; Power-up Entry Point
|
|
$fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
|
|
$fffe ; System Model ID
|
|
|
|
*/
|
|
|
|
/*
|
|
* See Ralf Brown: http://www.ctyme.com/intr/rb-1594.htm#Table515
|
|
* for more information.
|
|
*/
|
|
#define BIOS_MODEL 0xFC // PC-AT
|
|
#define BIOS_SUBMODEL 0x01 // AT models 319,339 8 MHz, Enh Keyb, 3.5"
|
|
#define BIOS_REVISION 0x00
|
|
// FIXME: Find a nice PS/2 486 + 487 BIOS combination!
|
|
|
|
static const BIOS_CONFIG_TABLE BiosConfigTable =
|
|
{
|
|
sizeof(BIOS_CONFIG_TABLE) - sizeof(((BIOS_CONFIG_TABLE*)0)->Length), // Length: Number of bytes following
|
|
|
|
BIOS_MODEL, // BIOS Model
|
|
BIOS_SUBMODEL, // BIOS Sub-Model
|
|
BIOS_REVISION, // BIOS Revision
|
|
|
|
// Feature bytes
|
|
{
|
|
0x78, // At the moment we don't have any Extended BIOS Area; see http://www.ctyme.com/intr/rb-1594.htm#Table510
|
|
0x00, // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
|
|
0x10, // Bit 4: POST supports ROM-to-RAM enable/disable
|
|
0x00,
|
|
0x00
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* WARNING! For compatibility purposes the string "IBM" should be at F000:E00E .
|
|
* Some programs otherwise look for "COPR. IBM" at F000:E008 .
|
|
*/
|
|
static const CHAR BiosCopyright[] = "0000000 NTVDM IBM COMPATIBLE 486 BIOS COPYRIGHT (C) ReactOS Team 1996-"COPYRIGHT_YEAR;
|
|
static const CHAR BiosVersion[] = "ReactOS NTVDM 32-bit BIOS Version "KERNEL_VERSION_STR"\0"
|
|
"BIOS32 Version "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")";
|
|
static const CHAR BiosDate[] = "06/17/13";
|
|
|
|
C_ASSERT(sizeof(BiosCopyright)-1 <= 0x5B); // Ensures that we won't overflow on the POST Code starting at F000:E05B
|
|
C_ASSERT(sizeof(BiosDate)-1 == 0x08);
|
|
|
|
/* 16-bit bootstrap code at F000:FFF0 */
|
|
static const BYTE Bootstrap[] =
|
|
{
|
|
0xEA, // jmp far ptr
|
|
0x5B, 0xE0, 0x00, 0xF0, // F000:E05B
|
|
};
|
|
|
|
/*
|
|
* POST code at F000:E05B. All the POST is done in 32 bit
|
|
* and only at the end it calls the bootstrap interrupt.
|
|
*/
|
|
static const BYTE PostCode[] =
|
|
{
|
|
LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_RESET, // Call BIOS POST
|
|
0xCD, BIOS_BOOTSTRAP_LOADER, // INT 0x19
|
|
0xCD, BIOS_ROM_BASIC, // INT 0x18
|
|
LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
|
|
};
|
|
|
|
|
|
/* 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)
|
|
{
|
|
/* Get the exception number and call the emulator API */
|
|
BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]);
|
|
EmulatorException(ExceptionNumber, Stack);
|
|
}
|
|
|
|
VOID WINAPI BiosEquipmentService(LPWORD Stack)
|
|
{
|
|
/* Return the equipment list */
|
|
setAX(Bda->EquipmentList);
|
|
}
|
|
|
|
VOID WINAPI BiosGetMemorySize(LPWORD Stack)
|
|
{
|
|
/* Return the conventional memory size in kB, typically 640 kB */
|
|
setAX(Bda->MemorySize);
|
|
}
|
|
|
|
static VOID WINAPI BiosMiscService(LPWORD Stack)
|
|
{
|
|
switch (getAH())
|
|
{
|
|
/* OS Hooks for Multitasking */
|
|
case 0x80: // Device Open
|
|
case 0x81: // Device Close
|
|
case 0x82: // Program Termination
|
|
case 0x90: // Device Busy
|
|
case 0x91: // Device POST
|
|
{
|
|
/* Return success by default */
|
|
setAH(0x00);
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Wait on External Event */
|
|
case 0x41:
|
|
{
|
|
BYTE Value;
|
|
BOOLEAN Return;
|
|
static DWORD StartingCount;
|
|
|
|
/* Check if this is the first time this BOP occurred */
|
|
if (!getCF())
|
|
{
|
|
/* Set the starting count */
|
|
StartingCount = Bda->TickCounter;
|
|
}
|
|
|
|
if (getBL() != 0 && (Bda->TickCounter - StartingCount) >= getBL())
|
|
{
|
|
/* Timeout expired */
|
|
setCF(0);
|
|
break;
|
|
}
|
|
|
|
if (getAL() & (1 << 4))
|
|
{
|
|
/* Read from the I/O port */
|
|
Value = IOReadB(getDX());
|
|
}
|
|
else
|
|
{
|
|
/* Read from the memory */
|
|
Value = *(LPBYTE)SEG_OFF_TO_PTR(getES(), getDI());
|
|
}
|
|
|
|
switch (getAL() & 7)
|
|
{
|
|
/* Any external event */
|
|
case 0:
|
|
{
|
|
/* Return if this is not the first time the BOP occurred */
|
|
Return = getCF();
|
|
break;
|
|
}
|
|
|
|
/* Compare and return if equal */
|
|
case 1:
|
|
{
|
|
Return = Value == getBH();
|
|
break;
|
|
}
|
|
|
|
/* Compare and return if not equal */
|
|
case 2:
|
|
{
|
|
Return = Value != getBH();
|
|
break;
|
|
}
|
|
|
|
/* Test and return if not zero */
|
|
case 3:
|
|
{
|
|
Return = (Value & getBH()) != 0;
|
|
break;
|
|
}
|
|
|
|
/* Test and return if zero */
|
|
case 4:
|
|
{
|
|
Return = (Value & getBH()) == 0;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("INT 15h, AH = 41h - Unknown condition type: %u\n", getAL() & 7);
|
|
Return = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Repeat the BOP if we shouldn't return */
|
|
setCF(!Return);
|
|
break;
|
|
}
|
|
|
|
/* Keyboard intercept */
|
|
case 0x4F:
|
|
{
|
|
/* CF should be set but let's just set it again just in case */
|
|
/* Do not modify AL (the hardware scan code), but set CF to continue processing */
|
|
// setCF(1);
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Wait */
|
|
case 0x86:
|
|
{
|
|
/*
|
|
* Interval in microseconds in CX:DX
|
|
* See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
|
|
* for more information.
|
|
*/
|
|
|
|
static ULONG CompletionTime = 0;
|
|
|
|
/* Check if we're already looping */
|
|
if (getCF())
|
|
{
|
|
if (GetTickCount() >= CompletionTime)
|
|
{
|
|
/* Stop looping */
|
|
setCF(0);
|
|
|
|
/* Clear the CF on the stack too */
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Set the CF on the stack */
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
|
|
/* Set the completion time and start looping */
|
|
CompletionTime = GetTickCount() + (MAKELONG(getDX(), getCX()) / 1000);
|
|
setCF(1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Copy Extended Memory */
|
|
case 0x87:
|
|
{
|
|
DWORD Count = (DWORD)getCX() * 2;
|
|
PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI());
|
|
DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24);
|
|
DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16);
|
|
DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24);
|
|
DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16);
|
|
|
|
/* Check for flags */
|
|
if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF;
|
|
if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF;
|
|
|
|
if ((Count > SourceLimit) || (Count > DestLimit))
|
|
{
|
|
setAX(0x80);
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Copy */
|
|
RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase),
|
|
(PVOID)((ULONG_PTR)BaseAddress + SourceBase),
|
|
Count);
|
|
|
|
setAX(ERROR_SUCCESS);
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Get Extended Memory Size */
|
|
case 0x88:
|
|
{
|
|
UCHAR Low, High;
|
|
|
|
/*
|
|
* Return the (usable) extended memory (after 1 MB)
|
|
* size in kB from CMOS.
|
|
*/
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW | CMOS_DISABLE_NMI);
|
|
Low = IOReadB(CMOS_DATA_PORT);
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH | CMOS_DISABLE_NMI);
|
|
High = IOReadB(CMOS_DATA_PORT);
|
|
setAX(MAKEWORD(Low, High));
|
|
|
|
/* Clear CF */
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Switch to Protected Mode */
|
|
case 0x89:
|
|
{
|
|
DPRINT1("BIOS INT 15h, AH=89h \"Switch to Protected Mode\" is UNIMPLEMENTED");
|
|
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
goto Default;
|
|
}
|
|
|
|
/* Get Configuration */
|
|
case 0xC0:
|
|
{
|
|
/* Return the BIOS ROM Configuration Table address in ES:BX */
|
|
// The BCT is found at F000:E6F5 for 100% compatible BIOSes.
|
|
setES(BIOS_SEGMENT);
|
|
setBX(0xE6F5);
|
|
|
|
/* Call successful; clear CF */
|
|
setAH(0x00);
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Return Extended-Bios Data-Area Segment Address (PS) */
|
|
case 0xC1:
|
|
{
|
|
/* We do not support EBDA yet */
|
|
UNIMPLEMENTED;
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
goto Default;
|
|
}
|
|
|
|
/* Pointing Device BIOS Interface (PS) */
|
|
case 0xC2:
|
|
{
|
|
// FIXME: Reenable this call when we understand why
|
|
// our included mouse driver doesn't correctly reenable
|
|
// mouse reporting!
|
|
// BiosMousePs2Interface(Stack);
|
|
// break;
|
|
goto Default;
|
|
}
|
|
|
|
/* Get CPU Type and Mask Revision */
|
|
case 0xC9:
|
|
{
|
|
/*
|
|
* We can see this function as a CPUID replacement.
|
|
* See Ralf Brown: http://www.ctyme.com/intr/rb-1613.htm
|
|
* for more information.
|
|
*/
|
|
|
|
/*
|
|
* Fast486 is a 486DX with FPU included,
|
|
* but old enough to not support CPUID.
|
|
*/
|
|
setCX(0x0400);
|
|
|
|
/* Call successful; clear CF */
|
|
setAH(0x00);
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
/* Get System Memory Map */
|
|
case 0xE8:
|
|
{
|
|
if (getAL() == 0x01)
|
|
{
|
|
/* The amount of memory between 1M and 16M, in kilobytes */
|
|
ULONG Above1M = (min(MAX_ADDRESS, 0x01000000) - 0x00100000) >> 10;
|
|
|
|
/* The amount of memory above 16M, in 64K blocks */
|
|
ULONG Above16M = (MAX_ADDRESS > 0x01000000) ? ((MAX_ADDRESS - 0x01000000) >> 16) : 0;
|
|
|
|
setAX(Above1M);
|
|
setBX(Above16M);
|
|
setCX(Above1M);
|
|
setDX(Above16M);
|
|
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
}
|
|
else if (getAL() == 0x20 && getEDX() == 'SMAP')
|
|
{
|
|
ULONG Offset = getEBX();
|
|
ULONG Length;
|
|
ULONG BytesWritten = 0;
|
|
BOOLEAN Hooked;
|
|
PBIOS_MEMORY_MAP Map = (PBIOS_MEMORY_MAP)SEG_OFF_TO_PTR(getES(), getDI());
|
|
|
|
/* Assume the buffer won't be large enough */
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
|
|
while (BytesWritten < getECX() && (ULONG_PTR)Map < (MAX_ADDRESS - sizeof(BIOS_MEMORY_MAP)))
|
|
{
|
|
/* Let's ask our memory controller */
|
|
if (!MemQueryMemoryZone(Offset, &Length, &Hooked))
|
|
{
|
|
/* No more memory blocks */
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
Map->BaseAddress = (ULONGLONG)Offset;
|
|
Map->Length = (ULONGLONG)Length;
|
|
Map->Type = Hooked ? BIOS_MEMORY_RESERVED : BIOS_MEMORY_AVAILABLE;
|
|
|
|
/* Go to the next record */
|
|
Map++;
|
|
Offset += Length;
|
|
BytesWritten += sizeof(BIOS_MEMORY_MAP);
|
|
}
|
|
|
|
setEAX('SMAP');
|
|
setEBX(Offset);
|
|
setECX(BytesWritten);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("BIOS Function INT 15h, AH = 0xE8 - unexpected AL = %02X, EDX = %08X\n",
|
|
getAL(), getEDX());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: Default:
|
|
{
|
|
DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
|
|
getAH());
|
|
|
|
/*
|
|
* The original signification of the error code 0x86 is that
|
|
* no PC Cassette is present. The CF is also set in this case.
|
|
* To keep backward compatibility, newer BIOSes use this value
|
|
* to indicate an unimplemented call in INT 15h.
|
|
*/
|
|
setAH(0x86);
|
|
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID WINAPI BiosRomBasic(LPWORD Stack)
|
|
{
|
|
PrintMessageAnsi(BiosCharPrint, "FATAL: INT18: BOOT FAILURE.");
|
|
|
|
/* ROM Basic is unsupported, display a message to the user */
|
|
DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing.");
|
|
|
|
/* Stop the VDM */
|
|
EmulatorTerminate();
|
|
}
|
|
|
|
|
|
extern VOID DosBootsectorInitialize(VOID);
|
|
extern VOID WINAPI BiosDiskService(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();
|
|
|
|
/*
|
|
* Read the boot sequence order from the CMOS, old behaviour AMI-style.
|
|
*
|
|
* For more information, see:
|
|
* 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 (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)
|
|
{
|
|
/* Boot from 1st floppy drive */
|
|
case 0:
|
|
{
|
|
setAH(0x02); // Read sectors
|
|
setAL(0x01); // Number of sectors
|
|
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;
|
|
#ifdef ADVANCED_DEBUGGING
|
|
DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
/* Boot from 1st HDD drive */
|
|
case 1:
|
|
{
|
|
setAH(0x02); // Read sectors
|
|
setAL(0x01); // Number of sectors
|
|
setDH(0x00); // Head 0
|
|
setCH(0x00); // Cylinder 0
|
|
setCL(0x01); // Sector 1
|
|
setDL(0x80); // First HDD 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;
|
|
#ifdef ADVANCED_DEBUGGING
|
|
DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
goto StartDos;
|
|
}
|
|
|
|
/* 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 */
|
|
DosBootsectorInitialize();
|
|
|
|
Quit:
|
|
/*
|
|
* Jump to 0000:7C00 to boot the OS.
|
|
*
|
|
* 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
|
|
* not be clean up and return properly), but by changing the CS:IP in the
|
|
* stack, so that when the interrupt returns, the modified CS:IP is popped
|
|
* off the stack and the CPU is correctly repositioned.
|
|
*/
|
|
Stack[STACK_CS] = 0x0000;
|
|
Stack[STACK_IP] = 0x7C00;
|
|
|
|
DPRINT("<-- BiosBootstrapLoader\n");
|
|
}
|
|
|
|
static VOID WINAPI BiosTimeService(LPWORD Stack)
|
|
{
|
|
switch (getAH())
|
|
{
|
|
/* Get System Time */
|
|
case 0x00:
|
|
{
|
|
/* Set AL to 1 if midnight had passed, 0 otherwise */
|
|
setAL(Bda->MidnightPassed ? 0x01 : 0x00);
|
|
|
|
/* Return the tick count in CX:DX */
|
|
setCX(HIWORD(Bda->TickCounter));
|
|
setDX(LOWORD(Bda->TickCounter));
|
|
|
|
/* Reset the midnight flag */
|
|
Bda->MidnightPassed = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Set System Time */
|
|
case 0x01:
|
|
{
|
|
/* Set the tick count to CX:DX */
|
|
Bda->TickCounter = MAKELONG(getDX(), getCX());
|
|
|
|
/* Reset the midnight flag */
|
|
Bda->MidnightPassed = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Get Real-Time Clock Time */
|
|
case 0x02:
|
|
{
|
|
UCHAR StatusB;
|
|
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_HOURS);
|
|
setCH(IOReadB(CMOS_DATA_PORT));
|
|
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_MINUTES);
|
|
setCL(IOReadB(CMOS_DATA_PORT));
|
|
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SECONDS);
|
|
setDH(IOReadB(CMOS_DATA_PORT));
|
|
|
|
/* Daylight Savings Time */
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_STATUS_B);
|
|
StatusB = IOReadB(CMOS_DATA_PORT);
|
|
setDL(StatusB & 0x01);
|
|
|
|
/* Clear CF */
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
// /* Set Real-Time Clock Time */
|
|
// case 0x03:
|
|
// {
|
|
// break;
|
|
// }
|
|
|
|
/* Get Real-Time Clock Date */
|
|
case 0x04:
|
|
{
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_CENTURY);
|
|
setCH(IOReadB(CMOS_DATA_PORT));
|
|
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_YEAR);
|
|
setCL(IOReadB(CMOS_DATA_PORT));
|
|
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_MONTH);
|
|
setDH(IOReadB(CMOS_DATA_PORT));
|
|
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_DAY);
|
|
setDL(IOReadB(CMOS_DATA_PORT));
|
|
|
|
/* Clear CF */
|
|
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
|
break;
|
|
}
|
|
|
|
// /* Set Real-Time Clock Date */
|
|
// case 0x05:
|
|
// {
|
|
// break;
|
|
// }
|
|
|
|
default:
|
|
{
|
|
DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
|
|
getAH());
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
|
|
{
|
|
/* Increase the system tick count */
|
|
Bda->TickCounter++;
|
|
}
|
|
|
|
|
|
// From SeaBIOS
|
|
static VOID PicSetIRQMask(USHORT off, USHORT on)
|
|
{
|
|
UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8;
|
|
IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on);
|
|
IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on);
|
|
}
|
|
|
|
// From SeaBIOS
|
|
VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func)
|
|
{
|
|
UCHAR vector;
|
|
|
|
PicSetIRQMask(1 << hwirq, 0);
|
|
if (hwirq < 8)
|
|
vector = BIOS_PIC_MASTER_INT + hwirq;
|
|
else
|
|
vector = BIOS_PIC_SLAVE_INT + hwirq - 8;
|
|
|
|
RegisterBiosInt32(vector, func);
|
|
}
|
|
|
|
|
|
VOID PicIRQComplete(BYTE IntNum)
|
|
{
|
|
/*
|
|
* If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
|
|
*/
|
|
if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
|
|
{
|
|
/* It was an IRQ from the master PIC */
|
|
IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
|
|
}
|
|
else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
|
|
{
|
|
/* It was an IRQ from the slave PIC */
|
|
IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
|
|
IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
|
|
}
|
|
}
|
|
|
|
static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack)
|
|
{
|
|
BYTE IrqNumber;
|
|
|
|
IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
|
|
IrqNumber = IOReadB(PIC_MASTER_CMD);
|
|
|
|
DPRINT("Master - IrqNumber = 0x%02X\n", IrqNumber);
|
|
|
|
PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
|
|
}
|
|
|
|
static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack)
|
|
{
|
|
BYTE IrqNumber;
|
|
|
|
IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
|
|
IrqNumber = IOReadB(PIC_SLAVE_CMD);
|
|
|
|
DPRINT("Slave - IrqNumber = 0x%02X\n", IrqNumber);
|
|
|
|
PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
|
|
}
|
|
|
|
// Timer IRQ 0
|
|
static VOID WINAPI BiosTimerIrq(LPWORD Stack)
|
|
{
|
|
/*
|
|
* Perform the system timer interrupt.
|
|
*
|
|
* Do not call directly BiosSystemTimerInterrupt(Stack);
|
|
* because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
|
|
* for their purpose...
|
|
*/
|
|
|
|
WORD AX = getAX();
|
|
WORD CX = getCX();
|
|
WORD DX = getDX();
|
|
WORD BX = getBX();
|
|
WORD BP = getBP();
|
|
WORD SI = getSI();
|
|
WORD DI = getDI();
|
|
WORD DS = getDS();
|
|
WORD ES = getES();
|
|
|
|
Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT);
|
|
|
|
setAX(AX);
|
|
setCX(CX);
|
|
setDX(DX);
|
|
setBX(BX);
|
|
setBP(BP);
|
|
setSI(SI);
|
|
setDI(DI);
|
|
setDS(DS);
|
|
setES(ES);
|
|
setCF(0);
|
|
|
|
// BiosSystemTimerInterrupt(Stack);
|
|
PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
|
|
}
|
|
|
|
|
|
static VOID BiosHwSetup(VOID)
|
|
{
|
|
/* Initialize the master and the slave PICs (cascade mode) */
|
|
IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
|
|
IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
|
|
|
|
/*
|
|
* Set the interrupt vector offsets for each PIC
|
|
* (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
|
|
*/
|
|
IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
|
|
IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT );
|
|
|
|
/* Tell the master PIC that there is a slave PIC at IRQ 2 */
|
|
IOWriteB(PIC_MASTER_DATA, 1 << 2);
|
|
/* Tell the slave PIC its cascade identity */
|
|
IOWriteB(PIC_SLAVE_DATA , 2);
|
|
|
|
/* Make sure both PICs are in 8086 mode */
|
|
IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086);
|
|
IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086);
|
|
|
|
/* Clear the masks for both PICs */
|
|
// IOWriteB(PIC_MASTER_DATA, 0x00);
|
|
// IOWriteB(PIC_SLAVE_DATA , 0x00);
|
|
/* Disable all IRQs */
|
|
IOWriteB(PIC_MASTER_DATA, 0xFF);
|
|
IOWriteB(PIC_SLAVE_DATA , 0xFF);
|
|
|
|
|
|
/* Initialize PIT Counter 0 - Mode 2, 16bit binary count */
|
|
// NOTE: Some BIOSes set it to Mode 3 instead.
|
|
IOWriteB(PIT_COMMAND_PORT, 0x34);
|
|
// 18.2Hz refresh rate
|
|
IOWriteB(PIT_DATA_PORT(0), 0x00);
|
|
IOWriteB(PIT_DATA_PORT(0), 0x00);
|
|
|
|
/* Initialize PIT Counter 1 - Mode 2, 8bit binary count */
|
|
IOWriteB(PIT_COMMAND_PORT, 0x54);
|
|
// DRAM refresh every 15ms: http://www.cs.dartmouth.edu/~spl/Academic/Organization/docs/PC%20Timer%208253.html
|
|
IOWriteB(PIT_DATA_PORT(1), 18);
|
|
|
|
/* Initialize PIT Counter 2 - Mode 3, 16bit binary count */
|
|
IOWriteB(PIT_COMMAND_PORT, 0xB6);
|
|
// Count for 440Hz
|
|
IOWriteB(PIT_DATA_PORT(2), 0x97);
|
|
IOWriteB(PIT_DATA_PORT(2), 0x0A);
|
|
|
|
|
|
/* Initialize PS/2 keyboard port */
|
|
// Enable the port
|
|
IOWriteB(PS2_CONTROL_PORT, 0xAE);
|
|
// Port interrupts and clock enabled,
|
|
// enable keyboard scancode translation.
|
|
// POST passed, force keyboard unlocking.
|
|
IOWriteB(PS2_CONTROL_PORT, 0x60);
|
|
IOWriteB(PS2_DATA_PORT , 0x6D);
|
|
// Enable data reporting
|
|
IOWriteB(PS2_DATA_PORT , 0xF4);
|
|
|
|
EnableHwIRQ(0, BiosTimerIrq);
|
|
}
|
|
|
|
static VOID InitializeBiosInt32(VOID)
|
|
{
|
|
USHORT i;
|
|
|
|
/* Initialize the callback context */
|
|
InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000);
|
|
|
|
/* Register the default BIOS interrupt vectors */
|
|
|
|
/*
|
|
* Zero out all of the IVT (0x00 -- 0xFF). Some applications
|
|
* indeed expect to have free vectors at the end of the IVT.
|
|
*/
|
|
RtlZeroMemory(BaseAddress, 0x0100 * sizeof(ULONG));
|
|
|
|
#if defined(ADVANCED_DEBUGGING) && (ADVANCED_DEBUGGING_LEVEL >= 3)
|
|
// Initialize all the interrupt vectors to the default one.
|
|
for (i = 0x00; i <= 0xFF; i++)
|
|
RegisterBiosInt32(i, NULL);
|
|
#endif
|
|
|
|
/* Initialize the exception interrupt vectors to a default Exception handler */
|
|
for (i = 0x00; i <= 0x07; i++)
|
|
RegisterBiosInt32(i, BiosException);
|
|
|
|
/* Initialize HW interrupt vectors to a default HW handler */
|
|
for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) // 0x08 -- 0x0F
|
|
RegisterBiosInt32(i, BiosHandleMasterPicIRQ);
|
|
for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++) // 0x70 -- 0x77
|
|
RegisterBiosInt32(i, BiosHandleSlavePicIRQ);
|
|
|
|
/* Initialize software vector handlers */
|
|
// BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
|
|
RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService );
|
|
RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize );
|
|
// BIOS_DISK_INTERRUPT : 0x13 (dskbios32.c)
|
|
// BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
|
|
RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService );
|
|
// BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
|
|
// BIOS_PRINTER_INTERRUPT: 0x17 -- UNIMPLEMENTED
|
|
RegisterBiosInt32(BIOS_ROM_BASIC , BiosRomBasic );
|
|
RegisterBiosInt32(BIOS_BOOTSTRAP_LOADER , BiosBootstrapLoader );
|
|
RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService );
|
|
// BIOS_KBD_CTRL_BREAK_INTERRUPT: 0x1B -- UNIMPLEMENTED
|
|
RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
|
|
|
|
/* Vectors that should be implemented (see above) */
|
|
RegisterBiosInt32(0x14, NULL);
|
|
RegisterBiosInt32(0x17, NULL);
|
|
RegisterBiosInt32(0x1B, NULL);
|
|
RegisterBiosInt32(0x4A, NULL); // User Alarm Handler
|
|
|
|
/* Relocated services by the BIOS (when needed) */
|
|
RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
|
|
RegisterBiosInt32(0x42, NULL); // Relocated Default INT 10h Video Services
|
|
|
|
/* Miscellaneous unimplemented vector handlers that should better have a default one */
|
|
RegisterBiosInt32(0x4B, NULL); // Virtual DMA Specification Services
|
|
RegisterBiosInt32(0x5C, NULL); // NetBIOS
|
|
|
|
// ROM-BASIC interrupts span from 0x80 up to 0xEF.
|
|
// They don't have any default handler at the moment.
|
|
|
|
/* Some vectors are in fact addresses to tables */
|
|
((PULONG)BaseAddress)[0x1D] = NULL32; // Video Parameter Tables
|
|
((PULONG)BaseAddress)[0x1E] = NULL32; // Diskette Parameters
|
|
((PULONG)BaseAddress)[0x1F] = NULL32; // 8x8 Graphics Font
|
|
((PULONG)BaseAddress)[0x41] = NULL32; // Hard Disk 0 Parameter Table Address
|
|
((PULONG)BaseAddress)[0x43] = NULL32; // Character Table (EGA, MCGA, VGA)
|
|
((PULONG)BaseAddress)[0x46] = NULL32; // Hard Disk 1 Drive Parameter Table Address
|
|
/* Tables that are always uninitialized */
|
|
((PULONG)BaseAddress)[0x44] = NULL32; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
|
|
((PULONG)BaseAddress)[0x48] = NULL32; // Cordless Keyboard Translation (PCjr)
|
|
((PULONG)BaseAddress)[0x49] = NULL32; // Non-Keyboard Scan-code Translation Table (PCJr)
|
|
}
|
|
|
|
static VOID InitializeBiosData(VOID)
|
|
{
|
|
UCHAR Low, High;
|
|
|
|
/* Initialize the BDA contents */
|
|
RtlZeroMemory(Bda, sizeof(*Bda));
|
|
|
|
/*
|
|
* Retrieve the basic equipment list from the CMOS
|
|
*/
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_EQUIPMENT_LIST | CMOS_DISABLE_NMI);
|
|
Bda->EquipmentList = IOReadB(CMOS_DATA_PORT);
|
|
// TODO: Update it if required.
|
|
Bda->EquipmentList &= 0x00FF; // High byte cleared for now...
|
|
|
|
/*
|
|
* Retrieve the conventional memory size
|
|
* in kB from the CMOS, typically 640 kB.
|
|
*/
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW | CMOS_DISABLE_NMI);
|
|
Low = IOReadB(CMOS_DATA_PORT);
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH | CMOS_DISABLE_NMI);
|
|
High = IOReadB(CMOS_DATA_PORT);
|
|
Bda->MemorySize = MAKEWORD(Low, High);
|
|
}
|
|
|
|
|
|
/*
|
|
* The BIOS POST (Power On-Self Test)
|
|
*/
|
|
/*static*/ VOID
|
|
WINAPI
|
|
Bios32Post(LPWORD Stack)
|
|
{
|
|
static BOOLEAN FirstBoot = TRUE;
|
|
BYTE ShutdownStatus;
|
|
|
|
/*
|
|
* Initialize BIOS/Keyboard/Video RAM dynamic data
|
|
*/
|
|
|
|
DPRINT("Bios32Post\n");
|
|
|
|
/* Disable interrupts */
|
|
setIF(0);
|
|
|
|
/* Set the data segment */
|
|
setDS(BDA_SEGMENT);
|
|
|
|
/* Initialize the stack */
|
|
// Temporary stack for POST (to be used only before initializing the INT vectors)
|
|
// setSS(0x0000);
|
|
// setSP(0x0400);
|
|
//
|
|
// Stack to be used after the initialization of the INT vectors
|
|
setSS(0x0000); // Stack at 00:8000, going downwards
|
|
setSP(0x8000);
|
|
|
|
/*
|
|
* Perform early CMOS shutdown status checks
|
|
*/
|
|
|
|
/* Read the CMOS shutdown status byte and reset it */
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
|
|
ShutdownStatus = IOReadB(CMOS_DATA_PORT);
|
|
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SHUTDOWN_STATUS | CMOS_DISABLE_NMI);
|
|
IOWriteB(CMOS_DATA_PORT, 0x00);
|
|
|
|
DPRINT1("Bda->SoftReset = 0x%04X ; ShutdownStatus = 0x%02X\n",
|
|
Bda->SoftReset, ShutdownStatus);
|
|
|
|
switch (ShutdownStatus)
|
|
{
|
|
/* Shutdown after Memory Tests (unsupported) */
|
|
case 0x01: case 0x02: case 0x03:
|
|
/* Shutdown after Protected Mode Tests (unsupported) */
|
|
case 0x06: case 0x07: case 0x08:
|
|
/* Shutdown after Block Move Test (unsupported) */
|
|
case 0x09:
|
|
{
|
|
DisplayMessage(L"Unsupported CMOS Shutdown Status value 0x%02X. The VDM will shut down.", ShutdownStatus);
|
|
EmulatorTerminate();
|
|
return;
|
|
}
|
|
|
|
/* Shutdown to Boot Loader */
|
|
case 0x04:
|
|
{
|
|
DPRINT1("Fast restart to Bootstrap Loader...\n");
|
|
goto Quit; // Reenable interrupts and exit.
|
|
}
|
|
|
|
/* Flush keyboard, issue an EOI... */
|
|
case 0x05:
|
|
{
|
|
IOReadB(PS2_DATA_PORT);
|
|
|
|
/* Send EOI */
|
|
IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
|
|
IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
|
|
|
|
// Fall back
|
|
}
|
|
|
|
/*
|
|
* ... and far JMP to user-specified location at 0040:0067
|
|
* (Bda->ResumeEntryPoint) with interrupts and NMI disabled.
|
|
*/
|
|
case 0x0A:
|
|
{
|
|
DPRINT1("Bda->ResumeEntryPoint = %04X:%04X\n",
|
|
HIWORD(Bda->ResumeEntryPoint),
|
|
LOWORD(Bda->ResumeEntryPoint));
|
|
|
|
/* Position execution pointers and return with interrupts disabled */
|
|
setCS(HIWORD(Bda->ResumeEntryPoint));
|
|
setIP(LOWORD(Bda->ResumeEntryPoint));
|
|
return;
|
|
}
|
|
|
|
/* Soft reset or unexpected shutdown... */
|
|
case 0x00:
|
|
/* ... or other possible shutdown codes: just continue the POST */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* FIXME: UNIMPLEMENTED!
|
|
* Check the word at 0040h:0072h (Bda->SoftReset) and do one of the
|
|
* following actions:
|
|
* - if the word is 0000h, perform a cold reboot (aka. Reset). Everything gets initialized.
|
|
* - if the word is 1234h, perform a warm reboot (aka. Ctrl-Alt-Del). Some stuff is skipped.
|
|
*/
|
|
switch (Bda->SoftReset)
|
|
{
|
|
case 0x0000:
|
|
{
|
|
if (!FirstBoot)
|
|
{
|
|
DisplayMessage(L"NTVDM is performing a COLD reboot! The program you are currently testing does not seem to behave correctly! The VDM will shut down...");
|
|
EmulatorTerminate();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x1234:
|
|
{
|
|
DisplayMessage(L"NTVDM is performing a WARM reboot! This is not supported at the moment. The VDM will shut down...");
|
|
EmulatorTerminate();
|
|
return;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
FirstBoot = FALSE;
|
|
|
|
/* Initialize the BDA */
|
|
InitializeBiosData();
|
|
|
|
/* Initialize the User Data Area at 0050:XXXX */
|
|
RtlZeroMemory(SEG_OFF_TO_PTR(0x50, 0x0000), sizeof(USER_DATA_AREA));
|
|
|
|
|
|
|
|
//////// NOTE: This is more or less bios-specific ////////
|
|
|
|
/*
|
|
* Initialize IVT and hardware
|
|
*/
|
|
|
|
// WriteUnProtectRom(...);
|
|
|
|
/* Register the BIOS 32-bit Interrupts */
|
|
InitializeBiosInt32();
|
|
|
|
/* Initialize platform hardware (PIC/PIT chips, ...) */
|
|
BiosHwSetup();
|
|
|
|
/* Initialize the Keyboard, Video and Mouse BIOS */
|
|
KbdBios32Post();
|
|
VidBiosPost();
|
|
MouseBios32Post();
|
|
DiskBios32Post();
|
|
|
|
// WriteProtectRom(...);
|
|
|
|
//////// End of more or less bios-specific section ///////
|
|
|
|
|
|
|
|
SearchAndInitRoms(&BiosContext);
|
|
|
|
/*
|
|
* End of the 32-bit POST portion. We then fall back into 16-bit where
|
|
* the rest of the POST code is executed, typically calling INT 19h
|
|
* to boot up the OS.
|
|
*/
|
|
|
|
Quit:
|
|
/* Enable interrupts */
|
|
setIF(1);
|
|
}
|
|
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
BOOLEAN Bios32Initialize(VOID)
|
|
{
|
|
/*
|
|
* Initialize BIOS/Keyboard/Video ROM static data
|
|
*/
|
|
|
|
/* System BIOS Copyright */
|
|
RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE000), BiosCopyright, sizeof(BiosCopyright)-1);
|
|
|
|
/* System BIOS Version */
|
|
RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE070), BiosVersion, sizeof(BiosVersion)-1);
|
|
|
|
/* System BIOS Date */
|
|
RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFF5), BiosDate, sizeof(BiosDate)-1);
|
|
|
|
/* Bootstrap code */
|
|
RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE05B), PostCode , sizeof(PostCode ));
|
|
RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFF0), Bootstrap, sizeof(Bootstrap));
|
|
|
|
/* BIOS ROM Information */
|
|
RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xE6F5), &BiosConfigTable, sizeof(BiosConfigTable));
|
|
|
|
/* System BIOS Model (same as Bct->Model) */
|
|
*(PBYTE)(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFFE)) = BIOS_MODEL;
|
|
|
|
/* Initialize the Keyboard and Video BIOS */
|
|
if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
|
|
{
|
|
/* Stop the VDM */
|
|
EmulatorTerminate();
|
|
return FALSE;
|
|
}
|
|
|
|
/* Redefine our POST function */
|
|
RegisterBop(BOP_RESET, Bios32Post);
|
|
|
|
WriteProtectRom((PVOID)TO_LINEAR(BIOS_SEGMENT, 0x0000),
|
|
ROM_AREA_END - TO_LINEAR(BIOS_SEGMENT, 0x0000) + 1);
|
|
|
|
/* We are done */
|
|
return TRUE;
|
|
}
|
|
|
|
VOID Bios32Cleanup(VOID)
|
|
{
|
|
DiskBios32Cleanup();
|
|
MouseBios32Cleanup();
|
|
VidBios32Cleanup();
|
|
KbdBiosCleanup();
|
|
}
|
|
|
|
/* EOF */
|