From c78a0037124a6215642300a39b1d7b035dac1519 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 5 Mar 2022 16:35:03 +0200 Subject: [PATCH] MMM01 support --- Core/debugger.c | 18 +++++++----- Core/gb.c | 3 +- Core/gb.h | 49 ++++++++++++++++++++----------- Core/mbc.c | 73 +++++++++++++++++++++++++++++++++++++++++------ Core/mbc.h | 4 ++- Core/memory.c | 36 +++++++++++++++++++++++ Core/save_state.c | 11 +++++-- 7 files changed, 159 insertions(+), 35 deletions(-) diff --git a/Core/debugger.c b/Core/debugger.c index 7a7fd87..b560011 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -1546,16 +1546,20 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg } else { static const char *const mapper_names[] = { - [GB_MBC1] = "MBC1", - [GB_MBC2] = "MBC2", - [GB_MBC3] = "MBC3", - [GB_MBC5] = "MBC5", - [GB_MBC7] = "MBC7", - [GB_HUC1] = "HUC-1", - [GB_HUC3] = "HUC-3", + [GB_MBC1] = "MBC1", + [GB_MBC2] = "MBC2", + [GB_MBC3] = "MBC3", + [GB_MBC5] = "MBC5", + [GB_MBC7] = "MBC7", + [GB_MMM01] = "MMM01", + [GB_HUC1] = "HUC-1", + [GB_HUC3] = "HUC-3", }; GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]); } + if (cartridge->mbc_type == GB_MMM01 || cartridge->mbc_type == GB_MBC1) { + GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank); + } GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank); if (cartridge->has_ram) { GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank); diff --git a/Core/gb.c b/Core/gb.c index 0b5081a..3c230bd 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1609,7 +1609,8 @@ void GB_reset(GB_gameboy_t *gb) gb->model = model; gb->version = GB_STRUCT_VERSION; - gb->mbc_rom_bank = 1; + GB_reset_mbc(gb); + gb->last_rtc_second = time(NULL); gb->cgb_ram_bank = 1; gb->io_registers[GB_IO_JOYP] = 0xCF; diff --git a/Core/gb.h b/Core/gb.h index 0405c4a..2937032 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -440,6 +440,7 @@ struct GB_gameboy_internal_s { /* MBC */ GB_SECTION(mbc, uint16_t mbc_rom_bank; + uint16_t mbc_rom0_bank; /* For multicart mappings . */ uint8_t mbc_ram_bank; uint32_t mbc_ram_size; bool mbc_ram_enable; @@ -466,21 +467,38 @@ struct GB_gameboy_internal_s { uint8_t ram_bank:4; } mbc5; - struct { - uint8_t rom_bank; - uint16_t x_latch; - uint16_t y_latch; - bool latch_ready:1; - bool eeprom_do:1; - bool eeprom_di:1; - bool eeprom_clk:1; - bool eeprom_cs:1; - uint16_t eeprom_command:11; - uint16_t read_bits; - uint8_t bits_countdown:5; - bool secondary_ram_enable:1; - bool eeprom_write_enabled:1; - } mbc7; + struct { + uint8_t rom_bank; + uint16_t x_latch; + uint16_t y_latch; + bool latch_ready:1; + bool eeprom_do:1; + bool eeprom_di:1; + bool eeprom_clk:1; + bool eeprom_cs:1; + uint16_t eeprom_command:11; + uint16_t read_bits; + uint8_t bits_countdown:5; + bool secondary_ram_enable:1; + bool eeprom_write_enabled:1; + } mbc7; + + struct { + uint8_t rom_bank_low:5; + uint8_t rom_bank_mid:2; + bool mbc1_mode:1; + + uint8_t rom_bank_mask:4; + uint8_t rom_bank_high:2; + uint8_t ram_bank_low:2; + + uint8_t ram_bank_high:2; + uint8_t ram_bank_mask:2; + + bool locked:1; + bool mbc1_mode_disable:1; + bool multiplex_mode:1; + } mmm01; struct { uint8_t bank_low:6; @@ -508,7 +526,6 @@ struct GB_gameboy_internal_s { uint8_t mode; } tpp1; }; - uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ bool camera_registers_mapped; uint8_t camera_registers[0x36]; uint8_t rumble_strength; diff --git a/Core/mbc.c b/Core/mbc.c index d330d8e..da798b1 100644 --- a/Core/mbc.c +++ b/Core/mbc.c @@ -18,9 +18,9 @@ const GB_cartridge_t GB_cart_defs[256] = { { GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY [0xB] = /* Todo: Not supported yet */ - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01 - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY + { GB_MMM01 , GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01 + { GB_MMM01 , GB_STANDARD_MBC, true , false, false, false}, // 0Ch MMM01+RAM + { GB_MMM01 , GB_STANDARD_MBC, true , true , false, false}, // 0Dh MMM01+RAM+BATTERY [0xF] = { GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY { GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY @@ -103,6 +103,40 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb) case GB_MBC7: gb->mbc_rom_bank = gb->mbc7.rom_bank; break; + case GB_MMM01: + if (gb->mmm01.locked) { + if (gb->mmm01.multiplex_mode) { + gb->mbc_rom0_bank = (gb->mmm01.rom_bank_low & (gb->mmm01.rom_bank_mask << 1)) | + ((gb->mmm01.rom_bank_low & (gb->mmm01.mbc1_mode? -1 : gb->mmm01.ram_bank_mask)) << 5) | + (gb->mmm01.rom_bank_high << 7); + gb->mbc_rom_bank = gb->mmm01.rom_bank_low | + (gb->mmm01.rom_bank_low << 5) | + (gb->mmm01.rom_bank_high << 7); + gb->mbc_ram_bank = gb->mmm01.rom_bank_mid | (gb->mmm01.ram_bank_high << 2); + } + else { + gb->mbc_rom0_bank = (gb->mmm01.rom_bank_low & (gb->mmm01.rom_bank_mask << 1)) | + (gb->mmm01.rom_bank_mid << 5) | + (gb->mmm01.rom_bank_high << 7); + gb->mbc_rom_bank = gb->mmm01.rom_bank_low | + (gb->mmm01.rom_bank_mid << 5) | + (gb->mmm01.rom_bank_high << 7); + if (gb->mmm01.mbc1_mode) { + gb->mbc_ram_bank = gb->mmm01.ram_bank_low | (gb->mmm01.ram_bank_high << 2); + } + else { + gb->mbc_ram_bank = (gb->mmm01.ram_bank_low & gb->mmm01.ram_bank_mask) | (gb->mmm01.ram_bank_high << 2); + } + } + if (gb->mbc_rom_bank == gb->mbc_rom0_bank) { + gb->mbc_rom_bank++; + } + } + else { + gb->mbc_rom_bank = -1; + gb->mbc_rom0_bank = -2; + } + break; case GB_HUC1: if (gb->huc1.mode == 0) { gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6); @@ -129,6 +163,20 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb) void GB_configure_cart(GB_gameboy_t *gb) { gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]]; + if (gb->cartridge_type->mbc_type == GB_MMM01) { + uint8_t *temp = malloc(0x8000); + memcpy(temp, gb->rom, 0x8000); + memmove(gb->rom, gb->rom + 0x8000, gb->rom_size - 0x8000); + memcpy(gb->rom + gb->rom_size - 0x8000, temp, 0x8000); + free(temp); + } + else { + const GB_cartridge_t *maybe_mmm01_type = &GB_cart_defs[gb->rom[gb->rom_size - 0x8000 + 0x147]]; + if (maybe_mmm01_type->mbc_type == GB_MMM01 && memcmp(gb->rom + 0x104, gb->rom + gb->rom_size - 0x8000 + 0x104, 0x30) == 0) { + gb->cartridge_type = maybe_mmm01_type; + } + } + if (gb->rom[0x147] == 0xBC && gb->rom[0x149] == 0xC1 && gb->rom[0x14A] == 0x65) { @@ -193,16 +241,25 @@ void GB_configure_cart(GB_gameboy_t *gb) } } - /* Set MBC5's bank to 1 correctly */ - if (gb->cartridge_type->mbc_type == GB_MBC5) { + GB_reset_mbc(gb); +} + +void GB_reset_mbc(GB_gameboy_t *gb) +{ + if (gb->cartridge_type->mbc_type == GB_MMM01) { + gb->mbc_rom_bank = -1; + gb->mbc_rom0_bank = -2; + } + else if (gb->cartridge_type->mbc_type == GB_MBC5) { gb->mbc5.rom_bank_low = 1; } - - /* Initial MBC7 state */ - if (gb->cartridge_type->mbc_type == GB_MBC7) { + else if (gb->cartridge_type->mbc_type == GB_MBC7) { gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000; gb->mbc7.latch_ready = true; gb->mbc7.read_bits = -1; gb->mbc7.eeprom_do = true; } + else { + gb->mbc_rom_bank = 1; + } } diff --git a/Core/mbc.h b/Core/mbc.h index 7e6cc82..c3f6190 100644 --- a/Core/mbc.h +++ b/Core/mbc.h @@ -11,6 +11,7 @@ typedef struct { GB_MBC3, GB_MBC5, GB_MBC7, + GB_MMM01, GB_HUC1, GB_HUC3, GB_TPP1, @@ -26,9 +27,10 @@ typedef struct { } GB_cartridge_t; #ifdef GB_INTERNAL -extern const GB_cartridge_t GB_cart_defs[256]; +internal extern const GB_cartridge_t GB_cart_defs[256]; internal void GB_update_mbc_mappings(GB_gameboy_t *gb); internal void GB_configure_cart(GB_gameboy_t *gb); +internal void GB_reset_mbc(GB_gameboy_t *gb); #endif #endif /* MBC_h */ diff --git a/Core/memory.c b/Core/memory.c index b6a1857..2644f12 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -838,6 +838,42 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case 0x4000: case 0x5000: gb->mbc7.secondary_ram_enable = value == 0x40; break; } break; + case GB_MMM01: + switch (addr & 0xF000) { + case 0x0000: case 0x1000: + gb->mbc_ram_enable = (value & 0xF) == 0xA; + if (!gb->mmm01.locked) { + gb->mmm01.ram_bank_mask = value >> 4; + gb->mmm01.locked = value & 0x40; + } + break; + case 0x2000: case 0x3000: + if (!gb->mmm01.locked) { + gb->mmm01.rom_bank_mid = value >> 5; + gb->mmm01.rom_bank_low = value; + } + else { + gb->mmm01.rom_bank_low &= (gb->mmm01.rom_bank_mask << 1); + gb->mmm01.rom_bank_low |= ~(gb->mmm01.rom_bank_mask << 1) & value; + } + break; + case 0x4000: case 0x5000: + gb->mmm01.ram_bank_low = value; + if (!gb->mmm01.locked) { + gb->mmm01.ram_bank_high = value >> 2; + gb->mmm01.rom_bank_high = value >> 4; + gb->mmm01.mbc1_mode_disable = value & 0x40; + } + break; + case 0x6000: case 0x7000: + gb->mmm01.mbc1_mode = (value & 1) && !gb->mmm01.mbc1_mode_disable; + if (!gb->mmm01.locked) { + gb->mmm01.rom_bank_mask = value >> 2; + gb->mmm01.multiplex_mode = value & 0x40; + } + break; + } + break; case GB_HUC1: switch (addr & 0xF000) { case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break; diff --git a/Core/save_state.c b/Core/save_state.c index 3b8dad5..f22df13 100644 --- a/Core/save_state.c +++ b/Core/save_state.c @@ -232,6 +232,8 @@ static size_t bess_size_for_cartridge(const GB_cartridge_t *cart) return sizeof(BESS_block_t) + 3 * sizeof(BESS_MBC_pair_t) + (cart->has_rtc? sizeof(BESS_RTC_t) : 0); case GB_MBC5: return sizeof(BESS_block_t) + 4 * sizeof(BESS_MBC_pair_t); + case GB_MMM01: + return sizeof(BESS_block_t) + 4 * sizeof(BESS_MBC_pair_t); case GB_HUC1: return sizeof(BESS_block_t) + 4 * sizeof(BESS_MBC_pair_t); case GB_HUC3: @@ -435,20 +437,25 @@ static int save_bess_mbc_block(GB_gameboy_t *gb, virtual_file_t *file) pairs[3] = (BESS_MBC_pair_t){LE16(0x4000), gb->mbc5.ram_bank}; mbc_block.size = 4 * sizeof(pairs[0]); break; + case GB_MMM01: + pairs[0] = (BESS_MBC_pair_t){LE16(0x2000), gb->mmm01.rom_bank_low | (gb->mmm01.rom_bank_mid << 5)}; + pairs[1] = (BESS_MBC_pair_t){LE16(0x6000), gb->mmm01.mbc1_mode | (gb->mmm01.rom_bank_mask << 2) | (gb->mmm01.multiplex_mode << 6)}; + pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->mmm01.ram_bank_low | (gb->mmm01.ram_bank_high << 2) | (gb->mmm01.rom_bank_high << 4) | (gb->mmm01.mbc1_mode_disable << 6)}; + pairs[3] = (BESS_MBC_pair_t){LE16(0x0000), (gb->mbc_ram_enable? 0xA : 0x0) | (gb->mmm01.ram_bank_mask << 4) | (gb->mmm01.locked << 6)}; + mbc_block.size = 4 * sizeof(pairs[0]); + break; case GB_HUC1: pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->huc1.ir_mode? 0xE : 0x0}; pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->huc1.bank_low}; pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->huc1.bank_high}; pairs[3] = (BESS_MBC_pair_t){LE16(0x6000), gb->huc1.mode}; mbc_block.size = 4 * sizeof(pairs[0]); - case GB_HUC3: pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->huc3.mode}; pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->huc3.rom_bank}; pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->huc3.ram_bank}; mbc_block.size = 3 * sizeof(pairs[0]); break; - case GB_TPP1: pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->tpp1.rom_bank}; pairs[1] = (BESS_MBC_pair_t){LE16(0x0001), gb->tpp1.rom_bank >> 8};