[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 28 Jun 2013 20:52:40 +0000 (20:52 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 28 Jun 2013 20:52:40 +0000 (20:52 +0000)
Implement "Get DOS Version" API call.
Implement BIOS keyboard IRQ handler and some "INT 16h" functions.

svn path=/branches/ntvdm/; revision=59350

subsystems/ntvdm/bios.c
subsystems/ntvdm/bios.h
subsystems/ntvdm/dos.c
subsystems/ntvdm/dos.h
subsystems/ntvdm/emulator.c

index 88afa6d..59a29eb 100644 (file)
 #include "bios.h"
 #include "emulator.h"
 #include "pic.h"
 #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;
 #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 **********************************************************/
 
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
@@ -38,12 +43,54 @@ static COORD BiosVideoAddressToCoord(ULONG Address)
     return Result;
 }
 
     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;
 /* 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);
     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;
 
     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);
     /* 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);
 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)
 {
 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);
 }
 
     PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
 }
 
index c3e2b01..a8c76fc 100644 (file)
 #define BIOS_PIC_SLAVE_INT 0x70
 #define BIOS_SEGMENT 0xF000
 #define VIDEO_BIOS_INTERRUPT 0x10
 #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 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);
 
 /* FUNCTIONS ******************************************************************/
 
 BOOLEAN BiosInitialize();
 VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
 VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress);
+WORD BiosPeekCharacter();
+WORD BiosGetCharacter();
 VOID BiosVideoService();
 VOID BiosVideoService();
+VOID BiosKeyboardService();
 VOID BiosHandleIrq(BYTE IrqNumber);
 
 #endif
 VOID BiosHandleIrq(BYTE IrqNumber);
 
 #endif
index ae84dd7..e592189 100644 (file)
@@ -9,6 +9,7 @@
 /* INCLUDES *******************************************************************/
 
 #include "dos.h"
 /* INCLUDES *******************************************************************/
 
 #include "dos.h"
+#include "bios.h"
 #include "emulator.h"
 
 /* PRIVATE VARIABLES **********************************************************/
 #include "emulator.h"
 
 /* PRIVATE VARIABLES **********************************************************/
@@ -85,6 +86,14 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
     return DestSegment;
 }
 
     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)
 /* 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);
 
                          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)),
         /* 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+
 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)
 }
 
 VOID DosPrintCharacter(CHAR Character)
@@ -749,6 +767,15 @@ VOID DosInt21h(WORD CodeSegment)
             break;
         }
 
             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:
         {
         /* Get Interrupt Vector */
         case 0x35:
         {
@@ -879,6 +906,7 @@ VOID DosInt21h(WORD CodeSegment)
         /* Unsupported */
         default:
         {
         /* Unsupported */
         default:
         {
+            DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
             EmulatorSetFlag(EMULATOR_FLAG_CF);
         }
     }
             EmulatorSetFlag(EMULATOR_FLAG_CF);
         }
     }
index b88dadb..38a9550 100644 (file)
@@ -15,7 +15,7 @@
 
 /* DEFINES ********************************************************************/
 
 
 /* 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
 #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
index 67b0cd3..de52f91 100644 (file)
@@ -214,6 +214,12 @@ static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
                 BiosVideoService();
                 break;
             }
                 BiosVideoService();
                 break;
             }
+            case VIDEO_KBD_INTERRUPT:
+            {
+                /* This is the keyboard BIOS interrupt, call the BIOS */
+                BiosKeyboardService();
+                break;
+            }
             case 0x20:
             {
                 DosInt20h(CodeSegment);
             case 0x20:
             {
                 DosInt20h(CodeSegment);