| @@ -15,8 +15,8 @@ TARGET_1 = nese | |||
| LDLIBS_1 = -lSDL2 | |||
| SRC_SRCS_1 = nese.c ines.c | |||
| SRC_SRCS_1 += nes.c ppu.c cart.c input.c | |||
| SRC_SRCS_1 += vram.c mapper.c | |||
| SRC_SRCS_1 += nes.c ppu.c input.c | |||
| SRC_SRCS_1 += cart.c mapper.c | |||
| SRC_SRCS_1 += sdl_render.c sdl_input.c | |||
| MAPDIR = src/map | |||
| @@ -54,10 +54,6 @@ int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { | |||
| } | |||
| } | |||
| if (0 == status) { | |||
| status = nes_map_init(cart->mapper, cart); | |||
| } | |||
| if (0 == status) { | |||
| if (hdr->flags_6 & ines_Flag_Horizontal) { | |||
| cart->flags |= Cart_Flag_Horizontal; | |||
| @@ -65,6 +61,10 @@ int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { | |||
| cart->flags &= ~Cart_Flag_Horizontal; | |||
| } | |||
| status = nes_map_init(cart->mapper, cart); | |||
| } | |||
| if (0 == status) { | |||
| cart->ines_mem = mem; | |||
| cart->ines_size = len; | |||
| } | |||
| @@ -1,8 +1,72 @@ | |||
| #include "map.h" | |||
| // Stub | |||
| 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; | |||
| nes_mapper mapper_mmc1 = { | |||
| uint8_t reg_shift; | |||
| uint8_t reg_control; | |||
| uint8_t reg_chr_0; | |||
| uint8_t reg_chr_1; | |||
| uint8_t reg_prg; | |||
| } mmc1_mapper; | |||
| /* | |||
| int mmc1_init(nes_mapper* nes_map, nes_cart* cart) { | |||
| nrom_mapper* map = malloc(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_ram = cart->wram; | |||
| map->prg_ram_size = nes_mem_wram_size; | |||
| map->shift_reg = 0; | |||
| } | |||
| return (NULL == map ? -1 : 0); | |||
| } | |||
| void nrom_done(nes_mapper* nes_map) { | |||
| free(nes_map->data); | |||
| } | |||
| static inline uint8_t* nrom_prg_addr(nrom_mapper* map, | |||
| uint16_t addr) { | |||
| 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)); | |||
| } | |||
| void nrom_prg_write(nes_mapper* map, uint16_t addr, uint8_t val) { | |||
| // No ROM writes. | |||
| } | |||
| 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]; | |||
| } | |||
| */ | |||
| nes_mapper mapper_mmc1 = { | |||
| /* | |||
| .init = mmc1_init, | |||
| .done = mmc1_done, | |||
| .prg_read = mmc1_prg_read, | |||
| .prg_write = mmc1_prg_write, | |||
| .chr_addr = mmc1_chr_addr, | |||
| .vram_addr = mmc1_chr_addr, | |||
| */ | |||
| }; | |||
| @@ -10,12 +10,13 @@ typedef struct { | |||
| int prg_ram_size; | |||
| uint8_t* chr_rom; | |||
| int chr_rom_size; | |||
| uint8_t mirror; | |||
| uint8_t vram[nes_vram_page_size * 2]; | |||
| } nrom_mapper; | |||
| // TODO: PRG_ROM region mirroring | |||
| int nrom_init(nes_mapper* nes_map, nes_cart* cart) { | |||
| nrom_mapper* map = malloc(sizeof(nrom_mapper)); | |||
| nrom_mapper* map = calloc(1, sizeof(nrom_mapper)); | |||
| nes_map->data = map; | |||
| if (NULL != map) { | |||
| map->prg_rom = cart->prg_rom; | |||
| @@ -24,6 +25,7 @@ int nrom_init(nes_mapper* nes_map, nes_cart* cart) { | |||
| 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); | |||
| } | |||
| @@ -53,10 +55,19 @@ uint8_t* nrom_chr_addr(nes_mapper* nes_map, uint16_t addr) { | |||
| return &map->chr_rom[addr % map->chr_rom_size]; | |||
| } | |||
| 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; | |||
| addr = ((page & 1) << 10U) | (addr & 0x3FFU); | |||
| return &map->vram[addr]; | |||
| } | |||
| nes_mapper mapper_nrom = { | |||
| .init = nrom_init, | |||
| .done = nrom_done, | |||
| .prg_read = nrom_prg_read, | |||
| .prg_write = nrom_prg_write, | |||
| .chr_addr = nrom_chr_addr, | |||
| .vram_addr = nrom_vram_addr, | |||
| }; | |||
| @@ -4,6 +4,10 @@ | |||
| #include <stdint.h> | |||
| #define nes_chr_page_size (0x1000U) | |||
| #define nes_vram_page_size (0x0400U) | |||
| struct nes_cart_t; | |||
| typedef struct nes_mapper_t { | |||
| @@ -12,6 +16,7 @@ typedef 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* (*chr_addr)(struct nes_mapper_t*, uint16_t addr); | |||
| uint8_t* (*vram_addr)(struct nes_mapper_t*, uint16_t addr); | |||
| void* data; | |||
| } nes_mapper; | |||
| @@ -40,6 +45,20 @@ static inline uint8_t* nes_map_chr_addr(nes_mapper* map, | |||
| return map->chr_addr(map, addr); | |||
| } | |||
| static inline uint8_t* nes_map_vram_addr(nes_mapper* map, | |||
| uint16_t addr) { | |||
| return map->vram_addr(map, addr); | |||
| } | |||
| static inline uint8_t nes_vram_read(nes_mapper* map, | |||
| uint16_t addr) { | |||
| return *(nes_map_vram_addr(map, addr)); | |||
| } | |||
| static inline void nes_vram_write(nes_mapper* map, | |||
| uint16_t addr, uint8_t val) { | |||
| *(nes_map_vram_addr(map, addr)) = val; | |||
| } | |||
| extern nes_mapper* nes_mappers[]; | |||
| @@ -49,7 +49,7 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { | |||
| nes_ppu_mem_vram_size) { | |||
| VRAM_LOG("PPU: VRAM READ %04x > %02x\n", ppu->addr, val); | |||
| ppu->data = nes_vram_read( | |||
| ppu->vram_map, | |||
| ppu->mapper, | |||
| ppu->addr - nes_ppu_mem_vram_start | |||
| ); | |||
| } else if (ppu->addr < nes_ppu_mem_pal_start) { | |||
| @@ -158,7 +158,7 @@ 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->vram_map, vram_addr, val); | |||
| nes_vram_write(ppu->mapper, vram_addr, val); | |||
| } | |||
| ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ? | |||
| @@ -183,11 +183,8 @@ int nes_ppu_init(nes_ppu* ppu, const nes_cart* cart) { | |||
| ppu->status = 0; | |||
| ppu->oam_addr = 0; | |||
| ppu->addr = 0; | |||
| ppu->vram_map = ( (cart->flags & Cart_Flag_Horizontal) ? | |||
| &vram_horizontal : | |||
| &vram_vertical ); | |||
| nes_ppu_reset(ppu); | |||
| return nes_vram_init(ppu->vram_map); | |||
| return 0; | |||
| } | |||
| int nes_ppu_run(nes_ppu* ppu, int cycles) { | |||
| @@ -3,8 +3,6 @@ | |||
| #include <stdint.h> | |||
| #include "vram.h" | |||
| struct nes_mapper_t; | |||
| struct nes_cart_t; | |||
| @@ -110,7 +108,6 @@ typedef struct { | |||
| struct nes_mapper_t* mapper; | |||
| uint8_t oam[nes_ppu_oam_size]; | |||
| uint8_t palette[nes_ppu_mem_pal_size]; | |||
| nes_vram_map* vram_map; | |||
| // Timing | |||
| int frame; | |||
| @@ -194,8 +194,8 @@ static void render_background_area(const nes_ppu* ppu, int page, | |||
| void* buffer, int pitch, | |||
| int xs, int ys, int w, int h) { | |||
| int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0; | |||
| const uint8_t* index_line = nes_vram_page(ppu->vram_map, | |||
| page); | |||
| const uint8_t* index_line = nes_map_vram_addr(ppu->mapper, | |||
| page << 10); | |||
| const uint8_t* attrs = index_line + 960U; | |||
| index_line += xs + (ys * nes_ppu_blocks_w); | |||
| uint8_t* dst_line = (uint8_t*)buffer; | |||
| @@ -1,92 +0,0 @@ | |||
| #include <stdlib.h> | |||
| #include "vram.h" | |||
| typedef enum { | |||
| Mirror_Method_Vertical, | |||
| Mirror_Method_Horizontal, | |||
| } vram_Mirror_Method; | |||
| typedef struct { | |||
| vram_Mirror_Method method; | |||
| uint8_t vram[nes_vram_page_size * 2]; | |||
| } vram_mirror; | |||
| static vram_mirror* vram_mirror_create(vram_Mirror_Method method) { | |||
| vram_mirror* mirror = calloc(1, sizeof(vram_mirror)); | |||
| if (NULL != mirror) mirror->method = method; | |||
| return mirror; | |||
| } | |||
| static void vram_mirror_destroy(vram_mirror* mirror) { | |||
| free(mirror); | |||
| } | |||
| static inline uint8_t* vram_mirror_page(vram_mirror* mirror, | |||
| int page) { | |||
| if (mirror->method == Mirror_Method_Vertical) { | |||
| page >>= 1; | |||
| } | |||
| return &mirror->vram[(page & 1) ? nes_vram_page_size : 0]; | |||
| } | |||
| static inline uint8_t* vram_mirror_map(vram_mirror* mirror, | |||
| uint16_t addr) { | |||
| uint8_t* page = vram_mirror_page(mirror, (addr >> 10)); | |||
| return &page[addr & nes_vram_addr_mask]; | |||
| } | |||
| static int vram_map_mirror_init_method(nes_vram_map* map, | |||
| vram_Mirror_Method method) { | |||
| map->data = vram_mirror_create(method); | |||
| return (NULL == map->data ? -1 : 0); | |||
| } | |||
| static int vram_map_mirror_init_horz(nes_vram_map* map) { | |||
| return vram_map_mirror_init_method( | |||
| map, Mirror_Method_Horizontal | |||
| ); | |||
| } | |||
| static int vram_map_mirror_init_vert(nes_vram_map* map) { | |||
| return vram_map_mirror_init_method( | |||
| map, Mirror_Method_Vertical | |||
| ); | |||
| } | |||
| static void vram_map_mirror_done(nes_vram_map* map) { | |||
| vram_mirror_destroy((vram_mirror*)map->data); | |||
| } | |||
| static uint8_t vram_map_mirror_read(nes_vram_map* map, | |||
| uint16_t addr) { | |||
| return *(vram_mirror_map((vram_mirror*)map->data, addr)); | |||
| } | |||
| static void vram_map_mirror_write(nes_vram_map* map, | |||
| uint16_t addr, | |||
| uint8_t val) { | |||
| *(vram_mirror_map((vram_mirror*)map->data, addr)) = val; | |||
| } | |||
| static uint8_t* vram_map_mirror_page(nes_vram_map* map, int page) { | |||
| return vram_mirror_page((vram_mirror*)map->data, page); | |||
| } | |||
| nes_vram_map vram_horizontal = { | |||
| .init = vram_map_mirror_init_horz, | |||
| .done = vram_map_mirror_done, | |||
| .read = vram_map_mirror_read, | |||
| .write = vram_map_mirror_write, | |||
| .page = vram_map_mirror_page, | |||
| }; | |||
| nes_vram_map vram_vertical = { | |||
| .init = vram_map_mirror_init_vert, | |||
| .done = vram_map_mirror_done, | |||
| .read = vram_map_mirror_read, | |||
| .write = vram_map_mirror_write, | |||
| .page = vram_map_mirror_page, | |||
| }; | |||
| @@ -1,48 +0,0 @@ | |||
| #ifndef NES_VRAM_ | |||
| #define NES_VRAM_ | |||
| #include <stdint.h> | |||
| #define nes_vram_page_size (0x400U) | |||
| #define nes_vram_page_mask (0xC00U) | |||
| #define nes_vram_addr_mask (0x3FFU) | |||
| typedef struct nes_vram_map_t { | |||
| int (*init)(struct nes_vram_map_t*); | |||
| void (*done)(struct nes_vram_map_t*); | |||
| uint8_t* (*page)(struct nes_vram_map_t*, int page); | |||
| uint8_t (*read)(struct nes_vram_map_t*, uint16_t addr); | |||
| void (*write)(struct nes_vram_map_t*, uint16_t addr, uint8_t val); | |||
| void* data; | |||
| } nes_vram_map; | |||
| static inline int nes_vram_init(nes_vram_map* map) { | |||
| return map->init(map); | |||
| } | |||
| static inline void nes_vram_done(nes_vram_map* map) { | |||
| map->done(map); | |||
| } | |||
| static inline uint8_t* nes_vram_page(nes_vram_map* map, int page) { | |||
| return map->page(map, page); | |||
| } | |||
| static inline uint8_t nes_vram_read(nes_vram_map* map, | |||
| uint16_t addr) { | |||
| return map->read(map, addr); | |||
| } | |||
| static inline void nes_vram_write(nes_vram_map* map, | |||
| uint16_t addr, uint8_t val) { | |||
| map->write(map, addr, val); | |||
| } | |||
| extern nes_vram_map vram_horizontal; | |||
| extern nes_vram_map vram_vertical; | |||
| #endif // NES_VRAM_ | |||