diff --git a/Makefile b/Makefile index e866eef..778b508 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ SRC_SRCS_1 += cart.c mapper.c SRC_SRCS_1 += sdl_render.c sdl_input.c MAPDIR = src/map -MAP_SRCS_1 = nrom.c mmc1.c cnrom.c +MAP_SRCS_1 = nrom.c mmc1.c cnrom.c mmc3.c EXT_SRCS_1 = e6502/e6502.c diff --git a/src/map/mmc1.c b/src/map/mmc1.c index 0486300..0c51be6 100644 --- a/src/map/mmc1.c +++ b/src/map/mmc1.c @@ -3,9 +3,6 @@ #include "map.h" -#define MAP_LOG(fmt, ...) //printf("MAP: " fmt "\n" __VA_OPT__(,) __VA_ARGS__) - - typedef struct { // TODO: Does this even support CHR ROM? uint8_t* prg_rom; diff --git a/src/map/mmc3.c b/src/map/mmc3.c new file mode 100644 index 0000000..887f8d4 --- /dev/null +++ b/src/map/mmc3.c @@ -0,0 +1,270 @@ +#include + +#include "map.h" + +typedef enum { + mmc3_Flag_Horizontal = 0b00000001, + mmc3_Flag_IRQ_Enabled = 0b00000010, + 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* prg_rom; + int prg_rom_banks; // 16 KB / 8 KB = 2 + uint8_t* chr_rom; + int chr_rom_banks; // 4 KB / 1 KB = 4 + + uint8_t* prg_bank[4]; // 8 KB + uint8_t* chr_bank[8]; // 1 KB + uint8_t* vram_bank[4]; + + mmc3_Flag flags; + uint8_t bank_select; + uint8_t irq_count; + uint8_t irq_latch; + + uint8_t wram[nes_mem_wram_size]; + uint8_t vram[2][nes_vram_page_size]; +} mmc3_mapper; + +static inline uint8_t* mmc3_prg_bank(mmc3_mapper* map, int bank) { + return &map->prg_rom[bank << 13]; +} + +static inline void mmc3_map_prg(mmc3_mapper* map, + int reg, int bank) { + MAP_LOG("PRG ROM: 8k $%04x <- bank %d", reg << 13, bank); + map->prg_bank[reg] = mmc3_prg_bank(map, bank); +} + +static inline void mmc3_update_rom_mode(mmc3_mapper* map) { + if (!(map->bank_select & mmc3_Bank_Select_PRG)) { + mmc3_map_prg(map, 2, map->prg_rom_banks - 2); + } else { + mmc3_map_prg(map, 0, map->prg_rom_banks - 2); + } +} + +static inline void mmc3_update_prg(mmc3_mapper* map, uint8_t bank) { + int reg = (map->bank_select & mmc3_Bank_Select_Reg); + + if (!(map->bank_select & mmc3_Bank_Select_PRG)) { + if (reg == 7) { + mmc3_map_prg(map, 1, bank); + } else { + mmc3_map_prg(map, 0, bank); + } + + } else { + if (reg == 7) { + mmc3_map_prg(map, 1, bank); + } else { + mmc3_map_prg(map, 2, bank); + } + } + +} + +static inline uint8_t* mmc3_chr_bank(mmc3_mapper* map, int bank) { + return &map->chr_rom[bank << 10]; +} + +static inline void mmc3_map_2k_chr(mmc3_mapper* map, + int reg, int bank) { + MAP_LOG("CHR ROM: 2k $%04x <- bank %d + %d", reg << 10, bank & ~1, bank | 1); + map->chr_bank[reg + 0] = mmc3_chr_bank(map, bank & ~1); + map->chr_bank[reg + 1] = mmc3_chr_bank(map, bank | 1); +} + +static inline void mmc3_map_1k_chr(mmc3_mapper* map, + int reg, int bank) { + MAP_LOG("CHR ROM: 1k $%04x <- bank %d", reg << 10, bank); + map->chr_bank[reg] = mmc3_chr_bank(map, bank); +} + +static inline void mmc3_update_chr(mmc3_mapper* map, uint8_t bank) { + int reg = (map->bank_select & mmc3_Bank_Select_Reg); + + if (!(map->bank_select & mmc3_Bank_Select_CHR)) { + if (1 >= reg) { + mmc3_map_2k_chr(map, reg * 2, bank); + } else { + mmc3_map_1k_chr(map, reg + 2, bank); + } + + } else { + if (1 >= reg) { + mmc3_map_2k_chr(map, 4 + (reg * 2), bank); + } else { + mmc3_map_1k_chr(map, reg - 2, bank); + } + } +} + +static inline void mmc3_update_vram(mmc3_mapper* map) { + if (!(map->flags & mmc3_Flag_Horizontal)) { + // Vertical mirroring + MAP_LOG("Vertical mirroring"); + map->vram_bank[0] = map->vram_bank[2] = map->vram[0]; + map->vram_bank[1] = map->vram_bank[3] = map->vram[1]; + } else { + // Horizontal mirroring + MAP_LOG("Horizontal mirroring"); + map->vram_bank[0] = map->vram_bank[1] = map->vram[0]; + map->vram_bank[2] = map->vram_bank[3] = map->vram[1]; + } +} + +static void mmc3_reset(nes_mapper* nes_map) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + map->flags = 0; + map->bank_select = 0; + map->irq_count = 0; + map->irq_latch = 0; + map->prg_bank[3] = mmc3_prg_bank(map, map->prg_rom_banks - 1); + mmc3_update_vram(map); +} + +static int mmc3_init(nes_mapper* nes_map, nes_cart* cart) { + mmc3_mapper* map = calloc(1, sizeof(mmc3_mapper)); + nes_map->data = map; + if (NULL != map) { + map->prg_rom = cart->prg_rom; + map->prg_rom_banks = cart->prg_rom_banks * 2; + map->chr_rom = cart->chr_rom; + map->chr_rom_banks = cart->chr_rom_banks * 4; + map->flags = (cart->flags & Cart_Flag_Horizontal) ? + mmc3_Flag_Horizontal : 0; + mmc3_reset(nes_map); + } + return (NULL == map ? -1 : 0); +} + +static void mmc3_done(nes_mapper* nes_map) { + free(nes_map->data); +} + +static inline uint8_t* mmc3_prg_addr(mmc3_mapper* map, + uint16_t addr) { + return &(map->prg_bank[(addr >> 13) & 3][addr & 0x1FFFU]); +} + +static inline uint8_t* mmc3_wram_addr(mmc3_mapper* map, + uint16_t addr) { + return &(map->wram[addr & 0x1FFFU]); +} + +static uint8_t mmc3_read(nes_mapper* nes_map, uint16_t addr) { + uint8_t* ptr = NULL; + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + if (addr >= nes_mem_rom_start) { + ptr = mmc3_prg_addr(map, addr); + } else if ( addr >= nes_mem_wram_start && + (map->flags & mmc3_Flag_WRAM_Enabled)) { + ptr = mmc3_wram_addr(map, addr); + MAP_LOG("WRAM: $%04x > %02x", addr, *ptr); + } + + uint8_t val = (NULL == ptr ? 0 : *ptr); + +// MAP_LOG("$%04x -> %04lx > %02x", addr, ptr - map->prg_rom, val); + + return val; +} + +static void mmc3_write(nes_mapper* nes_map, + uint16_t addr, uint8_t val) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + + if (addr >= nes_mem_rom_start) MAP_LOG("$%04x < %02x", addr, val); + + if (addr < nes_mem_wram_start) { + // Nothing prior to WRAM + + } else if (addr < nes_mem_rom_start) { + if ( (map->flags & mmc3_Flag_WRAM_Enabled) && + !(map->flags & mmc3_Flag_WRAM_Protect)) { +// MAP_LOG("WRAM: $%04x < %02x", addr, val); + *(mmc3_wram_addr(map, addr)) = val; + } + + } else if (addr < 0xA000U) { + if (addr & 1) { + // Bank data + if ((map->bank_select & mmc3_Bank_Select_Reg) >= 6) { + mmc3_update_prg(map, val); + } else { + mmc3_update_chr(map, val); + } + } else { + // Bank select + map->bank_select = val; + mmc3_update_rom_mode(map); + } + + } else if (addr < 0xC000U) { + if (addr & 1) { + MAP_LOG("WRAM %s, %s", (val & mmc3_Flag_WRAM_Enabled) ? "enabled" : "disabled", (val & mmc3_Flag_WRAM_Protect) ? "protected" : "writable"); + // WRAM protection + map->flags &= ~(mmc3_Flag_WRAM_Enabled | + mmc3_Flag_WRAM_Protect); + map->flags |= (val & (mmc3_Flag_WRAM_Enabled | + mmc3_Flag_WRAM_Protect)); + } else { + // Mirroring + map->flags &= ~mmc3_Flag_Horizontal; + map->flags |= (val & mmc3_Flag_Horizontal); + mmc3_update_vram(map); + } + + } else if (addr < 0xE000U) { + if (addr & 1) { + // TODO: IRQ reload + } else { + // TODO: IRQ latch + } + + } else { + if (addr & 1) { + // TODO: IRQ enable + } else { + // TODO: IRQ disable + } + } +} + +static uint8_t* mmc3_chr_addr(nes_mapper* nes_map, + uint16_t addr) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + return &map->chr_bank[(addr >> 10) & 7][addr & 0x3FFU]; +} + +static uint8_t* mmc3_vram_addr(nes_mapper* nes_map, + uint16_t addr) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + return &map->vram_bank[(addr >> 10) & 3][addr & 0x3FFU]; +} + +static void mmc3_chr_write(nes_mapper* map, + uint16_t addr, uint8_t val) { + // ROM only. + printf("CHR ROM Write: $%04x < %02x\n", addr, val); +} + +nes_mapper mapper_mmc3 = { + .init = mmc3_init, + .reset = mmc3_reset, + .done = mmc3_done, + .read = mmc3_read, + .write = mmc3_write, + .chr_addr = mmc3_chr_addr, + .vram_addr = mmc3_vram_addr, + .chr_write = mmc3_chr_write, +}; diff --git a/src/mapper.c b/src/mapper.c index 2eadbde..7828047 100644 --- a/src/mapper.c +++ b/src/mapper.c @@ -4,10 +4,12 @@ extern nes_mapper mapper_nrom; extern nes_mapper mapper_mmc1; extern nes_mapper mapper_cnrom; +extern nes_mapper mapper_mmc3; nes_mapper* nes_mappers[256] = { [ 0] = &mapper_nrom, [ 1] = &mapper_mmc1, [ 3] = &mapper_cnrom, + [ 4] = &mapper_mmc3, }; diff --git a/src/mapper.h b/src/mapper.h index 0f6fd26..1aacb06 100644 --- a/src/mapper.h +++ b/src/mapper.h @@ -4,6 +4,9 @@ #include +#define MAP_LOG(fmt, ...) printf("MAP: " fmt "\n" __VA_OPT__(,) __VA_ARGS__) + + struct nes_cart_t; typedef struct nes_mapper_t {