Browse Source

Deeper integration of APU interrupts - needs refactor?

v2
Nathaniel Walizer 8 months ago
parent
commit
9a698bb79c
5 changed files with 90 additions and 25 deletions
  1. +35
    -10
      src/apu.c
  2. +5
    -2
      src/apu.h
  3. +28
    -10
      src/f6502.c
  4. +1
    -0
      src/f6502.h
  5. +21
    -3
      src/nes.c

+ 35
- 10
src/apu.c View File

@@ -1,25 +1,35 @@
#include "memory.h"

//#define NESE_DEBUG "APU"
#include "log.h"


void nes_apu_init(nes_APU* apu, nes_APU_Memory* mem, int freq) {
// TODO
apu->frame_delay = nes_apu_quarter_frame_ticks;
// TODO: Set LFSR?
}

int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {
static int nes_apu_run(nes_APU* apu, nes_Memory* core_mem,
int ticks) {
// int ret = 0;
nes_APU_Memory* mem = &core_mem->apu;

for (int i = 0; i < core_mem->apu.n_events; ++i) {
int reg = core_mem->apu.events[i].reg;
int val = core_mem->apu.events[i].val;

LOGD("$40%02X < %02X", reg, val);

switch (reg) {
// TODO: 0x00 - 0x13
// TODO: 0x15
case 0x17: // Frame Counter
apu->frame_reg = val & ( apu_Frame_Mode |
apu_Frame_Inhibit);
(void)val;
#if 0
val &= (apu_Frame_Mode | apu_Frame_Inhibit);
apu->frame_reg = val;
apu->frame = 0;
apu->frame_delay = 0;
apu->frame_delay = nes_apu_quarter_frame_ticks;

if (val & apu_Frame_Mode) {
// TODO
@@ -32,9 +42,8 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {
if (val & apu_Frame_Inhibit) {
mem->status &= ~apu_Status_Frame_Int;
}
#endif
break;

}
}

@@ -42,10 +51,11 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {

// Frame advance

apu->frame_delay += nes_apu_hsync_ticks;
apu->frame_delay -= ticks;

if (apu->frame_delay >= nes_apu_quarter_frame_ticks) {
apu->frame_delay -= nes_apu_quarter_frame_ticks;
if (apu->frame_delay <= 0) {
LOGD("Quarter Frame (%d overshoot)", -apu->frame_delay);
apu->frame_delay += nes_apu_quarter_frame_ticks;

int end = 0;
int quarter_frame = 1;
@@ -80,8 +90,10 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {
}

if (end) {
LOGD("Frame sequence end (frame_reg %02X)", apu->frame_reg);
if (0 == apu->frame_reg) {
mem->status |= apu_Status_Frame_Int;
// ret = 1;
}
apu->frame = 0;
} else {
@@ -92,4 +104,17 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {
// TODO: DMC Interrupt

return (mem->status & apu_Status_Frame_Int);
// return ret;
}

int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {
int ticks = nes_apu_hsync_ticks - apu->ticks_handled;
apu->ticks_handled = 0;
return nes_apu_run(apu, core_mem, ticks);
}

int nes_apu_finish_qframe(nes_APU* apu, nes_Memory* core_mem) {
LOGD("Finishing Quarter Frame (%d remain)", apu->frame_delay);
apu->ticks_handled = apu->frame_delay;
return nes_apu_run(apu, core_mem, apu->frame_delay);
}

+ 5
- 2
src/apu.h View File

@@ -4,8 +4,9 @@
#include <stdint.h>


#define nes_apu_hsync_ticks (4U)
#define nes_apu_quarter_frame_ticks (262U)
#define nes_ppu_to_apu_tick_ratio (4U)
#define nes_apu_hsync_ticks (341U * 4U)
#define nes_apu_quarter_frame_ticks (341U * 262U)


typedef struct {
@@ -90,6 +91,7 @@ typedef struct {
nes_apu_Frame frame_reg;
int frame;
int frame_delay;
int ticks_handled;

uint8_t reg[20];

@@ -100,6 +102,7 @@ struct nes_Memory;

void nes_apu_init(nes_APU* apu, nes_APU_Memory* mem, int freq);
int nes_apu_hsync(nes_APU*, struct nes_Memory*);
int nes_apu_finish_qframe(nes_APU*, struct nes_Memory*);


#endif // NESE_APU_H_

+ 28
- 10
src/f6502.c View File

@@ -9,12 +9,12 @@
#include <stdio.h>
#endif

#ifdef F6502_TEST
#include "nes.h"

#include <stddef.h>
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type,member));})
#endif

//#define NESE_DEBUG "Core"
#include "log.h"
@@ -98,8 +98,12 @@ static inline uint8_t f6502_read(nes_Memory* mem,
case 0x4000:
switch (addr & 0x1FU) {
case 0x15: /* APU Status */
{ uint8_t ret = mem->apu.status;
{ f6502_Core* core = container_of(mem, f6502_Core, memory);

uint8_t ret = mem->apu.status;
mem->apu.status &= ~apu_Status_Frame_Int;
f6502_set_IRQ(core, 0);

return ret;
}

@@ -264,12 +268,22 @@ static inline int f6502_write(nes_Memory* mem,
case 0x4000:
switch (addr & 0x1FU) {
case 0x17: // APU Frame
// TODO: Clear interrupt here?
/*
if (val & apu_Frame_apu_Frame_Inhibit) {
f6502_Int_IRQ(core, 0);
{ f6502_Core* core = container_of(mem, f6502_Core, memory);
nes* sys = container_of(core, nes, core);
nes_APU* apu = &sys->apu;

// printf("APU: $%04X < %02X\n", addr, val);

apu->frame_reg = val;
apu->frame = 0;
apu->frame_delay = nes_apu_quarter_frame_ticks;

// Clear this ASAP
if (val & apu_Frame_Inhibit) {
mem->apu.status &= ~apu_Status_Frame_Int;
f6502_set_IRQ(core, 0);
}
*/
}
case 0x00:
case 0x01:
case 0x02:
@@ -437,8 +451,10 @@ static inline bool f6502_irq_ready(const f6502_Core* core) {
static inline int f6502_check_interrupts(f6502_Core* core) {
if (f6502_nmi_ready(core)) {
core->interrupts |= f6502_Int_NMI_Serviced;
LOGI("%lu: NMI", core->clocks);
return f6502_interrupt(core, f6502_Vector_NMI);
} else if (f6502_irq_ready(core)) {
LOGI("%lu: IRQ", core->clocks);
return f6502_interrupt(core, f6502_Vector_IRQ);
}
return 0;
@@ -1704,6 +1720,8 @@ int f6502_step(f6502_Core* core, int clocks) {
}
*/
clocks_elapsed += f6502_check_interrupts(core);
return ( f6502_do_step(core, clocks - clocks_elapsed) +
clocks_elapsed);
clocks_elapsed += f6502_do_step(core,
clocks - clocks_elapsed);
core->clocks += clocks_elapsed;
return clocks_elapsed;
}

+ 1
- 0
src/f6502.h View File

@@ -41,6 +41,7 @@ typedef enum {
} f6502_Interrupt;

struct f6502_Core {
uint64_t clocks;
f6502_Registers registers;
f6502_Interrupt interrupts;
nes_Memory memory;


+ 21
- 3
src/nes.c View File

@@ -3,7 +3,7 @@
#include "nes.h"
#include "port.h"

//#define NESE_DEBUG "NES"
#define NESE_DEBUG "NES"
#include "log.h"


@@ -88,7 +88,6 @@ static int nes_hsync(nes* sys, void* plat) {
// Emulate the happy part of the backdrop override quirk
int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
(mem->addr & 0x1FU) : 0;
LOGD("Background: %d", pal_idx);
// Don't use the rendering palette (masked transparency)
status = nese_frame_start(plat, mem->pal_bank[0x300 + pal_idx] & 0x3FU);

@@ -117,7 +116,6 @@ int nes_loop(nes* sys, void* plat) {
bool vbl_clear = false;

while (0 == status) {
// TODO: Move to inline PPU function?
int target_dot = nes_ppu_scanline_dots;

if ( nes_ppu_frame_end_line - 1 == sys->ppu.scanline &&
@@ -148,6 +146,26 @@ int nes_loop(nes* sys, void* plat) {
}

int dots_to_run = target_dot - dot;

// Will APU quarter frame hit this line?
// TODO: Only do this when the IRQ is pending?
if (sys->apu.frame_delay <= nes_apu_hsync_ticks) {
int qframe_dot = (
( sys->apu.frame_delay +
(nes_ppu_to_apu_tick_ratio - 1)) /
nes_ppu_to_apu_tick_ratio
);
if (dot >= qframe_dot) {
int irq = nes_apu_finish_qframe(
&sys->apu, &sys->core.memory
);
// TODO: How might this interfere with MMC3?
f6502_set_IRQ(&sys->core, irq);
} else {
dots_to_run = qframe_dot - dot;
}
}

if (dots_to_run > 0) {
int cpu_cycles = (dots_to_run + 2) / 3;
dot += 3 * f6502_step(&sys->core, cpu_cycles);


Loading…
Cancel
Save