| @@ -20,7 +20,7 @@ SRC_SRCS_1 += cart.c mapper.c | |||||
| SRC_SRCS_1 += sdl_render.c sdl_input.c | SRC_SRCS_1 += sdl_render.c sdl_input.c | ||||
| MAPDIR = src/map | 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 | 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; | ines_Header *hdr = (ines_Header*)mem; | ||||
| void* ptr = &hdr[1]; | void* ptr = &hdr[1]; | ||||
| cart->ines_mem = mem; | |||||
| cart->ines_size = len; | |||||
| status = ines_check_mem(hdr); | status = ines_check_mem(hdr); | ||||
| if (0 == status && (hdr->flags_6 & ines_Flag_Trainer)) { | 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) { | 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) { | 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; | status = -1; | ||||
| } else { | } else { | ||||
| cart->prg_rom = ptr; | cart->prg_rom = ptr; | ||||
| cart->prg_rom_size = prg_size; | |||||
| cart->prg_rom_banks = hdr->prg_size_lsb; | |||||
| } | } | ||||
| ptr += prg_size; | ptr += prg_size; | ||||
| } | } | ||||
| if (0 == status) { | 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 = ptr; | ||||
| cart->chr_rom_size = chr_size; | |||||
| cart->chr_rom_banks = hdr->chr_size_lsb * 2; | |||||
| ptr += chr_size; | ptr += chr_size; | ||||
| int index = (hdr->flags_6 & ines_Mapper_Nibble_Lo) >> 4 | | 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; | cart->flags &= ~Cart_Flag_Horizontal; | ||||
| } | } | ||||
| // Don't initialize the mapper until all flags are set! | |||||
| status = nes_map_init(cart->mapper, cart); | status = nes_map_init(cart->mapper, cart); | ||||
| } | } | ||||
| if (0 == status) { | |||||
| cart->ines_mem = mem; | |||||
| cart->ines_size = len; | |||||
| } | |||||
| return status; | return status; | ||||
| } | } | ||||
| @@ -12,9 +12,9 @@ typedef enum { | |||||
| typedef struct nes_cart_t { | typedef struct nes_cart_t { | ||||
| uint8_t* prg_rom; | uint8_t* prg_rom; | ||||
| int prg_rom_size; | |||||
| int prg_rom_banks; | |||||
| uint8_t* chr_rom; | uint8_t* chr_rom; | ||||
| int chr_rom_size; | |||||
| int chr_rom_banks; | |||||
| nes_Cart_Flags flags; | nes_Cart_Flags flags; | ||||
| struct nes_mapper_t* mapper; | struct nes_mapper_t* mapper; | ||||
| @@ -12,9 +12,7 @@ | |||||
| #define INES_ERR(...) INES_LOG("E", __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_trainer_size (0x200U) | |||||
| #define ines_magic "NES\x1a" | #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 { | typedef struct { | ||||
| // TODO: Does this even support CHR ROM? | // TODO: Does this even support CHR ROM? | ||||
| uint8_t* prg_rom; | uint8_t* prg_rom; | ||||
| int prg_rom_size; | |||||
| int prg_rom_banks; | |||||
| uint8_t reg_shift; | uint8_t reg_shift; | ||||
| uint8_t reg_n_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[0] = &map->prg_rom[0]; | ||||
| map->prg_bank[1] = &map->prg_rom[bank]; | map->prg_bank[1] = &map->prg_rom[bank]; | ||||
| } else if (mode == 3) { | } 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; | bank *= nes_prg_rom_page_size; | ||||
| map->prg_bank[0] = &map->prg_rom[bank]; | 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]; | 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)); | mmc1_mapper* map = calloc(1, sizeof(mmc1_mapper)); | ||||
| nes_map->data = map; | nes_map->data = map; | ||||
| if (NULL != 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); | mmc1_reset(nes_map); | ||||
| } | } | ||||
| return (NULL == map ? -1 : 0); | 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]; | 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, | static uint8_t* mmc1_vram_addr(nes_mapper* nes_map, | ||||
| uint16_t addr) { | uint16_t addr) { | ||||
| int page = (addr >> 10) & 3; | int page = (addr >> 10) & 3; | ||||
| @@ -211,4 +216,5 @@ nes_mapper mapper_mmc1 = { | |||||
| .write = mmc1_write, | .write = mmc1_write, | ||||
| .chr_addr = mmc1_chr_addr, | .chr_addr = mmc1_chr_addr, | ||||
| .vram_addr = mmc1_vram_addr, | .vram_addr = mmc1_vram_addr, | ||||
| .chr_write = mmc1_chr_write, | |||||
| }; | }; | ||||
| @@ -5,9 +5,9 @@ | |||||
| typedef struct { | typedef struct { | ||||
| uint8_t* prg_rom; | uint8_t* prg_rom; | ||||
| int prg_rom_size; | |||||
| int prg_rom_banks; | |||||
| uint8_t* chr_rom; | uint8_t* chr_rom; | ||||
| int chr_rom_size; | |||||
| int chr_rom_banks; | |||||
| uint8_t mirror; | uint8_t mirror; | ||||
| uint8_t vram[nes_vram_page_size * 2]; | uint8_t vram[nes_vram_page_size * 2]; | ||||
| uint8_t wram[nes_mem_wram_size]; | 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)); | nrom_mapper* map = calloc(1, sizeof(nrom_mapper)); | ||||
| nes_map->data = map; | nes_map->data = map; | ||||
| if (NULL != 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; | map->mirror = (cart->flags & Cart_Flag_Horizontal) ? 0 : 1; | ||||
| } | } | ||||
| return (NULL == map ? -1 : 0); | 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, | static inline uint8_t* nrom_prg_addr(nrom_mapper* map, | ||||
| uint16_t addr) { | uint16_t addr) { | ||||
| addr &= 0x7FFFU; | addr &= 0x7FFFU; | ||||
| if (addr > map->prg_rom_size) { | |||||
| if (map->prg_rom_banks == 1) { | |||||
| addr &= 0x3FFF; | addr &= 0x3FFF; | ||||
| } | } | ||||
| return &(map->prg_rom[addr]); | 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, | static uint8_t* nrom_chr_addr(nes_mapper* nes_map, | ||||
| uint16_t addr) { | 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, | 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]; | 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 = { | nes_mapper mapper_nrom = { | ||||
| .init = nrom_init, | .init = nrom_init, | ||||
| .reset = nrom_reset, | .reset = nrom_reset, | ||||
| @@ -87,4 +92,5 @@ nes_mapper mapper_nrom = { | |||||
| .write = nrom_write, | .write = nrom_write, | ||||
| .chr_addr = nrom_chr_addr, | .chr_addr = nrom_chr_addr, | ||||
| .vram_addr = nrom_vram_addr, | .vram_addr = nrom_vram_addr, | ||||
| .chr_write = nrom_chr_write, | |||||
| }; | }; | ||||
| @@ -3,9 +3,11 @@ | |||||
| extern nes_mapper mapper_nrom; | extern nes_mapper mapper_nrom; | ||||
| extern nes_mapper mapper_mmc1; | extern nes_mapper mapper_mmc1; | ||||
| extern nes_mapper mapper_cnrom; | |||||
| nes_mapper* nes_mappers[256] = { | nes_mapper* nes_mappers[256] = { | ||||
| [ 0] = &mapper_nrom, | [ 0] = &mapper_nrom, | ||||
| [ 1] = &mapper_mmc1, | [ 1] = &mapper_mmc1, | ||||
| [ 3] = &mapper_cnrom, | |||||
| }; | }; | ||||
| @@ -4,11 +4,6 @@ | |||||
| #include <stdint.h> | #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; | struct nes_cart_t; | ||||
| typedef struct nes_mapper_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); | 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* (*chr_addr)(struct nes_mapper_t*, uint16_t addr); | ||||
| uint8_t* (*vram_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; | void* data; | ||||
| } nes_mapper; | } nes_mapper; | ||||
| @@ -51,9 +47,19 @@ static inline uint8_t* nes_map_chr_addr(nes_mapper* map, | |||||
| return map->chr_addr(map, addr); | 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, | static inline uint8_t* nes_map_vram_addr(nes_mapper* map, | ||||
| uint16_t addr) { | 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, | static inline uint8_t nes_vram_read(nes_mapper* map, | ||||
| @@ -14,6 +14,10 @@ | |||||
| #define nes_clock_cpu_div (12U) | #define nes_clock_cpu_div (12U) | ||||
| #define nes_clock_ppu_div (4U) | #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_start (0x0000U) | ||||
| #define nes_mem_ram_size (0x0800U) | #define nes_mem_ram_size (0x0800U) | ||||
| #define nes_mem_ppu_start (0x2000U) | #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) { | if (ppu->addr < nes_ppu_mem_vram_start) { | ||||
| // printf("PPU: CHR MEM READ %04x > %02x\n", ppu->addr, val); | // 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 + | } else if (ppu->addr < nes_ppu_mem_vram_start + | ||||
| nes_ppu_mem_vram_size) { | nes_ppu_mem_vram_size) { | ||||
| VRAM_LOG("PPU: VRAM READ %04x > %02x\n", ppu->addr, val); | 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 | ppu->addr - nes_ppu_mem_vram_start | ||||
| ); | ); | ||||
| } else if (ppu->addr < nes_ppu_mem_pal_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->data = *(nes_map_chr_addr(ppu->mapper, | ||||
| ppu->addr)); | ppu->addr)); | ||||
| */ | |||||
| } | } | ||||
| } | } | ||||
| @@ -162,7 +163,7 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { | |||||
| } else { | } else { | ||||
| // printf("PPU: CHR MEM WRITE %04x > %02x\n", ppu->addr, val); | // 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) ? | 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, | static void update_sprite_hit(nes_ppu* ppu, int block_line, | ||||
| const void* back_line, | |||||
| const void* back_pixels, | |||||
| int back_pitch) { | int back_pitch) { | ||||
| const oam_sprite* sprite = (oam_sprite*)ppu->oam; | const oam_sprite* sprite = (oam_sprite*)ppu->oam; | ||||
| int x_fine = ppu->scroll_x % 8; | 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 (start_y < 0) start_y = 0; | ||||
| if (end_y > 8) end_y = 8; | if (end_y > 8) end_y = 8; | ||||
| int hit = -1; | 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; | back += (render_line + start_y) * back_pitch; | ||||
| for (int y = start_y; y < end_y; ++y) { | for (int y = start_y; y < end_y; ++y) { | ||||
| hit = eval_sprite_line( | hit = eval_sprite_line( | ||||
| ppu, render_line + y, | |||||
| ppu, render_line + y - y_fine, | |||||
| sprite, chr, back | sprite, chr, back | ||||
| ); | ); | ||||
| if (hit >= 0) { | if (hit >= 0) { | ||||