summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ines2.c4
-rw-r--r--mapper.c36
-rw-r--r--mapper.h18
-rw-r--r--mapper_001.c37
-rw-r--r--mapper_066.c37
-rw-r--r--memory.c48
-rw-r--r--mknes.c44
-rw-r--r--mknes.h46
-rw-r--r--opengl.c2
-rw-r--r--ppu.c314
-rw-r--r--render.c6
-rw-r--r--shader.h6
12 files changed, 297 insertions, 301 deletions
diff --git a/ines2.c b/ines2.c
index 7e3c17e..9fb743d 100644
--- a/ines2.c
+++ b/ines2.c
@@ -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);
diff --git a/mapper.c b/mapper.c
index 2e27304..a7954b1 100644
--- a/mapper.c
+++ b/mapper.c
@@ -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);
}
diff --git a/mapper.h b/mapper.h
index f38b000..46db0a4 100644
--- a/mapper.h
+++ b/mapper.h
@@ -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
}
diff --git a/memory.c b/memory.c
index 35c1d41..e0ed85a 100644
--- a/memory.c
+++ b/memory.c
@@ -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;
}
diff --git a/mknes.c b/mknes.c
index 54da663..0ca6749 100644
--- a/mknes.c
+++ b/mknes.c
@@ -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");
diff --git a/mknes.h b/mknes.h
index be2d946..ed22652 100644
--- a/mknes.h
+++ b/mknes.h
@@ -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,
diff --git a/opengl.c b/opengl.c
index 847fc12..255eac3 100644
--- a/opengl.c
+++ b/opengl.c
@@ -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);
diff --git a/ppu.c b/ppu.c
index 2c993c9..aff5166 100644
--- a/ppu.c
+++ b/ppu.c
@@ -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
+}
+
+
diff --git a/render.c b/render.c
index 1dc90aa..26b3c19 100644
--- a/render.c
+++ b/render.c
@@ -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);
}
}
}
diff --git a/shader.h b/shader.h
index df838df..1ea10fe 100644
--- a/shader.h
+++ b/shader.h
@@ -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