/* hp2100_pif.c: HP 12620A/12936A Privileged Interrupt Fence simulator | |
Copyright (c) 2008-2017, 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 | |
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" | |
/* Device flags */ | |
#define DEV_V_12936 (DEV_V_UF + 0) /* 12936A card */ | |
#define DEV_12936 (1 << DEV_V_12936) | |
/* PIF state variables */ | |
struct { | |
FLIP_FLOP control; /* control flip-flop */ | |
FLIP_FLOP flag; /* flag flip-flop */ | |
FLIP_FLOP flagbuf; /* flag buffer flip-flop */ | |
} pif = { CLEAR, CLEAR, CLEAR }; | |
/* PIF global routines */ | |
IOHANDLER pif_io; | |
t_stat pif_reset (DEVICE *dptr); | |
t_stat pif_set_card (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
/* PIF data structures. | |
pif_dib PIF device information block | |
pif_unit PIF unit list | |
pif_reg PIF register list | |
pif_mod PIF modifier list | |
pif_deb PIF debug list | |
pif_dev PIF device descriptor | |
Implementation note: | |
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. | |
*/ | |
DEVICE pif_dev; | |
DIB pif_dib = { &pif_io, PIF }; | |
UNIT pif_unit = { | |
UDATA (NULL, 0, 0) /* dummy unit */ | |
}; | |
REG pif_reg [] = { | |
{ FLDATA (CTL, pif.control, 0) }, | |
{ FLDATA (FLG, pif.flag, 0) }, | |
{ FLDATA (FBF, pif.flagbuf, 0) }, | |
{ ORDATA (SC, pif_dib.select_code, 6), REG_HRO }, | |
{ ORDATA (DEVNO, pif_dib.select_code, 6), REG_HRO }, | |
{ NULL } | |
}; | |
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 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_DEBUG | DEV_DISABLE, /* device flags */ | |
0, /* debug control flags */ | |
pif_deb, /* debug flag name table */ | |
NULL, /* memory size change routine */ | |
NULL }; /* logical device name */ | |
/* I/O signal handler. | |
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. | |
*/ | |
uint32 pif_io (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) | |
{ | |
const t_bool is_rte_pif = (pif_dev.flags & DEV_12936) == 0; | |
IOSIGNAL signal; | |
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ | |
while (working_set) { | |
signal = IONEXT (working_set); /* isolate next signal */ | |
switch (signal) { /* dispatch I/O signal */ | |
case ioCLF: /* clear flag flip-flop */ | |
pif.flag = pif.flagbuf = CLEAR; /* clear flag buffer and flag */ | |
break; | |
case ioSTF: /* set flag flip-flop */ | |
if (is_rte_pif) /* RTE PIF? */ | |
pif.flag = pif.flagbuf = SET; /* set flag buffer and flag */ | |
break; | |
case ioSFC: /* skip if flag is clear */ | |
if (is_rte_pif) /* RTE PIF? */ | |
setstdSKF (pif); /* card responds to SFC */ | |
break; | |
case ioSFS: /* skip if flag is set */ | |
if (is_rte_pif) /* RTE PIF? */ | |
setstdSKF (pif); /* card responds to SFS */ | |
break; | |
case ioIOO: /* I/O data output */ | |
if (!is_rte_pif) { /* DOS PIF? */ | |
pif.flag = pif.flagbuf = SET; /* set flag buffer and flag */ | |
working_set = working_set | ioSIR; /* set SIR (not normally done for IOO) */ | |
} | |
break; | |
case ioPOPIO: /* power-on preset to I/O */ | |
pif.flag = pif.flagbuf = /* set or clear flag and flag buffer */ | |
(is_rte_pif ? SET : CLEAR); | |
tprintf (pif_dev, TRACE_CMD, "Power-on reset\n"); | |
break; | |
case ioCRS: /* control reset */ | |
tprintf (pif_dev, TRACE_CMD, "Control reset\n"); | |
/* fall into ioCLC handler */ | |
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) { /* RTE PIF? */ | |
setstdPRL (pif); /* set standard PRL signal */ | |
setstdIRQ (pif); /* set standard IRQ signal */ | |
setstdSRQ (pif); /* set standard SRQ signal */ | |
} | |
else { /* DOS PIF */ | |
setPRL (dibptr->select_code, !(pif.control | pif.flag)); | |
setIRQ (dibptr->select_code, !pif.control & pif.flag & pif.flagbuf); | |
} | |
tprintf (pif_dev, TRACE_CMD, "Fence %s%s lower-priority interrupts\n", | |
(IRQ (dibptr->select_code) ? "requests an interrupt and " : ""), | |
(PRL (dibptr->select_code) ? "allows" : "inhibits")); | |
break; | |
case ioIAK: /* interrupt acknowledge */ | |
pif.flagbuf = CLEAR; | |
break; | |
default: /* all other signals */ | |
break; /* are ignored */ | |
} | |
working_set = working_set & ~signal; /* remove current signal from set */ | |
} | |
return stat_data; | |
} | |
/* Simulator reset routine */ | |
t_stat pif_reset (DEVICE *dptr) | |
{ | |
IOPRESET (&pif_dib); /* PRESET device (does not use PON) */ | |
return SCPE_OK; | |
} | |
/* Set card type. | |
val == 0 --> set to 12936A (DOS PIF) | |
val == 1 --> set to 12620A (RTE PIF) | |
*/ | |
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 */ | |
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; | |
} |