MIC Address Calculation — ND-120 CGA (DELILAH) Analysis¶
Full path: Verilog/mic-calculation.md
Scope: This document describes every signal that participates in the generation of the microcode RAM address MA[12:0] inside the CGA (DELILAH) gate array, and answers the specific question: do internal traps (MPV / page fault / illegal instruction) still fire when IONI (STS bit 15) is off, i.e. after an IOF instruction?
All line numbers refer to files in Verilog/.
Facts in this document are taken directly from the Verilog. Anything I could not verify from the code is explicitly flagged with [UNVERIFIED] or listed in the "Open questions" section at the end.
1. Top-level signal flow¶
The microcode address MA[12:0] is produced inside CGA_MIC (DELILAH-CPU/CGA_MIC/circuit/CGA_MIC.v). It goes out through port MA_12_0 (line 63) and drives the microcode RAM.
The internal data-path that produces MA[12:0] has four stages, in order:
(jump/ret/next/repeat source) ──► MASEL ──► W[12:0] ──┐
│
WCA[12:0] ───►┤ IPOS ──► MA[12:0]
CD[15:6] ───►┤ (mux)
TVEC[3:0] ───►┤
│
TRAPN, MAPN, EWCAN select
W[12:0] is the "would-be next micro-address" (jump / return / next-sequential / repeat), and IPOS is the last-stage mux that can overlay an opcode dispatch (CD[15:6] → page 2), a WCA-driven control-store write (WCA bus), or a trap vector (TVEC[3:0] with the two high MA bits forced) on top of W[12:0].
2. The final mux — CGA_MIC_IPOS¶
File: DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_IPOS.v.
Inputs:
| Port | Source | Role |
|---|---|---|
W_12_0 |
MASEL.regW |
Normal next microcode address |
WCA_12_0 |
CGA_MIC_WCAREG |
Address for writing new microcode |
CD_15_0 |
FIDB/CD bus (latched instruction word) | Opcode dispatch (CD[15:6] → MA[9:0]) |
TVEC_3_0 |
CGA_TRAP |
Trap vector (low 4 bits of MA) |
TRAPN |
CGA_TRAP_BRKDET |
Active-low trap pending |
MAPN |
Decoded from cycle state | Active-low "map opcode bits to MA" |
EWCAN |
Cycle control | Active-low "use WCA as MA" |
Output: MA_12_0[12:0] — the microcode RAM address.
2.1 Select-signal generation (lines 79–131)¶
s_gates1_out = NAND(MAPN , EWCA) // (EWCA = ~EWCAN)
s_gates2_out = AND (MAPN , TRAPN)
s_gates3_out = AND (TRAPN, s_gates1_out)
mux_selector[1] = ~s_gates2_out // = NAND(MAPN, TRAPN)
mux_selector[0] = ~s_gates3_out // = NAND(TRAPN, NAND(MAPN,EWCA))
mux_selector_12[0] = NAND3(TRAPN, EWCAN, MAPN)
mux_selector_12[1] = NAND (MAPN , TRAPN)
Truth table of the 2-bit selector (for bits 11..0 of MA), enumerated by activity of the three control signals:
| TRAPN | MAPN | EWCAN | sel[1] | sel[0] | Chosen source | Meaning |
|---|---|---|---|---|---|---|
| 1 | 1 | 1 | 0 | 0 | W_12_0 |
Normal microcode flow |
| 1 | 1 | 0 | 0 | 1 | WCA_12_0 |
Write-control-store address |
| 1 | 0 | x | 1 | 0 | CD_15_0 |
Opcode dispatch |
| 0 | x | x | 1 | 1 | TVEC_3_0 |
Trap vector |
(The enumeration above follows directly from the gates on lines 79–129 of CGA_MIC_IPOS.v.)
Note: when TRAPN is asserted (low), sel[1:0] == 11 regardless of MAPN / EWCAN. So a trap overrides everything else in this mux.
2.2 Bit-by-bit mux structure (lines 131–246)¶
- MA[12] is a 4-to-1 mux between
W[12],WCA[12],1,0. Its selector ismux_selector_12[1:0], which forces MA[12] to1whenTRAPN=0orMAPN=0andEWCAN=1. - MA[11] and MA[10] are 4-to-1 muxes between
W,WCA,1,0. UnderTRAPN=0the"11"input (gnd) is selected →MA[11] = MA[10] = 0. - MA[9:4] are 4-to-1 muxes between
W,WCA,CD[15:10],gnd. - MA[3:0] are 4-to-1 muxes between
W,WCA,CD[9:6],TVEC[3:0].
So on a trap the address becomes:
i.e. the trap vector lives at page 1_00_0000_xxxx (binary) = octal 10000–10017 in the control store. This is an unconditional hardware override; there is no microcode enable on it.
2.3 What is NOT an input to IPOS¶
IRQis not here. The interrupt request can only steer microcode flow by going throughTRAPN(see §4), or by being tested as a CSEL condition (see §5).IONI(STS[15]) is not here.- No ALU flag, no PIL, no PONI.
3. The W[12:0] data-path — CGA_MIC_MASEL¶
File: DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_MASEL.v.
MASEL produces W[12:0] (fed to IPOS) and IW[12:0] (fed back to IINC for next-increment and to MASEL itself for the REPEAT case).
3.1 The four sources¶
regREP_comb =
SEL_JUMP (SC6=0,SC5=0) : {CSBIT20, CSBIT[11:4], JMP[3:0]} // micro-JUMP
SEL_RETURN (SC6=0,SC5=1) : RET[12:0] // subroutine return
SEL_NEXT (SC6=1,SC5=0) : NEXT[12:0] // IW + 1 (with carry skip)
SEL_REPEAT (SC6=1,SC5=1) : IW[12:0] // re-run this word
(Lines 100–123.)
regW (= W_12_0) is a transparent latch of regREP while s_mclk_n is high, and holds the previously-registered IW while s_mclk_n is low (lines 132–138).
regIW (= IW_12_0) captures regREP on posedge s_mclk and resets to 0 when MRN=0 (lines 143–149).
3.2 The jump-target construction¶
s_jmpaddr_12_0 = { CSBIT20, // MA[12] ← control-store bit 20
CSBIT[11:4], // MA[11:4] ← CSBIT[11:4]
JMP[3:0] } // MA[3:0] ← JMP[3:0] (from ILC_MUX)
JMP[3:0] is built in the ILC_MUX in CGA_MIC.v (lines 758–781) from one of three 4-bit sources:
CSMIS0 |
CSVECT_N |
JMP[3:0] source |
|---|---|---|
| 0 | 0 | IR[3:0] |
| 0 | 1 | LAA[3:0] (latched A-register select) |
| 1 | x | CSBIT[3:0] (control-store literal) |
So a micro-JUMP's low-4-bits can be: a literal, an indirect via the latched IR, or an indirect via LAA.
3.3 NEXT — sequential increment (CGA_MIC_IINC)¶
Line 23 of CGA_MIC_IINC.v:
CIN is constructed in CGA_MIC.v lines 338–353:
Where CSWAN[1:0] comes from CGA_MIC_INCOUNT and LCS = ~ILCSN is "Internal Load Control Store". In plain terms: when the control store is being written (ILCSN=0 → LCS=0) the increment is suppressed (CIN=1, which here is the inactive state of the open-collector-style NAND), and during normal execution the CSWAN-driven write-ack gates the increment.
[UNVERIFIED] The exact semantics of CSWAN[1:0] vs LCS w.r.t. whether NEXT = IW or NEXT = IW+1 — I would need to re-read CGA_MIC_INCOUNT to pin down the polarities. The important point for this document: NEXT[12:0] is a pure function of IW[12:0], CSWAN, LCS. No trap, interrupt, or IONI signal is involved.
3.4 RETURN — microcode stack (CGA_MIC_STACK)¶
RET[12:0] is the top-of-stack output of a 74S482-style stack, clocked by SCLKN (negated MCLK). The stack operation is selected by SC[4:3]:
The stack is filled with NEXT[12:0] values on PUSH and popped on POP. No trap, interrupt, or IONI signal is involved.
3.5 Who drives SC[6:3]?¶
SC[6:3] is built inside CGA_MIC.v:
SC[3] = CSFS[3]_through_FF ( GATES_22: NOR(csts3_etrue, fs3_efalse) )
SC[4] = CSFS[4]_through_FF ( GATES_21: NOR(csts4_etrue, fs4_efalse) )
SC[5] = NOR(gates18_out, gates20_out)
SC[6] = NOR(LCSN, gates19_out)
where each of csts_etrue / fs_efalse is AND( s_etrue_or_efalse , CSTS_6_3[i] or FS_6_3[i] ), and in turn:
s_etrue = NAND(s_cond_n, gates5_out)
s_efalse = AND4(s_cond_n, csloop_n, lcs_n, csecond)
gates5_out = NAND3(LCS_N, csloop_n, csecond_n)
So SC[6:3] — and therefore which of {JUMP, RETURN, NEXT, REPEAT} MASEL selects — is a function of:
CSTS[6:3]: control-store "true-select" bits (fields of the current micro-word)FS[6:3]: control-store "false-select" bits (fromCONDREG, latched CSBIT[3:0])s_cond_n: the latched test-condition output ofCSEL(latched throughMEMORY_32, line 1085–1095)CSLOOP: micro-loop controlCSECOND: micro-"enable conditional" controlLCSN/LCS: load-control-store indicator
The selection between the JUMP / RETURN / NEXT / REPEAT sources is therefore ultimately gated by s_cond_n, which is produced by CSEL, described next.
4. Trap generation — summary of what drives TRAPN¶
TRAPN is produced by CGA_TRAP_BRKDET (DELILAH-CPU/CGA_TRAP/circuit/CGA_TRAP_BRKDET.v). The sum-of-products that drives TRAPN low (trap asserted) is, read from lines 160–264:
TRAPN low ⇐ BRK low AND CBRK high AND ETRAPN high
BRK low ⇐ gates12_out high OR CBRKN low
gates12_out= NOR3( gates10_out, gates5_out, gates20_out )
with:
gates5_out = OR4( IPV , WIP , RD2 , RV3 )— page/ring violations (all derived fromIPT_15_9+IPCR_1_0+VACC+ access type). These are MPV (memory protection violation) contributors.gates10_out = OR4( PGF , INTR , gates14_out , gates15_out )where:PGF = NAND4(VACC, ~IPT[6], ~IPT[5], ~IPT[4])— page fault.INTR = NAND(IFETCH, INTRQ)— level 10–15 interrupt, but only during IFETCH.gates14_out = NAND(VTRAP, VACC)— vector trap (illegal instruction / valid-opcode trap family).gates15_out = NAND(FTRAPN, IFETCHN)— fetch trap (II family).
gates20_out— a four-input OR ofA02_1..A02_4macros that cover additional ring/protection combinations on fetch/write/indirect/data accesses.CBRKN/ETRAPN— continue-break and external-trap inputs.
What is NOT in this equation¶
I grepped the entire CGA_TRAP/ tree for IONI, XIONI, STS, and found nothing. CGA_TRAP does not receive IONI as an input at its top-level port list (CGA_TRAP.v lines 14–36). PONI (paging-on) is the only status-register bit that feeds the trap logic, and it is used only for RESTR generation in CGA_TRAP_TVGEN.v:240–246:
So PONI gates only RESTR, not the trap dispatch itself.
5. IRQ — how it steers the microcode¶
IRQ (active-high, from CGA_INTR.IRQ) enters CGA_MIC at port IRQ (line 39) and is routed straight to CGA_MIC_CSEL as muxIn_2 of the 8-way test-condition mux (CGA_MIC_CSEL.v:121):
PLEXERS_1 (s_mux_selector = TSEL[2:0])
muxIn_0 = DZD
muxIn_1 = LCZ
muxIn_2 = IRQ ← here
muxIn_3 = RESTR
muxIn_4 = CFETCHN
muxIn_5 = OOD
muxIn_6 = SPARE
muxIn_7 = COND
So IRQ is only one of many testable conditions that the microcode can select with its TSEL[3:0] field. It is not an unconditional override. It becomes effective only if the current micro-instruction (via CONDREG → TSEL[2:0] = 010) picks IRQ as the condition to evaluate, in which case CSEL.CONDN reflects IRQ and that flows through:
CSEL.CONDN → D-FF MEMORY_32 → s_cond_n → { s_etrue, s_efalse } → SC[6:3] → MASEL selector → W[12:0] → IPOS → MA[12:0]
In other words, IRQ can cause a micro-JUMP (not a trap) by steering MASEL into SEL_JUMP, but only when the current microcode word has asked to test IRQ.
However, IRQ also goes into the CGA_INTR → INTRQN flip-flop (CGA_INTR.v:146–156) and from there into CGA_TRAP_BRKDET.INTR = NAND(IFETCH, INTRQ). That path DOES produce a hardware TRAPN assertion at the next IFETCH. See §6 below.
6. Answering the specific question¶
"Does IOF suppress the internal traps MPV, PF, II?"¶
Collecting the facts verified above:
IONIis produced at exactly one place in the CGA:CGA_ALU.v:173asIONI = s_sts_15_0[15].IONIleaves the CGA only asXIONI(CGA.v:551), which is routed to the panel.IONIis not a port of any of:CGA_MIC,CGA_TRAP,CGA_INTR,CGA_MAC,CGA_DCD. (grep IONIin each subtree returns zero matches.)- The microcode address mux in
CGA_MIC_IPOShas, as its only control signals,TRAPN,MAPN,EWCAN. None of these comes fromIONI. TRAPNinCGA_TRAP_BRKDETis derived from (PVIOL-family,PGF,VTRAP,FTRAP,CBRK,ETRAP, andINTRQgated byIFETCH). None of these derivations inspectsIONI.- The CPU side-channel by which
IONIcould affect microcode is via theCSELtest-condition mux — andIONIis not one of the 16 signals fed into CSEL's muxes either (CGA_MIC_CSEL.v:118–149).
Conclusion¶
In this Verilog implementation, IOF does NOT gate the trap-dispatch hardware. Internal traps (MPV via PVIOL, PGF page fault, VTRAP/FTRAP illegal-instruction family) will still assert TRAPN, still drive CGA_MIC_IPOS.mux_selector = 2'b11, and still steer MA[12:0] to {1, 0, 0, 0000, TVEC[3:0]} even when STS[15]=IONI=0 after an IOF.
The only thing IOF indirectly affects — insofar as any hardware in this tree inspects it — is whatever the microcode itself does when it tests status via the ALU: the microcode can read STS[15] onto FIDB, compare/branch on it, and decide whether to go into the PID/PIE service sequence. That is a microcode-level decision, not a gate-level one.
[UNVERIFIED] I have not disassembled the microcode ROM to verify how it actually tests IONI. The statement above about "microcode-level decision" is consistent with the hardware I've read, but it is not a property I can prove from the Verilog alone.
Also important — level 10–15 interrupts¶
The same analysis shows that program-level interrupts (the INTRQ path through CGA_INTR) are also NOT gated by IONI in hardware. CGA_INTR has no IONI input; CGA_TRAP_BRKDET.INTR = NAND(IFETCH, INTRQ) turns INTRQ into a trap every IFETCH, unconditionally on IONI.
This means that for IOF to actually mask level 10–15 interrupts, one of the following must be true (I could not verify which without more work):
(a) The microcode, at each IFETCH boundary where it would otherwise honour the trap, explicitly inspects STS[15] and takes a branch that re-issues the fetch instead of servicing the interrupt. [UNVERIFIED]
(b) Some gating I have missed. Candidates worth checking: CLIRQN (which resets the INTRQN flip-flop in CGA_INTR), the EPICMASKN path, or signals coming through cycle control.
(c) The hardware behaviour here diverges from the original ND-120, and this is an actual bug in the Verilog port. [UNVERIFIED]
See "Open questions" at the end.
7. Complete list of signals that impact MA[12:0]¶
Organised by the IPOS source they end up at, with origin.
7.1 Signals that select which source IPOS uses¶
| Signal | Drives | Origin |
|---|---|---|
TRAPN |
IPOS selector bit | CGA_TRAP_BRKDET (from PVIOL / PGF / VTRAP / FTRAP / INTRQ / CBRK / ETRAP) |
MAPN |
IPOS selector bit | Cycle control. [UNVERIFIED: I did not follow the chain back to its root.] |
EWCAN |
IPOS selector bit | Cycle control (write-control-store enable). |
7.2 Signals that make up the W[12:0] source (MASEL path)¶
| Signal | Role | Origin |
|---|---|---|
CSBIT20 |
MA[12] when JUMP | Microcode RAM output, control-store bit 20 |
CSBIT[11:4] |
MA[11:4] when JUMP | Microcode RAM output |
JMP[3:0] |
MA[3:0] when JUMP | ILC_MUX in CGA_MIC.v |
CSMIS0, CSVECT_N |
Selects between literal / LAA / IR as source of JMP | Microcode RAM output |
IR[3:0] |
One possible JMP source | IRLATCH in CGA_MIC.v, latched from CD[3:0] |
LAA[3:0] |
One possible JMP source | LAA_REG (driven by CSRASEL mux on CSBIT, PIL, IR, LC) |
CSBIT[3:0] |
One possible JMP source (literal) | Microcode RAM output |
RET[12:0] |
The RETURN source | CGA_MIC_STACK |
NEXT[12:0] |
The NEXT source | CGA_MIC_IINC |
IW[12:0] |
The REPEAT source; also the input to IINC and the stack | MASEL.regIW |
CIN (for NEXT) |
Whether to increment IW | CSWAN[1:0] from INCOUNT ∧ LCS |
SC5, SC6 |
Selector between JUMP/RET/NEXT/REPEAT | Logic in CGA_MIC.v (see §3.5), ultimately from s_cond_n |
SC3, SC4 |
Stack op (HOLD/POP/LOAD/PUSH) | Same logic in CGA_MIC.v as SC5/SC6 |
MRN |
Async reset for MASEL.regIW (and stack via SCLKN) |
System reset |
7.3 Signals that make up s_cond_n (and therefore SC[6:3])¶
All feed CSEL (CGA_MIC_CSEL.v):
| Signal | Meaning | Origin |
|---|---|---|
TSEL[3:0] |
Which condition to test | CONDREG (latched CSBIT[7:4]) |
COND |
Previous-cycle latched CSEL output (feedback) | CGA_MIC.MEMORY_32 |
CRY |
Carry flag | CGA_ALU |
DZD |
Divide-by-zero-detect | CGA_MIC.DZD_FF |
F11, F15 |
Flag bits 11 and 15 | CGA_ALU |
IRQ |
Interrupt request (only path of IRQ into MIC address) | CGA_INTR |
LCZ |
Loop-counter-zero | CGA_MIC LC compare |
OOD |
Out-of-data | CGA_MIC.OOD_FF |
OVF |
Overflow | CGA_ALU |
RESTR |
Restore (from TRAP — not IONI-gated) | CGA_TRAP_TVGEN |
SPARE |
Spare (reserved) | external |
STP |
Stop | external |
ZF |
Zero flag | CGA_ALU |
CFETCH |
Continuous-fetch | Cycle control |
ALUCLK |
Clock for the CSEL latch | Cycle control |
7.4 Signals that build the WCA[12:0] source (WCAREG)¶
CGA_MIC_WCAREG latches CD[15:0] under LWCAN. Governed by LCSN / LWCAN / MCLK. These gate where new microcode gets written, but cannot directly steer normal-mode execution because EWCAN determines whether the WCA mux path is picked at all.
7.5 Signals that build the CD-dispatch (opcode) source¶
CD[15:6] comes from the FIDB/CD bus latch. When MAPN=0 and TRAPN=1, IPOS places CD[15:10] on MA[9:4] and CD[9:6] on MA[3:0] and forces MA[12]=1, MA[11:10]=0. This is the opcode dispatch — the entry point from the currently-latched instruction word to its microcode handler.
7.6 Signals that build TVEC[3:0]¶
TVEC[3:0] is generated by CGA_TRAP_TVGEN_P2 from:
PGF,PVIOL, the per-ring violation linesRD,RV,WIP,PGUINTRQ(gated withIFETCH)VTRAPN,FTRAPN,DSTOPNPAN(panel),VACC(valid-access),TCLK
Again: no IONI in this derivation.
8. Module dependency graph (address-relevant only)¶
┌──────────────┐
│ CGA_ALU │─── IONI = STS[15] ─────────────► XIONI (panel only)
└──────────────┘
┌──────────────┐ INTRQN ┌────────────────┐ TRAPN ┌───────────┐
BINT, │ │───INTRQ──────────►│ CGA_TRAP_BRKDET│───────────►│ │
PAN, │ CGA_INTR │ (IFETCH) │ (+TVGEN) │ │ │
MOR, │ │ ┌──────►│ │──TVEC────► │ CGA_MIC │─MA[12:0]─►
POW, └──────────────┘ │ └────────────────┘ │ │ microcode
IOX, │ │ ▲ │ │ RAM
PARERR │ │ PT, PCR │ │ │
└────── IRQ ────────┴──────────────┴────► PONI (for RESTR)│ │
(to CSEL also) └───────────┘
▲
│
CD[15:0] (opcode dispatch)
W[12:0] (MASEL: JUMP/RET/NEXT/REPEAT)
WCA[12:0] (control-store write addr)
MAPN, EWCAN, TRAPN (mux selectors)
IONI has no arrow into this graph.
9. Answers to the six open questions¶
After tracing the Verilog sources and the microcode listing in Code/Microcode/ND-120-DELILAH-L.LISTING.txt, here is what the code actually says.
Q1 — How does IOF actually suppress interrupts, if not via hardware gating on IONI?¶
Answer: IOF is implemented as a PIC command issued by the microcode, not as a direct write to STS[15].
Evidence:
- Microcode listing line 46–47:
| 28 | PIC,IOF | … 150000 0 0 X00. | 15 | USED TO GIVE 'IOF'-CMD. TO INSTYS | | 29 | PIC,ION | … 170000 0 7 HAO | 17 | USED TO GIVE 'ION'-CMD. TO INSTYS |IOFandIONare CSCOMM commands 15 and 17 (octal) that the microcode issues to the PIC ("INSTYS" = interrupt system). - Microcode routine for the
IOFinstruction, at octal address000661: i.e. the microcode for the IOF instruction is literally one line that issuesPIC,IOF. - Corresponding
IONat octal000663issuesPIC,ION. - These CSCOMM codes are decoded in
CGA_DCD.v(see Q6 below for the decode pattern), producing control signals that set/clear an enable inside the PIC / interrupt controller.
[UNVERIFIED] I have not yet located the exact flip-flop in CGA_INTR (or elsewhere in the PIC chain) that is written by CSCOMM 15/17. What I can say for certain is that the PIC-side mask (EMPIDN, EPICMASKN) and the interrupt-request latch (via CLIRQN) are all driven by microcode CSCOMM decodes, and that IONI = STS[15] is a software-visible reflection of interrupt state rather than the primary gate.
Q2 — Is INTRQ → TRAPN being ungated by IONI the correct ND-120 behaviour?¶
Answer: yes — but not for the reason originally assumed. The gating happens inside the PIC, upstream of INTRQN, not downstream of it.
Evidence:
CGA_INTRdoes not receiveIONI.CGA_TRAP_BRKDETdoes not receiveIONI.- The microcode listing has this comment at octal
000043(line 934 in the OCR): This comment tells you that the microcode dispatch point at000043is reached when no interrupt is pending, and that the cause can be "a pagefault taken while the CPU is inIOFstate". This directly confirms: pagefaults fire duringIOF; interrupts do not. The gating of interrupts happens upstream, in the PIC, as a consequence of thePIC,IOFCSCOMM command.
Q3 — MAPN origin (traced)¶
Answer: MAPN is produced by PAL 44307C inside the cycle controller.
Evidence (PAL\PAL_44307C.v:86):
So MAP_n asserts (low) during the FORM phase (opcode decode), on cycle step CC2, when no break is pending and no terminate is pending. It is output from CYC_36.v:181 (cycle controller) and routed into CGA as XMAPN via CPU_PROC_CGA_33.v:248:
Role in MA[12:0]: when MAPN=0 and TRAPN=1, IPOS overlays CD[15:10] on MA[9:4] and CD[9:6] on MA[3:0], i.e. opcode dispatch. This is the entry point from the just-fetched instruction word into its microcode handler.
Q4 — MPV / PF terminology (traced)¶
Answer:
- MPV (Memory Protection Violation) = the PVIOL signal family: PVIOL = PGF ∨ WPV ∨ IPV ∨ FPV ∨ RPV (from CGA_TRAP_TVGEN.v:147–156).
- PF (Page Fault) = the PGF signal specifically: PGF = VACC ∧ ~IPT[6] ∧ ~IPT[5] ∧ ~IPT[4] (page-not-present, from CGA_TRAP_TVGEN.v:248–256).
- PONI ("Memory Protection ON") is a separate enable bit from IONI ("Interrupt ON"). Every PONI port comment in the HDL reads //! Memory Protection ON, PONI=1. PONI is managed by POF / PON style microcode commands — not IOF / ION.
PONI does gate paging at CGA_MAC_PTSEL.v (it preset-forces the PT-select JK flip-flop when PTM=0 and PONI=0, line 68–74), so when memory protection is off, the page-table bits that drive PGF/PVIOL behave as if everything is present/unrestricted. But that is POF, not IOF.
Q5 — II (illegal instruction) — MAJOR FINDING¶
Answer: the hardware trap inputs VTRAPN and FTRAPN are tied HIGH (inactive) at the board level. Illegal-instruction traps in this Verilog are handled entirely in microcode, not by the hardware trap path.
Evidence — CPU-BOARD-3202\circuit\CPU_PROC_CGA_33.v:244-245:
So the CGA inputs for fetch-trap and vector-trap are pulled inactive. None of BRKDET.gates14_out = NAND(VTRAP, VACC) or BRKDET.gates15_out = NAND(FTRAPN, IFETCHN) can ever assert.
Illegal instructions are instead caught in microcode. Evidence from the microcode listing:
- Line 1202:
ILLIN: A,7 ALUD,NONE IDBS,BMG COMM,SMPID T,JMP T,HOLD ILL12;TheILLINroutine issuesCOMM,SMPID(CSCOMM=12 → set PIC mask, inhibit all interrupts) and jumps toILL12. - Line 1203:
PRIV1: A,11 ALU,NONE IDBS,BMG COMM,SMPID T,JMP T,HOLD ILL12;Similar for privilege violations.
So "II" in this codebase is not a hardware trap — it is a microcode-level detection inside the opcode-dispatch handler.
Q6 — CLIRQN and EPICMASKN paths (traced)¶
Answer: both are driven by CSCOMM decodes in CGA_DCD.v. They are the actual microcode-controlled masking mechanisms for external interrupts.
Evidence:
CGA_DCD.v:1099-1132—CLIRQNis asserted (low) whenCSCOMM_4_0 = 00100binary = 4 octal (a CLIRQ command):NAND6( lcs_n , icscomm_n[4] , icscomm_n[3] , icscomm[2] , icscomm_n[1] , icscomm_n[0] ) → s_iclirq_group D-FF d=s_iclirq_group , qBar=s_iclirq s_clirq_n_out = ~(s_iclirq | s_mr) // also cleared by master resetCLIRQNdrivesresetof theINTRQNflip-flop inCGA_INTR.v:146–156, i.e. it clears the pending interrupt request after the microcode has serviced it.
EMPIDN— CSCOMM=12 octal ("SMPID, set mask reg, inh all ints") perDECODE_DGA_COMM.v:45:output EMPIDN, //! Enable MPID - Set bits in the micro-PID (Priority Interrupt Detect) // register in the PIC. Command #012. "set mask reg: inh all ints"EMPIDNfeedsCGA_INTR.EMPIDNand from there into the PIC mask logic (CGA_INTR_IRSRC,CGA_INTR_CNTLR_MDCD). This is the primary microcode-level mechanism to disable all external interrupts.
EPICMASKN— Generated inCGA_INTR_CNTLR_MDCD.v:144and routed back through the IDB (viaCGA_IDBCTL) so microcode can read the current PIC mask into the register file.
EPIC— CSCOMM=13 octal (from the NAND pattern atCGA_DCD.v:1137-1147). Enables the PIC mask update.
So the actual mask path used by IOF/ION is:
microcode: IOF instruction @ 000661
├─ issues CSCOMM=15 (PIC,IOF)
│
▼
CGA_DCD.v decodes CSCOMM=15
├─ (asserts some still-to-be-pinpointed PIC-enable signal)
│
▼
CGA_INTR (PIC) mask state updates, INTRQN stays high
│
▼
CGA_TRAP_BRKDET: INTR = NAND(IFETCH, INTRQ) stays inactive
│
▼
TRAPN not asserted for external interrupts.
Pagefault and MPV (PGF, PVIOL) bypass this entire chain — they are generated directly in CGA_TRAP_TVGEN from IPT + IPCR + VACC etc., and feed BRKDET independently.
10. Final answer to the original question¶
Bringing all of the above together:
When IONI (STS[15]) is off after IOF:
| Trap type | Code signal(s) | Hardware-disabled by IOF? | Why |
|---|---|---|---|
| MPV (memory protection viol.) | PVIOL = PGF ∨ WPV ∨ IPV ∨ FPV ∨ RPV |
No | CGA_TRAP has no IONI input; path to TRAPN unconditional |
| PF (page fault) | PGF |
No | Same as above |
| II (illegal instruction) | N/A (handled in microcode) | No | VTRAPN/FTRAPN are tied to 1 at board level; caught by microcode ILLIN/PRIV1 routines which always run |
| Level 10–15 external interrupts | INTRQ → TRAPN via IFETCH NAND |
Yes, but upstream in the PIC (via the CSCOMM command the IOF microcode issues), not by IONI gating in the trap-dispatch hardware |
IONI itself — the STS[15] bit — is effectively a software-visible flag maintained in parallel with the real PIC state, and displayed on the operator panel via XIONI. The actual interrupt suppression is done by the PIC state that the PIC,IOF CSCOMM command mutates.
Direct restatement for your original question:
"Will MPV, PF, and II internal interrupts trap the microcode if
IONIis off afterIOF?"
- MPV: yes.
PVIOL → BRKDET → TRAPN → IPOSfires regardless. - PF: yes.
PGF → BRKDET → TRAPN → IPOSfires regardless. - II: yes — via microcode, not via the hardware trap path. The
VTRAPN/FTRAPNinputs are tied inactive on this board; theILLIN/PRIV1microcode routines run during opcode dispatch independently ofIONI.
11. Second pass — the three previously-unverified items¶
On the follow-up investigation, I traced all three remaining items and significantly narrowed the uncertainty. Here are the findings.
11.1 STS[15] (IONI) and STS[14] (PONI) — how they are written¶
Verified. From CGA_ALU_STS.v:184–200, the two bits share identical structure:
SCAN_FF STS15_FF ( .CLK(ALUCLK), .D(FIDBO[15]), .TE(LDPILN), .TI(STS_15_0_out[15]), .QN(s_sts_15_n) );
SCAN_FF STS14_FF ( .CLK(ALUCLK), .D(FIDBO[14]), .TE(LDPILN), .TI(STS_15_0_out[14]), .QN(s_sts_14_n) );
A SCAN_FF captures D when TE=0 and TI (feedback) when TE=1. So both IONI and PONI are written from FIDBO[15:14] whenever LDPILN is asserted (low). All of STS[15:8] share the same LDPILN enable — loading PIL simultaneously updates IONI, PONI, DOUBLE (STS[13]), … and PIL[3:0]. There is no dedicated IONI-only or PONI-only write port.
11.2 LDPILN is CSCOMM=01 octal¶
Verified. CGA_DCD.v:810–820:
Pattern 00001 binary = CSCOMM=01 octal. So LDPILN asserts when the microcode issues COMM,LDPIL (or an equivalent named field). This is the path that physically writes STS[15]=IONI and STS[14]=PONI.
11.3 The IOF instruction microcode routine¶
Verified (partial). From the symbol (label -> address) index of the scanned listing Code/Microcode/ND-120 Mikroprogramlisting-L-ocr.pdf (the re-typed ND-120-DELILAH-L.LISTING.txt has no symbol index; the PDF page was not looked up):
The microcode listing at 003461 (ND-120-DELILAH-L.LISTING.txt lines 9422-9425) reads:
So the IOF instruction microcode:
- loads A = STS (reads the STS register)
- ALUF = PASSA (passes A through the ALU)
- IDBS = ALU (puts result on IDB)
- jumps to subroutine STSST (Status STore?) with STS.LO modifier — [UNVERIFIED] which bits STS.LO actually writes, but the name ("STS low") and the fact that this routine is shared between IOF, ION, POF, PON, SEX, REX strongly suggests it is the generic "modify status bits" subroutine.
STSST (STS STore) is shared across all instructions that flip status bits — a reasonable design, and it explains why the dedicated IOF2 microcode stub at 000661 (lines 2937–2938) is so short:
This microcode word issues two commands simultaneously:
- COMM,EPIC (CSCOMM=13 octal, from CGA_DCD.v:1137-1147) — asserts the EPIC control line, which enables PIC-side subcommand decoding for the current cycle.
- PIC,IOF — the PIC subcommand carried in a separate microcode field, latched by the PIC when EPIC=1.
So IOF is a two-stage decode: the main CSCOMM decoder issues EPIC, and the PIC-side decoder then interprets the additional PIC,xxx field.
[UNVERIFIED] I did not pinpoint the exact NAND gate inside CGA_INTR_CNTLR_MDCD / CGA_INTR_CNTLR that latches the PIC,IOF subcommand — following the EPIC-gated path into the PIC's internal state machine would be a separate trace of several files.
11.4 The EMPID mask — definitive confirmation¶
The bottom-up evidence from DECODE_DGA_COMM.v:621-631 is the cleanest:
NAND_GATE_6_INPUTS A183 (
.input1(s_cscomm_4_n), // 0
.input2(s_cscomm_4_0[3]), // 1
.input3(s_cscomm_2_n), // 0
.input4(s_cscomm_4_0[1]), // 1
.input5(s_cscomm_0_n), // 0 = 01010 = 0xA = 10d = EPIC.LDMPIE (set mask reg: inh all ints)
.input6(s_lcs_n),
.result(s_iempid_n)
);
The inline comment explicitly identifies CSCOMM=012 octal (binary 01010) as the EPIC.LDMPIE / SMPID command that inhibits all interrupts via the PIC mask register. EMPIDN drives CGA_INTR.EMPIDN, which enters CGA_INTR_IRSRC (IRSRC.v:68) and feeds the mask-source logic.
11.5 Consolidated picture of what IOF actually does¶
Putting it all together:
Microcode instruction IOF (@ 003461)
│
▼
• loads STS → IDB via ALU pass-through
• calls subroutine STSST with STS.LO modifier
│
▼
STSST (uses LDPIL mechanism, TBD location)
│
├──► COMM,LDPIL (CSCOMM=01)
│ │
│ ▼
│ LDPILN asserts → STS[15:8] <= FIDBO[15:8]
│ (IONI and PONI updated to new value on FIDB)
│
▼
IOF2 stub (@ 000661)
│
├──► COMM,EPIC (CSCOMM=13)
│ │
│ ▼
│ EPIC flag set
│
└──► PIC,IOF (sub-command latched by PIC under EPIC=1)
│
▼
(internal PIC enable flip-flop cleared → INTRQN stops firing)
Two independent state updates happen: the software-visible STS[15] via the ALU/LDPIL path, and the actual PIC-level enable via the EPIC/PIC-subcommand path. They are kept coherent by the microcode routine, not by hardware. The microcode guarantees the two are updated together.
11.6 PONI control path¶
Verified (partial). From the microcode listing, the instruction entries exist (e.g. POF, PON, POFZ, etc. in the symbol table). By the same analysis:
PONI = STS[14]is written only viaLDPIL(CSCOMM=01), exactly like IONI.- The
POF/PONmicrocode routines (I did not trace them in detail) almost certainly follow the same pattern: load STS via ALU, issueLDPILwith the right value on FIDB[14]. There may or may not be a separate paging-side enable command analogous toPIC,IOF. PONIadditionally gates paging inCGA_MAC_PTSEL.v:68–74via a JK-flip-flop preset, and gates one trap-related signal (RESTRinCGA_TRAP_TVGEN.v:240-246).
No PONI signal feeds any IPOS / MIC / TRAP trap-dispatch equation.
11.7 Remaining fully-unproven items¶
Tight list of what I still could not nail down from the HDL + microcode listing alone:
-
The exact gate inside
CGA_INTR_CNTLR_MDCD/CGA_INTR_CNTLRthat latches thePIC,IOF/PIC,IONsubcommand into a PIC-internal enable flip-flop. (I foundEMPIDN,EPIC,CLIRQN,EPICMASKN,EPICSN,EPICVNas PIC control inputs but did not produce a complete table mapping PIC subcommand-codes → internal PIC FFs.) -
The precise semantics of
STS.LOas a microcode modifier field — my best read from context is "write only the low half of STS" (so IONI/PONI don't get touched). But that read doesn't match the fact thatIOFcalls this routine and must update IONI. [UNVERIFIED] -
Whether the
POF/PONinstructions use an analogous EPIC+PIC-subcommand mechanism, or whether they directly LDPIL without a paging-side dispatch. I have not read their microcode.
Items 1 and 2 would require either (a) the ND-120 microprogrammer's guide referenced in the repo (ND-06.031.1 EN ND-110 and ND-120 Microprogrammer's Guide-Gandalf-OCR.pdf) or (b) a scripted decode of every microcode word that uses STS.LO and STSST.
None of these remaining uncertainties change the top-level answer: MPV and PF trap the microcode regardless of IONI, and II is handled by the microcode opcode-dispatch regardless of IONI (with the hardware VTRAPN/FTRAPN paths disabled at board level).
12. Files referenced¶
Verilog/DELILAH-CPU/CGA/circuit/CGA.vVerilog/DELILAH-CPU/CGA_ALU/circuit/CGA_ALU.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_IPOS.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_MASEL.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_IINC.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_CSEL.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_CONDREG.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_STACK.vVerilog/DELILAH-CPU/CGA_MIC/circuit/CGA_MIC_WCAREG.vVerilog/DELILAH-CPU/CGA_TRAP/circuit/CGA_TRAP.vVerilog/DELILAH-CPU/CGA_TRAP/circuit/CGA_TRAP_BRKDET.vVerilog/DELILAH-CPU/CGA_TRAP/circuit/CGA_TRAP_TVGEN.vVerilog/DELILAH-CPU/CGA_TRAP/circuit/CGA_TRAP_TBUF.vVerilog/DELILAH-CPU/CGA_INTR/circuit/CGA_INTR.v