diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-04-04 01:03:19 +0200 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-04-04 01:03:19 +0200 |
| commit | 8c82be43720d9e221a9e2541c9ff6151015838bb (patch) | |
| tree | 6ed341720934bbf69a386e254c4e9449f9051616 | |
| parent | 6274071e3857c1640cc5aef804cb86509ab312f9 (diff) | |
move read/write prg/chr/cirom data to mapper
| -rw-r--r-- | ines2.c | 4 | ||||
| -rw-r--r-- | mapper.c | 36 | ||||
| -rw-r--r-- | mapper.h | 18 | ||||
| -rw-r--r-- | mapper_001.c | 37 | ||||
| -rw-r--r-- | mapper_066.c | 37 | ||||
| -rw-r--r-- | memory.c | 48 | ||||
| -rw-r--r-- | mknes.c | 44 | ||||
| -rw-r--r-- | mknes.h | 46 | ||||
| -rw-r--r-- | opengl.c | 2 | ||||
| -rw-r--r-- | ppu.c | 314 | ||||
| -rw-r--r-- | render.c | 6 | ||||
| -rw-r--r-- | shader.h | 6 |
12 files changed, 297 insertions, 301 deletions
@@ -76,11 +76,11 @@ static int ines2_load(struct nes_state *state, char *path) { } // Read PRG - printf("prgsize_read: %ld\n", fread(state->rom, 1, prg_size, f)); + printf("prgsize_read: %ld\n", fread(state->prg_rom, 1, prg_size, f)); // Read CHR if present if(chr_size > 0) { - printf("chrsize_read: %ld\n", fread(state->chrrom, 1, chr_size, f)); + printf("chrsize_read: %ld\n", fread(state->chr_rom, 1, chr_size, f)); } fclose(f); @@ -7,15 +7,19 @@ struct mapper_entry { int id; - uint8_t (*read)(struct nes_state *state, uint32_t addr); - void (*write)(struct nes_state *state, uint32_t addr, uint8_t value); + uint8_t (*prg_read)(struct nes_state *state, uint32_t addr); + void (*prg_write)(struct nes_state *state, uint32_t addr, uint8_t value); + uint8_t (*chr_read)(struct nes_state *state, uint32_t addr); + void (*chr_write)(struct nes_state *state, uint32_t addr, uint8_t value); + uint8_t (*ciram_read)(struct nes_state *state, uint32_t addr); + void (*ciram_write)(struct nes_state *state, uint32_t addr, uint8_t value); void (*tick)(struct nes_state *state); void (*init)(struct nes_state *state); }; static struct mapper_entry mapper_table[] = { - { 0, mapper_001_read, mapper_001_write, mapper_001_tick, mapper_001_init }, - { 66, mapper_066_read, mapper_066_write, mapper_066_tick, mapper_066_init }, + { 0, mapper_001_prg_read, mapper_001_prg_write, mapper_001_chr_read, mapper_001_chr_write, mapper_001_ciram_read, mapper_001_ciram_write, mapper_001_tick, mapper_001_init }, + { 66, mapper_066_prg_read, mapper_066_prg_write, mapper_066_chr_read, mapper_066_chr_write, mapper_066_ciram_read, mapper_066_ciram_write, mapper_066_tick, mapper_066_init }, // { 1, mapper_mmc1_read, ... }, etc }; @@ -23,10 +27,14 @@ static void mapper_setup(struct nes_state *state) { uint32_t mapper = state->ines.mapper; for(uint32_t i = 0; i < sizeof(mapper_table)/sizeof(mapper_table[0]); i++) { if(mapper_table[i].id == mapper) { - state->mapper.read = mapper_table[i].read; - state->mapper.write = mapper_table[i].write; - state->mapper.tick = mapper_table[i].tick; - state->mapper.init = mapper_table[i].init; + state->mapper.prg_read = mapper_table[i].prg_read; + state->mapper.prg_write = mapper_table[i].prg_write; + state->mapper.chr_read = mapper_table[i].chr_read; + state->mapper.chr_write = mapper_table[i].chr_write; + state->mapper.ciram_read = mapper_table[i].ciram_read; + state->mapper.ciram_write = mapper_table[i].ciram_write; + state->mapper.tick = mapper_table[i].tick; + state->mapper.init = mapper_table[i].init; state->mapper.init(state); return; } @@ -34,9 +42,13 @@ static void mapper_setup(struct nes_state *state) { // NOTE(peter): Not sure how safe this is, but it sure is funny... printf("Unsupported mapper %d, falling back to NROM\n", mapper); - state->mapper.read = mapper_001_read; - state->mapper.write = mapper_001_write; - state->mapper.tick = mapper_001_tick; - state->mapper.init = mapper_001_init; + state->mapper.prg_read = mapper_001_prg_read; + state->mapper.prg_write = mapper_001_prg_write; + state->mapper.chr_read = mapper_001_chr_read; + state->mapper.chr_write = mapper_001_chr_write; + state->mapper.ciram_read = mapper_001_ciram_read; + state->mapper.ciram_write = mapper_001_ciram_write; + state->mapper.tick = mapper_001_tick; + state->mapper.init = mapper_001_init; state->mapper.init(state); } @@ -1,2 +1,20 @@ #include "mapper_066.h" + + + +struct mapper { + void (*init)(struct nes_state *state); + uint8_t (*prg_read)(struct nes_state *state, uint32_t addr); + void (*prg_write)(struct nes_state *state, uint32_t addr, uint8_t value); + uint8_t (*chr_read)(struct nes_state *state, uint32_t addr); + void (*chr_write)(struct nes_state *state, uint32_t addr, uint8_t value); + uint8_t (*ciram_read)(struct nes_state *state, uint32_t addr); + void (*ciram_write)(struct nes_state *state, uint32_t addr, uint8_t value); + void (*tick)(struct nes_state *state); +}; + +union mapper_data { + struct mapper_066 m066; +}; + diff --git a/mapper_001.c b/mapper_001.c index 8c65367..4704444 100644 --- a/mapper_001.c +++ b/mapper_001.c @@ -4,20 +4,43 @@ static void mapper_001_init(struct nes_state *state) { // Nothing to initialize for 001 } -static uint8_t mapper_001_read(struct nes_state *state, uint32_t addr) { +static uint8_t mapper_001_prg_read(struct nes_state *state, uint32_t addr) { uint32_t prg_size = state->ines.prg_size; uint32_t mask = (state->ines.prg_size == 16384) ? 0x3fff : 0x7fff; - return state->rom[addr & mask]; + return state->prg_rom[addr & mask]; } -static void mapper_001_write(struct nes_state *state, uint32_t addr, uint8_t value) { - (void)state; - (void)addr; - (void)value; +static void mapper_001_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} + +static uint8_t mapper_001_chr_read(struct nes_state *state, uint32_t addr) { + return state->chr_rom[addr]; +} + +static void mapper_001_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} + +static uint8_t mapper_001_ciram_read(struct nes_state *state, uint32_t addr) { + if(state->ines.mirroring == 0) { // Horizontal + addr = (addr & 0x800) | (addr & 0x3ff); + } else { // Vertical (default fallback) + addr = addr & 0x7ff; + } + + return state->ciram[addr]; +} + +static void mapper_001_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(state->ines.mirroring == 0) { + addr = (addr & 0x800) | (addr & 0x3ff); + } else { + addr = addr & 0x7ff; + } + + state->ciram[addr] = value; } static void mapper_001_tick(struct nes_state *state) { - (void)state; } diff --git a/mapper_066.c b/mapper_066.c index aabf471..7a2b542 100644 --- a/mapper_066.c +++ b/mapper_066.c @@ -4,15 +4,15 @@ static void mapper_066_init(struct nes_state *state) { state->map.m066.chr_offset = 0; } -static uint8_t mapper_066_read(struct nes_state *state, uint32_t addr) { +static uint8_t mapper_066_prg_read(struct nes_state *state, uint32_t addr) { if(addr >= 0x8000) { uint32_t base = state->map.m066.prg_offset; - return state->rom[base + (addr - 0x8000)]; + return state->prg_rom[base + (addr - 0x8000)]; } return 0; } -static void mapper_066_write(struct nes_state *state, uint32_t addr, uint8_t value) { +static void mapper_066_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { if(addr >= 0x8000) { uint32_t prg_bank = (value >> 4) & 3; uint32_t chr_bank = (value >> 0) & 3; @@ -22,6 +22,37 @@ static void mapper_066_write(struct nes_state *state, uint32_t addr, uint8_t val } } +static uint8_t mapper_066_chr_read(struct nes_state *state, uint32_t addr) { + return state->chr_rom[state->map.m066.chr_offset + addr]; +} + +static void mapper_066_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} + +static uint8_t mapper_066_ciram_read(struct nes_state *state, uint32_t addr) { + uint32_t mirrored = addr & 0x0fff; + + if(state->ines.mirroring == 0) { // Horizontal + mirrored = (mirrored & 0x800) | (mirrored & 0x3ff); + } else { // Vertical (default fallback) + mirrored = mirrored & 0x7ff; + } + + return state->ciram[mirrored]; +} + +static void mapper_066_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { + uint32_t mirrored = addr & 0x0fff; + + if(state->ines.mirroring == 0) { + mirrored = (mirrored & 0x800) | (mirrored & 0x3ff); + } else { + mirrored = mirrored & 0x7ff; + } + + state->ciram[mirrored] = value; +} + static void mapper_066_tick(struct nes_state *state) { // No IRQ or timing logic needed } @@ -13,17 +13,18 @@ static uint8_t memory_read(struct nes_state *restrict state, uint32_t offset) { if(offset < 0x2000) { return state->ram[offset & 0x07ff]; } else if(offset < 0x4000) { - switch(offset & 7) { - case 2: return ppu_read_2002(state); - case 4: return ppu_read_2004(state); - case 7: return ppu_read_2007(state); - default: return 0; - } + return ppu_read(state, offset); + // switch(offset & 7) { + // case 2: return ppu_read_2002(state); + // case 4: return ppu_read_2004(state); + // case 7: return ppu_read_2007(state); + // default: return 0; + // } } else if(offset < 0x4020) { // TODO: APU and I/O reads return 0; } else if(offset >= 0x6000) { - return state->mapper.read(state, offset); + return state->mapper.prg_read(state, offset); } else { return 0; } @@ -40,22 +41,23 @@ static void memory_write(struct nes_state *restrict state, uint32_t offset, uint if(offset < 0x2000) { state->ram[offset & 0x07ff] = value; } else if(offset < 0x4000) { - switch(offset & 7) { - case 0: ppu_write_2000(state, value); break; - case 1: ppu_write_2001(state, value); break; - case 3: ppu_write_2003(state, value); break; - case 4: ppu_write_2004(state, value); break; - case 5: ppu_write_2005(state, value); break; - case 6: ppu_write_2006(state, value); break; - case 7: ppu_write_2007(state, value); break; - default: break; - } + ppu_write(state, offset, value); + // switch(offset & 7) { + // case 0: ppu_write_2000(state, value); break; + // case 1: ppu_write_2001(state, value); break; + // case 3: ppu_write_2003(state, value); break; + // case 4: ppu_write_2004(state, value); break; + // case 5: ppu_write_2005(state, value); break; + // case 6: ppu_write_2006(state, value); break; + // case 7: ppu_write_2007(state, value); break; + // default: break; + // } } else if(offset == 0x4014) { ppu_dma_4014(state, value); } else if(offset < 0x4020) { // TODO: APU and I/O writes } else if(offset >= 0x6000) { - state->mapper.write(state, offset, value); + state->mapper.prg_write(state, offset, value); } } @@ -70,7 +72,7 @@ static uint8_t memory_read_dma(struct nes_state *restrict state, uint32_t offset // APU and I/O — usually ignored or blocked during DMA return 0; } else if(offset >= 0x6000) { - return state->mapper.read(state, offset); + return state->mapper.prg_read(state, offset); } else { return 0; } @@ -83,15 +85,11 @@ static uint8_t memory_read_dummy(struct nes_state *restrict state, uint32_t offs if(offset < 0x2000) { return 0; } else if(offset < 0x4000) { - switch(offset & 7) { - case 2: return ppu_read_2002(state); - case 7: return ppu_read_2007(state); - default: return 0; - } + return ppu_read(state, offset); } else if(offset < 0x4020) { return 0; } else if(offset >= 0x6000) { - return state->mapper.read(state, offset); + return state->mapper.prg_read(state, offset); } else { return 0; } @@ -75,14 +75,16 @@ static GLFWwindow *window; #define DEBUG_PRINT printf #include "timer.c" +#include "audio.c" + #include "opengl_loader.c" #include "opengl.c" #include "render.c" -#include "audio.c" + #include "callbacks.c" +// NES core #include "mapper.h" - #include "mknes.h" // #include "apu.c" #include "ppu.c" @@ -95,7 +97,6 @@ struct nes_state nstate; static uint32_t frames; - int main(int argc, char **argv) { #ifdef _WIN32 timeBeginPeriod(1); @@ -108,11 +109,11 @@ int main(int argc, char **argv) { // protect_opcode_lut(); ppu_reset(&nstate); // ines2_load(&nstate, "data/nrom/10-Yard Fight (USA, Europe).nes"); - ines2_load(&nstate, "data/nrom/Balloon Fight (USA).nes"); + // ines2_load(&nstate, "data/nrom/Balloon Fight (USA).nes"); // ines2_load(&nstate, "data/nrom/Excitebike (Japan, USA).nes"); // ines2_load(&nstate, "data/nrom/Ice Climber (USA, Europe, Korea).nes"); // ines2_load(&nstate, "data/nrom/Kung Fu (Japan, USA).nes"); - // ines2_load(&nstate, "data/nrom/Super Mario Bros. (World) (HVC-SM).nes"); + ines2_load(&nstate, "data/nrom/Super Mario Bros. (World) (HVC-SM).nes"); // ines2_load(&nstate, "data/nrom/Urban Champion (World).nes"); // ines2_load(&nstate, "data/nrom/Wrecking Crew (World).nes"); // ines2_load(&nstate, "data/nrom/scanline.nes"); @@ -129,8 +130,8 @@ int main(int argc, char **argv) { // ines2_load(&nstate, "data/nrom/NEStress.NES"); mapper_setup(&nstate); - uint32_t lo = nstate.mapper.read(&nstate, 0xfffc); - uint32_t hi = nstate.mapper.read(&nstate, 0xfffd); + uint32_t lo = nstate.mapper.prg_read(&nstate, 0xfffc); + uint32_t hi = nstate.mapper.prg_read(&nstate, 0xfffd); nstate.cpu.pc = (hi << 8) | lo; struct timer_handle *timer = timer_new(FRAME_INTERVAL_NS); @@ -171,7 +172,7 @@ int main(int argc, char **argv) { timer_start(timer); while(!glfwWindowShouldClose(window)) { - timer_wait(timer); + // timer_wait(timer); glfwPollEvents(); // // @@ -182,22 +183,21 @@ int main(int argc, char **argv) { nstate.ppu.frame_ready = 0; frames++; - uint32_t *dst = buffer; - uint8_t *src = nstate.ppu.pixels; - for(uint32_t y = 0; y < 240; ++y) { - for(uint32_t x = 0; x < 256; ++x) { - uint8_t val = *src++; - if(val >= 64) val = 0; - dst[x] = nes_palette[val]; - } - dst += BUFFER_WIDTH; - } -// - apply_phosphor_decay(); - render_frame(); + // uint32_t * restrict dst = buffer; + // uint8_t * restrict src = nstate.ppu.pixels; + // for(uint32_t y = 0; y < 240; ++y) { + // for(uint32_t x = 0; x < 256; ++x) { + // uint8_t val = *src++; + // if(val >= 64) val = 0; + // dst[x] = nes_palette[val]; + // } + // dst += BUFFER_WIDTH; + // } + // apply_phosphor_decay(); + // render_frame(); glfwSwapBuffers(window); } - +printf("%d\n", frames); glfwDestroyWindow(window); } else { fprintf(stderr, "Failed to create window\n"); @@ -1,33 +1,16 @@ - -// #define PPU_CTRL_NMI_ENABLE 0x80 -// #define PPU_CTRL_MASTER_SLAVE 0x40 -// #define PPU_CTRL_SPRITE_HEIGHT 0x20 -// #define PPU_CTRL_BG_TILE_SELECT 0x10 -// #define PPU_CTRL_SPRITE_TILE_SELECT 0x08 -// #define PPU_CTRL_NT_SELECT_Y 0x04 -// #define PPU_CTRL_NT_SELECT_X 0x02 -// #define PPU_CTRL_VRAM_INCREMENT 0x01 - -// #define PPU_MASK_SHOW_BG 0x08 -// #define PPU_MASK_SHOW_SPRITES 0x10 - -// #define PPU_STATUS_VBLANK 0x80 -// #define PPU_STATUS_SPRITE0_HIT 0x40 -// #define PPU_STATUS_OVERFLOW 0x20 - +#define PPU_CTRL_NMI 0x80 #define PPU_CTRL_BG_TILE_SELECT 0x10 #define PPU_CTRL_SPRITE_TILE_SELECT 0x08 -#define PPU_CTRL_NMI 0x80 #define PPU_CTRL_VRAM_INCREMENT 0x04 // Define constants for PPU control and mask bits #define PPU_CTRL_NMI 0x80 -#define PPU_CTRL_VRAM_INCREMENT 0x04 #define PPU_CTRL_SPRITE_HEIGHT 0x20 #define PPU_CTRL_SPRITE_TILE 0x08 +#define PPU_CTRL_VRAM_INCREMENT 0x04 #define PPU_MASK_SHOW_BG 0x08 #define PPU_MASK_SHOW_SPRITES 0x10 @@ -37,11 +20,6 @@ #define MIRROR_VERTICAL 1 #define MIRROR_FOURSCREEN 2 - -struct nes_state; - - - struct ppu_state { uint8_t pixels[256 * 240]; @@ -64,6 +42,8 @@ struct ppu_state { uint8_t vram_read_buffer; uint8_t write_latch; + uint8_t open_bus; + uint8_t palette[0x20]; uint32_t scanline; @@ -118,17 +98,6 @@ struct ines_state { uint32_t chr_size; }; -struct mapper { - void (*init)(struct nes_state *state); - uint8_t (*read)(struct nes_state *state, uint32_t addr); - void (*write)(struct nes_state *state, uint32_t addr, uint8_t value); - void (*tick)(struct nes_state *state); -}; - -union mapper_data { - struct mapper_066 m066; -}; - struct nes_state { struct ines_state ines; struct cpu_state cpu; @@ -141,11 +110,12 @@ struct nes_state { uint8_t ram[0x800]; uint8_t sram[0x2000]; uint8_t ciram[0x1000]; // NOTE(peter): Originally 0x800 bytes, but extended as it should work for up to fourway, this is optimization, reality is 2kb, but there is no side-effects, so this is fine! - uint8_t rom[4 * 1024 * 1024]; - uint8_t chrrom[4 * 1024 * 1024]; + uint8_t prg_rom[4 * 1024 * 1024]; + uint8_t chr_rom[4 * 1024 * 1024]; }; -static uint32_t nes_palette[64] = { + +__attribute__((aligned(4096))) static uint32_t nes_palette[64] = { 0x585858ff, 0x00237cff, 0x0d1099ff, 0x300092ff, 0x4f006cff, 0x600035ff, 0x5c0500ff, 0x461800ff, 0x272d00ff, 0x093e00ff, 0x004500ff, 0x004106ff, 0x003545ff, 0x000000ff, 0x000000ff, 0x000000ff, 0xa1a1a1ff, 0x0b53d7ff, 0x3337feff, 0x6621f7ff, 0x9515beff, 0xac166eff, 0xa62721ff, 0x864300ff, @@ -34,8 +34,6 @@ static void opengl_setup(void) { GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_start); GLuint fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_start); -printf("%d %d\n", vertex_shader, fragment_shader); - state.shader_program = glCreateProgram(); glAttachShader(state.shader_program, vertex_shader); glAttachShader(state.shader_program, fragment_shader); @@ -1,155 +1,127 @@ -static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); - -static void ppu_sprite_shift(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - if(!(ppu->reg_mask & 0x10)) { - return; - } +static uint8_t __attribute__((aligned(64))) ppu_bitreverse_lut[256] = { +# define R2(n) n, n + 2*64, n + 1*64, n + 3*64 +# define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16) +# define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 ) + R6(0), R6(2), R6(1), R6(3) +}; +#undef R2 +#undef R4 +#undef R6 - for(uint32_t i = 0; i < ppu->sprite_count; i++) { - if(ppu->sprite_positions[i] > 0) { - ppu->sprite_positions[i]--; - } else { - ppu->sprite_shift_lo[i] <<= 1; - ppu->sprite_shift_hi[i] <<= 1; - } - } -} +static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); static void ppu_reset(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; memset(ppu, 0, sizeof(struct ppu_state)); } -static uint32_t ppu_resolve_ciram(struct nes_state *state, uint32_t addr) { - addr &= 0x0fff; - - switch(state->ines.mirroring) { - case MIRROR_VERTICAL: { - // $2000/$2800 → $0000, $2400/$2C00 → $0400 - return (addr & 0x0400) | (addr & 0x03ff); - } - case MIRROR_HORIZONTAL: { - // $2000/$2400 → $0000, $2800/$2C00 → $0400 - return ((addr & 0x0800) >> 1) | (addr & 0x03ff); - } - default: { - return addr & 0x07ff; - } - } -} - - -static uint8_t ppu_ciram_read(struct nes_state *state, uint32_t addr) { - return state->ciram[ppu_resolve_ciram(state, addr)]; -} - -static void ppu_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { - state->ciram[ppu_resolve_ciram(state, addr)] = value; -} - -static void ppu_write_2000(struct nes_state *state, uint8_t value) { - struct ppu_state *ppu = &state->ppu; - ppu->reg_ctrl = value; - ppu->temp_addr = (ppu->temp_addr & 0xf3ff) | ((value & 0x03) << 10); -} - -static void ppu_write_2001(struct nes_state *state, uint8_t value) { - state->ppu.reg_mask = value; -} - -static void ppu_write_2003(struct nes_state *state, uint8_t value) { - state->ppu.oam_addr = value; -} - -static void ppu_write_2004(struct nes_state *state, uint8_t value) { - state->ppu.oam[state->ppu.oam_addr] = value; - state->ppu.oam_addr++; -} - -static void ppu_write_2005(struct nes_state *state, uint8_t value) { - struct ppu_state *ppu = &state->ppu; - - if(ppu->write_latch == 0) { - ppu->fine_x = value & 0x07; - ppu->temp_addr = (ppu->temp_addr & ~0x001f) | (value >> 3); - ppu->write_latch = 1; - } else { - ppu->temp_addr = (ppu->temp_addr & ~0x73e0) | ((value & 0x07) << 12) | ((value & 0xf8) << 2); - ppu->write_latch = 0; - } -} - -static void ppu_write_2006(struct nes_state *state, uint8_t value) { +static void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { struct ppu_state *ppu = &state->ppu; - if(ppu->write_latch == 0) { - ppu->temp_addr = (ppu->temp_addr & 0x00ff) | ((value & 0x3f) << 8); - ppu->write_latch = 1; - } else { - ppu->temp_addr = (ppu->temp_addr & 0xff00) | value; - ppu->vram_addr = ppu->temp_addr; - ppu->write_latch = 0; - } -} + switch(offset & 7) { + case 0: { // 2000 + ppu->reg_ctrl = value; + ppu->temp_addr = (ppu->temp_addr & 0xf3ff) | ((value & 0x03) << 10); + ppu->open_bus = value; + } break; + + case 1: { // 2001 + ppu->reg_mask = value; + ppu->open_bus = value; + } break; + + case 3: { // 2003 + ppu->oam_addr = value; + } break; + + case 4: { // 2004 + ppu->oam[ppu->oam_addr] = value; + ppu->oam_addr++; + } break; + + case 5: { // 2005 + if(ppu->write_latch == 0) { + ppu->fine_x = value & 0x07; + ppu->temp_addr = (ppu->temp_addr & ~0x001f) | (value >> 3); + ppu->write_latch = 1; + } else { + ppu->temp_addr = (ppu->temp_addr & ~0x73e0) | ((value & 0x07) << 12) | ((value & 0xf8) << 2); + ppu->write_latch = 0; + } + ppu->open_bus = value; + } break; -static void ppu_write_2007(struct nes_state *state, uint8_t value) { - struct ppu_state *ppu = &state->ppu; - uint32_t addr = ppu->vram_addr & 0x3fff; - if(addr < 0x2000) { - // CHR-RAM, skip - } else if(addr < 0x3f00) { - ppu_ciram_write(state, addr, value); - } else if(addr < 0x4000) { - uint32_t pal_addr = addr & 0x1f; - if((pal_addr & 0x13) == 0x10) { - pal_addr &= ~0x10; - } - ppu->palette[pal_addr] = value; + case 6: { // 2006 + if(ppu->write_latch == 0) { + ppu->temp_addr = (ppu->temp_addr & 0x00ff) | ((value & 0x3f) << 8); + ppu->write_latch = 1; + } else { + ppu->temp_addr = (ppu->temp_addr & 0xff00) | value; + ppu->vram_addr = ppu->temp_addr; + ppu->write_latch = 0; + } + ppu->open_bus = value; + } break; + + case 7: { // 2007 + uint32_t addr = ppu->vram_addr & 0x3fff; + if(addr < 0x2000) { + // CHR-RAM, skip + } else if(addr < 0x3f00) { + state->mapper.ciram_write(state, addr, value); + } else if(addr < 0x4000) { + uint32_t pal_addr = addr & 0x1f; + if((pal_addr & 0x13) == 0x10) { + pal_addr &= ~0x10; + } + ppu->palette[pal_addr] = value; + } + ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; + } break; } - - ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; } -static uint8_t ppu_read_2002(struct nes_state *state) { +static uint8_t ppu_read(struct nes_state *state, uint32_t offset) { struct ppu_state *ppu = &state->ppu; - uint8_t result = ppu->reg_status; - - ppu->reg_status &= ~0x80; - ppu->write_latch = 0; - - return result; -} - -static uint8_t ppu_read_2004(struct nes_state *state) { - return state->ppu.oam[state->ppu.oam_addr]; -} + uint8_t result = ppu->open_bus; + + switch(offset & 7) { + case 2: { // 2002 + result = ppu->reg_status; + ppu->reg_status &= ~0x80; + ppu->write_latch = 0; + } break; + + case 4: { // 2004 + result = ppu->oam[ppu->oam_addr]; + } break; + + case 7: { // 2007 + uint32_t addr = ppu->vram_addr & 0x3fff; + result = 0; + + if(addr < 0x2000) { + result = ppu->vram_read_buffer; + ppu->vram_read_buffer = state->mapper.chr_read(state, addr); + } else if(addr < 0x3f00) { + result = state->mapper.ciram_read(state, addr); + } else if(addr < 0x4000) { + uint32_t pal_addr = addr & 0x1f; + if((pal_addr & 0x13) == 0x10) { + pal_addr &= ~0x10; + } + result = ppu->palette[pal_addr]; + } -static uint8_t ppu_read_2007(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - uint32_t addr = ppu->vram_addr & 0x3fff; - uint8_t result = 0; - - if(addr < 0x2000) { - result = ppu->vram_read_buffer; - ppu->vram_read_buffer = state->chrrom[addr]; - } else if(addr < 0x3f00) { - result = ppu_ciram_read(state, addr); - } else if(addr < 0x4000) { - uint32_t pal_addr = addr & 0x1f; - if((pal_addr & 0x13) == 0x10) { - pal_addr &= ~0x10; - } - result = ppu->palette[pal_addr]; + ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; + } break; } - - ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; + ppu->open_bus = result; return result; } -#if 1 static void ppu_evaluate_sprites(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; uint8_t sprite_height = (ppu->reg_ctrl & 0x20) ? 16 : 8; @@ -183,40 +155,6 @@ static void ppu_evaluate_sprites(struct nes_state *state) { ppu->sprite_count = n; } -#else -static void ppu_evaluate_sprites(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - uint8_t sprite_height = (ppu->reg_ctrl & 0x20) ? 16 : 8; - uint8_t n = 0; - - for(uint8_t i = 0; i < 64; i++) { - uint8_t y = ppu->oam[i * 4 + 0]; - int row = (int)ppu->scanline - y; - - if(row >= 0 && row < sprite_height) { - if(n < 8) { - uint8_t *src = ppu->oam + i * 4; - uint8_t *dst = ppu->secondary_oam + n * 4; - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - ppu->sprite_indexes[n] = i; - if(i == 0) { - ppu->sprite_zero_hit_possible = 1; - } - n++; - } else { - ppu->reg_status |= 0x20; - break; - } - } - } - - ppu->sprite_count = n; -} -#endif - static void ppu_fetch_sprite_patterns(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; for(uint8_t i = 0; i < ppu->sprite_count; i++) { @@ -225,9 +163,7 @@ static void ppu_fetch_sprite_patterns(struct nes_state *state) { uint8_t row = ppu->scanline - y; uint8_t height = (ppu->reg_ctrl & 0x20) ? 16 : 8; - if(attr & 0x80) { - row = height - 1 - row; - } + row = (attr & 0x80) ? height - 1 - row : row; uint32_t addr; if(height == 16) { @@ -243,12 +179,12 @@ static void ppu_fetch_sprite_patterns(struct nes_state *state) { addr = bank + tile * 16 + row; } - uint8_t lsb = state->chrrom[addr]; - uint8_t msb = state->chrrom[addr + 8]; + uint8_t lsb = state->mapper.chr_read(state, addr); + uint8_t msb = state->mapper.chr_read(state, addr + 8); if(attr & 0x40) { - lsb = ((lsb * 0x0202020202ULL & 0x010884422010ULL) % 1023) & 0xff; - msb = ((msb * 0x0202020202ULL & 0x010884422010ULL) % 1023) & 0xff; + lsb = ppu_bitreverse_lut[lsb]; + msb = ppu_bitreverse_lut[msb]; } ppu->sprite_shift_lo[i] = lsb; @@ -272,10 +208,6 @@ static void ppu_render_pixel(struct nes_state *state) { uint32_t x = ppu->dot - 1; uint32_t y = ppu->scanline; - // if(x >= 256 || y >= 240) { - // return; - // } - uint32_t bit = 0x8000 >> ppu->fine_x; if(ppu->reg_mask & 0x08) { @@ -338,7 +270,18 @@ static void ppu_tick(struct nes_state *state) { } if(rendering && ((dot >= 2 && dot <= 257) || (dot >= 322 && dot <= 337))) { - ppu_sprite_shift(state); + + if(ppu->reg_mask & 0x10) { + for(uint32_t i = 0; i < ppu->sprite_count; i++) { + if(ppu->sprite_positions[i] > 0) { + ppu->sprite_positions[i]--; + } else { + ppu->sprite_shift_lo[i] <<= 1; + ppu->sprite_shift_hi[i] <<= 1; + } + } + } + ppu->bg_shift_pattern_low <<= 1; ppu->bg_shift_pattern_high <<= 1; ppu->bg_shift_attrib_low <<= 1; @@ -350,12 +293,12 @@ static void ppu_tick(struct nes_state *state) { switch(dot % 8) { case 1: { uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); - ppu->bg_next_tile_id = ppu_ciram_read(state, nt_addr); + ppu->bg_next_tile_id = state->mapper.ciram_read(state, nt_addr); break; } case 3: { uint32_t attr_addr = 0x23c0 | (ppu->vram_addr & 0x0c00) | ((ppu->vram_addr >> 4) & 0x38) | ((ppu->vram_addr >> 2) & 0x07); - uint8_t attr = ppu_ciram_read(state, attr_addr); + uint8_t attr = state->mapper.ciram_read(state, attr_addr & 0x0fff); uint8_t shift = ((ppu->vram_addr >> 4) & 4) | (ppu->vram_addr & 2); ppu->bg_next_tile_attrib = (attr >> shift) & 3; break; @@ -365,7 +308,7 @@ static void ppu_tick(struct nes_state *state) { uint32_t tile = ppu->bg_next_tile_id; uint32_t fine_y = (ppu->vram_addr >> 12) & 7; uint32_t addr_lsb = (base + tile * 16 + fine_y) & 0x1fff; - ppu->bg_next_tile_lsb = state->chrrom[addr_lsb]; + ppu->bg_next_tile_lsb = state->mapper.chr_read(state, addr_lsb); break; } case 7: { @@ -373,7 +316,7 @@ static void ppu_tick(struct nes_state *state) { uint32_t tile = ppu->bg_next_tile_id; uint32_t fine_y = (ppu->vram_addr >> 12) & 7; uint32_t addr_msb = (base + tile * 16 + fine_y + 8) & 0x1fff; - ppu->bg_next_tile_msb = state->chrrom[addr_msb]; + ppu->bg_next_tile_msb = state->mapper.chr_read(state, addr_msb); break; } case 0: { @@ -419,7 +362,7 @@ static void ppu_tick(struct nes_state *state) { ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); } - if(scanline == 261 && dot >= 280 && dot <= 304) { + if(scanline == 261 && dot == 304) { //>= 280 && dot <= 304) { ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); } @@ -479,6 +422,9 @@ static void ppu_dma_4014(struct nes_state *state, uint8_t page) { state->cycles++; ppu_tick(state); ppu_tick(state); ppu_tick(state); - ppu_write_2004(state, value); + // ppu_write_2004(state, value); + ppu_write(state, 4, value); } -}
\ No newline at end of file +} + + @@ -30,8 +30,8 @@ static inline void apply_phosphor_decay(void) { _mm_prefetch((char*)&src[x + 2 * BUFFER_WIDTH], _MM_HINT_T0); _mm_prefetch((char*)&dst[x + 2 * BUFFER_WIDTH], _MM_HINT_T0); - __m128i new_pixels = _mm_loadu_si128((__m128i*)&src[x]); - __m128i old_pixels = _mm_loadu_si128((__m128i*)&dst[x]); + __m128i new_pixels = _mm_load_si128((__m128i*)&src[x]); + __m128i old_pixels = _mm_load_si128((__m128i*)&dst[x]); __m256i old_lo = _mm256_cvtepu8_epi16(old_pixels); __m256i new_lo = _mm256_cvtepu8_epi16(new_pixels); @@ -41,7 +41,7 @@ static inline void apply_phosphor_decay(void) { __m128i final_pixels = _mm_packus_epi16(_mm256_castsi256_si128(blended), _mm256_extracti128_si256(blended, 1)); final_pixels = _mm_or_si128(final_pixels, _mm_and_si128(old_pixels, alpha_mask)); - _mm_storeu_si128((__m128i*)&dst[x], final_pixels); + _mm_store_si128((__m128i*)&dst[x], final_pixels); } } } @@ -8,10 +8,10 @@ #define CRTS_WARP 1 //-------------------------------------------------------------- // Try different masks -#define CRTS_MASK_GRILLE 1 +// #define CRTS_MASK_GRILLE 1 // #define CRTS_MASK_GRILLE_LITE 1 // #define CRTS_MASK_NONE 1 -// #define CRTS_MASK_SHADOW 1 +#define CRTS_MASK_SHADOW 1 // -------------------------------------------------------------- // Scanline thinness // 0.50 = fused scanlines @@ -30,5 +30,5 @@ // 0.25 = large amount of mask (not recommended, too dark) // 0.50 = recommended default // 1.00 = no shadow mask -#define INPUT_MASK 0.5 +#define INPUT_MASK 0.6 |
