mirror of
https://github.com/reactos/reactos.git
synced 2025-02-24 01:15:09 +00:00
[NTVDM]
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:
parent
4c4c69fc37
commit
944d6e8b5a
5 changed files with 233 additions and 2 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue