From 19f119e49c91580f49bb02f86bb905a05ba90d6b Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Mon, 21 Apr 2025 14:06:43 +0200 Subject: back to 2560fps after wrangling the ppu_state into two cachelines --- .gitignore | 1 + callbacks.c | 32 ++++++++++++++++---------------- mapper.c | 1 + memory.c | 10 ++++++++++ mknes.c | 15 +++++++++++---- mknes.h | 17 ++++++++++------- ppu.c | 3 +++ 7 files changed, 52 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index f9779a8..25005d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .vscode mknes data +documentation TODO dump_mappers fragment_shader.h diff --git a/callbacks.c b/callbacks.c index a610a43..4523a68 100644 --- a/callbacks.c +++ b/callbacks.c @@ -53,25 +53,25 @@ static void key_callback(GLFWwindow *window, int key, int scancode, int action, if(action == GLFW_PRESS) { switch(key) { - case GLFW_KEY_X: nes_state->input[0] |= (1 << 0); break; // A - case GLFW_KEY_Z: nes_state->input[0] |= (1 << 1); break; // B - case GLFW_KEY_SPACE: nes_state->input[0] |= (1 << 2); break; // Select - case GLFW_KEY_ENTER: nes_state->input[0] |= (1 << 3); break; // Start - case GLFW_KEY_UP: nes_state->input[0] |= (1 << 4); break; - case GLFW_KEY_DOWN: nes_state->input[0] |= (1 << 5); break; - case GLFW_KEY_LEFT: nes_state->input[0] |= (1 << 6); break; - case GLFW_KEY_RIGHT: nes_state->input[0] |= (1 << 7); break; + case GLFW_KEY_X: nes_state->input[0] |= (1 << 0); break; // A + case GLFW_KEY_Z: nes_state->input[0] |= (1 << 1); break; // B + case GLFW_KEY_SPACE: nes_state->input[0] |= (1 << 2); break; // Select + case GLFW_KEY_ENTER: nes_state->input[0] |= (1 << 3); break; // Start + case GLFW_KEY_UP: nes_state->input[0] |= (1 << 4); break; + case GLFW_KEY_DOWN: nes_state->input[0] |= (1 << 5); break; + case GLFW_KEY_LEFT: nes_state->input[0] |= (1 << 6); break; + case GLFW_KEY_RIGHT: nes_state->input[0] |= (1 << 7); break; } } else if(action == GLFW_RELEASE) { switch(key) { - case GLFW_KEY_X: nes_state->input[0] &= ~(1 << 0); break; - case GLFW_KEY_Z: nes_state->input[0] &= ~(1 << 1); break; - case GLFW_KEY_SPACE: nes_state->input[0] &= ~(1 << 2); break; - case GLFW_KEY_ENTER: nes_state->input[0] &= ~(1 << 3); break; - case GLFW_KEY_UP: nes_state->input[0] &= ~(1 << 4); break; - case GLFW_KEY_DOWN: nes_state->input[0] &= ~(1 << 5); break; - case GLFW_KEY_LEFT: nes_state->input[0] &= ~(1 << 6); break; - case GLFW_KEY_RIGHT: nes_state->input[0] &= ~(1 << 7); break; + case GLFW_KEY_X: nes_state->input[0] &= ~(1 << 0); break; + case GLFW_KEY_Z: nes_state->input[0] &= ~(1 << 1); break; + case GLFW_KEY_SPACE: nes_state->input[0] &= ~(1 << 2); break; + case GLFW_KEY_ENTER: nes_state->input[0] &= ~(1 << 3); break; + case GLFW_KEY_UP: nes_state->input[0] &= ~(1 << 4); break; + case GLFW_KEY_DOWN: nes_state->input[0] &= ~(1 << 5); break; + case GLFW_KEY_LEFT: nes_state->input[0] &= ~(1 << 6); break; + case GLFW_KEY_RIGHT: nes_state->input[0] &= ~(1 << 7); break; } } diff --git a/mapper.c b/mapper.c index 6d4891b..935ff61 100644 --- a/mapper.c +++ b/mapper.c @@ -37,6 +37,7 @@ static void mapper_default_tick(struct nes_state *state) { // No IRQ or timing l */ static struct mapper_entry mapper_table[] = { /* Mapper: 0 */ { 0x00, mapper_0000_prg_read, mapper_0000_prg_write, mapper_0000_chr_read, mapper_0000_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0000_init }, +/* Mapper: 2 */ { 0x02, mapper_2002_prg_read, mapper_2002_prg_write, mapper_2002_chr_read, mapper_2002_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_2002_init }, /* Mapper: 3 */ { 0x03, mapper_0003_prg_read, mapper_0003_prg_write, mapper_0003_chr_read, mapper_0003_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0003_init }, /* Mapper: 7 */ { 0x7, mapper_0007_prg_read, mapper_0007_prg_write, mapper_0007_chr_read, mapper_0007_chr_write, mapper_0007_ciram_read, mapper_0007_ciram_write, mapper_default_tick, mapper_0007_init }, /* Mapper: b */ { 0x0b, mapper_000b_prg_read, mapper_000b_prg_write, mapper_000b_chr_read, mapper_000b_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_000b_init }, diff --git a/memory.c b/memory.c index a2e094f..54962a5 100644 --- a/memory.c +++ b/memory.c @@ -8,15 +8,19 @@ static uint8_t memory_read(struct nes_state *restrict state, uint32_t offset) { if(LIKELY(offset < 0x2000)) { return state->ram[offset & 0x07ff]; + } else if(offset < 0x4000) { return ppu_read(state, offset); + } else if(offset == 0x4016 || offset == 0x4017) { uint32_t index = offset & 1; uint8_t value = (state->input_latch[index] >> state->input_bit[index]) & 1; state->input_bit[index]++; return value | 0x40; // Bit 6 open bus high, bit 7 low + } else if(LIKELY(offset >= 0x6000)) { return state->mapper.prg_read(state, offset); + } return 0; } @@ -28,10 +32,13 @@ static void memory_write(struct nes_state *restrict state, uint32_t offset, uint if(LIKELY(offset < 0x2000)) { state->ram[offset & 0x07ff] = value; + } else if(offset < 0x4000) { ppu_write(state, offset, value); + } else if(offset == 0x4014) { ppu_dma_4014(state, value); + } else if(offset == 0x4016) { uint8_t prev = state->input_strobe; state->input_strobe = value & 1; @@ -43,6 +50,7 @@ static void memory_write(struct nes_state *restrict state, uint32_t offset, uint state->input_bit[0] = 0; state->input_bit[1] = 0; } + } else if(offset >= 0x6000) { state->mapper.prg_write(state, offset, value); } @@ -53,8 +61,10 @@ static uint8_t memory_read_dma(struct nes_state *restrict state, uint32_t offset // Do not tick CPU/PPU/APU — caller handles timing if(LIKELY(offset < 0x2000)) { return state->ram[offset & 0x07ff]; + } else if(offset >= 0x6000) { return state->mapper.prg_read(state, offset); + } return 0; } diff --git a/mknes.c b/mknes.c index 9b58753..6bb0475 100644 --- a/mknes.c +++ b/mknes.c @@ -90,6 +90,7 @@ static GLFWwindow *window; #include "render.c" static uint32_t frames; // debug information +// #include "smb_tas.h" // REMOVE ME // NES core #include "mapper.h" @@ -160,7 +161,6 @@ static void dump_nametable_text(struct nes_state *state, const char *filename) { - int main(int argc, char **argv) { #ifdef _WIN32 timeBeginPeriod(1); @@ -179,7 +179,9 @@ int main(int argc, char **argv) { // ines2_load(nstate, "data/0000/Excitebike (Japan, USA).nes"); // ines2_load(nstate, "data/0000/Ice Climber (USA, Europe, Korea).nes"); // ines2_load(nstate, "data/0000/Kung Fu (Japan, USA).nes"); - ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).nes"); + // ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).nes"); + // ines2_load(nstate, "data/Super Mario Bros. (W) (V1.0) [!].nes"); + ines2_load(nstate, "data/Super Mario Bros. (JU) [!].nes"); // ines2_load(nstate, "data/0000/Urban Champion (World).nes"); // ines2_load(nstate, "data/0000/Wrecking Crew (World).nes"); // ines2_load(nstate, "data/0000/scanline.nes"); @@ -199,11 +201,13 @@ int main(int argc, char **argv) { // ines2_load(nstate, "data/0000/Xevious - The Avenger (USA).zip"); // ines2_load(nstate, "data/tv.nes"); + // ines2_load(nstate, "data/Rad Racer II (USA).zip"); + // ines2_load(nstate, "data/0003/Flipull - An Exciting Cube Game (Japan) (En).zip"); // ines2_load(nstate, "data/0003/Friday the 13th (USA).zip"); // ines2_load(nstate, "data/0003/Ghostbusters (Japan).zip"); // ines2_load(nstate, "data/0003/Gradius (USA).zip"); - ines2_load(nstate, "data/0007/Battletoads (USA).zip"); + // ines2_load(nstate, "data/0007/Battletoads (USA).zip"); // ines2_load(nstate, "data/0007/Beetlejuice (USA).zip"); // ines2_load(nstate, "data/0007/Cabal (USA).zip"); @@ -275,6 +279,9 @@ int main(int argc, char **argv) { // printf("Frame: %d\n", frames); + +// static int32_t tas_frame = 0; +// nstate->input[0] = tas_input[tas_frame++]; while(!nstate->ppu.frame_ready) { // PROFILE_NAMED("nes emulator"); cpu_tick(nstate); @@ -282,7 +289,7 @@ int main(int argc, char **argv) { nstate->ppu.frame_ready = 0; frames++; -dump_nametable_text(nstate, "_foofbomb.txt"); +// dump_nametable_text(nstate, "_foofbomb.txt"); uint32_t * restrict dst = display_buffer; //buffer; uint8_t * restrict src = nstate->pixels; diff --git a/mknes.h b/mknes.h index a9e58a0..f8e4be7 100644 --- a/mknes.h +++ b/mknes.h @@ -26,11 +26,11 @@ struct ppu_state { uint16_t bg_shift_attrib_low; uint16_t bg_shift_attrib_high; - uint32_t scanline; - uint32_t dot; - uint32_t vram_addr; - uint32_t temp_addr; - uint32_t fine_x; + uint16_t scanline; + uint16_t dot; + uint16_t vram_addr; + uint16_t temp_addr; + uint8_t fine_x; uint8_t bg_next_tile_id; uint8_t bg_next_tile_attrib; uint8_t bg_next_tile_lsb; @@ -49,20 +49,23 @@ struct ppu_state { uint8_t open_bus; - uint8_t frame_ready; uint8_t sprite_zero_hit_possible; uint8_t sprite_count; + uint8_t palette[32]; + + // NOTE(peter): CACHELINE 2 start here! + uint8_t sprite_indexes[8]; uint8_t sprite_positions[8]; uint8_t sprite_priorities[8]; uint8_t sprite_shift_lo[8]; uint8_t sprite_shift_hi[8]; - uint8_t palette[32]; uint8_t secondary_oam[32]; uint8_t oam[256]; + uint8_t frame_ready; } __attribute__((packed, aligned(64))); struct cpu_state { diff --git a/ppu.c b/ppu.c index 7042b19..b34115c 100644 --- a/ppu.c +++ b/ppu.c @@ -397,6 +397,9 @@ static void ppu_tick(struct nes_state *state) { } if(UNLIKELY(scanline == 241) && dot == 1) { + // static int32_t tas_frame = 0; + // state->input[0] = tas_input[tas_frame++]; + ppu->reg_status |= 0x80; if(ppu->reg_ctrl & 0x80) { state->nmi_pending = 1; -- cgit v1.2.3