In part 4, we looked at adding Multi-game support to our Gameboy cart however I’ve just recently found that we can’t play GBC games using it because the loader runs in Gameboy mode so when we jump back to 0x100 to soft reset, the Gameboy still thinks it’s in Gameboy mode.
Some GBC games like Super Mario Bros. Deluxe come up with a screen saying it can’t be run.
I tried to compare the difference in registers and I/O when starting a game in regular GB mode and GBC mode but apart from the A register and a few others if I changed those registers before trying to boot SMBD but we get some corrupted tiles.
I found that in GBDK you can also program in GBC mode, so I played around with the “Color Bar” example, removed a few parts of it so that I could print text and basically copy the loader code into it. The text looks faded but SMBD boots fine.
But a GB game like Tennis doesn’t look good, something about the palettes aren’t right. If you change register A to 0x01, it doesn’t help much either.
I played around with palette 0 trying to get the colour close to the original GB, some parts of it look sort of normal but the characters aren’t good.
So I decided to cut my losses and just have 2 loaders, one from GB games and one for GBC games – Multi-Game_Loader_v1.1_GBC & Multi-Game_Loader_v1.1_GB
Around about this time, I had ordered a 22 in 1 Pokemon cart to try adding support for it for a user using GBxCart RW but something happened that I hadn’t noticed before when booting it up and selecting a game, it’s like the whole Gameboy would reset.
Interesting, I thought about how they could be doing that and if there was a way I could do that myself, it would solve having to change the registers and I/O back to default, you wouldn’t really need code execute in HRAM anymore and more importantly you don’t have to worry about switching anything for GB or GBC games as the whole system would reset like you powered up for the first time.
The easiest solution I could think of was just pulling the reset line low, I put a quick resistor to ground and the GB did reset without any issues, nice! Now the question is how can we do this with our CPLD without resetting the CPLD because if we do, then the bank we switched to would also be reset, so it would boot back into the loader.
After a bit of testing, I had a solution that seems to work, unfortunately as we need extra circuitry it won’t work on the v1.0 PCB of the flash cart without modifications. I’m very close to finishing the FRAM cart PCB so I’m going to add this functionality to it.
What we’ll do is have 2 RC filters and a resistor on the reset line. The first RC filter will take a longer time todischarge which will go to our CPLD reset line and another RC filter that takes a little bit quicker to discharge to a multiReset pin. I just came up with the 10K, 1uF and 0.1uF values because I was already using those on the flash cart board.
The idea is to have the CPLD pull the reset line low through the resistor (1.5K), after a certain amount of time the Gameboy will reset. The multiReset pin’s capacitor (100nF) will discharge first, we can have code detect that it’s low, then change the CPLD output from pulling the reset line low to high. The multiReset line will go back high before the CPLD reset capacitor has a chance to discharge too much thus keeping the state of all the CPLD registers.
always @ (reset or detectMultiReset or inputCE or inputRD or inputWR) begin if (!detectMultiReset) begin // Reset on first boot and when resetting the Gameboy ourselves resetGB <= 1'b1; // Turn off reset end if (!reset) begin // On first boot highAddress <= 8'b0; romBank <= 7'b1; ...
For the CPLD code, there isn’t too many changes to make, just add our detectionMultiReset pin to the always@ and when it’s detected as being low, we set the resetGB pin back to high.
// Second stage - Check address 0x6000-6FFF for the bank to switch to if (multiStage == 2'd1 && inputAddress == 4'd6) begin romBankMulti <= inputData; multiStage <= 2'd2; // Turn off any more multi-game bank changing romBank <= 7'b1; ramBank <= 4'b0; resetGB <= 1'b0; // Reset Gameboy end
Once we are in the loaders second stage, we can reset some of the variables like romBank and ramBank and then set the resetGB pin low which will reset the Gameboy.
After a quick test on the GBA and GBC it works! Doesn’t work for the GBP, when resetting the Nintendo logo comes back all black. Edit: It looks like the GBP needs us to hold the reset line low a bit longer than the others, I’ll play around with the R/C values.
One other thing I want to add to the loader is MBC type detection, so we can force the CPLD to be in a certain MBC mode when the Gameboy resets. It would be a good compromise to have the CPLD always be in MBC5 mode by default and then we can use the loader to switch to MBC1/3 because most of those games are under 2MB so now we can have even better game compatibility instead of having to try detecting which MBC mode we need to be using.
// First stage - Check address 0x7000-7FFF for data byte or 0x63 (MBC1) or 0x64 (MBC3) or 0x65 (MBC5) if (multiStage == 2'd0 && (inputData == 7'h63 || inputData == 7'h64 || inputData == 7'h65) && inputAddress == 4'd7) begin multiStage <= 2'd1; if (inputData == 7'h63) begin mbc1Enabled <= 1'b1; mbc3Enabled <= 1'b0; mbc5Enabled <= 1'b0; end else if (inputData == 7'h64) begin mbc1Enabled <= 1'b0; mbc3Enabled <= 1'b1; mbc5Enabled <= 1'b0; end end
Not too many CPLD code changes needed for this, for the first stage of the multi-game check, instead of just checking for 0x65 to enter multi-game mode, we can define 0x63 for MBC1, 0x64 for MBC3 and 0x65 for MBC5.
// 0x2000-3FFF - Low 7 bits of ROM Bank Number (Write Only) with little MBC1 detection hack if ((inputAddress == 4'd2 || (inputAddress == 4'd3 && (mbc1Enabled || mbc3Enabled))) && !inputWR && inputRD && inputCE) begin if (inputData == 7'd0) begin if (mbc1Enabled || mbc3Enabled) begin romBank <= 1'b1; end else begin romBank <= 1'b0; end end
The other part is the 0x2000-0x3000 bank switching, we just specify which MBC mode allows for 0x3000 detection and then if bank 0 is requested if it should give bank 0 or 1.
if (joypad() & J_A) { readCharLocation = 0x3000 + ((UWORD) arrowLocation * (UWORD) 16); bankSelect = readlocation(readCharLocation); mbcSelect = readlocation(readCharLocation+1); multibankswitch(mbcSelect << 8 | bankSelect); }
For the Multi-game loader, next to the bank number we switch to, we’ll just add the MBC mode to.
; Load 0x7000 with MBC type (0x63, 64, 65) to unlock multi-game mode LD a,D LD (0x7000), a NOP NOP NOP ; Load 0x6000 with bank value LD a,E LD (0x6000), a ; By this time the GB should have reset NOP NOP NOP NOP
And for the ASM file, we can take out all the HRAM code loading, execution, etc and just make it all simple now. Download Multi-Game_Loader_v1.2 & Multi-Game_Maker_v1.3. CPLD code available here: https://github.com/insidegadgets/Gameboy-MBC5-MBC1-Hybrid
Now all I have to do is re-test all the games that don’t work with the old loader and see how we go.
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
Part 6: Multi-game support upgrade and Adding MBC types to CPLD