Skip to content

Commit

Permalink
NES: PRG or CHR banking errors are no longer fatal
Browse files Browse the repository at this point in the history
To be clear: A banking error is almost always a bug in our emulator! But often the game can still be played. Especially with CHR errors.

So it should be logged but not aborted.

This fixes most MMC5 games that seem to have been broken for a while...
  • Loading branch information
ducalex committed Feb 6, 2024
1 parent e19cacf commit 9c7624c
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 90 deletions.
66 changes: 36 additions & 30 deletions retro-core/components/nofrendo/nes/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,23 @@ static const mem_write_handler_t write_handlers[] =
/* Set 2KB memory page */
void mem_setpage(uint32 page, uint8 *ptr)
{
ASSERT(page < 32);
ASSERT(ptr);
if (page >= MEM_PAGECOUNT)
{
MESSAGE_ERROR("Invalid page #%d (max: %d) !\n", page, MEM_PAGECOUNT);
return;
}

mem.pages[page] = ptr - (page * MEM_PAGESIZE);
if (ptr)
mem.pages[page] = ptr - (page * MEM_PAGESIZE);
else
mem.pages[page] = NULL;

if (!MEM_PAGE_HAS_HANDLERS(mem.pages_read[page]))
if (mem.pages_read[page] != MEM_PAGE_HAS_HANDLERS)
{
mem.pages_read[page] = mem.pages[page];
}

if (!MEM_PAGE_HAS_HANDLERS(mem.pages_write[page]))
if (mem.pages_write[page] != MEM_PAGE_HAS_HANDLERS)
{
mem.pages_write[page] = mem.pages[page];
}
Expand All @@ -73,16 +79,16 @@ void mem_setpage(uint32 page, uint8 *ptr)
/* Get 2KB memory page */
uint8 *mem_getpage(uint32 page)
{
ASSERT(page < 32);

uint8 *page_ptr = mem.pages[page];

if (MEM_PAGE_IS_VALID_PTR(page_ptr))
if (page >= MEM_PAGECOUNT)
{
return page_ptr + (page * MEM_PAGESIZE);
MESSAGE_ERROR("Invalid page #%d (max: %d) !\n", page, MEM_PAGECOUNT);
return NULL;
}

return page_ptr;
if (!mem.pages[page])
return NULL;

return mem.pages[page] + (page * MEM_PAGESIZE);
}

/* read a byte of 6502 memory space */
Expand All @@ -91,7 +97,7 @@ IRAM_ATTR uint8 mem_getbyte(uint32 address)
uint8 *page = mem.pages_read[address >> MEM_PAGESHIFT];

/* Special memory handlers */
if (MEM_PAGE_HAS_HANDLERS(page))
if (page == MEM_PAGE_HAS_HANDLERS)
{
for (mem_read_handler_t *mr = mem.read_handlers; mr->read_func != NULL; mr++)
{
Expand All @@ -102,16 +108,13 @@ IRAM_ATTR uint8 mem_getbyte(uint32 address)
}

/* Unmapped region */
if (page == NULL)
if (page == MEM_PAGE_NOT_MAPPED)
{
MESSAGE_DEBUG("Read unmapped region: $%4X\n", address);
return 0xFF;
}
/* Paged memory */
else
{
return page[address];
}

return page[address];
}

/* write a byte of data to 6502 memory space */
Expand All @@ -120,7 +123,7 @@ IRAM_ATTR void mem_putbyte(uint32 address, uint8 value)
uint8 *page = mem.pages_write[address >> MEM_PAGESHIFT];

/* Special memory handlers */
if (MEM_PAGE_HAS_HANDLERS(page))
if (page == MEM_PAGE_HAS_HANDLERS)
{
for (mem_write_handler_t *mw = mem.write_handlers; mw->write_func != NULL; mw++)
{
Expand All @@ -134,15 +137,13 @@ IRAM_ATTR void mem_putbyte(uint32 address, uint8 value)
}

/* Unmapped region */
if (page == NULL)
if (page == MEM_PAGE_NOT_MAPPED)
{
MESSAGE_DEBUG("Write to unmapped region: $%2X to $%4X\n", address, value);
return;
}
/* Paged memory */
else
{
page[address] = value;
}

page[address] = value;
}

IRAM_ATTR uint32 mem_getword(uint32 address)
Expand All @@ -154,12 +155,17 @@ void mem_reset(void)
{
memset(&mem, 0, sizeof(mem));

mem_setpage(0, mem.ram);
mem_setpage(0, mem.ram); // $000 - $7FF
mem_setpage(1, mem.ram); // $800 - $FFF mirror
mem_setpage(2, mem.ram); // $1000 - $07FF mirror
mem_setpage(3, mem.ram); // $1800 - $1FFF mirror

int num_read_handlers = 0, num_write_handlers = 0;
for (size_t i = 4; i < MEM_PAGECOUNT; ++i)
{
mem_setpage(i, MEM_PAGE_NOT_MAPPED);
}

size_t num_read_handlers = 0, num_write_handlers = 0;
mapper_t *mapper = nes_getptr()->mapper;

// NES cartridge handlers
Expand Down Expand Up @@ -189,13 +195,13 @@ void mem_reset(void)
for (mem_read_handler_t *mr = mem.read_handlers; mr->read_func != NULL; mr++)
{
for (int i = mr->min_range; i < mr->max_range; i++)
mem.pages_read[i >> MEM_PAGESHIFT] = MEM_PAGE_USE_HANDLERS;
mem.pages_read[i >> MEM_PAGESHIFT] = MEM_PAGE_HAS_HANDLERS;
}

for (mem_write_handler_t *mw = mem.write_handlers; mw->write_func != NULL; mw++)
{
for (int i = mw->min_range; i < mw->max_range; i++)
mem.pages_write[i >> MEM_PAGESHIFT] = MEM_PAGE_USE_HANDLERS;
mem.pages_write[i >> MEM_PAGESHIFT] = MEM_PAGE_HAS_HANDLERS;
}

ASSERT(num_read_handlers <= MEM_HANDLERS_MAX);
Expand Down
8 changes: 2 additions & 6 deletions retro-core/components/nofrendo/nes/mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,8 @@
#define MEM_RAMSIZE 0x800

// This is kind of a hack, but for speed...
#define MEM_PAGE_USE_HANDLERS ((uint8*)1)
#define MEM_PAGE_READ_ONLY ((uint8*)2)
#define MEM_PAGE_WRITE_ONLY ((uint8*)3)

#define MEM_PAGE_HAS_HANDLERS(page) ((page) == MEM_PAGE_USE_HANDLERS)
#define MEM_PAGE_IS_VALID_PTR(page) ((page) > ((uint8*)100))
#define MEM_PAGE_HAS_HANDLERS ((uint8*)1)
#define MEM_PAGE_NOT_MAPPED NULL

#define MEM_HANDLERS_MAX 32

Expand Down
76 changes: 29 additions & 47 deletions retro-core/components/nofrendo/nes/mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ static rom_t *cart;
/* PRG-ROM/RAM bankswitching */
void mmc_bankprg(unsigned size, unsigned address, int bank, uint8 *base)
{
// ASSERT(size == 8 || size == 16 || size == 32);
size_t banks = 16;

// if (base == PRG_ANY)
Expand All @@ -46,40 +47,31 @@ void mmc_bankprg(unsigned size, unsigned address, int bank, uint8 *base)
banks = cart->prg_ram_banks;
}

if (base == NULL)
{
MESSAGE_ERROR("MMC: Invalid pointer! Addr: $%04X Bank: %d Size: %d\n", address, bank, size);
abort();
}

switch (size)
{
case 8:
base += ((bank >= 0 ? bank : (banks) + bank) % (banks)) << 13;
break;

case 16:
base += ((bank >= 0 ? bank : (banks / 2) + bank) % (banks / 2)) << 14;
break;
banks /= (size / 8);

case 32:
base += ((bank >= 0 ? bank : (banks / 4) + bank) % (banks / 4)) << 15;
break;
if (bank < 0)
bank += banks;

default:
MESSAGE_ERROR("MMC: Invalid bank size! Addr: $%04X Bank: %d Size: %d\n", address, bank, size);
abort();
if (base == NULL || banks == 0 || bank < 0) // || bank > banks)
{
MESSAGE_ERROR("MMC: Bogus PRG mapping! Address: $%04X, Size: %dKB, Bank: %d, Banks: %d, Base: %p\n",
address, size, bank, banks, base);
return;
}

for (int i = 0; i < (size * 0x400 / MEM_PAGESIZE); i++)
bank %= banks;
base += bank * (size * 1024);

for (size_t i = 0, num = (size * 1024 / MEM_PAGESIZE); i < num; ++i)
{
mem_setpage((address >> MEM_PAGESHIFT) + i, base + i * MEM_PAGESIZE);
mem_setpage((address >> MEM_PAGESHIFT) + i, &base[i * MEM_PAGESIZE]);
}
}

/* CHR-ROM/RAM bankswitching */
void mmc_bankchr(unsigned size, unsigned address, int bank, uint8 *base)
{
// ASSERT(size == 1 || size == 2 || size == 4 || size == 8);
size_t banks = 128;

if (base == CHR_ANY)
Expand All @@ -96,32 +88,22 @@ void mmc_bankchr(unsigned size, unsigned address, int bank, uint8 *base)
banks = cart->chr_ram_banks;
}

switch (size)
banks *= (8 / size);

if (bank < 0)
bank += banks;

if (base == NULL || banks == 0 || bank < 0) // || bank > banks)
{
case 1:
bank = (bank >= 0 ? bank : (banks * 8) + bank) % (banks * 8);
ppu_setpage(1, address >> 10, &base[bank << 10] - address);
break;

case 2:
bank = (bank >= 0 ? bank : (banks * 4) + bank) % (banks * 4);
ppu_setpage(2, address >> 10, &base[bank << 11] - address);
break;

case 4:
bank = (bank >= 0 ? bank : (banks * 2) + bank) % (banks * 2);
ppu_setpage(4, address >> 10, &base[bank << 12] - address);
break;

case 8:
bank = (bank >= 0 ? bank : (banks) + bank) % (banks);
ppu_setpage(8, 0, &base[bank << 13]);
break;

default:
MESSAGE_ERROR("MMC: Invalid CHR bank size %d\n", size);
abort();
MESSAGE_ERROR("MMC: Bogus CHR mapping! Address: $%04X, Size: %dKB, Bank: %d, Banks: %d, Base: %p\n",
address, size, bank, banks, base);
return;
}

bank %= banks;
base += bank * (size * 1024);

ppu_setpage(size, address >> 10, base - address);
}

/* Mapper initialization routine */
Expand Down
4 changes: 2 additions & 2 deletions retro-core/components/nofrendo/nes/ppu.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ IRAM_ATTR void ppu_write(uint32 address, uint8 value)
}
}

void ppu_setopt(ppu_option_t n, uint8_t val)
void ppu_setopt(ppu_option_t n, int val)
{
// Some options need special care
switch (n)
Expand All @@ -349,7 +349,7 @@ void ppu_setopt(ppu_option_t n, uint8_t val)
ppu.options[n] = val;
}

uint8_t ppu_getopt(ppu_option_t n)
int ppu_getopt(ppu_option_t n)
{
return ppu.options[n];
}
Expand Down
7 changes: 3 additions & 4 deletions retro-core/components/nofrendo/nes/ppu.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ typedef struct
bool vram_present;

/* Misc runtime options */
uint8_t options[16];
int options[16];
} ppu_t;

/* Mirroring / Paging */
Expand All @@ -144,12 +144,11 @@ uint8 *ppu_getnametable(int nt);

/* Control */
ppu_t *ppu_init(void);
void ppu_refresh(void);
void ppu_reset(void);
void ppu_shutdown(void);
bool ppu_enabled(void);
void ppu_setopt(ppu_option_t n, uint8_t val);
uint8_t ppu_getopt(ppu_option_t n);
void ppu_setopt(ppu_option_t n, int val);
int ppu_getopt(ppu_option_t n);

void ppu_setlatchfunc(ppu_latchfunc_t func);
void ppu_setvreadfunc(ppu_vreadfunc_t func);
Expand Down
2 changes: 1 addition & 1 deletion retro-core/components/nofrendo/nes/rom.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ rom_t *rom_loadmem(uint8 *data, size_t size)
if (entry->prg_ram != rom.prg_ram_banks)
{
MESSAGE_WARN("ROM: prg_ram_banks mismatch! (DB: %d, ROM: %d)\n", entry->prg_ram, rom.prg_ram_banks);
// rom.prg_rom_banks = entry->prg_ram;
// rom.prg_ram_banks = entry->prg_ram;
}

if (entry->chr_rom > -1 && entry->chr_rom != rom.chr_rom_banks)
Expand Down

0 comments on commit 9c7624c

Please sign in to comment.