mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2011 lines
55 KiB
2011 lines
55 KiB
#include "insignia.h"
|
|
#include "host_def.h"
|
|
|
|
/*
|
|
* O/S include files.
|
|
*/
|
|
#include <stdio.h>
|
|
#include TypesH
|
|
|
|
/*
|
|
* SoftPC Revision 2.0
|
|
*
|
|
* Title : ica.c
|
|
*
|
|
* Description : Interrupt Controller Adapter
|
|
*
|
|
* Author : Jim Hatfield
|
|
* (Upgraded to Rev. 2 by David Rees)
|
|
*
|
|
* Notes : The ICA is responsible for maintaining a mapping
|
|
* between an Interrupt Request line and a vector
|
|
* number defining an entry in the Interrupt Vector
|
|
* table. On reciept of a hardware interrupt, it
|
|
* passes the appropriate vector number to the cpu.
|
|
*
|
|
* The following functions are provided:
|
|
*
|
|
* ica0_init() - Initialise the first ICA (0 = Master)
|
|
* ica1_init() - Initialise the first ICA (1 = Slave)
|
|
* ica_inb() - Read a byte from an ICA register
|
|
* ica_outb() - Write a command (byte) to the ICA
|
|
*
|
|
* ica_hw_interrupt() - Raise a hardware interrupt line
|
|
* ica_clear_int() - Drop an interrupt line
|
|
* ica_intack() - Acknowledge an interrupt
|
|
*
|
|
* If DEBUG is defined, the following function
|
|
* is provided:
|
|
*
|
|
* ica_dump() - printd out contents of one element
|
|
* of adapter_state[]
|
|
*
|
|
* Restrictions : This software emulates an Intel 8259A Priority Interrupt
|
|
* controller as defined in the Intel Specification pp 2-95 to
|
|
* 2-112 and pp 2-114 to 2-181, except for the following:
|
|
*
|
|
* 1) Cascade mode is not supported at all. This mode requires
|
|
* that there is more than one 8259A in a system, whereas
|
|
* the PC/XT has only one.
|
|
*
|
|
* 2) 8080/8085 mode is not supported at all. In this mode the
|
|
* 8259A requires three INTA pulses from the CPU, and an 8088
|
|
* only gives two. This would cause the device to lock up and
|
|
* cease to function.
|
|
*
|
|
* 3) Level triggered mode is not supported. The device is
|
|
* assumed to operate in edge triggered mode. A call of
|
|
* ica_hw_interrupt by another adapter will cause a bit to
|
|
* be latched into the Interrupt Request Register. A subsequent
|
|
* call of ica_clear_int will cause the bit to be unlatched.
|
|
*
|
|
* 4) Buffered mode has no meaning in a software emulation and
|
|
* so is ignored.
|
|
*
|
|
* 5) An enhancement is provided such that an adapter may raise
|
|
* more than one interrupt in one call of ica_hw_interrupt.
|
|
* The effect of this is that as soon as an INTACK is called
|
|
* another interrupt is requested. If the chip is in Automatic
|
|
* EOI mode then all of the interrupts will be generated in
|
|
* one burst.
|
|
*
|
|
* 5a) A further enhancement is provided such that a delay
|
|
* (a number of Intel instructions) can be requested before
|
|
* the interrupt takes effect. This delay applies to every
|
|
* interrupt if more than one is requested.
|
|
*
|
|
* 6) Special Fully Nested mode is not supported, since it is
|
|
* a submode of Cascade Mode.
|
|
*
|
|
* 7) Polling is not completely implemented. When a Poll is
|
|
* received and there was an interrupt request, the CPU INT
|
|
* line (which must have been high) is pulled low. This
|
|
* software does NOT reset the jump table address since there
|
|
* may be a software interrupt outstanding. However it does
|
|
* remove the evidence of a hardware interrupt, which will
|
|
* cause the CPU to reset the table address itself.
|
|
*
|
|
* When an unsupported mode is set, it is remembered for
|
|
* diagnostic purposes, even though it is not acted upon.
|
|
*
|
|
* Modifications for Revision 2.0 :
|
|
* 1) Restrictions 1 and 6 are lifted. The PC-AT contains two
|
|
* 8259A interrupt controllers. The first (ICA 0) is in Master
|
|
* mode, and the second (ICA 1) is in slave mode, and its
|
|
* interrupts are directed to IR2 on the master chip. Hence
|
|
* cascade mode must be supported to the extent necessary
|
|
* to emulate this situation. Also, Special Fully Nested
|
|
* Mode must work too. NB. The AT BIOS does NOT initialise
|
|
* the Master 8259A to use Special Fully Nested Mode.
|
|
*
|
|
* 2) Restriction 5a (which is an enhancement) has been
|
|
* eliminated. Apparently this never really achieved
|
|
* its aim.
|
|
*
|
|
* 3) All the static variables declared in this module
|
|
* have been placed within a structure, ADAPTER_STATE,
|
|
* which is used as the type for a two-element array.
|
|
* This allows the code to emulate two 8259As.
|
|
*
|
|
* 4) The routine ica_standard_vector_address() has been
|
|
* eliminated, because it is not used anymore.
|
|
*
|
|
* 5) The function ica_init() has been split into two:
|
|
* ica0_init() and ica1_init(). The initialization
|
|
* via ICWs will now be done by a BIOS POST routine.
|
|
*
|
|
* 6) In the PC-AT, an 8259A determines its Master/Slave
|
|
* state by examining the state of the SP/EN pin. We
|
|
* simulate this by setting a flag 'ica_master' to
|
|
* the appropriate value in the ica_init() routines.
|
|
*
|
|
* 7) The guts of the exported function ica_intack()
|
|
* have been placed in an internal routine,
|
|
* ica_accept(). This change allows for the INTAs
|
|
* to work for both the master and slave 8259As.
|
|
*
|
|
* 8) Added debug function (ica_dump) to allow module
|
|
* testing.
|
|
*
|
|
*/
|
|
|
|
#ifdef SCCSID
|
|
LOCAL char SccsID[]="@(#)ica.c 1.31 10/7/94 Copyright Insignia Solutions Ltd.";
|
|
#endif
|
|
|
|
#ifdef SEGMENTATION
|
|
/*
|
|
* The following #include specifies the code segment into which this
|
|
* module will by placed by the MPW C compiler on the Mac II running
|
|
* MultiFinder.
|
|
*/
|
|
#include "SOFTPC_ICA.seg"
|
|
#endif
|
|
|
|
/* these ports have iret hooks enabled automatically - others will have to
|
|
* define hooked_irets as parts of their configuration
|
|
*/
|
|
#if defined(CPU_40_STYLE) || defined(NTVDM) || defined(GISP_CPU)
|
|
#define HOOKED_IRETS /* switch on IRET hooks */
|
|
#endif
|
|
/*
|
|
* SoftPC include files
|
|
*/
|
|
#include "xt.h"
|
|
#include "trace.h"
|
|
#include "ios.h"
|
|
#include CpuH
|
|
#include "ica.h"
|
|
#include "host.h"
|
|
#include "yoda.h"
|
|
#include "debug.h"
|
|
|
|
#ifdef NTVDM
|
|
#include "nt_eoi.h"
|
|
#endif
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Local Data
|
|
* ============================================================================
|
|
*/
|
|
|
|
/*
|
|
* Table of function pointers to access PIC routines
|
|
*/
|
|
void (*ica_inb_func) IPT2(io_addr, port, IU8 *, value);
|
|
void (*ica_outb_func) IPT2(io_addr, port, IU8, value);
|
|
void (*ica_hw_interrupt_func) IPT3(IU32, adapter, IU32, line_no,
|
|
IS32, call_count);
|
|
#ifdef DELAYED_INTS
|
|
void (*ica_hw_interrupt_delay_func) IPT4(IU32, adapter, IU8, line_no,
|
|
IS32, call_count, IS32, delay);
|
|
#endif
|
|
void (*ica_clear_int_func) IPT2(IU32, adapter, IU32, line_no);
|
|
|
|
/*
|
|
* This variable only appears to be accessed local to this module, so make it
|
|
* local for 4.0 rather than depending on xt.c to define it. If anyone
|
|
* really needs to set it globally, they should put it into ica.h rather than
|
|
* kludging our nice clean cpu interface. So there.
|
|
*/
|
|
#ifdef CPU_40_STYLE
|
|
LOCAL IUM8 ica_lock;
|
|
|
|
#define MAX_ISR_DEPTH 2
|
|
#define ICA_INTACK_REJECT -1
|
|
#endif
|
|
|
|
#ifndef PROD
|
|
char icamsgbuf[132]; /* Text buffer for debug messages */
|
|
#endif
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Data relating to 8259A is replicated in two element array.
|
|
* ============================================================================
|
|
*/
|
|
|
|
#ifdef NTVDM
|
|
/*
|
|
* NT VDM has to export ICA definition for host and for X86 kernel. To make the
|
|
* namings of these externally referenced type/var more sensible but leaving
|
|
* the code substantially as the SoftPC base, use macros to 'edit' the
|
|
* adapter_state variable and type.
|
|
*/
|
|
VDMVIRTUALICA VirtualIca[2];
|
|
#define adapter_state VirtualIca
|
|
#define ADAPTER_STATE VDMVIRTUALICA
|
|
|
|
#define EOI_HOOKS /* switch on EOI hooks */
|
|
|
|
/* iret hook related defines */
|
|
#ifndef MONITOR
|
|
/* only valid in real mode or when enabled from DPMI */
|
|
#define host_valid_iret_hook() (!getPE() || iretHooksEnabled)
|
|
#define host_bop_style_iret_hooks() (FALSE)
|
|
#else
|
|
#define host_valid_iret_hook() (TRUE)
|
|
#define host_bop_style_iret_hooks() (TRUE)
|
|
|
|
#endif
|
|
extern ULONG DelayIrqLine; /* see nt_eoi.c, delayed ints */
|
|
BOOL PMEmulHookEnabled = FALSE; /* PM iret hooks for emulator on/off */
|
|
#define host_ica_real_locks() (TRUE)
|
|
#else /* !NTVDM */
|
|
|
|
/* regular SoftPC definitions for ica */
|
|
ADAPTER_STATE adapter_state[2];
|
|
|
|
/* iret hook related defines */
|
|
#ifdef PROD
|
|
#define host_valid_iret_hook() (TRUE)
|
|
#else /* allow disabling of iret hooks whilst debugging */
|
|
#define host_valid_iret_hook() (iretHooksEnabled)
|
|
#endif
|
|
#define host_bop_style_iret_hooks() (FALSE)
|
|
#define host_iret_bop_table_addr(line) (0)
|
|
|
|
/* crit section style locks only available on NT */
|
|
#define host_ica_lock()
|
|
#define host_ica_unlock()
|
|
#define host_ica_real_locks() (FALSE)
|
|
|
|
#endif /* NTVDM */
|
|
|
|
#ifdef HOOKED_IRETS /* iret hook related variables */
|
|
LOCAL IBOOL iretHooksEnabled = FALSE;
|
|
LOCAL IU16 iretHookMask = 0; /* No interrupts hooked by default */
|
|
#endif
|
|
LOCAL IU16 iretHookActive = 0;
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Local defines
|
|
* ============================================================================
|
|
*/
|
|
|
|
#define ICA_BASE_MASK 0xf8 /* Mask to get relevant bits out */
|
|
|
|
/*
|
|
* The following defines describe the usage of the mode bits
|
|
*/
|
|
|
|
#define ICA_IC4 0x0001 /* 0 -> no ICW4, 1 -> ICW4 will be sent */
|
|
#define ICA_SINGL 0x0002 /* 0 -> cascade, 1 -> single mode */
|
|
#define ICA_ADI 0x0004 /* 0 -> 8 byte, 1 -> 4 byte interval */
|
|
#define ICA_LTIM 0x0008 /* 0 -> edge, 1 -> level trigger */
|
|
#define ICA_ICW1_MASK 0x000f /* Mask to select above bits in mode */
|
|
|
|
#define ICA_MPM 0x0010 /* 0 -> 8080, 1 -> 8086/8088 mode */
|
|
#define ICA_AEOI 0x0020 /* 1 -> Automatic End-Of-Int Mode is on */
|
|
#define ICA_MS 0x0040 /* 0 -> slave, 1 -> master mode */
|
|
#define ICA_BUF 0x0080 /* 1 -> Buffered Mode is on */
|
|
#define ICA_SFNM 0x0100 /* 1 -> Special Fully Nested Mode is on */
|
|
#define ICA_ICW4_MASK 0x01f0 /* Mask to select above bits in mode */
|
|
|
|
#define ICA_SMM 0x0200 /* 1 -> Special Mask Mode is on */
|
|
#define ICA_RAEOI 0x0400 /* 1 -> Rotate on Auto EOI Mode is on */
|
|
#define ICA_RIS 0x0800 /* 0 -> deliver IRR, 1 -> deliver ISR */
|
|
#define ICA_POLL 0x1000 /* 1 -> Polling is now in progress */
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Macros
|
|
* ============================================================================
|
|
*/
|
|
#define ICA_PORT_0 \
|
|
(adapter ? ICA1_PORT_0 : ICA0_PORT_0)
|
|
|
|
#define ICA_PORT_1 \
|
|
(adapter ? ICA1_PORT_1 : ICA0_PORT_1)
|
|
|
|
#define adapter_for_port(port) \
|
|
((port >= ICA0_PORT_START && port <= ICA0_PORT_END) \
|
|
? ICA_MASTER \
|
|
: ICA_SLAVE \
|
|
)
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Internal functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
LOCAL void ica_interrupt_cpu IPT2(IU32, adapter, IU32, line);
|
|
IU8 ica_scan_irr IPT1(IU32, adapter);
|
|
IS32 ica_accept IPT1(IU32, adapter);
|
|
|
|
#ifdef HOOKED_IRETS
|
|
extern IU32 ica_iret_hook_needed IPT1(IU32, line);
|
|
#endif
|
|
|
|
void
|
|
SWPIC_init_funcptrs IFN0()
|
|
{
|
|
/*
|
|
* initialize PIC access functions for SW [emulated] PIC
|
|
*/
|
|
ica_inb_func = SWPIC_inb;
|
|
ica_outb_func = SWPIC_outb;
|
|
#ifdef DELAYED_INTS
|
|
ica_hw_interrupt_delay_func = SWPIC_hw_interrupt_delay;
|
|
#endif
|
|
ica_hw_interrupt_func = SWPIC_hw_interrupt;
|
|
ica_clear_int_func = SWPIC_clear_int;
|
|
}
|
|
|
|
/*
|
|
* Please note that ica_eoi is called by SUN_VA code and thus needs to
|
|
* be global.
|
|
*/
|
|
|
|
void ica_eoi IFN3(IU32, adapter, IS32 *, line, IBOOL, rotate)
|
|
{
|
|
/*
|
|
* End Of Interrupt. If *line is -1, this is a non-specific EOI
|
|
* otherwise it is the number of the line to clear. If rotate is
|
|
* TRUE, then set the selected line to lowest priority.
|
|
*/
|
|
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
IS32 i, j;
|
|
IU8 bit;
|
|
IS32 EoiLineNo = -1; /* if EOI_HOOKS defined this is used otherwise */
|
|
/* rely on compiler elimination as not read */
|
|
|
|
if (*line == -1) /* non specific EOI */
|
|
{
|
|
/*
|
|
* Clear the highest priority bit in the ISR
|
|
*/
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
j = (asp->ica_hipri + i) & 7;
|
|
bit = (1 << j);
|
|
if (asp->ica_isr & bit)
|
|
{
|
|
asp->ica_isr &= ~bit;
|
|
*line = j;
|
|
EoiLineNo = (IS32)*line;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else /* EOI on specific line */
|
|
{
|
|
bit = 1 << *line;
|
|
if (asp->ica_isr & bit)
|
|
EoiLineNo = *line;
|
|
asp->ica_isr &= ~bit;
|
|
}
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** CPU END-OF-INT (%d) ****", *line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
if (rotate && (*line >= 0))
|
|
asp->ica_hipri = (*line + 1) & 0x07;
|
|
|
|
#ifdef EOI_HOOKS
|
|
/*
|
|
* CallOut to device registered EOI Hooks
|
|
*/
|
|
if (EoiLineNo != -1)
|
|
host_EOI_hook(EoiLineNo + (adapter << 3), asp->ica_count[EoiLineNo]);
|
|
#endif /* EOI_HOOKS */
|
|
|
|
/*
|
|
* There may be a lower priority interrupt pending, so check
|
|
*/
|
|
if ((i = ica_scan_irr(adapter)) & 0x80)
|
|
ica_interrupt_cpu(adapter, i & 0x07);
|
|
}
|
|
|
|
IU8 ica_scan_irr IFN1(IU32, adapter)
|
|
{
|
|
/*
|
|
* This is the routine which will decide whether an interrupt should
|
|
* be generated. It scans the IRR, the IMR and the ISR to determine
|
|
* whether one is possible. It is also called when the processor has
|
|
* accepted the interrupt to see which one to deliver.
|
|
*
|
|
* A bit set in the IRR will generate an interrupt if:
|
|
*
|
|
* 1) The corresponding bit in the IMR is clear
|
|
* AND
|
|
* 2) The corresponding bit and all higher priority bits in the ISR are
|
|
* clear (unless Special Mask Mode, in which case ISR is ignored)
|
|
*
|
|
* The highest priority set bit which meets the above conditions (if any)
|
|
* will be returned with an indicator bit (in the style needed by a Poll)
|
|
*/
|
|
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
IU32 i, j;
|
|
IU8 bit, irr_and_not_imr;
|
|
IU8 iret_hook_mask;
|
|
|
|
/* if iret hooks are not being used, iretHookActive will always be 0 */
|
|
iret_hook_mask = (IU8)(iretHookActive >> (adapter << 3));
|
|
|
|
#ifdef NTVDM
|
|
iret_hook_mask |= (IU8)(DelayIrqLine >> (adapter << 3));
|
|
#endif
|
|
/*
|
|
* A bit can only cause an int if it is set in the IRR
|
|
* and clear in the IMR. Generate a set of such bits
|
|
*/
|
|
|
|
irr_and_not_imr = asp->ica_irr & ~(asp->ica_imr | iret_hook_mask);
|
|
|
|
/*
|
|
* Check the trivial case first: no bits set
|
|
*/
|
|
|
|
if (!irr_and_not_imr)
|
|
return(7);
|
|
|
|
/*
|
|
* Handle Special Mask Mode separately, to avoid a test in the loop
|
|
*/
|
|
|
|
if (asp->ica_mode & ICA_SMM)
|
|
{
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
j = (asp->ica_hipri + i) & 7;
|
|
|
|
if (irr_and_not_imr & (1 << j))
|
|
return(0x80 + j); /* Return line no. + indicator */
|
|
}
|
|
}
|
|
else
|
|
#ifndef DELAYED_INTS /* normal code */
|
|
{
|
|
if (asp->ica_mode & ICA_SFNM)
|
|
return(7); /* No interrupt possible */
|
|
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
j = (asp->ica_hipri + i) & 7;
|
|
bit = (1 << j);
|
|
if (asp->ica_isr & bit)
|
|
{
|
|
if (irr_and_not_imr & bit)
|
|
return(0x80 + j);/* Return line no. + indicator */
|
|
return(7); /* No interrupt possible */
|
|
}
|
|
|
|
if (irr_and_not_imr & bit)
|
|
return(0x80 + j); /* Return line no. + indicator */
|
|
}
|
|
/* Strange. We should not have got here. */
|
|
return(7);
|
|
}
|
|
#else /* DELAYED_INTS */
|
|
{
|
|
IU32 jt;
|
|
IS32 tdelay;
|
|
|
|
/* Some very dubious code here - attempt to ignore high priority
|
|
* delayed interrupts if there is a lower priority one with less
|
|
* delay */
|
|
j=8;tdelay=999999;
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
jt = (asp->ica_hipri + i) & 7;
|
|
bit = (1 << jt);
|
|
if (asp->ica_isr & bit)
|
|
{
|
|
return(7); /* No interrupt possible */
|
|
}
|
|
|
|
if (irr_and_not_imr & bit)
|
|
if(asp->ica_curr_delay[jt] < tdelay)
|
|
{
|
|
j=jt;tdelay=asp->ica_curr_delay[jt];
|
|
}
|
|
}
|
|
if(j ^= 8)
|
|
return(0x80 + j); /* Return line no. + indicator */
|
|
else
|
|
/* Strange. We should not have got here. */
|
|
return(7);
|
|
}
|
|
#endif /* DELAYED_INTS */
|
|
}
|
|
|
|
|
|
IS32 ica_accept IFN1(IU32, adapter)
|
|
{
|
|
/*
|
|
* NOTE: There is no need to set the lock here, since we are called
|
|
* either from the cpu interrupt code, or from ica_inb, both of
|
|
* which will have set it for us.
|
|
*/
|
|
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
IU32 line1;
|
|
IS32 line2;
|
|
IU8 bit;
|
|
|
|
/*
|
|
* Drop the INT line
|
|
*/
|
|
|
|
asp->ica_cpu_int = FALSE;
|
|
|
|
/*
|
|
* Scan the IRR to find the line which we will use.
|
|
* There should be one set, but check anyway
|
|
* It there isn't, use line 7.
|
|
*/
|
|
|
|
if (!((line1 = (IU32)ica_scan_irr(adapter)) & 0x80))
|
|
{
|
|
#if defined(NTVDM) && defined(MONITOR)
|
|
/* Skip spurious ints. These are any that are caused by clearing an
|
|
* int when the cpu has already registered that there is an int to
|
|
* service. This change tries to remove the performance impact on
|
|
* the monitor.
|
|
*/
|
|
return(-1);
|
|
#endif
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_int_accept: No interrupt found!!", DUMP_NONE);
|
|
#endif
|
|
line1 = 7;
|
|
}
|
|
else
|
|
{
|
|
line1 &= 0x07;
|
|
|
|
#ifdef CPU_40_STYLE
|
|
/* allow some recursion within hooked ISRs */
|
|
if (asp->isr_depth[line1] >= MAX_ISR_DEPTH)
|
|
{
|
|
/* disable further interrupts on this line */
|
|
iretHookActive |= 1 << ((adapter << 3) + line1);
|
|
|
|
/* do lost iret test here */
|
|
asp->isr_max_depth_req[line1]++;
|
|
|
|
/* reached maximum ISR recursion - don't do interrupt */
|
|
return((IS32)-1);
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
bit = (1 << line1);
|
|
asp->ica_isr |= bit;
|
|
|
|
#ifndef DELAYED_INTS
|
|
|
|
if (--(asp->ica_count[line1]) <= 0)
|
|
{ /* If count exhausted for this line */
|
|
asp->ica_irr &= ~bit; /* Then finally clear IRR bit */
|
|
asp->ica_count[line1] = 0; /* Just in case */
|
|
}
|
|
|
|
#else /* DELAYED_INTS */
|
|
if (--(asp->ica_count[line1]) <= 0)
|
|
{ /* If count exhausted for this line */
|
|
asp->ica_irr &= ~bit; /* Then finally clear IRR bit */
|
|
asp->ica_count[line1] = 0; /* Just in case */
|
|
asp->ica_curr_delay[line1]=0; /* Just in case */
|
|
asp->ica_delay[line1] = 0; /* Just in case */
|
|
}
|
|
else /* reset delay count */
|
|
asp->ica_curr_delay[line1] = asp->ica_delay[line1];
|
|
#endif /* DELAYED_INTS */
|
|
|
|
}
|
|
|
|
/*
|
|
* If we are in Automatic EOI mode, then issue a non-specific EOI
|
|
*/
|
|
|
|
if (asp->ica_mode & ICA_AEOI)
|
|
{
|
|
line2 = -1;
|
|
ica_eoi(adapter, &line2, (asp->ica_mode & ICA_RAEOI) == ICA_RAEOI);
|
|
}
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** CPU INTACK (%d) ****", line1 + asp->ica_base);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
return((IS32)line1);
|
|
}
|
|
|
|
LOCAL void ica_interrupt_cpu IFN2(IU32, adapter, IU32, line)
|
|
{
|
|
/*
|
|
* This routine actually interrupts the CPU. The method it does this
|
|
* is host specific, and is done in host_cpu_interrupt().
|
|
*/
|
|
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
|
|
/*
|
|
* If the INT line is already high, do nothing.
|
|
*/
|
|
|
|
if (asp->ica_cpu_int)
|
|
{
|
|
#ifndef PROD
|
|
if ((io_verbose & ICA_VERBOSE) && (asp->ica_int_line != line))
|
|
{
|
|
sprintf(icamsgbuf,"******* INT LINE ALREADY HIGH line=%d ****", asp->ica_int_line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
asp->ica_int_line = line;
|
|
|
|
#ifndef DELAYED_INTS
|
|
return;
|
|
#else
|
|
/* save delay for old int */
|
|
asp->ica_curr_delay[asp->ica_int_line]=cpu_int_delay;
|
|
#endif /* DELAYED_INTS */
|
|
|
|
}
|
|
|
|
/*
|
|
* Set the ICA internal flags
|
|
*/
|
|
|
|
asp->ica_int_line = line;
|
|
asp->ica_cpu_int = TRUE;
|
|
|
|
#ifdef DELAYED_INTS
|
|
cpu_int_delay = asp->ica_curr_delay[line];
|
|
#endif /* DELAYED_INTS */
|
|
|
|
if (asp->ica_master) /* If ICA is Master */
|
|
{
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** CPU INTERRUPT (%x) ****", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Set the 'hardware interrupt' bit in cpu_interrupt_map
|
|
*/
|
|
|
|
#ifndef DELAYED_INTS
|
|
#ifndef CPU_40_STYLE /* No globals in the 4.0 I/F! */
|
|
cpu_int_delay = 0;
|
|
#endif
|
|
#else /* DELAYED_INTS */
|
|
cpu_int_delay = asp->ica_curr_delay[line];
|
|
#endif /* DELAYED_INTS */
|
|
|
|
host_set_hw_int();
|
|
|
|
#ifdef A2CPU
|
|
host_cpu_interrupt();
|
|
#endif
|
|
|
|
#ifdef NTVDM
|
|
/* call wow routine to check for application unable to service ints */
|
|
if (WOWIdleRoutine)
|
|
(*WOWIdleRoutine)();
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{ /* If ICA is Slave */
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** SLAVE ICA INTERRUPT (%x) ****", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
/*
|
|
* Signal the Master ICA.
|
|
* NB. A kludge is used here. We know that we have
|
|
* been called from ica_hw_interrupt(), and
|
|
* therefore ica_lock will be at least 1. To
|
|
* get the effect we want, it is necessary to
|
|
* reduce the value of ica_lock for the duration
|
|
* of the call to ica_hw_interrupt.
|
|
*
|
|
* If the host has implemented critical section style locking
|
|
* then the above kludge does not apply.
|
|
*/
|
|
|
|
ica_lock--;
|
|
|
|
#ifndef DELAYED_INTS
|
|
|
|
ica_hw_interrupt(ICA_MASTER, asp->ica_ssr, 1);
|
|
|
|
#else /* DELAYED_INTS */
|
|
adapter_state[ICA_MASTER].ica_curr_delay[line] = asp->ica_curr_delay[line];
|
|
adapter_state[ICA_MASTER].ica_delay[line] = asp->ica_delay[line];
|
|
ica_hw_interrupt_delay(ICA_MASTER, asp->ica_ssr, 1, cpu_int_delay);
|
|
#endif /* DELAYED_INTS */
|
|
|
|
ica_lock++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ============================================================================
|
|
* External functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
void SWPIC_inb IFN2(io_addr, port, IU8 *, value)
|
|
{
|
|
#ifndef PROD
|
|
char *reg_name;
|
|
#endif /* nPROD */
|
|
IU32 adapter = adapter_for_port(port);
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
|
|
/*
|
|
* First check the validity of the port
|
|
*/
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
if ((port != ICA_PORT_0) && (port != ICA_PORT_1))
|
|
{
|
|
sprintf(icamsgbuf, "ica_inb: bad port (%x)", port);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If we are in the middle of a Poll command, then respond to it
|
|
*/
|
|
|
|
if (asp->ica_mode & ICA_POLL)
|
|
{
|
|
ica_lock = 1; /* Lock out signal handlers */
|
|
host_ica_lock(); /* real lock if supported */
|
|
|
|
asp->ica_mode &= ~ICA_POLL;
|
|
|
|
if ((*value = ica_scan_irr(adapter)) & 0x80) /* See if there is one */
|
|
{
|
|
(void) ica_accept(adapter); /* Acknowledge it */
|
|
host_clear_hw_int();
|
|
/* cpu_int_call_count[0] = 0; Not used anymore */
|
|
}
|
|
|
|
ica_lock = 0;
|
|
host_ica_unlock(); /* free lock if supported */
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_inb: responding to Poll with %x", *value);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* If the address is ICA_PORT_0, then deliver either the IRR or the ISR,
|
|
* depending on the setting of mode bit ICA_RIS. If the address is
|
|
* ICA_PORT_1, then deliver the IMR
|
|
*/
|
|
|
|
else
|
|
{
|
|
if (port == ICA_PORT_0)
|
|
if (asp->ica_mode & ICA_RIS)
|
|
{
|
|
*value = asp->ica_isr;
|
|
#ifndef PROD
|
|
reg_name = "ISR";
|
|
#endif /* nPROD */
|
|
}
|
|
else
|
|
{
|
|
*value = asp->ica_irr;
|
|
#ifndef PROD
|
|
reg_name = "IRR";
|
|
#endif /* nPROD */
|
|
}
|
|
else
|
|
{
|
|
*value = asp->ica_imr;
|
|
#ifndef PROD
|
|
reg_name = "IMR";
|
|
#endif /* nPROD */
|
|
}
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_inb: delivered %s value %x", reg_name, *value);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void SWPIC_outb IFN2(io_addr, port, IU8, value)
|
|
{
|
|
|
|
/*
|
|
* Data sent may either be ICWs or OCWs. All of the OCWs are recognisable
|
|
* individually, but only ICW1 may be recognised directly. It will always
|
|
* be followed by ICW2, and optionally by ICW3 and/or ICW4, depending upon
|
|
* exactly what sort of ICW1 was sent. We use a sequence variable to track
|
|
* this and make sure we interpret the data correctly. After power-on, we
|
|
* ignore everything until we get an ICW1.
|
|
*/
|
|
|
|
/*
|
|
* Some defines to detect command types
|
|
*/
|
|
#define ICA_SMM_CMD 0x40
|
|
#define ICA_POLL_CMD 0x04
|
|
#define ICA_RR_CMD 0x02
|
|
|
|
/*
|
|
* Local variables
|
|
*/
|
|
IU32 adapter = adapter_for_port(port);
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
|
|
SAVED IS32 sequence[2] /* -1 -> power is on but no ICWs received */
|
|
= { -1, -1 }; /* 0 -> fully initialised, OK to proceed */
|
|
/* 2 -> ICW1 received, awaiting ICW2 */
|
|
/* 3 -> ICW2 received, awaiting ICW3 */
|
|
/* 4 -> awaiting ICW4 */
|
|
|
|
IU32 i; /* Counter */
|
|
IS32 line; /* Interrupt line number */
|
|
|
|
/*
|
|
* First check the validity of the port
|
|
*/
|
|
|
|
if ((port & 0xfffe) != ICA_PORT_0)
|
|
{
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: bad port (%x)", port);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If we get an ICW1 then we are into initialisation
|
|
*/
|
|
|
|
if (((port & 1) == 0) && (value & 0x10)) /**** ICW1 ****/
|
|
{
|
|
asp->ica_irr = 0; /* Clear all pending interrupts */
|
|
asp->ica_isr = 0; /* Clear all in-progress interrupts */
|
|
asp->ica_imr = 0; /* Clear the mask register */
|
|
asp->ica_ssr = 0; /* No slaves selected */
|
|
asp->ica_base = 0; /* No base address */
|
|
|
|
asp->ica_hipri = 0; /* Line 0 is highest priority */
|
|
|
|
asp->ica_mode = value & ICA_ICW1_MASK;
|
|
/* Set supplied mode bits from ICW1 */
|
|
|
|
for(i = 0; i < 8; i++)
|
|
asp->ica_count[i] = 0; /* Clear IRR extension */
|
|
|
|
asp->ica_cpu_int = FALSE; /* No CPU INT outstanding */
|
|
sequence[adapter] = 2; /* Prepare for the rest of the sequence */
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_outb: ICW1 detected, initialisation begins", DUMP_NONE);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/**/
|
|
|
|
/*
|
|
* Lock out calls from signal handlers
|
|
*/
|
|
|
|
ica_lock = 1;
|
|
host_ica_lock(); /* real lock if supported */
|
|
|
|
/*
|
|
* It wasn't an ICW1, so use the sequence variable to direct our activities
|
|
*/
|
|
|
|
switch(sequence[adapter])
|
|
{
|
|
case 0: /* We are expecting an OCW */
|
|
if (port & 1) /* Odd address -> OCW1 */
|
|
{
|
|
asp->ica_imr = value & 0xff;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: new IMR: %x", value);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
if (asp->ica_cpu_int)
|
|
{
|
|
/* We might have masked out a pending interrupt */
|
|
if (asp->ica_imr & (1 << asp->ica_int_line))
|
|
{
|
|
asp->ica_cpu_int = FALSE; /* No CPU INT outstanding */
|
|
if (asp->ica_master)
|
|
host_clear_hw_int();
|
|
else
|
|
ica_clear_int(ICA_MASTER,asp->ica_ssr);
|
|
}
|
|
}
|
|
/*
|
|
* We might have unmasked a pending interrupt
|
|
*/
|
|
if (!asp->ica_cpu_int && (line = ica_scan_irr(adapter)) & 0x80)
|
|
ica_interrupt_cpu(adapter, line & 0x07); /* Generate interrupt */
|
|
}
|
|
else
|
|
/**/
|
|
if ((value & 8) == 0) /* Bit 3 unset -> OCW2 */
|
|
{
|
|
switch ((value >> 5) & 0x07)
|
|
{
|
|
case 0: /* Clear rotate in auto EOI */
|
|
asp->ica_mode &= ~ICA_RAEOI;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_outb: Clear Rotate in Auto EOI",DUMP_NONE);
|
|
#endif
|
|
break;
|
|
|
|
case 1: /* Non-specific EOI */
|
|
line = -1; /* -1 -> highest priority */
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_outb: Non-specific EOI", DUMP_NONE);
|
|
#endif
|
|
ica_eoi(adapter, &line, FALSE);
|
|
break;
|
|
|
|
case 2: /* No operation */
|
|
break;
|
|
|
|
case 3: /* Specific EOI command */
|
|
line = value & 0x07;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: Specific EOI, line %d", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
ica_eoi(adapter, &line, FALSE);
|
|
break;
|
|
|
|
case 4: /* Set rotate in auto EOI mode */
|
|
asp->ica_mode |= ICA_RAEOI;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_outb: Set Rotate in Auto EOI",DUMP_NONE);
|
|
#endif
|
|
break;
|
|
|
|
case 5: /* Rotate on non-specific EOI */
|
|
line = -1; /* -1 -> non specific */
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_outb: Rotate on Non-specific EOI",DUMP_NONE);
|
|
#endif
|
|
ica_eoi(adapter, &line, TRUE);
|
|
break;
|
|
|
|
case 6: /* Set priority */
|
|
asp->ica_hipri = (value + 1) & 0x07;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: Set Priority, line %d", value & 0x07);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case 7: /* Rotate on specific EOI */
|
|
line = value & 0x07;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: Rotate on specific EOI, line %d", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
ica_eoi(adapter, &line, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
/**/
|
|
else /* Bit 3 set -> OCW3 */
|
|
{
|
|
if (value & ICA_SMM_CMD) /* Set/unset SMM */
|
|
{
|
|
asp->ica_mode = (asp->ica_mode & ~ICA_SMM) | (((IU16)value << 4) & ICA_SMM);
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
if (asp->ica_mode & ICA_SMM)
|
|
trace("ica_outb: Special Mask Mode set", DUMP_NONE);
|
|
else
|
|
trace("ica_outb: Special Mask Mode unset", DUMP_NONE);
|
|
#endif
|
|
}
|
|
|
|
if (value & ICA_POLL_CMD) /* We are being polled */
|
|
{
|
|
asp->ica_mode |= ICA_POLL;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
trace("ica_outb: Poll detected!", DUMP_NONE);
|
|
#endif
|
|
}
|
|
else
|
|
if (value & ICA_RR_CMD) /* Select IRR or ISR */
|
|
{
|
|
asp->ica_mode = (asp->ica_mode & ~ICA_RIS) | (((IU16)value << 11) & ICA_RIS);
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
if (asp->ica_mode & ICA_RIS)
|
|
trace("ica_outb: ISR selected", DUMP_NONE);
|
|
else
|
|
trace("ica_outb: IRR selected", DUMP_NONE);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
|
|
/**/
|
|
case 2: /* We are expecting a ICW2 */
|
|
if (!(port & 1)) /* Should be odd address, so check */
|
|
{
|
|
#ifndef PROD
|
|
sprintf(icamsgbuf, "ica_outb: bad port (%x) while awaiting ICW2",
|
|
(unsigned)port);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
asp->ica_base = value & ICA_BASE_MASK;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: vector base set to %x", asp->ica_base);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
if (!(asp->ica_mode & ICA_SINGL))
|
|
sequence[adapter] = 3;
|
|
else
|
|
if (asp->ica_mode & ICA_IC4)
|
|
sequence[adapter] = 4;
|
|
else
|
|
sequence[adapter] = 0;
|
|
}
|
|
break;
|
|
|
|
/**/
|
|
case 3: /* We are expecting a ICW3 */
|
|
if (!(port & 1)) /* Should be odd address, so check */
|
|
{
|
|
#ifndef PROD
|
|
sprintf(icamsgbuf, "ica_outb: bad port (%x) while awaiting ICW3",
|
|
(unsigned)port);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
asp->ica_ssr = value & 0xff;
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: slave register set to %x", asp->ica_ssr);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
if (asp->ica_mode & ICA_IC4)
|
|
sequence[adapter] = 4;
|
|
else
|
|
sequence[adapter] = 0;
|
|
}
|
|
break;
|
|
|
|
/**/
|
|
case 4: /* We are expecting a ICW4 */
|
|
if (!(port & 1)) /* Should be odd address, so check */
|
|
{
|
|
#ifndef PROD
|
|
sprintf(icamsgbuf, "ica_outb: bad port (%x) while awaiting ICW4",
|
|
(unsigned)port);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
asp->ica_mode = (asp->ica_mode & ~ICA_ICW4_MASK)
|
|
| (((IU16)value << 4) & ICA_ICW4_MASK);
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "ica_outb: IC4 value %x", value);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
/*
|
|
* Check the mode bits for sensible values
|
|
*/
|
|
if (!(asp->ica_mode & ICA_MPM))
|
|
trace("ica_outb: attempt to set up 8080 mode!", DUMP_NONE);
|
|
|
|
if ((asp->ica_mode & ICA_BUF) && !(asp->ica_mode & ICA_MS)
|
|
&& !(asp->ica_mode & ICA_SINGL))
|
|
trace("ica_outb: attempt to set up slave mode!", DUMP_NONE);
|
|
#endif
|
|
}
|
|
sequence[adapter] = 0;
|
|
break;
|
|
|
|
case -1: /* Power on but so far uninitialised */
|
|
#ifndef PROD
|
|
sprintf(icamsgbuf, "ica_outb: bad port/value (%x/%x) while awaiting ICW1",
|
|
(unsigned)port, value);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
#endif
|
|
break;
|
|
|
|
default: /* This cannot happen */;
|
|
#ifndef PROD
|
|
trace("ica_outb: impossible error, programmer brain-dead", DUMP_NONE);
|
|
#endif
|
|
}
|
|
|
|
ica_lock = 0;
|
|
host_ica_unlock(); /* free lock if supported */
|
|
}
|
|
|
|
|
|
#ifndef DELAYED_INTS
|
|
void SWPIC_hw_interrupt IFN3(IU32, adapter, IU32, line_no, IS32, call_count)
|
|
#else /* DELAYED_INTS */
|
|
void SWPIC_hw_interrupt_delay IFN4(IU32, adapter, IU32, line_no, IS32, call_count, IS32, delay)
|
|
#endif /* DELAYED_INTS */
|
|
{
|
|
/*
|
|
* This routine is called by an adapter to raise an interrupt line.
|
|
* It may or may not interrupt the CPU. The CPU may or may not take
|
|
* any notice.
|
|
*/
|
|
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
IU8 bit;
|
|
IU32 line;
|
|
|
|
#ifndef PROD
|
|
SAVED char *linename[2][8] =
|
|
{
|
|
{
|
|
"TIMER",
|
|
"KEYBOARD",
|
|
"RESERVED",
|
|
"COM2",
|
|
"COM1",
|
|
"PARALLEL2",
|
|
"DISKETTE",
|
|
"PARALLEL1"
|
|
},
|
|
{
|
|
"REALTIME CLOCK",
|
|
"PC NETWORK",
|
|
"RESERVED",
|
|
"RESERVED",
|
|
"RESERVED",
|
|
"COPROCESSOR",
|
|
"FIXED DISK",
|
|
"RESERVED"
|
|
}
|
|
};
|
|
#endif
|
|
|
|
host_ica_lock();
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE_LOCK)
|
|
{
|
|
if(adapter>1 || line_no>7)
|
|
printf("**** H/W INTERRUPT (%sx%d) [%d:%d] ****\n",
|
|
linename[adapter][line_no], call_count,adapter,line_no);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If there is a request already outstanding on this line, then leave
|
|
* the IRR alone, but make a pass through anyway to action previously
|
|
* received but locked calls (see below for details).
|
|
*/
|
|
|
|
bit = (1 << line_no);
|
|
if (!(asp->ica_irr & bit))
|
|
{
|
|
asp->ica_irr |= bit; /* Pray we don't get a signal here! */
|
|
|
|
#ifdef DELAYED_INTS
|
|
asp->ica_delay[line_no] = delay;
|
|
asp->ica_curr_delay[line_no] = delay;
|
|
#endif /* DELAYED_INTS */
|
|
}
|
|
asp->ica_count[line_no] += call_count; /* Add the further requests */
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** H/W INTERRUPT (%sx%d) ****",
|
|
linename[adapter][line_no], call_count);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check the lock flag. If it is set, then this routine is being called
|
|
* from a signal handler while something else is going on. We can't just
|
|
* ignore the call since we might lose a keyboard interrupt. What we do
|
|
* is to set ica_irr and ica_count as normal (ie code above), then return.
|
|
* The next interrupt which gets through this test will cause the stored
|
|
* interrupt to be processed. This means that any code which plays around
|
|
* with ica_irr and ica_count should take a copy first to prevent problems.
|
|
*
|
|
* If the host supports real (critical section style) locks, then we won't
|
|
* get here in the above situation, so eliminate the following test. That
|
|
* leaves both primitive & real lock styles intact.
|
|
*/
|
|
|
|
if (!host_ica_real_locks())
|
|
{
|
|
if (ica_lock++)
|
|
{
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE_LOCK)
|
|
{
|
|
sprintf(icamsgbuf, "*");
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
ica_lock--;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef CPU_40_STYLE
|
|
if (asp->isr_depth[line_no] >= MAX_ISR_DEPTH)
|
|
{
|
|
asp->isr_max_depth_req[line_no]++;
|
|
|
|
/* first cut at lost iret hook detection - to be upgraded */
|
|
if (asp->isr_max_depth_req[line_no] > 80)
|
|
{
|
|
asp->isr_max_depth_req[line_no] = 0;
|
|
|
|
/* all stacked entries to be cleared */
|
|
asp->isr_depth[line_no] = 0;
|
|
|
|
/* permit intrs on this line */
|
|
iretHookActive &= ~(1 << ((adapter << 3) + line_no));
|
|
|
|
/* clear CPU side stack */
|
|
if (!host_bop_style_iret_hooks())
|
|
PurgeLostIretHookLine(((adapter << 3) + line_no) + 1);
|
|
}
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
/*
|
|
* Now scan the IRR to see if we can raise a CPU interrupt.
|
|
*/
|
|
|
|
if ((line = ica_scan_irr(adapter)) & 0x80)
|
|
ica_interrupt_cpu(adapter, line & 0x07);
|
|
|
|
ica_lock = 0;
|
|
host_ica_unlock();
|
|
}
|
|
|
|
#ifdef DELAYED_INTS
|
|
void SWPIC_hw_interrupt(adapter, line_no, call_count)
|
|
IU32 adapter;
|
|
IU8 line_no;
|
|
IS32 call_count;
|
|
{
|
|
ica_hw_interrupt_delay(adapter, line_no, call_count, 0);
|
|
}
|
|
#endif /* DELAYED_INTS */
|
|
|
|
void SWPIC_clear_int IFN2(IU32, adapter, IU32, line_no)
|
|
{
|
|
/*
|
|
* This routine is called by an adapter to lower an input line.
|
|
* The line will then not interrupt the CPU, unless of course
|
|
* it has already done so.
|
|
*/
|
|
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
IU8 bit, irr_check;
|
|
|
|
host_ica_lock();
|
|
/*
|
|
* Decrement the call count and if zero clear the bit in the IRR
|
|
*/
|
|
|
|
bit = (1 << line_no);
|
|
if (--(asp->ica_count[line_no]) <= 0)
|
|
{
|
|
irr_check = asp->ica_irr;
|
|
asp->ica_irr &= ~bit;
|
|
asp->ica_count[line_no] = 0; /* Just in case */
|
|
if ((!asp->ica_master) && (ica_scan_irr(adapter)==7))
|
|
{
|
|
asp->ica_cpu_int=FALSE;
|
|
ica_clear_int(ICA_MASTER,asp->ica_ssr);
|
|
}
|
|
#ifdef EOI_HOOKS
|
|
/*
|
|
// If the line has a pending interrupt, call the eoi hook
|
|
// to release any device waiting for an EoiHook.
|
|
*/
|
|
if ((irr_check & bit) != 0)
|
|
host_EOI_hook(line_no + (adapter << 3), -1);
|
|
#endif /* EOI_HOOKS */
|
|
}
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** ICA_CLEAR_INT, line %d ****", line_no);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
host_ica_unlock();
|
|
}
|
|
|
|
/*
|
|
* The emulation code associated with this interrupt line has decided it
|
|
* doesn't want to generate any more interrupts, even though the ICA may not
|
|
* have got through all the interrupts previously requested.
|
|
* Simply clear the corresponding interrupt count.
|
|
*/
|
|
void ica_hw_interrupt_cancel IFN2(IU32, adapter, IU32, line_no)
|
|
{
|
|
host_ica_lock();
|
|
adapter_state[adapter].ica_count[line_no] = 0;
|
|
host_ica_unlock();
|
|
ica_clear_int(adapter, line_no);
|
|
}
|
|
|
|
#ifdef HOOKED_IRETS
|
|
GLOBAL IS32
|
|
ica_intack IFN1(IU32 *, hook_address)
|
|
#else
|
|
GLOBAL IS32
|
|
ica_intack IFN0()
|
|
#endif /* HOOKED_IRETS */
|
|
{
|
|
/*
|
|
* This routine is called by the CPU when it wishes to acknowledge
|
|
* an interrupt. It is equivalent to the INTA pulses from the real
|
|
* device. The interrupt number is delivered.
|
|
* It can also be called from ica_inb as a Poll.
|
|
*
|
|
* Modification for Rev. 2:
|
|
*
|
|
* It is now necessary to detect whether a slave interrupt controller
|
|
* is attached to a particular interrupt request line on the master
|
|
* ICA. If a slave exists, it must be accessed to discover the
|
|
* interrupt vector.
|
|
*/
|
|
IS32 line; /* the IRQ line */
|
|
IU8 bit; /* bitmask for 'line' */
|
|
IS32 int_no; /* The interrupt number to return, 0-255 */
|
|
ADAPTER_STATE *asp; /* working pointer to adapter */
|
|
|
|
host_ica_lock(); /* real lock if supported */
|
|
|
|
line = ica_accept(ICA_MASTER);
|
|
|
|
#ifdef CPU_40_STYLE
|
|
if (line == -1) /* skip any spurious ints */
|
|
{
|
|
host_ica_unlock();
|
|
return ICA_INTACK_REJECT;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
bit = (1 << line);
|
|
if (adapter_state[ICA_MASTER].ica_ssr & bit)
|
|
{
|
|
line = ica_accept(ICA_SLAVE);
|
|
int_no = line + adapter_state[ICA_SLAVE].ica_base;
|
|
|
|
#ifdef CPU_40_STYLE
|
|
if (line == -1) /* skip any spurious ints */
|
|
{
|
|
host_ica_unlock();
|
|
return ICA_INTACK_REJECT;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
#ifdef CPU_40_STYLE
|
|
/* do callback processing for action interrupt */
|
|
asp = &adapter_state[ICA_SLAVE];
|
|
if (asp->callback_fn[line] != NO_ICA_CALLBACK)
|
|
{
|
|
/* invoke callback function */
|
|
(*asp->callback_fn[line])(asp->callback_parm[line]);
|
|
/* clear callback state reject intack call */
|
|
asp->callback_fn[line] = NO_ICA_CALLBACK;
|
|
host_ica_unlock();
|
|
return ICA_INTACK_REJECT;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
line += 8; /* make in range 8 - 15 for iret hook */
|
|
}
|
|
else
|
|
{
|
|
asp = &adapter_state[ICA_MASTER]; /* also excuse to use asp */
|
|
|
|
#ifdef CPU_40_STYLE
|
|
/* do callback processing for action interrupt */
|
|
if (asp->callback_fn[line] != NO_ICA_CALLBACK)
|
|
{
|
|
/* invoke callback function */
|
|
(*asp->callback_fn[line])(asp->callback_parm[line]);
|
|
/* clear callback state & return reject */
|
|
asp->callback_fn[line] = NO_ICA_CALLBACK;
|
|
host_ica_unlock();
|
|
return ICA_INTACK_REJECT;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
int_no = line + asp->ica_base;
|
|
}
|
|
|
|
#ifdef HOOKED_IRETS
|
|
/* check whether IRET Hook required for interrupt on this line.
|
|
* If IRET trapping mechanism is via bops on stack then this may
|
|
* also be conditional on the current state of the emulated hardware.
|
|
* This is checked via a host call (or define).
|
|
*/
|
|
if (host_valid_iret_hook())
|
|
{
|
|
*hook_address = ica_iret_hook_needed(line);
|
|
|
|
#ifdef CPU_40_STYLE
|
|
if (*hook_address != 0)
|
|
{
|
|
/* about to do iret hooked interrupt so increase depth */
|
|
asp->isr_depth[line]++;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
}
|
|
#endif /* HOOKED_IRETS */
|
|
|
|
host_ica_unlock(); /* real lock if supported */
|
|
|
|
return(int_no);
|
|
}
|
|
|
|
#ifdef CPU_40_STYLE
|
|
/*(
|
|
=========================== action_interrupt ==========================
|
|
|
|
PURPOSE: Associate an action with an interrupt on the line. When the CPU
|
|
is next able to process an interrupt on the requested line, the
|
|
callback function will be executed. That callback can then call
|
|
the relevant hardware interrupt interface once it has performed
|
|
the rest of the associated emulation.
|
|
|
|
INPUT: adapter: IU32. master/slave.
|
|
line: IU32. IRQ line interrupt will appear on.
|
|
func: callback function address to callback when line available.
|
|
parm: IU32. parameter to pass to above fn.
|
|
|
|
OUTPUT: Returns false (failure) if action_int already pending on that line
|
|
otherwise true (success).
|
|
|
|
=========================================================================
|
|
)*/
|
|
GLOBAL IBOOL
|
|
action_interrupt IFN4(IU32, adapter, IU32, line, ICA_CALLBACK, func, IU32, parm)
|
|
{
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
|
|
host_ica_lock(); /* real lock if available */
|
|
|
|
line &= 7;
|
|
|
|
/* check if callback already outstanding on this line */
|
|
if (asp->callback_fn[line] != NO_ICA_CALLBACK)
|
|
{
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "action_interrupt called before line %d cleared", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif /* PROD */
|
|
host_ica_unlock(); /* real unlock if available */
|
|
return(FALSE);
|
|
}
|
|
|
|
/* store callback information */
|
|
asp->callback_fn[line] = func;
|
|
asp->callback_parm[line] = parm;
|
|
|
|
/* set interrupt request bit */
|
|
asp->ica_irr |= (1 << line);
|
|
asp->ica_count[line]++;
|
|
|
|
/* make apparent interrupt visible to apps */
|
|
asp->ica_isr |= (1 << line);
|
|
|
|
/* get cpu attention for this int. (i.e. get intack called a.s.a.p) */
|
|
host_set_hw_int();
|
|
|
|
host_ica_unlock(); /* real unlock */
|
|
}
|
|
|
|
/*(
|
|
======================== cancel_action_interrupt =======================
|
|
|
|
PURPOSE: Associate an action with an interrupt on the line. When the CPU
|
|
is next able to process an interrupt on the requested line, the
|
|
callback function will be executed. That callback can then call
|
|
the relevant hardware interrupt interface once it has performed
|
|
the rest of the associated emulation.
|
|
|
|
INPUT: adapter: IU32. master/slave.
|
|
line: IU32. IRQ line to cancel interrupt action
|
|
|
|
OUTPUT: None.
|
|
|
|
=========================================================================
|
|
)*/
|
|
GLOBAL void
|
|
cancel_action_interrupt IFN2(IU32, adapter, IU32, line)
|
|
{
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
|
|
host_ica_lock(); /* real lock if available */
|
|
|
|
/* remove visibility of interrupt request. */
|
|
asp->ica_isr &= ~(1 << line);
|
|
|
|
/* irr & count should be cleared by intack, but possible this fn.
|
|
* has been called before the callback has been executed.
|
|
*/
|
|
if (asp->callback_fn[line] != NO_ICA_CALLBACK)
|
|
{
|
|
asp->ica_irr &= ~(1 << line);
|
|
asp->ica_count[line] = 0;
|
|
}
|
|
|
|
/* clear callback information */
|
|
asp->callback_fn[line] = NO_ICA_CALLBACK;
|
|
asp->callback_parm[line] = 0;
|
|
|
|
host_ica_unlock(); /* remove real lock */
|
|
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
#ifdef HOOKED_IRETS
|
|
|
|
#ifndef GISP_CPU /* GISP has own version of this routine */
|
|
GLOBAL IU32 ica_iret_hook_needed IFN1(IU32, line)
|
|
{
|
|
IU16 ireq_mask = 1 << line;
|
|
|
|
#ifndef PROD
|
|
if (line < 0 || line > 15)
|
|
{
|
|
/* Line is out of range */
|
|
sprintf(icamsgbuf, "**** ICA IRET HOOK IMPOSSIBLE line %d ****", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* does this line require iret hooks */
|
|
if (!(iretHookMask & ireq_mask))
|
|
/* Line not hooked. */
|
|
return 0;
|
|
|
|
/* if iret hooks implemented via bops, check bop table addresses ok */
|
|
if (host_bop_style_iret_hooks())
|
|
return(host_iret_bop_table_addr(line));
|
|
else
|
|
return(line + 1);
|
|
}
|
|
#endif /* GISP_CPU */
|
|
|
|
GLOBAL void
|
|
ica_iret_hook_control IFN3(IU32, adapter, IU32, line, IBOOL, enable)
|
|
{
|
|
#ifndef CCPU /* CCPU doesn't support iret hooks */
|
|
int mask = 1 << (line + (adapter << 3));
|
|
|
|
if (enable)
|
|
iretHookMask |= mask;
|
|
else
|
|
iretHookMask &= ~mask;
|
|
#endif /* CCPU */
|
|
}
|
|
|
|
GLOBAL void
|
|
ica_iret_hook_called IFN1(IU32, line)
|
|
{
|
|
ADAPTER_STATE *asp;
|
|
IU32 adapter = line >> 3;
|
|
IU8 i;
|
|
|
|
#ifndef PROD
|
|
if (io_verbose & ICA_VERBOSE)
|
|
{
|
|
sprintf(icamsgbuf, "**** ICA IRET HOOK, line %d ****", line);
|
|
trace(icamsgbuf, DUMP_NONE);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CPU_40_STYLE
|
|
asp = &adapter_state[adapter];
|
|
line >>= (adapter << 3);
|
|
asp->isr_depth[line]--;
|
|
|
|
/* cant go beyond max depth so can clear counters used at max depth */
|
|
asp->isr_max_depth_req[line] = 0;
|
|
asp->isr_max_depth_ack[line] = 0;
|
|
|
|
/* enable interrupts on this line */
|
|
if (asp->isr_depth[line] < MAX_ISR_DEPTH)
|
|
iretHookActive &= ~(1 << line);
|
|
#else
|
|
UNUSED(asp); /* anti warning */
|
|
iretHookActive &= ~(1 << line);
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
|
|
if ((i = ica_scan_irr(adapter)) & 0x80)
|
|
ica_interrupt_cpu(adapter, i & 0x07);
|
|
}
|
|
|
|
/* Interfaces to allow iret hooks to be enabled/disabled. On NT, enable called
|
|
* from DPMI when app requests a switch into protected mode. The assumption is
|
|
* that if it does this, it will not lose an IRET BOP table as it might if it
|
|
* does its own protected mode memory management. The disable fn. is then
|
|
* called when an app. exits.
|
|
* On non NTVDM ports with hooks enabled the hooks are enabled at initialisation
|
|
* time. (N.B. this is mainly a debugging feature).
|
|
*/
|
|
GLOBAL void
|
|
ica_enable_iret_hooks()
|
|
{
|
|
iretHooksEnabled = TRUE;
|
|
}
|
|
|
|
#ifdef NTVDM
|
|
// The following routines are used to support IRET hooks. If an interrupt
|
|
// uses an IRET hook then the ICA will not generate a interrupt of that
|
|
// type until the IRET hook has been called.
|
|
|
|
|
|
// Disable/Enable iret hooks for an ireq line
|
|
|
|
void ica_enable_iret_hook(int adapter, int line, int enable)
|
|
{
|
|
VDMVIRTUALICA *asp = &VirtualIca[adapter];
|
|
int mask = 1 << (line + adapter*8);
|
|
|
|
host_ica_lock();
|
|
|
|
|
|
// Update iret hook mask
|
|
|
|
if(enable)
|
|
IretHooked |= mask; // Enable iret hook
|
|
else
|
|
IretHooked &= ~mask; // Disable iret hook
|
|
|
|
host_ica_unlock();
|
|
}
|
|
#endif
|
|
|
|
|
|
GLOBAL void
|
|
ica_disable_iret_hooks()
|
|
{
|
|
iretHooksEnabled = FALSE;
|
|
}
|
|
#endif /* HOOKED_IRETS */
|
|
|
|
#ifdef NTVDM
|
|
/* Provide external hook to call interrupts - for VDDs that can't see
|
|
* function pointer.
|
|
*/
|
|
void call_ica_hw_interrupt IFN3(IU32, adapter, IU32, line, IS32, call_count)
|
|
{
|
|
ica_hw_interrupt(adapter, line, call_count);
|
|
}
|
|
#endif /* NTVDM */
|
|
|
|
#ifdef SEGMENTATION
|
|
/*
|
|
* The following #include specifies the code segment into which this
|
|
* module will by placed by the MPW C compiler on the Mac II running
|
|
* MultiFinder.
|
|
*/
|
|
#include "SOFTPC_INIT.seg"
|
|
#endif
|
|
|
|
#define INIT0_ICW1 (IU8)0x11
|
|
#define INIT0_ICW2 (IU8)0x08
|
|
#define INIT0_ICW3 (IU8)0x04
|
|
#define INIT0_ICW4 (IU8)0x01
|
|
/* POST leaves some int lines masked out (including comms and lpt lines) */
|
|
/* The setting of the bits is spread out throughout the real POST code
|
|
* but is collected into one place here. I think that this will not cause
|
|
* any problems but it is conceivable that it will harm other OS's than
|
|
* DOS (eg OS/2 or coherent etc.)
|
|
*/
|
|
#define INIT0_OCW1 (IU8)0xb8
|
|
|
|
void ica0_post IFN0()
|
|
{
|
|
ica_outb(ICA0_PORT_0, INIT0_ICW1);
|
|
ica_outb(ICA0_PORT_1, INIT0_ICW2);
|
|
ica_outb(ICA0_PORT_1, INIT0_ICW3);
|
|
ica_outb(ICA0_PORT_1, INIT0_ICW4);
|
|
ica_outb(ICA0_PORT_1, INIT0_OCW1);
|
|
}
|
|
|
|
void ica0_init IFN0()
|
|
{
|
|
io_addr i;
|
|
|
|
/*
|
|
* Set up the IO chip select logic for adapter 0. (Master).
|
|
*/
|
|
|
|
io_define_inb(ICA0_ADAPTOR, ica_inb_func);
|
|
io_define_outb(ICA0_ADAPTOR, ica_outb_func);
|
|
|
|
for(i = ICA0_PORT_START; i <= ICA0_PORT_END; i++)
|
|
io_connect_port(i, ICA0_ADAPTOR, IO_READ_WRITE);
|
|
|
|
adapter_state[ICA_MASTER].ica_master = TRUE;
|
|
|
|
#ifdef CPU_40_STYLE
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
adapter_state[ICA_MASTER].callback_fn[i] = NO_ICA_CALLBACK;
|
|
adapter_state[ICA_MASTER].isr_depth[i] = 0;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
#if defined(HOOKED_IRETS) && !defined(NTVDM)
|
|
/* on iret-hooked, non NT ports, enable iret hooks */
|
|
ica_enable_iret_hooks();
|
|
#endif
|
|
}
|
|
|
|
|
|
#define INIT1_ICW1 (IU8)0x11
|
|
#define INIT1_ICW2 (IU8)0x70
|
|
#define INIT1_ICW3 (IU8)0x02
|
|
#define INIT1_ICW4 (IU8)0x01
|
|
/* POST leaves some int lines masked out (reserved lines and RTC) */
|
|
/* see the comment on POST setting mask bits for master ica */
|
|
#define INIT1_OCW1 (IU8)0x9d
|
|
|
|
void ica1_post IFN0()
|
|
{
|
|
ica_outb(ICA1_PORT_0, INIT1_ICW1);
|
|
ica_outb(ICA1_PORT_1, INIT1_ICW2);
|
|
ica_outb(ICA1_PORT_1, INIT1_ICW3);
|
|
ica_outb(ICA1_PORT_1, INIT1_ICW4);
|
|
ica_outb(ICA1_PORT_1, INIT1_OCW1);
|
|
}
|
|
|
|
void ica1_init IFN0()
|
|
{
|
|
io_addr i;
|
|
|
|
/*
|
|
* Set up the IO chip select logic for adapter 1. (Slave).
|
|
*/
|
|
|
|
io_define_inb(ICA1_ADAPTOR, ica_inb_func);
|
|
io_define_outb(ICA1_ADAPTOR, ica_outb_func);
|
|
|
|
for(i = ICA1_PORT_START; i <= ICA1_PORT_END; i++)
|
|
io_connect_port(i, ICA1_ADAPTOR, IO_READ_WRITE);
|
|
|
|
adapter_state[ICA_SLAVE].ica_master = FALSE;
|
|
|
|
#ifdef CPU_40_STYLE
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
adapter_state[ICA_SLAVE].callback_fn[i] = NO_ICA_CALLBACK;
|
|
adapter_state[ICA_SLAVE].isr_depth[i] = 0;
|
|
}
|
|
#endif /* CPU_40_STYLE */
|
|
|
|
}
|
|
|
|
#ifndef PROD
|
|
/*
|
|
* The following functions are used for DEBUG purposes only.
|
|
*/
|
|
LOCAL void
|
|
ica_print_int IFN2(char *, str, IS32, val)
|
|
{
|
|
printf("%-20s 0x%02X\n", str, val);
|
|
}
|
|
|
|
LOCAL void
|
|
ica_print_str IFN2(char *, str, char *, val)
|
|
{
|
|
printf("%-20s %s\n", str, val);
|
|
}
|
|
|
|
GLOBAL void
|
|
ica_dump IFN1(IU32, adapter)
|
|
{
|
|
ADAPTER_STATE *asp = &adapter_state[adapter];
|
|
|
|
if (adapter == ICA_MASTER)
|
|
printf("MASTER 8259A State:\n\n");
|
|
else
|
|
printf("SLAVE 8259A State:\n\n");
|
|
|
|
ica_print_str("ica_master", (asp->ica_master ? "Master" : "Slave"));
|
|
ica_print_int("ica_irr", asp->ica_irr);
|
|
ica_print_int("ica_isr", asp->ica_isr);
|
|
ica_print_int("ica_imr", asp->ica_imr);
|
|
ica_print_int("ica_ssr", asp->ica_ssr);
|
|
ica_print_int("ica_base", asp->ica_base);
|
|
ica_print_int("ica_hipri", asp->ica_hipri);
|
|
ica_print_int("ica_mode", asp->ica_mode);
|
|
ica_print_int("ica_int_line", asp->ica_int_line);
|
|
ica_print_str("ica_cpu_int", (asp->ica_cpu_int ? "TRUE" : "FALSE"));
|
|
|
|
printf("\n\n");
|
|
}
|
|
#endif /* PROD */
|
|
#ifdef NTVDM
|
|
|
|
void SoftPcEoi(int Adapter, int* Line) {
|
|
ica_eoi(Adapter, Line, 0);
|
|
}
|
|
|
|
//Restart delayed interrupts
|
|
|
|
BOOL ica_restart_interrupts(int adapter)
|
|
{
|
|
int i;
|
|
|
|
if((i = ica_scan_irr(adapter)) & 0x80) {
|
|
ica_interrupt_cpu(adapter, i &= 0x07);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
//New ICA interrupt state reset function
|
|
|
|
void ica_reset_interrupt_state(void)
|
|
{
|
|
int line_no;
|
|
|
|
host_ica_lock();
|
|
|
|
for(line_no = 0; line_no < 8; line_no++) {
|
|
VirtualIca[ICA_MASTER].ica_count[line_no] =
|
|
VirtualIca[ICA_SLAVE].ica_count[line_no] = 0;
|
|
ica_clear_int(ICA_MASTER,line_no);
|
|
ica_clear_int(ICA_SLAVE,line_no);
|
|
}
|
|
|
|
|
|
//Clear interrupt counters
|
|
VirtualIca[ICA_MASTER].ica_cpu_int =
|
|
VirtualIca[ICA_SLAVE].ica_cpu_int = FALSE;
|
|
|
|
DelayIretHook = 0;
|
|
DelayIrqLine = 0;
|
|
|
|
//Tell CPU to remove any pending interrupts
|
|
host_clear_hw_int();
|
|
|
|
host_ica_unlock();
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle callout from DPMI to say that an app has asked DPMI to switch it
|
|
* to protected mode. We use this as an indicator that a protected mode app
|
|
* will work with the Iret Hook system. If an app does it's own thing with
|
|
* selectors et al, the BOP table will be hidden, swallowed and generally
|
|
* lost. Attempts then to transfer control to it will fault.
|
|
* Known examples of such unfriendliness are the DOS Lotus 123 r3 series.
|
|
*/
|
|
VOID EnableEmulatorIretHooks(void)
|
|
{
|
|
PMEmulHookEnabled = TRUE;
|
|
}
|
|
|
|
/*
|
|
* The app is closing - turn off the Iret Hooks in case the next app is
|
|
* iret hook unfriendly. If it's a friendly app, we'll be called again
|
|
* via the Enable... routine above.
|
|
*/
|
|
VOID DisableEmulatorIretHooks(void)
|
|
{
|
|
PMEmulHookEnabled = FALSE;
|
|
}
|
|
|
|
//
|
|
// Retry DelayInts (not iret hooks!)
|
|
//
|
|
// IrqLine - IrqLineBitMask, to be cleared
|
|
//
|
|
VOID ica_RestartInterrupts(ULONG IrqLine)
|
|
{
|
|
#ifdef MONITOR
|
|
|
|
//
|
|
// on x86 we may get multiple bits set
|
|
// so check both slave and master
|
|
//
|
|
UndelayIrqLine = 0;
|
|
|
|
if (!ica_restart_interrupts(ICA_SLAVE))
|
|
ica_restart_interrupts(ICA_MASTER);
|
|
#else
|
|
host_ica_lock();
|
|
|
|
DelayIrqLine &= ~IrqLine;
|
|
|
|
ica_restart_interrupts(IrqLine >> 3 ? ICA_SLAVE : ICA_MASTER);
|
|
|
|
host_ica_unlock();
|
|
#endif
|
|
}
|
|
#endif /* NTVDM */
|