RGBDS(5)

๐Ÿ“ฆ
version 1.0.4 ยท 4 versions
, in arch rolling, nixpkgs unstable

โœจ NAME โœจ

rgbds โ€” object file format documentation ๐Ÿ’…

This is RGBDS(5), aka the File Formats Manual entry for the RGB object file format. Buckle up, bestie โ€” we're going binary-structure deep. ๐Ÿซถ

๐Ÿ’– DESCRIPTION ๐Ÿ’–

This is the description of the RGB object file format that rgbasm(1) outputs and rgblink(1) reads. It's literally the handoff between assembler and linker โ€” main character energy, no cap. โœจ

๐Ÿฆ‹ FILE STRUCTURE ๐Ÿฆ‹

Okay so these are the types you're gonna see:

  • LONG โ€” a 32-bit integer stored in little-endian format. ๐Ÿง 
  • BYTE โ€” an 8-bit integer.
  • STRING โ€” a 0-terminated string of BYTE.
  • Brackets after a type, like LONG[n], mean n consecutive elements โ€” here, LONGs. ๐ŸŒ€

All items are contiguous, with no padding anywhere โ€” and yes, that also means they may not be aligned in the file! ๐Ÿ˜ญ No cozy alignment naps. Everything is packed.

REPT n means the fields between the REPT and the corresponding ENDR are repeated n times. ๐Ÿ”

All IDs refer to objects within the file. For example, symbol ID $0001 refers to the second symbol defined in this object file's โ€œSymbolsโ€ array. The only exception is the โ€œSource file infoโ€ nodes, whose IDs are backwards โ€” i.e. source node ID $0000 refers to the last node in the array, not the first one. ๐Ÿซ  References to other object files are made by imports (symbols), by name (sections), etc. โ€” but never by ID. Periodt.

This is the opening act, and it's short:

  • BYTE Magic[4] โ€” the magic bytes are literally "RGB9" ๐Ÿ”ฎ.
  • LONG RevisionNumber โ€” the format's revision number this file uses. This is always in the same place in all revisions. Stable placement, we love to see it.
  • LONG NumberOfSymbols โ€” how many symbols are defined in this object file.
  • LONG NumberOfSections โ€” how many sections are defined in this object file.

๐ŸŒธ Source file info ๐ŸŒธ

  • LONG NumberOfNodes โ€” the number of source context nodes contained in this file.

Then it's a REPT NumberOfNodes situation, and each node has all this:

  • LONG ParentID โ€” ID of the parent node, with -1 meaning that this is the root node.
โš ๏ธ Important: the nodes are actually written in reverse order, meaning the node with ID 0 is the last one in the list! Don't get got by this, bestie. ๐Ÿ˜ญ
  • LONG ParentLineNo โ€” line at which the parent node's context was exited; meaningless for the root node.
  • BYTE Type โ€” bits 0โ€“6 indicate the node's type:
    • 0 โ€” REPT node ๐ŸŒ€
    • 1 โ€” File node ๐Ÿ“„
    • 2 โ€” Macro node ๐Ÿงฉ

Bit 7 being set means that the node is โ€œquietedโ€ (see โ€œExcluding locations from backtracesโ€ in rgbasm(5)). ๐Ÿคซ

  • IF Type โ‰  0 โ€” if the node is not a REPT node...
    • STRING Name โ€” the node's name: either a file name, or the macro's name prefixed by its definition's file name (e.g. src/includes/defines.asm::error). ๐Ÿ“›
  • ELSE โ€” if the node is a REPT, it also contains the iteration counter of all parent REPTs:
    • LONG Depth
    • LONG Iter[Depth] โ€” the number of REPT iterations, by increasing depth. ๐Ÿ“ˆ
  • ENDC โ€” closes that conditional.
  • ENDR โ€” closes the whole repeated node block.

๐Ÿ’… Symbols ๐Ÿ’…

REPT NumberOfSymbols โ€” each symbol entry starts with:

  • STRING Name โ€” this symbol's name. Local symbols are stored as their full name (Scope.symbol). ๐Ÿ“›
  • BYTE Type โ€” one of:
    • 0 โ€” Local symbol only used in this file. ๐Ÿ 
    • 1 โ€” Import of an exported symbol (by name) from another object file. ๐Ÿ“ฅ
    • 2 โ€” Exported symbol visible from other object files. ๐Ÿ“ค
  • IF Type โ‰  1 โ€” if the symbol is defined in this object file...
    • LONG NodeID โ€” context in which the symbol was defined.
    • LONG LineNo โ€” line number in the context at which the symbol was defined.
    • LONG SectionID โ€” the ID of the section in which the symbol is defined. If the symbol doesn't belong to any specific section (i.e. it's a constant), this field contains -1.
    • LONG Value โ€” the symbol's value. If the symbol belongs to a section, this is the offset within that symbol's section.
  • ENDC โ€” closes the IF Type โ‰  1 conditional.
  • ENDR โ€” closes the symbol repeat.

๐Ÿ‘‘ Sections ๐Ÿ‘‘

REPT NumberOfSections โ€” each section has:

  • STRING Name โ€” the section's name.
  • LONG NodeID โ€” context in which the section was defined.
  • LONG LineNo โ€” line number in the context at which the section was defined.
  • LONG Size โ€” the section's size, in bytes.
  • BYTE Type โ€” bits 0โ€“2 indicate the section's type:
    • 0 โ€” WRAM0
    • 1 โ€” VRAM
    • 2 โ€” ROMX
    • 3 โ€” ROM0
    • 4 โ€” HRAM
    • 5 โ€” WRAMX
    • 6 โ€” SRAM
    • 7 โ€” OAM

Bit 7 being set means that the section is a โ€œunionโ€ (see โ€œUnionized sectionsโ€ in rgbasm(5)). Bit 6 being set means that the section is a โ€œfragmentโ€ (see โ€œSection fragmentsโ€ in rgbasm(5)). These two bits are mutually exclusive. โš”๏ธ

  • LONG Address โ€” address this section must be placed at. This must either be valid for the section's Type (as affected by flags like -t or -d in rgblink(1)), or -1 to indicate that the linker should automatically decide โ€” the section is โ€œfloatingโ€. ๐ŸŽˆ
  • LONG Bank โ€” ID of the bank this section must be placed in. This must either be valid for the section's Type (with the same caveats as for the Address), or -1 to indicate that the linker should automatically decide.
  • BYTE Alignment โ€” how many bits of the section's address should be equal to AlignOfs, starting from the least-significant bit.
  • LONG AlignOfs โ€” alignment offset. Must be strictly less than 1 << Alignment. ๐Ÿ“
  • IF Type = 2 || Type = 3 โ€” if the section has ROM type, it contains data:
    • BYTE Data[Size] โ€” the section's raw data. Bytes that will be patched over must be present, even though their contents will be overwritten. ๐Ÿงฑ
    • LONG NumberOfPatches โ€” how many patches must be applied to this section's Data.

Then REPT NumberOfPatches โ€” each patch is:

  • LONG NodeID โ€” context in which the patch was defined.
  • LONG LineNo โ€” line number in the context at which the patch was defined.
  • LONG Offset โ€” offset within the section's Data at which the patch should be applied. Must not be greater than the section's Size minus the patch's size (see Type below).
  • LONG PCSectionID โ€” ID of the section in which PC is located. This is usually the same section within which the patch is applied, except for e.g. LOAD blocks, see โ€œRAM codeโ€ in rgbasm(5).
  • LONG PCOffset โ€” offset of the PC symbol within the section designated by PCSectionID. It is expected that PC points to the instruction's first byte for instruction operands (i.e. jp @ must be an infinite loop), and to the patch's first byte otherwise (db, dw, dl). ๐ŸŽฏ
  • BYTE Type โ€” one of:
    • 0 โ€” Single-byte patch
    • 1 โ€” Little-endian two-byte patch
    • 2 โ€” Little-endian four-byte patch
    • 3 โ€” Single-byte jr patch; the patch's value will be subtracted to PC + 2 (i.e. jr @ must be the infinite loop 18 FE). ๐Ÿ•น๏ธ
  • LONG RPNSize โ€” size of the RPNExpr below.
  • BYTE RPNExpr[RPNSize] โ€” the patch's value, encoded as a RPN expression (see โ€œRPN expressionsโ€).

Then ENDR closes the patch repeat, and ENDC closes the ROM-data conditional.

โญ Assertions โญ

  • LONG NumberOfAssertions โ€” how many assertions this object file contains.
  • REPT NumberOfAssertions โ€” and then the assertion entries begin.

Assertions are essentially patches with a message. ๐Ÿ’ฌ

  • LONG NodeID โ€” the context where this assertion got defined. iykyk, it's just the node it lives in ๐Ÿซถ
  • LONG LineNo โ€” which line in that context the assertion was defined on. receipts! ๐Ÿ“
  • LONG Offset โ€” literally unused, leftover from the patch structure. it's giving vestigial, don't worry about it ๐Ÿ’€
  • LONG PCSectionID โ€” the ID of the section that PC is chillin' in.
  • LONG PCOffset โ€” where the PC symbol sits inside the section named by PCSectionID. that's its spot, periodt.
  • BYTE Type โ€” tells you what should happen when the expression evaluates to a non-zero value. the options are:
    • 0 โ€” print a warning message, and continue linking normally. soft launch vibes โœจ
    • 1 โ€” print an error message so linking will fail, but still let the other assertions get evaluated. it's not over till it's over ๐Ÿ˜ญ
    • 2 โ€” print a fatal error message and abort immediately. no thoughts, no other assertions, just done ๐Ÿ’…
  • LONG RPNSize โ€” how big the RPNExpr below is.
  • BYTE RPNExpr[RPNSize] โ€” the patch's value, encoded as an RPN expression (peep "RPN expressions"). this is the main character of the struct ngl ๐Ÿ‘‘
  • STRING Message โ€” the message that gets displayed if the expression evaluates to a non-zero value. if it's empty, a generic message shows up instead. no custom text? no problem.
  • ENDR

โœจ RPN expressions โœจ

okay bestie so expressions in the object file are stored as RPN โ€” that's "Reverse Polish Notation" โ€” and it's this gorge notation that lets you compute literally arbitrary expressions with just a simple stack. ๐Ÿฅž

example time, because the way this works is actually iconic: the expression 2 5 - first pushes the value "2" onto the stack, then "5". then the - operator pops two arguments off the stack, subtracts them, and pushes the result ("3") right back on. slay. a well-formed RPN expression never tries to pop from an empty stack, and leaves exactly one value in it at the end. that's the vibe check for correctness fr fr.

RGBDS encodes RPN expressions as an array of BYTEs. the first byte encodes either an operator, or a literal โ€” and literals gobble up more BYTEs after them:

  • $00 โ€” Addition operator (+)
  • $01 โ€” Subtraction operator (-)
  • $02 โ€” Multiplication operator (*)
  • $03 โ€” Division operator (/)
  • $04 โ€” Modulo operator (%)
  • $05 โ€” Negation (unary -)
  • $06 โ€” Exponent operator (**)
  • $10 โ€” Bitwise OR operator (|)
  • $11 โ€” Bitwise AND operator (&)
  • $12 โ€” Bitwise XOR operator (^)
  • $13 โ€” Bitwise complement operator (unary ~)
  • $21 โ€” Logical AND operator (&&)
  • $22 โ€” Logical OR operator (||)
  • $23 โ€” Logical complement operator (unary !)
  • $30 โ€” Equality operator (==)
  • $31 โ€” Non-equality operator (!=)
  • $32 โ€” Greater-than operator (>)
  • $33 โ€” Less-than operator (<)
  • $34 โ€” Greater-than-or-equal operator (>=)
  • $35 โ€” Less-than-or-equal operator (<=)
  • $40 โ€” Left shift operator (<<)
  • $41 โ€” Arithmetic/signed right shift operator (>>)
  • $42 โ€” Logical/unsigned right shift operator (>>>)
  • $50 โ€” BANK(symbol); followed by the symbol's LONG ID.
  • $51 โ€” BANK(section); followed by the section's STRING name.
  • $52 โ€” PC's BANK() (i.e. BANK(@)). hi bestie ๐Ÿ‘‹
  • $53 โ€” SIZEOF(section); followed by the section's STRING name.
  • $54 โ€” STARTOF(section); followed by the section's STRING name.
  • $55 โ€” SIZEOF(sectiontype); followed by the sectiontype's BYTE value (see the Type values in "Sections").
  • $56 โ€” STARTOF(sectiontype); followed by the sectiontype's BYTE value (see the Type values in "Sections").
  • $60 โ€” ldh check. checks if the value is a valid ldh operand (see "Load Instructions" in gbz80(7)), i.e. that it's between either $00 and $FF, or $FF00 and $FFFF, both inclusive. the value is then ANDed with $00FF (& $FF).
  • $61 โ€” rst check. checks if the value is a valid rst vector (see "RST vec" in gbz80(7)), that is, one of $00, $08, $10, $18, $20, $28, $30, or $38. the value is then ORed with $C7 (| $C7).
  • $62 โ€” bit/res/set check; followed by the instruction's BYTE mask. checks if the value is a valid bit index (see e.g. "BIT u3, r8" in gbz80(7)), that is, from 0 to 7. the value is then ORed with the instruction's mask.
  • $70 โ€” HIGH byte.
  • $71 โ€” LOW byte.
  • $72 โ€” BITWIDTH value.
  • $73 โ€” TZCOUNT value.
  • $80 โ€” Integer literal; followed by the LONG integer. big number energy ๐Ÿ”ฅ
  • $81 โ€” A symbol's value; followed by the symbol's LONG ID.

๐Ÿ‘€ SEE ALSO ๐Ÿ‘€


๐Ÿ“œ HISTORY ๐Ÿ“œ

rgbasm(1) and rgblink(1) were originally written by Carsten Sรธrensen as part of the ASMotor package, and was later repackaged in RGBDS by Justin Lloyd. it's now maintained by a number of contributors at https://github.com/gbdev/rgbds. legends, honestly ๐Ÿซถ๐Ÿ‘‘


okay that's the whole page bestie, you ate that ๐Ÿ’… this lil masterpiece comes to you from Debian, dated September 22, 2026, and it's the RGBDS(5) page โ€” go forth and encode your RPN with confidence, mwah ๐ŸŽ€โœจ๐Ÿ’–

๐Ÿ“ฆ where this page comes from