TPP1 support

This commit is contained in:
Lior Halphon 2021-04-10 23:56:41 +03:00
parent 44c75ae7be
commit f24489b983
5 changed files with 156 additions and 21 deletions

View File

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

View File

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

View File

@ -12,6 +12,7 @@ typedef struct {
GB_MBC5,
GB_HUC1,
GB_HUC3,
GB_TPP1,
} mbc_type;
enum {
GB_STANDARD_MBC,

View File

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

View File

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