| @@ -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 | |||
| MAP_SRCS_1 = nrom.c mmc1.c cnrom.c | |||
| EXT_SRCS_1 = e6502/e6502.c | |||
| @@ -10,6 +10,9 @@ int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { | |||
| ines_Header *hdr = (ines_Header*)mem; | |||
| void* ptr = &hdr[1]; | |||
| cart->ines_mem = mem; | |||
| cart->ines_size = len; | |||
| status = ines_check_mem(hdr); | |||
| if (0 == status && (hdr->flags_6 & ines_Flag_Trainer)) { | |||
| @@ -18,22 +21,21 @@ 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; | |||
| int prg_size = nes_prg_rom_page_size * hdr->prg_size_lsb; | |||
| if (prg_size <= 0) { | |||
| INES_ERR("Bad program ROM size: %d / %d", | |||
| prg_size, nes_mem_rom_size); | |||
| INES_ERR("Bad program ROM size: %#x", prg_size); | |||
| status = -1; | |||
| } else { | |||
| cart->prg_rom = ptr; | |||
| cart->prg_rom_size = prg_size; | |||
| cart->prg_rom_banks = hdr->prg_size_lsb; | |||
| } | |||
| ptr += prg_size; | |||
| } | |||
| if (0 == status) { | |||
| int chr_size = ines_chr_rom_chunk * hdr->chr_size_lsb; | |||
| int chr_size = nes_chr_page_size * hdr->chr_size_lsb; | |||
| cart->chr_rom = ptr; | |||
| cart->chr_rom_size = chr_size; | |||
| cart->chr_rom_banks = hdr->chr_size_lsb * 2; | |||
| ptr += chr_size; | |||
| int index = (hdr->flags_6 & ines_Mapper_Nibble_Lo) >> 4 | | |||
| @@ -52,14 +54,10 @@ int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { | |||
| cart->flags &= ~Cart_Flag_Horizontal; | |||
| } | |||
| // Don't initialize the mapper until all flags are set! | |||
| status = nes_map_init(cart->mapper, cart); | |||
| } | |||
| if (0 == status) { | |||
| cart->ines_mem = mem; | |||
| cart->ines_size = len; | |||
| } | |||
| return status; | |||
| } | |||
| @@ -12,9 +12,9 @@ typedef enum { | |||
| typedef struct nes_cart_t { | |||
| uint8_t* prg_rom; | |||
| int prg_rom_size; | |||
| int prg_rom_banks; | |||
| uint8_t* chr_rom; | |||
| int chr_rom_size; | |||
| int chr_rom_banks; | |||
| nes_Cart_Flags flags; | |||
| struct nes_mapper_t* mapper; | |||
| @@ -12,9 +12,7 @@ | |||
| #define INES_ERR(...) INES_LOG("E", __VA_ARGS__) | |||
| #define ines_trainer_size (512U) | |||
| #define ines_prg_rom_chunk (16384U) | |||
| #define ines_chr_rom_chunk (8192U) | |||
| #define ines_trainer_size (0x200U) | |||
| #define ines_magic "NES\x1a" | |||
| @@ -0,0 +1,96 @@ | |||
| #include <stdlib.h> | |||
| #include "map.h" | |||
| typedef struct { | |||
| uint8_t* prg_rom; | |||
| int prg_rom_banks; | |||
| uint8_t* chr_rom; | |||
| int chr_rom_banks; | |||
| uint8_t mirror; | |||
| uint8_t vram[nes_vram_page_size * 2]; | |||
| uint8_t* chr_bank; | |||
| } cnrom_mapper; | |||
| static void cnrom_reset(nes_mapper* nes_map) { | |||
| cnrom_mapper* map = (cnrom_mapper*)nes_map->data; | |||
| map->chr_bank = 0; | |||
| } | |||
| static int cnrom_init(nes_mapper* nes_map, nes_cart* cart) { | |||
| cnrom_mapper* map = calloc(1, sizeof(cnrom_mapper)); | |||
| nes_map->data = map; | |||
| if (NULL != map) { | |||
| map->prg_rom = cart->prg_rom; | |||
| map->prg_rom_banks = cart->prg_rom_banks; | |||
| map->chr_rom = cart->chr_rom; | |||
| map->chr_rom_banks = cart->chr_rom_banks; | |||
| map->mirror = (cart->flags & Cart_Flag_Horizontal) ? 0 : 1; | |||
| cnrom_reset(nes_map); | |||
| } | |||
| return (NULL == map ? -1 : 0); | |||
| } | |||
| static void cnrom_done(nes_mapper* nes_map) { | |||
| free(nes_map->data); | |||
| } | |||
| static inline uint8_t* cnrom_prg_addr(cnrom_mapper* map, | |||
| uint16_t addr) { | |||
| addr &= 0x7FFFU; | |||
| if (map->prg_rom_banks == 1) { | |||
| addr &= 0x3FFF; | |||
| } | |||
| return &(map->prg_rom[addr]); | |||
| } | |||
| static uint8_t cnrom_read(nes_mapper* map, uint16_t addr) { | |||
| uint8_t val = 0; | |||
| if (addr >= nes_mem_rom_start) { | |||
| val = *(cnrom_prg_addr((cnrom_mapper*)map->data, addr)); | |||
| } | |||
| return val; | |||
| } | |||
| static void cnrom_write(nes_mapper* nes_map, | |||
| uint16_t addr, uint8_t val) { | |||
| cnrom_mapper* map = (cnrom_mapper*)nes_map->data; | |||
| if (addr >= nes_mem_rom_start) { | |||
| map->chr_bank = &map->chr_rom[(val % map->chr_rom_banks) * | |||
| (nes_chr_page_size * 2)]; | |||
| } | |||
| } | |||
| static uint8_t* cnrom_chr_addr(nes_mapper* nes_map, | |||
| uint16_t addr) { | |||
| return &((cnrom_mapper*)nes_map->data)->chr_bank[addr]; | |||
| } | |||
| static uint8_t* cnrom_vram_addr(nes_mapper* nes_map, | |||
| uint16_t addr) { | |||
| cnrom_mapper* map = (cnrom_mapper*)nes_map->data; | |||
| int page = addr >> 10U; | |||
| page >>= map->mirror; | |||
| addr = ((page & 1) << 10U) | (addr & 0x3FFU); | |||
| return &map->vram[addr]; | |||
| } | |||
| static void cnrom_chr_write(nes_mapper* nes_map, | |||
| uint16_t addr, uint8_t val) { | |||
| // ROM only. | |||
| } | |||
| nes_mapper mapper_cnrom = { | |||
| .init = cnrom_init, | |||
| .reset = cnrom_reset, | |||
| .done = cnrom_done, | |||
| .read = cnrom_read, | |||
| .write = cnrom_write, | |||
| .chr_addr = cnrom_chr_addr, | |||
| .vram_addr = cnrom_vram_addr, | |||
| .chr_write = cnrom_chr_write, | |||
| }; | |||
| @@ -9,7 +9,7 @@ | |||
| typedef struct { | |||
| // TODO: Does this even support CHR ROM? | |||
| uint8_t* prg_rom; | |||
| int prg_rom_size; | |||
| int prg_rom_banks; | |||
| uint8_t reg_shift; | |||
| uint8_t reg_n_shift; | |||
| @@ -83,11 +83,10 @@ static void mmc1_update_prg(mmc1_mapper* map) { | |||
| 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); | |||
| MAP_LOG("PRG: %d + %d", bank, map->prg_rom_banks - 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 - | |||
| map->prg_bank[1] = &map->prg_rom[(map->prg_rom_banks - 1) * | |||
| nes_prg_rom_page_size]; | |||
| } | |||
| } | |||
| @@ -108,8 +107,8 @@ 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->prg_rom = cart->prg_rom; | |||
| map->prg_rom_banks = cart->prg_rom_banks; | |||
| mmc1_reset(nes_map); | |||
| } | |||
| return (NULL == map ? -1 : 0); | |||
| @@ -194,6 +193,12 @@ static uint8_t* mmc1_chr_addr(nes_mapper* nes_map, | |||
| return &((mmc1_mapper*)nes_map->data)->chr_bank[page][addr]; | |||
| } | |||
| static void mmc1_chr_write(nes_mapper* nes_map, | |||
| uint16_t addr, uint8_t val) { | |||
| // TODO: Check if this is ROM? | |||
| *(mmc1_chr_addr(nes_map, addr)) = val; | |||
| } | |||
| static uint8_t* mmc1_vram_addr(nes_mapper* nes_map, | |||
| uint16_t addr) { | |||
| int page = (addr >> 10) & 3; | |||
| @@ -211,4 +216,5 @@ nes_mapper mapper_mmc1 = { | |||
| .write = mmc1_write, | |||
| .chr_addr = mmc1_chr_addr, | |||
| .vram_addr = mmc1_vram_addr, | |||
| .chr_write = mmc1_chr_write, | |||
| }; | |||
| @@ -5,9 +5,9 @@ | |||
| typedef struct { | |||
| uint8_t* prg_rom; | |||
| int prg_rom_size; | |||
| int prg_rom_banks; | |||
| uint8_t* chr_rom; | |||
| int chr_rom_size; | |||
| int chr_rom_banks; | |||
| uint8_t mirror; | |||
| uint8_t vram[nes_vram_page_size * 2]; | |||
| uint8_t wram[nes_mem_wram_size]; | |||
| @@ -20,10 +20,10 @@ 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) { | |||
| 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_rom = cart->prg_rom; | |||
| map->prg_rom_banks = cart->prg_rom_banks; | |||
| map->chr_rom = cart->chr_rom; | |||
| map->chr_rom_banks = cart->chr_rom_banks; | |||
| map->mirror = (cart->flags & Cart_Flag_Horizontal) ? 0 : 1; | |||
| } | |||
| return (NULL == map ? -1 : 0); | |||
| @@ -36,7 +36,7 @@ static void nrom_done(nes_mapper* nes_map) { | |||
| static inline uint8_t* nrom_prg_addr(nrom_mapper* map, | |||
| uint16_t addr) { | |||
| addr &= 0x7FFFU; | |||
| if (addr > map->prg_rom_size) { | |||
| if (map->prg_rom_banks == 1) { | |||
| addr &= 0x3FFF; | |||
| } | |||
| return &(map->prg_rom[addr]); | |||
| @@ -66,8 +66,7 @@ static void nrom_write(nes_mapper* map, | |||
| 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]; | |||
| return &(((nrom_mapper*)nes_map->data)->chr_rom[addr]); | |||
| } | |||
| static uint8_t* nrom_vram_addr(nes_mapper* nes_map, | |||
| @@ -79,6 +78,12 @@ static uint8_t* nrom_vram_addr(nes_mapper* nes_map, | |||
| return &map->vram[addr]; | |||
| } | |||
| static void nrom_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_nrom = { | |||
| .init = nrom_init, | |||
| .reset = nrom_reset, | |||
| @@ -87,4 +92,5 @@ nes_mapper mapper_nrom = { | |||
| .write = nrom_write, | |||
| .chr_addr = nrom_chr_addr, | |||
| .vram_addr = nrom_vram_addr, | |||
| .chr_write = nrom_chr_write, | |||
| }; | |||
| @@ -3,9 +3,11 @@ | |||
| extern nes_mapper mapper_nrom; | |||
| extern nes_mapper mapper_mmc1; | |||
| extern nes_mapper mapper_cnrom; | |||
| nes_mapper* nes_mappers[256] = { | |||
| [ 0] = &mapper_nrom, | |||
| [ 1] = &mapper_mmc1, | |||
| [ 3] = &mapper_cnrom, | |||
| }; | |||
| @@ -4,11 +4,6 @@ | |||
| #include <stdint.h> | |||
| #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 { | |||
| @@ -19,6 +14,7 @@ typedef struct nes_mapper_t { | |||
| 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 (*chr_write)(struct nes_mapper_t*, uint16_t addr, uint8_t val); | |||
| void* data; | |||
| } nes_mapper; | |||
| @@ -51,9 +47,19 @@ static inline uint8_t* nes_map_chr_addr(nes_mapper* map, | |||
| return map->chr_addr(map, addr); | |||
| } | |||
| static inline uint8_t nes_chr_read(nes_mapper* map, | |||
| uint16_t addr) { | |||
| return *(nes_map_chr_addr(map, addr)); | |||
| } | |||
| static inline void nes_chr_write(nes_mapper* map, | |||
| uint16_t addr, uint8_t val) { | |||
| return map->chr_write(map, addr, val); | |||
| } | |||
| static inline uint8_t* nes_map_vram_addr(nes_mapper* map, | |||
| uint16_t addr) { | |||
| return map->vram_addr(map, addr); | |||
| return map->vram_addr(map, addr & 0x1FFFU); | |||
| } | |||
| static inline uint8_t nes_vram_read(nes_mapper* map, | |||
| @@ -14,6 +14,10 @@ | |||
| #define nes_clock_cpu_div (12U) | |||
| #define nes_clock_ppu_div (4U) | |||
| #define nes_chr_page_size (0x1000U) | |||
| #define nes_vram_page_size (0x0400U) | |||
| #define nes_prg_rom_page_size (0x4000U) | |||
| #define nes_mem_ram_start (0x0000U) | |||
| #define nes_mem_ram_size (0x0800U) | |||
| #define nes_mem_ppu_start (0x2000U) | |||
| @@ -43,8 +43,7 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { | |||
| if (ppu->addr < nes_ppu_mem_vram_start) { | |||
| // printf("PPU: CHR MEM READ %04x > %02x\n", ppu->addr, val); | |||
| ppu->data = *(nes_map_chr_addr(ppu->mapper, | |||
| ppu->addr)); | |||
| ppu->data = nes_chr_read(ppu->mapper, ppu->addr); | |||
| } else if (ppu->addr < nes_ppu_mem_vram_start + | |||
| nes_ppu_mem_vram_size) { | |||
| VRAM_LOG("PPU: VRAM READ %04x > %02x\n", ppu->addr, val); | |||
| @@ -53,9 +52,11 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { | |||
| ppu->addr - nes_ppu_mem_vram_start | |||
| ); | |||
| } else if (ppu->addr < nes_ppu_mem_pal_start) { | |||
| // printf("PPU: BLANK READ %04x > %02x\n", ppu->addr, val); | |||
| printf("PPU: BLANK READ %04x > %02x\n", ppu->addr, val); | |||
| /* | |||
| ppu->data = *(nes_map_chr_addr(ppu->mapper, | |||
| ppu->addr)); | |||
| */ | |||
| } | |||
| } | |||
| @@ -162,7 +163,7 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { | |||
| } else { | |||
| // printf("PPU: CHR MEM WRITE %04x > %02x\n", ppu->addr, val); | |||
| *(nes_map_chr_addr(ppu->mapper, ppu->addr)) = val; | |||
| nes_chr_write(ppu->mapper, ppu->addr, val); | |||
| } | |||
| ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ? | |||
| @@ -405,7 +405,7 @@ static int eval_sprite_line(const nes_ppu* ppu, int line, | |||
| } | |||
| static void update_sprite_hit(nes_ppu* ppu, int block_line, | |||
| const void* back_line, | |||
| const void* back_pixels, | |||
| int back_pitch) { | |||
| const oam_sprite* sprite = (oam_sprite*)ppu->oam; | |||
| int x_fine = ppu->scroll_x % 8; | |||
| @@ -422,11 +422,11 @@ static void update_sprite_hit(nes_ppu* ppu, int block_line, | |||
| if (start_y < 0) start_y = 0; | |||
| if (end_y > 8) end_y = 8; | |||
| int hit = -1; | |||
| const uint8_t* back = (uint8_t*)back_line + x_fine; | |||
| const uint8_t* back = (uint8_t*)back_pixels + x_fine; | |||
| back += (render_line + start_y) * back_pitch; | |||
| for (int y = start_y; y < end_y; ++y) { | |||
| hit = eval_sprite_line( | |||
| ppu, render_line + y, | |||
| ppu, render_line + y - y_fine, | |||
| sprite, chr, back | |||
| ); | |||
| if (hit >= 0) { | |||