Banked memory — the model, and where it was wrong
Status: the fix has landed. Banking is now its own board,
bankmem(docs/boards/bankmem.md,src/boards/bankmem.{h,cpp}), with one decoder per real card; thememoryboard is plain, unbanked RAM/ROM again. Theb810type is gone and the SIMH-derivedbank_typetable is deleted. This page is kept as the audit that led there — it records what the manuals say, what the old model said, and why the two diverged. The decisions taken are recorded at the end (The fix that landed).
This page is a developer-facing audit of how altairsim modelled S-100 memory-bank switching
(src/boards/s100-memory.h BankType/BankSpec, src/boards/s100-memory.cpp kBanks[])
against the period manuals for the real cards, distilled in reference/. It exists because
that audit turned up a problem worth writing down before it is fixed: most of the banked
encodings we ship do not match the hardware, and the reason is that they were taken from another
emulator's source instead of a manual.
Nothing here changes code. It records what the manuals say, what our model says, and where the two diverge, so the correction (a separate branch — see The follow-on) starts from facts. The per-card facts below are sourced to the five new references:
reference/SD Systems ExpandoRAM.md— the original ("I")reference/SD Systems ExpandoRAM II.mdreference/Vector Graphic 64K Dynamic RAM.mdreference/North Star HRAM.mdreference/Cromemco 64KZ RAM.md(covers 64KZ and 64KZ-II)
The cardinal rule we broke
docs/sources.md states the rule at the top: "Period manuals and first-hand artifacts only.
Never read another emulator's source to learn how hardware works — that explicitly includes SIMH
/ AltairZ80. Second-hand facts inherit second-hand mistakes."
The banked-memory encodings in kBanks[] were taken from Patrick's SIMH s100_bram.c, not from
the period manuals — and it shows. Of the four banked types we can now check against a manual,
only vram (Vector Graphic) is right. eram, cram, and hram each diverge from the real
card, in exactly the way the rule warns about: s100_bram.c is a generalization
({port, banks, one-hot?, mask} swapping a whole 64K plane), and the real cards are not
generalizations — each is a different mechanism, and three of them do not fit that shape at all.
The comment block in s100-memory.cpp:14 and the prose in docs/boards/s100-memory.md:213
("five real cards, and no two alike") present the SIMH table as if it were the hardware. It is
not. This is a genuine violation of the project's central sourcing discipline, discovered only
when the actual manuals were read.
The scorecard
bank_type |
Real card (manual) | Real scheme | Our model (kBanks[]) |
Verdict |
|---|---|---|---|---|
eram |
SD Systems ExpandoRAM I | No I/O port at all — flat memory-mapped; a DIP switch straps 4 chip-groups to fixed 8K/16K address slices | port FF, 8 banks, binary (byte = bank), swap 64K plane | ✗ wrong |
eram |
SD Systems ExpandoRAM II | port FF, byte = page number (0–9); an 82S130 PROM + octal board-select switches decode it to a 32K/48K partition; 4 chip-banks/board | (same eram row) |
✗ wrong (port matches, encoding does not) |
vram |
Vector Graphic 64K | port 40, one-hot 1<<bank, 8 banks, reset → bank 0 |
port 40, 8 banks, one-hot | ✓ correct |
cram |
Cromemco 64KZ / 64KZ-II | port 40, byte = 8-bit mask of active banks (bit N ⇒ bank N on/off, several at once), 8 banks | port 40, 7 banks, one-hot select-one, mask 0x7F |
✗ wrong |
hram |
North Star HRAM | port C0, bit 0 = on(0)/off(1), bits 1–7 = one-hot bank toggle, ≤ 6 usable banks, old-off-then-new-on | port C0, 16 banks, binary, mask 0x0F, swap 64K plane |
✗ wrong |
b810 |
(claims "AB Digital Design B810") | unsourced — no manual; cannot be verified | port 40, 16 banks, binary | ✗ remove |
eram — matches neither ExpandoRAM
The eram byte-is-the-bank / 64K-plane-swap model matches neither real ExpandoRAM. The
original (I) has no I/O port whatsoever — its J1 pinout carries only memory-bus signals, and
its "banks" are four fixed chip-groups strapped to address slices by a DIP switch; it is an
ordinary static-address memory board, not a bank switcher. The II does use port FF (so our port
is right), but the byte it takes is a page number decoded by an on-board 82S130 PROM into a
32K or 48K partition, with the board's identity set by octal board-select switches — not a bank
number, and not a 64K plane. Our eram is the SIMH generalization sitting on the II's port.
vram — correct, and worth keeping as the proof
Vector Graphic is exactly what we model: port 40H, one-hot 1<<bank, 8 banks, and bank 0
force-enabled at reset (the manual's theory-of-operation even explains the latch-clear + DO0
inversion that makes reset electrically equal to writing 0x01). The OASIS quirk (0x41/
0x42 tolerated as banks 0/1) is empirical, not in this manual, and stays empirical. This is the
one banked type whose encoding an implementer can trust today.
cram — wrong mechanism, not just wrong numbers
Cromemco's BANK SELECT byte is a bit-mask: "BIT 0 (LSB) controls BANK 0, BIT 1 controls
BANK 1 … a logic 1 control word bit activates its corresponding memory BANK; a logic 0 …
deactivates" (64KZ manual p. 17). Several banks can be active at once — OUT 40H,28H activates
banks 3 and 5. We model a one-hot select-one with only 7 banks and bit 7 masked off. That
is wrong on the bank count (8, not 7), on the masking (bit 7 is bank 7), and on the core
semantics (mask-enable-many vs select-one). The "seven banks / bit 7 is not a bank" story in the
code and docs is a SIMH artifact, not Cromemco hardware.
hram — the byte is not a bank number
North Star's port-C0 byte is bit 0 = on/off command, bits 1–7 = a one-hot address of which
bank the command acts on — banks are toggled individually (software must switch the old bank off
before the new one on), and one of bits 5/6/7 is spent on parity, leaving ≤ 6 usable banks. We
model a 16-bank binary plane-swap. Verified directly against the scan (MVI A,08H = "turn on bank
3", MVI A,09H = "turn off bank 3"). Wrong on encoding, bank count, and protocol.
b810 — unsourced, remove it
b810 ("AB Digital Design B810") has no manual behind it and cannot be verified (§0.1 forbids
shipping hardware behavior we cannot source). Patrick's decision: remove it. It is also the
"fifth card" that props up the "five real cards" and "three of the five share port 0x40"
narrative in the code and docs — remove it and that narrative must be re-counted (the real
port-0x40 sharers among the sourced cards are vram and cram).
The fix that landed
The correction became the bankmem board (docs/boards/bankmem.md). What was done, and the
design decisions taken:
- Banking is its own board,
bankmem, with acard=variant strap — one class, four per-card decoders (vectorone-hot select-one,cromemco64kz8-bit mask,northstaron/off + one-hot toggle,expandoram2PROM page-select). This was chosen over piling more knobs ontomemory: the real cards do not share one parameterization, andDESIGN.md§4.3 argues a board must own its decode. The unifying model is segments (a RAM slice + address window + enabled flag); the port write toggles the flags per the card's rule. memoryis plain RAM/ROM again — nobank_type, nobanks/bankproperties, no I/O port, a flat 64K store. This is also the honest model of the ExpandoRAM I (no port at all).b810removed — unsourced (§0.1).eramretired — the ExpandoRAM I is plain memory; the ExpandoRAM II iscard=expandoram2.expandoram2is a documented approximation — a binary page-select, because the real 82S130 PROM decode is not transcribable from the scan. The caveat is stated in the board doc and in the code; a PROM dump would let the page decode be made faithful. What the PROM's stock variants do is modelled, though (follow-up,feat/bankmem-partition-ram):partition=ex32|ex48gives a fixed common region shared by every page (EX-32 = 32K banked + 32K common; EX-48 = 48K + 16K), andram=sets board size up to 256K. That is what a banked CP/M needs — the resident OS and the bank-switch routine live in common and survive the pageOUT. SD Systems' own banked CP/M 3 does exactly this (bank number → port FF,COMBAS=C0⇒partition=ex48); the general mechanism is now in place, while board-select across multiple coordinated boards remains the approximated part.v2z80romwas kept as its own board — its onboard paged ROM overlay (two 4K EEPROM pages at F000-FFFF, phantom-shadowing RAM, tied to a CPU card's identity) is a different thing from an S-100 banked-RAM card; folding it intobankmem(a RAM board) would reintroduce the very "ROM on a banked card is unsourced" tension the design refuses. Closed as: stays separate.
See also
docs/boards/s100-memory.md— the board's design doc, now describing plain RAM/ROM only.DESIGN.md§10.2 — why there is noBANK=in the monitor.docs/sources.md— the sourcing rule this page documents breaking.