Implement 8259 Programmable Interrupt Controller emulation.


svn path=/branches/ntvdm/; revision=59268
This commit is contained in:
Aleksandar Andrejevic 2013-06-20 19:00:07 +00:00
parent c1bb00076e
commit b44c5d3a6f
4 changed files with 248 additions and 0 deletions

View file

@ -5,6 +5,7 @@ list(APPEND SOURCE
bios.c
dos.c
emulator.c
hardware.c
ntvdm.c
ntvdm.rc)

View 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;
}

202
subsystems/ntvdm/hardware.c Normal file
View file

@ -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);
}
}

View 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
@ -43,6 +45,28 @@
#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);