[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Thu, 20 Jun 2013 19:00:07 +0000 (19:00 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Thu, 20 Jun 2013 19:00:07 +0000 (19:00 +0000)
Implement 8259 Programmable Interrupt Controller emulation.

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

subsystems/ntvdm/CMakeLists.txt
subsystems/ntvdm/bios.c
subsystems/ntvdm/hardware.c [new file with mode: 0644]
subsystems/ntvdm/ntvdm.h

index af24b91..3145102 100644 (file)
@@ -5,6 +5,7 @@ list(APPEND SOURCE
     bios.c
     dos.c
     emulator.c
+    hardware.c
     ntvdm.c
     ntvdm.rc)
 
index 63f9f03..72f3f67 100644 (file)
@@ -55,6 +55,22 @@ BOOLEAN BiosInitialize()
     CursorRow = ConsoleInfo.dwCursorPosition.Y;
     ConsoleWidth = ConsoleInfo.dwSize.X;
     ConsoleHeight = ConsoleInfo.dwSize.Y;
+    
+    /* Initialize the PIC */
+    PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
+    PicWriteCommand(PIC_SLAVE_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
+    
+    /* Set the interrupt offsets */
+    PicWriteData(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
+    PicWriteData(PIC_SLAVE_DATA, BIOS_PIC_SLAVE_INT);
+    
+    /* Tell the master PIC there is a slave at IRQ 2 */
+    PicWriteData(PIC_MASTER_DATA, 1 << 2);
+    PicWriteData(PIC_SLAVE_DATA, 2);
+    
+    /* Make sure the PIC is in 8086 mode */
+    PicWriteData(PIC_MASTER_DATA, PIC_ICW4_8086);
+    PicWriteData(PIC_SLAVE_DATA, PIC_ICW4_8086);
 
     return TRUE;
 }
diff --git a/subsystems/ntvdm/hardware.c b/subsystems/ntvdm/hardware.c
new file mode 100644 (file)
index 0000000..77aeee7
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            hardware.c
+ * PURPOSE:         Minimal hardware emulation
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+typedef struct _PIC
+{
+    BOOLEAN Initialization;
+    BYTE MaskRegister;
+    BYTE InServiceRegister;
+    BYTE IntOffset;
+    BYTE ConfigRegister;
+    BYTE CascadeRegister;
+    BOOLEAN CascadeRegisterSet;
+    BOOLEAN AutoEoi;
+    BOOLEAN Slave;
+    BOOLEAN ReadIsr;
+} PIC, *PPIC;
+
+static PIC MasterPic, SlavePic;
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BYTE PicReadCommand(BYTE Port)
+{
+    PPIC Pic;
+    
+    /* Which PIC are we accessing? */
+    if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
+    else Pic = &SlavePic;
+    
+    if (Pic->ReadIsr)
+    {
+        /* Read the in-service register */
+        Pic->ReadIsr = FALSE;
+        return Pic->InServiceRegister;
+    }
+    else
+    {
+        /* The IRR is always 0, as the emulated CPU receives the interrupt instantly */
+        return 0;
+    }
+}
+
+VOID PicWriteCommand(BYTE Port, BYTE Value)
+{
+    PPIC Pic;
+    
+    /* Which PIC are we accessing? */
+    if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
+    else Pic = &SlavePic;
+    
+    if (Value & PIC_ICW1)
+    {
+        /* Start initialization */
+        Pic->Initialization = TRUE;
+        Pic->IntOffset = 0xFF;
+        Pic->CascadeRegisterSet = FALSE;
+        Pic->ConfigRegister = Value;
+        return;
+    }
+    
+    if (Value & PIC_OCW3)
+    {
+        /* This is an OCR3 */
+        if (Value == PIC_OCW3_READ_ISR)
+        {
+            /* Return the ISR on next read from command port */
+            Pic->ReadIsr = TRUE;
+        }
+        
+        return;
+    }
+    
+    /* This is an OCW2 */
+    if (Value & PIC_OCW2_EOI)
+    {
+        if (Value & PIC_OCW2_SL)
+        {
+            /* If the SL bit is set, clear a specific IRQ */
+            Pic->InServiceRegister &= ~(1 << (Value & PIC_OCW2_NUM_MASK));
+        }
+        else
+        {
+            /* Otherwise, clear all of them */
+            Pic->InServiceRegister = 0;
+        }
+    }
+}
+
+BYTE PicReadData(BYTE Port)
+{
+    /* Read the mask register */
+    if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
+    else return SlavePic.MaskRegister;
+}
+
+VOID PicWriteData(BYTE Port, BYTE Value)
+{
+    PPIC Pic;
+    
+    /* Which PIC are we accessing? */
+    if (Port == PIC_MASTER_DATA) Pic = &MasterPic;
+    else Pic = &SlavePic;
+    
+    /* Is the PIC ready? */
+    if (!Pic->Initialization)
+    {
+        /* Yes, this is an OCW1 */
+        Pic->MaskRegister = Value;
+        return;
+    }
+    
+    /* Has the interrupt offset been set? */
+    if (Pic->IntOffset == 0xFF)
+    {
+        /* This is an ICW2, set the offset (last three bits always zero) */
+        Pic->IntOffset = Value & 0xF8;
+        
+        /* Check if we are in single mode and don't need an ICW4 */
+        if ((Pic->ConfigRegister & PIC_ICW1_SINGLE)
+            && !(Pic->ConfigRegister & PIC_ICW1_ICW4))
+        {
+            /* Yes, done initializing */
+            Pic->Initialization = FALSE;
+        }
+        return;
+    }
+    
+    /* Check if we are in cascade mode and the cascade register was not set */
+    if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
+    {
+        /* This is an ICW3 */
+        Pic->CascadeRegister = Value;
+        Pic->CascadeRegisterSet = TRUE;
+        
+        /* Check if we need an ICW4 */
+        if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
+        {
+            /* No, done initializing */
+            Pic->Initialization = FALSE;
+        }
+        return;
+    }
+    
+    /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
+    if (Value & PIC_ICW4_AEOI)
+    {
+        /* Use automatic end-of-interrupt */
+        Pic->AutoEoi = TRUE;
+    }
+    
+    /* Done initializing */
+    Pic->Initialization = FALSE;
+}
+
+VOID PicInterruptRequest(BYTE Number)
+{
+    if (Number >= 0 && Number < 8)
+    {
+        /* Check if the interrupt is busy or in a cascade */
+        if (MasterPic.CascadeRegister & (1 << Number)
+            || MasterPic.InServiceRegister & (1 << Number))
+        {
+            return;
+        }
+        
+        MasterPic.InServiceRegister |= 1 << Number;
+        EmulatorInterrupt(MasterPic.IntOffset + Number);
+    }
+    else if (Number >= 8 && Number < 16)
+    {
+        Number -= 8;
+        
+        /*
+         * The slave PIC is connected to IRQ 2, always! If the master PIC
+         * was misconfigured, don't do anything.
+         */
+        if (!(MasterPic.CascadeRegister & (1 << 2))
+            || SlavePic.CascadeRegister != 2)
+        {
+            return;
+        }
+        
+        /* Check if the interrupt is busy or in a cascade */
+        if (SlavePic.CascadeRegister & (1 << Number)
+            || SlavePic.InServiceRegister & (1 << Number))
+        {
+            return;
+        }
+        
+        SlavePic.InServiceRegister |= 1 << Number;
+        EmulatorInterrupt(SlavePic.IntOffset + Number);
+    }
+}
index b3bfe50..44555c8 100644 (file)
@@ -23,6 +23,8 @@
 #define MAX_ADDRESS TO_LINEAR(MAX_SEGMENT, MAX_OFFSET)
 #define ROM_AREA_START 0xC0000
 #define ROM_AREA_END 0xFFFFF
+#define BIOS_PIC_MASTER_INT 0x08
+#define BIOS_PIC_SLAVE_INT 0x70
 #define BIOS_SEGMENT 0xF000
 #define VIDEO_BIOS_INTERRUPT 0x10
 #define SPECIAL_INT_NUM 0xFF
 #define CONSOLE_VIDEO_MEM_START 0xB8000
 #define CONSOLE_VIDEO_MEM_END 0xBFFFF
 
+/* Programmable interval timer (PIT) */
+#define PIT_CHANNELS 3
+#define PIT_BASE_FREQUENCY 1193182LL
+#define PIT_DATA_PORT(x) (0x40 + (x))
+#define PIT_COMMAND_PORT 0x43
+
+/* Programmable interrupt controller (PIC) */
+#define PIC_MASTER_CMD 0x20
+#define PIC_MASTER_DATA 0x21
+#define PIC_SLAVE_CMD 0xA0
+#define PIC_SLAVE_DATA 0xA1
+#define PIC_ICW1 0x10
+#define PIC_ICW1_ICW4 (1 << 0)
+#define PIC_ICW1_SINGLE (1 << 1)
+#define PIC_ICW4_8086 (1 << 0)
+#define PIC_ICW4_AEOI (1 << 1)
+#define PIC_OCW2_NUM_MASK 0x07
+#define PIC_OCW2_EOI (1 << 5)
+#define PIC_OCW2_SL (1 << 6)
+#define PIC_OCW3 (1 << 3)
+#define PIC_OCW3_READ_ISR 0x0B
+
 #define EMULATOR_FLAG_CF (1 << 0)
 #define EMULATOR_FLAG_PF (1 << 2)
 #define EMULATOR_FLAG_AF (1 << 4)
@@ -184,6 +208,11 @@ VOID DosInt20h(WORD CodeSegment);
 VOID DosInt21h(WORD CodeSegment);
 VOID DosBreakInterrupt();
 VOID BiosVideoService();
+BYTE PicReadCommand(BYTE Port);
+VOID PicWriteCommand(BYTE Port, BYTE Value);
+BYTE PicReadData(BYTE Port);
+VOID PicWriteData(BYTE Port, BYTE Value);
+VOID PicInterruptRequest(BYTE Number);
 VOID EmulatorSetStack(WORD Segment, WORD Offset);
 VOID EmulatorExecute(WORD Segment, WORD Offset);
 VOID EmulatorInterrupt(BYTE Number);