ASM syntax

A lil' bit of context

To help you understand ASM, let's first talk about what it is. Basically, ASM is raw CPU instructions. You're literally writing directives for the CPU.
ASM is not a language, but a class of languages; rather, there is one ASM language per CPU in existence. The reason is that, for example, GBz80 ASM is the ASM language understood by the GB's CPU, the GBz80. Similarly, x86 ASM is the ASM understood by the x86 family of CPUs.

From now on, though, "ASM" will refer to "GB ASM", "GBz80 ASM", "LR35902 ASM" and any other synonym.

Here is a snippet of ASM, just to give you a taste.

Start:
    di

    cp a, $11 ; Compare value left by boot ROM
    jr nz, .notGBC
    ld a, 1
    jr .gotConsoleType

.notGBC
    xor a

.gotConsoleType
    ldh [hConsoleType], a ; Store console type for later checking (in FFFE, to avoid clearing)

    xor a
    ldh [rLCDC], a ; Shut LCD down

    ; Clear HRAM, except console type
    ld c, $FD
.clearHRAM
    ld [$ff00+c], a
    dec c
    bit 7, c
    jr nz, .clearHRAM

If you're familiar with C, this snippet is more or less this:

    a = a == 0x11 ? 1 : 0;
    *hConsoleType = a;

    a = 0;
    *LCDC = a;

    c = 0xFD;
    do {
        *c = a;
        c--;
    } while(c & 0x80);

We'll see the point of this snippet in a moment. For now, we're going to use it as an example of how ASM is written, so its purpose is irrelevant (though it will be explained later).

A line of ASM

To explain how ASM is written, we'll analyze one line of the lines of the snippet presented above:

cp a, $11 ; Compare value left by boot ROM
.

This line is split in two by the half-colon (;). To its left is the instruction; to its right is the comment.

A comment can be anything, and is ignored by the compiler (and thus aren't included in the final ROM): the point is to provide context to some code. Comments are optional, and can also be alone on their line.

Let's take a closer look at the instruction: cp a, $11. The first part, cp, is the instruction proper. The second part, a, $11 is a comma-separated list of operands. Thus, the two operands here are a and $11. Some instructions don't take any operands (di), some may take a different number of them (jr nz, .notGBC versus jr .gotConsoleType).

We will learn instructions as we discover some concepts. For now, you don't need to know what the instructions below do -- in fact, the programs are nonsensical, they're just here as syntax examples.

Labels

You may have noticed that some lines deviate from this rule: Start: and .notGBC, for example. These are labels. A label simply designates a memory address, the one where the following instruction is located. Labels can be on the same line as an instruction and/or a comment. Labels may optionally be followed by a colon. I really recommend putting a colon always, it helps with readability.

; These 3 labels are the same. Useful for aliasing, for example.
Label:; You don't have to separate with spaces. Cool!
OtherLabel

Eh: db $FE ; You can have all three on the same line!


Label: ; RGBDS will complain that you defined this twice

It's of course invalid to create two labels with the same name. It's actually fairly annoying when it happens, because RGBDS tends to not give you line numbers in the error message. Cue heavy Ctrl+F and/or grep in your project files.

But then what about these other labels starting with a dot? These are local labels. Basically, a local label is only accessible inside its global label. Here's an example for clarity:

.alone ; BAD: no global label declared before it

GlobalLabel:
    ld hl, .local ; Refers to the "db $FF"'s address

.local
    db $FF

    ld e, a ; More stuff, who cares!


AnotherGlobalLabel:
    ld a, e

.local
    ld sp, $E000

    dw .local ; Refers to the "ld sp, $E000"'s address


DontDoThis:

.duplicate ; You can define a local label at the beginning of its "block".
    ld b, b
.duplicate ; BAD: twice the same name!

Note that since labels refer to memory locations, they're just numbers. Thus, you can perform arithmetics with labels!

    ld bc, DataEnd - Data ; Gets the length of a block of data, for example

    ; (...)

Data:
    ; Some data here
DataEnd:

Tricky Tabs!

This one is an annoying tidbit. Basically, RGBDS is very touchy about tabs. Rest assured, you can use tabs (\t) or spaces alike, even mix both, it doesn't care. You can even be inconsistent! No, the problem lies in labels and instructions.

Rule #1: labels must be at the beginning of the line.

Good:

    Bad: ; RGBDS will complain, probably about a macro not being defined. Don't worry, it will make sense later.
 OhNo: ; Same

Rule #2: instructions may not be at the beginning of the line.

    ld a, Good

cp Bad ; Oh no

Rule #3: comments... are don't care.

Alright, in the next page, we'll learn our first instruction!