diff --git a/index.html b/index.html index 274af2d..6ed6162 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,16 @@ + + + +
diff --git a/projects/Gameboy/setup.html b/projects/Gameboy/setup.html index a003969..b2f770a 100644 --- a/projects/Gameboy/setup.html +++ b/projects/Gameboy/setup.html @@ -136,13 +136,12 @@On the GBA, specific areas of memory are mapped directly to hardware functions. To get the GBA to do anything, you - need to write specific bits to specific sections of memory. This is known as memory-mapped IO. This means if - we want to create some sprites we need to understand the register layout of the system memory. Luckily the hardware + need to write specific bits to specific sections of memory. This is known as memory-mapped IO. Luckily the hardware is well documented in the Tonc Text and the CowBite Spec. Disclaimer: Hardware documentation for this blog post are mostly summarized from theses sources. You really should read those for a full @@ -152,7 +151,7 @@
Below is a table of the important sections of memory. We will mostly be focused on the ones highlighted since they - control video output. + control video output. I'm placing this in a separate section since you will likely need to reference it often.
System ROM | -0000:0000h |
- 0000:3FFFh
+ | 0x00000000 |
+ 0x00003FFF
|
16KB | 32 bit
@@ -183,8 +182,8 @@ Game Boy Development |
---|---|---|---|---|---|---|
EWRAM | -0200:0000h |
- 0203:FFFFh
+ | 0x02000000 |
+ 0x0203FFFF
|
256KB | 16 bit
@@ -198,8 +197,8 @@ Game Boy Development |
IWRAM | -0300:0000h |
- 0300:7FFFh
+ | 0x03000000 |
+ 0x03007FFF
|
32KB | 32 bit
@@ -212,8 +211,8 @@ Game Boy Development |
IO RAM | -0400:0000h |
- 0400:03FFh
+ | 0x04000000 |
+ 0x040003FF
|
1KB | 16 bit
@@ -226,8 +225,8 @@ Game Boy Development |
PAL RAM | -0500:0000h |
- 0500:03FFh
+ | 0x05000000 |
+ 0x050003FF
|
1KB | 16 bit
@@ -238,8 +237,8 @@ Game Boy Development |
VRAM | -0600:0000h |
- 0601:7FFFh
+ | 0x06000000 |
+ 0x06017FFF
|
96KB | 16 bit
@@ -252,8 +251,8 @@ Game Boy Development |
OAM | -0700:0000h |
- 0700:03FFh
+ | 0x07000000 |
+ 0x070003FF
|
1KB | 32 bit
@@ -264,7 +263,7 @@ Game Boy Development |
PAK ROM | -0800:0000h |
+ 0x08000000 |
var | var | @@ -278,7 +277,7 @@||
Cart RAM | -0E00:0000h |
+ 0x0E000000 |
var | var | @@ -294,44 +293,10 @@
- Our goal now is to get a sprite displayed on the screen. This may sound simple... however... there is a lot you need to know first. I will begin by explaining how sprites are structured into Tiles and Palettes. Then I will explain the memory registers (VRAM, PAL RAM, and OAM) where sprite data/attributes are stored. Then I will explain the display register REG_DISPCNT which is the primary control of the screen. This is going to be a long "spit facts at you" section, but its pretty vital to your understanding of the code. After this, I promise we can start programming. If you don't care how anything works you can skip this section. -
- -- The GBA CAN represent graphics in a bitmap mode (meaning to plot a pixel at location (x,y), go to location y*w+x and - fill in the color). You COULD make games based purely on the bitmap modes, but very few do (DOOM does). Unless - you're willing to optimize the crap out of your code, you'll have a hard time doing it. The vast majority of games - use the GBA's hardware graphics, which come in the forms of tiles and palettes. -
- -- Sprites are smaller objects between 8x8 to 64x64 pixels in size. The GBA allows for 128 sprites and you can - move them independently of each other. Sprites are composed of a matrix of smaller bitmaps called tiles. Your - basic tile is an 8x8 pixel bitmap. Tiles come in two types: 4BPP (bits per pixel) and 8BPP. The - sprites I made don't require more than 16 colors so I will use 4BPP mode. -
- -
- 8BPP - 256 Colors (28), 64 bytes per tile, 8 bits per pixel. Each pixel in a tile is an 8-bit
- index value of a 256 color palette.
-
- 4BPP - 16 Colors (24), 32 bytes per tile, 4 bits per pixel. Attribute 2 of the OAM data
- contains a 4 bit index into 16 distinct palettes which contain 16 colors values.
-
- I know that was a lot of information so lets look at some pretty pictures now. Consider the picture below of a - sprite that I made of my friend Will. This is a 32x32 pixel image (I scaled up by factor of 4 so that I can - demonstrate tiles and palettes). The first thing we do is break the image into a tilemap. The tilemap should - consist of 16 tiles each containing 8x8 pixels. Also note that the sprite consists of just 8 unique colors - (because Will is just a simple little guy). This set of colors is known as the palette. The first color in - the palette is considered to be transparent (meaning it wont be rendered). + Images on the GBA are composed of tiles and palettes. The palette is simply a list of the colors used in an image. The pixel values in the image are indices of the palette. The image is broken into 8x8 pixel segments called tiles. The tiles form a bitmap of the image.
- The tilemap is 16x8x8 = 1024 pixels total. Each pixel contains a 4 bit index of the color palette. This means the - sprite is 1024 * (4/8) = 512 Bytes. The color palette has 8 colors. Each color is a 16 bit value (using 5 - bits for red, green, and blue, and ignoring last bit). Meaning the palette takes up 8x(16/8) = 16 Bytes. So, total - storage of the sprite is 512+16 = 528 Bytes. We can reduce this by noticing some of the tiles are identical - (completely black) so there is no need to store them twice. Im not going to do this just to keep things simple but - its an option if you're feeling spicy. - -
- F E D C B A 9 8 7 6 5 4 3 2 1 0 - X B B B B B G G G G G R R R R R - - 0-4 (R) = Red - 5-9 (G) = Green - A-F (B) = Blue -- - - -
- None of this really matters until you put the 512B of tile data and 16B of palette data into memory. Now we get to - touch some code! Lets start with the tiles first. We need to put the tiles in VRAM which starts at 0x06000000 - and is 96KB long. Tonclib breaks the VRAM into charblocks. Each charblock is 16KB, so there is 6 charblocks - in VRAM. The first 4 are for background tiles and the last two are for sprite tiles. This means two things: we can - have (16KB*2)/32B = 1024 Sprite Tiles, and sprite tiles are stored 16KB*4 = 64KB (0x10000) into VRAM. In - other words: sprite tiles start at 0x06010000. So to get the sprite tiles into memory we need to copy all - 512B to 0x06010000 which is known as tile_mem_obj in tonclib. + All colors on the GBA are represented as a 16 bit value, using 5 bits for red, green, and blue, and ignoring bit 15. A color is represented as a u16, and a palette is an array of u16.
- ++ F E D C B A 9 8 7 6 5 4 3 2 1 0 + X B B B B B G G G G G R R R R R +
- Palettes are stored in PAL RAM which starts at 0x05000000 and is 1KB long. PAL RAM is split into two even - sections. The first for backgrounds and the second for sprites. So we have 512B for sprite palettes which starts at - 0x05000000+512B = 0x05000200. Since each color is 16 bits (2B) that means we have room for 512B/(2B/Color) = 256 - Colors. In 4BPP Mode this means we have 16 palettes each with 16 colors (16*16 = 256). So to get the sprite palette - into memory we need to copy all 16B to 0x05000200 which is known as pal_obj_mem in tonclib. + Palettes are stored in PAL RAM which starts at 0x05000000 and is 1KB long. PAL RAM is split into two even 512B sections (0x200). The first section is used for backgrounds and the second for sprites. Since each color is 16 bits (2B) that means background and sprites both can have space for 512B/(2B/Color) = 256 Colors. Tonc has some useful types and macros for this:
+- The Object Attribute Memory (OAM) which starts at 0x07000000, tells the GBA how you want to draw each sprite (object). This register is a little complex so read the Tonc Text for more info. The basics you need to know is each sprite (or object) has 3 attributes. I'll provide some details for those below but you don't need to understand them fully for this blog post. + Tiles come in two types: 4bPP (bits per pixel) and 8bPP. The main difference is the number of colors your image has: 256 Colors (28) with 8bPP OR 16 Colors (24) with 4bPP. So basically you can have one palette of 256 Colors (each pixel in a tile is an 8-bit index value of a 256 color palette) OR you can have 16 palettes of 16 colors each (each pixel in a tile is an 4 bit index into 16 distinct palettes which contain 16 colors values). I like the format of 4bPP and most of my graphics don't require more than 16 colors so I only use 4bPP.
-Attribute 0 - y coordinate, and the shape of the sprite.
-define | -description - | -
---|---|
ATTR0_Y# - | -Y coordinate. Marks the top of the sprite. - | -
ATTR0_REG, ATTR0_AFF, ATTR0_HIDE, ATTR0_AFF_DBL. - ATTR0_MODE# - | -(Affine) object mode. Use to hide the sprite or govern
- affine mode.
-
|
-
ATTR0_BLEND, ATTR0_WIN. ATTR0_GFX# - | -Gfx mode. Flags for special effects.
-
|
-
ATTR0_MOSAIC - | -Enables mosaic effect. - | -
ATTR0_4BPP, ATTR0_8BPP - | -Color mode. 16 colors (4bpp) if cleared; - 256 colors (8bpp) if set. - | -
ATTR0_SQUARE, ATTR0_WIDE, ATTR0_TALL. ATTR0_SHAPE# - | -Sprite shape. This and the sprite's size
- (attr1{E-F} ) determines the sprite's real size
- |
-
Attribute 1 - x coordinate and the size of the sprite.
-define | -description - | -
---|---|
ATTR1_X# - | -X coordinate. Marks left of the sprite. - | -
ATTR1_AFF# - | -Affine index. Specifies the OAM_AFF_ENTY this
- sprite uses. Valid only if the affine flag
- (attr0 {8}) is set.
- |
-
ATTR1_HFLIP, ATTR1_VFLIP. ATTR1_FLIP# - | -Horizontal/vertical flipping flags. Used only if
- the affine flag (attr0 ) is clear; otherwise they're
- part of the affine index.
- |
-
ATTR1_SIZE# - | -Sprite size. Kinda. Together with the shape bits
- (attr0 {E-F}) these determine the sprite's real size
- |
-
Attribute 2 - control which tiles/palettes to display and its background priority.
- -define | -description - | -
---|---|
ATTR2_ID# - | -Base tile-index of sprite. Note that in bitmap modes - this must be 512 or higher. - | -
ATTR2_PRIO# - | -Priority. Higher priorities are drawn first (and therefore
- can be covered by later sprites and backgrounds). Sprites cover
- backgrounds of the same priority, and for sprites of the
- same priority, the higher OBJ_ATTR s are drawn first.
- |
-
ATTR2_PALBANK# - | -Palette-bank to use when in 16-color mode. Has no effect if
- the color mode flag (attr0 {C}) is set.
- |
-
- tonc provides these attributes in a useful struct defined below. We will discuss how to use this later. + Tiles are stored in VRAM which starts at 0x06000000 and is 96KB (0x18000) long. VRAM is divided into segments called charblocks. + + Each charblock is 16KB (0x4000 bytes) long, giving 0x18000/0x4000 = 6 blocks in VRAM. + + The first 4 charblocks (0-3) are for background tiles and the last two (4-5) are for sprite tiles. This means that sprite tiles are stored 6KB*4 = 64KB (0x10000) into VRAM. In + other words: sprite tiles start at 0x06010000.
- - -- Finally, our last topic in this section. The REG_DISPCNT register is the primary control of the screen. This register is the first 15 bits of the IO RAM. The bit-layout of this register and their meanings can be found in the following table. I will highlight the ones we are most concerned with. + Each tile is 8x8 pixels. Each pixel in (4bPP mode) is 4 bits. Which means that every tile is 32 bytes (0x20). This means we can have 0x18000/0x20 = 3072 total tiles. We can divide that by 6 to find we can have 512 tiles per charblock.
-bits | -name | -define | -description
+
- By setting the bits for DCNT_OBJ, we can enable objects (sprites) to be seen. We need to also tell the GBA how the sprite tiles are stored. We can do this by setting the bits for DCNT_OBJ_1D. In 1D mode, tiles are stored sequentially. So the sprite I showed earlier would actually be stored in memory like this: + Tonc stores tiles as an array of 8 u32, and defines a charblock as an array of 512 tiles. You can view those types and macros below: -
-
-
+
+
+
-
+
- To create our sprite, we first need to draw it. I prefer to use photoshop for this but there are many tools available and you cant really go wrong. Just remember your sprite must be a set size. I will list the possible sizes below. For simplicity I will use the same 32x32 pixel sprite of our good pal Will from the previous section. + So remember how I was just saying VRAM is divided into 16KB chunks called charblocks and the first 4 charblocks are for background tiles? Yeah that's not exactly the whole story. VRAM is actually divided into both charblocks and screenblocks. Each screenblock is 2048 bytes (0x800) long, giving 32 screenblocks in the first 4 charblocks. Charblocks and screenblocks use the same addresses in memory. This means they overlap. -
- Now that we have our sprite image, we need to use a tool called Usenti to convert it to C code. I will provide the original Will sprite image below for your use. - - -
-
-
-
- - Simply download this file, and open it in Usenti. You should see a window that looks like this: - - -
-
-
-
-
- You will first want to make sure you have the correct number of colors by selecting
- Don't panic this is actually really simple. We want to select basically all the options in the screenshot. First select
-
-
-
-
+ // from tonc_types.h
+ typedef u16 SCR_ENTRY, SE;
+ typedef SCR_ENTRY SCREENBLOCK[1024];
-
+ // from tonc_memmap.h
+ #define MEM_VRAM 0x06000000
+ #define se_mem ((SCREENBLOCK*)MEM_VRAM)
+
-
-May 7, 2023+Game Boy Development++ The goal of this project is to develop programs for the Nintendo Game Boy Advance using the C programming language. + The Game Boy Advance (GBA) comes in several different variations with similar hardware. Being released in 2003, the + Game Boy Advance SP was my first childhood handheld console. The GBA also has the benefit of being a very well + documented console so research on its hardware and development libraries such as "Tonc" are very accessible to the + average person. + + + + +
+
+
+
++ We will begin our development by first explaining how the development environment is setup. Because useful tools + such as "make" are not conveniently available in Windows, we will be using a linux distribution for our development. + I have chosen to use Windows Subsystem for Linux (WSL). WSL is a feature of Windows that allows developers to + run a Linux environment without the need for a separate virtual machine or dual booting. Additionally, we will need + a compiler that can turn C code into a GBA binary. For this I have chosen to use devkitPro, which is based on + the GNU compiler collection (GCC). We will also need a program capable of running our compiled binaries for testing + purposes. There are many options available for this, but I have chosen to go with mGBA. If you would like to + run your compiled code on actual hardware, I suggest purchasing a EZ-FLASH + OMEGA from a source such as Amazon. Additionally, a useful programming library that you can use is Tonc + v1.4.2. This library comes with very useful documentation that can be found here. + + ++ We will begin our setup by installing WSL. This + is a very simple process and only requires you to open Windows Command Prompt in administrator mode and enter the + following command: + + + + ++ The distro will default to Ubuntu. Now that WSL is installed we can launch it using this command: + + + + ++ We now need to setup a workspace directory. I created the following folder in WSL: + + + + ++ To make things simple you may clone the entire repo to this directory: + + + + ++ Now we will need to install devkitPro as mentioned earlier. + + + + ++ You may need to make a symlink for /etc/mtab. See https://github.com/Microsoft/WSL/issues/150. I did not need + to do this. + + + + ++ devkitPro provides libraries for many systems, however, we are only interested in the one used for GBA development. + To install this we will run the following (Install All): + + + + ++ Now we will need to define some environment variables so that we can reference devkitPro by name in our makefile. To + do so run the following commands: + + + + ++ Now open VSCode in Windows. In the bottom left corner, select the green button. Then select "Connect to WSL" and + File > Open the instillation folder. + + +
+
+
+
+ + Now open a terminal in VSCode and ensure you can build the project. + + + + ++ If you want to make your own project, simply create a new folder on the desktop and copy the Makefile I have created. You will also need to create a + source and include folder. The source folder must contain a main.c file with a int main(){} function. + + + +
+
+
+
+
++ On the GBA, specific areas of memory are mapped directly to hardware functions. To get the GBA to do anything, you + need to write specific bits to specific sections of memory. This is known as memory-mapped IO. This means if + we want to create some sprites we need to understand the register layout of the system memory. Luckily the hardware + is well documented in the Tonc Text and the CowBite Spec. Disclaimer: Hardware + documentation for this blog post are mostly summarized from theses sources. You really should read those for a full + understanding. However, I know you're here because you don't want to do that. So, I will summarize the content you + need to know for rest of this blog post. This is where the fun begins... + + ++ Below is a table of the important sections of memory. We will mostly be focused on the ones highlighted since they + control video output. + + +
+
+
+
+
++ Our goal now is to get a sprite displayed on the screen. This may sound simple... however... there is a lot you need to know first. I will begin by explaining how sprites are structured into Tiles and Palettes. Then I will explain the memory registers (VRAM, PAL RAM, and OAM) where sprite data/attributes are stored. Then I will explain the display register REG_DISPCNT which is the primary control of the screen. This is going to be a long "spit facts at you" section, but its pretty vital to your understanding of the code. After this, I promise we can start programming. If you don't care how anything works you can skip this section. + + +Tiles and Palettes+ ++ The GBA CAN represent graphics in a bitmap mode (meaning to plot a pixel at location (x,y), go to location y*w+x and + fill in the color). You COULD make games based purely on the bitmap modes, but very few do (DOOM does). Unless + you're willing to optimize the crap out of your code, you'll have a hard time doing it. The vast majority of games + use the GBA's hardware graphics, which come in the forms of tiles and palettes. + + ++ Sprites are smaller objects between 8x8 to 64x64 pixels in size. The GBA allows for 128 sprites and you can + move them independently of each other. Sprites are composed of a matrix of smaller bitmaps called tiles. Your + basic tile is an 8x8 pixel bitmap. Tiles come in two types: 4BPP (bits per pixel) and 8BPP. The + sprites I made don't require more than 16 colors so I will use 4BPP mode. + + +
+ 8BPP - 256 Colors (28), 64 bytes per tile, 8 bits per pixel. Each pixel in a tile is an 8-bit
+ index value of a 256 color palette.
+ + I know that was a lot of information so lets look at some pretty pictures now. Consider the picture below of a + sprite that I made of my friend Will. This is a 32x32 pixel image (I scaled up by factor of 4 so that I can + demonstrate tiles and palettes). The first thing we do is break the image into a tilemap. The tilemap should + consist of 16 tiles each containing 8x8 pixels. Also note that the sprite consists of just 8 unique colors + (because Will is just a simple little guy). This set of colors is known as the palette. The first color in + the palette is considered to be transparent (meaning it wont be rendered). + + +
+
+
+
+
+
+
+
+
+ + The tilemap is 16x8x8 = 1024 pixels total. Each pixel contains a 4 bit index of the color palette. This means the + sprite is 1024 * (4/8) = 512 Bytes. The color palette has 8 colors. Each color is a 16 bit value (using 5 + bits for red, green, and blue, and ignoring last bit). Meaning the palette takes up 8x(16/8) = 16 Bytes. So, total + storage of the sprite is 512+16 = 528 Bytes. We can reduce this by noticing some of the tiles are identical + (completely black) so there is no need to store them twice. Im not going to do this just to keep things simple but + its an option if you're feeling spicy. + + + F E D C B A 9 8 7 6 5 4 3 2 1 0 + X B B B B B G G G G G R R R R R + + 0-4 (R) = Red + 5-9 (G) = Green + A-F (B) = Blue ++ + + + VRAM, PAL RAM, and OAM+ ++ None of this really matters until you put the 512B of tile data and 16B of palette data into memory. Now we get to + touch some code! Lets start with the tiles first. We need to put the tiles in VRAM which starts at 0x06000000 + and is 96KB long. Tonclib breaks the VRAM into charblocks. Each charblock is 16KB, so there is 6 charblocks + in VRAM. The first 4 are for background tiles and the last two are for sprite tiles. This means two things: we can + have (16KB*2)/32B = 1024 Sprite Tiles, and sprite tiles are stored 16KB*4 = 64KB (0x10000) into VRAM. In + other words: sprite tiles start at 0x06010000. So to get the sprite tiles into memory we need to copy all + 512B to 0x06010000 which is known as tile_mem_obj in tonclib. + + + + ++ Palettes are stored in PAL RAM which starts at 0x05000000 and is 1KB long. PAL RAM is split into two even + sections. The first for backgrounds and the second for sprites. So we have 512B for sprite palettes which starts at + 0x05000000+512B = 0x05000200. Since each color is 16 bits (2B) that means we have room for 512B/(2B/Color) = 256 + Colors. In 4BPP Mode this means we have 16 palettes each with 16 colors (16*16 = 256). So to get the sprite palette + into memory we need to copy all 16B to 0x05000200 which is known as pal_obj_mem in tonclib. + + + + ++ The Object Attribute Memory (OAM) which starts at 0x07000000, tells the GBA how you want to draw each sprite (object). This register is a little complex so read the Tonc Text for more info. The basics you need to know is each sprite (or object) has 3 attributes. I'll provide some details for those below but you don't need to understand them fully for this blog post. + + +Attribute 0 - y coordinate, and the shape of the sprite. +
+
+
+
Attribute 1 - x coordinate and the size of the sprite. +
+
+
+
Attribute 2 - control which tiles/palettes to display and its background priority. + +
+
+
+
+ tonc provides these attributes in a useful struct defined below. We will discuss how to use this later. + + + + +REG_DISPCNT+ ++ Finally, our last topic in this section. The REG_DISPCNT register is the primary control of the screen. This register is the first 15 bits of the IO RAM. The bit-layout of this register and their meanings can be found in the following table. I will highlight the ones we are most concerned with. + + +
+ By setting the bits for DCNT_OBJ, we can enable objects (sprites) to be seen. We need to also tell the GBA how the sprite tiles are stored. We can do this by setting the bits for DCNT_OBJ_1D. In 1D mode, tiles are stored sequentially. So the sprite I showed earlier would actually be stored in memory like this: + + +
+
+
+
+ + In code, setting these bits is really easy thanks to the tonclib library. We simply need to do the following: + + + +
+
+
+
++ To create our sprite, we first need to draw it. I prefer to use photoshop for this but there are many tools available and you cant really go wrong. Just remember your sprite must be a set size. I will list the possible sizes below. For simplicity I will use the same 32x32 pixel sprite of our good pal Will from the previous section. + + +
+ Now that we have our sprite image, we need to use a tool called Usenti to convert it to C code. I will provide the original Will sprite image below for your use. + + +
+
+
+
+ + Simply download this file, and open it in Usenti. You should see a window that looks like this: + + +
+
+
+
+
+ You will first want to make sure you have the correct number of colors by selecting
+
+
+
+
+ Don't panic this is actually really simple. We want to select basically all the options in the screenshot. First select
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
|
---|