Zalo DS Blog

Thursday, July 28, 2016

Game Boy Development - Tips and Tricks (I)

As promised here is the first post with some advices based on my experience programming for the Game Boy during the Bit Bit Jam 3.

1. Choosing your SDK

The Gameboy has some different SDks you can choose to work with. The two main ones are the GBDK and the RGBDS. Choosing between any of them depends on what you want to accomplish and what skills you have. Check the next table with pros and cons of each one

C and asm asm (better than asm in GBDK*)
Slower if you use C Asm is always faster but requires experience
Faster to write programs in C Assembly is very low level, requires more time
Reliable for a jam If you are not an skilled programmer you might not finish your jam entry
Horrible SDK for learning C because of the 8bits limitations, no debugger.... Perfect for learning asm. You can even use a debugger with the BGB emulator 

With all these in mind, I chose GBDK because I have a lot of experience with C and C++ and I was feelling more confident to finish the game with it.

*so I've heard, because I didn't have the chance to try it

2. Getting your environment ready

I will suggest spending some time doing this, it can save you from lot of problems that will happen that last hour before sending the game. Code completion, launching the emu after compiling and the ability to add or delete files from your project easily are the things you should focus to get before starting your project.

A couple of weeks before the jam I started playing with GBDK and getting my development environment ready. Same as I did for the 3ds, Ds and the Wii I started configuring visual studio and some makefiles (see this post for more info). Most of the tutorials I followed along the internet were using some dos .bat files but I was looking for something more general. I created a makefile that:

- Compiles any source found under the folder src, so you don't have to specify the list of files to compile

- Parse the extension of the file to select the bank (anything.c will be on bank0, anything.b1.c will be on bank1, anything.b2.c on bank 2 and so on...)

- Automatically changes the unsigned char of the resources exported by GameBoy Map Builder and GameBoy Tile designed to const unsigned char

And some other features that you can check here. The important thing is having a confortable environment so you only have to worry about coding the videogame

If you are under Windows you will need a way to run makefiles. Since I have been using devkitpro for a long time I already had msys installed. One funny thing that I noticed is that the version of make that comes with devkitpro doesn't display any compilation errors so I guess the stderr is not properly configured. If you have the same problem you just need to download the last version of make for windows here

3. Banks

I would recommend reading the full documentation of the GBDK prior to any development. And specially trying to understand banks, what they are and how they work. AntonioND explains it pretty well on this post. The idea is very simple. If you take a look at the size of variables of the GBDK you'll see that the size of a pointer is 2 bytes. The maximum address you can point to with 2 bytes is 65535 (2^16). In these addresses the gameboy needs to get access to everything, not only the rom. Basically it needs 32kb for internal registers: sound, video, power, etc (you can find the complete list here if you are curious) and then there are other 32 kb for rom access(the cartdridge data). These 32kb are splitted into two: a fixed part that is always accesible (called bank0) and a swapable part (by default set to bank1). You can change the starting address of bank1 and make it point to addresses beyond 65535. Each group of 16386 (16kb) is known as a bank

So the idea is splitting your code along any of these banks and before excuting any part of it swapping the start address so the code can be reached. Take some time to understand this because it is extremely important. And also pay attention to the next things:

- You can only swap banks from code that is stored in bank0. Actually you can swap banks from any bank if you dare but your program will crash and it has an easy explanation. In any computer no matter how big or small it is there is a register (usually known as EIP) that contains the address of the next instruction to be executed. If no jump occurs then this register will automatically advance to the next register and execute the next instruction and basically that's how programs run. So, what will happen if you are in bank2 for example and swap to banks3? The EIP will execute the command to change the bank (in bank2) then it will advance to the next register and will try to execute the next command. The problem is that this comand that was supposed to belong to bank2 now belongs to bank3 and it can be literally anything. An easy solution to this is calling a function on bank0 (always accesible) that swaps banks, do the job on that new bank and then before returning swap banks back to bank2

- Lcc errors. You will eventually find a message like this from the compiler "warning : possibly wrote twice at addr 407a (21>21)". This will happen everytime you write code larger than 16kb and part of it ends in addresses that belong to the next bank. When this happens you should start moving your code to another bank

- Try to fill all banks with some info. If you have decided your game is gonna take 4 banks then the size of the game will be 64kb (16kb per bank) event if you don't fill them. Because of this it is a good idea to put some info in each bank from the beginning, otherwise you won't see the message I was telling you before when a bank gets full and your program will randomly crash at some point

- A stack can be very helpful to deal with bank swapping. Implementing a stack is very simple, you can check my implementation here. Once you have a stack implemented you can use it to move between banks like I do here. Everytime I want to change to a new bank I call PUSH_BANK and when I finish I just call POP_BANK so it automatically leaves me on the previous bank. Using this technique I was able to call gbt_update that automatically changes the bank to bank1 on my vblank interruption and then return to the bank I was previously working on calling REFRESH_BANK, fixing the issue AntonioND is stating on this post

4. Basic Maths

The Gameboy isn't very powerful and it can't even deal with multiplications or divisions, so you should avoid them at all cost (you can use them, but the performance will suffer). Most of these multiplications and divisions can be replaced by simpler operations, like:

- instead of multiplying by any power of 2 use the shift left operator (<<). Specially for multiplying by 2 use x << 1

- instead of dividing by any power of 2 use the shirft right operator (>>). Specially for dividing by 2 use x >> 1

- instead of the module operator (%) with any power of 2 you can do a mask. For example for getting the module 8 of a number do x & 0x7 (7 is 111 in binary so using & here turns everything except the last 3 digits into 0 which is what we want)

- the gbdk manual encourages you to use unsigned values. Take into account that these values are always bigger than 0. So doing something like 3 - 4 will result in an overflow giving you 255 (or 65535 if you are using 16bits) and checking if this is < 0 will unexpectecly return false. The best way I found to deal with this is masking against 0x8000 (1000 0000 0000 0000 in binary) and checking that the result is 0 (that 1 in the highest bit will only happen after an overflow)


Post a Comment

<< Home