From 9c7624c7256c24eb63e0a81f97b8183eb8204f0b Mon Sep 17 00:00:00 2001 From: Alex Duchesne Date: Tue, 6 Feb 2024 01:02:34 -0500 Subject: [PATCH] NES: PRG or CHR banking errors are no longer fatal 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... --- retro-core/components/nofrendo/nes/mem.c | 66 ++++++++++---------- retro-core/components/nofrendo/nes/mem.h | 8 +-- retro-core/components/nofrendo/nes/mmc.c | 76 +++++++++--------------- retro-core/components/nofrendo/nes/ppu.c | 4 +- retro-core/components/nofrendo/nes/ppu.h | 7 +-- retro-core/components/nofrendo/nes/rom.c | 2 +- 6 files changed, 73 insertions(+), 90 deletions(-) diff --git a/retro-core/components/nofrendo/nes/mem.c b/retro-core/components/nofrendo/nes/mem.c index 10ce5d9e8..dc2f29b9c 100644 --- a/retro-core/components/nofrendo/nes/mem.c +++ b/retro-core/components/nofrendo/nes/mem.c @@ -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]; } @@ -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 */ @@ -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++) { @@ -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 */ @@ -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++) { @@ -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) @@ -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 @@ -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); diff --git a/retro-core/components/nofrendo/nes/mem.h b/retro-core/components/nofrendo/nes/mem.h index 2229c9911..62413001e 100644 --- a/retro-core/components/nofrendo/nes/mem.h +++ b/retro-core/components/nofrendo/nes/mem.h @@ -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 diff --git a/retro-core/components/nofrendo/nes/mmc.c b/retro-core/components/nofrendo/nes/mmc.c index 31cc14ad8..90873cd78 100644 --- a/retro-core/components/nofrendo/nes/mmc.c +++ b/retro-core/components/nofrendo/nes/mmc.c @@ -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) @@ -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) @@ -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 */ diff --git a/retro-core/components/nofrendo/nes/ppu.c b/retro-core/components/nofrendo/nes/ppu.c index 3d976cfb7..2db6ba862 100644 --- a/retro-core/components/nofrendo/nes/ppu.c +++ b/retro-core/components/nofrendo/nes/ppu.c @@ -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) @@ -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]; } diff --git a/retro-core/components/nofrendo/nes/ppu.h b/retro-core/components/nofrendo/nes/ppu.h index 655f30e4e..b58a24ce2 100644 --- a/retro-core/components/nofrendo/nes/ppu.h +++ b/retro-core/components/nofrendo/nes/ppu.h @@ -132,7 +132,7 @@ typedef struct bool vram_present; /* Misc runtime options */ - uint8_t options[16]; + int options[16]; } ppu_t; /* Mirroring / Paging */ @@ -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); diff --git a/retro-core/components/nofrendo/nes/rom.c b/retro-core/components/nofrendo/nes/rom.c index 65215f6a2..6d7122496 100644 --- a/retro-core/components/nofrendo/nes/rom.c +++ b/retro-core/components/nofrendo/nes/rom.c @@ -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)