Overview
This appendix documents two ROM image layouts used by RISC OS systems: the 32-bit RISC OS Ltd Select-style format and the RISC OS 5 format.
The structures described here are based on the format notes and parsing logic in riscos/pymods/core/podule.py. That mainly matters where the loader comments preserve historical variation or describe a recognition heuristic rather than a single fixed outer header.
ROM format reference
Common conventions
Both formats are parsed as little-endian streams of 32-bit words. The complete image is treated as one contiguous file. Once a ROM is accepted, its module chain is walked as a series of length-prefixed modules, where each module begins with a 32-bit size word and the next module starts immediately after that many bytes.
The podule loader treats the module payload itself as beginning immediately after each size word, so a module-chain entry of length N contributes a RISC OS module chunk covering bytes +4 to +(N-1) of that entry.
RISC OS Ltd Select-style ROM format
The 32-bit RISC OS Ltd ROM format used by Select-style ROMs is described in the source comments as three main regions followed by footer data.
| Offset | Size | Region | Contents |
|---|---|---|---|
| 0 | &100 | initialisation code | Initialisation code area. |
| 100 | &7F00 | system initialisation | System initialisation block, if present. |
| 8000 | variable | ROM image | RISC OS ROM image header and ROM contents in the normal case. |
| end-16-x | variable | ROM identification records | Optional tagged identification records stored immediately before the 16-byte tail. |
| end-16 | 16 | tail identification | Mandatory 16-byte tail block ending with the module-chain pointer. |
The system-initialisation block may be absent. If the word at &100 is itself a ROM-image header, the ROM image is treated as starting there instead of at &8000.
System-initialisation header
When present, the system-initialisation area begins with the word &31495352, which is the ASCII signature RSI1.
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 4 | id | Signature RSI1 (&31495352). |
| 4 | &7EFC | system initialisation data | System-initialisation data. The parser only checks the signature and does not decode the remaining fields. |
ROM image header
The ROM image proper begins with a six-word header identified by the word &31676F43, the ASCII signature Cog1.
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 4 | id | Signature Cog1 (&31676F43). |
| 4 | 4 | kernel version | Kernel version word. |
| 8 | 4 | flags | ROM-image flags. Bit 0 marks a 32-bit ROM. |
| 12 | 4 | rom size | Expected total ROM image size in bytes. |
| 16 | 4 | base linkage address | Preferred linkage base address. |
| 20 | 4 | entry offset | Offset of the ROM entry point. |
| 24 | variable | memory initialisation data | Additional ROM-image data begins here. |
The Cog1 signature identifies this header. Flag bit 0 marks the image as a 32-bit ROM. The declared ROM size should not be smaller than the file length; otherwise the image should be treated as truncated.
If the base linkage address is megabyte-aligned, it can be used as the ROM's mapping or linkage base. Older ROMs may omit meaningful values in the later header words, so non-aligned values are commonly ignored by tolerant loaders.
Tail identification block
The final 16 bytes of the image contain the mandatory RISC OS Ltd tail block:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 4 | id | Signature RISC (&43534952). |
| 4 | 4 | checksum | Checksum word. |
| 8 | 4 | module chain offset | Offset from the start of the ROM image to the first module-chain entry. |
| 12 | 4 | reserved | Reserved word, expected to be zero. |
The module-chain offset points to the first length-prefixed module-chain entry. Each entry begins with a size word. A zero size terminates the chain; a chain that runs beyond the declared ROM size is invalid.
ROM identification records
Immediately before the 16-byte tail, the format may contain an optional reverse list of tagged identification records. These records are scanned backwards from the tail.
Each record is laid out as:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | variable | data | Identification payload. |
| size-8 | 4 | record size | Total record size in bytes, including this trailing size/type pair. |
| size-4 | 4 | type | Identification type, using the same sub-reason numbering as OS_ReadSysInfo 9's ROM-information values (see below); -1 terminates the reverse scan. |
The parser expects a non-negative size and stops when it sees type -1. For selected record types it exposes the payload as device-data chunks. The type values are OS_ReadSysInfo 9's ReadSysInfoReason_ROMInformation sub-reason codes:
| Type | Meaning |
|---|---|
| 0 | OS name. Mapped to podule device-data description text. |
| 1 | Part number. Mapped to podule device-data part-number text. |
| 2 | Build date. Mapped to podule device-data date text. |
| 3 | Dealer name. Mapped to podule device-data place-of-manufacture text. |
| 4 | User name. Not currently exposed as a device-data chunk by this parser. |
| 5 | User address. Not currently exposed as a device-data chunk by this parser. |
| 6 | Printable OS description. Not currently exposed as a device-data chunk by this parser. |
| 7 | Hardware platform name. Not currently exposed as a device-data chunk by this parser. |
RISC OS 5 ROM format
A RISC OS 5 ROM image consists of a combined HAL plus OS image with a 16-byte tail. Unlike the RISC OS Ltd layout, there is no single fixed outer header at offset zero.
Unlike the RISC OS Ltd format's base linkage address, which is a value read from the image's own header, the RISC OS 5 format has no equivalent header field. The loader instead maps the image at a fixed address, &FC000000, rather than anything read from the file. A RISC OS 5 image therefore has no equivalent of a declared base address as a genuine format property; the mapping address is a fixed property of the loader, not of the file.
One common recognition strategy is to check for two marker strings anywhere in the file: OSIm and UtilityModule. If either is absent, the image is rejected immediately.
OS image header
Within the RISC OS 5 image, the OS section begins with the word &4D49534F, the ASCII signature OSIm. The source comments describe the header as:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 4 | id | Signature OSIm (&4D49534F). |
| 4 | 4 | flags | OS-image flags. Bit 0 indicates a compressed image. |
| 8 | 4 | image size | OS-image size in bytes. |
| 12 | 4 | entry table offset | Offset to the OS entry-point table. |
| 16 | 4 | entry count | Number of entries in the OS table. |
| 20 | 4 | compressed size | Compressed image size. |
| 24 | 4 | decompression header offset | Offset to the decompression header. |
| 28 | 4 | compression offset | Offset to the compressed data stream. |
The loader described by the source comments does not decode this header further; it relies on the presence of the marker and on the module-chain and tail heuristics described below.
ROM tail
The final 16 bytes of the file are expected to contain:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 4 | signature | ROM signature word &FFFFFFFF. This is the word actually checked by the loader's tail validation; see the note below. |
| 4 | 4 | post word | POST word. The loader does not interpret it. |
| 8 | 4 | negative checksum | 32-bit negative checksum. |
| 12 | 4 | crc | Low 32 bits of the documented 64-bit CRC field. |
The source comments in podule.py describe this layout the other way around, with the POST word at offset +0 and the ROM signature at offset +4. That ordering does not match the loader's own validation code: ROMImageIdentifierRO5.identify() unpacks the final 16 bytes as four little-endian words and checks only the first word, at offset +0, against &FFFFFFFF; it never inspects the word at offset +4 for this purpose. This appendix follows the offset that the implementing code actually validates rather than the comment's labelling, since a builder or loader following the comment's ordering would place the signature at the wrong offset for interoperability with this implementation. The byte ranges occupied by the checksum and CRC fields are unaffected by this discrepancy.
Extended footer tags
The comments in the source describe an optional extended footer immediately before the POST word. It begins with a four-byte footer header:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 2 | backward offset | Offset backwards from this word to the first footer tag. |
| 2 | 2 | footer crc | 16-bit CRC for the footer data. |
The maximum extended-footer length is therefore 64 Kbytes. Each tag within that footer is:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 1 | tag id | Tag identifier. |
| 1 | 1 | length | Tag payload length in bytes. |
| 2 | variable | data | Tag data bytes. |
The footer tags are part of the described format even if a given loader does not currently decode them.
Module-chain discovery heuristic
The parser locates the module chain by searching for the string UtilityModule, aligning that position down to a word boundary, and then scanning backwards by words for a value equal to:
offset_of_UtilityModule_string - current_word_offset + Module_Title
When such a word is found, it is treated as the title-offset word inside a logical UtilityModule header. The module containing that title is therefore assumed to begin Module_Title bytes earlier, and the preceding word is treated as the length word for a special UtilityModule record.
In the loader behaviour described by the source, that UtilityModule record is not exposed as a normal module chunk. It is skipped, and the real module chain starts at the following entry.
Footer summaries
RISC OS Ltd footer summary
This is the mandatory 16-byte tail at the end of a Select-style ROM:
+0 4 bytes 'RISC' +4 4 bytes checksum +8 4 bytes offset to first module-chain entry +12 4 bytes reserved (normally zero)
RISC OS 5 footer summary
This is the final 16-byte tail used by the RISC OS 5 format, ordered to match the offset actually validated by the loader rather than the source comment's labelling (see "ROM tail" above):
+0 4 bytes &FFFFFFFF ROM signature (validated by the loader) +4 4 bytes POST word (not used) +8 4 bytes 32-bit negative checksum +12 4 bytes CRC field