mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-22 11:12:07 +00:00
1219 lines
33 KiB
Groff
1219 lines
33 KiB
Groff
.\"
|
||
.\" This file is part of RGBDS.
|
||
.\"
|
||
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||
.\"
|
||
.\" SPDX-License-Identifier: MIT
|
||
.\"
|
||
.Dd March 13, 2018
|
||
.Dt RGBASM 5
|
||
.Os RGBDS Manual
|
||
.Sh NAME
|
||
.Nm rgbasm
|
||
.Nd language documentation
|
||
.Sh DESCRIPTION
|
||
This is the full description of the language used by
|
||
.Xr rgbasm 1 .
|
||
The description of the instructions supported by the GameBoy CPU is in
|
||
.Xr gbz80 7 .
|
||
.Pp
|
||
.Sh GENERAL
|
||
.Ss Syntax
|
||
The syntax is line‐based, just as in any other assembler, meaning that you do
|
||
one instruction or pseudo‐op per line:
|
||
.Pp
|
||
.Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar \&;comment Oc
|
||
.Pp
|
||
Example:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
John: ld a,87 ;Weee
|
||
.Ed
|
||
.Pp
|
||
All pseudo‐ops, mnemonics and registers (reserved keywords) are case‐insensitive
|
||
and all labels are case‐sensitive.
|
||
.Pp
|
||
There are two syntaxes for comments.
|
||
In both cases, a comment ends at the end of the line.
|
||
The most common one is: anything that follows a semicolon
|
||
.Ql \&;
|
||
(that isn't inside a string) is a comment.
|
||
There is another format: anything that follows a
|
||
.Ql *
|
||
that is placed right at the start of
|
||
a line is a comment.
|
||
The assembler removes all comments from the code before doing anything else.
|
||
.Pp
|
||
Sometimes lines can be too long and it may be necessary to split them.
|
||
The syntax to do so is the following one:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
DB 1, 2, 3, 4 \[rs]
|
||
5, 6, 7, 8
|
||
.Ed
|
||
.Pp
|
||
This works anywhere in the code except inside of strings.
|
||
To split strings it is needed to use
|
||
.Fn STRCAT
|
||
like this:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
DB STRCAT("Hello ", \[rs]
|
||
"world!")
|
||
.Ed
|
||
.Pp
|
||
.Ss Sections
|
||
.Ic SECTION Ar name , type
|
||
.Pp
|
||
.Ic SECTION Ar name , type , options
|
||
.Pp
|
||
.Ic SECTION Ar name , type Ns Bo Ar addr Bc
|
||
.Pp
|
||
.Ic SECTION Ar name , type Ns Bo Ar addr Bc , Ar options
|
||
.Pp
|
||
Before 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.
|
||
.Pp
|
||
.Ar name
|
||
is a string enclosed in double quotes and can be a new name or the name of an
|
||
existing section.
|
||
All sections assembled at the same time that have the same name and type are
|
||
considered to be the same section, and their code is put together in the object
|
||
file generated by the assembler.
|
||
All other sections must have a unique name, even in different source files, or
|
||
the linker will treat it as an error.
|
||
.Pp
|
||
Possible section
|
||
.Ar type Ns s
|
||
are as follows:
|
||
.Pp
|
||
.Bl -tag
|
||
.It Cm ROM0
|
||
A ROM section.
|
||
.Ar addr
|
||
can range from $0000–$3FFF (or $0000–$7FFF if tiny ROM mode is enabled in
|
||
.Xr rgblink 1 ) .
|
||
.It Cm ROMX
|
||
A banked ROM section.
|
||
.Ar addr
|
||
can range from $4000–$7FFF.
|
||
.Ar bank
|
||
can range from 1 to 511.
|
||
Not available if tiny ROM mode is enabled in
|
||
.Xr rgblink 1 .
|
||
.It Cm VRAM
|
||
A banked video RAM section.
|
||
.Ar addr
|
||
can range from $8000–$9FFF.
|
||
.Ar bank
|
||
can be 0 or 1 but bank 1 is unavailable if DMG mode is enabled in
|
||
.Xr rgblink 1 .
|
||
Memory in this section can only be allocated with
|
||
.Sy DS ,
|
||
not filled with data.
|
||
.It Cm SRAM
|
||
A banked external (save) RAM section.
|
||
.Ar addr
|
||
can range from $A000–$BFFF.
|
||
.Ar bank
|
||
can range from 0 to 15.
|
||
Memory in this section can only be allocated with
|
||
.Sy DS ,
|
||
not filled with data.
|
||
.It Cm WRAM0
|
||
A general-purpose RAM section.
|
||
.Ar addr
|
||
can range from $C000–$CFFF, or $C000–$DFFF if DMG mode is enabled in
|
||
.Xr rgblink 1 .
|
||
Memory in this section can only be allocated with
|
||
.Sy DS ,
|
||
not filled with data.
|
||
.It Cm WRAMX
|
||
A banked general-purpose RAM section.
|
||
.Ar addr
|
||
can range from $D000–$DFFF.
|
||
.Ar bank
|
||
can range from 1 to 7.
|
||
Memory in this section can only be allocated with
|
||
.Sy DS ,
|
||
not filled with data.
|
||
Not available if DMG mode is enabled in
|
||
.Xr rgblink 1 .
|
||
.It Cm OAM
|
||
An object attributes RAM section.
|
||
.Ar addr
|
||
can range from $FE00-$FE9F.
|
||
Memory in this section can only be allocated with
|
||
.Sy DS ,
|
||
not filled with data.
|
||
.It Cm HRAM
|
||
A high RAM section.
|
||
.Ar addr
|
||
can range from $FF80–$FFFE.
|
||
Memory in this section can only be allocated with
|
||
.Sy DS ,
|
||
not filled with data.
|
||
.Pp
|
||
.Sy Note :
|
||
If you use this method of allocating HRAM the assembler will
|
||
.Em not
|
||
choose the short addressing mode in the LD instructions
|
||
.Sy LD [$FF00+n8],A
|
||
and
|
||
.Sy LD A,[$FF00+n8]
|
||
because the actual address calculation is done by the linker.
|
||
If you find this undesirable you can use
|
||
.Ic RSSET , RB ,
|
||
or
|
||
.Ic RW
|
||
instead or use the
|
||
.Sy LDH [$FF00+n8],A
|
||
and
|
||
.Sy LDH A,[$FF00+n8]
|
||
syntax instead.
|
||
This forces the assembler to emit the correct instruction and the linker to
|
||
check if the value is in the correct range.
|
||
This optimization can be disabled by passing the
|
||
.Fl L
|
||
flag to
|
||
.Xr rgbasm 1 .
|
||
.El
|
||
.Pp
|
||
.Ar option Ns s are comma separated and may include:
|
||
.Bl -tag
|
||
.It Cm BANK Ns Bq Ar bank
|
||
Specify which
|
||
.Ar bank
|
||
for the linker to place the section.
|
||
.It Cm ALIGN Ns Bq Ar align
|
||
Place the section at an address whose
|
||
.Ar align
|
||
least‐significant bits are zero.
|
||
It is a syntax error to use this option with
|
||
.Ar addr .
|
||
.El
|
||
.Pp
|
||
If
|
||
.Bq Ar addr
|
||
is not specified, the section is considered
|
||
.Dq floating ;
|
||
the linker will automatically calculate an appropriate address for the section.
|
||
Similarly, if
|
||
.Cm BANK Ns Bq Ar bank
|
||
is not specified, the linker will automatically find a bank with enough space.
|
||
.Pp
|
||
Sections can also be placed by using a linkerscript file.
|
||
The format is described in
|
||
.Xr 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.
|
||
.Pp
|
||
Section examples:
|
||
.Bd -literal -offset indent
|
||
SECTION "CoolStuff",ROMX
|
||
.Ed
|
||
.Pp
|
||
This switches to the section called
|
||
.Dq CoolStuff
|
||
(or creates it if it doesn't already exist) and defines it as a code section.
|
||
.Pp
|
||
The following example defines a section that can be placed anywhere in any ROMX
|
||
bank:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
SECTION "CoolStuff",ROMX
|
||
.Ed
|
||
.Pp
|
||
If it is needed, the the base address of the section can be specified:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
SECTION "CoolStuff",ROMX[$4567]
|
||
.Ed
|
||
.Pp
|
||
An example with a fixed bank:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
SECTION "CoolStuff",ROMX[$4567],BANK[3]
|
||
.Ed
|
||
.Pp
|
||
And if you only want to force the section into a certain bank, and not it's
|
||
position within the bank, that's also possible:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
SECTION "CoolStuff",ROMX,BANK[7]
|
||
.Ed
|
||
.Pp
|
||
Alignment examples:
|
||
one use could be when using DMA to copy data or when it is needed to align the
|
||
start of an array to 256 bytes to optimize the code that accesses it.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
SECTION "OAM Data",WRAM0,ALIGN[8] ; align to 256 bytes
|
||
|
||
SECTION "VRAM Data",ROMX,BANK[2],ALIGN[4] ; align to 16 bytes
|
||
.Ed
|
||
.Pp
|
||
.Sy Hint :
|
||
If you think this is a lot of typing for doing a simple
|
||
.Dq org
|
||
type thing you can quite easily write an intelligent macro (called
|
||
.Ic ORG
|
||
for example) that uses
|
||
.Ic @
|
||
for the section name and determines
|
||
correct section type etc as arguments for
|
||
.Ic SECTION .
|
||
.Ss Section Stack
|
||
.Ic POPS
|
||
and
|
||
.Ic PUSHS
|
||
provide the interface to the section stack.
|
||
.Pp
|
||
.Ic PUSHS
|
||
will push the current section context on the section stack.
|
||
.Ic POPS
|
||
can then later be used to restore it.
|
||
Useful for defining sections in included files when you don't want to destroy
|
||
the section context for the program that included your file.
|
||
The number of entries in the stack is limited only by the amount of memory in
|
||
your machine.
|
||
.Sh SYMBOLS
|
||
.Pp
|
||
.Ss Symbols
|
||
RGBDS supports several types of symbols:
|
||
.Pp
|
||
.Bl -hang
|
||
.It Sy Label
|
||
Used to assign a memory location with a name
|
||
.It Sy EQUate
|
||
Give a constant a name.
|
||
.It Sy SET
|
||
Almost the same as EQUate, but you can change the value of a SET during
|
||
assembling.
|
||
.It Sy Structure Po Sy the RS group Pc
|
||
Define a structure easily.
|
||
.It Sy String equate Pq Sy EQUS
|
||
Give a frequently used string a name.
|
||
Can also be used as a mini-macro, like
|
||
.Fd #define
|
||
in C.
|
||
.It Sy MACRO
|
||
A block of code or pseudo instructions that you invoke like any other mnemonic.
|
||
You can give them arguments too.
|
||
.El
|
||
.Pp
|
||
A symbol cannot have the same name as a reserved keyword.
|
||
.Bl -hang
|
||
.It Sy Label
|
||
.Pp
|
||
One of the assembler's main tasks is to keep track of addresses for you so you
|
||
don't have to remember obscure numbers but can make do with a meaningful name, a
|
||
label.
|
||
.Pp
|
||
This can be done in a number of ways:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
GlobalLabel
|
||
AnotherGlobal:
|
||
\&.locallabel
|
||
\&.yet_a_local:
|
||
AnotherGlobal.with_another_local:
|
||
ThisWillBeExported:: ;note the two colons
|
||
ThisWillBeExported.too::
|
||
.Ed
|
||
.Pp
|
||
In the line where a label is defined there musn't be any whitespace before it.
|
||
Local labels are only accessible within the scope they are defined.
|
||
A scope starts after a global label and ends at the next global label.
|
||
Declaring a label (global or local) with :: does an EXPORT at the same time.
|
||
Local labels can be declared as scope.local or simply as as .local.
|
||
If the former notation is used, the scope must be the actual current scope.
|
||
.Pp
|
||
Labels will normally change their value during the link process and are thus not
|
||
constant.
|
||
The exception is the case in which the base address of a section is fixed, so
|
||
the address of the label is known at assembly time.
|
||
.Pp
|
||
The subtraction of two labels is only constant (known at assembly time) if they
|
||
are two local labels that belong to the same scope, or they are two global
|
||
labels that belong to sections with fixed base addresses.
|
||
.Pp
|
||
.It Sy EQU
|
||
.Pp
|
||
EQUates are constant symbols.
|
||
They can, for example, be used for things such as bit-definitions of hardware
|
||
registers.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
EXIT_OK EQU $00
|
||
EXIT_FAILURE EQU $01
|
||
.Ed
|
||
.Pp
|
||
Note that a colon (:) following the label-name is not allowed.
|
||
EQUates cannot be exported and imported.
|
||
They don't change their value during the link process.
|
||
.It Sy SET
|
||
.Pp
|
||
SETs are similar to EQUates.
|
||
They are also constant symbols in the sense that their values are defined during
|
||
the assembly process.
|
||
These symbols are normally used in macros.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
ARRAY_SIZE EQU 4
|
||
COUNT SET 2
|
||
COUNT SET ARRAY_SIZE+COUNT
|
||
.Ed
|
||
.Pp
|
||
Note that a colon (:) following the label-name is not allowed.
|
||
SETs cannot be exported and imported.
|
||
Alternatively you can use = as a synonym for SET.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
COUNT = 2
|
||
.Ed
|
||
.Pp
|
||
.It Sy RSSET , RSRESET , RB , RW
|
||
.Pp
|
||
The RS group of commands is a handy way of defining structures:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
RSRESET
|
||
str_pStuff RW 1
|
||
str_tData RB 256
|
||
str_bCount RB 1
|
||
str_SIZEOF RB 0
|
||
.Ed
|
||
.Pp
|
||
The example defines four equated symbols:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
str_pStuff = 0
|
||
str_tData = 2
|
||
str_bCount = 258
|
||
str_SIZEOF = 259
|
||
.Ed
|
||
.Pp
|
||
There are four commands in the RS group of commands:
|
||
.Pp
|
||
.Bl -column "RSSET constexpr"
|
||
.It Sy Command Ta Sy Meaning
|
||
.It Ic RSRESET Ta Resets the _RS counter to zero.
|
||
.It Ic RSSET Ar constexpr Ta Sets the
|
||
.Ic _RS No counter to Ar constexpr .
|
||
.It Ic RB Ar constexpr Ta Sets the preceding symbol to
|
||
.Ic _RS No and adds Ar constexpr No to Ic _RS .
|
||
.It Ic RW Ar constexpr Ta Sets the preceding symbol to
|
||
.Ic _RS No and adds Ar constexpr No * 2 to Ic _RS.
|
||
.It Ic RL Ar constexpr Ta Sets the preceding symbol to
|
||
.Ic _RS No and adds Ar constexpr No * 4 to Ic _RS.
|
||
.El
|
||
.Pp
|
||
Note that a colon (:) following the symbol-name is not allowed.
|
||
.Sy RS
|
||
symbols cannot be exported and imported.
|
||
They don't change their value during the link process.
|
||
.Pp
|
||
.It Sy EQUS
|
||
.Pp
|
||
EQUS is used to define string-symbols.
|
||
Wherever the assembler meets a string symbol its name is replaced with its
|
||
value.
|
||
If you are familiar with C you can think of it as the same as
|
||
.Fd #define .
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
COUNTREG EQUS "[hl+]"
|
||
ld a,COUNTREG
|
||
|
||
PLAYER_NAME EQUS "\[rs]"John\[rs]""
|
||
db PLAYER_NAME
|
||
.Ed
|
||
.Pp
|
||
Note that : following the label-name is not allowed, and that strings must be
|
||
quoted to be useful.
|
||
.Pp
|
||
This will be interpreted as:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
ld a,[hl+]
|
||
db "John"
|
||
.Ed
|
||
.Pp
|
||
String-symbols can also be used to define small one-line macros:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
PUSHA EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n"
|
||
.Ed
|
||
.Pp
|
||
Note that a colon (:) following the label-name is not allowed.
|
||
String equates can't be exported or imported.
|
||
.Pp
|
||
.Sy Important note :
|
||
An EQUS can be expanded to a string that contains another EQUS
|
||
and it will be expanded as well.
|
||
This means that, if you aren't careful, you may trap the assembler into an
|
||
infinite loop if there's a circular dependency in the expansions.
|
||
Also, a MACRO can have inside an EQUS which references the same MACRO, which has
|
||
the same problem.
|
||
.Pp
|
||
.It Sy MACRO
|
||
.Pp
|
||
One of the best features of an assembler is the ability to write macros for it.
|
||
Macros also provide a method of passing arguments to them and they can then
|
||
react to the input using IF-constructs.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
MyMacro: MACRO
|
||
ld a,80
|
||
call MyFunc
|
||
ENDM
|
||
.Ed
|
||
.Pp
|
||
Note that a colon (:) following the macro-name is required.
|
||
Macros can't be exported or imported.
|
||
It's valid to call a macro from a macro (yes, even the same one).
|
||
.Pp
|
||
The above example is a very simple macro.
|
||
You execute the macro by typing its name.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
add a,b
|
||
ld sp,hl
|
||
MyMacro ;This will be expanded
|
||
sub a,87
|
||
.Ed
|
||
.Pp
|
||
When the assembler meets MyMacro it will insert the macrodefinition (the text
|
||
enclosed in
|
||
.Ic MACRO
|
||
/
|
||
.Ic ENDM ) .
|
||
.Pp
|
||
Suppose your macro contains a loop.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
LoopyMacro: MACRO
|
||
xor a,a
|
||
\&.loop ld [hl+],a
|
||
dec c
|
||
jr nz,.loop
|
||
ENDM
|
||
.Ed
|
||
.Pp
|
||
This is fine.
|
||
That is, if you only use the macro once per scope.
|
||
To get around this problem there is a special label string equate called
|
||
.Ic \[rs]@
|
||
that you can append to your labels and it will then expand to a unique string.
|
||
.Pp
|
||
.Ic \[rs]@
|
||
also works in REPT-blocks should you have any loops there.
|
||
.Bd -literal -offset indent
|
||
LoopyMacro: MACRO
|
||
xor a,a
|
||
\&.loop\[rs]@ ld [hl+],a
|
||
dec c
|
||
jr nz,.loop\[rs]@
|
||
ENDM
|
||
.Ed
|
||
.Pp
|
||
.Sy Important note :
|
||
Since a MACRO can call itself (or a different MACRO that calls the first one)
|
||
there can be problems of circular dependency.
|
||
They trap the assembler in an infinite loop, so you have to be careful when
|
||
using recursion with MACROs.
|
||
Also, a MACRO can have inside an EQUS which references the same MACRO, which has
|
||
the same problem.
|
||
.Pp
|
||
.Sy Macro Arguments
|
||
.Pp
|
||
I'd like LoopyMacro a lot better if I didn't have to pre-load the registers
|
||
with values and then call it.
|
||
What I'd like is the ability to pass it arguments and it then loaded the
|
||
registers itself.
|
||
.Pp
|
||
And I can do that.
|
||
In macros you can get the arguments by using the special macro string equates
|
||
.Ic \[rs]1
|
||
through
|
||
.Ic \[rs]9 ,
|
||
.Ic \[rs]1
|
||
being the first argument
|
||
specified on the calling of the macro.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
LoopyMacro: MACRO
|
||
ld hl,\[rs]1
|
||
ld c,\[rs]2
|
||
xor a,a
|
||
\&.loop\[rs]@ ld [hl+],a
|
||
dec c
|
||
jr nz,.loop\[rs]@
|
||
ENDM
|
||
.Ed
|
||
.Pp
|
||
Now I can call the macro specifying two arguments.
|
||
The first being the address and the second being a bytecount.
|
||
The macro will then reset all bytes in this range.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
LoopyMacro MyVars,54
|
||
.Ed
|
||
.Pp
|
||
Arguments are passed as string equates.
|
||
There's no need to enclose them in quotes.
|
||
An expression will not be evaluated first but passed directly.
|
||
This means that it's probably a very good idea to use brackets around
|
||
.Ic \[rs]1
|
||
to
|
||
.Ic \[rs]9
|
||
if you perform further calculations on them.
|
||
For instance, if you pass 1 + 2 as the first argument and then do
|
||
.Ic PRINTV
|
||
.Ic \[rs]1
|
||
* 2
|
||
you will get the value 5 on screen and not 6 as you might have expected.
|
||
.Pp
|
||
In reality, up to 256 arguments can be passed to a macro, but you can only use
|
||
the first 9 like this.
|
||
If you want to use the rest, you need to use the keyword
|
||
.Ic SHIFT .
|
||
.Pp
|
||
Line continuations work as usual inside macros or lists of arguments of macros.
|
||
Strings, however, are a bit trickier.
|
||
The following example shows how to use strings as arguments for a macro:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
PrintMacro : MACRO
|
||
PRINTT \[rs]1
|
||
ENDM
|
||
|
||
PrintMacro STRCAT(\[rs]"Hello\[rs]"\[rs], \[rs]
|
||
\[rs]" world\[rs]\[rs]n\[rs]")
|
||
.Ed
|
||
.Pp
|
||
.Ic SHIFT
|
||
is a special command only available in macros.
|
||
Very useful in REPT-blocks.
|
||
It will shift the arguments by one to the left.
|
||
.Ic \[rs]1
|
||
will get the value of
|
||
.Ic \[rs]2 ,
|
||
.Ic \[rs]2
|
||
will get the value in
|
||
.Ic \[rs]3
|
||
and so forth.
|
||
.Pp
|
||
This is the only way of accessing the value of arguments from 10 to 256.
|
||
.Pp
|
||
.El
|
||
.Ss Exporting and importing symbols
|
||
Importing 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.
|
||
.Pp
|
||
Exporting of symbols has to be done manually, importing is done automatically
|
||
if the assembler doesn't know where a symbol is defined.
|
||
.Pp
|
||
.Ic EXPORT Ar label Bq , Ar label No , ...
|
||
.Pp
|
||
The assembler will make label accessible to other files during the link process.
|
||
.Pp
|
||
.Ic GLOBAL Ar label Bq , Ar label No , ...
|
||
.Pp
|
||
If label is defined during the assembly it will be exported, if not, it will be
|
||
imported.
|
||
Handy (very!) for include-files.
|
||
Note that, since importing is done automatically, this keyword has the same
|
||
effect as
|
||
.Ic EXPORT .
|
||
.Ss Purging symbols
|
||
.Ic PURGE
|
||
allows you to completely remove a symbol from the symbol table as if it had
|
||
never existed.
|
||
USE WITH EXTREME CAUTION!!!
|
||
I can't stress this enough, you seriously need to know what you are doing.
|
||
DON'T purge symbol that you use in expressions the linker needs to calculate.
|
||
In fact, it's probably not even safe to purge anything other than string symbols
|
||
and macros.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
Kamikaze EQUS "I don't want to live anymore"
|
||
AOLer EQUS "Me too"
|
||
PURGE Kamikaze, AOLer
|
||
.Ed
|
||
.Pp
|
||
Note that string symbols that are part of a
|
||
.Ic PURGE
|
||
command WILL NOT BE EXPANDED as the ONLY exception to this rule.
|
||
.Ss Predeclared Symbols
|
||
The following symbols are defined by the assembler:
|
||
.Pp
|
||
.Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__"
|
||
.It Sy Type Ta Sy Name Ta Sy Contents
|
||
.It Ic EQU Ta Ic @ Ta PC value
|
||
.It Ic EQU Ta Ic _PI Ta Fixed point \[*p]
|
||
.It Ic SET Ta Ic _RS Ta _RS Counter
|
||
.It Ic EQU Ta Ic _NARG Ta Number of arguments passed to macro
|
||
.It Ic EQU Ta Ic __LINE__ Ta The current line number
|
||
.It Ic EQUS Ta Ic __FILE__ Ta The current filename
|
||
.It Ic EQUS Ta Ic __DATE__ Ta Today's date
|
||
.It Ic EQUS Ta Ic __TIME__ Ta The current time
|
||
.It Ic EQUS Ta Ic __ISO_8601_LOCAL__ Ta ISO 8601 timestamp (local)
|
||
.It Ic EQUS Ta Ic __ISO_8601_UTC__ Ta ISO 8601 timestamp (UTC)
|
||
.It Ic EQU Ta Ic __UTC_YEAR__ Ta Today's year
|
||
.It Ic EQU Ta Ic __UTC_MONTH__ Ta Today's month number, 1-12
|
||
.It Ic EQU Ta Ic __UTC_DAY__ Ta Today's day of the month, 1-31
|
||
.It Ic EQU Ta Ic __UTC_HOUR__ Ta Current hour, 0-23
|
||
.It Ic EQU Ta Ic __UTC_MINUTE__ Ta Current minute, 0-59
|
||
.It Ic EQU Ta Ic __UTC_SECOND__ Ta Current second, 0-59
|
||
.It Ic EQU Ta Ic __RGBDS_MAJOR__ Ta Major version number of RGBDS.
|
||
.It Ic EQU Ta Ic __RGBDS_MINOR__ Ta Minor version number of RGBDS.
|
||
.It Ic EQU Ta Ic __RGBDS_PATCH__ Ta Patch version number of RGBDS.
|
||
.El
|
||
.Pp
|
||
.Sh DEFINING DATA
|
||
.Ss Defining constant data
|
||
.Ic DB
|
||
defines a list of bytes that will be stored in the final image.
|
||
Ideal for tables and text (which is not zero-terminated).
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
DB 1,2,3,4,"This is a string"
|
||
.Ed
|
||
.Pp
|
||
Alternatively, you can use
|
||
.Ic DW
|
||
to store a list of words (16-bits) or
|
||
.Ic DL
|
||
to store a list of doublewords/longs (32-bits).
|
||
Strings are not allowed as arguments to
|
||
.Ic DW
|
||
and
|
||
.Ic DL .
|
||
.Pp
|
||
You can also use
|
||
.Ic DB ,
|
||
.Ic DW
|
||
and
|
||
.Ic DL
|
||
without arguments, or leaving empty elements at any point in the list.
|
||
This works exactly like
|
||
.Sy DS 1 ,
|
||
.Sy DS 2
|
||
and
|
||
.Sy DS 4
|
||
respectively.
|
||
Consequently,
|
||
.Ic DB ,
|
||
.Ic DW
|
||
and
|
||
.Ic DL
|
||
can be used in a
|
||
.Sy WRAM0 No / Sy WRAMX No / Sy HRAM No / Sy VRAM No / Sy SRAM
|
||
section.
|
||
.Ss Declaring variables in a RAM section
|
||
.Ic DS
|
||
allocates a number of bytes.
|
||
The content is undefined.
|
||
This is the preferred method of allocationg space in a RAM section.
|
||
You can, however, use
|
||
.Ic DB ,
|
||
.Ic DW
|
||
and
|
||
.Ic DL
|
||
without any arguments instead.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
DS str_SIZEOF ;allocate str_SIZEOF bytes
|
||
.Ed
|
||
.Pp
|
||
.Ss Including binary files
|
||
You probably have some graphics you'd like to include.
|
||
Use
|
||
.Ic INCBIN
|
||
to include a raw binary file as it is.
|
||
If the file isn't found in the current directory, the include-path list passed
|
||
to the linker on the command line will be searched.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
INCBIN "titlepic.bin"
|
||
INCBIN "sprites/hero.bin"\ ; UNIX
|
||
INCBIN "sprites\[rs]\[rs]hero.bin"\ ; Windows
|
||
.Ed
|
||
.Pp
|
||
You can also include only part of a file with
|
||
.Ic INCBIN .
|
||
The example below includes 256 bytes from data.bin starting from byte 78.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
INCBIN "data.bin",78,256
|
||
.Ed
|
||
.Ss Unions
|
||
Unions allow multiple memory allocations to share the same space in memory,
|
||
like unions in C.
|
||
This allows you to easily reuse memory for different purposes, depending on
|
||
the game's state.
|
||
.Pp
|
||
You create unions using the
|
||
.Ic UNION ,
|
||
.Ic NEXTU
|
||
and
|
||
.Ic ENDU
|
||
keywords.
|
||
.Ic NEXTU
|
||
lets you create a new block of allocations, and you may use it as many times
|
||
within a union as necessary.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
UNION
|
||
Name: ds 8
|
||
Nickname: ds 8
|
||
NEXTU
|
||
Health: dw
|
||
Something: ds 3
|
||
Lives: db
|
||
NEXTU
|
||
Temporary: ds 19
|
||
ENDU
|
||
.Ed
|
||
.Pp
|
||
This union will use up 19 bytes, as this is the size of the largest block
|
||
(the last one, containing 'Temporary').
|
||
Of course, as 'Name', 'Health', and 'Temporary' all point to the same memory
|
||
locations, writes to any one of these will affect values read from the others.
|
||
.Pp
|
||
Unions may be used in any section, but code and data may not be included.
|
||
.Sh THE MACRO LANGUAGE
|
||
.Pp
|
||
.Ss Printing things during assembly
|
||
These three instructions type text and values to stdout.
|
||
Useful for debugging macros or wherever you may feel the need to tell yourself
|
||
some important information.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
PRINTT "I'm the greatest programmer in the whole wide world\[rs]n"
|
||
PRINTI (2 + 3) / 5
|
||
PRINTV $FF00 + $F0
|
||
PRINTF MUL(3.14, 3987.0)
|
||
.Ed
|
||
.Pp
|
||
.Bl -inset
|
||
.It Ic PRINTT
|
||
prints out a string.
|
||
.It Ic PRINTV
|
||
prints out an integer value in hexadecimal or, as in the example, the result of
|
||
a calculation.
|
||
Unsurprisingly, you can also print out a constant symbols value.
|
||
.It Ic PRINTI
|
||
prints out a signed integer value.
|
||
.It Ic PRINTF
|
||
prints out a fixed point value.
|
||
.El
|
||
.Ss Automatically repeating blocks of code
|
||
Suppose you're feeling lazy and you want to unroll a time consuming loop.
|
||
.Ic REPT
|
||
is here for that purpose.
|
||
Everything between
|
||
.Ic REPT
|
||
and
|
||
.Ic ENDR
|
||
will be repeated a number of times just as if you done a copy/paste operation
|
||
yourself.
|
||
The following example will assemble
|
||
.Sy add a,c
|
||
four times:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
REPT 4
|
||
add a,c
|
||
ENDR
|
||
.Ed
|
||
.Pp
|
||
You can also use
|
||
.Ic REPT
|
||
to generate tables on the fly:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
; --
|
||
; -- Generate a 256 byte sine table with values between 0 and 128
|
||
; --
|
||
ANGLE SET 0.0
|
||
REPT 256
|
||
DB (MUL(64.0,SIN(ANGLE))+64.0)>>16
|
||
ANGLE SET ANGLE+256.0
|
||
ENDR
|
||
.Ed
|
||
.Pp
|
||
.Ic REPT
|
||
is also very useful in recursive macros and, as in macros, you can also use the
|
||
special label operator
|
||
.Ic \[rs]@ .
|
||
REPT-blocks can be nested.
|
||
.Ss Aborting the assembly process
|
||
.Ic FAIL
|
||
and
|
||
.Ic WARN
|
||
can be used to print errors and warnings respectively during the assembly
|
||
process.
|
||
This is especially useful for macros that get an invalid argument.
|
||
.Ic FAIL
|
||
and
|
||
.Ic WARN
|
||
take a string as the only argument and they will print this string out as a
|
||
normal error with a line number.
|
||
.Pp
|
||
.Ic FAIL
|
||
stops assembling immediately while
|
||
.Ic WARN
|
||
shows the message but continues afterwards.
|
||
.Ss Including other source files
|
||
Use
|
||
.Ic INCLUDE
|
||
to process another assembler-file and then return to the current file when done.
|
||
If the file isn't found in the current directory the include-path list will be
|
||
searched.
|
||
You may nest
|
||
.Ic INCLUDE
|
||
calls infinitely (or until you run out of memory, whichever comes first).
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
INCLUDE "irq.inc"
|
||
.Ed
|
||
.Pp
|
||
.Ss Conditional assembling
|
||
The four commands
|
||
.Ic IF ,
|
||
.Ic ELIF ,
|
||
.Ic ELSE ,
|
||
and
|
||
.Ic ENDC
|
||
are used to conditionally assemble parts of your file.
|
||
This is a powerful feature commonly used in macros.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
IF NUM < 0
|
||
PRINTT "NUM < 0\[rs]n"
|
||
ELIF NUM == 0
|
||
PRINTT "NUM == 0\[rs]n"
|
||
ELSE
|
||
PRINTT "NUM > 0\[rs]n"
|
||
ENDC
|
||
.Ed
|
||
.Pp
|
||
The
|
||
.Ic ELIF
|
||
and
|
||
.Ic ELSE
|
||
blocks are optional.
|
||
.Ic IF No / Ic ELIF No / Ic ELSE No / Ic ENDC
|
||
blocks can be nested.
|
||
.Pp
|
||
Note that if an
|
||
.Ic ELSE
|
||
block is found before an
|
||
.Ic ELIF
|
||
block, the
|
||
.Ic ELIF
|
||
block will be ignored.
|
||
All
|
||
.Ic ELIF
|
||
blocks must go before the
|
||
.Ic ELSE
|
||
block.
|
||
Also, if there is more than one
|
||
.Ic ELSE
|
||
block, all of them but the first one are ignored.
|
||
.Ss Integer and Boolean expressions
|
||
An expression can be composed of many things.
|
||
Expressions are always evaluated using signed 32-bit math.
|
||
.Pp
|
||
The most basic expression is just a single number.
|
||
.Pp
|
||
.Sy Numeric Formats
|
||
.Pp
|
||
There are a number of numeric formats.
|
||
.Pp
|
||
.Bl -dash -compact
|
||
.It
|
||
Hexadecimal: $0123456789ABCDEF.
|
||
Case-insensitive
|
||
.It
|
||
Decimal: 0123456789
|
||
.It
|
||
Octal: &01234567
|
||
.It
|
||
Binary: %01
|
||
.It
|
||
Fixedpoint (16.16): 01234.56789
|
||
.It
|
||
Character constant: "ABYZ"
|
||
.It
|
||
Gameboy graphics: \`0123
|
||
.El
|
||
.Pp
|
||
The last one, Gameboy graphics, is quite interesting and useful.
|
||
The values are actually pixel values and it converts the
|
||
.Do chunky Dc data to Do planar Dc data as used in the Gameboy.
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
DW \`01012323
|
||
.Ed
|
||
.Pp
|
||
Admittedly, an expression with just a single number is quite boring.
|
||
To spice things up a bit there are a few operators you can use to perform
|
||
calculations between numbers.
|
||
.Pp
|
||
.Sy Operators
|
||
.Pp
|
||
A great number of operators you can use in expressions are available (listed in
|
||
order of precedence):
|
||
.Pp
|
||
.Bl -column -offset indent "Operator"
|
||
.It Sy Operator Ta Sy Meaning
|
||
.It Li \&( \&) Ta Precedence override
|
||
.It Li FUNC() Ta Function call
|
||
.It Li ~ + - Ta Unary not/plus/minus
|
||
.It Li * / % Ta Multiply/divide/modulo
|
||
.It Li << >> Ta Shift left/right
|
||
.It Li & \&| ^ Ta Binary and/or/xor
|
||
.It Li + - Ta Add/subtract
|
||
.It Li != == <= Ta Boolean comparison
|
||
.It Li >= < > Ta Boolean comparison (Same precedence as the others)
|
||
.It Li && || Ta Boolean and/or
|
||
.It Li \&! Ta Unary Boolean not
|
||
.El
|
||
.Pp
|
||
The result of the boolean operators is zero if when FALSE and non-zero when
|
||
TRUE.
|
||
It is legal to use an integer as the condition for IF blocks.
|
||
You can use symbols instead of numbers in your expression if you wish.
|
||
.Pp
|
||
An expression is said to be constant when it doesn't change its value during
|
||
linking.
|
||
This basically means that you can't use labels in those expressions.
|
||
The instructions in the macro-language all require expressions that are
|
||
constant.
|
||
The only exception is the subtraction of labels in the same section or labels
|
||
that belong to sections with a fixed base addresses, all of which must be
|
||
defined in the same source file (the calculation cannot be passed to the object
|
||
file generated by the assembler).
|
||
In this case, the result is a constant that can be calculated at assembly time.
|
||
.Pp
|
||
.Ss Fixed‐point Expressions
|
||
Fixed point constants are basically normal 32-bit constants where the upper 16
|
||
bits are used for the integer part and the lower 16 bits are used for the
|
||
fraction (65536ths).
|
||
This means that you can use them in normal integer expression, and some integer
|
||
operators like plus and minus don't care whether the operands are integer or
|
||
fixed-point.
|
||
You can easily convert a fixed-point number to an integer by shifting it right
|
||
16 bits.
|
||
It follows that you can convert an integer to a fixed-point number by shifting
|
||
it left.
|
||
.Pp
|
||
Some things are different for fixed-point math, though, which is why you have
|
||
the following functions to use:
|
||
.EQ
|
||
delim $$
|
||
.EN
|
||
.Pp
|
||
.Bl -column -offset indent "ATAN2(x, y)"
|
||
.It Sy Name Ta Sy Operation
|
||
.It Fn DIV x y Ta $x \[di] y$
|
||
.It Fn MUL x y Ta $x \[mu] y$
|
||
.It Fn SIN x Ta $sin ( x )$
|
||
.It Fn COS x Ta $cos ( x )$
|
||
.It Fn TAN x Ta $tan ( x )$
|
||
.It Fn ASIN x Ta $asin ( x )$
|
||
.It Fn ACOS x Ta $acos ( x )$
|
||
.It Fn ATAN x Ta $atan ( x )$
|
||
.It Fn ATAN2 x y Ta Angle between $( x , y )$ and $( 1 , 0 )$
|
||
.El
|
||
.EQ
|
||
delim off
|
||
.EN
|
||
.Pp
|
||
These functions are extremely useful for automatic generation of various tables.
|
||
A circle has 65536.0 degrees.
|
||
Sine values are between
|
||
.Bq -1.0 ; 1.0 .
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
; --
|
||
; -- Generate a 256 byte sine table with values between 0 and 128
|
||
; --
|
||
ANGLE SET 0.0
|
||
REPT 256
|
||
DB (MUL(64.0,SIN(ANGLE))+64.0)>>16
|
||
ANGLE SET ANGLE+256.0
|
||
ENDR
|
||
.Ed
|
||
.Pp
|
||
.Ss String Expressions
|
||
The most basic string expression is any number of characters contained in double
|
||
quotes ("for instance").
|
||
Like in C, the escape character is \[rs], and there are a number of commands you
|
||
can use within a string:
|
||
.Pp
|
||
.Bl -column -offset indent "String"
|
||
.It Sy String Ta Sy Meaning
|
||
.It Li \[rs]\[rs] Ta Backslash
|
||
.It Li \[rs]" Ta Double quote
|
||
.It Li \[rs], Ta Comma
|
||
.It Li \[rs]{ Ta Curly bracket left
|
||
.It Li \[rs]} Ta Curly bracket right
|
||
.It Li \[rs]n Ta Newline ($0A)
|
||
.It Li \[rs]t Ta Tab ($09)
|
||
.It Li \[rs]1 - \[rs]9 Ta Macro argument (Only the body of a macros)
|
||
.It Li \[rs]@ Ta Label name suffix (Only in the body of macros and repts)
|
||
.El
|
||
.Pp
|
||
A funky feature is
|
||
.Sy {symbol}
|
||
within a string.
|
||
This will examine the type of the symbol and insert its value accordingly.
|
||
If symbol is a string symbol, the symbols value is simply copied.
|
||
If it's a numeric symbol, the value is converted to hexadecimal notation and
|
||
inserted as a string.
|
||
.Pp
|
||
HINT: The
|
||
.Sy {symbol}
|
||
construct can also be used outside strings.
|
||
The symbol's value is again inserted as a string.
|
||
This is just a short way of doing
|
||
.Dq {symbol} .
|
||
.Pp
|
||
Whenever the macro-language expects a string you can actually use a string
|
||
expression.
|
||
This consists of one or more of these function (yes, you can nest them).
|
||
Note that some of these functions actually return an integer and can be used as
|
||
part of an integer expression!
|
||
.Pp
|
||
.Bl -column "STRSUB_str,_pos,_len"
|
||
.It Sy Name Ta Sy Operation
|
||
.It Fn STRLEN string Ta Returns the number of characters in string
|
||
.It Fn STRCAT str1 str2 Ta Appends str2 to str1.
|
||
.It Fn STRCMP str1 str2 Ta Returns negative if str1 is alphabetically lower
|
||
than str2, zero if they match, positive if str1 is greater than str2.
|
||
.It Fn STRIN str1 str2 Ta Returns the position of str2 in str1 or zero if it's
|
||
not present (first character is position 1).
|
||
.It Fn STRSUB str pos len Ta Returns a substring from str starting at pos
|
||
(first character is position 1) and with len characters.
|
||
.It Fn STRUPR str Ta Converts all characters in str to capitals and returns the
|
||
new string.
|
||
.It Fn STRLWR str Ta Converts all characters in str to lower case and returns
|
||
the new string.
|
||
.El
|
||
.Pp
|
||
.Ss Character maps
|
||
.Pp
|
||
When writing text that is meant to be displayed in the Game Boy, the ASCII
|
||
characters used in the source code may not be the same ones used in the tileset
|
||
used in the ROM.
|
||
For example, the tiles used for uppercase letters may be placed starting at tile
|
||
index 128, which makes it difficult to add text strings to the ROM.
|
||
.Pp
|
||
Character maps allow the code to map strings up to 16 characters long to an
|
||
abitrary 8-bit value:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
CHARMAP "<LF>", 10
|
||
CHARMAP "í", 20
|
||
CHARMAP "A", 128
|
||
.Ed
|
||
.Pp
|
||
.Sy Note:
|
||
Character maps affect all strings in the file from the point in which they are
|
||
defined.
|
||
This means that any string that the code may want to print as debug information
|
||
will also be affected by it.
|
||
.Pp
|
||
.Sy Note:
|
||
The output value of a mapping can be 0.
|
||
If this happens, the assembler will treat this as the end of the string and the
|
||
rest of it will be trimmed.
|
||
.Pp
|
||
.Ss Other functions
|
||
There are a few other functions that do various useful things:
|
||
.Pp
|
||
.Bl -column "BANK(arg)"
|
||
.It Sy Name Ta Sy Operation
|
||
.It Fn BANK arg Ta Returns a bank number.
|
||
If
|
||
.Ar arg
|
||
is the symbol
|
||
.Ic @ ,
|
||
this function returns the bank of the current section.
|
||
If
|
||
.Ar arg
|
||
is a string, it returns the bank of the section that has that name.
|
||
If
|
||
.Ar arg
|
||
is a label, it returns the bank number the label is in.
|
||
For labels, as the linker has to resolve this, it can't be used when the
|
||
expression has to be constant.
|
||
.It Fn DEF label Ta Returns TRUE if
|
||
.Ar label
|
||
has been defined.
|
||
.It Fn HIGH arg Ta Returns the top 8 bits of the operand if
|
||
.Ar arg
|
||
is a label or constant, or the top 8-bit register if it is a 16-bit register.
|
||
.It Fn LOW arg Ta Returns the bottom 8 bits of the operand if
|
||
.Ar arg
|
||
is a label or constant, or the bottom 8-bit register if it is a 16-bit register
|
||
(AF isn't a valid register for this function).
|
||
.El
|
||
.Pp
|
||
.Sh MISCELLANEOUS
|
||
.Ss Changing options while assembling
|
||
.Ic OPT
|
||
can be used to change some of the options during assembling the
|
||
source instead of defining them on the commandline.
|
||
.Pp
|
||
.Ic OPT
|
||
takes a comma-seperated list of options as its argument:
|
||
.Pp
|
||
.Bd -literal -offset indent
|
||
PUSHO
|
||
OPT g.oOX ;Set the GB graphics constants to use these characters
|
||
DW `..ooOOXX
|
||
POPO
|
||
DW `00112233
|
||
.Ed
|
||
.Pp
|
||
The options that OPT can modify are currently:
|
||
.Sy b , e
|
||
and
|
||
.Sy g .
|
||
.Pp
|
||
.Ic POPO
|
||
and
|
||
.Ic PUSHO
|
||
provide the interface to the option stack.
|
||
.Ic PUSHO
|
||
will push the current set of options on the option stack.
|
||
.Ic POPO
|
||
can then later be used to restore them.
|
||
Useful if you want to change some options in an include file and you don't want
|
||
to destroy the options set by the program that included your file.
|
||
The stacks number of entries is limited only by the amount of memory in your
|
||
machine.
|
||
.Sh SEE ALSO
|
||
.Xr rgbasm 1 ,
|
||
.Xr rgblink 1 ,
|
||
.Xr rgblink 5 ,
|
||
.Xr rgbds 5 ,
|
||
.Xr rgbds 7 ,
|
||
.Xr gbz80 7
|
||
.Sh HISTORY
|
||
.Nm rgbds
|
||
was originally written by Carsten S\(/orensen as part of the ASMotor package,
|
||
and was later packaged in RGBDS by Justin Lloyd.
|
||
It is now maintained by a number of contributors at
|
||
.Lk https://github.com/rednex/rgbds .
|