Browse Source

Fix PPU timing

- DMA was being double-counted for PPU cycles
master
Nathaniel Walizer 1 year ago
parent
commit
41536a2b8f
4 changed files with 25 additions and 27 deletions
  1. +1
    -3
      src/nes.c
  2. +1
    -1
      src/nese.c
  3. +15
    -16
      src/ppu.c
  4. +8
    -7
      src/ppu.h

+ 1
- 3
src/nes.c View File

@@ -43,9 +43,7 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) {
nes_mem_read(sys, ((uint16_t)val << 8) + i);
}
sys->cpu.cycle += 513U;
sys->ppu.cycle += (513U * nes_clock_cpu_div) /
nes_clock_ppu_div;
// TODO: Increment APU cycles?
// Other subsystem cycles are driven by CPU cycles

} else if (addr < nes_mem_exp_start) {
nes_apu_write(&sys->apu, addr, val);


+ 1
- 1
src/nese.c View File

@@ -58,7 +58,7 @@ int main(int argc, char* argv[]) {
int last_frame_rendered = -1;
for (int i = 0; i < n_loops && status == 0; ++i) {
int run = 0;
status = nes_run(&sys, 12, &run);
status = nes_run(&sys, nes_clock_cpu_div, &run);
total_cycles += run;
/*
float us_run = ( run * 1000. * 1000. *


+ 15
- 16
src/ppu.c View File

@@ -141,34 +141,27 @@ int nes_ppu_run(nes_ppu* ppu, int cycles) {

ppu->cycle += cycles;

if ( 0 != ppu->hit_line &&
ppu->scanline > ppu->hit_line &&
ppu->cycle > ppu->hit_dot) {
ppu->status |= ppu_Status_Hit;
}

while (ppu->cycle >= nes_ppu_dots) {
ppu->cycle -= nes_ppu_dots;
if ( ppu->scanline <= nes_ppu_prerender &&
if ( ppu->scanline < nes_ppu_prerender &&
(ppu->frame & 1)) {
// Prerender line is one dot shorter in odd frames
// Fake it by incrementing the cycle in this case
// TODO: Only if actually rendering
ppu->cycle++;
}

ppu->scanline++;
if (ppu->scanline >= nes_ppu_prerender +
nes_ppu_height +
nes_ppu_postrender +
nes_ppu_vblank) {

if (ppu->scanline >= nes_ppu_frame) {
ppu->status &= ~(ppu_Status_VBlank | ppu_Status_Hit);
ppu->hit_line = 0;
ppu->hit_dot = 0;
ppu->scanline = 0;
ppu->scanline -= nes_ppu_frame;
ppu->frame++;
// TODO: Render callback if vblank was previously set
} else if (ppu->scanline >= nes_ppu_prerender +
nes_ppu_height +
nes_ppu_postrender) {
// TODO: Render callback if vblank was previously set?

} else if (ppu->scanline >= nes_ppu_active) {
ppu->status |= ppu_Status_VBlank;
if (ppu->control & ppu_Control_VBlank) {
vblank = 1;
@@ -176,6 +169,12 @@ int nes_ppu_run(nes_ppu* ppu, int cycles) {
}
}

if ( 0 != ppu->hit_line &&
ppu->scanline > ppu->hit_line &&
ppu->cycle > ppu->hit_dot) {
ppu->status |= ppu_Status_Hit;
}

return vblank;
}



+ 8
- 7
src/ppu.h View File

@@ -10,15 +10,16 @@
#define nes_ppu_postrender (1U)
#define nes_ppu_vblank (20U)

#define nes_ppu_active_cycles \
(nes_ppu_dots * (nes_ppu_prerender + \
nes_ppu_height + \
nes_ppu_postrender))
#define nes_ppu_active (nes_ppu_prerender + \
nes_ppu_height + \
nes_ppu_postrender)

#define nes_ppu_vblank_cycles (nes_ppu_dots * nes_ppu_vblank)
#define nes_ppu_frame (nes_ppu_active + nes_ppu_vblank)

#define nes_frame_cycles (nes_ppu_active_cycles + \
nes_ppu_vblank_cycles)
#define nes_ppu_active_cycles (nes_ppu_dots * nes_ppu_active)
#define nes_ppu_vblank_cycles (nes_ppu_dots * nes_ppu_vblank)

#define nes_ppu_frame_cycles (nes_ppu_dots * nes_ppu_frame)

#define nes_ppu_scan_w (320U) // Includes full overscan
#define nes_ppu_scan_h nes_ppu_height


Loading…
Cancel
Save