From 10c7ca845b317469a8cf37b4404373adad62ec32 Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Mon, 17 Mar 2025 02:15:33 -0700 Subject: [PATCH] Add Mapper 1 (MMC1) with a little refactoring --- src/cart.c | 17 ++++- src/f6502.c | 17 +++-- src/map/map000.c | 7 +- src/map/map001.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++ src/mapper.c | 3 +- src/mapper.h | 16 +++-- src/memory.h | 2 +- 7 files changed, 205 insertions(+), 19 deletions(-) create mode 100644 src/map/map001.c diff --git a/src/cart.c b/src/cart.c index e994a0c..5eeb824 100644 --- a/src/cart.c +++ b/src/cart.c @@ -53,15 +53,26 @@ int nes_cart_load_mem(void* data, int size, nes* sys) { } if (0 == status) { - // Unused & Palette - for (int page = 12; page < 16; ++page) { - mem->ppu.bank[page] = mem->ppu.pal_bank; + // PPU Bank 0 - 7: Default pattern tables + for (int page = 0; page < 8; ++page) { + mem->ppu.bank[page] = chr_page(&mem->ppu, page); } + // PPU Bank 8 - 11: Mirrored VRAM nes_ppu_set_mirroring( &mem->ppu, hdr->flags_6 & ines_Flag_Vert_Mirror ); + // PPU Bank 12 - 15: Unused & Palette + for (int page = 12; page < 16; ++page) { + mem->ppu.bank[page] = mem->ppu.pal_bank; + } + + // Map the SRAM bank to battery-backed SRAM if present + if (hdr->flags_6 & ines_Flag_Battery) { + mem->sram_bank = mem->sram; + } + int i_mapper = (hdr->flags_7 & ines_Mapper_Nibble_Hi) | ((hdr->flags_6 & ines_Mapper_Nibble_Lo) >> 4); const nes_Mapper *mapper = nes_mappers[i_mapper]; diff --git a/src/f6502.c b/src/f6502.c index 3b05c0b..e054797 100644 --- a/src/f6502.c +++ b/src/f6502.c @@ -293,13 +293,22 @@ static inline int f6502_write(nes_Memory* mem, break; case 0x6000: - if (mem->sram_bank) { + if (mem->mapper.write_sram) { + ret = mem->mapper.write_sram(&mem->mapper, + mem, addr, val); + } else if (mem->sram_bank) { mem->sram_bank[addr & 0x1FFFU] = val; mem->flags |= nes_Mem_SRAM_Used; + } + break; - } else if (mem->mapper.write_sram) { - ret = mem->mapper.write_sram(&mem->mapper, - addr, val); + case 0x8000: + case 0xA000: + case 0xC000: + case 0xE000: + if (mem->mapper.write_rom) { + ret = mem->mapper.write_rom(&mem->mapper, + mem, addr, val); } } #endif diff --git a/src/map/map000.c b/src/map/map000.c index 86eb89d..9fb6de3 100644 --- a/src/map/map000.c +++ b/src/map/map000.c @@ -15,17 +15,12 @@ static int map000_init(nes_Mapper* map, const ines_Header* hdr, mem->rom_bank[3] = prg_rom_page(mem, 1); } - int page = 0; - // Pattern tables - for ( ; page < 8; ++page) { - mem->ppu.bank[page] = chr_page(&mem->ppu, page); - } - return 0; } const nes_Mapper map000 = { + .name = "NROM", .max_chr_banks = 8, .init = map000_init, }; diff --git a/src/map/map001.c b/src/map/map001.c new file mode 100644 index 0000000..1ae854b --- /dev/null +++ b/src/map/map001.c @@ -0,0 +1,162 @@ +#include + +#include "memory.h" + +//#define DEBUG "MMC1" +#include "log.h" + + +typedef struct { + uint8_t reg_shift; + uint8_t reg_n_shift; + uint8_t reg_control; + uint8_t reg_chr_0; + uint8_t reg_chr_1; + uint8_t reg_prg; +} map001_data; + + +static inline void mmc1_update_vram(map001_data* data, + nes_Memory* mem) { + const nes_Nametable_Mirroring mirror[4] = { + nes_Mirror_Only_0, + nes_Mirror_Only_1, + nes_Mirror_Vertical, + nes_Mirror_Horizontal, + }; + LOGD("Mirroring %d", mirror[data->reg_control & 0b11]); + nes_ppu_set_mirroring(&mem->ppu, + mirror[data->reg_control & 0b11]); +} + +static inline void mmc1_update_chr(map001_data* data, + nes_Memory* mem) { + int n_banks = mem->ppu.n_chr_banks / 4; + int bank_0 = 0, bank_1 = 0; + if (!(data->reg_control & 0b10000)) { + bank_0 = (data->reg_chr_0 & 0b11110) % n_banks; + bank_1 = bank_0 + 1; + } else { + bank_0 = data->reg_chr_0 % n_banks; + bank_1 = data->reg_chr_1 % n_banks; + } + + LOGD("CHR: %d + %d", bank_0, bank_1); + + mem->ppu.bank[0] = chr_page(&mem->ppu, (bank_0 * 4) + 0); + mem->ppu.bank[1] = chr_page(&mem->ppu, (bank_0 * 4) + 1); + mem->ppu.bank[2] = chr_page(&mem->ppu, (bank_0 * 4) + 2); + mem->ppu.bank[3] = chr_page(&mem->ppu, (bank_0 * 4) + 3); + mem->ppu.bank[4] = chr_page(&mem->ppu, (bank_1 * 4) + 0); + mem->ppu.bank[5] = chr_page(&mem->ppu, (bank_1 * 4) + 1); + mem->ppu.bank[6] = chr_page(&mem->ppu, (bank_1 * 4) + 2); + mem->ppu.bank[7] = chr_page(&mem->ppu, (bank_1 * 4) + 3); +} + +static inline void mmc1_update_prg(map001_data* data, + nes_Memory* mem) { + int n_banks = mem->n_rom_banks / 2; + int mode = (data->reg_control >> 2) & 3; + int bank_0 = (data->reg_prg & 0b01111) % n_banks; + int bank_1 = bank_0; + if (!(mode & 0b10)) { + bank_0 = (bank_0 & 0b01110); + bank_1 = bank_0 + 1; + } else if (mode == 2) { + bank_0 = 0; + } else if (mode == 3) { + bank_1 = n_banks - 1; + } + + LOGD("PRG: %d + %d", bank_0, bank_1); + + mem->rom_bank[0] = prg_rom_page(mem, (bank_0 * 2) + 0); + mem->rom_bank[1] = prg_rom_page(mem, (bank_0 * 2) + 1); + mem->rom_bank[2] = prg_rom_page(mem, (bank_1 * 2) + 0); + mem->rom_bank[3] = prg_rom_page(mem, (bank_1 * 2) + 1); +} + +static void map001_reset(nes_Mapper* map, nes_Memory* mem) { + map001_data* data = (map001_data*)map->data; + data->reg_shift = 0b10000; + data->reg_control = 0b01100; + data->reg_chr_0 = 0; + data->reg_chr_1 = 0; + data->reg_prg = 0; + mmc1_update_prg(data, mem); + mmc1_update_chr(data, mem); + mmc1_update_vram(data, mem); +} + +static int map001_init(nes_Mapper* map, const ines_Header* hdr, + nes_Memory* mem) { + int status = -1; + + map->data = calloc(1, sizeof(map001_data)); + + if (NULL != map->data) { + map001_reset(map, mem); + status = 0; + } + + return status; +} + +static int map001_write_rom(nes_Mapper* map, nes_Memory* mem, + uint16_t addr, uint8_t val) { + map001_data* data = (map001_data*)map->data; + + LOGD("Write $%04x < %02x", addr, val); + + if (val & 0x80U) { + data->reg_shift = 0b10000; + data->reg_control |= 0b01100; + mmc1_update_prg(data, mem); + } else { + // TODO: Handle consecutive-cycle writes? + int done = (data->reg_shift & 1); + data->reg_shift = (data->reg_shift >> 1) | + ((val & 1) << 4); + if (done) { + switch ((addr >> 13) & 3) { + case 0: + LOGD("Control %02x", data->reg_shift); + data->reg_control = data->reg_shift; + mmc1_update_chr(data, mem); + mmc1_update_vram(data, mem); + mmc1_update_prg(data, mem); + break; + + case 1: + LOGD("CHR_0 %02x", data->reg_shift); + data->reg_chr_0 = data->reg_shift; + mmc1_update_chr(data, mem); + break; + + case 2: + LOGD("CHR_1 %02x", data->reg_shift); + data->reg_chr_1 = data->reg_shift; + mmc1_update_chr(data, mem); + break; + + case 3: + LOGD("PRG %02x", data->reg_shift); + data->reg_prg = data->reg_shift; + mmc1_update_prg(data, mem); + break; + } + data->reg_shift = 0b10000; + } + } + + return 0; +} + + +const nes_Mapper map001 = { + .name = "MMC1", + .max_chr_banks = 128, + .init = map001_init, + .reset = map001_reset, + .write_rom = map001_write_rom, +}; diff --git a/src/mapper.c b/src/mapper.c index e49db98..6b6c27e 100644 --- a/src/mapper.c +++ b/src/mapper.c @@ -2,8 +2,9 @@ extern const nes_Mapper map000; - +extern const nes_Mapper map001; const nes_Mapper* nes_mappers[256] = { &map000, + &map001, }; diff --git a/src/mapper.h b/src/mapper.h index 88c282b..c277cdb 100644 --- a/src/mapper.h +++ b/src/mapper.h @@ -9,16 +9,24 @@ struct nes_Memory; struct nes_Mapper { + const char* name; int max_chr_banks; + int (*init)(struct nes_Mapper*, const ines_Header*, struct nes_Memory*); - bool (*write_rom)(struct nes_Mapper*, uint16_t addr, uint8_t val); - bool (*write_sram)(struct nes_Mapper*, uint16_t addr, uint8_t val); - bool (*write_apu)(struct nes_Mapper*, uint16_t addr, uint8_t val); - uint8_t (*read_apu)(struct nes_Mapper*, uint16_t addr); + void (*done)(struct nes_Mapper*); + void (*reset)(struct nes_Mapper*, struct nes_Memory*); + + int (*write_rom)(struct nes_Mapper*, struct nes_Memory*, uint16_t addr, uint8_t val); + int (*write_sram)(struct nes_Mapper*, struct nes_Memory*, uint16_t addr, uint8_t val); + int (*write_apu)(struct nes_Mapper*, struct nes_Memory*, uint16_t addr, uint8_t val); + uint8_t (*read_apu)(struct nes_Mapper*, struct nes_Memory*, uint16_t addr); + void (*hsync)(struct nes_Mapper*); void (*vsync)(struct nes_Mapper*); + void (*ppu_bus)(struct nes_Mapper*, uint16_t addr); void (*ppu_mem_mode)(struct nes_Mapper*, uint8_t mode); + void* data; }; typedef struct nes_Mapper nes_Mapper; diff --git a/src/memory.h b/src/memory.h index a417ea2..6a1a1f9 100644 --- a/src/memory.h +++ b/src/memory.h @@ -23,9 +23,9 @@ struct nes_Memory { uint8_t ram[65536]; #else uint8_t ram[NES_RAM_SIZE / 4]; // Mirrored 3x + uint8_t sram[NES_SRAM_SIZE]; nes_Memory_Flag flags; uint8_t reserved[3]; - uint8_t* sram; // Actual SRAM, if used - could this be a flag? uint8_t* sram_bank; // Mapped to 0x6000 - 0x7FFF uint8_t* rom; uint8_t* rom_bank[4];