Add mapper 165 to the NES emulator
The NES emulator cannot run Fire Emblem. After investigation, I found the current code doesn't support mapper 165. I tried adding mapper 165 with AI assistance, but the results aren't satisfactory. While the game runs normally, there's background graphical corruption during dialogues and when opening menus. My code is as follows:
/* ** map165.c ** ** mapper 165 interface (NES-EVENT) ** Based on MMC3 implementation */
#include "nes/nes.h"
/* Mapper 165 (NES-EVENT) specific registers */ static struct { uint8 reg[8]; // Internal registers uint8 command; // Current command uint8 chr_mode; // CHR mode (0 or 1) uint8 counter; // IRQ counter uint8 latch; // IRQ latch value uint8 enabled; // IRQ enabled flag } irq;
static uint32 reg8000; static uint16 vrombase; static bool fourscreen;
/* CHR control function */ static bool chr_control(uint16 address) { address &= 0x1FF0; return (address == 0x1FD0 || address == 0x1FE0); }
/* Update CHR banks */ static void update_chr_banks(void) { uint16 bank[2] = {0, 0};
if (!irq.chr_mode) {
bank[0] = irq.reg[0];
bank[1] = irq.reg[2];
} else {
bank[0] = irq.reg[1];
bank[1] = irq.reg[4];
}
bank[0] &= ~3;
mmc_bankvrom(1, 0x0000, bank[0] | 0x00);
mmc_bankvrom(1, 0x0400, bank[0] | 0x01);
mmc_bankvrom(1, 0x0800, bank[0] | 0x02);
mmc_bankvrom(1, 0x0C00, bank[0] | 0x03);
bank[1] &= ~3;
mmc_bankvrom(1, 0x1000, bank[1] | 0x00);
mmc_bankvrom(1, 0x1400, bank[1] | 0x01);
mmc_bankvrom(1, 0x1800, bank[1] | 0x02);
mmc_bankvrom(1, 0x1C00, bank[1] | 0x03);
}
/* Mapper 165 write handler */ static void map_write(uint32 address, uint8 value) { switch (address & 0xE001) { case 0x8000: irq.command = value; break;
case 0x8001:
{
int reg_num = irq.command & 0x07;
irq.reg[reg_num] = value;
switch (reg_num)
{
case 0: // CHR bank 0
case 1: // CHR bank 1
case 2: // CHR bank 2
case 4: // CHR bank 4
update_chr_banks();
break;
case 6: // PRG bank 0
mmc_bankrom(8, (irq.command & 0x40) ? 0xC000 : 0x8000, value & 0x3F);
break;
case 7: // PRG bank 1
mmc_bankrom(8, 0xA000, value & 0x3F);
break;
}
break;
}
case 0xA000:
if (fourscreen)
ppu_setmirroring(PPU_MIRROR_FOUR);
else if (value & 1)
ppu_setmirroring(PPU_MIRROR_HORI);
else
ppu_setmirroring(PPU_MIRROR_VERT);
break;
case 0xC000:
// IRQ latch
irq.latch = value;
break;
case 0xC001:
// IRQ reload
irq.counter = 0;
break;
case 0xE000:
// IRQ disable
irq.enabled = false;
break;
case 0xE001:
// IRQ enable
irq.enabled = true;
break;
default:
MESSAGE_DEBUG("map165: unhandled write: address=%p, value=0x%x\n", (void*)address, value);
break;
}
}
/* HBlank callback for IRQ */ static void map_hblank(nes_t *nes) { if (nes->scanline > 240) return;
if (!ppu_enabled())
return;
if (irq.counter == 0)
irq.counter = irq.latch;
else
irq.counter--;
if (irq.enabled && irq.counter == 0)
nes6502_irq();
}
/* State save/load */ static void map_getstate(uint8 *state) { state[0] = irq.counter; state[1] = irq.latch; state[2] = irq.enabled; state[3] = irq.command; state[4] = irq.chr_mode; memcpy(state + 5, irq.reg, sizeof(irq.reg)); }
static void map_setstate(uint8 *state) { irq.counter = state[0]; irq.latch = state[1]; irq.enabled = state[2]; irq.command = state[3]; irq.chr_mode = state[4]; memcpy(irq.reg, state + 5, sizeof(irq.reg)); update_chr_banks(); }
/* Mapper initialization */ static void map_init(rom_t *cart) { irq.enabled = irq.counter = irq.latch = 0; reg8000 = vrombase = 0; fourscreen = cart->fourscreen;
memset(&irq, 0, sizeof(irq));
// Initial banks
mmc_bankrom(16, 0x8000, 0); // Set PRG bank 0
mmc_bankrom(16, 0xC000, MMC_LASTBANK); // Set PRG bank 1
// Initial mirroring
ppu_setmirroring(PPU_MIRROR_VERT);
// Initial CHR banks
update_chr_banks();
}
mapintf_t map165_intf = { .number = 165, .name = "NES-EVENT", .init = map_init, .vblank = NULL, .hblank = map_hblank, .get_state = map_getstate, .set_state = map_setstate, .mem_read = {}, .mem_write = { { 0x8000, 0xFFFF, map_write } }, };