Paged ROM is a system used in all 8 bit BBC series computers and in the Electron, whereby a large pool of ROM-based software can be used all at once by dividing it into 16K slots and paging one at a time into the address space. Depending on the hardware the pool may be 64K up to 256K in size. The MOS manages paged ROMs automatically and provides an extensive API to ROM-based software. There are very few problems in practice, as nearly all applications stick to the API.
In a diagram of memory where the address space runs from bottom to top, the paged ROM slots appear parallel to the main memory map, extending away to the side. For this reason the system is also known as sideways ROM. Later models and custom machines have RAM fitted in some slots and this is usually called sideways RAM rather than 'paged RAM'.
The equivalent system in the Archimedes series are Modules.
- 1 Rationale
- 2 Features of paged ROM
- 3 Hardware
- 4 Operation
- 5 API
- 5.1 ROM headers
- 5.2 Language entry
- 5.3 Language memory allocation
- 5.4 Service memory allocation
- 5.5 Tube memory allocation
- 5.6 Paged ROM service calls
- 5.7 Extended vectors
- 6 References
It is clear that the BBC Micro was designed with paged ROMs in mind from the start. As the 6502 CPU cannot address more than 64K, a way was needed to fit in all current and future firmware (such as disc filing systems, ROM filing systems, device drivers and applications) without taking up too much of the address space.
In addition, some classes of application (termed 'languages' by Acorn but not necessarily languages) needed to spare as much RAM as possible for the user. In an education environment they had to load up faster and more reliably than cassettes but require less capital outlay than floppy discs. (Drives and interfaces were prohibitively expensive at the time, especially for local education authorities.)
Acorn found that most of these pieces of firmware were loosely coupled and so could share the address space. EPROMs also made a good choice for distributing 'languages', given the needs of the market.
Features of paged ROM
Languages are ROM based applications that take control of the computer on power-up and interface between the user and the operating system. BASIC, LISP and FORTH are examples of language ROMs released at the time. In modern terms they would be called shells, and so they need not be programming languages, or command line based (such as the word processors View and Wordwise). They may even be user applications (for example Podd, a specialised educational title.)
Exactly one language is active at any time. The MOS starts one automatically on power up, and can invoke another on command by its slot number, but the usual way of starting a language is by name, through a *command passed to its service entry point by the MOS. At the very least, each language allows the user to enter *commands to select another language (the cue is * by convention, although a language may use another character or interface.)
On power up and hard reset the Models A, B and B+ and Electron will select the language in the highest logical slot number. The Master and Master Compact store the slot number of the current language in non-volatile memory and restore it on power up and hard reset. On soft reset the 'current language' is re-selected.
The service routine, which must be present in all ROMs except for BASIC, answers a query or performs some function for the MOS and then returns. The service routine may vary in size and complexity up to 100 per cent of the ROM. A valid ROM that has no language entry point is termed a service ROM (although languages are free to offer extensive service routines as well.)
This class of ROM includes all disc filing systems, utility ROMs and device drivers, as these do not interface continually with the user but are invoked by programs, languages and the MOS.
ROM filing system
RFS ROMs are a special class of service ROM that hosts data files. These ROMs act as a virtual tape loop, read by the ROM Filing System (RFS) built into the MOS. The MOS can search for files automatically but each file is serially accessed and of course, read only.
The service routine is small, typically implementing just the two relevant RFS calls, to save as much space as possible for the data itself. (A larger service routine can decompress the data transparently; such a ROM has recently been developed.) Since the Cassette and ROM Filing Systems share most of their code, the data consists of a stream of bytes in a variation of the Acorn cassette format.
RFS ROMs allow BASIC programs and machine code to be loaded from EPROM into main memory, so that ROM based machine code versions do not need to be assembled.
See also the main article: Sideways RAM
The B+ 128K and Master 128 provide 64 kilobytes of sideways RAM: in slots 12, 13 and the two highest or lowest slots in the B+, and in slots 4 to 7 in the Master. These slots behave like sideways ROMs in every respect except that they are writeable. They can therefore provide all the above features, or serve as general purpose RAM to suitably aware programs.
Note that some commercial ROM-based software attempts to overwrite itself so that it cannot be copied and run from sideways RAM. Care must also be taken that a valid ROM is not corrupted so as to hang the machine on the next ROM service call. If such an event occurs the computer must be powered down to clear the memory.
These machines also provide extended addressing so that sideways RAM can be handled by any MOS call that takes a 32-bit address, thereby providing a continuous 64K of extra memory. A version of BASIC named BAS128 has a similar but independent feature. BAS128 puts the display into shadow mode, runs in main memory and uses sideways RAM for the user's program and variables. Machine code can be assembled and executed there, as the assembler translates virtual addresses to their actual locations in sideways RAM – provided that no references are made between sideways RAM slots. Code can also be assembled there to run in main memory under OPTions 4 to 7.
In all these machines, paged ROMs appear in a 16K window from &8000 to &BFFF. Each paged ROM is therefore assembled to an absolute address of &8000. ROMs smaller than 16K are duplicated until they fill the address space. The top 16K (&C000..&FFFF) of memory is reserved for the MOS, FRED, JIM, SHEILA (and HAZEL), which are permanently available.
These computers are fitted with five sockets for 16K ROMs. At first all the sockets were taken up by the MOS, in 4K EPROMs, and BASIC; but once the MOS matured and was released as a mask ROM, the jumpers were reconfigured to provide paged ROM service. The MOS sits in its own dedicated socket, and BASIC in one of the other four. BASIC need not be present; it can be replaced by any other 'language' which will take control of the machine and (hopefully) provide all the same functionality.
The heart of the paged ROM system is IC 76, a 74LS163 quad latch known as ROMSEL. It appears in SHEILA in the low four bits of address &FE30. It is a write-only register but the MOS maintains a copy of the current ROM selection, in RAM at address &F4. A paged ROM can therefore discover its slot number by reading this address.
Only the low two outputs of ROMSEL are connected, to a quad decoder in IC 20 which provides Chip Select signals to four of the ROM sockets. The easternmost of these is assigned slot number 15, which has highest priority for all ROM service calls and resources. The others are assigned slot numbers 14 13, and 12; the fifth, westernmost socket contains the MOS. The table below lists the slot assignments according to the circuit diagram, though it was later recommended to put the DFS in the lowest slot:
|Socket number (hex)||Assigned device and contents|
|F||IC 101, "AUX ROM", typically containing BASIC or BASIC II|
|E||IC 100, "AUX ROM", typically empty|
|D||IC 88, "DISC&NET ROM", typically empty|
|C||IC 52, "BASIC ROM", typically containing the DFS|
|B||Mapped to slot &F|
|A||Mapped to slot &E|
|9||Mapped to slot &D|
|8||Mapped to slot &C|
|7||Mapped to slot &F|
|6||Mapped to slot &E|
|5||Mapped to slot &D|
|4||Mapped to slot &C|
|3||Mapped to slot &F|
|2||Mapped to slot &E|
|1||Mapped to slot &D|
|0||Mapped to slot &C|
Homemade ROM expansions can connect spare address lines to the upper two outputs of ROMSEL and deliver multiple slots from one socket; the MOS does not need to be modified as it will assign lower numbers to the extra applications found.
A large number of commercial ROM expansion boards have also been produced; these connect to the motherboard in various ways and some offer extra features, such as shadow RAM which inspired the feature in the [[Model B plus|Model B+]]. The largest has sixteen sockets so as to fill all available slots, four of these replacing the motherboard sockets that have been covered over. These boards either tap the unused outputs of ROMSEL or replicate the entire latch. For the most part they are compatible but reliability can be a problem (due to the mechanical difficulties and to overheating.)
Certain commercial ROM titles (notably Mini Office II and SPELL-MASTER) were supplied on their own carrier boards and used custom circuits and protocols to deliver more than 16 KB from their one assigned slot. These systems are self contained and independent of ROMSEL.
A paged ROM system was retained to allow expansion modules to supply firmware, but as an entry level machine not many modules were expected to be present at once, and so efficient allocation of ROM slots was sacrificed for economy. The table below lists the standard slot assignments:
|Socket number (hex)||Assigned device and contents|
|F||External ROM (+ disables internal devices)|
|B||IC 2, BASIC II|
|7||External ROM (internal devices must first be disabled)|
The ULA exposes a ROM selection register in SHEILA at address &FE05. It is a write-only register but the MOS maintains a copy of the current ROM selection, in RAM at address &F4. A paged ROM can therefore discover its slot number by reading this address. Expansion modules with ROMs must partially emulate (track) the ULA and, if providing multiple ROM sockets, must supply their own selection register at address &FE05.
The ULA can either be in 'external ROM mode' or 'internal ROM mode'. External ROM mode is enabled when a value between 12 and 15 is written to &FE05. In this mode the keyboard and BASIC are disabled, and it is up to an expansion module to supply paged ROM content. Internal ROM mode is enabled when a value between 8 and 11 is written to &FE05. This also selects the keyboard (slots 8 or 9) or BASIC ROM (10 or 11, both the same image); the relevant device will stay enabled even if a value of 0 to 7 is later written to &FE05. If the expansion module responds to paged ROM addresses at this time there will be bus contention, and a corrupted value will appear on the data bus.
Certain expansion modules (such as those fitted with Slogger's Electron
Expansion 2.0) can host ROMs in slots 12 to 15, and languages in these slots
can start up automatically. Languages in slots 0 to 7 can only be entered by
their *command or by
*FX 142,n, as the MOS must disable the
The Model B's motherboard was redesigned to make use of the newer ICs available, and issued as the Model B+. There are six 32K sockets on the west side of the motherboard. One of these is permanently assigned to BASIC and the MOS; the other five can accept 32K devices (in which case each half appears in a separate 16K slot) or any smaller device (in which case the contents are duplicated to fill the two slots.) A jumper is provided for each socket to support 16K or 32K ROMs, and another jumper allows BASIC to be moved to a low slot to allow another language to take priority. In the B+ 128K, a total of 64K of sideways RAM can be mapped into slots that have no corresponding ROM socket. The DFS recognises this on hard reset and increases the memory size displayed in the banner.
From the B+ User Guide: "The sockets are numbered, counting anticlockwise from the MOS/BASIC socket at the top right, 14/15, 10/11, 8/9, 2/3, 4/5, and 6/7." The table below lists the standard slot assignments:
|Socket number (hex)||Assigned device and contents|
|F||IC 71, BASIC II|
|D||Sideways RAM (B+ 128K only)|
|B||IC 68, DFS 2.26 + SRAM 1.05|
|9||IC 62||top 16K|
|7||IC 57||top 16K|
|5||IC 44||top 16K|
|3||IC 35||top 16K|
|1||Sideways RAM (B+ 128K only) or BASIC II (alternative location)|
The upper 12K of shadow RAM appears at locations &8000 to &AFFF when a value above 127 is written to ROMSEL. The memory is also accessible via OSWORD calls 5 and 6 (q.v.) Code executed from the top 4K of this memory also 'sees' and can manipulate video RAM, i.e. shadow RAM in shadow mode or main RAM in non-shadow mode. The MOS will not search for languages or service ROMs in shadow RAM.
Further advances meant that the Master firmware was embedded in one 128K device. This is assigned the highest seven ROM slots; the MOS takes up the remaining 16K and spills into spare space in the paged ROMs. Incidentally the ROM, a custom chip known as EROS, is 'too big for its boots'; users wanting to patch bugs in the firmware are forced to replace the chip with a 32 pin EPROM on a carrier board to emulate the non-standard pinout.
There is one 16K socket on the motherboard, operating like the ones in the Model B and serving slot 8; and two 32K sockets like those in the Model B+ serving slots 4 to 7. These last four slots are normally turned over to sideways RAM, deactivating the sockets. Slots 0 to 3 are assigned (again in pairs) to the cartridge sockets on top of the machine. The table below lists the standard slot assignments:
|Socket number (hex)||Assigned device and contents|
|F||IC 24, EROS||Terminal 1.20 + Tube host + CFS + character set|
|E||View B3.0 + MOS code|
|D||Acorn ADFS 1.50|
|B||Edit IV 1.00|
|9||DFS 2.24 + SRAM 1.04|
|8||IC 27||Often used for ANFS|
|7||Sideways RAM or IC 37||top 16K|
|5||Sideways RAM or IC 41||top 16K|
|3||SK4, Front cartridge slot||top 16K|
|1||SK3, Rear cartridge slot||top 16K|
Since alternative languages and filing systems cannot be placed 'above' the
Acorn firmware to take priority, the MOS provides a 'soft unplugging'
*UNPLUG n and
*INSERT n update the
non-volatile memory so that the MOS will ignore or consider, respectively,
firmware in slot
n. The commands take effect on the next hard
ROMSEL in this machine is a read-write latch at &FE30, but a RAM copy is still maintained at &F4 for compatibility.
The upper 12K of shadow RAM, which was available to the user in the B+, is split in two and allocated to the MOS (as ANDY) and paged ROMs (as HAZEL, which lies outside the paged ROM address space). The B+ User Guide warns: "Note: references to this 12K of memory which are made in this manual are not necessarily applicable to other Acorn products."
This machine follows the Master layout but the firmware is reduced to 64K. The ROM is usually soldered in and carries the MOS and three paged ROM slots with utilities, ADFS and BASIC. There is also a 64K socket and Acorn issued a ROM for it containing applications. This ROM is optional and so not considered part of the firmware.
There are three 16K sockets and one 32K socket for the user's devices. None of these are 'hidden' by sideways RAM, which is permanently available.
|Socket number (hex)||Assigned device and contents|
|F||IC 16, MOS||Utilities|
|C||IC 49, "Baby B System ROM"||top 16K|
|7||64 KB sideways RAM|
|1||IC 38 or expansion port||top 16K|
Any code may change the ROM paging register at will. However, a routine that returns to its caller is expected to preserve the ROM selection on exit, unless changing the selection is its purpose. This includes system calls, interrupt service routines and code called from an interpreted language.
Conversely, all code must mirror changes to ROMSEL in the RAM copy so that the routines that serve or interrupt it may restore the ROM slot it expects to see. As is often mentioned, the MOS runs under constant interrupts. Many of these may result in service calls and the OS will page in ROMs unexpectedly to issue them. It is therefore essential that code that accesses paged ROMs directly keep the RAM copy of the paging register up to date.
To select a ROM slot, its number must first be written to the RAM copy at &F4. If the machine is an Electron, and the target slot number is 7 or less, a value between 12 and 15 must be written to &FE05 to turn off internal ROM. Then the number is written to ROMSEL, &FE05 (Electron) or &FE30 (other machines.) When code running in the paged ROM area changes between slots, the op-code after the instruction that stores ROMSEL should be the same in the old and new slots so that the address bus and Chip Select signals may stabilise without ill effect.
There is nothing to prevent a language or service software taking up more than 16K; when the firmware has control it can page in another part of itself with a revolving bookcase subroutine (identical and at the same location in all slots; for example in Inter-WORD.) Languages need not save the old slot number except for reference; service routines must push the slot number (or private latch setting) on the stack, and pop it on exit. The only caveat is that a service routine must be available to the MOS at all times.
An alternative (and less intensive) method is to have a language resident in one 16K slot, and pass user MOS calls (usually OSWORD) which are handled by one or more of the other slots.
On power up or hard reset the MOS scans all slots for a valid ROM header, disabling slots where no header is found. This is to prevent hangups caused by jumping into empty sockets or uninitialised RAM. It does not however protect against malfunctions due to a slightly corrupted ROM image.
Every language or service ROM must provide the following header at addresses &8000 onwards. This is also used for code loaded and executed in second processor memory. See also Code header.
|Start address (hex)||Value(s) (hex)||Meaning|
||Language entry point|
||Service entry point|
|| ROM type byte:
b7 = ROM contains a service entry
0 6502 BASIC 8 Z80 1 Turbo6502 9 32016 2 6502 10 - 3 68000 11 80186 4 - 12 80286 5 - 13 ARM 6 - 14 - 7 PDP-11 15 -
||Offset to copyright string|
||Binary version number, for user inspection only|
||ROM title, displayed before entering language|
||(Optional) Version string in the format NUL + "n.nn (dd mmm yyyy)"|
||(Optional) Other data here is ignored.|
||NUL and "(C)", required for MOS acceptance, name of author, etc|
|| (Optional) Tube transfer address.|
+pointer to 6502 relocation bit-map descriptor table (MOS 3.50)
|| (Optional) Offset from start of ROM to 32016 Tube execution address.|
A ROM offering a language must set bit 6 of its ROM type byte and must provide an entry point at an address appropriate for the target CPU. A ROM header is also checked for with code loaded executed in second processor memory. The entry point is at an offset from the start of the ROM code or from the execution address of a file.
With careful selection of opcodes the language entry can be executed as 6502 code and by the target CPU, see Multiple CPU language entry.
Only the MOS should call the language entry point. The MOS provides the
*FX 142,n command to invoke a language by its
ROM number. Paged ROMs, upon recognising the *command for their language
within their service routine, should issue OSBYTE call 142 with X equal to
their ROM number, found in location &F4 and also in X on entry to the
The official way to enter BASIC is by the
*BASIC command. The
MOS recognises it on the language's behalf, and selects the ROM in which
BASIC was found (the highest priority valid ROM with a language entry but no
service entry). If there is no BASIC ROM, it treats the *command as
unrecognised and offers it to the sideways ROMs and then the current filing
system. In this way third parties can replace BASIC with an extended version
on disk or in other ROMs, or can select a BASIC appropriate for a non-6502
When language code is executed, it is entered at the entry point appropriate
for the code indicated by the ROM type. This entry point is at an offset
from the code's start address. The start address is &8000 for a ROM being
executed in the I/O processor, either &8000 or the Tube transfer address for
a ROM copied to a second processor, a file's execution address for a file
loaded and executed in a second processor, or the target address of a
*GO command. Note that this means that to get expected results,
ROM images must be saved with the execution address the same as the load
The ROM type byte at start + 6 indicates what CPU the language code is intended to execute on. This also indicates where the code is entered to execute it. If there is no ROM header (indicated by there being no copyright string), then code is always entered at the start address.
6502 Code (types 0-2)
6502 code is entered at the start address with A=1. Note that 6502 BASIC is special in that the ROM type byte is &60, indicating a language with no service entry.
A 6502 language entry point is entered with registers set as following:
|0||Enter Tube environment with no language selected.|
|2||Return in Y the next character of the soft key expansion set up by call 3.|
|3||Set up soft key expansion for the key in Y, and return its length in Y.|
Prior to MOS 3.50, a 6502 language ROM could either be assembled to run at &8000, and work on either side of the Tube (eg BASIC), or could be assembled at a different address, and only run as a language on the host 6502 (eg HIBASIC).
MOS 3.50 supports relocation tables that allow one ROM to operate at either &8000 on the host 6502 or a higher address on the second processor, by performing address relocation as it is copied across. In MOS 3.50, BASIC, EDIT and View have relocation tables.
Note that no range checks are made: the host transfers the entire 16K of ROM space for all second processor types, and the 6502 Tube OS in RAM at &F800 does not prevent itself being overwritten. This means that the highest possible Tube relocation address is &B800; a <16K ROM image, or a ROM image with unwanted data like the relocation tables, cannot be placed in the second processor at a higher address to increase HIMEM.
PDP-11 Code (type 7)
PDP-11 code is entered at the offset from the start address specified after the Tube transfer address. If there is no Tube transfer address, the code is entered at the start address.
Z80 Code (type 8)
Z80 code is entered at the start address with A=1.
32016 Code (type 9)
32016 code is entered at the offset from the start address specified after the Tube transfer address. If the ROM type byte indicates that there is no Tube transfer address, the offset is still used, and a dummy Tube transfer address must be present. Code is entered with R1=1.
80186 Code (type 11)
80186 code is entered at the start address with AL=&01 if DOS has not been booted, and AL<>&01 if DOS has been booted.
ARM Code (type 13)
If bit 7 of the ROM type byte is set (ie, &CD or &ED), indicating the presence of a service entry, then bytes start+0 to start+2 are a 24-bit entry address.
If bit 7 of the ROM type byte is clear (ie, &4D), indicating the absence of a service entry, then bytes start+0 to start+3 are a 32-bit execution address, overlapping the service entry point which does not exist. When stored as a RomFS file, the Tube transfer address is the file's load address (even though the ROM type byte indicates no tranfer address exists), and is followed by the file's length.
If byte at start+0 is &4C, then the Sprow ARMCoPro uses bytes start+1 to start+2 as the 16-bit entry address.
Language memory allocation
The current language is allocated memory in the language processor.
- 6502 code is allocated fixed memory at &400 to &7FF, zero page workspace from &00 to &8F and a variable amount of memory from the bottom of memory returned by OSBYTE &83 ("OSHWM") to the top of memory returned by OSBYTE &84 ("HIMEM"). Some languages reserve part of this allocation for their user; for example BASIC sets aside zero page &70 to &8F.
- PDP-11 code is allocated memory from &100 to the top of memory returned by OSBYTE &84.
- Z80 code is allocated memory from &100 to the top of memory returned by OSBYTE &84.
- 32016 code is allocated memory from &100 to the top of memory returned by OSBYTE &84.
- ARM code on the ARM Evaluation System is allocated memory from &1000 to the top of memory returned by SWI "OS_GetEnv".
- ARM code on the Sprow ARM Copro is allocated memory from &8000 to the top of memory returned by SWI "OS_GetEnv".
Service memory allocation
Service ROMs can claim pages of shared and private workspace (1 page = 256 bytes) in the I/O processor. Service code is only ever executed in the 6502 I/O processor. Workspace will be in main memory, unless the machine is a Master and the ROMs have been written to use HAZEL.
Different categories of service (such as filing systems, interrupt handlers and utilities) are permitted to used certain fixed memory areas depending on the purpose. For full details please see The New Advanced User Guide.
Certain service code uses I/O processor memory between OSHWM and HIMEM to function, either necessarily or optionally for speed. Examples are *COMPACT or *SRLOAD. Such usage should be avoided where possible, and must be documented, as such commands can lead to user data loss.
Tube memory allocation
When the Tube is active, the host Tube support code uses all the memory below OSHWM in the I/O processor that would otherwise be allocated to a host-based language. OSHWM to HIMEM remains available for application or user use. Specialised Tube-aware languages or utilities can make use of this area using OS calls, such as the printer buffer utility supplied with the 65C102 coprocessor. Such use is ultimately under the control of the language processor - as in a non-Tube system, service code in the I/O processor should not normally use the region without permission.
Paged ROM service calls
See also the main article: Paged ROM service calls
Whenever a service is required from paged ROMs, the MOS pages in each valid slot in descending order and calls the service entry point at &8003. Every ROM must have a JMP instruction at this address except for BASIC, which the MOS treats specially.
This daisy-chaining means that a service call must always return; if a ROM does not recognise the call it must return with all registers preserved, and if it has responded to it completely then it must return with A=0 so that all lower ROMs are passed a null service call.
The MOS call vectors cannot point directly into the paged ROM space, as its contents may be incorrect at the time a vector is followed. Instead, they must point to one of the extended vector routines in the MOS. These are stubs that call an intriguing common routine, which pages in the appropriate ROM, calls the actual address and restores the old ROM slot on exit.
|Name||Address||Name||Address||Routine||Vector||(MOS 1.20)||ROM slot||(MOS 1.20)|
evt, the base address of the extended vector table, can be found by calling OSBYTE &A8; the LSB is returned in X and the MSB in Y. In all operating systems its value is &0D9F.
For instance, a ROM may execute the following to redirect OSFILE to its routine at &B000:
... PHP \ save interrupt state SEI \ disable interrupts LDA #&A8 \ set up OSBYTE call LDX #0 \ to get base address LDY #&FF \ of extended vector table. JSR &FFF4 \ call OSBYTE STX &AA \ store base address in zero page STY &AB \ X=low byte, Y=high byte LDY #&1B \ offset of FILEV's entry in ext.vec. table LDA #&00 \ LSB of our OSFILE routine's address STA (&AA),Y \ store in extended vector table INY \ add 1 to offset; Y=&1C LDA #&B0 \ MSB of our OSFILE routine's address STA (&AA),Y \ store after LSB INY \ add 1 to offset; Y=&1D LDA &F4 \ get our ROM slot number STA (&AA),Y \ store after MSB LDA #&1B \ set A=LSB of FILEV's ext.vec. handler STA &0212 \ store in FILEV LDA #&FF \ set A=MSB of FILEV's ext.vec. handler STA &0213 \ store in FILEV PLP \ restore interrupt state ...
If filing system vectors are claimed, after they are changed the ROM should issue service call &0F.
- John Kortink's STH Forums post with details of the Electron's paged ROM system
- Sprow's sideways ROM authoring notes (PDF)
- Slogger Electron Expansion 2.0 User Guide (Word document)