Feed on
Posts
Comments

From our previous part, we looked at how to add Multi-game support to our cart. In this part, we’re going to switch out the 128KB SRAM for 32KB FRAM so that we no longer need a coin cell battery to keep our saves and also look at adding 2MB MBC1 ROM support; at the moment we are limited to 512KB MBC1 ROM support.

We’ll be using the Cypress 32KB FM18W08 as our FRAM chip which only costs $1-2 each from AliExpress. It has the usual CE, WE, OE lines and address/data lines like an SRAM chip does so it’s almost a drop in replacement.

The one thing that datasheet mentioned compared to SRAM is that the CE line needs to be strobed when you change the address, mainly due to the fact that the memory access needs a pre-charge which is performed when CE goes high for a certain amount of time.

When you read data from the memory cells to the internal buffer, the data from the memory cells is lost until you write it back, this is what the pre-charge is for (odd name for it), it writes the data from internal buffer to back to the memory cells so it’s a good idea to have a pull-up on the CE line (which I do already have). More information on F-RAM pre-charging can be found here.

With that out of the way, I built up another Gameboy cart, glued down the FRAM chip and wired it up.

Without modifying any of the existing CPLD MBC code, I decided to see how it went because in GBxCart RW I always pulse the CE pin for each address access, perhaps some games would work fine? I loaded up Pokemon Crystal with a save file and it worked without any issues.

After quick search to see which games may have been affected when users converted their SRAM based carts to FRAM, two games came up Super Mario Land 2 and Wario Land – Super Mario Land 3. These games seem to use part of the cartridge RAM as part of the system RAM so they might have been accessing it by keeping CE low during multiple reads/writes.

In Super Mario Land 2, the character model is corrupted and once you load a level the enemies models are missing. For Wario Land – Super Mario Land 3, it’s the same sort of story.

What we need to do is set the CE pin high after the Gameboy has finished each read/write to the FRAM even if it keeps CE low and since our RAM CE pin is controlled by the CPLD we have full control.

From the Gameboy typical timing diagram, the best place might be when the clock line goes high because that’s also when CE is supposed to go high (the Gameboy would keep CE low when doing multiple reads/writes).

if ((inputAddress == 4'hA || inputAddress == 4'hB) && ramEnabled && (!inputRD || !inputWR)) begin
	if (clock) begin
		ramCE <= 1'b1;
	end
	else begin
		ramCE <= inputCE;
	end
	highAddress <= ramBank;
end

In the CPLD code, I set the clock line as an input so when the Gameboy accesses the cart’s RAM, if clock is high we set the FRAM CE high and if clock is low we set the FRAM CE to be the same state as the Gameboy CE line like before which would be low.

After re-testing the 2 games, they both work fine, that was easy! I’ll need to test a fair bit of other games to make sure it’s all good. We can take out the CPLD code which allowed for switching above 32KB of RAM and we now have our MBC5/1 Hybrid 32KB FRAM code: GB MBC5-1 Hybrid – R1 (2MB ROM, 32KB FRAM). Testing current consumption compared to the SRAM cart, it’s about 5 – 7mA less which is good.

Adding MBC1 2MB ROM Support

I had wanted to add 2MB MBC1 support but the games that I had tried didn’t seem to above 512KB after a few seconds of game play so it would be difficult to see if they worked properly or not.

I picked up a copy of Yu-Gi-Oh! Duel Monsters as it’s a 1MB MBC1 game in the hope that it might switch past 512KB early plus I don’t think I’ve ever tried a MBC1 game past 512KB on GBxCart RW. Once it arrived, I tested it and read the ROM and RAM without issues. After loading the game to the flash cart, it didn’t work, just a white screen, nice now we try adding support for it.

Since MBC1 has a few un-usable banks, I wondered if that would mean we had to handle banks 20h, 40h, 60h differently, i.e redirect them to 21h, 41h, 61h.

So I opened up the rom file at 0x80000 (20h) and compared it to 0x84000 (21h) and they were exactly the same, that makes it simple, we don’t need to do any redirection.

// 0x6000-7FFF - MBC1 ROM/RAM Mode Detection
if ((inputAddress == 4'd6 || inputAddress == 4'd7) && !inputWR && inputRD && inputCE) begin
	mbc1Detect303FOn <= 1'b1;
	mbc1Detected607F <= 1'b1;
	mbc1RomRamSelect <= inputData[0:0];
end

The first thing we do is add a mbc1RomRamSelect register to detect if the mode has been switched to ROM or RAM mode.

MBC1 in ROM banking mode, it takes the 5 bits we select at 0x2000-3FFF and combines them with the 2 high bits at 0x4000-5FFF so we’ll have to do that too.

// 0x2000-3FFF - Low 7 bits of ROM Bank Number (Write Only) with little MBC1 detection hack
if (((inputAddress == 4'd2) || inputAddress == 4'd3 && mbc1Detect303FOn) && !inputWR && inputRD && inputCE) begin
	...
	// MBC1 handling of ROM size
	if (mbc1Detected607F && !mbc3or5Locked) begin
		if (mbc1RomRamSelect) begin // 32KB RAM mode (not supported)
			romBank <= inputData[4:0];
		end
		else begin // 2MB ROM Mode
			romBank <= (romBank & 7'h60) | inputData[4:0];
		end
	end
	else begin // MBC 2/3/5
		romBank <= inputData;
	end
end

If we detected a write at 0x6000-7000 and we aren’t locked to MBC3/5 (because MBC3 has an RTC commands which can write at this address) then we check if we are ROM or RAM mode. If no write to 0x6000-7000 has been detected and we aren’t locked to MBC3/5 then we assign the romBank to be inputData as we were doing before.

If in RAM mode (not supported yet), we only care about the low 5 bits of the ROM and that’s it. Once we are in ROM mode, we keep the 2 high bits (& 0x60) that might be set in romBank variable already (see below) and then add in our low 5 bits to it.

// 0x4000-5FFF - RAM Bank Number (Write Only)
if ((inputAddress == 4'd4 || inputAddress == 4'd5) && !inputWR && inputRD && inputCE) begin
	if (mbc1Detected607F && !mbc3or5Locked) begin // MBC1 ROM size handling, ignores RAM banking request
		if (!mbc1RomRamSelect) begin
			romBank <= (romBank & 7'h1F) | (inputData[1:0] << 5); //inputData[1:0] << 5;
		end
	end
	else begin // Allow access to all RAM banks for regular MBC 3/5 carts
		ramBank <= inputData[3:0];
	end
end

Now for the 2 high bits side, we do the same checks – if we detected a write at 0x6000-7000 and we aren’t locked to MBC3/5 and we are in ROM mode. Here we keep the lower 5 bits (& 0x1F) and then shift the 2 bits we read left by 5 places so that they are now our high bits.

So I loaded up the game to the flash cart and gave it a shot and it works!

I was curious to see how the game loaded the rom banks at the 2 different locations so opened it up in BGB. It looks like they set the low 5 bits first and then the 2 high bits second, I would have thought it would be the opposite.

// 0x2000-3FFF - Low 7 bits of ROM Bank Number (Write Only) with little MBC1 detection hack
if (((inputAddress == 4'd2) || inputAddress == 4'd3 && mbc1Detect303FOn) && !inputWR && inputRD && inputCE) begin
	if (inputData == 7'd0) begin // Detects MBC1 (most of the time)
		romBank <= 1'b1;
		mbc1Detect303FOn <= 1'b1;
	end

Actually if it was done the opposite way, my code wouldn’t work because if a game requests bank 0, then I just set the bank to 1 but that would actually wipe out the 2 high bits.

If we read this cart back in GBxCart RW I can see that problem, I get a duplicate of 0x4000 at location 0x80000 because the 2 high bits were wiped.

// 0x2000-3FFF - Low 7 bits of ROM Bank Number (Write Only) with little MBC1 detection hack
if (((inputAddress == 4'd2) || inputAddress == 4'd3 && mbc1Detect303FOn) && !inputWR && inputRD && inputCE) begin
	if (inputData == 7'd0) begin // Detects MBC1 (most of the time)
		if (mbc1Detected607F && !mbc3or5Locked && !mbc1RomRamSelect) begin // Allow for more than 512KB ROM on MBC1
			romBank <= (romBank & 7'h60) | 7'b1;
		end
		else begin // For all others
			romBank <= 1'b1;
		end
		mbc1Detect303FOn <= 1'b1;
	end
	else begin

The solution is when a request for bank 0 comes in, we check the usual 3 things again to determine if it’s MBC1, not locked to MBC3/5 and in ROM mode. If so we keep the 2 high bits of the rom bank and set the low 5 bits to be 1 (or we could just set it to 0 too as it’s a duplicate anyway).

Now GBxCart RW reads back the ROM without issues. Compared to the Firmware R1 (default) release, adding MBC1 2MB support consumes about 3-5mA more, there aren’t many games that use more than 512KB in MBC1. Download: GB MBC5-1 Hybrid – R1.1 (2MB ROM, 128KB SRAM)

You can check out the different firmware versions at our Github page: https://github.com/insidegadgets/Gameboy-MBC5-MBC1-Hybrid

Now all that’s left is to layout the board for the FRAM and this time have the cart hole be in the same place like a genuine cart.

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