From 5409798e800b6deb5d5874401a2925d1e18d8bd3 Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Mon, 7 Apr 2025 15:55:23 +0200 Subject: almost back to normal after bytestream excursion --- build.sh | 3 +- mknes.c | 17 +++++- ppu.c | 169 +++++--------------------------------------------------- ppu_registers.c | 135 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 159 deletions(-) create mode 100644 ppu_registers.c diff --git a/build.sh b/build.sh index aed20c0..6ab9fc1 100755 --- a/build.sh +++ b/build.sh @@ -5,7 +5,8 @@ PROJECT_NAME="mknes" # Change this for each new project # Base configuration common to all builds CFLAGS="-std=gnu11 " -CFLAGS+="-mavx2 -mbmi2 -march=native -mfunction-return=keep -mindirect-branch=keep " +CFLAGS+="-mavx2 -mbmi2 -march=native " +CFLAGS+="-mfunction-return=keep -mindirect-branch=keep " CFLAGS+="-fwrapv -ffast-math -fno-trapping-math -fwhole-program " CFLAGS+="-fno-stack-protector -fno-PIE -no-pie -fno-strict-aliasing -ffunction-sections -fdata-sections " # CFLAGS+="-fno-exceptions -fno-rtti -fno-use-cxa-atexit " diff --git a/mknes.c b/mknes.c index 977a3be..255b123 100644 --- a/mknes.c +++ b/mknes.c @@ -87,6 +87,7 @@ static GLFWwindow *window; #include "mknes.h" // #include "apu.c" #include "ppu.c" +#include "ppu_registers.c" #include "memory.c" #include "cpu.c" #include "ines2.c" @@ -132,9 +133,10 @@ int main(int argc, char **argv) { timeBeginPeriod(1); #endif + state.toggle_crt_emulation = 1; struct nes_state *nstate = allocate_nes_state(); + // ppu_generate_full_timeline(); - state.toggle_crt_emulation = 1; setbuf(stdout, 0); init_opcode_lut(); init_opcode_ud_lut(); @@ -225,12 +227,20 @@ int main(int argc, char **argv) { timer_start(timer); +#if 1 + for(uint32_t i = 0; i < 0x5000; ++ i) { + while(!nstate->ppu.frame_ready) { + // PROFILE_NAMED("nes emulator"); + cpu_tick(nstate); + } + nstate->ppu.frame_ready = 0; + frames++; + } +#else while(!glfwWindowShouldClose(window)) { - // for(uint32_t i = 0; i < 0x5000; ++ i) { timer_wait(timer); glfwPollEvents(); -// // while(!nstate->ppu.frame_ready) { // PROFILE_NAMED("nes emulator"); cpu_tick(nstate); @@ -252,6 +262,7 @@ int main(int argc, char **argv) { render_frame(); glfwSwapBuffers(window); } +#endif printf("total frames: %6.6d total cycles: %ld\n", frames, nstate->cycles); glfwDestroyWindow(window); } else { diff --git a/ppu.c b/ppu.c index d2772d8..2015b1d 100644 --- a/ppu.c +++ b/ppu.c @@ -10,119 +10,11 @@ static uint8_t __attribute__((aligned(64))) ppu_bitreverse_lut[256] = { #undef R4 #undef R6 -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)); } -__attribute__((always_inline, hot)) -static inline void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { - struct ppu_state *ppu = &state->ppu; - - 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; - - 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) { - state->mapper.chr_write(state, addr, value); - } else if(addr < 0x3f00) { - state->mapper.ciram_write(state, addr, value); - } else if(addr < 0x4000) { - uint32_t pal_addr = addr & 0x1f; - if((pal_addr & 3) == 0) { - pal_addr &= ~0x10; - } - ppu->palette[pal_addr] = value; - } - ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; - } break; - } -} - -__attribute__((always_inline, hot)) -static inline uint8_t ppu_read(struct nes_state *state, uint32_t offset) { - struct ppu_state *ppu = &state->ppu; - 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]; - } - - ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; - } break; - } - ppu->open_bus = result; - return result; -} __attribute__((always_inline, hot)) static inline void ppu_evaluate_sprites(struct nes_state *state) { @@ -326,31 +218,27 @@ static void ppu_tick(struct nes_state *state) { case 1: { uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); ppu->bg_next_tile_id = state->mapper.ciram_read(state, nt_addr); - break; - } + } 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 = 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; - } + } break; case 5: { uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; 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->mapper.chr_read(state, addr_lsb); - break; - } + } break; case 7: { uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; 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->mapper.chr_read(state, addr_msb); - break; - } + } break; case 0: { ppu->bg_shift_pattern_low = (ppu->bg_shift_pattern_low & 0xff00) | ppu->bg_next_tile_lsb; ppu->bg_shift_pattern_high = (ppu->bg_shift_pattern_high & 0xff00) | ppu->bg_next_tile_msb; @@ -365,9 +253,7 @@ static void ppu_tick(struct nes_state *state) { } else { ppu->vram_addr++; } - - break; - } + } break; } } break; @@ -427,31 +313,31 @@ static void ppu_tick(struct nes_state *state) { case 1: { uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); ppu->bg_next_tile_id = state->mapper.ciram_read(state, nt_addr); - break; - } + } 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 = 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; - } + } break; + case 5: { uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; 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->mapper.chr_read(state, addr_lsb); - break; - } + } break; + case 7: { uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; 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->mapper.chr_read(state, addr_msb); - break; - } + } break; + case 0: { ppu->bg_shift_pattern_low = (ppu->bg_shift_pattern_low & 0xff00) | ppu->bg_next_tile_lsb; ppu->bg_shift_pattern_high = (ppu->bg_shift_pattern_high & 0xff00) | ppu->bg_next_tile_msb; @@ -467,8 +353,7 @@ static void ppu_tick(struct nes_state *state) { ppu->vram_addr++; } - break; - } + } break; } } break; @@ -521,29 +406,3 @@ static void ppu_tick(struct nes_state *state) { } } -__attribute__((always_inline, hot)) -static inline void ppu_dma_4014(struct nes_state *state, uint8_t page) { - uint32_t base = page << 8; - - // Add 1 or 2 idle cycles depending on current CPU cycle - uint8_t idle_cycles = (state->cycles & 1) ? 1 : 2; - for(uint8_t i = 0; i < idle_cycles; i++) { - state->cycles++; - ppu_tick(state); - } - - for(uint32_t i = 0; i < 256; i++) { - uint32_t addr = base + i; - - state->cycles++; - ppu_tick(state); - uint8_t value = memory_read_dma(state, addr); - - state->cycles++; - ppu_tick(state); - // ppu_write_2004(state, value); - ppu_write(state, 4, value); - } -} - - diff --git a/ppu_registers.c b/ppu_registers.c new file mode 100644 index 0000000..bd85cd8 --- /dev/null +++ b/ppu_registers.c @@ -0,0 +1,135 @@ +__attribute__((always_inline, hot)) +static inline void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { + struct ppu_state *ppu = &state->ppu; + + 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; + + 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) { + state->mapper.chr_write(state, addr, value); + } else if(addr < 0x3f00) { + state->mapper.ciram_write(state, addr, value); + } else if(addr < 0x4000) { + uint32_t pal_addr = addr & 0x1f; + if((pal_addr & 3) == 0) { + pal_addr &= ~0x10; + } + ppu->palette[pal_addr] = value; + } + ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; + } break; + } +} + +__attribute__((always_inline, hot)) +static inline uint8_t ppu_read(struct nes_state *state, uint32_t offset) { + struct ppu_state *ppu = &state->ppu; + 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]; + } + + ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; + } break; + } + ppu->open_bus = result; + return result; +} + +static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); + +__attribute__((always_inline, hot)) +static inline void ppu_dma_4014(struct nes_state *state, uint8_t page) { + uint32_t base = page << 8; + + // Add 1 or 2 idle cycles depending on current CPU cycle + uint8_t idle_cycles = (state->cycles & 1) ? 1 : 2; + for(uint8_t i = 0; i < idle_cycles; i++) { + state->cycles++; + ppu_tick(state); + } + + for(uint32_t i = 0; i < 256; i++) { + uint32_t addr = base + i; + + state->cycles++; + ppu_tick(state); + uint8_t value = memory_read_dma(state, addr); + + state->cycles++; + ppu_tick(state); + // ppu_write_2004(state, value); + ppu_write(state, 4, value); + } +} + + -- cgit v1.2.3