From c6f39bc60bc0a07cb3c0b46d9b8e9f1e45c0f467 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 12 Nov 2021 17:44:51 +0200 Subject: [PATCH] Initial MBC7 support --- Core/debugger.c | 1 + Core/gb.c | 27 +++++++-- Core/gb.h | 21 +++++++ Core/mbc.c | 16 +++++ Core/mbc.h | 1 + Core/memory.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 212 insertions(+), 6 deletions(-) diff --git a/Core/debugger.c b/Core/debugger.c index 80f4d46..5547721 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -1541,6 +1541,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg [GB_MBC2] = "MBC2", [GB_MBC3] = "MBC3", [GB_MBC5] = "MBC5", + [GB_MBC7] = "MBC7", [GB_HUC1] = "HUC-1", [GB_HUC3] = "HUC-3", }; diff --git a/Core/gb.c b/Core/gb.c index 7142d90..24d7329 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1010,9 +1010,11 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t reset_rtc: gb->last_rtc_second = time(NULL); gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ - gb->huc3.days = 0xFFFF; - gb->huc3.minutes = 0xFFF; - gb->huc3.alarm_enabled = false; + if (gb->cartridge_type->mbc_type == GB_HUC3) { + gb->huc3.days = 0xFFFF; + gb->huc3.minutes = 0xFFF; + gb->huc3.alarm_enabled = false; + } exit: return; } @@ -1116,9 +1118,11 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path) reset_rtc: gb->last_rtc_second = time(NULL); gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ - gb->huc3.days = 0xFFFF; - gb->huc3.minutes = 0xFFF; - gb->huc3.alarm_enabled = false; + if (gb->cartridge_type->mbc_type == GB_HUC3) { + gb->huc3.days = 0xFFFF; + gb->huc3.minutes = 0xFFF; + gb->huc3.alarm_enabled = false; + } exit: fclose(f); return; @@ -1844,6 +1848,17 @@ void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode) } } +bool GB_has_accelerometer(GB_gameboy_t *gb) +{ + return gb->cartridge_type->mbc_type == GB_MBC7; +} + +void GB_set_accelerometer_values(GB_gameboy_t *gb, double x, double y) +{ + gb->accelerometer_x = x; + gb->accelerometer_y = y; +} + void GB_get_rom_title(GB_gameboy_t *gb, char *title) { memset(title, 0, 17); diff --git a/Core/gb.h b/Core/gb.h index 1cec2ac..448c3e5 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -443,6 +443,22 @@ struct GB_gameboy_internal_s { uint8_t rom_bank_high:1; 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 bank_low:6; @@ -609,6 +625,7 @@ struct GB_gameboy_internal_s { GB_color_correction_mode_t color_correction_mode; double light_temperature; bool keys[4][GB_KEY_MAX]; + double accelerometer_x, accelerometer_y; GB_border_mode_t border_mode; GB_sgb_border_t borrowed_border; bool tried_loading_sgb_border; @@ -835,6 +852,10 @@ unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm /* RTC emulation mode */ void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode); +/* For cartridges motion controls */ +bool GB_has_accelerometer(GB_gameboy_t *gb); +void GB_set_accelerometer_values(GB_gameboy_t *gb, double x, double y); + /* For integration with SFC/SNES emulators */ void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback); void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback); diff --git a/Core/mbc.c b/Core/mbc.c index b76778c..82771eb 100644 --- a/Core/mbc.c +++ b/Core/mbc.c @@ -34,6 +34,8 @@ const GB_cartridge_t GB_cart_defs[256] = { { GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE { GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM { GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY + [0x22] = + { GB_MBC7 , GB_STANDARD_MBC, true, true, false, false}, // 22h MBC7+ACCEL+EEPROM [0xFC] = { GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported) @@ -97,6 +99,9 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb) gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8); gb->mbc_ram_bank = gb->mbc5.ram_bank; break; + case GB_MBC7: + gb->mbc_rom_bank = gb->mbc7.rom_bank; + break; case GB_HUC1: if (gb->huc1.mode == 0) { gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6); @@ -148,6 +153,9 @@ void GB_configure_cart(GB_gameboy_t *gb) if (gb->cartridge_type->mbc_type == GB_MBC2) { gb->mbc_ram_size = 0x200; } + else if (gb->cartridge_type->mbc_type == GB_MBC7) { + gb->mbc_ram_size = 0x100; + } else if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) { gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1); @@ -187,4 +195,12 @@ void GB_configure_cart(GB_gameboy_t *gb) 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) { + gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000; + gb->mbc7.latch_ready = true; + gb->mbc7.read_bits = -1; + gb->mbc7.eeprom_do = true; + } } diff --git a/Core/mbc.h b/Core/mbc.h index 709bd1f..7e6cc82 100644 --- a/Core/mbc.h +++ b/Core/mbc.h @@ -10,6 +10,7 @@ typedef struct { GB_MBC2, GB_MBC3, GB_MBC5, + GB_MBC7, GB_HUC1, GB_HUC3, GB_TPP1, diff --git a/Core/memory.c b/Core/memory.c index 0174eb1..4e6665e 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -301,8 +301,28 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr) return gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)]; } +static uint8_t read_mbc7_ram(GB_gameboy_t *gb, uint16_t addr) +{ + if (!gb->mbc_ram_enable || !gb->mbc7.secondary_ram_enable) return 0xFF; + if (addr >= 0xB000) return 0xFF; + switch ((addr >> 4) & 0xF) { + case 2: return gb->mbc7.x_latch; + case 3: return gb->mbc7.x_latch >> 8; + case 4: return gb->mbc7.y_latch; + case 5: return gb->mbc7.y_latch >> 8; + case 6: return 0; + case 8: return gb->mbc7.eeprom_do | (gb->mbc7.eeprom_di << 1) | + (gb->mbc7.eeprom_clk << 6) | (gb->mbc7.eeprom_cs << 7); + } + return 0xFF; +} + static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) { + if (gb->cartridge_type->mbc_type == GB_MBC7) { + return read_mbc7_ram(gb, addr); + } + if (gb->cartridge_type->mbc_type == GB_HUC3) { switch (gb->huc3.mode) { case 0xC: // RTC read @@ -730,6 +750,13 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) break; } break; + case GB_MBC7: + switch (addr & 0xF000) { + case 0x0000: case 0x1000: gb->mbc_ram_enable = value == 0x0A; break; + case 0x2000: case 0x3000: gb->mbc7.rom_bank = value; break; + case 0x4000: case 0x5000: gb->mbc7.secondary_ram_enable = value == 0x40; break; + } + break; case GB_HUC1: switch (addr & 0xF000) { case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break; @@ -905,8 +932,133 @@ static bool huc3_write(GB_gameboy_t *gb, uint8_t value) } } +static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) +{ + if (!gb->mbc_ram_enable || !gb->mbc7.secondary_ram_enable) return; + if (addr >= 0xB000) return; + switch ((addr >> 4) & 0xF) { + case 0: { + if (value == 0x55) { + gb->mbc7.latch_ready = true; + gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000; + } + } + case 1: { + if (value == 0xAA) { + gb->mbc7.latch_ready = false; + gb->mbc7.x_latch = 0x81D0 + 0x70 * gb->accelerometer_x; + gb->mbc7.y_latch = 0x81D0 + 0x70 * gb->accelerometer_y; + } + } + case 8: { + gb->mbc7.eeprom_cs = value & 0x80; + gb->mbc7.eeprom_di = value & 2; + if (gb->mbc7.eeprom_cs) { + if (!gb->mbc7.eeprom_clk && (value & 0x40)) { // Clocked + gb->mbc7.eeprom_do = gb->mbc7.read_bits >> 15; + gb->mbc7.read_bits <<= 1; + gb->mbc7.read_bits |= 1; + if (gb->mbc7.bits_countdown == 0) { + /* Not transferring extra bits for a command*/ + gb->mbc7.eeprom_command <<= 1; + gb->mbc7.eeprom_command |= gb->mbc7.eeprom_di; + if (gb->mbc7.eeprom_command & 0x400) { + // Got full command + switch ((gb->mbc7.eeprom_command >> 6) & 0xF) { + case 0x8: + case 0x9: + case 0xA: + case 0xB: + // READ + gb->mbc7.read_bits = LE16(((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F]); + gb->mbc7.eeprom_command = 0; + break; + case 0x3: // EWEN (Eeprom Write ENable) + gb->mbc7.eeprom_write_enabled = true; + gb->mbc7.eeprom_command = 0; + break; + case 0x0: // EWDS (Eeprom Write DiSable) + gb->mbc7.eeprom_write_enabled = false; + gb->mbc7.eeprom_command = 0; + break; + case 0x4: + case 0x5: + case 0x6: + case 0x7: + // WRITE + if (gb->mbc7.eeprom_write_enabled) { + ((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0; + } + gb->mbc7.bits_countdown = 16; + // We still need to process this command, don't erase eeprom_command + break; + case 0xC: + case 0xD: + case 0xE: + case 0xF: + // ERASE + if (gb->mbc7.eeprom_write_enabled) { + ((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0xFFFF; + gb->mbc7.read_bits = 0x3FFF; // Emulate some time to settle + } + gb->mbc7.eeprom_command = 0; + break; + case 0x2: + // ERAL (ERase ALl) + if (gb->mbc7.eeprom_write_enabled) { + memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); + ((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0xFFFF; + gb->mbc7.read_bits = 0xFF; // Emulate some time to settle + } + gb->mbc7.eeprom_command = 0; + break; + case 0x1: + // WRAL (WRite ALl) + if (gb->mbc7.eeprom_write_enabled) { + memset(gb->mbc_ram, 0, gb->mbc_ram_size); + } + gb->mbc7.bits_countdown = 16; + // We still need to process this command, don't erase eeprom_command + break; + } + } + } + else { + // We're shifting in extra bits for a WRITE/WRAL command + gb->mbc7.bits_countdown--; + gb->mbc7.eeprom_do = true; + if (gb->mbc7.eeprom_di) { + uint16_t bit = LE16(1 << gb->mbc7.bits_countdown); + if (gb->mbc7.eeprom_command & 0x100) { + // WRITE + ((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] |= bit; + } + else { + // WRAL + for (unsigned i = 0; i < 0x7F; i++) { + ((uint16_t *)gb->mbc_ram)[i] |= bit; + } + } + } + if (gb->mbc7.bits_countdown == 0) { // We're done + gb->mbc7.eeprom_command = 0; + gb->mbc7.read_bits = (gb->mbc7.eeprom_command & 0x100)? 0xFF : 0x3FFF; // Emulate some time to settle + } + } + } + } + gb->mbc7.eeprom_clk = value & 0x40; + } + } +} + static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) { + if (gb->cartridge_type->mbc_type == GB_MBC7) { + write_mbc7_ram(gb, addr, value); + return; + } + if (gb->cartridge_type->mbc_type == GB_HUC3) { if (huc3_write(gb, value)) return; }