![]() |
![]()
| ![]() |
![]()
NAME
DESCRIPTIONThis is the full description of the assembly language used by rgbasm(1). For the full description of instructions in the machine language supported by the Game Boy CPU, see gbz80(7). It is advisable to have some familiarity with the Game Boy hardware before reading this document. RGBDS is specifically targeted at the Game Boy, and thus a lot of its features tie directly to its concepts. This document is not intended to be a Game Boy hardware reference. Generally, “the linker” will refer to rgblink(1), but any program that processes RGBDS object files (described in rgbds(5)) can be used in its place. SYNTAXThe syntax is line-based, just as in any other assembler. Each line may have components in this order: [directive]
[; comment] [label:]
[instruction [:: instruction ...]]
[; comment] Directives are commands to the assembler itself, such as
Labels tie a name to a specific location within a section (see Labels below). Instructions are assembled into Game Boy opcodes. Multiple
instructions on one line can be separated by double colons
‘ The available instructions are documented in gbz80(7). Note that where an instruction requires an 8-bit register
r8, Note also that where an instruction requires a condition code
cc, All reserved keywords (directives, register names, etc.) are case-insensitive; all identifiers (labels and other symbol names) are case-sensitive. Comments are used to give humans information about the code, such as explanations. The assembler always ignores comments and their contents. There are two kinds of comments, inline and block. Inline comments
are anything that follows a semicolon
‘ An example demonstrating these syntax features: SECTION "My Code", ROM0 ; a directive MyFunction: ; a label push hl ; an instruction /* ...and multiple instructions, with mixed case */ ld a, [hli] :: LD H, [HL] :: Ld l, a pop /*wait for it*/ hl ret Sometimes lines can be too long and it may be necessary to split them. To do so, put a backslash at the end of the line: DB 1, 2, 3, \ 4, 5, 6, \ ; Put it before any comments 7, 8, 9 DB "Hello, \ ; Space before the \ is included world!" ; Any leading space is included Symbol interpolationA funky feature is writing a symbol between
‘ Symbol interpolations can be nested, too! DEF topic EQUS "life, the universe, and \"everything\"" DEF meaning EQUS "answer" ; Defines answer = 42 DEF {meaning} = 42 ; Prints "The answer to life, the universe, and "everything" is $2A" PRINTLN "The {meaning} to {topic} is {{meaning}}" PURGE topic, meaning, {meaning} Symbols can be
interpolated
even in the contexts that disable automatic
expansion
of string constants: ‘ It's possible to change the way symbols are printed by specifying
a print format like so:
‘
All the format specifier parts are optional except the
‘
Examples: SECTION "Test", ROM0[2] X: ; This works with labels **whose address is known** DEF Y = 3 ; This also works with variables DEF SUM EQU X + Y ; And likewise with numeric constants ; Prints "%0010 + $3 == 5" PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}" rsset 32 DEF PERCENT rb 1 ; Same with offset constants DEF VALUE = 20 DEF RESULT = MUL(20.0, 0.32) ; Prints "32% of 20 = 6.40" PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}" DEF WHO EQUS STRLWR("WORLD") ; Prints "Hello world!" PRINTLN "Hello {s:WHO}!" Although, for these examples, EXPRESSIONSAn expression can be composed of many things. Numeric expressions are always evaluated using signed 32-bit math. Zero is considered to be the only "false" number, all non-zero numbers (including negative) are "true". An expression is said to be "constant" if
The instructions in the macro-language generally require constant expressions. Numeric formatsThere are a number of numeric formats.
Underscores are also accepted in numbers, except at the beginning
of one. This can be useful for grouping digits, like
‘ The "character constant" form yields the value the character maps to in the current charmap. For example, by default (refer to ascii(7)) ‘"A"’ yields 65. See Character maps for information on charmaps. The last one, Game Boy graphics, is quite interesting and useful. After the backtick, 8 digits between 0 and 3 are expected, corresponding to pixel values. The resulting value is the two bytes of tile data that would produce that row of pixels. For example, ‘`01012323’ is equivalent to ‘$0F55’. You can also use symbols, which are implicitly replaced with their value. OperatorsYou can use these operators in numeric expressions (listed from highest to lowest precedence):
‘**’ raises a number to a
non-negative power. It is the only
right-associative
operator, meaning that ‘ ‘~’ complements a value by inverting all 32 of its bits. ‘%’ is used to get the remainder of the
corresponding division, so that ‘ Shifting works by shifting all bits in the left operand either left (‘<<’) or right (‘>>’) by the right operand's amount. When shifting left, all newly-inserted bits are reset; when shifting right, they are copies of the original most significant bit instead. This makes ‘a << b’ and ‘a >> b’ equivalent to multiplying and dividing by 2 to the power of b, respectively. Comparison operators return 0 if the comparison is false, and 1 otherwise. Unlike in many other languages, and for technical reasons,
The operators ‘&&’ and ‘&’ with a zero constant as either operand will be constant 0, and ‘||’ with a non-zero constant as either operand will be constant 1, even if the other operand is non-constant. ‘!’ returns 1 if the operand was 0, and 0 otherwise. Even a non-constant operand with any non-zero bits will return 0. Integer functionsBesides operators, there are also some functions which have more specialized uses.
Fixed-point expressionsFixed-point numbers are technically just integers, but
conceptually they have a decimal point at a fixed location (hence the name).
This gives them increased precision, at the cost of a smaller range, while
remaining far cheaper to manipulate than floating-point numbers (which
The default precision of all fixed-point numbers is 16 bits,
meaning the lower 16 bits are used for the fractional part; so they count in
65536ths of 1.0. This precision can be changed with the
Since fixed-point values are still just integers, you can use them in normal integer expressions. You can easily truncate a fixed-point number into an integer by shifting it right by the number of fractional bits. It follows that you can convert an integer to a fixed-point number by shifting it left that same amount. Note that the current number of fractional bits can be computed as
The following functions are designed to operate with fixed-point numbers:
There are no functions for fixed-point addition and subtraction, because the ‘+’ and ‘-’ operators can add and subtract pairs of fixed-point operands. Note that some operators or functions are
meaningful when combining integers and fixed-point values. For example,
‘
2.0 * 3 ’ is equivalent to
‘MUL(2.0, 3.0) ’, and
‘6.0 / 2 ’ is equivalent to
‘DIV(6.0, 2.0) ’. Be careful and think
about what the operations mean when doing this sort of thing.All of these fixed-point functions can take an optional
final argument, which is the precision to use for that one operation. For
example, ‘ The The trigonometry functions ( These functions are useful for automatic generation of various tables. For example: ; Generate a table of 128 sine values ; from sin(0.0) to sin(0.5) excluded, ; with amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]. FOR angle, 0.0, 0.5, 0.5 / 128 db MUL(SIN(angle) + 1.0, 128.0 / 2) >> 16 ENDR String expressionsThe most basic string expression is any number of characters
contained in double quotes (‘
Multi-line strings are contained in triple quotes
(‘ Raw strings are prefixed by a hash ‘#’. Inside them,
backslashes and braces are treated like regular characters, so they will not
be expanded as macro arguments, interpolated symbols, or escape sequences.
For example, the raw string
‘ The following functions operate on string expressions, and return strings themselves.
The following functions operate on string expressions, but return integers.
Note that the first character of a string is at index 0, and the last is at index -1. The following legacy functions are similar to other functions that operate on string expressions, but for historical reasons, they count characters starting from position 1, not from index 0! (Position -1 still counts from the last character.)
Character mapsWhen writing text strings that are meant to be displayed on the Game Boy, the character encoding in the ROM may need to be different than the source file encoding. For example, the tiles used for uppercase letters may be placed starting at tile index 128, which differs from ASCII starting at 65. Character maps allow mapping strings to arbitrary sequences of numbers: CHARMAP "A", 42 CHARMAP ":)", 39 CHARMAP "<br>", 13, 10 CHARMAP "€", $20ac This would result in ‘ Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes. It is possible to create multiple character maps and then switch between them as desired. This can be used to encode debug information in ASCII and use a different encoding for other purposes, for example. Initially, there is one character map called ‘main’ and it is automatically selected as the current character map from the beginning. There is also a character map stack that can be used to save and restore which character map is currently active.
Note: Modifications to a character map take effect immediately from that point onward. Other functionsThere are a few other functions that do things beyond numeric or string operations:
SECTIONSBefore you can start writing code, you must define a section. This tells the assembler what kind of information follows and, if it is code, where to put it. SECTION name,
type SECTION name,
type, options SECTION name,
type[addr] SECTION name,
type[addr],
options name is a string enclosed in double quotes, and can be a new name or the name of an existing section. If the type doesn't match, an error occurs. All other sections must have a unique name, even in different source files, or the linker will treat it as an error. Possible section types are as follows:
Since RGBDS produces ROMs, code and data can only be placed in
options are comma-separated and may include:
If [addr] is not specified, the section is
considered “floating”; the linker will automatically calculate
an appropriate address for the section. Similarly, if
Sections can also be placed by using a linker script file. The format is described in rgblink(5). They allow the user to place floating sections in the desired bank in the order specified in the script. This is useful if the sections can't be placed at an address manually because the size may change, but they have to be together. Section examples:
The current section can be ended without starting a new section by
using Section stack
SECTION "Code", ROM0 Function: ld a, 42 PUSHS "Variables", WRAM0 wAnswer: db POPS ld [wAnswer], a RAM codeSometimes you want to have some code in RAM. But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some point. This means the code (or data) will not be stored in the place it
gets executed. Luckily, SECTION "LOAD example", ROMX CopyCode: ld de, RAMCode ld hl, RAMLocation ld c, RAMCode.end - RAMCode .loop ld a, [de] inc de ld [hli], a dec c jr nz, .loop ret RAMCode: LOAD "RAM code", WRAM0 RAMLocation: ld hl, .string ld de, $9864 .copy ld a, [hli] ld [de], a inc de and a jr nz, .copy ret .string db "Hello World!\0" ENDL .end A In the example above, all of the code and data will end up in the “LOAD example” section. You will notice the ‘RAMCode’ and ‘RAMLocation’ labels. The former is situated in ROM, where the code is stored, the latter in RAM, where the code will be loaded. You cannot nest The current
Unionized sectionsWhen you're tight on RAM, you may want to define overlapping
static memory allocations, as explained in the
Allocating
overlapping spaces in RAM section. However, a
Different declarations of the same unionized section are not appended, but instead overlaid on top of each other, just like Allocating overlapping spaces in RAM. Similarly, the size of an unionized section is the largest of all its declarations. Section fragmentsSection fragments are sections with a small twist: when several of
the same name are encountered, they are concatenated instead of producing an
error. This works within the same file (paralleling the behavior
"plain" sections has in previous versions), but also across object
files. To declare an section fragment, add a
When RGBASM merges two fragments, the one encountered later is appended to the one encountered earlier. When RGBLINK merges two fragments, the one whose file was
specified last is appended to the one whose file was specified first. For
example, assuming ‘ rgblink -o rom.gb baz.o foo.o
bar.o baz.o ’
first, followed by the one from ‘foo.o ’,
and the one from ‘bar.o ’ last.
SYMBOLSRGBDS supports several types of symbols:
Symbol names can contain ASCII letters, numbers, underscores
‘_’, hashes ‘#’, dollar signs ‘$’,
and at signs ‘@’. However, they must begin with either a
letter or an underscore. Additionally, label names can contain up to a
single dot ‘ A symbol cannot have the same name as a reserved keyword, unless
its name is a “raw identifier” prefixed by a hash
‘#’. For example,
‘ LabelsOne of the assembler's main tasks is to keep track of addresses for you, so you can work with meaningful names instead of “magic” numbers. Labels enable just that: a label ties a name to a specific location within a section. A label resolves to a bank and address, determined at the same time as its parent section's (see further in this section). A label is defined by writing its name at the beginning of a line,
followed by one or two colons, without any whitespace between the label name
and the colon(s). Declaring a label (global or local) with two colons
‘ A label is said to be
local if its name
contains a dot ‘ For convenience, local labels can use a shorthand syntax: when a symbol name starting with a dot is found (for example, inside an expression, or when declaring a label), then the current “label scope” is implicitly prepended. Defining a global label sets it as the current “label scope”, until the next global label definition, or the end of the current section. Here are some examples of label definitions: GlobalLabel: AnotherGlobal: .locallabel ; This defines "AnotherGlobal.locallabel" .another_local: AnotherGlobal.with_another_local: ThisWillBeExported:: ; Note the two colons ThisWillBeExported.too:: In a numeric expression, a label evaluates to its address in
memory. (To obtain its bank, use the
‘ SECTION "Player tiles", VRAM vPlayerTiles: ds 6 * 16 .end A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants. However, if the section in which the label is defined has a fixed base address, its value is known at assembly time. Also, while Anonymous labelsAnonymous labels are useful for short blocks of code. They are defined like normal labels, but without a name before the colon. Anonymous labels are independent of label scoping, so defining one does not change the scoped label, and referencing one is not affected by the current scoped label. Anonymous labels are referenced using a colon
‘ ld hl, :++ : ld a, [hli] ; referenced by "jr nz" ldh [c], a dec c jr nz, :- ret : ; referenced by "ld hl" dw $7FFF, $1061, $03E0, $58A5 VariablesAn equal sign ‘=’ is used to define mutable numeric symbols. Unlike the other symbols described below, variables can be redefined. This is useful for internal symbols in macros, for counters, etc. DEF ARRAY_SIZE EQU 4 DEF COUNT = 2 DEF COUNT = 3 DEF COUNT = ARRAY_SIZE + COUNT DEF COUNT *= 2 ; COUNT now has the value 14 Note that colons ‘ Variables can be conveniently redefined by compound assignment operators like in C:
Examples: DEF x = 10 DEF x += 1 ; x == 11 DEF y = x - 1 ; y == 10 DEF y *= 2 ; y == 20 DEF y >>= 1 ; y == 10 DEF x ^= y ; x == 1 Declaring a variable with Numeric constants
def SCREEN_WIDTH equ 160 ; In pixels def SCREEN_HEIGHT equ 144 Note that colons ‘ If you
really need to,
the def NUM_ITEMS equ 0 MACRO add_item redef NUM_ITEMS equ NUM_ITEMS + 1 def ITEM_{02x:NUM_ITEMS} equ \1 ENDM add_item 1 add_item 4 add_item 9 add_item 16 assert NUM_ITEMS == 4 assert ITEM_04 == 16 Declaring a numeric constant with Offset constantsThe RS group of commands is a handy way of defining structure offsets: RSRESET DEF str_pStuff RW 1 DEF str_tData RB 256 DEF str_bCount RB 1 DEF str_SIZEOF RB 0 The example defines four constants as if by: DEF str_pStuff EQU 0 DEF str_tData EQU 2 DEF str_bCount EQU 258 DEF str_SIZEOF EQU 259 There are five commands in the RS group of commands:
If the constexpr argument to
Note that colons ‘ Declaring an offset constant with String constants
DEF COUNTREG EQUS "[hl+]" ld a, COUNTREG DEF PLAYER_NAME EQUS "\"John\"" db PLAYER_NAME This will be interpreted as: ld a, [hl+] db "John" String constants can also be used to define small one-line macros: DEF pusha EQUS "push af\npush bc\npush de\npush hl\n" Note that colons ‘ String constants, like numeric constants, cannot be redefined.
However, the DEF s EQUS "Hello, " REDEF s EQUS "{s}world!" ; prints "Hello, world!" PRINTLN "{s}\n" String constants can't be exported or imported. Important note: When a string constant is
expanded, its expansion may contain another string constant, which will be
expanded as well, and may be recursive. If this creates an infinite loop,
MacrosOne of the best features of an assembler is the ability to write
macros for it. Macros can be called with arguments, and can react depending
on input using MACRO my_macro ld a, 80 call MyFunc ENDM The example above defines
‘ Macros can't be exported or imported. Nesting macro definitions is not possible, so this won't work: MACRO outer MACRO inner PRINTLN "Hello!" ENDM ; this actually ends the 'outer' macro... ENDM ; ...and then this is a syntax error! But you can work around this limitation using
MACRO outer DEF definition EQUS "MACRO inner\nPRINTLN \"Hello!\"\nENDM" definition PURGE definition ENDM More about how to define and invoke macros is described in THE MACRO LANGUAGE below. Exporting and importing symbolsImporting and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file. Exporting of symbols has to be done manually, importing is done
automatically if The following will cause symbol1, symbol2 and so on to be accessible to other files during the link process:
For example, if you have the following three files: ‘ SECTION "a", WRAM0 LabelA: ‘ SECTION "b", WRAM0 ExportedLabelB1:: ExportedLabelB2: EXPORT ExportedLabelB2 ‘ SECTION "C", ROM0[0] dw LabelA dw ExportedLabelB1 dw ExportedLabelB2 Then ‘ $ rgbasm -o a.o a.asm $ rgbasm -o b.o b.asm $ rgbasm -o c.o c.asm $ rgblink a.o b.o c.o error: c.asm(2): Unknown symbol "LabelA" Linking failed with 1 error Note also that only exported symbols will appear in symbol and map files produced by rgblink(1). Purging symbols
DEF Kamikaze EQUS "I don't want to live anymore" AOLer: DB "Me too lol" PURGE Kamikaze, AOLer ASSERT !DEF(Kamikaze) && !DEF(AOLer) String constants are not expanded within the symbol names. Predeclared symbolsThe following symbols are defined by the assembler:
The current time values will be taken from the
DEFINING DATADefining constant data in ROM
DB 1,2,3,4,"This is a string" Alternatively, you can use Strings are handled a little specially: they first undergo charmap conversion (see Character maps), then each resulting character is output individually. For example, under the default charmap, the following two lines are identical: DW "Hello!" DW "H", "e", "l", "l", "o", "!" If you do not want this special handling, enclose the string in parentheses.
; outputs 3 bytes: $AA, $AA, $AA DS 3, $AA ; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB DS 7, $BB, $CC You can also use Including binary data filesYou probably have some graphics, level data, etc. you'd like to
include. Use INCBIN "titlepic.bin" INCBIN "sprites/hero.bin" You can also include only part of a file with
INCBIN "data.bin", 78, 256 The length argument is optional. If only the start position is specified, the bytes from the start position until the end of the file will be included. Statically allocating space in RAM
DS 42 ; Allocates 42 bytes Empty space in RAM sections will not be initialized. In ROM
sections, it will be filled with the value passed to the
Instead of an exact number of bytes, you can specify
Allocating overlapping spaces in RAMUnions allow multiple static memory allocations to overlap, like unions in C. This does not increase the amount of memory available, but allows re-using the same memory region for different purposes. A union starts with a ; Let's say PC == $C0DE here UNION ; Here, PC == $C0DE wName:: ds 10 ; Now, PC == $C0E8 wNickname:: ds 10 ; PC == $C0F2 NEXTU ; PC is back to $C0DE wHealth:: dw ; PC == $C0E0 wLives:: db ; PC == $C0E1 ds 7 ; PC == $C0E8 wBonus:: db ; PC == $C0E9 NEXTU ; PC is back to $C0DE again wVideoBuffer: ds 16 ; PC == $C0EE ENDU ; Afterward, PC == $C0F2 In the example above, ‘wName, wHealth’, and
‘wVideoBuffer’ all have the same value; so do
‘wNickname’ and ‘wBonus’. Thus, keep in mind
that ‘ This whole union's total size is 20 bytes, the size of the largest block (the first one, containing ‘wName’ and ‘wNickname’). Unions may be nested, with each inner union's size being determined as above, and affecting its outer union like any other allocation. Unions may be used in any section, but they may only contain
space-allocating directives like Requesting alignmentWhile If the constraint cannot be met (for example because the section
is fixed at an incompatible address), an error is produced. Note that
There may be times when you don't just want to specify an
alignment constraint at the current location, but also skip ahead until the
constraint can be satisfied. In that case, you can use If the constraint cannot be met by skipping any amount of space,
an error is produced. Note that
THE MACRO LANGUAGEInvoking macrosA macro is invoked by using its name at the beginning of a line, like a directive, followed by any comma-separated arguments. add a, b ld sp, hl my_macro ; This will be expanded sub a, 87 my_macro 42 ; So will this ret c my_macro 1, 2 ; And this After Important note: When a macro body is expanded,
its expansion may contain another macro invocation, which will be expanded
as well, and may be recursive. If this creates an infinite loop,
It's possible to pass arguments to macros as well! MACRO lb ld \1, (\2) << 8 | (\3) ENDM lb hl, 20, 18 ; Expands to "ld hl, ((20) << 8) | (18)" lb de, 3 + 1, NUM**2 ; Expands to "ld de, ((3 + 1) << 8) | (NUM**2)" You expand the arguments inside the macro body by using the escape
sequences This bracketed syntax supports decimal numbers and numeric
symbols, where negative values count from the last argument. For example,
‘ Other macro arguments and symbol interpolations will also be
expanded inside the angle brackets. For example, if
‘ Macro arguments are passed as string constants, although there's no need to enclose them in quotes. Thus, arguments are not evaluated as expressions, but instead are expanded directly inside the macro body. This means that they support all the escape sequences of strings (see String expressions above), as well as some of their own:
Line continuations work as usual inside macros or lists of macro arguments. However, some characters need to be escaped, as in the following example: MACRO PrintMacro1 PRINTLN STRCAT(\1) ENDM PrintMacro1 "Hello "\, \ "world" MACRO PrintMacro2 PRINT \1 ENDM PrintMacro2 STRCAT("Hello ", \ "world\n") The comma in ‘ Since macro arguments are expanded directly, it's often a good idea to put parentheses around them if they're meant as part of a numeric expression. For instance, consider the following: MACRO print_double PRINTLN \1 * 3 ENDM print_double 1 + 2 The body will expand to ‘ The
There are some escape sequences which are only valid inside the body of a macro:
The MACRO loop_c_times xor a, a .loop ld [hl+], a dec c jr nz, .loop ENDM If you use this macro more than once in the same label scope, it
will define ‘ MACRO loop_c_times_fixed xor a, a .loop\@ ld [hl+], a dec c jr nz, .loop\@ ENDM This will expand to a different value in each invocation, similar
to
Automatically repeating blocks of codeSuppose you want to unroll a time-consuming loop without
copy-pasting it. REPT 4 add a, c ENDR You can also use ; Generate a table of square values from 0**2 = 0 to 100**2 = 10000 DEF x = 0 REPT 101 dw x * x DEF x += 1 ENDR As in macros, you can also use the escape sequence
A common pattern is to repeat a block for each value in some
range. FOR N, 256 dw N * N ENDR It acts just as if you had done: DEF N = 0 dw N * N DEF N = 1 dw N * N DEF N = 2 dw N * N ; ... DEF N = 255 dw N * N DEF N = 256 You can customize the range of
The FOR V, 4, 25, 5 PRINT "{d:V} " DEF V *= 2 ENDR PRINTLN "done {d:V}" This will print: 4 9 14 19 24 done 29 Just like with You can stop a repeating block with the
FOR V, 1, 100 PRINT "{d:V}" IF V == 5 PRINT " stop! " BREAK ENDC PRINT ", " ENDR PRINTLN "done {d:V}" This will print: 1, 2, 3, 4, 5 stop! done 5 Conditionally assembling blocks of codeThe four commands IF NUM < 0 PRINTLN "NUM < 0" ELIF NUM == 0 PRINTLN "NUM == 0" ELSE PRINTLN "NUM > 0" ENDC The Note that if an Including other source filesUse INCLUDE "irq.inc" You may also implicitly Printing things during assemblyThe PRINT "Hello world!\n" PRINTLN "Hello world!" PRINT _NARG, " arguments\n" PRINTLN "sum: ", 2+3, " product: ", 2*3 PRINTLN STRFMT("E = %f", 2.718) Aborting the assembly process
If you need to ensure some assumption is correct when compiling,
you can use Function: xor a ASSERT LOW(MyByte) == 0 ld h, HIGH(MyByte) ld l, a ld a, [hli] ; You can also indent this! ASSERT BANK(OtherFunction) == BANK(Function) call OtherFunction ; Lowercase also works ld hl, FirstByte ld a, [hli] assert FirstByte + 1 == SecondByte ld b, [hl] ret .end ; If you specify one, a message will be printed STATIC_ASSERT .end - Function < 256, "Function is too large!" First, the difference between Second, as shown above, a string can be optionally added at the end, to give insight into what the assertion is checking. Finally, you can add one of MISCELLANEOUSChanging options while assembling
PUSHO OPT g.oOX, Wdiv ; acts like command-line -g.oOX -Wdiv DW `..ooOOXX ; uses the graphics constant characters from OPT g PRINTLN $80000000/-1 ; prints a warning about division POPO DW `00112233 ; uses the default graphics constant characters PRINTLN $80000000/-1 ; no warning by default
PUSHO b.X, g.oOX DB %..XXXX.. DW `..ooOOXX POPO SEE ALSOrgbasm(1), rgblink(1), rgblink(5), rgbfix(1), rgbgfx(1), gbz80(7), rgbasm-old(5), rgbds(5), rgbds(7) HISTORYrgbasm(1) was originally written by Carsten Sørensen as part of the ASMotor package, and was later repackaged in RGBDS by Justin Lloyd. It is now maintained by a number of contributors at https://github.com/gbdev/rgbds.
|