#include "memory.h" //#define NESE_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) { map001_reset(map, mem); return 0; } 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, .data_size = sizeof(map001_data), .init = map001_init, .reset = map001_reset, .write_rom = map001_write_rom, };