RGBDS(5)
โจ 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 ofBYTE.- Brackets after a type, like
LONG[n], meannconsecutive 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.
๐ Header ๐
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-1meaning 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โ bits0โ6indicate 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 DepthLONG 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 theIF Type โ 1conditional.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โ bits0โ2indicate the section's type:0โ WRAM01โ VRAM2โ ROMX3โ ROM04โ HRAM5โ WRAMX6โ SRAM7โ 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'sType(as affected by flags like-tor-dinrgblink(1)), or-1to 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'sType(with the same caveats as for theAddress), or-1to indicate that the linker should automatically decide.BYTE Alignmentโ how many bits of the section's address should be equal toAlignOfs, starting from the least-significant bit.LONG AlignOfsโ alignment offset. Must be strictly less than1 << 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'sData.
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'sDataat which the patch should be applied. Must not be greater than the section'sSizeminus the patch's size (seeTypebelow).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.LOADblocks, see โRAM codeโ inrgbasm(5).LONG PCOffsetโ offset of the PC symbol within the section designated byPCSectionID. 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 patch1โ Little-endian two-byte patch2โ Little-endian four-byte patch3โ Single-bytejrpatch; the patch's value will be subtracted to PC + 2 (i.e.jr @must be the infinite loop18 FE). ๐น๏ธ
LONG RPNSizeโ size of theRPNExprbelow.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 byPCSectionID. 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 theRPNExprbelow 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'sLONGID.$51โBANK(section); followed by the section'sSTRINGname.$52โ PC'sBANK()(i.e.BANK(@)). hi bestie ๐$53โSIZEOF(section); followed by the section'sSTRINGname.$54โSTARTOF(section); followed by the section'sSTRINGname.$55โSIZEOF(sectiontype); followed by the sectiontype'sBYTEvalue (see the Type values in "Sections").$56โSTARTOF(sectiontype); followed by the sectiontype'sBYTEvalue (see the Type values in "Sections").$60โldhcheck. checks if the value is a validldhoperand (see "Load Instructions" ingbz80(7)), i.e. that it's between either$00and$FF, or$FF00and$FFFF, both inclusive. the value is then ANDed with$00FF(& $FF).$61โrstcheck. checks if the value is a validrstvector (see "RST vec" ingbz80(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/setcheck; followed by the instruction'sBYTEmask. checks if the value is a valid bit index (see e.g. "BIT u3, r8" ingbz80(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 theLONGinteger. big number energy ๐ฅ$81โ A symbol's value; followed by the symbol'sLONGID.
๐ 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
- arch rolling: rgbds 1.0.4-1 ยท MIT
- nixpkgs unstable: rgbds 1.0.4
- ๐ the page's license: MIT
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms