Implement "Get DOS Version" API call.
Implement BIOS keyboard IRQ handler and some "INT 16h" functions.


svn path=/branches/ntvdm/; revision=59350
This commit is contained in:
Aleksandar Andrejevic 2013-06-28 20:52:40 +00:00
parent 4c4c69fc37
commit 944d6e8b5a
5 changed files with 233 additions and 2 deletions

View file

@ -11,12 +11,17 @@
#include "bios.h"
#include "emulator.h"
#include "pic.h"
#include "ps2.h"
#include "timer.h"
/* PRIVATE VARIABLES **********************************************************/
static BYTE CursorRow, CursorCol;
static WORD ConsoleWidth, ConsoleHeight;
static BYTE BiosKeyboardMap[256];
static WORD BiosKbdBuffer[BIOS_KBD_BUFFER_SIZE];
static UINT BiosKbdBufferStart = 0, BiosKbdBufferEnd = 0;
static BOOLEAN BiosKbdBufferEmpty = TRUE;
/* PRIVATE FUNCTIONS **********************************************************/
@ -38,12 +43,54 @@ static COORD BiosVideoAddressToCoord(ULONG Address)
return Result;
}
static BOOLEAN BiosKbdBufferPush(WORD Data)
{
/* If it's full, fail */
if (!BiosKbdBufferEmpty && (BiosKbdBufferStart == BiosKbdBufferEnd))
{
return FALSE;
}
/* Otherwise, add the value to the queue */
BiosKbdBuffer[BiosKbdBufferEnd] = Data;
BiosKbdBufferEnd++;
BiosKbdBufferEnd %= BIOS_KBD_BUFFER_SIZE;
BiosKbdBufferEmpty = FALSE;
/* Return success */
return TRUE;
}
static BOOLEAN BiosKbdBufferTop(LPWORD Data)
{
/* If it's empty, fail */
if (BiosKbdBufferEmpty) return FALSE;
/* Otherwise, get the value and return success */
*Data = BiosKbdBuffer[BiosKbdBufferStart];
return TRUE;
}
static BOOLEAN BiosKbdBufferPop()
{
/* If it's empty, fail */
if (BiosKbdBufferEmpty) return FALSE;
/* Otherwise, remove the value and return success */
BiosKbdBufferStart++;
BiosKbdBufferStart %= BIOS_KBD_BUFFER_SIZE;
if (BiosKbdBufferStart == BiosKbdBufferEnd) BiosKbdBufferEmpty = TRUE;
return TRUE;
}
/* PUBLIC FUNCTIONS ***********************************************************/
BOOLEAN BiosInitialize()
{
INT i;
WORD Offset = 0;
HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
@ -85,6 +132,9 @@ BOOLEAN BiosInitialize()
ConsoleWidth = ConsoleInfo.dwSize.X;
ConsoleHeight = ConsoleInfo.dwSize.Y;
/* Set the console input mode */
SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
/* Initialize the PIC */
PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
PicWriteCommand(PIC_SLAVE_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
@ -185,6 +235,56 @@ VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress)
}
}
WORD BiosPeekCharacter()
{
WORD CharacterData;
/* Check if there is a key available */
if (BiosKbdBufferEmpty) return 0xFFFF;
/* Get the key from the queue, but don't remove it */
BiosKbdBufferTop(&CharacterData);
return CharacterData;
}
WORD BiosGetCharacter()
{
WORD CharacterData;
HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
INPUT_RECORD InputRecord;
DWORD Count;
/* Check if there is a key available */
if (!BiosKbdBufferEmpty)
{
/* Get the key from the queue, and remove it */
BiosKbdBufferTop(&CharacterData);
BiosKbdBufferPop();
}
else
{
while (TRUE)
{
/* Wait for a console event */
WaitForSingleObject(ConsoleInput, INFINITE);
/* Read the event, and make sure it's a keypress */
if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count)) continue;
if (InputRecord.EventType != KEY_EVENT) continue;
if (!InputRecord.Event.KeyEvent.bKeyDown) continue;
/* Save the scan code and end the loop */
CharacterData = (InputRecord.Event.KeyEvent.wVirtualScanCode << 8)
| InputRecord.Event.KeyEvent.uChar.AsciiChar;
break;
}
}
return CharacterData;
}
VOID BiosVideoService()
{
HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
@ -270,8 +370,100 @@ VOID BiosVideoService()
}
}
VOID BiosKeyboardService()
{
DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
switch (HIBYTE(Eax))
{
case 0x00:
{
/* Read the character (and wait if necessary) */
EmulatorSetRegister(EMULATOR_REG_AX, BiosGetCharacter());
break;
}
case 0x01:
{
WORD Data = BiosPeekCharacter();
if (Data != 0xFFFF)
{
/* There is a character, clear ZF and return it */
EmulatorSetRegister(EMULATOR_REG_AX, Data);
EmulatorClearFlag(EMULATOR_FLAG_ZF);
}
else
{
/* No character, set ZF */
EmulatorSetFlag(EMULATOR_FLAG_ZF);
}
break;
}
}
}
VOID BiosHandleIrq(BYTE IrqNumber)
{
switch (IrqNumber)
{
/* PIT IRQ */
case 0:
{
/* Perform the system timer interrupt */
EmulatorInterrupt(0x1C);
break;
}
/* Keyboard IRQ */
case 1:
{
BYTE ScanCode, VirtualKey;
WORD Character;
/* Check if there is a scancode available */
if (!(KeyboardReadStatus() & 1)) break;
/* Get the scan code and virtual key code */
ScanCode = KeyboardReadData();
VirtualKey = MapVirtualKey(ScanCode, MAPVK_VSC_TO_VK);
/* Check if this is a key press or release */
if (!(ScanCode & (1 << 7)))
{
/* Key press */
if (VirtualKey == VK_NUMLOCK
|| VirtualKey == VK_CAPITAL
|| VirtualKey == VK_SCROLL)
{
/* For toggle keys, toggle the lowest bit in the keyboard map */
BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
}
/* Set the highest bit */
BiosKeyboardMap[VirtualKey] |= (1 << 7);
/* Find out which character this is */
ToAscii(ScanCode, VirtualKey, BiosKeyboardMap, &Character, 0);
/* Push it onto the BIOS keyboard queue */
BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF));
}
else
{
/* Key release, unset the highest bit */
BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
}
break;
}
}
/* Send End-of-Interrupt to the PIC */
if (IrqNumber > 8) PicWriteCommand(PIC_SLAVE_CMD, PIC_OCW2_EOI);
PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
}

View file

@ -23,14 +23,19 @@
#define BIOS_PIC_SLAVE_INT 0x70
#define BIOS_SEGMENT 0xF000
#define VIDEO_BIOS_INTERRUPT 0x10
#define VIDEO_KBD_INTERRUPT 0x16
#define CONSOLE_FONT_HEIGHT 8
#define BIOS_KBD_BUFFER_SIZE 256
/* FUNCTIONS ******************************************************************/
BOOLEAN BiosInitialize();
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress);
WORD BiosPeekCharacter();
WORD BiosGetCharacter();
VOID BiosVideoService();
VOID BiosKeyboardService();
VOID BiosHandleIrq(BYTE IrqNumber);
#endif

View file

@ -9,6 +9,7 @@
/* INCLUDES *******************************************************************/
#include "dos.h"
#include "bios.h"
#include "emulator.h"
/* PRIVATE VARIABLES **********************************************************/
@ -85,6 +86,14 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
return DestSegment;
}
static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
{
PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
/* Just set the owner */
Mcb->OwnerPsp = NewOwner;
}
/* PUBLIC FUNCTIONS ***********************************************************/
WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
@ -412,6 +421,9 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
CommandLine, ExeSize + (sizeof(DOS_PSP) >> 4) + i,
EnvBlock);
/* The process owns its own memory */
DosChangeMemoryOwner(Segment, Segment);
/* Copy the program to Segment:0100 */
RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
+ TO_LINEAR(Segment, 0x100)),
@ -548,7 +560,13 @@ Done:
CHAR DosReadCharacter()
{
// TODO: STDIN can be redirected under DOS 2.0+
return _getch();
CHAR Character = 0;
/* A zero value for the character indicates a special key */
do Character = BiosGetCharacter();
while (!Character);
return Character;
}
VOID DosPrintCharacter(CHAR Character)
@ -749,6 +767,15 @@ VOID DosInt21h(WORD CodeSegment)
break;
}
/* Get DOS Version */
case 0x30:
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
EmulatorSetRegister(EMULATOR_REG_AX, PspBlock->DosVersion);
break;
}
/* Get Interrupt Vector */
case 0x35:
{
@ -879,6 +906,7 @@ VOID DosInt21h(WORD CodeSegment)
/* Unsupported */
default:
{
DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
EmulatorSetFlag(EMULATOR_FLAG_CF);
}
}

View file

@ -15,7 +15,7 @@
/* DEFINES ********************************************************************/
#define DOS_VERSION 0x0600
#define DOS_VERSION MAKEWORD(6, 0)
#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

View file

@ -214,6 +214,12 @@ static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
BiosVideoService();
break;
}
case VIDEO_KBD_INTERRUPT:
{
/* This is the keyboard BIOS interrupt, call the BIOS */
BiosKeyboardService();
break;
}
case 0x20:
{
DosInt20h(CodeSegment);