Skip to content

Commit

Permalink
NES: Improved timing to fix a few games
Browse files Browse the repository at this point in the history
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...
  • Loading branch information
ducalex committed Feb 5, 2024
1 parent 3fc7fc4 commit fc7d462
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 30 deletions.
35 changes: 11 additions & 24 deletions retro-core/components/nofrendo/nes/nes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

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

Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 5 additions & 6 deletions retro-core/components/nofrendo/nes/nes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit fc7d462

Please sign in to comment.