RISC OS Build EnvironmentROBE

Appendix49Appendix O: RISC OS ROM image formats

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.

OffsetSizeRegionContents
0&100initialisation codeInitialisation code area.
100&7F00system initialisationSystem initialisation block, if present.
8000variableROM imageRISC OS ROM image header and ROM contents in the normal case.
end-16-xvariableROM identification recordsOptional tagged identification records stored immediately before the 16-byte tail.
end-1616tail identificationMandatory 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.

OffsetSizeFieldContents
04idSignature RSI1 (&31495352).
4&7EFCsystem initialisation dataSystem-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.

OffsetSizeFieldContents
04idSignature Cog1 (&31676F43).
44kernel versionKernel version word.
84flagsROM-image flags. Bit 0 marks a 32-bit ROM.
124rom sizeExpected total ROM image size in bytes.
164base linkage addressPreferred linkage base address.
204entry offsetOffset of the ROM entry point.
24variablememory initialisation dataAdditional 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:

OffsetSizeFieldContents
04idSignature RISC (&43534952).
44checksumChecksum word.
84module chain offsetOffset from the start of the ROM image to the first module-chain entry.
124reservedReserved 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:

OffsetSizeFieldContents
0variabledataIdentification payload.
size-84record sizeTotal record size in bytes, including this trailing size/type pair.
size-44typeIdentification 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:

TypeMeaning
0OS name. Mapped to podule device-data description text.
1Part number. Mapped to podule device-data part-number text.
2Build date. Mapped to podule device-data date text.
3Dealer name. Mapped to podule device-data place-of-manufacture text.
4User name. Not currently exposed as a device-data chunk by this parser.
5User address. Not currently exposed as a device-data chunk by this parser.
6Printable OS description. Not currently exposed as a device-data chunk by this parser.
7Hardware 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:

OffsetSizeFieldContents
04idSignature OSIm (&4D49534F).
44flagsOS-image flags. Bit 0 indicates a compressed image.
84image sizeOS-image size in bytes.
124entry table offsetOffset to the OS entry-point table.
164entry countNumber of entries in the OS table.
204compressed sizeCompressed image size.
244decompression header offsetOffset to the decompression header.
284compression offsetOffset 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:

OffsetSizeFieldContents
04signatureROM signature word &FFFFFFFF. This is the word actually checked by the loader's tail validation; see the note below.
44post wordPOST word. The loader does not interpret it.
84negative checksum32-bit negative checksum.
124crcLow 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.

The comments in the source describe an optional extended footer immediately before the POST word. It begins with a four-byte footer header:

OffsetSizeFieldContents
02backward offsetOffset backwards from this word to the first footer tag.
22footer crc16-bit CRC for the footer data.

The maximum extended-footer length is therefore 64 Kbytes. Each tag within that footer is:

OffsetSizeFieldContents
01tag idTag identifier.
11lengthTag payload length in bytes.
2variabledataTag 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.

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)

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