NAUG Ch17 — Paged ROMs
Holmes & Dickens, The New Advanced User Guide, pp.290-333. Largest single chapter in the book — covers the paged ROM as a software system: header format, language vs service ROMs, the full service-call protocol, RFS (the *ROM filing system), OSRDRM, extended vectors, 100 Hz polling.
This complements paged-rom (which covers the hardware paging — &FE30, ANDY, sideways RAM testing). Ch17 is about what lives in the 16 KB ROM window and how it talks to MOS.
Key facts captured
ROM types
- Language ROM: provides a language entry point at offset 0. Receives control after reset /
*command. Owns user RAMOSHWM..HIMEMand 4 pages of workspace at&400-&7FFplus zp&00-&8F. Examples: BASIC, FORTH, BCPL, VIEW. - Service ROM: provides a service entry point at offset 3. Called by MOS on every service-call event (unknown
*command, BRK, OSBYTE/OSWORD passthrough, etc.). Examples: DFS, ADFS, printer drivers.
Most ROMs are both — a language ROM with a service entry. BASIC is the historical exception (language-only).
ROM header (16 + t + v + c bytes)
offset size field
0 3 JMP <language_entry> ; or three zeros if not a language
3 3 JMP <service_entry>
6 1 ROM type byte
7 1 copyright offset (= 10 + len(title) + len(version))
8 1 binary version number
9 [t] title string (terminated by zero)
9+t 1 zero byte
10+t [v] version string (e.g. "1.00")
10+t+v 1 zero byte
11+t+v [c] copyright string — MUST start with "(C)" (`&28,&43,&29`)
11+t+v+c 1 zero byte
12+t+v+c 4 Tube relocation address (for 2nd-processor copy)
16+t+v+c+ ... code and data
ROM type byte (offset 6)
| Bits | Meaning |
|---|---|
| 0-3 | Processor type: 0=6502 BASIC, 1=6502 Turbo, 2=6502 code (non-BASIC), 3=6800, 8=Z80, 9=32016, &B=80186, &C=80286, &D=ARM |
| 4 | (Electron only) Firm-key expansion present |
| 5 | Language has Tube relocation address |
| 6 | Is a language (initialise on reset) — Master is strict about this |
| 7 | Has a service entry — set on virtually all ROMs |
Typical service-only ROM: &82 (service + 6502 code).
Service-call dispatch
When MOS needs to do a service-call event:
- MOS pages in each ROM from highest priority (15) downward.
- JSRs the ROM’s service entry (offset 3) with:
A= reason code (the “why”)X= current ROM number (so the ROM can read its slot)Y= call-specific parameter
- If the ROM claims the call: clear A (return
A=0) and RTS. MOS stops scanning. - If the ROM doesn’t claim: return A unchanged. MOS continues to the next-lower-priority ROM.
ROM number 15 = highest priority, 0 = lowest. So the priority gradient runs opposite to ROM ID — bigger number = first to see service calls.
Service call reason codes (NAUG §17.4.1 p295-304)
Full table in service-calls. Highlights:
&01Absolute workspace claim (reset only)&02Relative private workspace claim (reset only)&03Auto-boot (reset)&04Unrecognised*command — the main extension point for service ROMs&05Unknown interrupt — IRQ chain fallback&06BRK occurred&07Unknown OSBYTE&08Unknown OSWORD&09*HELPquery&0A-&0CWorkspace/NMI claim/release (issued by ROMs via OSBYTE&8F)&0D/&0ERFS init / get-byte (the *ROM filing system)&0FVectors claimed (notify other ROMs)&10Close *SPOOL/*EXEC files&11Font implosion/explosion warning (Electron/Model B)&12Initialise filing system&15100 Hz poll (Electron + Master only)&27Reset call (Master)&FE/&FFTube system init
Workspace allocation protocol
On every reset, MOS issues:
- Service call
&01to each ROM in priority order, with Y = current “top of fixed area” (starts at&E, growing by Y returned). - Service call
&02with Y = top of absolute area, each ROM may claim private pages (stored in&DF0+ROM_ID, one byte per ROM). - Service call
&03(auto-boot) — the highest-priority filing-system ROM that responds becomes the default FS.
So a service ROM can quietly grab a page or two of RAM at reset time without the user noticing — until it pushes OSHWM up.
Why you can’t JSR into another ROM
The paging register &FE30 is constantly mutated by MOS. If you JSR into code at &8000-&BFFF from outside the paged ROM, you can’t guarantee which ROM is currently in. The mechanisms to safely call into another ROM:
OSRDRM(&FFB9) — Read one byte from a specified ROM. SetY= ROM number,&F6/&F7= address. Returns byte in A. The OS handles the paging for you. Slow per-byte (each call costs OSRDRM dispatch overhead). On older MOS without OSRDRM, you must do the paging yourself with interrupts disabled.- Service call
&8F— Issue a paged-ROM service call from user code (OSBYTE &8Fwith X=reason, Y=parameter). MOS scans the ROMs as usual; appropriate ROM claims and runs its service entry. - Extended vector — Point an OS vector (e.g. WRCHV
&20E) at a 3-byte entry in the extended vector table (origin viaOSBYTE &A8/&A9). Each entry isaddr_lo, addr_hi, rom_num. MOS will page in the named ROM before transferring control. This is how filing systems install themselves: the FS vector points to a stub in&FF00+that jumps via extended-vector to ROM code.
OSRDRM — read byte from any ROM
JSR &FFB9 (no indirection). Set:
Y= ROM number&F6/&F7= address within&8000-&BFFF
Returns byte in A. Not available across Tube (second processor can’t reach the paging register).
Extended-vector layout
OS vectors at &200-&234 (e.g. WRCHV &20E) by default point into MOS ROM. To intercept:
- OS vector → stub address in
&FF00+vector_num*3(in MOS ROM). - That stub reads the 3-byte entry at
extvec_origin + vector_num*3:- 2 bytes target address
- 1 byte ROM number (MOS pages it in first)
- JMPs to the target address.
OSBYTE &A8/&A9 returns the LSB/MSB of extvec_origin. Procedure to hook (NAUG §17.4.3 p312-313):
; Want to intercept vector number n (e.g. n = &E/2 = 7 for WRCHV)
; 1. SEI
; 2. Read OSBYTE &A8/&A9 → V (extended vector origin)
; 3. Store my_entry_addr_lo at V + 3n
; Store my_entry_addr_hi at V + 3n + 1
; Store my_rom_num at V + 3n + 2
; 4. Save (vector &200+2n) for chain-through
; 5. Write &FF00+3n into (&200+2n)
; 6. CLI100 Hz paged-ROM polling (Master/Electron only)
If your ROM wants to do background work every 10 ms without owning an IRQ:
OSBYTE &16— increment polling semaphore.OSBYTE &17— decrement polling semaphore.OSBYTE &B9— read/write semaphore.
When the semaphore is non-zero, MOS issues service call &15 at 100 Hz. Your ROM should decrement Y by the number of work-units it consumed.
OSBYTE &A4 — check for 6502 code
Given an address in X+Y, this OSBYTE verifies the data is a valid paged-ROM image (copyright string at offset 7, etc.) and that the processor type byte at offset 6 indicates 6502. If not a language ROM, BRKs with “This is not a language”. If not 6502, BRKs with “I cannot run this code”. Use before OSBYTE &8E to enter a sideways-RAM-loaded ROM.
Filed into
- paged-roms — ROM header format, language vs service distinction, language entry protocol, ROM-installation workflow.
- service-calls — Complete service-call reference table with reason codes and entry/exit conventions.
- Updates: paged-rom cross-links to these; previous “don’t write
&FE30directly” rule now has the OSRDRM / extended-vector alternatives spelled out.
Open follow-ups
*ROMdata block format (CRC, file headers) — captured in source page but not filed into a dedicated wiki page; only useful if writing a ROM-resident filing system.- Service call
&21-&26(Master HAZEL workspace claims) — only relevant to filing-system development. - Worked printer-buffer ROM example (§17.4.2) and RFS example (§17.5.7) — pattern is captured; full code in
raw/manuals/...PDF for reference.
This wiki is curated by Claude following the LLM-Wiki methodology — a human curates source documents, the LLM compiles structured cross-linked markdown. Content may contain errors, omissions, or stale claims. For authoritative information refer to the original source documents in the bbc-documents GitHub archive.