MMM01 support

This commit is contained in:
Lior Halphon 2022-03-05 16:35:03 +02:00
parent a621803e82
commit c78a003712
7 changed files with 159 additions and 35 deletions

View File

@ -1551,11 +1551,15 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
[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);

View File

@ -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;

View File

@ -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;
@ -482,6 +483,23 @@ struct GB_gameboy_internal_s {
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;
uint8_t bank_high:3;
@ -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;

View File

@ -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->mbc5.rom_bank_low = 1;
GB_reset_mbc(gb);
}
/* Initial MBC7 state */
if (gb->cartridge_type->mbc_type == GB_MBC7) {
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;
}
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;
}
}

View File

@ -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 */

View File

@ -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;

View File

@ -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};