summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Fors <peter.fors@mindkiller.com>2025-04-07 15:55:23 +0200
committerPeter Fors <peter.fors@mindkiller.com>2025-04-07 15:55:23 +0200
commit5409798e800b6deb5d5874401a2925d1e18d8bd3 (patch)
tree640f57759bd2b35b3797651b07fa4d4b4a03e4fc
parentdebadc464578ac2be6b75a95f6ea6f95bfecba79 (diff)
almost back to normal after bytestream excursion
-rwxr-xr-xbuild.sh3
-rw-r--r--mknes.c17
-rw-r--r--ppu.c169
-rw-r--r--ppu_registers.c135
4 files changed, 165 insertions, 159 deletions
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);
+ }
+}
+
+