summaryrefslogtreecommitdiff
path: root/ppu.c
diff options
context:
space:
mode:
Diffstat (limited to 'ppu.c')
-rw-r--r--ppu.c314
1 files changed, 130 insertions, 184 deletions
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
+}
+
+