Every now and then I receive an email about adding support for older flash carts but they are usually are hard to find or there isn’t too much information out about them which makes it difficult to add support. I had one user contact me about adding support for the GB Smart 16M flash cart and they were happy to do a swap of my flash cart for theirs. They mentioned it had 4x flash chips which was interesting and were also able to provide me with an empty menu file named GB16M.gb.
A few weeks later it turned up.
Before receiving it, I was thinking how could they fit 4x flash chips on the cart and then I saw they used the low pin count versions, ah makes sense. Perhaps they choose those because they were cheaper than going with a 2MB flash chip. I plugged the cart into my GBA and it loaded up Super Mario Land straight away so it seems to be working. One thing to be careful of is the battery, it is loosely installed and it sort of slid out when I opened the cart. You wouldn’t want to open it up only to lose your saves!
After dumping the cart contents and selecting 2MB as the ROM size, I could see that DK and GB Gallery were also on the cartridge, I’m guessing once we pass the 512KB mark, it switches to the next chip so that means DK started on the 2nd flash chip and GB Gallery started on the 3rd flash chip.
After looking at the AT49BV040 datasheet, it uses the same programming commands as other chips as you would expect so I quickly threw together a dedicated flashing program for it, just a chip erase and then programming each byte one at a time. It did work fine for the first 512KB flash chip but then how do we erase the next chip?
gb_flash_write_address_byte(0x5555, 0xAA); gb_flash_write_address_byte(0x2AAA, 0x55); gb_flash_write_address_byte(0x5555, 0x80); gb_flash_write_address_byte(0x5555, 0xAA); gb_flash_write_address_byte(0x2AAA, 0x55); gb_flash_write_address_byte(0x5555, 0x10);
Here’s the chip erase commands. If we wish to issue 0x5555, we have to change the bank to 0x21 and then to issue 0x2AAA we would change the bank to 0x20 but we can’t actually issue 0x2AAA as that would try and write to the first 16KB bank that doesn’t change (0x0000 – 0x4000).
set_bank(0x2100, 0x21); gb_flash_write_address_byte(0x5555, 0xAA); set_bank(0x2100, 0x20); gb_flash_write_address_byte(0x6AAA, 0x55); set_bank(0x2100, 0x21); gb_flash_write_address_byte(0x5555, 0x80); gb_flash_write_address_byte(0x5555, 0xAA); set_bank(0x2100, 0x20); gb_flash_write_address_byte(0x6AAA, 0x55); set_bank(0x2100, 0x21); gb_flash_write_address_byte(0x5555, 0x10); set_bank(0x2100, 0x20); wait_for_flash_chip_erase_ff();
We would have to actually write 0x6AAA – the memory mapper would process our request and ignore the A14 bit we set because it’s already handling that itself. It’s all a bit confusing but I can confirm it does work though I didn’t actually implement it this way, I only found this out whilst I was writing this post!
Menu game generation
The next course of action is to look at the menu to see if there is anything we can determine from it. When loading it up in BGB, it’s just blank saying “no data present”. We’ll enable the BGB > Exception, break on access and break on nonstandard writes and see what we can find.
When I looked at it the first time, it didn’t really make sense however upon looking at it a week later, it made sense what they were doing. What they seem to do is switch banks and then look for certain bytes that are known to be at the start of GB games, if it matches, then it will list that game in the menu. They switch banks every 32KB, so bank 0x02, 0x04 and so on.
It appears their checking starts at 0x4105 which is the 1 byte into the start of the Nintendo logo, so I manually put in a 32KB game at 0x10000 (bank 4) and the last address they checked was 0x4133 – the end of the Nintendo logo.
After that it jumps to another place and continues to read from 0x4134 to 0x4143 which is the game title and then reads 0x4147 the cartridge type (MBC), 0x4148 the ROM size and lastly 0x4149 the RAM size – everything you would need to initialise your emulated Multi-MBC properly.
Here I’ve loaded 2 games and when I press A to load a game, surprisingly BGB seems to have support for the menu and actually launches the game! Not sure why it says SmartCart32M but looks like the GB Smart 32M would use a similar menu program. The GB Smart 32M cart uses a single Intel 32Mbit flash chip.
We can see that ROM0 has been changed to ROM4 which is usually not possible for a regular MBC1-5 cart.
One strange issue is that games larger than 32KB need to be aligned properly. For example, Super Mario Land a 64KB won’t run if placed at 0x8000 (32KB) but will run if placed at 0x10000 (64KB). Games that are 512KB need to be placed at the start of the 2nd, 3rd or 4th flash chip (0x80000) if you are using the loader. For my multi-game loader cart, I don’t have this size alignment issue.
Menu game switching
Now that we have games listed in the menu, we should be able to see the writes that the menu makes in order to re-map the first 16KB of the ROM (0x0000 – 0x3FFF) which in normal operation never changes. If we can figure out how to have that re-mapped to line up with the start of the 2nd flash chip, then we won’t have to continually switch banks as you saw before when erasing the flash (and also for programming too).
With the first game at 0x10000 in the menu rom, I saw a write in the WRAM section to 0x2000 with the byte 0x04 so it looks like it’s loading the start of the game to the bank and then a write to 0x1000 with 0xA5, possibly to acknowledge the request however in BGB I can see that location 0x0000 hasn’t changed to ROM4 as yet.
Further on, we can see it actually re-reads ROM size and RAM size but not the cartridge type for the MBC.
Later on at address 0x7000 it loads byte 0x36 and then at 0x1000 it loads 0x98 possibly to confirm like before and then it looks like ROM4 is mapped at 0x0000 (according to BGB, right after 0x7000 is issued that’s when the mapping takes effect). For the next game on the list Tetris, address 0x7000 is loaded with 0x37.
Doing a bit more experimentation, for DK located at the start of the 2nd flash chip, it loaded 0x23 and for GB Gallery at the start of the 3rd flash chip it loaded 0x22 – doesn’t seem to really add up but no matter, I’ll just use what DK wrote 0x23.
Adding it all together for flashing
set_bank(0x2000, 0x20); set_bank(0x1000, 0xA5); set_bank(0x7000, 0x22); set_bank(0x1000, 0x98);
Now we can add together everything we have learnt about this cartridge. My first test program was to write to address 0x2000 with 0x20 and include all the other pieces so it would re-map 0x0000 to be the start of the 2nd flash chip. I read the ROM and it did indeed change properly, excellent!
But will it accept doing the re-mapping again for the 3rd flash chip (0x2000, 0x40) after we mapped the 2nd flash chip? It didn’t seem to.
for (uint8_t z = 0; z < 255; z++) { set_bank(0x2000, 0x40); // 3nd flash chip set_bank(0x1000, 0xA5); set_bank(0x7000, z); set_bank(0x1000, 0x98);
I just did a quick for loop to see what happens when we change 0x7000 to any value, after a quick run, it seems like if we issue 0x7000, 0x00, etc and then the bank we want with 0x7000, 0x23, etc and it works.
// Calculate ROM banks remaining if more than 0x20 (one flash chip) if (romBanks > 0x20) { currentBankSize = 0x20; } while (romBanksRemaining >= 1) { ... // Switch to the next flash chip if (chipNo >= 2) { set_bank(0x2000, 0x20 * (chipNo-1)); set_bank(0x1000, 0xA5); set_bank(0x7000, 0x00); set_bank(0x1000, 0x98); set_bank(0x2000, 0x20 * (chipNo-1)); set_bank(0x1000, 0xA5); set_bank(0x7000, 0x23); set_bank(0x1000, 0x98); } // Erase flash chip gb_flash_write_address_byte(0x5555, 0xAA); ... wait_for_flash_chip_erase_ff(); // Write ROM currAddr = 0x0000; for (uint16_t bank = 1; bank < currentBankSize; bank++) { ... } chipNo++; romBanksRemaining -= currentBankSize; }
We can start putting everything together. One thing we need to keep track of in our flashing program is how many banks remain and which flash chip we are on. When we switch flash chips, we will need to erase it before programming and know how many banks remain to be writing on the flash chip. For a 2MB game, after writing the first 512KB of data, we switch to the 2nd flash chip, erase it, write 512KB to it, switch to the 3rd flash chip and so on.
Basic Menu Maker
There are many ways we could make a menu maker for this cart, really it’s just an alignment utility more than anything else. We could have a GUI with the 4 flash chips displayed or having a listing of ROM files and ask the user to say which games should be where, though I don’t want to spend much time on it.
// Check if we need to align the file uint32_t alignmentAmount = 0; if (outputFileSize % fileSize != 0) { // Keep adding 32KB until we are aligned while (outputFileSize % fileSize != 0) { alignmentAmount += 32768; outputFileSize += 32768; } } // Align the file if (alignmentAmount >= 1) { FILE *romWriteFile = fopen("output.gb", "r+b"); fseek (romWriteFile, 0, SEEK_END); // Add 32KB of 0xFF to output.gb uint8_t buffer[1] = {0xFF}; for (uint32_t x = 0; x < alignmentAmount; x++) { fwrite(buffer, 1, 1, romWriteFile); } fclose(romReadFile); } // Append the rom file ...
Even simpler than that is to only allow 1 file to be dragged and dropped into the .exe and it will just create an output.gb file if one doesn’t exist. If it does exist, it will do a modulus of the output.gb compared to the file dragged and dropped. If the modulus isn’t 0, we keep adding 32KB of 0xFFs until the modulus result is 0. It’s now up to the user to determine if they want to add in all their 32KB games first and then add the larger games later.
And that’s all there is, GBxCart RW now supports the GB Smart 16M cart!
Brilliant work Alex!
Mate, I have two GB smart 32M. Would the GBxCart RW mini work with those?
I have a very old serial device which I can’t use any longer due to not having serial on any computer anymore and the GBxCart would make those two GB smart useful again, also thanks to the Linux support.
Thank you!
Ah, I just read that the 32M isn’t supported… yet.
Would you like to get one of my carts? I’d send it to you if you’re interested into adding support to it.
Thanks,
Hi Febs,
Thanks to one of our users, the GB Smart 32M appears to be flashable now!
I’ve tried to write my own merger with the GB Smart 32M, it works like a charm with your rule: beginning offset of any game in the multiboot file must always be equal or superior to its own size. This implies that GB Smart 16M can handle one 8 MB rom and GB Smart 32M can handle one 16 MB rom at most. 256kB games can be written anywhere. Seems the number of games is limited to 15 too.
Oh, it’s Mb and kb, not MB and kB, sorry.