diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 3bfa3da..2a24fe1 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -15,8 +15,9 @@ * [The VCount Register](ch02/the_vcount_register.md) * [light_cycle](ch02/light_cycle.md) * [Ch 3: Memory and Objects](ch03/index.md) - * [GBA Memory](ch03/gba_memory.md) - * [Tiled Backgrounds](ch03/tiled_backgrounds.md) + * [GBA Memory Mapping](ch03/gba_memory_mapping.md) + * [Tile Data](ch03/tile_data.md) + * [Regular Backgrounds](ch03/regular_backgrounds.md) * [Object Basics](ch03/object_basics.md) * [GBA RNG](ch03/gba_rng.md) * [memory_game](ch03/memory_game.md) diff --git a/book/src/ch03/gba_memory.md b/book/src/ch03/gba_memory_mapping.md similarity index 83% rename from book/src/ch03/gba_memory.md rename to book/src/ch03/gba_memory_mapping.md index 3ae16d6..54d9b78 100644 --- a/book/src/ch03/gba_memory.md +++ b/book/src/ch03/gba_memory_mapping.md @@ -1,19 +1,19 @@ -# GBA Memory +# GBA Memory Mapping The [GBA Memory Map](http://problemkaputt.de/gbatek.htm#gbamemorymap) has several memory portions to it, each with their own little differences. Most of the memory has pre-determined use according to the hardware, but there is also space for games to use as a scratch pad in whatever way the game sees fit. -The memory ranges listed here are _inclusive_, so they end with a lot of `F`s -and `E`s. +The memory ranges listed here are _inclusive_, so they end with a lot of F's +and E's. We've talked about volatile memory before, but just as a reminder I'll say that -all of the memory we'll talk about here should be accessed with volatile with +all of the memory we'll talk about here should be accessed using volatile with two exceptions: 1) Work RAM (both internal and external) can be used normally, and if the - compiler is able to totally elide any reads and writes that's okay. + compiler is able to totally elide some reads and writes that's okay. 2) However, if you set aside any space in Work RAM where an interrupt will communicate with the main program then that specific location will have to keep using volatile access, since the compiler never knows when an interrupt @@ -38,13 +38,11 @@ external work ram has only a 16-bit bus (if you read/write a 32-bit value it silently breaks it up into two 16-bit operations) and also 2 wait cycles (extra CPU cycles that you have to expend _per 16-bit bus use_). -In other words, we should think of EWRAM as if it was "heap space" in a normal -application. You can take the time to go store something within EWRAM, or to -load it out of EWRAM, but you should always avoid doing a critical computation -on values in EWRAM. It's a bit of a pain, but if you wanna be speedy and you -have more than just one manipulation that you want to do, you should pull the -value into a local variable, do all of your manipulations, and then push it back -out at the end. +It's most helpful to think of EWRAM as slower, distant memory, similar to the +"heap" in a normal application. You can take the time to go store something +within EWRAM, or to load it out of EWRAM, but if you've got several operations +to do in a row and you're worried about time you should pull that value into +local memory, work on your local copy, and then push it back out to EWRAM. ## Internal Work RAM / IWRAM @@ -54,11 +52,9 @@ This is a smaller pile of space, but it has a 32-bit bus and no wait. By default, `0x3007F00` to `0x3007FFF` is reserved for interrupt and BIOS use. The rest of it is totally up to you. The user's stack space starts at -`0x3007F00` and proceeds _down_ from there. In other words, if you start your -own customized IWRAM use at `0x3000000` and go up, eventually you might hit your -stack. However, most reasonable uses won't actually cause a memory collision. -It's just something you should know about if you're using a ton of stack or -IWRAM and then get problems. +`0x3007F00` and proceeds _down_ from there. For best results you should probably +start at `0x3000000` and then go upwards. Under normal use it's unlikely that +the two memory regions will crash into each other. ## IO Registers @@ -95,9 +91,9 @@ and then there's 256 entries for object palette data (starting at `0x5000200`). The GBA also has two modes for palette access: 8-bits-per-pixel (8bpp) and 4-bits-per-pixel (4bpp). -* In 8bpp mode an (8-bit) palette index value within a background or sprite +* In 8bpp mode an 8-bit palette index value within a background or sprite simply indexes directly into the 256 slots for that type of thing. -* In 4bpp mode a (4-bit) palette index value within a background or sprite +* In 4bpp mode a 4-bit palette index value within a background or sprite specifies an index within a particular "palbank" (16 palette entries each), and then a _separate_ setting outside of the graphical data determines which palbank is to be used for that background or object (the screen entry data for @@ -143,14 +139,16 @@ of things, but they're _interlaced_ with each other all the way through. Now, [GBATEK](http://problemkaputt.de/gbatek.htm#lcdobjoamattributes) and [CowByte](https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites)) -doesn't quite give names to the two data types, though +doesn't quite give names to the two data types here. [TONC](https://www.coranac.com/tonc/text/regobj.htm#sec-oam) calls them -`OBJ_ATTR` and `OBJ_AFFINE`. We'll give them Rust names of course. In Rust terms -their layout would look like this: +`OBJ_ATTR` and `OBJ_AFFINE`, but we'll be giving them names fitting with the +Rust naming convention. Just know that if you try to talk about it with others +they might not be using the same names. In Rust terms their layout would look +like this: ```rust #[repr(C)] -pub struct ObjectAttribute { +pub struct ObjectAttributes { attr0: u16, attr1: u16, attr2: u16, @@ -173,7 +171,7 @@ pub struct AffineMatrix { (Note: the `#[repr(C)]` part just means that Rust must lay out the data exactly in the order we specify, which otherwise it is not required to do). -So, we've got 1024 bytes in OAM and each `ObjectAttribute` value is 8 bytes, so +So, we've got 1024 bytes in OAM and each `ObjectAttributes` value is 8 bytes, so naturally we can support up to 128 objects. _At the same time_, we've got 1024 bytes in OAM and each `AffineMatrix` is 32 @@ -181,7 +179,7 @@ bytes, so we can have 32 of them. But, as I said, these things are all _interlaced_ with each other. See how there's "filler" fields in each struct? If we imagine the OAM as being just an -array of one type or the other, indexes 0/1/2/3 of the `ObjectAttribute` array +array of one type or the other, indexes 0/1/2/3 of the `ObjectAttributes` array would line up with index 0 of the `AffineMatrix` array. It's kinda weird, but that's just how it works. When we setup functions to read and write these values we'll have to be careful with how we do it. We probably _won't_ want to use @@ -207,7 +205,8 @@ mirror that you choose to access the game pak through affects which wait state setting it uses (configured via IO register of course). Unfortunately, the details come down more to the game pak hardware that you load your game onto than anything else, so there's not much I can say right here. We'll eventually -talk about it more later, +talk about it more later when I'm forced to do the boring thing and just cover +all the IO registers that aren't covered anywhere else. One thing of note is the way that the 16-bit bus affects us: the instructions to execute are coming through the same bus as the rest of the game data, so we want @@ -227,9 +226,10 @@ builds starts with `thumbv4`. * `0xE000000` to `0xE00FFFF` (64k) The game pak SRAM has an 8-bit bus. Why did Pokémon always take so long to save? -This is why. It also has some amount of wait, but as with the ROM, the details -depend on your game pak hardware (and also as with ROM, you can adjust the -settings with an IO register, should you need to). +Saving the whole game one byte at a time is why. The SRAM also has some amount +of wait, but as with the ROM, the details depend on your game pak hardware (and +also as with ROM, you can adjust the settings with an IO register, should you +need to). One thing to note about the SRAM is that the GBA has a Direct Memory Access (DMA) feature that can be used for bulk memory movements in some cases, but the diff --git a/book/src/ch03/tiled_backgrounds.md b/book/src/ch03/regular_backgrounds.md similarity index 100% rename from book/src/ch03/tiled_backgrounds.md rename to book/src/ch03/regular_backgrounds.md diff --git a/book/src/ch03/tile_data.md b/book/src/ch03/tile_data.md new file mode 100644 index 0000000..7728a4d --- /dev/null +++ b/book/src/ch03/tile_data.md @@ -0,0 +1,118 @@ +# Tile Data + +When using the GBA's hardware graphics, if you want to let the hardware do most +of the work you have to use Modes 0, 1 or 2. However, to do that we first have +to learn about how tile data works inside of the GBA. + +## Tiles + +Fundamentally, a tile is an 8x8 image. If you want anything bigger than 8x8 you +need to arrange several tiles so that it looks like whatever you're trying to +draw. + +As was already mentioned, the GBA supports two different color modes: 4 bits per +pixel and 8 bits per pixel. So already we have two types of tile that we need to +model. The pixel bits always represent an index into the PALRAM. + +* With 4 bits per pixel, the PALRAM is imagined to be 16 **palbank** sections of + 16 palette entries each. The image data selects the index within the palbank, + and an external configuration selects which palbank is used. +* With 8 bits per pixel, the PALRAM is imagined to be a single 256 entry array + and the index just directly picks which of the 256 colors is used. + +So, already we have some Rust types we can define: + +```rust +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct Tile4bpp { + data: [u32; 8] +} + +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct Tile8bpp { + data: [u32; 16] +} +``` + +I hope this makes sense so far. At 4bpp, we have 4 bits per pixel, times 8 +pixels per line, times 8 lines: 256 bits required. Similarly, at 8 bits per +pixel we'll need 512 bits. Why are we defining them as arrays of `u32` values? +Because when it comes time to do bulk copies the fastest way to it will be to go +one whole machine word at a time. If we make the data inside the type be an +array of `u32` then it'll already be aligned for fast `u32` bulk copies. + +Keeping track of the current color depth is naturally the _programmer's_ +problem. If you get it wrong you'll see a whole ton of garbage pixels all over +the screen, and you'll probably be able to guess why. You know, unless you did +one of the other things that can make a bunch of garbage pixels show up all over +the screen. Graphics programming is fun like that. + +## Charblocks + +Tiles don't just sit on their own, they get grouped into **charblocks**. They're +called that because tiles represent characters... even though they're also used +to draw the background? I don't get it exactly, but that's just what they're +called in other documents and I don't have a significantly better name for the +concept, so that's what we'll call it. + +A charblock is 16kb long (`0x4000` bytes), which means that the number of tiles +that fit into a charblock depends on your color depth. With 4bpp you get 512 +tiles, and with 8bpp there's 256 tiles. So they'd be something like this: + +```rust +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct Charblock4bpp { + data: [Tile4bpp; 512], +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct Charblock8bpp { + data: [Tile8bpp; 256], +} +``` + +You'll note that we can't even derive `Debug` or `Default` any more because the +arrays are so big. Rust supports Clone and Copy for arrays of any size, but the +rest is still size 32 or less. We won't generally be making up an entire +Charblock on the fly though, so it's not a big deal. If we _absolutely_ had to, +we could call `core::mem::zeroed()`, but we really don't want to be trying to +build a whole charblock at runtime. We'll usually want to define our tile data +as `const` charblock values (or even parts of charblock values) that we load out +of the game pak ROM. + +Anyway, with 16k per charblock and only 96k total in VRAM, it's easy math to see +that there's 6 different charblocks in VRAM when in a tiled mode. The first four +of these are for backgrounds, and the other two are for objects. There's rules +for how a tile ID on a background or object selects a tile within a charblock, +but since they're different between backgrounds and objects we'll cover that on +their own pages. + +## Image Editing + +It's very important to note that if you use a normal image editor you'll get +very bad results if you translate that directly into GBA memory. + +Imagine you have part of an image that's 16 by 16 pixels, aka 2 tiles by 2 +tiles. The data for that bitmap is the 1st row of the 1st tile, then the 1st row +of the 2nd tile. However, when we translate that into the GBA, the first 8 +pixels will indeed be the first 8 tile pixels, but then the next 8 pixels in +memory will be used as the _2nd row of the first tile_, not the 1st row of the +2nd tile. + +So, how do we fix this? + +Well, the simple but annoying way is to edit your tile image as being an 8 pixel +wide image and then have the image get super tall as you add more and more +tiles. It can work, but it's really impractical if you have any multi-tile +things that you're trying to do. + +Instead, there are some image conversion tools that devkitpro provides in their +gba-dev section. They let you take normal images and then repackage them and +export it in various formats that you can then compile into your project. + +TODO: make Ketsuban write some tips on how to use whatever the image converter +is called. diff --git a/docs/ch00/index.html b/docs/ch00/index.html index 205a348..7d13b49 100644 --- a/docs/ch00/index.html +++ b/docs/ch00/index.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/hello1.html b/docs/ch01/hello1.html index d246070..6e2bbaf 100644 --- a/docs/ch01/hello1.html +++ b/docs/ch01/hello1.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/hello2.html b/docs/ch01/hello2.html index edd70c2..a0e6f8f 100644 --- a/docs/ch01/hello2.html +++ b/docs/ch01/hello2.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/index.html b/docs/ch01/index.html index 5aa67e3..74bc689 100644 --- a/docs/ch01/index.html +++ b/docs/ch01/index.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/io_registers.html b/docs/ch01/io_registers.html index 3a8dac3..0a30bbc 100644 --- a/docs/ch01/io_registers.html +++ b/docs/ch01/io_registers.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/the_display_control_register.html b/docs/ch01/the_display_control_register.html index 3aa138b..cd2adfa 100644 --- a/docs/ch01/the_display_control_register.html +++ b/docs/ch01/the_display_control_register.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/video_memory_intro.html b/docs/ch01/video_memory_intro.html index 664d303..85820c7 100644 --- a/docs/ch01/video_memory_intro.html +++ b/docs/ch01/video_memory_intro.html @@ -72,7 +72,7 @@
diff --git a/docs/ch01/volatile.html b/docs/ch01/volatile.html index f5dd093..c37599e 100644 --- a/docs/ch01/volatile.html +++ b/docs/ch01/volatile.html @@ -72,7 +72,7 @@
diff --git a/docs/ch02/index.html b/docs/ch02/index.html index 5dfdb90..5126505 100644 --- a/docs/ch02/index.html +++ b/docs/ch02/index.html @@ -72,7 +72,7 @@
diff --git a/docs/ch02/light_cycle.html b/docs/ch02/light_cycle.html index 34e2c59..48e9a81 100644 --- a/docs/ch02/light_cycle.html +++ b/docs/ch02/light_cycle.html @@ -72,7 +72,7 @@
diff --git a/docs/ch02/the_key_input_register.html b/docs/ch02/the_key_input_register.html index 13d28c1..e919193 100644 --- a/docs/ch02/the_key_input_register.html +++ b/docs/ch02/the_key_input_register.html @@ -72,7 +72,7 @@
diff --git a/docs/ch02/the_vcount_register.html b/docs/ch02/the_vcount_register.html index 228fa8f..243a922 100644 --- a/docs/ch02/the_vcount_register.html +++ b/docs/ch02/the_vcount_register.html @@ -72,7 +72,7 @@
diff --git a/docs/ch03/gba_memory.html b/docs/ch03/gba_memory_mapping.html similarity index 95% rename from docs/ch03/gba_memory.html rename to docs/ch03/gba_memory_mapping.html index 6d9a244..65649dc 100644 --- a/docs/ch03/gba_memory.html +++ b/docs/ch03/gba_memory_mapping.html @@ -3,7 +3,7 @@ - GBA Memory - Rust GBA Guide + GBA Memory Mapping - Rust GBA Guide @@ -72,7 +72,7 @@
@@ -136,7 +136,7 @@
-

GBA Memory

+

GBA Memory Mapping

The GBA Memory Map has several memory portions to it, each with their own little differences. Most of the memory has pre-determined use according to the hardware, but there is also @@ -361,7 +361,7 @@ one byte at a time when you're using the SRAM.

- @@ -379,7 +379,7 @@ one byte at a time when you're using the SRAM.

- diff --git a/docs/ch03/gba_rng.html b/docs/ch03/gba_rng.html index ef13707..8188184 100644 --- a/docs/ch03/gba_rng.html +++ b/docs/ch03/gba_rng.html @@ -72,7 +72,7 @@
diff --git a/docs/ch03/index.html b/docs/ch03/index.html index 3f59899..bcc698b 100644 --- a/docs/ch03/index.html +++ b/docs/ch03/index.html @@ -72,7 +72,7 @@
@@ -147,8 +147,8 @@ memory for tiled graphics, but also things like all those IO registers, where our RAM is for scratch space, all that stuff. Even if we can't put all of them to use at once, it's helpful to have an idea of what will be available in the long run.

-

Tiled modes bring us two big new concepts that each have their own complexity: -backgrounds and objects. They share some concepts, but fundamentally the +

Tiled modes bring us three big new concepts that each have their own complexity: +tiles, backgrounds, and objects. Backgrounds and objects both use tiles, but the background is for creating a very large static space that you can scroll around the view within, and the objects are about having a few moving bits that appear over the background. Careful use of backgrounds and objects is key to having the @@ -177,7 +177,7 @@ check, and then if they match the pair disappears.

- @@ -195,7 +195,7 @@ check, and then if they match the pair disappears.

- diff --git a/docs/ch03/memory_game.html b/docs/ch03/memory_game.html index d142c4d..5d83569 100644 --- a/docs/ch03/memory_game.html +++ b/docs/ch03/memory_game.html @@ -72,7 +72,7 @@
diff --git a/docs/ch03/object_basics.html b/docs/ch03/object_basics.html index 2f607e2..a06ebda 100644 --- a/docs/ch03/object_basics.html +++ b/docs/ch03/object_basics.html @@ -72,7 +72,7 @@
@@ -144,7 +144,7 @@