simh-testsetgenerator/HP2100/hp2100_pif.c
2020-02-16 22:25:15 -08:00

439 lines
18 KiB
C

/* hp2100_pif.c: HP 12620A/12936A Privileged Interrupt Fence simulator
Copyright (c) 2008-2018, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
PIF 12620A/12936A Privileged Interrupt Fence
11-Jun-18 JDB Revised I/O model
15-Mar-17 JDB Trace flags are now global
11-Mar-17 JDB Revised the trace outputs
17-Jan-17 JDB Changed "hp_---sc" and "hp_---dev" to "hp_---_dib"
13-May-16 JDB Modified for revised SCP API function parameter types
10-Feb-12 JDB Deprecated DEVNO in favor of SC
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
26-Jun-08 JDB Rewrote device I/O to model backplane signals
18-Jun-08 JDB Created PIF device
References:
- 12620A Breadboard Interface Kit Operating and Service Manual
(12620-90001, May 1978)
- 12936A Privileged Interrupt Fence Accessory Installation and Service Manual
(12936-90001, March 1974)
The Privileged Interupt Fence (PIF) was used in DOS and RTE systems to
provide privileged interrupt capability. In non-privileged systems, DOS and
RTE vectored all interrupts though the Central Interrupt Control (CIC)
routine. Within CIC, the interrupt system was turned off, the interrupt was
categorized, the associated driver was identified and mapped into logical
memory (if necessary), and the driver entered to handle the device service.
When the driver exited, the interrupt system was turned on before returning
to the point of interruption in the user's program. In addition, the DOS and
RTE operating systems themselves executed with the interrupt system off, as
they were not reentrant.
This process proved too lengthy for certain devices, which would lose
interrupts or be forced to limit I/O speeds as a result. To allow faster
service, a driver could be written as a "privileged" driver and generated
into a privileged system. A privileged system operated with the interrupt
system on when handling unprivileged device interrupts or executing within
the operating system. The PIF card was installed in the I/O backplane to
separate privileged from unprivileged devices by controlling the interrupt
priority chain signal (PRL) to lower-priority devices. The privileged cards
located below the fence were allowed to interrupt the service routines of the
unprivileged cards that were located above the fence.
When an unprivileged device interrupted, CIC would be entered as usual, and
the interrupt system would be turned off. However, after the system state
was saved, the PIF would be configured to break the priority chain (deny
PRL), so that subsequent interrupts from all unprivileged devices would be
deferred. Then the interrupt system would be turned on before normal CIC
processing continued. Interrupts from additional unprivileged devices would
be held off by the PIF until the driver completed and CIC returned, just as
in a non-privileged system.
However, if a privileged device interrupted, the interrupt would be allowed,
because the interrupt system was on, and the priority chain was intact for
the devices below the fence. A privileged device bypassed CIC and entered
the associated device driver directly, and this would occur even if an
unprivileged device driver or the operating system itself were executing.
This provided very fast interrupt service time.
HP produced two PIF cards: the 12936A Privileged Interrupt Fence Accessory
for DOS, and the 12620A Breadboard Interface for RTE. They behaved quite
differently and were not interchangeable.
The 12620A had the standard control and flag circuitry. It behaved as most
cards did; setting control and flag together lowered PRL and generated an
interrupt. The control and flag flip-flops were set and cleared with STC/CLC
and STF/CLF instructions. The SFS/SFC instructions could be used to test the
flag state.
The 12936A had a unique behavior. Setting either control or flag lowered
PRL. An interrupt occurred when flag was set and control was clear. The
control flip-flop was controlled with STC/CLC. The flag flip-flop was set
with OTA/B and cleared with CLF. SFC and SFS were not implemented and never
skipped.
*/
#include "hp2100_defs.h"
#include "hp2100_io.h"
/* Device flags */
#define DEV_V_12936 (DEV_V_UF + 0) /* 12936A card */
#define DEV_12936 (1 << DEV_V_12936)
/* Interface state */
typedef struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
} CARD_STATE;
static CARD_STATE pif; /* per-card state */
/* Interface local SCP support routines */
static INTERFACE pif_interface;
/* Interface local SCP support routines */
static t_stat pif_reset (DEVICE *dptr);
static t_stat pif_set_card (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
/* Interface SCP data structures */
/* Device information block */
static DIB pif_dib = {
&pif_interface, /* the device's I/O interface function pointer */
PIF, /* the device's select code (02-77) */
0, /* the card index */
"12620A/12936A Privileged Interrupt Fence", /* the card description */
NULL /* the ROM description */
};
/* Unit list.
Implementation notes:
1. The SIMH developer's manual says that a device's unit list may be NULL.
However, if this is done, the register state cannot be examined or
altered via SCP. To work around this problem, we define a dummy unit
that is not used otherwise.
*/
static UNIT pif_unit [] = {
{ UDATA (NULL, 0, 0) }
};
/* Register list */
static REG pif_reg [] = {
/* Macro Name Location Offset */
/* ------ ------ ------------------- ------ */
{ FLDATA (CTL, pif.control, 0) },
{ FLDATA (FLG, pif.flag, 0) },
{ FLDATA (FBF, pif.flag_buffer, 0) },
DIB_REGS (pif_dib),
{ NULL }
};
/* Modifier list */
static MTAB pif_mod [] = {
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ------------------- ----- ------------ ------------ -------------- -------------- ----------------- */
{ MTAB_XDV, 0, NULL, "12620A", &pif_set_card, NULL, NULL },
{ MTAB_XDV, 1, NULL, "12936A", &pif_set_card, NULL, NULL },
{ MTAB_XDV, 0, "TYPE", NULL, NULL, &pif_show_card, NULL },
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &pif_dib },
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &pif_dib },
{ 0 }
};
/* Debugging trace list */
static DEBTAB pif_deb [] = {
{ "CMD", TRACE_CMD }, /* interface commands */
{ "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */
{ NULL, 0 }
};
/* Device descriptor */
DEVICE pif_dev = {
"PIF", /* device name */
pif_unit, /* unit array */
pif_reg, /* register array */
pif_mod, /* modifier array */
1, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&pif_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&pif_dib, /* device information block */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
pif_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL }; /* logical device name */
/* Interface local SCP support routines */
/* Privileged interrupt fence interface.
Operation of the 12620A and the 12936A is different. The I/O responses of
the two cards are summarized below:
Signal 12620A Action 12936A Action
------ -------------------- --------------------
POPIO Set FBF, FLG Clear FBF, FLG
CRS Clear CTL Clear CTL
CLC Clear CTL Clear CTL
STC Set CTL Set CTL
CLF Clear FBF, FLG Clear FBF, FLG
STF Set FBF, FLG none
SFC Skip if FLG clear none
SFS Skip if FLG set none
IOI none none
IOO none Set FBF, FLG
PRL ~(CTL * FLG) ~(CTL + FLG)
IRQ CTL * FLG * FBF ~CTL * FLG * FBF
IAK Clear FBF Clear FBF
SRQ Follows FLG Not driven
Note that PRL and IRQ are non-standard for the 12936A.
*/
static SIGNALS_VALUE pif_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const t_bool is_rte_pif = (pif_dev.flags & DEV_12936) == 0; /* TRUE if 12620A card */
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
while (working_set) {
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
pif.flag_buffer = CLEAR; /* clear flag buffer and flag */
pif.flag = CLEAR;
break;
case ioSTF: /* set flag flip-flop */
if (is_rte_pif) /* RTE PIF? */
pif.flag_buffer = SET; /* set flag buffer */
break;
case ioENF: /* enable flag */
if (pif.flag_buffer == SET) /* if the flag buffer flip-flop is set */
pif.flag = SET; /* then set the flag flip-flop */
break;
case ioSFC: /* skip if flag is clear */
if (is_rte_pif && pif.flag == CLEAR) /* only the 12620A card */
outbound.signals |= ioSKF; /* responds to SFC */
break;
case ioSFS: /* skip if flag is set */
if (is_rte_pif && pif.flag == SET) /* only the 12620A card */
outbound.signals |= ioSKF; /* responds to SFS */
break;
case ioIOO: /* I/O data output */
if (is_rte_pif == FALSE) { /* DOS PIF? */
pif.flag_buffer = SET; /* set flag buffer */
working_set |= ioENF | ioSIR; /* set ENF and SIR (not normally done for IOO) */
}
break;
case ioPOPIO: /* power-on preset to I/O */
if (is_rte_pif)
pif.flag_buffer = SET;
else {
pif.flag_buffer = CLEAR;
pif.flag = CLEAR;
}
tprintf (pif_dev, TRACE_CMD, "Power-on reset\n");
break;
case ioCRS: /* control reset */
pif.control = CLEAR; /* clear control */
tprintf (pif_dev, TRACE_CMD, "Control reset\n");
break;
case ioCLC: /* clear control flip-flop */
pif.control = CLEAR; /* clear control */
break;
case ioSTC: /* set control flip-flop */
pif.control = SET; /* set control */
break;
case ioSIR: /* set interrupt request */
if (is_rte_pif & pif.control & pif.flag /* if control and flag are set (12620A) */
|| !is_rte_pif & (pif.control | pif.flag)) /* or control or flag are clear (12936A) */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (~(is_rte_pif ^ pif.control) /* if control is set (12620A) or clear (12936A) */
& pif.flag & pif.flag_buffer) /* and flag and flag buffer are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
if (is_rte_pif && pif.flag == SET) /* if 12620A and flag is set */
outbound.signals |= ioSRQ; /* then assert SRQ */
tprintf (pif_dev, TRACE_CMD, "Fence %s%s lower-priority interrupts\n",
(outbound.signals & cnIRQ ? "requests an interrupt and " : ""),
(outbound.signals & cnPRL ? "allows" : "inhibits"));
break;
case ioIAK: /* interrupt acknowledge */
pif.flag_buffer = CLEAR;
break;
case ioIEN: /* interrupt enable */
irq_enabled = TRUE;
break;
case ioPRH: /* priority high */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioIOI: /* not used by this interface */
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
}
return outbound; /* return the outbound signals and value */
}
/* Simulator reset routine */
static t_stat pif_reset (DEVICE *dptr)
{
io_assert (dptr, ioa_POPIO); /* PRESET the device */
return SCPE_OK;
}
/* Privileged interrupt fence local utility routines */
/* Set card type.
val == 0 --> set to 12936A (DOS PIF)
val == 1 --> set to 12620A (RTE PIF)
*/
static t_stat pif_set_card (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if ((val < 0) || (val > 1) || (cptr != NULL)) /* sanity check */
return SCPE_ARG; /* bad argument */
if (val) /* DOS PIF selected? */
pif_dev.flags = pif_dev.flags | DEV_12936; /* set to 12936A */
else /* RTE PIF selected */
pif_dev.flags = pif_dev.flags & ~DEV_12936; /* set to 12620A */
return SCPE_OK;
}
/* Show card type */
static t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (pif_dev.flags & DEV_12936)
fputs ("12936A", st);
else
fputs ("12620A", st);
return SCPE_OK;
}