From fc7d46210ff7244433dbe860d7f4408757ecd776 Mon Sep 17 00:00:00 2001 From: Alex Duchesne Date: Sun, 4 Feb 2024 21:32:42 -0500 Subject: [PATCH] NES: Improved timing to fix a few games To fix Battletoads Double Dragon I did the following things: 1. run the CPU before running the PPU 2. Ensure hblank interrupts happen no earlier than cpu cycle 86 3. Removed the float counters, we don't need that (in)accuracy These changes also fix other games like SMB3 and Kirby (the bottom window jumping around) and I was able to get rid of the MMC3 hack entirely. IMPORTANT: I'm sure this will break other games and hopefully I remember to do more testing before merging to master... --- retro-core/components/nofrendo/nes/nes.c | 35 ++++++++---------------- retro-core/components/nofrendo/nes/nes.h | 11 ++++---- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/retro-core/components/nofrendo/nes/nes.c b/retro-core/components/nofrendo/nes/nes.c index f599f2ebc..94441114f 100644 --- a/retro-core/components/nofrendo/nes/nes.c +++ b/retro-core/components/nofrendo/nes/nes.c @@ -33,20 +33,16 @@ nes_t *nes_getptr(void) /* run emulation for one frame */ void nes_emulate(bool draw) { - int elapsed_cycles = 0; - while (nes.scanline < nes.scanlines_per_frame) { - nes.cycles += nes.cycles_per_scanline; + // Running a little bit ahead seems to fix both Battletoads games... + int elapsed_cycles = nes6502_execute(86 - 12); ppu_renderline(nes.vidbuf, nes.scanline, draw); if (nes.scanline == 241) { - /* 7-9 cycle delay between when VINT flag goes up and NMI is taken */ - elapsed_cycles = nes6502_execute(7); - nes.cycles -= elapsed_cycles; - + elapsed_cycles += nes6502_execute(6); if (nes.ppu->ctrl0 & PPU_CTRL0F_NMI) nes6502_nmi(); @@ -55,9 +51,16 @@ void nes_emulate(bool draw) } if (nes.mapper->hblank) + { + // Mappers use various techniques to detect horizontal blank and we can't accommodate + // all of them unfortunately. But ~86 cycles seems to work fine for everything tested. + elapsed_cycles += nes6502_execute(86 - elapsed_cycles); nes.mapper->hblank(nes.scanline); + } + + nes.cycles += nes.cycles_per_scanline; - elapsed_cycles = nes6502_execute(nes.cycles); + elapsed_cycles += nes6502_execute(nes.cycles - elapsed_cycles); apu_fc_advance(elapsed_cycles); nes.cycles -= elapsed_cycles; @@ -83,20 +86,6 @@ void nes_settimer(nes_timer_t *func, int period) nes.timer_period = period; } -void nes_setcompathacks(void) -{ - // Hack to fix many MMC3 games with status bar vertical alignment issues - // The issue is that the CPU and PPU aren't running in sync - if (nes.cart->checksum == 0xD8578BFD || // Zen Intergalactic - nes.cart->checksum == 0x2E6301ED || // Super Mario Bros 3 - nes.cart->checksum == 0x5ED6F221 || // Kirby's Adventure - nes.cart->checksum == 0xD273B409) // Power Blade 2 - { - nes.cycles_per_scanline += 2.5; - MESSAGE_INFO("NES: Enabled MMC3 Timing Hack\n"); - } -} - /* insert a cart into the NES */ int nes_insertcart(const char *filename, const char *biosfile) { @@ -172,8 +161,6 @@ int nes_insertcart(const char *filename, const char *biosfile) MESSAGE_INFO("NES: BIOS file loaded from '%s'.\n", biosfile); } - nes_setcompathacks(); - nes_reset(true); return status; diff --git a/retro-core/components/nofrendo/nes/nes.h b/retro-core/components/nofrendo/nes/nes.h index 49de6b75a..60861667e 100644 --- a/retro-core/components/nofrendo/nes/nes.h +++ b/retro-core/components/nofrendo/nes/nes.h @@ -62,11 +62,11 @@ typedef struct nes_s nes_t; typedef enum { - SYS_UNKNOWN, SYS_NES_NTSC, SYS_NES_PAL, SYS_FAMICOM, - SYS_DETECT = 0, + SYS_UNKNOWN, + SYS_DETECT = -1, } nes_type_t; typedef void (nes_timer_t)(int cycles); @@ -91,15 +91,14 @@ typedef struct nes_s int overscan; /* Timing constants */ - /* TO DO: re-check if float is still necessary...*/ int refresh_rate; int scanlines_per_frame; - float cycles_per_scanline; - float cpu_clock; + int cycles_per_scanline; + int cpu_clock; /* Timing counters */ int scanline; - float cycles; + int cycles; /* Periodic timer */ nes_timer_t *timer_func;