From 44c75ae7be199d7267921fdb988a53b4d562698b Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 10 Apr 2021 18:43:24 +0300 Subject: [PATCH 1/7] Remove commented out code --- Core/display.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/display.c b/Core/display.c index 72e89c89..6979a0ac 100644 --- a/Core/display.c +++ b/Core/display.c @@ -574,7 +574,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) else if (gb->model & GB_MODEL_NO_SFC_BIT) { if (gb->icd_pixel_callback) { icd_pixel = pixel; - //gb->icd_pixel_callback(gb, pixel); } } else if (gb->cgb_palettes_ppu_blocked) { From f24489b9834781ef4c3fff86fa58da3f7560c151 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 10 Apr 2021 23:56:41 +0300 Subject: [PATCH 2/7] TPP1 support --- Core/gb.h | 7 +++- Core/mbc.c | 17 ++++++++ Core/mbc.h | 1 + Core/memory.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++--- Core/timing.c | 47 +++++++++++++++------- 5 files changed, 156 insertions(+), 21 deletions(-) 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); From 0c5e15b49dd8a8167dffc11a2fadecc74464bd33 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 11 Apr 2021 02:38:58 +0300 Subject: [PATCH 3/7] Correct emulation of count overflow in ATTR_CHR, fixes #372 --- Core/sgb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/sgb.c b/Core/sgb.c index c77b0db6..f22eb3e5 100644 --- a/Core/sgb.c +++ b/Core/sgb.c @@ -269,7 +269,8 @@ static void command_ready(GB_gameboy_t *gb) #endif uint8_t x = command->x; uint8_t y = command->y; - if (x >= 20 || y >= 18 || (count + 3) / 4 > sizeof(gb->sgb->command) - sizeof(*command) - 1) { + count = MIN(count, 20 * 18); + if (x >= 20 || y >= 18) { /* TODO: Verify with the SFC BIOS */ break; } From 42471095e41e6d062cee9cf408360fbbbfa33540 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 11 Apr 2021 22:38:25 +0300 Subject: [PATCH 4/7] Normalize invalid weekdays only after a $11 command --- Core/memory.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Core/memory.c b/Core/memory.c index 3e7d4a41..20aaac9a 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -192,9 +192,20 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) 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 0: { // Week count + unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days); + if (gb->rtc_latched.high & 0x20) { + return total_days / 7 - 1; + } + return total_days / 7; + } + case 1: { // Week count + unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days); + if (gb->rtc_latched.high & 0x20) { + return gb->rtc_latched.hours | 0xe0; // Hours and weekday + } + return gb->rtc_latched.hours | ((total_days % 7) << 5); // Hours and weekday + } case 2: return gb->rtc_latched.minutes; case 3: return gb->rtc_latched.seconds; } @@ -616,7 +627,7 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) 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 &= ~0xe0; gb->rtc_real.high |= flags; break; } @@ -762,8 +773,12 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case 0: { unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days); total_days = total_days % 7 + value * 7; + bool had_illegal_weekday = gb->rtc_latched.high & 0x20; gb->rtc_latched.days = total_days; gb->rtc_latched.high = total_days >> 8; + if (had_illegal_weekday) { + gb->rtc_latched.high |= 0x20; + } return; } case 1: { @@ -772,6 +787,9 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->rtc_latched.hours = value & 0x1F; gb->rtc_latched.days = total_days; gb->rtc_latched.high = total_days >> 8; + if ((value & 0xE0) == 0xE0) { // Illegal weekday + gb->rtc_latched.high |= 0x20; + } return; } case 2: gb->rtc_latched.minutes = value; return; From 763de9d2e0c8846a4dc82f67971392f88e028a75 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 11 Apr 2021 22:52:34 +0300 Subject: [PATCH 5/7] Fix Rumble support in TPP1 --- Core/memory.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Core/memory.c b/Core/memory.c index 20aaac9a..00821310 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -640,6 +640,13 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case 0x19: gb->rtc_real.high &= ~0x40; break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + gb->rumble_strength = value & 3; + break; } } break; From 80f422d0cad6580f81028c1bdfb91b6aadf609f7 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 11 Apr 2021 23:16:31 +0300 Subject: [PATCH 6/7] Respect TPP1 feature flags for rumble and RTC --- Core/debugger.c | 7 +++++-- Core/gb.c | 4 ++++ Core/rumble.c | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Core/debugger.c b/Core/debugger.c index 73bd80ae..be80fd00 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -1523,7 +1523,9 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg const GB_cartridge_t *cartridge = gb->cartridge_type; if (cartridge->has_ram) { - GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size); + bool has_battery = gb->cartridge_type->has_battery && + (gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 8)); + GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", has_battery? " battery-backed": "", gb->mbc_ram_size); } else { GB_log(gb, "No cartridge RAM\n"); @@ -1565,7 +1567,8 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg GB_log(gb, "No MBC\n"); } - if (cartridge->has_rumble) { + if (gb->cartridge_type->has_rumble && + (gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) { GB_log(gb, "Cart contains a Rumble Pak\n"); } diff --git a/Core/gb.c b/Core/gb.c index 3a0864d4..e6747c67 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -609,6 +609,8 @@ typedef union { int GB_save_battery_size(GB_gameboy_t *gb) { if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. + if (gb->cartridge_type->mbc_type == GB_TPP1 && !(gb->rom[0x153] & 8)) return 0; // Nothing to save. + if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ if (gb->cartridge_type->mbc_type == GB_HUC3) { @@ -621,6 +623,7 @@ int GB_save_battery_size(GB_gameboy_t *gb) int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size) { if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. + if (gb->cartridge_type->mbc_type == GB_TPP1 && !(gb->rom[0x153] & 8)) return 0; // Nothing to save. if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ if (size < GB_save_battery_size(gb)) return EIO; @@ -678,6 +681,7 @@ int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size) int GB_save_battery(GB_gameboy_t *gb, const char *path) { if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. + if (gb->cartridge_type->mbc_type == GB_TPP1 && !(gb->rom[0x153] & 8)) return 0; // Nothing to save. if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ FILE *f = fopen(path, "wb"); if (!f) { diff --git a/Core/rumble.c b/Core/rumble.c index 87eb8703..5f83c479 100644 --- a/Core/rumble.c +++ b/Core/rumble.c @@ -15,7 +15,8 @@ void GB_handle_rumble(GB_gameboy_t *gb) if (gb->rumble_mode == GB_RUMBLE_DISABLED) { return; } - if (gb->cartridge_type->has_rumble) { + if (gb->cartridge_type->has_rumble && + (gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) { if (gb->rumble_on_cycles + gb->rumble_off_cycles) { gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles)); gb->rumble_on_cycles = gb->rumble_off_cycles = 0; From 251dd15ff91cf7638a93d8386f4eee3af3b4fcdd Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 11 Apr 2021 23:36:42 +0300 Subject: [PATCH 7/7] Fixed a bug where the screen would not redraw when certain controllers are rumbling in specific strengths in the Cocoa port --- JoyKit/JOYController.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/JoyKit/JOYController.m b/JoyKit/JOYController.m index ca2d1b18..8ec12794 100644 --- a/JoyKit/JOYController.m +++ b/JoyKit/JOYController.m @@ -166,6 +166,7 @@ typedef union { double _sentRumbleAmp; unsigned _rumbleCounter; bool _deviceCantSendReports; + dispatch_queue_t _rumbleQueue; } - (instancetype)initWithDevice:(IOHIDDeviceRef) device hacks:(NSDictionary *)hacks @@ -490,9 +491,11 @@ typedef union { {.timeEnabled = 0, .dutyLength = 0, .enabled = 0, .dutyOff = 0, .dutyOn = 0}, } }; - } + _rumbleQueue = dispatch_queue_create([NSString stringWithFormat:@"Rumble Queue for %@", self.deviceName].UTF8String, + NULL); + return self; } @@ -564,7 +567,9 @@ typedef union { } } } - [self updateRumble]; + dispatch_async(_rumbleQueue, ^{ + [self updateRumble]; + }); } - (void)elementChanged:(IOHIDElementRef)element @@ -699,7 +704,9 @@ typedef union { _physicallyConnected = false; [exposedControllers removeObject:self]; [self setRumbleAmplitude:0]; - [self updateRumble]; + dispatch_sync(_rumbleQueue, ^{ + [self updateRumble]; + }); _device = nil; }