|
| 1 | +# Getting started |
| 2 | + |
| 3 | +In this lesson, we will start a new project from scratch. |
| 4 | +We will make a [Breakout](https://en.wikipedia.org/wiki/Breakout_%28video_game%29) / [Arkanoid](https://en.wikipedia.org/wiki/Arkanoid) clone, which we'll call "Unbricked"! |
| 5 | +(Though you are free to give it any other name you like, as it will be *your* project.) |
| 6 | + |
| 7 | +Open a terminal and make a new directory (`mkdir unbricked`), and then enter it (`cd unbricked`), just like you did for ["Hello, world!"](../part1/hello_world.md). |
| 8 | + |
| 9 | +Start by creating a file called `main.asm`, and include `hardware.inc` in your code. |
| 10 | + |
| 11 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:includes}} |
| 12 | +{{#include ../../unbricked/getting-started/main.asm:includes}} |
| 13 | +``` |
| 14 | +You may be wondering what purpose `hardware.inc` serves. |
| 15 | +Well, the code we write only really affects the CPU, but does not do anything with the rest of the console (not directly, anyway). |
| 16 | +To interact with other components (like the graphics system, say), [Memory-Mapped <abbr title="Input/Output">I/O</abbr>](https://en.wikipedia.org/wiki/Memory-mapped_I/O) (MMIO) is used: basically, [memory](../part1/memory.md) in a certain range (addresses $FF00–FF7F) does special things when accessed. |
| 17 | + |
| 18 | +These bytes of memory being interfaces to the hardware, they are called *hardware registers* (not to be mistaken with [the CPU registers](../part1/registers.md)). |
| 19 | +For example, the "PPU status" register is located at address $FF41. |
| 20 | +Reading from that address reports various bits of info regarding the graphics system, and writing to it allows changing some parameters. |
| 21 | +But, having to remember all the numbers ([non-exhaustive list](https://gbdev.io/pandocs/Power_Up_Sequence.html#hardware-registers)) would be very tedious—and this is where `hardware.inc` comes into play! |
| 22 | +`hardware.inc` defines one constant for each of these registers (for example, `rSTAT` for the aforementioned "PPU status" register), plus some additional constants for values read from or written to these registers. |
| 23 | + |
| 24 | +::: tip |
| 25 | + |
| 26 | +Don't worry if this flew over your head, we'll see an example below with `rLCDC` and `LCDCF_ON`. |
| 27 | + |
| 28 | +By the way, the `r` stands for "register", and the `F` in `LCDCF` stands for "flag". |
| 29 | + |
| 30 | +::: |
| 31 | + |
| 32 | +Next, make room for the header. |
| 33 | +[Remember from Part Ⅰ](../part1/header.md) that the header is where some information that the Game Boy relies on is stored, so you don't want to accidentally leave it out. |
| 34 | + |
| 35 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:header}} |
| 36 | +{{#include ../../unbricked/getting-started/main.asm:header}} |
| 37 | +``` |
| 38 | + |
| 39 | +The header jumps to `EntryPoint`, so let's write that now: |
| 40 | + |
| 41 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:entry}} |
| 42 | +{{#include ../../unbricked/getting-started/main.asm:entry}} |
| 43 | +``` |
| 44 | + |
| 45 | +The next few lines wait until "VBlank", which is the only time you can safely turn off the screen (doing so at the wrong time could damage a real Game Boy, so this is very crucial). |
| 46 | +We'll explain what VBlank is and talk about it more later in the tutorial. |
| 47 | + |
| 48 | +Turning off the screen is important because loading new tiles while the screen is on is tricky—we'll touch on how to do that in Part 3. |
| 49 | + |
| 50 | +Speaking of tiles, we're going to load some into VRAM next, using the following code: |
| 51 | + |
| 52 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:copy_tiles}} |
| 53 | +{{#include ../../unbricked/getting-started/main.asm:copy_tiles}} |
| 54 | +``` |
| 55 | + |
| 56 | +This loop might be [reminiscent of part Ⅰ](../part1/jumps.md#conditional-jumps). |
| 57 | +It copies starting at `Tiles` to `$9000` onwards, which is the part of VRAM where our [tiles](../part1/tiles.md) are going to be stored. |
| 58 | +Recall that `$9000` is where the data of background tile $00 lies, and the data of subsequent tiles follows right after. |
| 59 | +To get the number of bytes to copy, we will do just like in Part Ⅰ: using another label at the end, called `TilesEnd`, the difference between it (= the address after the last byte of tile data) and `Tiles` (= the address of the first byte) will be exactly that length. |
| 60 | + |
| 61 | +That said, we haven't written `Tiles` nor any of the related data yet. |
| 62 | +We'll get to that later! |
| 63 | + |
| 64 | +Almost done now—next, write another loop, this time for copying [the tilemap](../part1/tilemap.md). |
| 65 | + |
| 66 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:copy_map}} |
| 67 | +{{#include ../../unbricked/getting-started/main.asm:copy_map}} |
| 68 | +``` |
| 69 | + |
| 70 | +Note that while this loop's body is exactly the same as `CopyTiles`'s, the 3 values loaded into `de`, `hl`, and `bc` are different. |
| 71 | +These determine the source, destination, and size of the copy, respectively. |
| 72 | + |
| 73 | +::: tip "[<abbr title="Don't Repeat Yourself">DRY</abbr>](https://en.wikipedia.org/wiki/Don't_Repeat_Yourself)" |
| 74 | + |
| 75 | +If you think that this is super redundant, you are not wrong, and we will see later how to write actual, reusable *functions*. |
| 76 | +But there is more to them than meets the eye, so we will start tackling them much later. |
| 77 | + |
| 78 | +::: |
| 79 | + |
| 80 | +Finally, let's turn the screen back on, and set a [background palette](../part1/palettes.md). |
| 81 | +Rather than writing the non-descript number `%10000001` (or $81 or 129, to taste), we make use of two constants graciously provided by `hardware.inc`: `LCDCF_ON` and `LCDCF_BGON`. |
| 82 | +When written to [`rLCDC`](https://gbdev.io/pandocs/LCDC), the former causes the PPU and screen to turn back on, and the latter enables the background to be drawn. |
| 83 | +(There are other elements that could be drawn, but we are not enabling them yet.) |
| 84 | +Combining these constants must be done using `|`, the *binary "or"* operator; we'll see why later. |
| 85 | + |
| 86 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:end}} |
| 87 | +{{#include ../../unbricked/getting-started/main.asm:end}} |
| 88 | +``` |
| 89 | + |
| 90 | +There's one last thing we need before we can build the ROM, and that's the graphics. |
| 91 | +We will draw the following screen: |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | +In `hello-world.asm`, tile data had been written out by hand in hexadecimal; this was to let you see how the sausage is made at the lowest level, but *boy* is it impractical to write! |
| 96 | +This time, we will employ a more friendly way, which will let us write each row of pixels more easily. |
| 97 | +For each row of pixels, instead of writing [the bitplanes](../part1/tiles.md#encoding) directly, we will use a backtick (`` ` ``) followed by 8 characters. |
| 98 | +Each character defines a single pixel, intuitively from left to right; it must be one of 0, 1, 2, and 3, representing the corresponding color index in [the palette](../part1/palettes.md). |
| 99 | + |
| 100 | +::: tip |
| 101 | + |
| 102 | +If the character selection isn't to your liking, you can use [RGBASM's `-g` option](https://rgbds.gbdev.io/docs/v0.5.2/rgbasm.1#g) or [`OPT g`](https://rgbds.gbdev.io/docs/v0.5.2/rgbasm.5/#Changing_options_while_assembling) to pick others. |
| 103 | +For example, `rgbasm -g '.xXO' (...)` or `OPT g.xXO` would swap the four characters to `.`, `x`, `X`, and `O` respectively. |
| 104 | + |
| 105 | +::: |
| 106 | + |
| 107 | +For example: |
| 108 | + |
| 109 | +```rgbasm |
| 110 | + dw `01230123 ; This is equivalent to `db $55,$33` |
| 111 | +``` |
| 112 | + |
| 113 | +You may have noticed that we are using `dw` instead of `db`; the difference between these two will be explained later. |
| 114 | +We already have tiles made for this project, so you can copy [this premade file](../../unbricked/getting-started/tileset.asm), and paste it at the end of your code. |
| 115 | + |
| 116 | +Then copy the tilemap from [this file](../../unbricked/getting-started/tilemap.asm), and paste it after the `TilesEnd` label. |
| 117 | + |
| 118 | +You can build the ROM now, by running the following commands in your terminal: |
| 119 | + |
| 120 | +```console |
| 121 | +$ rgbasm -L -o main.o main.asm |
| 122 | +$ rgblink -o unbricked.gb main.o |
| 123 | +$ rgbfix -v -p 0xFF unbricked.gb |
| 124 | +``` |
| 125 | + |
| 126 | +If you run this in your emulator, you should see the following: |
| 127 | + |
| 128 | + |
| 129 | + |
| 130 | +That white square seems to be missing! |
| 131 | +You may have noticed this comment earlier, somewhere in the tile data: |
| 132 | + |
| 133 | +```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/getting-started/main.asm:custom_logo}} |
| 134 | +{{#include ../../unbricked/getting-started/main.asm:custom_logo}} |
| 135 | +``` |
| 136 | + |
| 137 | +The logo tiles were left intentionally blank so that you can choose your own. |
| 138 | +You can use one of the following pre-made logos, or try coming up with your own! |
| 139 | + |
| 140 | +- **RGBDS Logo** |
| 141 | + |
| 142 | +  |
| 143 | + |
| 144 | + [Source](../../unbricked/getting-started/rgbds.asm) |
| 145 | + |
| 146 | +- **Duck** |
| 147 | + |
| 148 | +  |
| 149 | + |
| 150 | + [Source](../../unbricked/getting-started/duck.asm) |
| 151 | + |
| 152 | +- **Tail** |
| 153 | + |
| 154 | +  |
| 155 | + |
| 156 | + [Source](../../unbricked/getting-started/tail.asm) |
| 157 | + |
| 158 | +Replace the blank tiles with the new graphics, build the game again, and you should see your logo of choice in the bottom-right! |
0 commit comments