diff --git a/test/test_intel/autosearch/12_4004_authoritative_spec.md b/test/test_intel/autosearch/12_4004_authoritative_spec.md new file mode 100644 index 00000000..4271067d --- /dev/null +++ b/test/test_intel/autosearch/12_4004_authoritative_spec.md @@ -0,0 +1,112 @@ +# Intel 4004 Authoritative Specification + +Sources (in `autosearch/pdfs/`): + +- **[M4]** *MCS-4 Micro Computer Set Users Manual*, Intel, **Feb 1973** (175 pp). Page numbers below are the printed body footers. +- **[M40]** *MCS-40 User's Manual*, Intel, Nov 1974 — Ch. 1 also describes the 4004; used as cross-check. +- **[I]** olbits MCS-4 instruction reference (e4004.szyc.org/iset.html) — used only to cross-check opcode hex against [M4] Table V. + +--- + +## 1. Pin contract — 16-pin DIP ([M4] §III, pp. 7–9; supplies p. 102) + +The 4004 (Nov 1971) is the first commercial single-chip microprocessor. PMOS, two-phase dynamic clock; **Vss = GND, Vdd = −15 V ±5%**. Logic 1 = low (negative) voltage; logic 0 = Vss ([M4] p. 6). + +| Pin | Name | Dir | +|---|---|---| +| 1–4 | D0..D3 | I/O — multiplexed 4-bit data/address bus (D3 = MSB) | +| 5 | Vss | GND | +| 6, 7 | Φ1, Φ2 | In — non-overlapping clocks | +| 8 | SYNC | Out | +| 9 | RESET | In | +| 10 | TEST | In | +| 11 | CM-ROM | Out | +| 12 | Vdd | −15 V | +| 13–16 | CM-RAM3..CM-RAM0 | Out (CM-RAM0 = pin 16) | + +[M4] p. 14 confirms pin 9 = RESET, pin 10 = TEST. CM-RAM0 auto-selected after RESET ([M4] pp. 6, 14). + +## 2. The 8-phase instruction cycle ([M4] Fig. 2 p. 6, text pp. 5–6) + +One cycle = **8 clock periods** = 10.8 µs at 750 kHz. Phases in order: **A1, A2, A3, M1, M2, X1, X2, X3**. Verbatim p. 5: *"the CPU sends 12 bits of address (in three 4-bit bytes on the data bus) … in the first three cycles (A1, A2, A3). … The selected ROM chip sends back 8 bits of instruction (OPR, OPA) to the CPU in the next two cycles (M1, M2)."* + +| Phase | Driver | Contents | +|---|---|---| +| A1 | CPU | PC[3:0] — **low nibble first** (Fig. 2: "Lower 4-bit Address") | +| A2 | CPU | PC[7:4] | +| A3 | CPU | PC[11:8] (high 4 bits = chip select 1-of-16) | +| M1 | ROM | OPR (high nibble of opcode) | +| M2 | ROM | OPA (low nibble) | +| X1 | CPU | execute (idle on bus for most ops) | +| X2 | CPU/ROM | SRC: chip-select addr; I/O read: ROM/RAM drives ACC data | +| X3 | CPU | SRC: char addr; otherwise idle | + +Endianness on the bus: **PC low-nibble first**, chip-select last. + +## 3. SYNC ([M4] p. 6, Fig. 2) + +Generated once every 8 clocks; one clock wide; asserted during **phase X3** to mark end-of-cycle / beginning of A1 of the next. 4001/4002 derive their internal phase counters from SYNC + Φ2. + +## 4. CM-ROM / CM-RAM strobes ([M4] pp. 13–14, Fig. 4) + +- **SRC**: at **X2**, CM-ROM and the selected CM-RAMᵢ go true together while the bus carries the 4 high bits of the SRC address (chip select). At **X3** the bus carries char address. +- **I/O & RAM instruction (WRM/RDM/WRR/...)**: at **M2** of that instruction's cycle, CM-ROM + selected CM-RAMᵢ are re-asserted so the latched chip executes OPA (which is on the bus at M2) ([M4] p. 14, step 4). + +## 5. Register file ([M4] §III.A pp. 7–10, Tables III–IV) + +- **Program counter & stack**: 4 × 12 bits — one is the live PC, three form the push-down stack ⇒ **3-deep subroutine stack** ([M4] p. 7). +- **Index registers**: 16 × 4 bits, also addressable as 8 × 8-bit pairs R0R1…R14R15 ([M4] §III.B.2 p. 12). +- **Accumulator** 4 bits + **carry/link** flip-flop CY ([M4] p. 8). +- **Command-control register** (3 bits, latched by DCL). + +## 6. Instruction set — 46 opcodes ([M4] Table V pp. 15–16, cross-checked with [I]) + +`*` = 2-byte. First byte shown. + +| Opcode | Mnemonic | Notes | +|---|---|---| +| `00` | NOP | | +| `1C` (`10..1F`) | *JCN cccc | + addr byte; in-page branch | +| `20,22,…,2E` | *FIM Pn | + imm8 → reg pair | +| `21,23,…,2F` | SRC Pn | send pair as RAM/ROM addr at X2/X3 | +| `30…3E` | FIN Pn | indirect ROM fetch via P0 | +| `31…3F` | JIN Pn | jump to (PC.high : Pn) | +| `40..4F` | *JUN | + addr byte; 12-bit jump | +| `50..5F` | *JMS | + addr byte; push PC+2; jump | +| `60..6F` | INC Rn | | +| `70..7F` | *ISZ Rn | + addr; in-page branch if ≠0 | +| `80..8F` | ADD Rn | A ← A + Rn + CY | +| `90..9F` | SUB Rn | A ← A + ~Rn + ~CY | +| `A0..AF` | LD Rn | | +| `B0..BF` | XCH Rn | CY unaffected | +| `C0..CF` | BBL d | pop; A ← d | +| `D0..DF` | LDM d | | +| `E0..EF` | I/O+RAM grp | WRM(E0) WMP(E1) WRR(E2) WPM(E3) WR0..3(E4..E7) SBM(E8) RDM(E9) RDR(EA) ADM(EB) RD0..3(EC..EF) | +| `F0..FD` | ACC group | CLB(F0) CLC(F1) IAC(F2) CMC(F3) CMA(F4) RAL(F5) RAR(F6) TCC(F7) DAC(F8) TCS(F9) STC(FA) DAA(FB) KBP(FC) DCL(FD); FE/FF unused | + +## 7. JCN condition encoding ([M4] p. 27 + p. 16 footnote (1)) + +OPA bits **C1 C2 C3 C4** (D3..D0): C1=1 → invert sense; C2=1 → ACC==0; C3=1 → CY==1; C4=1 → TEST pin == 0 (high voltage, i.e. logic-0). Logic ([M4] p. 28): +`JUMP = C1·((ACC=0)·C2 + (CY=1)·C3 + TEST·C4) + ~C1·~(…)`. **Page-wrap exception**: if JCN sits at words 254/255, the taken target lands on the *next* page ([M4] p. 28). + +## 8. JMS / BBL ([M4] §III.B.3 pp. 12–13, Table IV; JMS p. 28) + +JMS is 2 bytes; **the return address pushed = PC+2** ([M4] p. 12 footnote (3)). Push moves PC up one stack level; depth = 3. Verbatim [M4] p. 13: *"If a fourth JMS occurs, the deepest return address (the first one stored) is lost."* BBL pops + ACC ← D. + +## 9. TEST pin ([M4] p. 14 + p. 28) + +Asynchronous; sampled by condition logic only when JCN is being executed. "Jump if test = logic 0" means the pin is at the high (Vss) level — i.e. active-low in conventional polarity. + +## 10. Reset state ([M4] §III.A.5 p. 9) + +Verbatim: *"During reset … all RAM's and static FF's are cleared, and the data bus is set to 0. After reset, program control will start from 0 step and CM-RAM0 is selected. To completely clear all registers and RAM locations in the CPU the reset signal must be applied for at least 8 full instruction cycles (64 clock cycles) … (256 clock cycles for the 4002 RAM)."* + +→ After RESET held ≥ 64 CPU clocks: **PC=0, ACC=0, CY=0, all 16 index regs = 0, all 3 stack words = 0, CM-RAM0 selected, condition FF = 0**. + +--- + +## Open questions + +- **SYNC pulse exact polarity / phase boundary** — [M4] Fig. 2 is a poor scan; the pulse sits on X3 but the rising-vs-falling edge isn't recoverable from the OCR. Cross-validate against the 4004.com redrawn schematics. +- **CM-ROM during a normal opcode fetch** — [M4] Fig. 4 details only SRC and the I/O instruction; for a plain fetch CM-ROM is asserted A3→M2 in every emulator surveyed but the manual does not say so explicitly. +- **What X1 is used for on most instructions** — Table V doesn't tabulate per-phase activity; reference emulators treat X1 as a no-op cycle for fetch+execute. diff --git a/test/test_intel/autosearch/13_4040_authoritative_spec.md b/test/test_intel/autosearch/13_4040_authoritative_spec.md new file mode 100644 index 00000000..6e88e614 --- /dev/null +++ b/test/test_intel/autosearch/13_4040_authoritative_spec.md @@ -0,0 +1,104 @@ +# Intel 4040 — Delta Spec vs 4004 + +Source (in `autosearch/pdfs/`): + +- **[M40]** *MCS-40 User's Manual for Logic Designers*, Intel, **Nov 1974**. Page numbers below are the printed Ch. 1 footers ("1‑5", etc.). + +The 4040 (1974) is binary-compatible with the 4004 — the 46 original opcodes execute identically — and adds 14 new opcodes plus interrupt, single-step, extended ROM/register banks ([M40] §1 p. 1‑1, summary p. 1‑22). + +--- + +## 1. Pin contract — 24-pin DIP ([M40] Pin Description table pp. 1‑5/1‑6, Fig. 1‑5) + +Vss = GND; Vdd = Vss − 15.0 V ±5%; PMOS, two-phase clock. + +| Pin | Name | Description ([M40] verbatim where quoted) | +|---|---|---| +| 1–4 | D0..D3 | Bidirectional 4-bit data/address bus | +| 5 | STPA | "STOP ACKNOWLEDGE output … open drain" | +| 6 | STP | "STOP input … logic 1 causes the processor to enter the STOP mode" | +| 7 | INT | "INTERRUPT input signal" | +| 8 | INTA | "INTERRUPT ACKNOWLEDGE output … remains active until cleared by the new BRANCH BACK and SRC (BBS) instruction"; open drain | +| 9 | Vss | GND (most positive) | +| 10, 11 | Φ1, Φ2 | Non-overlapping clocks | +| 12 | RESET | "A 1 level … forces the program counter to 0. To completely clear … RESET must be applied for **96 clock cycles (12 machine cycles)**" | +| 13 | TEST | sampled by JCN | +| 14 | Vdd | Main supply, Vss − 15 V | +| 15 | Vdd2 | Output-buffer supply (may be reduced) | +| 16 | SYNC | Marks start of instruction cycle | +| 17–20 | CM-RAM0..3 | RAM bank select | +| 21 | Vdd1 | Timing-circuit supply; "only SYNC will be generated when this pin is the only active Vdd" — standby | +| 22 | CM-ROM0 | ROM bank-0 select | +| 23 | CM-ROM1 | ROM bank-1 select (NEW vs 4004) | +| 24 | CY | "CARRY output buffer … updated at X1"; open drain (NEW) | + +vs the 4004 the 4040 adds: STP, STPA, INT, INTA, a second CM-ROM line, a CY pin, and dual standby supplies. INT/STP/STPA/INTA are active-high logic-1 (negative voltage). + +## 2. New instructions — 14 total ([M40] p. 1‑22 summary) + +All 14 use OPR=`0000` and live in OPA `0001..1110` of the 4004 (NOP `0000 0000` is preserved). + +| Mnemonic | Opcode | Effect | +|---|---|---| +| HLT | `01` | Halt; HALT and STOP FFs set at X3 | +| BBS | `02` | Branch back from interrupt: restore PC, SRC reg, index-bank FF | +| LCR | `03` | ACC ← Command Register | +| OR4 | `04` | ACC ← ACC OR R4 | +| OR5 | `05` | ACC ← ACC OR R5 | +| AN6 | `06` | ACC ← ACC AND R6 | +| AN7 | `07` | ACC ← ACC AND R7 | +| DB0 | `08` | Designate ROM bank 0 (CM-ROM0); takes effect 3 cycles later | +| DB1 | `09` | Designate ROM bank 1 (CM-ROM1) | +| SB0 | `0A` | Select index-register bank 0 | +| SB1 | `0B` | Select index-register bank 1 | +| EIN | `0C` | Enable interrupt | +| DIN | `0D` | Disable interrupt | +| RPM | `0E` | Read program memory (via 4289 device) | + +`0F` is unused. The brief asked about a "JNT" — there is none; "jump on no test" is just JCN with C1=1, C4=1 (same as on 4004). + +## 3. Interrupt sequence ([M40] §"INTERRUPT and STOP Control Logic" pp. 1‑11..1‑13, Fig. 1‑11) + +When INT is sampled high at M2 and EIN is in effect: +1. Current instruction completes. +2. The next cycle becomes a forced JMS to **page 0, location 3** ([M40] p. 1‑12 verbatim: *"The subroutine address is forced to be page 0, location 3"*) — i.e. PC ← `0x003`. The pre-interrupt PC (NOT incremented) is pushed. +3. At **X3** of that cycle the **INTA flip-flop is set** and the INTA pin asserts. +4. The index-register bank FF and the SRC register are saved automatically; bank FF resets to 0. +5. Handler runs from `0x003`. INTA stays asserted (blocking further interrupts) until the handler executes **BBS**, which pops the stack, restores PC, re-emits the saved SRC at X2/X3 with CM-ROM/CM-RAM at X2 (re-arming the previously selected ROM/RAM), restores the bank FF, and clears INTA. + +There is no vector table — INT always lands at `0x003`. + +## 4. STOP / STOP-ACK protocol ([M40] §"STOP/HALT Mode Operation" pp. 1‑10..1‑11, Fig. 1‑9, 1‑10) + +- STP=1 latched at M2 → internal STOP FF set at X3 → CPU executes NOPs in a loop (clock keeps running) and STPA asserts. +- Resume: STP=0 latched at M2 of cycle N → STOP FF reset at X3 → "Normal processor operation resumes at instruction cycle N+1" ([M40] p. 1‑10). +- HLT sets STOP and HALT FFs at X3; exit via STP pulse OR INT (Fig. 1‑13 shows INT-exit forcing HALT FF reset, then taking the interrupt at `0x003`). +- INT and STP latched together: **STP wins** ([M40] p. 1‑13). + +## 5. Extended index-register file — 24 × 4 bits ([M40] p. 1‑11) + +Three banks of 8 registers (R0..R7): +- **Bank 0** (default after RESET, SB0): low 8 registers. +- **Bank 1** (SB1): mirror low 8. +- **Upper bank R8..R15**: shared, always visible. + +R16..R23 are **not directly named** — they are Bank-1's incarnation of R0..R7. SB0/SB1 FF auto-saved/restored across interrupt. + +## 6. PC stack — 8 × 12 bits = **7-deep** subroutine nesting ([M40] §"Extended Address Register Stack" p. 1‑12; bullet on p. 1‑1) + +One slot holds the live PC, the other 7 are return addresses; on overflow the deepest is lost (same behaviour as 4004). + +## 7. Other deltas + +- **Two CM-ROM lines** (DB0/DB1 select) → 8 K × 8 ROM addressable ([M40] p. 1‑12). DB0/DB1 take effect on the **3rd cycle after execution**, and INT/STOP/HALT are internally inhibited during those 3 cycles ([M40] p. 1‑13). +- **CY exposed on pin 24**, updated at X1 ([M40] p. 1‑6). +- RESET clears interrupt enable; an EIN must be re-issued before INT is honored ([M40] p. 1‑13). +- Cycle time same as 4004: 8 clock periods per machine cycle. + +--- + +## Open questions + +- Exact phase relationships of **STPA** and **INTA** open-drain transitions — [M40] Fig. 1‑9 implies STPA goes active the cycle after STP is latched, but the scan is hard to read. Verify against silicon or markablov/i40xx source. +- Behaviour when INT arrives during a 2-byte instruction (JMS, FIM, JCN, JUN, ISZ): [M40] says the current instruction "completes" before the forced JMS, but doesn't tabulate the cycle accounting. +- Whether DB0/DB1's 3-cycle inhibit also blocks WRR/RDR to the previously-selected bank during the transition — [M40] is silent. diff --git a/test/test_intel/autosearch/14_4004_4040_reference_implementations.md b/test/test_intel/autosearch/14_4004_4040_reference_implementations.md new file mode 100644 index 00000000..9e2ac4af --- /dev/null +++ b/test/test_intel/autosearch/14_4004_4040_reference_implementations.md @@ -0,0 +1,52 @@ +# Intel 4004 / 4040 — Permissive Reference Emulators + +License vetting performed against the GitHub `license` field via API on 2026‑04‑29. +**Excluded** (GPL/copyleft): MAME `mame/src/devices/cpu/mcs40/mcs40.cpp` (GPL‑2.0+); `carlini/intel-4004-in-4004-bytes-of-c` (GPL‑3.0). The user-suggested `lkesteloot/intel-4004` returns 404 — does not exist. + +## Candidates (all SPDX MIT or equivalent) + +### 1. **markablov/i40xx** — https://github.com/markablov/i40xx +- **License**: MIT (verified via API; LICENSE file in `packages/i40xx-emu`). +- **Language**: JavaScript (Node, ESM). 18 ★. +- **Coverage**: 4004 *and* 4040 in one core. +- **Core file**: `packages/i40xx-emu/src/cpu/cpu.js` — **~600 LOC** (19,551 bytes). Pin map in `pins.js` (44 LOC). Companion packages: assembler (`i40xx-asm`), linker, preprocess, an ICE-like UI, and a `pi-digits` program demo. +- **Tests**: `i40xx-program-pi-digits` package serves as integration smoke test (computes π via the 4040). No Busicom 141‑PF in-repo. + +### 2. **Kostu96/K4004** — https://github.com/Kostu96/K4004 +- **License**: MIT (root LICENSE). +- **Language**: C++17. 4 ★. +- **Coverage**: separate `K4004.cpp`/`K4040.cpp` cores with a shared `instructions.cpp`. +- **Core LOC**: K4004.cpp 3,654 B (~140 LOC), K4040.cpp 2,956 B, instructions.cpp 7,593 B (~280 LOC). +- **Tests**: `emulator_core/tests/instructions_tests.cpp` (11.7 KB of GoogleTest cases). **Includes Busicom 141‑PF**: `programs/busicom/busicom_141-PF.obj` (3,846 B object) + 122 KB disassembly. Note `programs/busicom/LICENSE.md` carries its own (Busicom-firmware) license — separate from the emulator's MIT. +- **Verdict**: best single repo to cross-validate against — has both cores and Busicom. + +### 3. **lpg2709/emulator-Intel-4004** — https://github.com/lpg2709/emulator-Intel-4004 +- **License**: MIT. 67 ★. +- **Language**: C99, single-file core `src/4004_chip.c` (9,762 B, ~360 LOC) + `4004_chip.h`. Built-in disassembler/assembler. +- **Tests**: dedicated `test/` dir; ROMs in `roms/`. Uses Zig build (`build.zig`). No 4040. + +### 4. **alshapton/Pyntel4004** — https://github.com/alshapton/Pyntel4004 +- **License**: MIT. Python. Has docs site (pyntel4004.readthedocs.io) and a `test/` dir. +- **Coverage**: 4004 only. Useful as a *spec-style* reference because each opcode is its own well-commented Python file. + +### 5. **markablov/js-4004** — duplicate redirect of i40xx (same repo metadata returned). + +## Tricky points — cross-checked against [M4]/[M40] and the implementations above + +**(α) JMS/JUN encoding** — `0101 AAAA` `AAAA AAAA` for JMS, `0100 AAAA` `AAAA AAAA` for JUN. The **high 4 bits of the 12-bit target are in the LOW nibble of the first byte (the OPA)**, the low 8 bits are the second byte. **Confirmed** by [M4] Table V p. 16 (OPA = `A3 A3 A3 A3` followed by `A2 A2 A2 A2 A1 A1 A1 A1`) and by `i40xx/cpu.js` and `K4004/instructions.cpp`. No disagreement. + +**(β) FIM encoding** — `0010 RRR0` + 1 immediate byte → 8‑bit immediate to register pair P (RRR encodes one of 8 pairs; LSB=0 distinguishes from SRC `0010 RRR1`). **Confirmed** by [M4] Table V p. 16. So FIM P0 = 0x20, FIM P1 = 0x22, … FIM P7 = 0x2E. + +**(γ) ADD with carry** — [M4] p. 25 verbatim: *"(RRRR) + (ACC) + (CY) → ACC, CY"*. CY is an input. After the op CY = 1 if `sum > 15`. Index register unchanged. Implementations agree. SUB is `~Rn + ACC + ~CY` (one's-complement form per [M4] p. 26) — i.e. CY=0 means borrow-in, CY=1 means no borrow-in; on result, CY=0 = borrow generated, CY=1 = no borrow. Note this is **inverted** vs 8080 borrow polarity — emulator authors get this wrong frequently. + +**(δ) DAA** — opcode **`1111 1011` = 0xFB** ([M4] p. 31, [I] cross-check). Verbatim: *"The accumulator is incremented by 6 if either the carry/link is 1 or if the accumulator content is greater than 9. The carry/link is set to a 1 if the result generates a carry, otherwise it is unaffected."* This **differs from 8080's DAA** in two ways: (i) it acts on a single 4-bit nibble (no high/low nibble split), so there's no auxiliary-carry concept; (ii) **CY is sticky** — only set, never cleared (8080's also has this property, but only after step 2). No half-carry flag exists. + +## Recommendation + +For Velxio, port the instruction-decode skeleton from **markablov/i40xx** (single core covers both chips, MIT, smallest to read) and validate cycle-accurate behaviour against **Kostu96/K4004**'s Busicom 141‑PF run — that's the canonical Intel 4004 acceptance test (the chip was originally built for it). + +## Open questions + +- Has **anyone** published a cycle-by-cycle JCN/ISZ trace test? Neither i40xx nor K4004 ships one. Would have to be derived from [M4] §VII descriptions. +- Whether `K4004`'s 4040 implements the **3-cycle DB0/DB1 inhibit window** correctly — needs source inspection. +- Pyntel4004 imports `pyntel4004` from PyPI; its tests cover instruction decode but not pin-level timing.