2013-06-26 22:58:41 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
|
|
* FILE: pic.c
|
|
|
|
* PURPOSE: Programmable Interrupt Controller emulation
|
2013-12-24 15:30:59 +00:00
|
|
|
* (Interrupt Controller Adapter (ICA) in Windows terminology)
|
2013-06-26 22:58:41 +00:00
|
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2013-07-22 13:51:26 +00:00
|
|
|
#define NDEBUG
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
#include "emulator.h"
|
2013-11-26 20:20:51 +00:00
|
|
|
#include "io.h"
|
|
|
|
#include "pic.h"
|
2013-06-26 22:58:41 +00:00
|
|
|
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
|
|
|
|
static PIC MasterPic, SlavePic;
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
static BYTE PicReadCommand(BYTE Port)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2013-10-27 00:37:01 +00:00
|
|
|
/* Read the interrupt request register */
|
|
|
|
return Pic->IntRequestRegister;
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
static VOID PicWriteCommand(BYTE Port, BYTE Value)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
static BYTE PicReadData(BYTE Port)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
|
|
|
/* Read the mask register */
|
|
|
|
if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
|
|
|
|
else return SlavePic.MaskRegister;
|
|
|
|
}
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
static VOID PicWriteData(BYTE Port, BYTE Value)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
static BYTE WINAPI PicReadPort(ULONG Port)
|
2013-11-26 20:20:51 +00:00
|
|
|
{
|
|
|
|
switch (Port)
|
|
|
|
{
|
|
|
|
case PIC_MASTER_CMD:
|
|
|
|
case PIC_SLAVE_CMD:
|
|
|
|
{
|
|
|
|
return PicReadCommand(Port);
|
|
|
|
}
|
|
|
|
|
|
|
|
case PIC_MASTER_DATA:
|
|
|
|
case PIC_SLAVE_DATA:
|
|
|
|
{
|
|
|
|
return PicReadData(Port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
static VOID WINAPI PicWritePort(ULONG Port, BYTE Data)
|
2013-11-26 20:20:51 +00:00
|
|
|
{
|
|
|
|
switch (Port)
|
|
|
|
{
|
|
|
|
case PIC_MASTER_CMD:
|
|
|
|
case PIC_SLAVE_CMD:
|
|
|
|
{
|
|
|
|
PicWriteCommand(Port, Data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PIC_MASTER_DATA:
|
|
|
|
case PIC_SLAVE_DATA:
|
|
|
|
{
|
|
|
|
PicWriteData(Port, Data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
VOID PicInterruptRequest(BYTE Number)
|
|
|
|
{
|
|
|
|
BYTE i;
|
|
|
|
|
2014-01-11 14:40:03 +00:00
|
|
|
if (/* Number >= 0 && */ Number < 8)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2013-12-03 23:50:33 +00:00
|
|
|
/* Check if any of the higher-priority interrupts are busy */
|
2013-12-24 15:30:59 +00:00
|
|
|
for (i = 0; i <= Number; i++)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
|
|
|
if (MasterPic.InServiceRegister & (1 << Number)) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the interrupt is masked */
|
|
|
|
if (MasterPic.MaskRegister & (1 << Number)) return;
|
|
|
|
|
2013-10-27 00:37:01 +00:00
|
|
|
/* Set the appropriate bit in the IRR and interrupt the CPU */
|
|
|
|
MasterPic.IntRequestRegister |= 1 << Number;
|
|
|
|
EmulatorInterruptSignal();
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:50:33 +00:00
|
|
|
/* Check if any of the higher-priority interrupts are busy */
|
2013-06-26 22:58:41 +00:00
|
|
|
if (MasterPic.InServiceRegister != 0) return;
|
2013-10-27 00:37:01 +00:00
|
|
|
for (i = 0; i <= Number; i++)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
|
|
|
if (SlavePic.InServiceRegister & (1 << Number)) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the interrupt is masked */
|
|
|
|
if (SlavePic.MaskRegister & (1 << Number)) return;
|
|
|
|
|
|
|
|
/* Set the IRQ 2 bit in the master ISR */
|
2013-10-27 00:37:01 +00:00
|
|
|
if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << 2);
|
|
|
|
|
|
|
|
/* Set the appropriate bit in the IRR and interrupt the CPU */
|
|
|
|
SlavePic.IntRequestRegister |= 1 << Number;
|
|
|
|
EmulatorInterruptSignal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BYTE PicGetInterrupt(VOID)
|
|
|
|
{
|
|
|
|
INT i, j;
|
|
|
|
|
|
|
|
/* Search interrupts by priority */
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
/* Check if this line is cascaded to the slave PIC */
|
|
|
|
if ((i == 2)
|
|
|
|
&& MasterPic.CascadeRegister & (1 << 2)
|
|
|
|
&& SlavePic.Slave
|
|
|
|
&& (SlavePic.CascadeRegister == 2))
|
|
|
|
{
|
|
|
|
/* Search the slave PIC interrupts by priority */
|
|
|
|
for (j = 0; j < 8; j++) if ((j != 1) && SlavePic.IntRequestRegister & (1 << j))
|
|
|
|
{
|
|
|
|
/* Clear the IRR flag */
|
|
|
|
SlavePic.IntRequestRegister &= ~(1 << j);
|
|
|
|
|
|
|
|
/* Set the ISR flag, unless AEOI is enabled */
|
|
|
|
if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << j);
|
|
|
|
|
|
|
|
/* Return the interrupt number */
|
|
|
|
return SlavePic.IntOffset + j;
|
|
|
|
}
|
|
|
|
}
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2013-10-27 00:37:01 +00:00
|
|
|
if (MasterPic.IntRequestRegister & (1 << i))
|
|
|
|
{
|
|
|
|
/* Clear the IRR flag */
|
|
|
|
MasterPic.IntRequestRegister &= ~(1 << i);
|
|
|
|
|
|
|
|
/* Set the ISR flag, unless AEOI is enabled */
|
|
|
|
if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i);
|
|
|
|
|
|
|
|
/* Return the interrupt number */
|
|
|
|
return MasterPic.IntOffset + i;
|
|
|
|
}
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
2013-10-27 00:37:01 +00:00
|
|
|
|
|
|
|
/* Spurious interrupt */
|
|
|
|
if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7;
|
|
|
|
else return MasterPic.IntOffset + 7;
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
VOID PicInitialize(VOID)
|
2013-11-26 20:20:51 +00:00
|
|
|
{
|
|
|
|
/* Register the I/O Ports */
|
|
|
|
RegisterIoPort(PIC_MASTER_CMD , PicReadPort, PicWritePort);
|
|
|
|
RegisterIoPort(PIC_SLAVE_CMD , PicReadPort, PicWritePort);
|
|
|
|
RegisterIoPort(PIC_MASTER_DATA, PicReadPort, PicWritePort);
|
|
|
|
RegisterIoPort(PIC_SLAVE_DATA , PicReadPort, PicWritePort);
|
|
|
|
}
|
|
|
|
|
2013-12-24 15:30:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
WINAPI
|
|
|
|
call_ica_hw_interrupt(INT ms,
|
|
|
|
BYTE line,
|
|
|
|
INT count)
|
|
|
|
{
|
|
|
|
BYTE InterruptNumber = line;
|
|
|
|
|
|
|
|
/* Check for PIC validity */
|
|
|
|
if (ms != ICA_MASTER || ms != ICA_SLAVE) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust the interrupt request number according to the parameters,
|
|
|
|
* by adding an offset == 8 to the interrupt number.
|
|
|
|
*
|
|
|
|
* Indeed VDDs calling this function usually subtracts 8 so that they give:
|
|
|
|
*
|
|
|
|
* ms | line | corresponding interrupt number
|
|
|
|
* ------------+--------+--------------------------------
|
|
|
|
* ICA_MASTER | 0 -- 7 | 0 -- 7
|
|
|
|
* ICA_SLAVE | 0 -- 7 | 8 -- 15
|
|
|
|
*
|
|
|
|
* and PicInterruptRequest subtracts again 8 to the interrupt number
|
|
|
|
* if it is greater or equal than 8 (so that it determines which PIC
|
|
|
|
* to use via the interrupt number).
|
|
|
|
*/
|
|
|
|
if (ms == ICA_SLAVE) InterruptNumber += 8;
|
|
|
|
|
|
|
|
/* Send the specified number of interrupt requests */
|
|
|
|
while (count-- > 0)
|
|
|
|
{
|
|
|
|
PicInterruptRequest(InterruptNumber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
/* EOF */
|