| @@ -1,7 +1,8 @@ | |||
| CC = gcc | |||
| LD = $(CC) | |||
| CFLAGS = -g -Wall -Werror -Wshadow -I.. #-DE6502_DEBUG | |||
| LDFLAGS = | |||
| PFLAGS = -g | |||
| CFLAGS = $(PFLAGS) -Wall -Werror -Wshadow -I.. | |||
| LDFLAGS = $(PFLAGS) | |||
| OBJDIR = obj | |||
| SRCDIR = src | |||
| @@ -13,13 +14,20 @@ BINDIR = bin | |||
| TARGET_1 = nese | |||
| LDLIBS_1 = -lSDL2 | |||
| SRC_SRCS_1 = nese.c ines.c nes.c ppu.c input.c vram.c | |||
| 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 += sdl_render.c sdl_input.c | |||
| MAPDIR = src/map | |||
| MAP_SRCS_1 = nrom.c mmc1.c | |||
| EXT_SRCS_1 = e6502/e6502.c | |||
| SRCS_1 = $(SRC_SRCS_1:%=$(SRCDIR)/%) $(EXT_SRCS_1) | |||
| SRCS_1 = $(SRC_SRCS_1:%=$(SRCDIR)/%) | |||
| SRCS_1 += $(MAP_SRCS_1:%=$(MAPDIR)/%) | |||
| SRCS_1 += $(EXT_SRCS_1) | |||
| OBJS_1 = $(SRCS_1:%.c=$(OBJDIR)/%.o) | |||
| @@ -0,0 +1,114 @@ | |||
| #include <sys/mman.h> | |||
| #include "cart.h" | |||
| #include "ines.h" | |||
| #include "mapper.h" | |||
| int nes_cart_init_mem(nes_cart* cart, void* mem, int len) { | |||
| int status = 0; | |||
| ines_Header *hdr = (ines_Header*)mem; | |||
| void* ptr = &hdr[1]; | |||
| status = ines_check_mem(hdr); | |||
| if (0 == status && (hdr->flags_6 & ines_Flag_Trainer)) { | |||
| // Skip trainer | |||
| ptr += ines_trainer_size; | |||
| } | |||
| if (0 == status) { | |||
| int prg_size = ines_prg_rom_chunk * hdr->prg_size_lsb; | |||
| if (prg_size > nes_mem_rom_size || prg_size <= 0) { | |||
| INES_ERR("Bad program ROM size: %d / %d", | |||
| prg_size, nes_mem_rom_size); | |||
| status = -1; | |||
| } else { | |||
| cart->prg_rom = ptr; | |||
| cart->prg_rom_size = prg_size; | |||
| } | |||
| ptr += prg_size; | |||
| } | |||
| 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; | |||
| } | |||
| 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]; | |||
| if (NULL == cart->mapper) { | |||
| INES_ERR("No mapper found for type %d", index); | |||
| status = -1; | |||
| } | |||
| } | |||
| 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; | |||
| } else { | |||
| cart->flags &= ~Cart_Flag_Horizontal; | |||
| } | |||
| cart->ines_mem = mem; | |||
| cart->ines_size = len; | |||
| } | |||
| return status; | |||
| } | |||
| void nes_cart_done(nes_cart* cart) { | |||
| if (NULL != cart->ines_mem) { | |||
| munmap(cart->ines_mem, cart->ines_size); | |||
| cart->ines_mem = NULL; | |||
| } | |||
| } | |||
| int nes_cart_init_file(nes_cart* cart, FILE* file) { | |||
| int status = 0; | |||
| int size = -1; | |||
| void* mem = NULL; | |||
| // Get file size | |||
| status = fseek(file, 0, SEEK_END); | |||
| if (0 != status) { | |||
| INES_ERR("Failed to check file size"); | |||
| } else { | |||
| size = ftell(file); | |||
| } | |||
| // Map file | |||
| if (0 == status) { | |||
| mem = mmap(NULL, size, PROT_READ, MAP_PRIVATE, | |||
| fileno(file), 0); | |||
| if (NULL == mem) { | |||
| INES_ERR("Failed to map file (%d bytes)", size); | |||
| status = -1; | |||
| } | |||
| } | |||
| // Check in memory; unmap on failure | |||
| if (0 == status) { | |||
| status = nes_cart_init_mem(cart, mem, size); | |||
| if (0 != status) { | |||
| munmap(mem, size); | |||
| } | |||
| } | |||
| return status; | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| #ifndef NES_CART_H_ | |||
| #define NES_CART_H_ | |||
| #include <stdint.h> | |||
| #include <stdio.h> | |||
| #define nes_mem_wram_size (0x2000U) | |||
| typedef enum { | |||
| Cart_Flag_Vertical = 0b0, | |||
| Cart_Flag_Horizontal = 0b1, | |||
| } nes_Cart_Flags; | |||
| typedef struct nes_cart_t { | |||
| uint8_t* prg_rom; | |||
| 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; | |||
| void* ines_mem; | |||
| int ines_size; | |||
| } nes_cart; | |||
| int nes_cart_init_file(nes_cart*, FILE* file); | |||
| int nes_cart_init_mem(nes_cart*, void*, int len); | |||
| void nes_cart_done(nes_cart*); | |||
| #endif // NES_CART_H_ | |||
| @@ -4,90 +4,34 @@ | |||
| #include "nes.h" | |||
| #define INES_LOG(tag, fmt, ...) \ | |||
| fprintf(stderr, tag ": iNES: " fmt "\n" __VA_OPT__(,) __VA_ARGS__) | |||
| #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) | |||
| int ines_check(ines_Header* ret, FILE* file) { | |||
| int ines_check_mem(void* mem) { | |||
| int status = 0; | |||
| ines_Header hdr = {0}; | |||
| ines_Header *hdr = (ines_Header*)mem; | |||
| if (1 != fread(&hdr, sizeof(ines_Header), 1, file)) { | |||
| INES_ERR("Failed to read header"); | |||
| status = -1; | |||
| } | |||
| if (0 == status && 0 != memcmp(hdr.magic, | |||
| ines_magic, | |||
| sizeof(hdr.magic))) { | |||
| if (0 != memcmp(hdr->magic, ines_magic, sizeof(hdr->magic))) { | |||
| INES_ERR("Bad file magic: expected '%.4s', got '%.4s'", | |||
| ines_magic, hdr.magic); | |||
| status =-1; | |||
| } | |||
| if (0 == status && NULL != ret) { | |||
| *ret = hdr; | |||
| ines_magic, hdr->magic); | |||
| status = -1; | |||
| } | |||
| return status; | |||
| } | |||
| int ines_load(nes_cart* cart, FILE* file) { | |||
| int ines_check(ines_Header* ret, FILE* file) { | |||
| int status = 0; | |||
| ines_Header hdr = {0}; | |||
| status = ines_check(&hdr, file); | |||
| if (0 == status && (hdr.flags_6 & ines_Flag_Trainer)) { | |||
| // Skip trainer | |||
| status = fseek(file, ines_trainer_size, SEEK_CUR); | |||
| } | |||
| if (0 == status) { | |||
| int prg_size = ines_prg_rom_chunk * hdr.prg_size_lsb; | |||
| if (prg_size > nes_mem_rom_size) { | |||
| INES_ERR("Program ROM too large: %d > %d", | |||
| prg_size, nes_mem_rom_size); | |||
| status = -1; | |||
| } else if (1 != fread(cart->rom.prg, prg_size, 1, file)) { | |||
| INES_ERR("Failed to read program ROM"); | |||
| status = -1; | |||
| } else if (1U == hdr.prg_size_lsb) { | |||
| // If there's only one PRG_ROM chunk, duplicate it | |||
| memcpy(cart->rom.prg + ines_prg_rom_chunk, | |||
| cart->rom.prg, ines_prg_rom_chunk); | |||
| } | |||
| if (1 != fread(&hdr, sizeof(ines_Header), 1, file)) { | |||
| INES_ERR("Failed to read header"); | |||
| status = -1; | |||
| } | |||
| if (0 == status) { | |||
| int chr_size = ines_chr_rom_chunk * hdr.chr_size_lsb; | |||
| if (chr_size > nes_ppu_mem_size) { | |||
| INES_ERR("Sprite ROM too large: %d > %d", | |||
| chr_size, nes_ppu_mem_size); | |||
| status = -1; | |||
| } else if (1 != fread(cart->rom.chr, chr_size, 1, file)) { | |||
| INES_ERR("Failed to read sprite ROM"); | |||
| status = -1; | |||
| } | |||
| status = ines_check_mem(&hdr); | |||
| } | |||
| if (0 == status) { | |||
| if (hdr.flags_6 & ines_Flag_Horizontal) { | |||
| cart->flags |= Cart_Flag_Horizontal; | |||
| } else { | |||
| cart->flags &= ~Cart_Flag_Horizontal; | |||
| } | |||
| cart->mapper = (hdr.flags_6 & ines_Mapper_Nibble_Lo) >> 4 | | |||
| (hdr.flags_7 & ines_Mapper_Nibble_Hi); | |||
| if (0 == status && NULL != ret) { | |||
| *ret = hdr; | |||
| } | |||
| return status; | |||
| @@ -7,6 +7,16 @@ | |||
| #include "nes.h" | |||
| #define INES_LOG(tag, fmt, ...) \ | |||
| fprintf(stderr, tag ": iNES: " fmt "\n" __VA_OPT__(,) __VA_ARGS__) | |||
| #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_magic "NES\x1a" | |||
| typedef enum { | |||
| @@ -45,9 +55,9 @@ typedef struct { | |||
| } __attribute__ (( packed )) ines_Header; | |||
| int ines_check(ines_Header*, FILE*); | |||
| int ines_check_file(ines_Header*, FILE*); | |||
| int ines_load(nes_cart*, FILE*); | |||
| int ines_check_mem(void*); | |||
| #endif // INES_H_ | |||
| @@ -0,0 +1,2 @@ | |||
| #include "../mapper.h" | |||
| #include "../cart.h" | |||
| @@ -0,0 +1,8 @@ | |||
| #include "map.h" | |||
| // Stub | |||
| nes_mapper mapper_mmc1 = { | |||
| }; | |||
| @@ -0,0 +1,62 @@ | |||
| #include <stdlib.h> | |||
| #include "map.h" | |||
| 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; | |||
| } nrom_mapper; | |||
| // TODO: PRG_ROM region mirroring | |||
| int nrom_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; | |||
| } | |||
| 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_nrom = { | |||
| .init = nrom_init, | |||
| .done = nrom_done, | |||
| .prg_read = nrom_prg_read, | |||
| .prg_write = nrom_prg_write, | |||
| .chr_addr = nrom_chr_addr, | |||
| }; | |||
| @@ -0,0 +1,11 @@ | |||
| #include "mapper.h" | |||
| extern nes_mapper mapper_nrom; | |||
| extern nes_mapper mapper_mmc1; | |||
| nes_mapper* nes_mappers[256] = { | |||
| [ 0] = &mapper_nrom, | |||
| [ 1] = &mapper_mmc1, | |||
| }; | |||
| @@ -0,0 +1,47 @@ | |||
| #ifndef NES_MAPPER_H_ | |||
| #define NES_MAPPER_H_ | |||
| #include <stdint.h> | |||
| struct nes_cart_t; | |||
| typedef struct nes_mapper_t { | |||
| int (*init)(struct nes_mapper_t*, struct nes_cart_t* cart); | |||
| 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* (*chr_addr)(struct nes_mapper_t*, uint16_t addr); | |||
| void* data; | |||
| } nes_mapper; | |||
| static inline int nes_map_init(nes_mapper* map, | |||
| struct nes_cart_t* cart) { | |||
| return map->init(map, cart); | |||
| } | |||
| static inline void nes_map_done(nes_mapper* map) { | |||
| map->done(map); | |||
| } | |||
| static inline uint8_t nes_map_prg_read(nes_mapper* map, | |||
| uint16_t addr) { | |||
| return map->prg_read(map, addr); | |||
| } | |||
| static inline void nes_map_prg_write(nes_mapper* map, | |||
| uint16_t addr, | |||
| uint8_t val) { | |||
| map->prg_write(map, addr, val); | |||
| } | |||
| static inline uint8_t* nes_map_chr_addr(nes_mapper* map, | |||
| uint16_t addr) { | |||
| return map->chr_addr(map, addr); | |||
| } | |||
| extern nes_mapper* nes_mappers[]; | |||
| #endif | |||
| @@ -1,6 +1,7 @@ | |||
| #include <stdio.h> | |||
| #include "nes.h" | |||
| #include "mapper.h" | |||
| uint8_t nes_mem_read(nes* sys, uint16_t addr) { | |||
| @@ -25,10 +26,12 @@ uint8_t nes_mem_read(nes* sys, uint16_t addr) { | |||
| // 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 = sys->cart.rom.prg[addr - nes_mem_rom_start]; | |||
| val = nes_map_prg_read(sys->cart.mapper, | |||
| addr - nes_mem_rom_start); | |||
| } | |||
| return val; | |||
| @@ -65,16 +68,15 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) { | |||
| sys->cart.wram[addr - nes_mem_wram_start] = val; | |||
| } else { | |||
| // No ROM writes | |||
| nes_map_prg_write(sys->cart.mapper, | |||
| addr - nes_mem_rom_start, val); | |||
| } | |||
| } | |||
| int nes_init(nes* sys) { | |||
| e6502_init(&sys->cpu, (e6502_Read*)nes_mem_read, | |||
| (e6502_Write*)nes_mem_write, sys); | |||
| return nes_ppu_init(&sys->ppu, sys->cart.rom.chr, | |||
| (sys->cart.flags & Cart_Flag_Horizontal), | |||
| sys->cart.mapper); | |||
| return nes_ppu_init(&sys->ppu, &sys->cart); | |||
| } | |||
| void nes_reset(nes* sys) { | |||
| @@ -1,6 +1,7 @@ | |||
| #ifndef NES_H_ | |||
| #define NES_H_ | |||
| #include "cart.h" | |||
| #include "apu.h" | |||
| #include "ppu.h" | |||
| #include "input.h" | |||
| @@ -22,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) | |||
| @@ -37,24 +38,6 @@ | |||
| #define nes_apu_map_size (0x20U) | |||
| typedef struct { | |||
| // TODO: Dynamic size support | |||
| uint8_t prg[nes_mem_rom_size]; | |||
| uint8_t chr[nes_ppu_mem_size]; | |||
| } nes_cart_rom; | |||
| typedef enum { | |||
| Cart_Flag_Vertical = 0b0, | |||
| Cart_Flag_Horizontal = 0b1, | |||
| } nes_Cart_Flags; | |||
| typedef struct { | |||
| nes_cart_rom rom; | |||
| uint8_t wram[nes_mem_wram_size]; | |||
| nes_Cart_Flags flags; | |||
| int mapper; | |||
| } nes_cart; | |||
| typedef struct { | |||
| e6502_Core cpu; | |||
| nes_ppu ppu; | |||
| @@ -2,7 +2,7 @@ | |||
| #include <stdlib.h> | |||
| #include <time.h> | |||
| #include "ines.h" | |||
| #include "nes.h" | |||
| #include "render.h" | |||
| #include "input.h" | |||
| @@ -68,7 +68,7 @@ int main(int argc, char* argv[]) { | |||
| int n_loops = (argc > 1) ? atoi(argv[1]) : 0; | |||
| if (n_loops <= 0) n_loops = INT_MAX; | |||
| status = ines_load(&sys.cart, stdin); | |||
| status = nes_cart_init_file(&sys.cart, stdin); | |||
| nes_Renderer* rend = &sdl_renderer; | |||
| if (status == 0) { | |||
| @@ -1,6 +1,8 @@ | |||
| #include <stdio.h> | |||
| #include "ppu.h" | |||
| #include "mapper.h" | |||
| #include "cart.h" | |||
| // TODO: Retain open bus bits? | |||
| @@ -41,7 +43,8 @@ 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 = ppu->chr_mem[ppu->addr]; | |||
| ppu->data = *(nes_map_chr_addr(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); | |||
| @@ -51,7 +54,8 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { | |||
| ); | |||
| } else if (ppu->addr < nes_ppu_mem_pal_start) { | |||
| // printf("PPU: BLANK READ %04x > %02x\n", ppu->addr, val); | |||
| ppu->data = ppu->chr_mem[ppu->addr]; | |||
| ppu->data = *(nes_map_chr_addr(ppu->mapper, | |||
| ppu->addr)); | |||
| } | |||
| } | |||
| @@ -174,13 +178,12 @@ void nes_ppu_reset(nes_ppu* ppu) { | |||
| ppu->cycle = 0; | |||
| } | |||
| int nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem, | |||
| int horizontal, int mapper) { | |||
| ppu->chr_mem = chr_mem; | |||
| int nes_ppu_init(nes_ppu* ppu, const nes_cart* cart) { | |||
| ppu->mapper = cart->mapper; | |||
| ppu->status = 0; | |||
| ppu->oam_addr = 0; | |||
| ppu->addr = 0; | |||
| ppu->vram_map = ( horizontal ? | |||
| ppu->vram_map = ( (cart->flags & Cart_Flag_Horizontal) ? | |||
| &vram_horizontal : | |||
| &vram_vertical ); | |||
| nes_ppu_reset(ppu); | |||
| @@ -1,11 +1,15 @@ | |||
| #ifndef ENES_PPU_H_ | |||
| #define ENES_PPU_H_ | |||
| #ifndef NES_PPU_H_ | |||
| #define NES_PPU_H_ | |||
| #include <stdint.h> | |||
| #include "vram.h" | |||
| struct nes_mapper_t; | |||
| struct nes_cart_t; | |||
| #define DBG_LOG(...) printf(__VA_ARGS__) | |||
| #ifdef DEBUG_OAM | |||
| @@ -103,7 +107,7 @@ typedef enum { | |||
| typedef struct { | |||
| // Memory | |||
| uint8_t* chr_mem; | |||
| 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; | |||
| @@ -134,8 +138,7 @@ typedef struct { | |||
| uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr); | |||
| void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val); | |||
| void nes_ppu_reset(nes_ppu* ppu); | |||
| int nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem, | |||
| int horizonal, int mapper); | |||
| int nes_ppu_init(nes_ppu* ppu, const struct nes_cart_t*); | |||
| typedef enum { | |||
| ppu_Result_Halt = -1, | |||
| @@ -148,4 +151,4 @@ typedef enum { | |||
| nes_ppu_Result nes_ppu_run(nes_ppu* ppu, int cycles); | |||
| #endif // ENES_PPU_H_ | |||
| #endif // NES_PPU_H_ | |||
| @@ -2,6 +2,7 @@ | |||
| #include "render.h" | |||
| #include "ppu.h" | |||
| #include "mapper.h" | |||
| static SDL_Color nes_palette[64] = { | |||
| @@ -27,6 +28,12 @@ static SDL_Color nes_palette[64] = { | |||
| }; | |||
| static inline uint8_t* chr_mem(const nes_ppu* ppu, | |||
| uint16_t addr) { | |||
| return ppu->mapper->chr_addr(ppu->mapper, addr); | |||
| } | |||
| typedef struct { | |||
| SDL_Window* window; | |||
| SDL_Renderer* renderer; | |||
| @@ -164,7 +171,7 @@ static void sdl_render_done(nes_Renderer* rend) { | |||
| static void render_bg_sprite(const nes_ppu* ppu, int index, | |||
| const uint8_t* pal, | |||
| void* loc, int pitch) { | |||
| uint8_t* sprite = &ppu->chr_mem[index * 16U]; | |||
| uint8_t* sprite = chr_mem(ppu, index * 16U); | |||
| uint8_t* dst_line = (uint8_t*)loc; | |||
| for (int y = 8; y > 0; --y) { | |||
| @@ -263,7 +270,7 @@ typedef enum { | |||
| static void render_sprite(nes_ppu* ppu, int index, | |||
| const uint8_t* pal, uint8_t attr, | |||
| void* loc, int pitch) { | |||
| uint8_t* sprite = &ppu->chr_mem[index * 16U]; | |||
| uint8_t* sprite = chr_mem(ppu, index * 16U); | |||
| uint8_t* dst_line = (uint8_t*)loc; | |||
| int dx = 1; | |||
| if (attr & oam_Attr_Flip_X) { | |||
| @@ -407,7 +414,7 @@ static void update_sprite_hit(nes_ppu* ppu, int block_line, | |||
| if (ppu->control & ppu_Control_Sprite_Bank) { | |||
| index += 0x100U; | |||
| } | |||
| const uint8_t* chr = &ppu->chr_mem[index * 16U]; | |||
| const uint8_t* chr = chr_mem(ppu, index * 16U); | |||
| int render_line = block_line * 8U; | |||
| int start_y = (sprite->y + 1) + y_fine - render_line; | |||
| int end_y = start_y + 8; | |||