diff --git a/src/cart.c b/src/cart.c index a73ffeb..2b25940 100644 --- a/src/cart.c +++ b/src/cart.c @@ -19,7 +19,7 @@ int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { if (0 == status) { int prg_size = ines_prg_rom_chunk * hdr->prg_size_lsb; - if (prg_size > nes_mem_rom_size || prg_size <= 0) { + if (prg_size <= 0) { INES_ERR("Bad program ROM size: %d / %d", prg_size, nes_mem_rom_size); status = -1; @@ -32,19 +32,10 @@ int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { if (0 == status) { int chr_size = ines_chr_rom_chunk * hdr->chr_size_lsb; - if (chr_size > nes_ppu_mem_size || chr_size <= 0) { - INES_ERR("Bad sprite ROM size: %d / %d", - chr_size, nes_ppu_mem_size); - status = -1; - - } else { - cart->chr_rom = ptr; - cart->chr_rom_size = chr_size; - } + cart->chr_rom = ptr; + cart->chr_rom_size = chr_size; ptr += chr_size; - } - if (0 == status) { int index = (hdr->flags_6 & ines_Mapper_Nibble_Lo) >> 4 | (hdr->flags_7 & ines_Mapper_Nibble_Hi); cart->mapper = nes_mappers[index]; diff --git a/src/cart.h b/src/cart.h index 334fe27..bb6c757 100644 --- a/src/cart.h +++ b/src/cart.h @@ -5,9 +5,6 @@ #include -#define nes_mem_wram_size (0x2000U) - - typedef enum { Cart_Flag_Vertical = 0b0, Cart_Flag_Horizontal = 0b1, @@ -18,7 +15,6 @@ typedef struct nes_cart_t { int prg_rom_size; uint8_t* chr_rom; int chr_rom_size; - uint8_t wram[nes_mem_wram_size]; nes_Cart_Flags flags; struct nes_mapper_t* mapper; diff --git a/src/map/map.h b/src/map/map.h index a98997c..4a493f1 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -1,2 +1,2 @@ +#include "../nes.h" #include "../mapper.h" -#include "../cart.h" diff --git a/src/map/mmc1.c b/src/map/mmc1.c index 98da90a..2e71a90 100644 --- a/src/map/mmc1.c +++ b/src/map/mmc1.c @@ -1,72 +1,214 @@ +#include + #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; int prg_rom_size; - uint8_t* prg_ram; - int prg_ram_size; - uint8_t* chr_rom; - int chr_rom_size; 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; + + uint8_t* chr_bank[2]; + uint8_t* vram_bank[4]; + uint8_t* prg_bank[2]; + + uint8_t vram[2][nes_vram_page_size]; + uint8_t chr[32][nes_chr_page_size]; + uint8_t wram[nes_mem_wram_size]; } mmc1_mapper; -/* -int mmc1_init(nes_mapper* nes_map, nes_cart* cart) { - nrom_mapper* map = malloc(sizeof(nrom_mapper)); + +static void mmc1_update_vram(mmc1_mapper* map) { + // VRAM selection + int nametable = (map->reg_control & 0b11); + if (nametable == 0) { + MAP_LOG("VRAM: 1-screen, lower bank"); + map->vram_bank[0] = map->vram_bank[1] = + map->vram_bank[2] = map->vram_bank[3] = + map->vram[0]; + } else if (nametable == 1) { + MAP_LOG("VRAM: 1-screen, upper bank"); + map->vram_bank[0] = map->vram_bank[1] = + map->vram_bank[2] = map->vram_bank[3] = + map->vram[1]; + } else if (nametable == 2) { + MAP_LOG("VRAM: 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 if (nametable == 3) { + MAP_LOG("VRAM: 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 mmc1_update_chr(mmc1_mapper* map) { + // CHR RAM selection + if (!(map->reg_control & 0b10000)) { + int bank = (map->reg_chr_0 & 0b11110); + MAP_LOG("CHR: 8 KB: %d + %d", bank, bank + 1); + map->chr_bank[0] = map->chr[bank]; + map->chr_bank[1] = map->chr[bank + 1]; + } else { + MAP_LOG("CHR: %d + %d", map->reg_chr_0, map->reg_chr_1); + map->chr_bank[0] = map->chr[map->reg_chr_0]; + map->chr_bank[1] = map->chr[map->reg_chr_1]; + } +} + +static void mmc1_update_prg(mmc1_mapper* map) { + // PRG ROM selection + int mode = (map->reg_control >> 2) & 3; + int bank = (map->reg_prg & 0b01111); + if (!(mode & 0b10)) { + bank = (bank & 0b01110); + MAP_LOG("PRG: 32 KB %d + %d", bank, bank + 1); + bank *= nes_prg_rom_page_size; + map->prg_bank[0] = &map->prg_rom[bank]; + map->prg_bank[1] = &map->prg_rom[bank + + nes_prg_rom_page_size]; + } else if (mode == 2) { + MAP_LOG("PRG: %d + %d", 0, bank); + bank *= nes_prg_rom_page_size; + map->prg_bank[0] = &map->prg_rom[0]; + map->prg_bank[1] = &map->prg_rom[bank]; + } else if (mode == 3) { + MAP_LOG("PRG: %d + %d", bank, (map->prg_rom_size / + nes_prg_rom_page_size) - 1); + bank *= nes_prg_rom_page_size; + map->prg_bank[0] = &map->prg_rom[bank]; + map->prg_bank[1] = &map->prg_rom[map->prg_rom_size - + nes_prg_rom_page_size]; + } +} + +static void mmc1_reset(nes_mapper* nes_map) { + mmc1_mapper* map = (mmc1_mapper*)nes_map->data; + map->reg_shift = 0b10000; + map->reg_control = 0; + map->reg_chr_0 = 0; + map->reg_chr_1 = 0; + map->reg_prg = 0; + mmc1_update_prg(map); + mmc1_update_chr(map); + mmc1_update_vram(map); +} + +static int mmc1_init(nes_mapper* nes_map, nes_cart* cart) { + mmc1_mapper* map = calloc(1, sizeof(mmc1_mapper)); nes_map->data = map; if (NULL != map) { map->prg_rom = cart->prg_rom; map->prg_rom_size = cart->prg_rom_size; - map->chr_rom = cart->chr_rom; - map->chr_rom_size = cart->chr_rom_size; - map->prg_ram = cart->wram; - map->prg_ram_size = nes_mem_wram_size; - - map->shift_reg = 0; + mmc1_reset(nes_map); } return (NULL == map ? -1 : 0); } -void nrom_done(nes_mapper* nes_map) { +static void mmc1_done(nes_mapper* nes_map) { free(nes_map->data); } -static inline uint8_t* nrom_prg_addr(nrom_mapper* map, +static inline uint8_t* mmc1_prg_addr(mmc1_mapper* map, uint16_t addr) { - if (addr > map->prg_rom_size) { - addr &= 0x3FFF; + int bank = (addr >> 14) & 1; + addr &= 0x3FFFU; + return &map->prg_bank[bank][addr]; +} + +static inline uint8_t* mmc1_wram_addr(mmc1_mapper* map, + uint16_t addr) { + return &(map->wram[addr & 0x1FFFU]); +} + +static uint8_t mmc1_read(nes_mapper* map, uint16_t addr) { + uint8_t val = 0; + if (addr >= nes_mem_rom_start) { + val = *(mmc1_prg_addr((mmc1_mapper*)map->data, addr)); + } else if (addr >= nes_mem_wram_start) { + val = *(mmc1_wram_addr((mmc1_mapper*)map->data, addr)); } - return &(map->prg_rom[addr]); + return val; } -uint8_t nrom_prg_read(nes_mapper* map, uint16_t addr) { - return *(nrom_prg_addr((nrom_mapper*)map->data, addr)); +static void mmc1_write(nes_mapper* nes_map, + uint16_t addr, uint8_t val) { + mmc1_mapper* map = (mmc1_mapper*)nes_map->data; + if (addr >= nes_mem_rom_start) { + MAP_LOG("Write $%04x < %02x", addr, val); + if (val & 0x80U) { + MAP_LOG("Resetting"); + map->reg_shift = 0b10000; + map->reg_control |= 0b01100; + mmc1_update_prg(map); + } else { + // TODO: Handle consective-cycle writes? + int done = (map->reg_shift & 1); + map->reg_shift = (map->reg_shift >> 1) | + ((val & 1) << 4); + if (done) { + int reg = (addr >> 13) & 0b11; + if (reg == 0) { + MAP_LOG("Control %02x", map->reg_shift); + map->reg_control = map->reg_shift; + mmc1_update_chr(map); + mmc1_update_vram(map); + mmc1_update_prg(map); + } else if (reg == 1) { + MAP_LOG("CHR_0 %02x", map->reg_shift); + map->reg_chr_0 = map->reg_shift; + mmc1_update_chr(map); + } else if (reg == 2) { + MAP_LOG("CHR_1 %02x", map->reg_shift); + map->reg_chr_1 = map->reg_shift; + mmc1_update_chr(map); + } else { + MAP_LOG("PRG %02x", map->reg_shift); + map->reg_prg = map->reg_shift; + mmc1_update_prg(map); + } + map->reg_shift = 0b10000; + } + } + + } else if ( addr < nes_mem_rom_start && + addr >= nes_mem_wram_start) { + *(mmc1_wram_addr(map, addr)) = val; + } } -void nrom_prg_write(nes_mapper* map, uint16_t addr, uint8_t val) { - // No ROM writes. +static uint8_t* mmc1_chr_addr(nes_mapper* nes_map, + uint16_t addr) { + int page = (addr >> 12) & 1; + addr &= 0xFFFU; + return &((mmc1_mapper*)nes_map->data)->chr_bank[page][addr]; } -uint8_t* nrom_chr_addr(nes_mapper* nes_map, uint16_t addr) { - nrom_mapper* map = (nrom_mapper*)nes_map->data; - return &map->chr_rom[addr % map->chr_rom_size]; +static uint8_t* mmc1_vram_addr(nes_mapper* nes_map, + uint16_t addr) { + int page = (addr >> 10) & 3; + int loc = addr & 0x3FFU; +// MAP_LOG("VRAM $%04x -> %p", addr, ((mmc1_mapper*)nes_map->data)->vram_bank[page]); + return &((mmc1_mapper*)nes_map->data)->vram_bank[page][loc]; } -*/ nes_mapper mapper_mmc1 = { -/* .init = mmc1_init, + .reset = mmc1_reset, .done = mmc1_done, - .prg_read = mmc1_prg_read, - .prg_write = mmc1_prg_write, + .read = mmc1_read, + .write = mmc1_write, .chr_addr = mmc1_chr_addr, - .vram_addr = mmc1_chr_addr, -*/ + .vram_addr = mmc1_vram_addr, }; diff --git a/src/map/nrom.c b/src/map/nrom.c index 0bdf44e..23937da 100644 --- a/src/map/nrom.c +++ b/src/map/nrom.c @@ -6,16 +6,17 @@ typedef struct { uint8_t* prg_rom; int prg_rom_size; - uint8_t* prg_ram; - int prg_ram_size; uint8_t* chr_rom; int chr_rom_size; uint8_t mirror; uint8_t vram[nes_vram_page_size * 2]; + uint8_t wram[nes_mem_wram_size]; } nrom_mapper; -int nrom_init(nes_mapper* nes_map, nes_cart* cart) { +static void nrom_reset(nes_mapper* nes_map) {} + +static int nrom_init(nes_mapper* nes_map, nes_cart* cart) { nrom_mapper* map = calloc(1, sizeof(nrom_mapper)); nes_map->data = map; if (NULL != map) { @@ -23,39 +24,54 @@ int nrom_init(nes_mapper* nes_map, nes_cart* cart) { map->prg_rom_size = cart->prg_rom_size; map->chr_rom = cart->chr_rom; map->chr_rom_size = cart->chr_rom_size; - map->prg_ram = cart->wram; - map->prg_ram_size = nes_mem_wram_size; map->mirror = (cart->flags & Cart_Flag_Horizontal) ? 0 : 1; } return (NULL == map ? -1 : 0); } -void nrom_done(nes_mapper* nes_map) { +static void nrom_done(nes_mapper* nes_map) { free(nes_map->data); } static inline uint8_t* nrom_prg_addr(nrom_mapper* map, uint16_t addr) { + addr &= 0x7FFFU; if (addr > map->prg_rom_size) { addr &= 0x3FFF; } return &(map->prg_rom[addr]); } -uint8_t nrom_prg_read(nes_mapper* map, uint16_t addr) { - return *(nrom_prg_addr((nrom_mapper*)map->data, addr)); +static inline uint8_t* nrom_wram_addr(nrom_mapper* map, + uint16_t addr) { + return &(map->wram[addr & 0x1FFFU]); +} + +static uint8_t nrom_read(nes_mapper* map, uint16_t addr) { + uint8_t val = 0; + if (addr >= nes_mem_rom_start) { + val = *(nrom_prg_addr((nrom_mapper*)map->data, addr)); + } else if (addr >= nes_mem_wram_start) { + val = *(nrom_wram_addr((nrom_mapper*)map->data, addr)); + } + return val; } -void nrom_prg_write(nes_mapper* map, uint16_t addr, uint8_t val) { - // No ROM writes. +static void nrom_write(nes_mapper* map, + uint16_t addr, uint8_t val) { + if (addr < nes_mem_rom_start && addr >= nes_mem_wram_start) { + *(nrom_wram_addr((nrom_mapper*)map->data, addr)) = val; + } } -uint8_t* nrom_chr_addr(nes_mapper* nes_map, uint16_t addr) { +static uint8_t* nrom_chr_addr(nes_mapper* nes_map, + uint16_t addr) { nrom_mapper* map = (nrom_mapper*)nes_map->data; return &map->chr_rom[addr % map->chr_rom_size]; } -uint8_t* nrom_vram_addr(nes_mapper* nes_map, uint16_t addr) { +static uint8_t* nrom_vram_addr(nes_mapper* nes_map, + uint16_t addr) { nrom_mapper* map = (nrom_mapper*)nes_map->data; int page = addr >> 10U; page >>= map->mirror; @@ -65,9 +81,10 @@ uint8_t* nrom_vram_addr(nes_mapper* nes_map, uint16_t addr) { nes_mapper mapper_nrom = { .init = nrom_init, + .reset = nrom_reset, .done = nrom_done, - .prg_read = nrom_prg_read, - .prg_write = nrom_prg_write, + .read = nrom_read, + .write = nrom_write, .chr_addr = nrom_chr_addr, .vram_addr = nrom_vram_addr, }; diff --git a/src/mapper.h b/src/mapper.h index 3c0e01f..3b82a52 100644 --- a/src/mapper.h +++ b/src/mapper.h @@ -4,17 +4,19 @@ #include -#define nes_chr_page_size (0x1000U) -#define nes_vram_page_size (0x0400U) +#define nes_chr_page_size (0x1000U) +#define nes_vram_page_size (0x0400U) +#define nes_prg_rom_page_size (0x4000U) struct nes_cart_t; typedef struct nes_mapper_t { int (*init)(struct nes_mapper_t*, struct nes_cart_t* cart); + void (*reset)(struct nes_mapper_t*); void (*done)(struct nes_mapper_t*); - uint8_t (*prg_read)(struct nes_mapper_t*, uint16_t addr); - void (*prg_write)(struct nes_mapper_t*, uint16_t addr, uint8_t val); + uint8_t (*read)(struct nes_mapper_t*, uint16_t addr); + void (*write)(struct nes_mapper_t*, uint16_t addr, uint8_t val); uint8_t* (*chr_addr)(struct nes_mapper_t*, uint16_t addr); uint8_t* (*vram_addr)(struct nes_mapper_t*, uint16_t addr); void* data; @@ -25,19 +27,23 @@ static inline int nes_map_init(nes_mapper* map, return map->init(map, cart); } +static inline void nes_map_reset(nes_mapper* map) { + map->reset(map); +} + static inline void nes_map_done(nes_mapper* map) { map->done(map); } -static inline uint8_t nes_map_prg_read(nes_mapper* map, +static inline uint8_t nes_map_read(nes_mapper* map, uint16_t addr) { - return map->prg_read(map, addr); + return map->read(map, addr); } -static inline void nes_map_prg_write(nes_mapper* map, +static inline void nes_map_write(nes_mapper* map, uint16_t addr, uint8_t val) { - map->prg_write(map, addr, val); + map->write(map, addr, val); } static inline uint8_t* nes_map_chr_addr(nes_mapper* map, diff --git a/src/nes.c b/src/nes.c index aeb6993..b3e312b 100644 --- a/src/nes.c +++ b/src/nes.c @@ -22,16 +22,8 @@ uint8_t nes_mem_read(nes* sys, uint16_t addr) { } else if (addr < nes_mem_exp_start) { val = nes_apu_read(&sys->apu, addr); - } else if (addr < nes_mem_wram_start) { - // TODO: Expansion ROM support - - } else if (addr < nes_mem_rom_start) { - // TODO: Send to mapper? - val = sys->cart.wram[addr - nes_mem_wram_start]; - } else { - val = nes_map_prg_read(sys->cart.mapper, - addr - nes_mem_rom_start); + val = nes_map_read(sys->cart.mapper, addr); } return val; @@ -61,15 +53,8 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) { } else if (addr < nes_mem_exp_start) { nes_apu_write(&sys->apu, addr, val); - } else if (addr < nes_mem_wram_start) { - // No ROM writes - - } else if (addr < nes_mem_rom_start) { - sys->cart.wram[addr - nes_mem_wram_start] = val; - } else { - nes_map_prg_write(sys->cart.mapper, - addr - nes_mem_rom_start, val); + nes_map_write(sys->cart.mapper, addr, val); } } diff --git a/src/nes.h b/src/nes.h index dcd654d..5b95dc8 100644 --- a/src/nes.h +++ b/src/nes.h @@ -23,7 +23,7 @@ #define nes_mem_exp_start (0x4020U) #define nes_mem_exp_size (0x1FE0U) #define nes_mem_wram_start (0x6000U) -//#define nes_mem_wram_size (0x2000U) +#define nes_mem_wram_size (0x2000U) #define nes_mem_rom_start (0x8000U) #define nes_mem_rom_size (0x8000U) diff --git a/src/ppu.c b/src/ppu.c index 73c7207..2a416a4 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -159,6 +159,10 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { #endif // DEBUG_VRAM // printf("PPU: VRAM %04x < %02x\n", vram_addr, val); nes_vram_write(ppu->mapper, vram_addr, val); + + } else { +// printf("PPU: CHR MEM WRITE %04x > %02x\n", ppu->addr, val); + *(nes_map_chr_addr(ppu->mapper, ppu->addr)) = val; } ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ?