#include "insignia.h" #include "host_def.h" /* * O/S include files. */ #include #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 */