Feed on
Posts
Comments

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

Leave a Reply