| /* hp2100_pif.c: HP 12620A/12936A privileged interrupt fence simulator | |
| Copyright (c) 2008-2012, 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 | |
| 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, 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 */ | |
| 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, 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_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 [] = { | |
| { 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, "SC", "SC", &hp_setsc, &hp_showsc, &pif_dev }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NMO, 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 (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) | |
| { | |
| const char *hold_or_clear = (signal_set & ioCLF ? ",C" : ""); | |
| 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 */ | |
| 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 */ | |
| working_set = working_set | ioSIR; /* set SIR (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")); | |
| break; | |
| case ioCRS: /* control reset */ | |
| 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 (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); | |
| } | |
| if (DEBUG_PRS (pif_dev)) | |
| fprintf (sim_deb, ">>PIF: [SIR] PRL = %d, IRQ = %d\n", | |
| PRL (dibptr->select_code), | |
| IRQ (dibptr->select_code)); | |
| 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, 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; | |
| } |