|
- #include "memory.h"
-
- #define NESE_DEBUG "MMC3"
- #include "log.h"
-
-
- typedef enum {
- mmc3_Flag_Horizontal = 0b00000001,
- mmc3_Flag_IRQ_Enabled = 0b00000010,
- mmc3_Flag_IRQ_Reload = 0b00000100,
- mmc3_Flag_CHR_RAM = 0b00001000,
- mmc3_Flag_Battery = 0b00100000,
- mmc3_Flag_WRAM_Protect = 0b01000000,
- mmc3_Flag_WRAM_Enabled = 0b10000000,
- } mmc3_Flag;
-
- typedef enum {
- mmc3_Bank_Select_Reg = 0b00000111,
- mmc3_Bank_Select_PRG = 0b01000000,
- mmc3_Bank_Select_CHR = 0b10000000,
- } mmc3_Bank_Select;
-
- typedef struct {
- uint8_t r[8];
- uint8_t flags;
- uint8_t bank_select;
- uint8_t irq_count;
- uint8_t irq_latch;
- } map004_data;
-
-
- static inline void mmc3_update_vram(map004_data* data,
- nes_Memory* mem) {
- nes_ppu_set_mirroring(
- &mem->ppu,
- data->flags & mmc3_Flag_Horizontal ?
- nes_Mirror_Horizontal : nes_Mirror_Vertical
- );
- }
-
- static inline void mmc3_map_prg(nes_Memory* mem,
- int bank, int page) {
- LOGD("PRG ROM: 8k $%04x <- bank %d", 0x8000 + (bank << 13), page);
- mem->rom_bank[bank] = prg_rom_page(mem,
- page % mem->n_rom_banks);
- }
-
-
- static inline void mmc3_update_prg(map004_data* data,
- nes_Memory* mem,
- int reg, uint8_t bank) {
- if (reg == 7) {
- mmc3_map_prg(mem, 1, bank);
- } else {
- if (!(data->bank_select & mmc3_Bank_Select_PRG)) {
- mmc3_map_prg(mem, 0, bank);
- } else {
- mmc3_map_prg(mem, 2, bank);
- }
- }
- }
-
- static inline void mmc3_map_2k_chr(nes_Memory* mem,
- int reg, int bank) {
- bank = (bank & ~1) % mem->ppu.n_chr_banks;
- LOGD("CHR ROM: 2k $%04x <- bank %d + %d", reg << 10, bank, bank | 1);
- mem->ppu.bank[reg + 0] = chr_page(&mem->ppu, bank);
- mem->ppu.bank[reg + 1] = chr_page(&mem->ppu, bank | 1);
- }
-
- static inline void mmc3_map_1k_chr(nes_Memory* mem,
- int reg, int bank) {
- bank = bank % mem->ppu.n_chr_banks;
- LOGD("CHR ROM: 1k $%04x <- bank %d", reg << 10, bank);
- mem->ppu.bank[reg] = chr_page(&mem->ppu, bank);
- }
-
-
- static inline void mmc3_update_chr(map004_data* data,
- nes_Memory* mem,
- int reg, uint8_t bank) {
- if (!(data->bank_select & mmc3_Bank_Select_CHR)) {
- if (1 >= reg) {
- mmc3_map_2k_chr(mem, reg * 2, bank);
- } else {
- mmc3_map_1k_chr(mem, reg + 2, bank);
- }
-
- } else {
- if (1 >= reg) {
- mmc3_map_2k_chr(mem, 4 + (reg * 2), bank);
- } else {
- mmc3_map_1k_chr(mem, reg - 2, bank);
- }
- }
- }
-
- static inline void mmc3_update_rom_mode(map004_data* data,
- nes_Memory* mem,
- uint8_t val) {
- uint8_t delta = (data->bank_select ^ val);
-
- data->bank_select = val;
-
- if (delta & mmc3_Bank_Select_PRG) {
- mmc3_map_prg(mem, 1, data->r[7]);
- if (!(val & mmc3_Bank_Select_PRG)) {
- mmc3_map_prg(mem, 0, data->r[6]);
- mmc3_map_prg(mem, 2, mem->n_rom_banks - 2);
- } else {
- mmc3_map_prg(mem, 0, mem->n_rom_banks - 2);
- mmc3_map_prg(mem, 2, data->r[6]);
- }
- }
-
- if (delta & mmc3_Bank_Select_CHR) {
- mmc3_update_chr(data, mem, 0, data->r[0]);
- mmc3_update_chr(data, mem, 1, data->r[1]);
- mmc3_update_chr(data, mem, 2, data->r[2]);
- mmc3_update_chr(data, mem, 3, data->r[3]);
- mmc3_update_chr(data, mem, 4, data->r[4]);
- mmc3_update_chr(data, mem, 5, data->r[5]);
- }
- }
-
-
- static void map004_reset(nes_Mapper* map, nes_Memory* mem) {
- map004_data* data = (map004_data*)map->data;
- data->irq_count = 0;
- data->irq_latch = 0;
- mmc3_update_rom_mode(data, mem, 0);
- mmc3_map_prg(mem, 3, mem->n_rom_banks - 1);
- mmc3_update_vram(data, mem);
- }
-
- static int map004_init(nes_Mapper* map, const ines_Header* hdr,
- nes_Memory* mem) {
- map004_data* data = (map004_data*)map->data;
- data->flags = (hdr->flags_6 & ines_Flag_Vert_Mirror) ?
- mmc3_Flag_Horizontal : 0;
- data->bank_select = mmc3_Bank_Select_PRG |
- mmc3_Bank_Select_CHR;
- map004_reset(map, mem);
- return 0;
- }
-
- static int map004_protect_sram(nes_Mapper* map, nes_Memory* mem,
- uint16_t addr, uint8_t val) {
- // Intercept any writes!
- return 0;
- }
-
- static int map004_write_rom(nes_Mapper* map, nes_Memory* mem,
- uint16_t addr, uint8_t val) {
- map004_data* data = (map004_data*)map->data;
-
- LOGD("$%04x < %02x", (int)addr, (int)val);
-
- switch ((addr >> 13) & 3) {
- case 0: // 0x8000 - 0x9FFF
- if (addr & 1) {
- // Bank data
- int reg = (data->bank_select & mmc3_Bank_Select_Reg);
- if (reg >= 6) {
- mmc3_update_prg(data, mem, reg, val);
- } else {
- mmc3_update_chr(data, mem, reg, val);
- }
- data->r[reg] = val;
- } else {
- // Bank Select
- mmc3_update_rom_mode(data, mem, val);
- }
- break;
-
- case 1: // 0xA000 - 0xBFFF
- if (addr & 1) {
- LOGD("SRAM %s, %s", (val & mmc3_Flag_WRAM_Enabled) ? "enabled" : "disabled", (val & mmc3_Flag_WRAM_Protect) ? "protected" : "writable");
- // WRAM protection
- data->flags &= ~(mmc3_Flag_WRAM_Enabled |
- mmc3_Flag_WRAM_Protect);
- data->flags |= (val & (mmc3_Flag_WRAM_Enabled |
- mmc3_Flag_WRAM_Protect));
- // TODO: Set these on load / reset
- if (val & mmc3_Flag_WRAM_Enabled) {
- mem->sram_bank = mem->sram;
- } else {
- mem->sram_bank = NULL;
- }
- if (val & mmc3_Flag_WRAM_Protect) {
- map->write_sram = map004_protect_sram;
- } else {
- map->write_sram = NULL;
- }
- } else {
- // Mirroring
- data->flags &= ~mmc3_Flag_Horizontal;
- data->flags |= (val & mmc3_Flag_Horizontal);
- mmc3_update_vram(data, mem);
- }
- break;
-
- case 2: // 0xC000 - 0xDFFF
- if (addr & 1) {
- LOGD("IRQ Reload");
- data->flags |= mmc3_Flag_IRQ_Reload;
- } else {
- LOGD("IRQ Latch: %d", val);
- data->irq_latch = val;
- }
- break;
-
- case 3: // 0xE000 - 0xFFFF
- LOGD("IRQ %s", (addr & 1) ? "Enable" : "Disable");
- if (addr & 1) {
- data->flags |= mmc3_Flag_IRQ_Enabled;
- } else {
- data->flags &= ~mmc3_Flag_IRQ_Enabled;
- map->irq_callback(map->irq_arg, 0);
- }
- break;
- }
-
- return 0;
- }
-
- static int map004_hsync(nes_Mapper* map) {
- map004_data* data = (map004_data*)map->data;
-
- if ( data->irq_count <= 0 ||
- (data->flags & mmc3_Flag_IRQ_Reload)) {
- data->irq_count = data->irq_latch;
- data->flags &= ~mmc3_Flag_IRQ_Reload;
- } else {
- data->irq_count--;
- }
-
- if ( data->irq_count <= 0 &&
- (data->flags & mmc3_Flag_IRQ_Enabled)) {
- LOGD("IRQ Trigger");
- map->irq_callback(map->irq_arg, 1);
- data->irq_count = 0;
- }
-
- return 0;
- }
-
-
- const nes_Mapper map004 = {
- .name = "MMC3",
- .max_chr_banks = 256,
- .data_size = sizeof(map004_data),
- .init = map004_init,
- .reset = map004_reset,
- .write_rom = map004_write_rom,
- .hsync = map004_hsync,
- };
|