Feed on
Posts
Comments

From our previous part, the PCBs arrived and I started testing games. There were a few that didn’t work and was able to get some to work by adding some MBC1 support via a few little detection hacks. This part, we’ll look at how to add Multi-game support plus use GBDK to create our game selection screen loader.


(sneak peak of loader screen)

Multi-game support is pretty useful if you have a lot of little games that are 32-256KB but don’t want to have a cart just for each of them. They might also be games that don’t need to save because it can be very easy to load the wrong game and it could potentially overwrite the SRAM.

The way multi-game support works is once you select a game, it send either the bank or the memory location of the game to the custom MBC via an address that wouldn’t likely be written to, the ROM bank is changed, the Gameboy is soft reset and then the game loads.

Just to test the concept, we can append one 32KB game to another 32KB game, flash it to the cart and then add a rom bank adjustment variable for multi-carts. If we change the variable to be 2, it will select bank 2 of the flash chip when the Gameboy asks for bank 0. When it asks for bank 1, we simply add the romBankMulti variable to the rom bank the Gameboy asked for; we are always adjusting the rom bank to be 2 banks higher than what it asks for in this case.

Now we just need to make that rom bank adjustment variable configurable so what’s the best address for us to send the bank command to our CPLD? We also need to consider that users may not use the multi-game loader so it shouldn’t conflict with normal cart operation.

The best address seems to be 0x6000 to 0x7FFF, as long as we don’t conflict with the MBC1 ROM/RAM mode detection, we won’t be using bank 0 anyway as that’s where the loader will be and we just won’t allow bank 1 to be used.

MBC3 carts also use this address for the RTC but I’m hoping that since there is no RTC that perhaps it won’t try to write anything useful. But what if for some reason, MBC3 wrote to the RTC anyway, it could accidentally select a bank to switch to whilst in the game. What we could do is have a 2 stage bank loader, firstly we send a special byte to an address to unlock the multi-game support and then another byte at another address for the bank to switch to.

Firstly we have our 2 bit variable for the stage we are at, when at stage 0, we listen for the byte 0x65 at address 0x7000-7FFF which then changes the stage to 1. We then listen for the address 0x6000-6FFF and read the data as our multi-game bank variable. We change the stage variable to be 2 so that we won’t trigger the stage 0/1 sections again and for good measure we reset the romBank to 1 and turn off MBC1 0x3000-3FFF support (as later on I found that GBDK creates MBC1 ROMs and one of the first things it does is switch to bank 0 which turns on the 0x3000-3FFF detection code I have).

But wait, now we have a new problem, when we write to the flash cart, we get stuck at around 0x7000-7FFF because the special byte 0x65 would almost certainly be in ROM bank 1 or higher within games, so it’s switching the bank to somewhere else. Now we need to detect if a write is coming from a flash cart or from the Gameboy.

For the AM29F016B flash chip, it writes to the address 0x555 with byte 0xAA at the start, lucky for us, that’s the RAM enable address which normally won’t ever see 0xAA by a Gameboy, so we can simply detect for 0xAA (I’m only checking for 7 bits so it’s really 0x2A for me).

Now we can add a bit more protection to our multi-game bank switching, firstly if MBC1 0x3000-3FFF is on, then we know it’s likely an MBC1 ROM as mentioned before, we can check if writing has been detecting, if it has we won’t allow for multi-game bank switching and lastly if the stage is either 0 or 1, plus the usual WR low, RD high and CS high.

Another problem emerged, the cart wouldn’t work in GBxCart RW, I couldn’t read back the ROM. I started playing around and found that the cart needed to be reset (reset pin low for a few milliseconds) and then it would always work fine. I’m guessing since I have a 10K resistor going from VCC to RST on GBxCart RW and with the new extra logic I added to the CPLD, there may not be enough time to initialise the CPLD and before it can detect the reset line is low it’s already too late. Writing to the flash cart now works without any issues!

Multi-game loader using GBDK

Now it’s time for making our Multi-game loader using GBDK. I hadn’t used GBDK before and started playing around with it for a few days, trying out the simple examples, having a sense of how the basics works, printing text, key press detection, moving the printf location and lastly how to link ASM files to the C code it uses so we could potentially do things on our own, luckily there was an example but it still took a little bit to work my way through it.

The loader needs to be written in such a way so that’s it’s dynamic, so users don’t need to re-compile with GBDK to add their ROMs to it. When playing around with GBDK, it seems to create a 32KB file but only uses about 0x2100 of it in my case. What if we could have the loader read data back from it’s own ROM file at say 0x3000? I could add the game titles, the bank numbers and the number of games in that location as the rest of the file is just all 0xFFs (blank/not used).

Adding ASM to your C file in GBDK is relatively simple, first you specify the function in the C file and it expects a 16 bit number (WORD).

In the ASM file, you use the same function name but with an underscore at the start. The first few commands are from the example, when GBDK switches over to ASM it transfers the number we sent to the stack so here it loads those values back to DE from the stack. DE can be broken up so D is the high 8 bits and E is the low 8 bits.

We load the two values DE back to HL and by using the brackets around (HL) to tell it to read that location and put the 8 bit data it read back into E. We load D with 0 as we only want to read the 8 bits LSB (E) and return which returns the result DE back to our C code.

We can now access it like above, give it the address to read and store the result back.

I think I’ll store the total number of games in 0x2FFF to align everything nicely to 0x3000 which will store the bank number of a game and the next 15 bytes will be the game title, 0x3010 will be the bank number of the next game and so on.

After a bit of playing around, we can check if games were detected and list them out one by one and have an arrow to show which one is selected.

The last thing remaining is the code to unlock the multi-game cart mode and specify the bank, it starts off the same as the other ASM code so we’ll skip that part. We can just load any value we like to register A by using hash before the number (#0x65) and then we output the 0x65 to the address location 0x7000. Next we grab the lower 8 bits (E) of the bank value we received (because we won’t ever need more than 8 bits in my case), load that to register A and then put that at 0x6000. Lastly after playing around with where we should jump to for a soft reset, it appears 0x100 is the best place (BGB also starts there when you reload a ROM file).

Once the A button is pressed, we read the multi-game bank number from the location corresponding to the game selected and load that value to our bank switching code.

However, we’re not actually done, when we initiate the last bank switch command, it will switch banks straight away and not actually give us enough time to soft reset the Gameboy. When testing it, it would just hang (later on I found that it could have been a problem with the game I used “Tennis”, it doesn’t work with the working solution either).

One solution might be to add another stage which would activate the bank switch in the CPLD when the ROM was read next or another method could be to run the commands from bank 1, switch bank 0 out and then switch bank 1 out too.

In the middle of this I was helping a user who was trying to write to a clone Multi-game cart, it seemed to stop around 0x7002, interesting, that’s the same sort of problem I had so I had a ROM dump of the cart before they tried flashing it.

For that cart, it looks like it divides a 24 bit number into 3x 8 bit chunks and sends each chunk to a different location, 0x7000, 0x7001 and 0x7002, looks like they can read all the address lines which is handy. In the above example, they point to the ROM address 2,154,640 which is just over 2MB by writing 0x20, 0xE0, 0x90 (not shown in picture above) and this number changes when loading the other games too. After that they load bank 1 to the lower 8 bits of MBC5, zero out the high bit then jump to 0x100.

Edit 10 Sep 18: Upon further analysis, it appears that it isn’t a 24 bit number, the first and second number looks to somehow make the bank number and the last number stays the same for each 4 games, maybe some sort of 6 or 8MB boundary that separates them, kind of odd.

So it looks like the sort of thing I’m doing, they just have a few no operations (nops) along the way just in case, so strange that it works for them. However when looking at it again, the most surprising thing is that they are executing these commands in HRAM!

The area they are writing to is the High RAM which from the Gameboy Memory Map is supposed to be used for stack space or quick RAM access. It makes sense now, you load the commands you want in the RAM, jump to the RAM and start executing from it and then you are free to change the banks as you wish and the soft reset will still be executed. Once a soft reset occurs, I believe it should clear out the most of the memory locations.

The issue now is, we can’t actually write our commands to HRAM like we can for the ROM, what we have to do is get the actual ASM instructions byte codes for our existing commands and write those bytes to the HRAM so they will be parsed as our commands.

Above is the actual ASM instructions for the commands we issued, so we now have to write 0x3E then 0x65 somewhere in the HRAM which would correspond to ld a,65 when it executes.

Here’s a few commands to show you how they look like, after we write the commands to the HRAM we then jump to the start of the HRAM with our code and it starts executing them, nice!

When running in BGB as you can see it has the right commands as before, except now we’re executing from the HRAM. I tested appending the Tetris game and it all worked!

Although some games don’t work, they either don’t refresh the screen or have a white screen, seems like 80% of games work. Those that don’t work, do work normally when the game is flashed as a single game to the cart, for example, like Tennis.


I looked into it a bit more and the Gameboy Manual provides the default register and I/O states so I went ahead and implement this in my code, unfortunately it didn’t make any difference but I’ll leave it in for good measure; the clone multi-cart game doesn’t seem to reset the registers or I/O (but they could be doing it elsewhere).

Edit 10 Sep: I was playing around with BGB’s “Load ROM without reset” function and eventually found that I wasn’t resetting the 0xFFFF (IE) register to 0x00 (was 0x09), now the Tennis game works! I have a list of non-working multi-game cart games and only a handful of them work now.

Edit 15 Sep: It appears the loader only works for Gameboy (non-colour) games so I’ve made another loader just for Gameboy Colour games.

Download insideGadgets_Multi-Game_Loader_v1.1_GB or insideGadgets_Multi-Game_Loader_v1.1_GBC

Multi-Game Cart Maker

Now it’s time to make a simple program which will do the calculating of banks, reading of game title, appending the games and provide us a single output ROM file.

Firstly we just calculate the file sizes of all the ROMs that were dragged and dropped (it has to be done all at once), check if the output rom file will be too large and then copy the base ROM file to our output file.

I’ve manually cut off everything after the 0x2FFE location in the base rom file so that we can just append the games count and game titles to the output file.

We just open up each game, read and write the game title, write the rom bank we are up (we start off with bank 2), calculate the rom banks for the game and add that to the rom bank we are up to. A 32KB game has 2 rom banks, so after that game, we’re up to rom bank 4 for the next game.

Once everything has been appended to the output rom file, we can calculate the remaining 0xFF’s to add into that file to make it end at 0x7FFF and then we append all the game roms to that output file.

Download insideGadgets_Multi-Game_Cart_Maker_v1.0

Here’s a video of the end result, works good.

There are a few features which could be added, some of them would be interesting to play around with:
– Save game manager on the loader (could allow you to save or load your save game to a section on the flash chip)
– A feature to prevent you from loading a different game if you have a save in another game
– Most games only use 8-32KB of SRAM, could allow for switching of ram banks so you can play multiple games that use saves at any time without having to backup your save before hand
– A GUI version of the Maker, drag and drop, sort games, change game titles, etc
– The loader could send another special command to the CPLD to tell it what MBC type the game is

Parts:
Part 1: CPLD as the MBC and adding Flash as our ROM
Part 2: Adding the SRAM
Part 3: PCBs arrived, Adding some MBC1 support and troubleshooting a few games
Part 4: Adding Multi-game support
Part 5: Using 32KB FRAM and Adding MBC1 2MB ROM Support

Leave a Reply