/* hp2100_pif.c: HP 12620A/12936A privileged interrupt fence simulator | |
Copyright (c) 2008, 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 | |
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, Mar-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 */ | |
FLIP_FLOP pif_control = CLEAR; /* control flip-flop */ | |
FLIP_FLOP pif_flag = CLEAR; /* flag flip-flop */ | |
FLIP_FLOP pif_flagbuf = CLEAR; /* flag buffer flip-flop */ | |
/* PIF global routines */ | |
uint32 pif_io (uint32 select_code, IOSIG signal, uint32 data); | |
t_stat pif_reset (DEVICE *dptr); | |
t_stat pif_set_card (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, 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, &pif_io }; | |
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 (DEVNO, pif_dib.devno, 6), REG_HRO }, | |
{ NULL } | |
}; | |
MTAB pif_mod [] = { | |
{ MTAB_XTD | MTAB_VDV, 0, NULL, "12620A", &pif_set_card, NULL, NULL }, | |
{ MTAB_XTD | MTAB_VDV, 1, NULL, "12936A", &pif_set_card, NULL, NULL }, | |
{ MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &pif_show_card, NULL }, | |
{ MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &pif_dev }, | |
{ 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 */ | |
NULL, /* 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 (uint32 select_code, IOSIG signal, uint32 data) | |
{ | |
const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); | |
const t_bool is_rte_pif = (pif_dev.flags & DEV_12936) == 0; | |
const IOSIG base_signal = IOBASE (signal); /* derive base signal */ | |
switch (base_signal) { /* dispatch base I/O signal */ | |
case ioCLF: /* clear flag flip-flop */ | |
pif_flag = pif_flagbuf = CLEAR; /* clear flag buffer and flag */ | |
if (DEBUG_PRS (pif_dev)) | |
fputs (">>PIF: [CLF] Flag cleared\n", sim_deb); | |
break; | |
case ioSTF: /* set flag flip-flop */ | |
if (is_rte_pif) { /* RTE PIF? */ | |
pif_flag = pif_flagbuf = SET; /* set flag buffer and flag */ | |
if (DEBUG_PRS (pif_dev)) | |
fputs (">>PIF: [STF] Flag set\n", sim_deb); | |
} | |
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 */ | |
pif_io (select_code, ioSIR, 0); /* set IRQ (not normally done for IOO) */ | |
if (DEBUG_PRS (pif_dev)) | |
fprintf (sim_deb, ">>PIF: [OTx%s] Flag set\n", hold_or_clear); | |
} | |
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); | |
if (DEBUG_PRS (pif_dev)) | |
fprintf (sim_deb, ">>PIF: [POPIO] Flag %s\n", | |
(is_rte_pif ? "set" : "cleared")); | |
/* fall into CRS handler */ | |
case ioCRS: /* control reset */ | |
/* fall into CLC handler */ | |
case ioCLC: /* clear control flip-flop */ | |
pif_control = CLEAR; /* clear control */ | |
if (DEBUG_PRS (pif_dev)) | |
fprintf (sim_deb, ">>PIF: [%s%s] Control cleared\n", | |
(signal == ioCRS ? "CRS" : "CLC"), hold_or_clear); | |
break; | |
case ioSTC: /* set control flip-flop */ | |
pif_control = SET; /* set control */ | |
if (DEBUG_PRS (pif_dev)) | |
fprintf (sim_deb, ">>PIF: [STC%s] Control set\n", hold_or_clear); | |
break; | |
case ioSIR: /* set interrupt request */ | |
if (is_rte_pif) { /* RTE PIF? */ | |
setstdPRL (select_code, pif); /* set standard PRL signal */ | |
setstdIRQ (select_code, pif); /* set standard IRQ signal */ | |
setstdSRQ (select_code, pif); /* set standard SRQ signal */ | |
} | |
else { /* DOS PIF */ | |
setPRL (select_code, !(pif_control | pif_flag)); | |
setIRQ (select_code, !pif_control & pif_flag & pif_flagbuf); | |
} | |
if (DEBUG_PRS (pif_dev)) | |
fprintf (sim_deb, ">>PIF: [SIR] PRL = %d, IRQ = %d\n", | |
PRL (select_code), IRQ (select_code)); | |
break; | |
case ioIAK: /* interrupt acknowledge */ | |
pif_flagbuf = CLEAR; | |
break; | |
default: /* all other signals */ | |
break; /* are ignored */ | |
} | |
if (signal > ioCLF) /* multiple signals? */ | |
pif_io (select_code, ioCLF, 0); /* issue CLF */ | |
else if (signal > ioSIR) /* signal affected interrupt status? */ | |
pif_io (select_code, ioSIR, 0); /* set interrupt request */ | |
return data; | |
} | |
/* Simulator reset routine */ | |
t_stat pif_reset (DEVICE *dptr) | |
{ | |
pif_io (pif_dib.devno, ioPOPIO, 0); /* send POPIO signal */ | |
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, 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, void *desc) | |
{ | |
if (pif_dev.flags & DEV_12936) | |
fputs ("12936A", st); | |
else | |
fputs ("12620A", st); | |
return SCPE_OK; | |
} |