diff --git a/Core/gb.h b/Core/gb.h index 7210e7a6..b09a5063 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -441,10 +441,10 @@ struct GB_gameboy_internal_s { uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ bool camera_registers_mapped; uint8_t camera_registers[0x36]; - bool rumble_state; + uint8_t rumble_strength; bool cart_ir; - // TODO: move to huc3/mbc3 struct when breaking save compat + // TODO: move to huc3/mbc3/tpp1 struct when breaking save compat uint8_t huc3_mode; uint8_t huc3_access_index; uint16_t huc3_minutes, huc3_days; @@ -453,6 +453,9 @@ struct GB_gameboy_internal_s { uint8_t huc3_read; uint8_t huc3_access_flags; bool mbc3_rtc_mapped; + uint16_t tpp1_rom_bank; + uint8_t tpp1_ram_bank; + uint8_t tpp1_mode; ); diff --git a/Core/mbc.c b/Core/mbc.c index 1e91ce2a..ffb8d9dd 100644 --- a/Core/mbc.c +++ b/Core/mbc.c @@ -111,12 +111,24 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb) gb->mbc_rom_bank = gb->huc3.rom_bank; gb->mbc_ram_bank = gb->huc3.ram_bank; break; + case GB_TPP1: + gb->mbc_rom_bank = gb->tpp1_rom_bank; + gb->mbc_ram_bank = gb->tpp1_ram_bank; + gb->mbc_ram_enable = (gb->tpp1_mode == 2) || (gb->tpp1_mode == 3); + break; } } void GB_configure_cart(GB_gameboy_t *gb) { gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]]; + if (gb->rom[0x147] == 0xbc && + gb->rom[0x149] == 0xc1 && + gb->rom[0x14a] == 0x65) { + static const GB_cartridge_t tpp1 = {GB_TPP1, GB_STANDARD_MBC, true, true, true, true}; + gb->cartridge_type = &tpp1; + gb->tpp1_rom_bank = 1; + } if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) { GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n"); @@ -130,6 +142,11 @@ 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_TPP1) { + if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) { + gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1); + } + } else { static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000}; gb->mbc_ram_size = ram_sizes[gb->rom[0x149]]; diff --git a/Core/mbc.h b/Core/mbc.h index 6a23300f..3bbe7827 100644 --- a/Core/mbc.h +++ b/Core/mbc.h @@ -12,6 +12,7 @@ typedef struct { GB_MBC5, GB_HUC1, GB_HUC3, + GB_TPP1, } mbc_type; enum { GB_STANDARD_MBC, diff --git a/Core/memory.c b/Core/memory.c index b1619a6b..3e7d4a41 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -178,7 +178,31 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) } } - if ((!gb->mbc_ram_enable) && + if (gb->cartridge_type->mbc_type == GB_TPP1) { + switch (gb->tpp1_mode) { + case 0: + switch (addr & 3) { + case 0: return gb->tpp1_rom_bank; + case 1: return gb->tpp1_rom_bank >> 8; + case 2: return gb->tpp1_ram_bank; + case 3: return gb->rumble_strength | (((gb->rtc_real.high & 0xC0) ^ 0x40) >> 4); + } + case 2: + case 3: + break; // Read RAM + case 5: + switch (addr & 3) { + case 0: return (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days) / 7; // Week count + case 1: return gb->rtc_latched.hours | + (((((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days) % 7) << 5); // Hours and weekday + case 2: return gb->rtc_latched.minutes; + case 3: return gb->rtc_latched.seconds; + } + default: + return 0xFF; + } + } + else if ((!gb->mbc_ram_enable) && gb->cartridge_type->mbc_subtype != GB_CAMERA && gb->cartridge_type->mbc_type != GB_HUC1 && gb->cartridge_type->mbc_type != GB_HUC3) { @@ -335,9 +359,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) } if (addr < 0xFF00) { - return 0; - } if (addr < 0xFF80) { @@ -539,8 +561,8 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case 0x3000: gb->mbc5.rom_bank_high = value; break; case 0x4000: case 0x5000: if (gb->cartridge_type->has_rumble) { - if (!!(value & 8) != gb->rumble_state) { - gb->rumble_state = !gb->rumble_state; + if (!!(value & 8) != !!gb->rumble_strength) { + gb->rumble_strength = gb->rumble_strength? 0 : 3; } value &= 7; } @@ -567,6 +589,49 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break; } break; + case GB_TPP1: + switch (addr & 3) { + case 0: + gb->tpp1_rom_bank &= 0xFF00; + gb->tpp1_rom_bank |= value; + break; + case 1: + gb->tpp1_rom_bank &= 0xFF; + gb->tpp1_rom_bank |= value << 8; + break; + case 2: + gb->tpp1_ram_bank = value; + break; + case 3: + switch (value) { + case 0: + case 2: + case 3: + case 5: + gb->tpp1_mode = value; + break; + case 0x10: + memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real)); + break; + case 0x11: { + uint8_t flags = gb->rtc_real.high & 0xc0; + memcpy(&gb->rtc_real, &gb->rtc_latched, sizeof(gb->rtc_real)); + gb->rtc_real.high &= ~0xc0; + gb->rtc_real.high |= flags; + break; + } + case 0x14: + gb->rtc_real.high &= ~0x80; + break; + case 0x18: + gb->rtc_real.high |= 0x40; + break; + case 0x19: + gb->rtc_real.high &= ~0x40; + break; + } + } + break; } GB_update_mbc_mappings(gb); } @@ -688,6 +753,36 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; } + if (gb->cartridge_type->mbc_type == GB_TPP1) { + switch (gb->tpp1_mode) { + case 3: + break; + case 5: + switch (addr & 3) { + case 0: { + unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days); + total_days = total_days % 7 + value * 7; + gb->rtc_latched.days = total_days; + gb->rtc_latched.high = total_days >> 8; + return; + } + case 1: { + unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days); + total_days = total_days / 7 * 7 + (value >> 5); + gb->rtc_latched.hours = value & 0x1F; + gb->rtc_latched.days = total_days; + gb->rtc_latched.high = total_days >> 8; + return; + } + case 2: gb->rtc_latched.minutes = value; return; + case 3: gb->rtc_latched.seconds = value; return; + } + return; + default: + return; + } + } + if ((!gb->mbc_ram_enable) && gb->cartridge_type->mbc_type != GB_HUC1) return; diff --git a/Core/timing.c b/Core/timing.c index 1da82a38..d240525d 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -288,10 +288,22 @@ static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles) while (gb->last_rtc_second + 60 * 60 * 24 < current_time) { gb->last_rtc_second += 60 * 60 * 24; if (++gb->rtc_real.days == 0) { - if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ - gb->rtc_real.high |= 0x80; /* Overflow bit */ + if (gb->cartridge_type->mbc_type == GB_TPP1) { + if ((gb->rtc_real.high & 7) >= 6) { /* Bit 8 of days*/ + gb->rtc_real.high &= 0x40; + gb->rtc_real.high |= 0x80; /* Overflow bit */ + } + else { + gb->rtc_real.high++; + } + } + else { + if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ + gb->rtc_real.high |= 0x80; /* Overflow bit */ + } + + gb->rtc_real.high ^= 1; } - gb->rtc_real.high ^= 1; } } @@ -308,11 +320,22 @@ static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles) if (++gb->rtc_real.days != 0) continue; - if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ - gb->rtc_real.high |= 0x80; /* Overflow bit */ + if (gb->cartridge_type->mbc_type == GB_TPP1) { + if ((gb->rtc_real.high & 7) >= 6) { /* Bit 8 of days*/ + gb->rtc_real.high &= 0x40; + gb->rtc_real.high |= 0x80; /* Overflow bit */ + } + else { + gb->rtc_real.high++; + } + } + else { + if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ + gb->rtc_real.high |= 0x80; /* Overflow bit */ + } + + gb->rtc_real.high ^= 1; } - - gb->rtc_real.high ^= 1; } } } @@ -344,13 +367,9 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) gb->cycles_since_last_sync += cycles; gb->cycles_since_run += cycles; - if (gb->rumble_state) { - gb->rumble_on_cycles++; - } - else { - gb->rumble_off_cycles++; - } - + gb->rumble_on_cycles += gb->rumble_strength & 3; + gb->rumble_off_cycles += (gb->rumble_strength & 3) ^ 3; + if (!gb->stopped) { // TODO: Verify what happens in STOP mode GB_dma_run(gb); GB_hdma_run(gb);