Skip to content

Commit

Permalink
apply prettier formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
avivace committed Nov 30, 2023
1 parent 3077cf7 commit fad07ea
Show file tree
Hide file tree
Showing 22 changed files with 250 additions and 218 deletions.
52 changes: 29 additions & 23 deletions src/part1/assembly.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Assembly basics

Alright, now that we know what the tools *do*, let's see what language RGBASM speaks.
Alright, now that we know what the tools _do_, let's see what language RGBASM speaks.
I will take a short slice of the beginning of `hello-world.asm`, so that we agree on the line numbers, and you can get some syntax highlighting even if your editor doesn't support it.

```rgbasm,linenos,start={{#line_no_of "" ../assets/hello-world.asm:basics}}
{{#include ../assets/hello-world.asm:basics}}
```

Let's analyze it.
Note that I will be ignoring a *lot* of RGBASM's functionality; if you're curious to know more, you should wait until parts II and III, or [read the docs](https://rgbds.gbdev.io/docs).
Note that I will be ignoring a _lot_ of RGBASM's functionality; if you're curious to know more, you should wait until parts II and III, or [read the docs](https://rgbds.gbdev.io/docs).

## Comments

We'll start with line {{#line_no_of "^\s*;" ../assets/hello-world.asm:basics}}, which should appear gray above.
Semicolons `;` denote *comments*.
Everything from a semicolon to the end of the line is *ignored* by RGBASM.
Semicolons `;` denote _comments_.
Everything from a semicolon to the end of the line is _ignored_ by RGBASM.
As you can see on line {{#line_no_of "^.*\s.*;" ../assets/hello-world.asm:basics}}, comments need not be on an otherwise empty line.

Comments are a staple of every good programming language; they are useful to give context as to what code is doing.
Expand All @@ -25,8 +25,9 @@ In any language, good comments are very useful; in assembly, they play an even m

Assembly is a very line-based language.
Each line can contain one of two things:
- a *directive*, which instructs RGBASM to do something, or
- an *instruction*[^instr_directive], which is written directly into the ROM.

- a _directive_, which instructs RGBASM to do something, or
- an _instruction_[^instr_directive], which is written directly into the ROM.

We will talk about directives later, for now let's focus on instructions: for example, in the snippet above, we will ignore lines {{#line_no_of "^\s*INCLUDE" ../assets/hello-world.asm:basics}} (`INCLUDE`), {{#line_no_of "^\s*ds" ../assets/hello-world.asm:basics}} (`ds`), and {{#line_no_of "^\s*SECTION" ../assets/hello-world.asm:basics}} (`SECTION`).

Expand All @@ -35,17 +36,17 @@ The console's processor (<abbr title="Central Processing Unit">CPU</abbr>) execu
Like baking a cake, drawing a "Hello World" image, or displaying a Game Boy programming tutorial!
\*wink\* \*wink\*

Instructions have a *mnemonic*, which is a name they are given, and *operands*, which indicate what they should act upon.
For example, in "melt the chocolate and butter in a saucepan", *the whole sentence* would be the instruction, *the verb* "melt" would be the mnemonic, and "chocolate", "butter", and "saucepan" the operands, i.e. some kind of parameters to the operation.
Instructions have a _mnemonic_, which is a name they are given, and _operands_, which indicate what they should act upon.
For example, in "melt the chocolate and butter in a saucepan", _the whole sentence_ would be the instruction, _the verb_ "melt" would be the mnemonic, and "chocolate", "butter", and "saucepan" the operands, i.e. some kind of parameters to the operation.

Let's discuss the most fundamental instruction, **`ld`**.
`ld` stands for "LoaD", and its purpose is simply to copy data from its right operand (["<abbr title="Right-Hand Side">RHS</abbr>"](https://en.wikipedia.org/wiki/Sides_of_an_equation)) into its left operand (["<abbr title="Left-Hand Side">LHS</abbr>"](https://en.wikipedia.org/wiki/Sides_of_an_equation)).
For example, take line {{#line_no_of "^\s*ld a, 0" ../assets/hello-world.asm:basics}}'s `ld a, 0`: it copies ("loads") the value 0 into the 8-bit register `a`[^ld_imm_from].
If you look further in the file, line {{#line_no_of "^\s*ld a, b" ../assets/hello-world.asm}} has `ld a, b`, which causes the value in register `b` to be copied into register `a`.

Instruction | Mnemonic | Effect
------------|----------|----------------------
Load | `ld` | Copies values around
| Instruction | Mnemonic | Effect |
| ----------- | -------- | -------------------- |
| Load | `ld` | Copies values around |

::: tip:ℹ️

Expand All @@ -64,15 +65,15 @@ The descriptions there are more succinct, since they're intended as reminders, n

In a way, instructions are destined to the console's CPU, and comments are destined to the programmer.
But some lines are neither, and are instead sort of metadata destined to RGBDS itself.
Those are called *directives*, and our Hello World actually contains three of those.
Those are called _directives_, and our Hello World actually contains three of those.

### Including other files

```rgbasm,linenos
{{#include ../assets/hello-world.asm:4}}
```

Line 1 *includes* `hardware.inc`[^hw_inc_directives].
Line 1 _includes_ `hardware.inc`[^hw_inc_directives].
Including a file has the same effect as if you copy-pasted it, but without having to actually do that.

It allows sharing code across files easily: for example, if two files `a.asm` and `b.asm` were to include `hardware.inc`, you would only need to modify `hardware.inc` once for the modifications to apply to both `a.asm` and `b.asm`.
Expand All @@ -88,7 +89,7 @@ We will discuss constants in more detail in Part Ⅱ.

Let's first explain what a "section" is, then we will see what line 3 does.

A section represents a contiguous range of memory, and by default, ends up *somewhere* not known in advance.
A section represents a contiguous range of memory, and by default, ends up _somewhere_ not known in advance.
If you want to see where all the sections end up, you can ask RGBLINK to generate a "map file" with the `-m` flag:

```console
Expand Down Expand Up @@ -123,12 +124,12 @@ We will discuss them in Part Ⅱ.
The `[$100]` part is more interesting, in that it is unique to this section.
See, I said above that:

> a section \[...\] by default, ends up *somewhere* not known in advance.
> a section \[...\] by default, ends up _somewhere_ not known in advance.
However, some memory locations are special, and so sometimes we need a specific section to span a specific range of memory.
To enable this, RGBASM provides the `[addr]` syntax, which *forces* the section's starting address to be `addr`.
To enable this, RGBASM provides the `[addr]` syntax, which _forces_ the section's starting address to be `addr`.

In this case, the memory range $100–$14F is special, as it is the *ROM's header*.
In this case, the memory range $100–$14F is special, as it is the _ROM's header_.
We will discuss the header in a couple lessons, but for now, just know that we need not to put any of our code or data in that space.
How do we do that?
Well, first, we begin a section at address $100, and then we need to reserve some space.
Expand All @@ -142,14 +143,14 @@ Well, first, we begin a section at address $100, and then we need to reserve som
Line 7 claims to "Make room for the header", which I briefly mentioned just above.
For now, let's focus on what `ds` actually does.

`ds` is used for *statically* allocating memory.
`ds` is used for _statically_ allocating memory.
It simply reserves some amount of bytes, which are set to a given value.
The first argument to `ds`, here `$150 - @`, is *how many bytes to reserve*.
The second (optional) argument, here `0`, is *what value to set each reserved byte to*[^ds_pattern].
The first argument to `ds`, here `$150 - @`, is _how many bytes to reserve_.
The second (optional) argument, here `0`, is _what value to set each reserved byte to_[^ds_pattern].

We will see why these bytes must be reserved in a couple of lessons.

It is worth mentioning that this first argument here is an *expression*.
It is worth mentioning that this first argument here is an _expression_.
RGBDS (thankfully!) supports arbitrary expressions essentially anywhere.
This expression is a simple subtraction: $150 minus `@`, which is a special symbol that stands for "the current memory address".

Expand All @@ -168,20 +169,25 @@ Let's see about those!
---

[^instr_directive]:

Technically, instructions in RGBASM are implemented as directives, basically writing their encoded form to the ROM; but the distinction between the instructions in the source code and those in the final ROM is not worth bringing up right now.

[^ld_imm_from]:
The curious reader may ask where the value is copied *from*. The answer is simply that the \"immediate\" byte ($00 in this example) is stored in ROM just after the instruction's opcode byte, and it's what gets copied to `a`.

The curious reader may ask where the value is copied _from_. The answer is simply that the \"immediate\" byte ($00 in this example) is stored in ROM just after the instruction's opcode byte, and it's what gets copied to `a`.
We will come back to this when we talk about how instructions are encoded later on.

[^hw_inc_directives]:

`hardware.inc` itself contains more directives, in particular to define a lot of symbols.
They will be touched upon much later, so we won't look into `hardware.inc` yet.

[^sect_name]:

Section names actually only need to be unique for "plain" sections, and function differently with "unionized" and "fragment" sections, which we will discuss much later.

[^ds_pattern]:
Actually, since RGBASM 0.5.0, `ds` can accept a *list* of bytes, and will repeat the pattern for as many bytes as specified.

Actually, since RGBASM 0.5.0, `ds` can accept a _list_ of bytes, and will repeat the pattern for as many bytes as specified.
It just complicates the explanation slightly, so I omitted it for now.
Also, if the argument is omitted, it defaults to what is specified using the `-p` option **to RGBASM**.
56 changes: 28 additions & 28 deletions src/part1/bin_and_hex.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Binary and hexadecimal

Before we talk about the code, a bit of background knowledge is in order.
When programming at a low level, understanding of *[binary](https://en.wikipedia.org/wiki/Binary_number)* and *[hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal)* is mandatory.
When programming at a low level, understanding of _[binary](https://en.wikipedia.org/wiki/Binary_number)_ and _[hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal)_ is mandatory.
Since you may already know about both of these, a summary of the RGBDS-specific information is available at the end of this lesson.

So, what's binary?
It's a different way to represent numbers, in what's called *base 2*.
It's a different way to represent numbers, in what's called _base 2_.
We're used to counting in [base 10](https://en.wikipedia.org/wiki/Decimal), so we have 10 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9.
Here's how digits work:

Expand All @@ -27,12 +27,12 @@ And here we can see the digits that make up the number!

:::

Decimal digits form a unique *decomposition* of numbers in powers of 10 (*deci*mal is base 10, remember?).
Decimal digits form a unique _decomposition_ of numbers in powers of 10 (*deci*mal is base 10, remember?).
But why stop at powers of 10?
We could use other bases instead, such as base 2.
(Why base 2 specifically will be explained later.)

Binary is base 2, so there are only two digits, called *bits*: 0 and 1.
Binary is base 2, so there are only two digits, called _bits_: 0 and 1.
Thus, we can generalize the principle outlined above, and write these two numbers in a similar way:

```
Expand All @@ -46,14 +46,14 @@ Thus, we can generalize the principle outlined above, and write these two number
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
```

So, by applying the same principle, we can say that in base 2, 42 is written as `101010`, and 1024 as `10000000000`.
So, by applying the same principle, we can say that in base 2, 42 is written as `101010`, and 1024 as `10000000000`.
Since you can't tell ten (decimal 10) and two (binary 10) apart, RGBDS assembly has binary numbers prefixed by a percent sign: 10 is ten, and %10 is two.

Okay, but why base 2 specifically?
Rather conveniently, a bit can only be 0 or 1, which are easy to represent as "ON" or "OFF", empty or full, etc!
If you want, at home, to create a one-bit memory, just take a box.
If it's empty, it stores a 0; if it contains *something*, it stores a 1.
Computers thus primarily manipulate binary numbers, and this has a *slew* of implications, as we will see throughout this entire tutorial.
If it's empty, it stores a 0; if it contains _something_, it stores a 1.
Computers thus primarily manipulate binary numbers, and this has a _slew_ of implications, as we will see throughout this entire tutorial.

## Hexadecimal

Expand All @@ -63,7 +63,7 @@ Take %10000000000, aka 2048; when in decimal only 4 digits are required, binary
And, did you notice that I actually wrote one zero too few?
Fortunately, hexadecimal is here to save the day! 🦸

Base 16 works just the same as every other base, but with 16 digits, called *nibbles*: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F.
Base 16 works just the same as every other base, but with 16 digits, called _nibbles_: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F.

```
42 = 2 × 16 + 10
Expand All @@ -75,26 +75,26 @@ Base 16 works just the same as every other base, but with 16 digits, called *nib

Like binary, we will use a prefix to denote hexadecimal, namely `$`.
So, 42 = $2A, and 1024 = $400.
This is *much* more compact than binary, and slightly more than decimal, too; but what makes hexadecimal very interesting is that one nibble corresponds *exactly* to 4 bits!

Nibble | Bits
:------:|:----:
$0 | %0000
$1 | %0001
$2 | %0010
$3 | %0011
$4 | %0100
$5 | %0101
$6 | %0110
$7 | %0111
$8 | %1000
$9 | %1001
$A | %1010
$B | %1011
$C | %1100
$D | %1101
$E | %1110
$F | %1111
This is _much_ more compact than binary, and slightly more than decimal, too; but what makes hexadecimal very interesting is that one nibble corresponds _exactly_ to 4 bits!

| Nibble | Bits |
| :----: | :---: |
| $0 | %0000 |
| $1 | %0001 |
| $2 | %0010 |
| $3 | %0011 |
| $4 | %0100 |
| $5 | %0101 |
| $6 | %0110 |
| $7 | %0111 |
| $8 | %1000 |
| $9 | %1001 |
| $A | %1010 |
| $B | %1011 |
| $C | %1100 |
| $D | %1101 |
| $E | %1110 |
| $F | %1111 |

This makes it very easy to convert between binary and hexadecimal, while retaining a compact enough notation.
Thus, hexadecimal is used a lot more than binary.
Expand Down
10 changes: 5 additions & 5 deletions src/part1/header.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ What is this mysterious header, why are we making room for it, and more question

## What is the header?

First order of business is explaining what the header *is*.
First order of business is explaining what the header _is_.
It's the region of memory from $0104 to $014F (inclusive).
It contains metadata about the ROM, such as its title, Game Boy Color compatibility, size,
two checksums, and interestingly, the Nintendo logo that is displayed during the power-on animation.
Expand Down Expand Up @@ -38,9 +38,9 @@ The header is intimately tied to what is called the **boot ROM**.
The most observant and/or nostalgic of you may have noticed the lack of the boot-up animation and the Game Boy's signature "ba-ding!" in Emulicious.
When the console powers up, the CPU does not begin executing instructions at address $0100 (where our ROM's entry point is), but at $0000.

However, at that time, a small program called the *boot ROM*, burned within the CPU's silicon, is "overlaid" on top of our ROM!
However, at that time, a small program called the _boot ROM_, burned within the CPU's silicon, is "overlaid" on top of our ROM!
The boot ROM is responsible for the startup animation, but it also checks the ROM's header!
Specifically, it verifies that the Nintendo logo and header checksums are correct; if either check fails, the boot ROM intentionally *locks up*, and our game never gets to run :(
Specifically, it verifies that the Nintendo logo and header checksums are correct; if either check fails, the boot ROM intentionally _locks up_, and our game never gets to run :(

::: tip For the curious

Expand Down Expand Up @@ -97,7 +97,7 @@ warning: Overwrote a non-zero byte in the header checksum
warning: Overwrote a non-zero byte in the global checksum
```

*I'm sure these warnings are nothing to be worried about...*
_I'm sure these warnings are nothing to be worried about..._
(Depending on your version of RGBDS, you may have gotten different warnings, or none at all.)

Let's run the ROM, click on Console on the debugger's bottom window, press <kbd><kbd>F5</kbd></kbd> a few times, and...
Expand All @@ -120,7 +120,7 @@ What is it doing there?
So the `jp` at $0100 went there, and started executing instructions (`3E CE` is the raw form of `ld a, $CE`), but then $ED does not encode any valid instruction, so the CPU locks up.

But why is `EntryPoint` there?
Well, as you may have figured out from the warnings RGBFIX printed, it *overwrites* the header area in the ROM.
Well, as you may have figured out from the warnings RGBFIX printed, it _overwrites_ the header area in the ROM.
However, RGBLINK is **not** aware of the header (because RGBLINK is not only used to generate ROMs!), so you must explicitly reserve space for the header area.

::: danger:🥴
Expand Down
5 changes: 4 additions & 1 deletion src/part1/hello_world.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Note that we will need to type a lot of commands, so open a terminal now.
It's a good idea to create a new directory (`mkdir gb_hello_world`, for example, then `cd gb_hello_world` to enter the new directory).

Grab the following files (right-click each link, "Save Link As..."), and place them all in this new directory:

- [`hello-world.asm`](../assets/hello-world.asm)
- [`hardware.inc`](https://raw.githubusercontent.com/gbdev/hardware.inc/v4.0/hardware.inc)

Expand Down Expand Up @@ -46,6 +47,7 @@ If you need whitespace within an argument, you must quote it:
:::

It should look like this:

<script id="asciicast-weljUlcp1KC5GqS9jqV62dy5m" src="https://asciinema.celforyon.fr/a/weljUlcp1KC5GqS9jqV62dy5m.js" async></script>

(If you encounter an error you can't figure out by yourself, don't be afraid to [ask us](../index.md#feedback)! We'll sort it out.)
Expand All @@ -58,7 +60,8 @@ Now, we just need to run it; open Emulicious, then go "File", then "Open File",
<source src="../assets/vid/hello_world.webm" type="video/webm">
<source src="../assets/vid/hello_world.mp4" type="video/mp4">

<img src="../assets/vid/hello_world.gif" alt="Video demonstration in Emulicious">
<img src="../assets/vid/hello_world.gif" alt="Video demonstration in Emulicious">

</video>

You could also take a flash cart (I use the [EverDrive GB X5](https://krikzz.com/store/home/47-everdrive-gb.html), but there are plenty of alternatives), load up your ROM onto it, and run it on an actual console!
Expand Down
Loading

0 comments on commit fad07ea

Please sign in to comment.