summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apu.c164
-rw-r--r--memory.c58
-rw-r--r--mknes.c6
-rw-r--r--mknes.h15
-rw-r--r--ppu_registers.c6
5 files changed, 226 insertions, 23 deletions
diff --git a/apu.c b/apu.c
new file mode 100644
index 0000000..0c16123
--- /dev/null
+++ b/apu.c
@@ -0,0 +1,164 @@
+
+
+static inline void ppu_tick(struct nes_state *state);
+static inline uint8_t memory_read(struct nes_state *state, uint32_t offset);
+
+// DMC frequency table (NTSC)
+static const uint16_t dmc_rate_table[16] = {
+ 428, 380, 340, 320, 286, 254, 226, 214,
+ 190, 160, 142, 128, 106, 85, 72, 54
+};
+
+// $4015 write
+static void apu_write4015(struct nes_state *state, uint8_t val) {
+ struct apu_state *apu = &state->apu;
+
+ if(val & 0x10) {
+ if(!apu->dmc_dma_enabled || apu->dmc_bytes_remaining == 0) {
+ apu->dmc_current_addr = 0xC000 + ((uint16_t)apu->dmc_sample_addr << 6);
+ apu->dmc_bytes_remaining = ((uint16_t)apu->dmc_sample_len << 4);
+ }
+ apu->dmc_dma_enabled = 1;
+ } else {
+ apu->dmc_dma_enabled = 0;
+ }
+}
+
+// $4015 read
+static uint8_t apu_read4015(struct nes_state *state) {
+ struct apu_state *apu = &state->apu;
+ uint8_t result = 0;
+
+ if(apu->dmc_bytes_remaining > 0) {
+ result |= 0x10;
+ }
+ if(apu->irq_pending) {
+ result |= 0x40;
+ apu->irq_pending = 0;
+ }
+ return result;
+}
+
+// $4010–$4013, $4015 write
+static void apu_write(struct nes_state *state, uint16_t addr, uint8_t val) {
+ struct apu_state *apu = &state->apu;
+
+ switch(addr) {
+ case 0x4010: {
+ apu->dmc_irq_enable = (val >> 7) & 1;
+ apu->dmc_loop_flag = (val >> 6) & 1;
+ apu->dmc_freq_index = val & 0x0F;
+ } break;
+ case 0x4011: {
+ // DAC write ignored
+ } break;
+ case 0x4012: {
+ apu->dmc_sample_addr = val;
+ } break;
+ case 0x4013: {
+ apu->dmc_sample_len = val;
+ } break;
+ case 0x4015: {
+ apu_write4015(state, val);
+ } break;
+ }
+}
+
+// APU tick
+static inline void apu_tick(struct nes_state *state) {
+ struct apu_state *apu = &state->apu;
+
+ apu->frame_cycle++;
+
+ if(apu->mode == 0) {
+ if(apu->frame_cycle == 7457) {
+ // Quarter frame
+ } else if(apu->frame_cycle == 14913) {
+ // Quarter frame
+ // Half frame
+ } else if(apu->frame_cycle == 22371) {
+ // Quarter frame
+ } else if(apu->frame_cycle == 29829) {
+ // Half frame
+ if(!apu->irq_inhibit) {
+ apu->irq_pending = 1;
+ }
+ } else if(apu->frame_cycle >= 29830) {
+ apu->frame_cycle = 0;
+ }
+ } else {
+ if(apu->frame_cycle == 7457) {
+ // Quarter frame
+ } else if(apu->frame_cycle == 14913) {
+ // Quarter frame
+ // Half frame
+ } else if(apu->frame_cycle == 22371) {
+ // Quarter frame
+ } else if(apu->frame_cycle == 29829) {
+ // Half frame
+ } else if(apu->frame_cycle == 37281) {
+ // Quarter frame
+ } else if(apu->frame_cycle >= 37282) {
+ apu->frame_cycle = 0;
+ }
+ }
+
+ // if(apu->mode == 0) {
+ // if(apu->frame_cycle == 7457 || apu->frame_cycle == 14913 || apu->frame_cycle == 22371) {
+ // // Quarter frame
+ // }
+ // if(apu->frame_cycle == 14913 || apu->frame_cycle == 29829) {
+ // // Half frame
+ // }
+ // if(apu->frame_cycle == 29829 && !apu->irq_inhibit) {
+ // apu->irq_pending = 1;
+ // }
+ // if(apu->frame_cycle >= 29830) {
+ // apu->frame_cycle = 0;
+ // }
+ // } else {
+ // if(apu->frame_cycle == 7457 || apu->frame_cycle == 14913 || apu->frame_cycle == 22371 || apu->frame_cycle == 37281) {
+ // // Quarter frame
+ // }
+ // if(apu->frame_cycle == 14913 || apu->frame_cycle == 29829) {
+ // // Half frame
+ // }
+ // if(apu->frame_cycle >= 37282) {
+ // apu->frame_cycle = 0;
+ // }
+ // }
+
+ if(apu->dmc_dma_enabled && apu->dmc_bytes_remaining > 0) {
+ apu->dmc_sample_timer++;
+ if(apu->dmc_sample_timer >= dmc_rate_table[apu->dmc_freq_index]) {
+ apu->dmc_sample_timer = 0;
+
+ uint8_t val = memory_read(state, apu->dmc_current_addr);
+ (void)val;
+
+ apu->dmc_current_addr++;
+ if(apu->dmc_current_addr == 0x0000) {
+ apu->dmc_current_addr = 0x8000;
+ }
+
+ apu->dmc_bytes_remaining--;
+
+ if(apu->dmc_bytes_remaining == 0) {
+ if(apu->dmc_loop_flag) {
+ apu->dmc_current_addr = 0xC000 + ((uint16_t)apu->dmc_sample_addr << 6);
+ apu->dmc_bytes_remaining = ((uint16_t)apu->dmc_sample_len << 4);
+ } else {
+ if(apu->dmc_irq_enable) {
+ apu->irq_pending = 1;
+ }
+ apu->dmc_dma_enabled = 0;
+ }
+ }
+
+ for(uint32_t i = 0; i < 4; i++) {
+ state->cpu.cycles++;
+ ppu_tick(state);
+ }
+ }
+ }
+}
diff --git a/memory.c b/memory.c
index 1eaf489..db5fcd0 100644
--- a/memory.c
+++ b/memory.c
@@ -1,10 +1,46 @@
+#if 1
__attribute__((hot))
static inline uint8_t memory_read(struct nes_state *state, uint32_t offset) {
state->cpu.cycles++;
ppu_tick(state);
- // apu_tick(state);
+ apu_tick(state);
+
+ uint8_t result = 0;
+
+ if(offset >= 0x8000) { // MOST
+ result = state->mapper_function.prg_rom_read(state, offset);
+
+ } else if(offset < 0x2000) { // SECOND
+ result = state->ram[offset & 0x07ff];
+
+ } else if(offset < 0x4000) { // THIRD
+ result = ppu_read(state, offset);
+
+ } else if(offset == 0x4015) { // APU status
+ result = apu_read4015(state);
+
+ } else if(offset == 0x4016 || offset == 0x4017) {
+ uint32_t index = offset & 1;
+ uint8_t value = (state->ppu.input_latch[index] >> state->ppu.input_bit[index]) & 1;
+ state->ppu.input_bit[index]++;
+ result = value | 0x40; // Bit 6 open bus high, bit 7 low
+
+ } else if(offset >= 0x6000) {
+ result = state->mapper_function.prg_ram_read(state, offset);
+ }
+
+ return result;
+}
+
+#else
+
+__attribute__((hot))
+static inline uint8_t memory_read(struct nes_state *state, uint32_t offset) {
+ state->cpu.cycles++;
+ ppu_tick(state);
+ apu_tick(state);
uint8_t result = 0;
@@ -29,12 +65,13 @@ static inline uint8_t memory_read(struct nes_state *state, uint32_t offset) {
return result;
}
+#endif
__attribute__((hot))
static inline uint8_t memory_read_dummy(struct nes_state *state, uint32_t offset) {
state->cpu.cycles++;
ppu_tick(state);
- // apu_tick(state);
+ apu_tick(state);
if(offset >= 0x2000 && offset < 0x4000) {
return ppu_read(state, offset);
@@ -46,7 +83,7 @@ __attribute__((hot))
static inline void memory_write(struct nes_state *state, uint32_t offset, uint8_t value) {
state->cpu.cycles++;
ppu_tick(state);
- // apu_tick(state);
+ apu_tick(state);
if(offset < 0x2000) {
state->ram[offset & 0x07ff] = value;
@@ -55,32 +92,23 @@ static inline void memory_write(struct nes_state *state, uint32_t offset, uint8_
ppu_write(state, offset, value);
} else if(offset < 0x4018) {
- // APU + joypad + DMA
if(offset == 0x4014) {
ppu_dma_4014(state, value);
- // } else if(offset == 0x4015) { // DMC Enable (APU)
- // state->apu.dmc_dma_enabled = (value & 0x10) ? 1 : 0;
-
} else if(offset == 0x4016) {
+ // joypad strobe
uint8_t prev = state->ppu.input_strobe;
state->ppu.input_strobe = value & 1;
if(prev == 1 && (value & 1) == 0) {
- // Latch current inputs
state->ppu.input_latch[0] = state->ppu.input[0];
state->ppu.input_latch[1] = state->ppu.input[1];
state->ppu.input_bit[0] = 0;
state->ppu.input_bit[1] = 0;
}
- // } else if(offset == 0x4017) { // Frame Counter (APU)
- // state->apu.mode = (value >> 7) & 1;
- // state->apu.irq_inhibit = (value >> 6) & 1;
- // state->apu.frame_cycle = 0;
- // if(state->apu.irq_inhibit) {
- // state->apu.irq_pending = 0;
- // }
+ } else if(offset >= 0x4010 && offset <= 0x4017) {
+ apu_write(state, offset, value);
}
} else if(offset >= 0x6000) { // NOTE(peter): Might need to move this upwards when we implement a mapper that has prg_ram_*
diff --git a/mknes.c b/mknes.c
index a2f764c..068bd83 100644
--- a/mknes.c
+++ b/mknes.c
@@ -92,7 +92,7 @@ static uint32_t frames; // debug information
// NES core
#include "mappers/mapper.h"
#include "mknes.h"
-// #include "apu.c"
+#include "apu.c"
#include "ppu.c"
#include "ppu_registers.c"
#include "memory.c"
@@ -142,11 +142,11 @@ int main(int argc, char **argv) {
// ines2_load(nstate, "data/0000/raster_demos/RasterTest3e.NES");
// ines2_load(nstate, "data/0000/NEStress.NES");
// ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).zip");
- // ines2_load(nstate, "data/0042/Super Mario Bros. + Duck Hunt (USA).zip");
+ ines2_load(nstate, "data/0042/Super Mario Bros. + Duck Hunt (USA).zip");
// ines2_load(nstate, "data/0000/Xevious - The Avenger (USA).zip");
// ines2_load(nstate, "data/tv.nes");
- ines2_load(nstate, "data/Life Force (USA).zip"); // 2002
+ // ines2_load(nstate, "data/Life Force (USA).zip"); // 2002
// ines2_load(nstate, "data/0003/Flipull - An Exciting Cube Game (Japan) (En).zip");
// ines2_load(nstate, "data/0003/Friday the 13th (USA).zip");
diff --git a/mknes.h b/mknes.h
index 8d12e95..0ae37f4 100644
--- a/mknes.h
+++ b/mknes.h
@@ -68,11 +68,22 @@ struct ppu_state {
struct apu_state {
uint32_t frame_cycle;
+
uint8_t mode;
uint8_t irq_inhibit;
uint8_t irq_pending;
+
+ uint8_t dmc_irq_enable;
+ uint8_t dmc_loop_flag;
uint8_t dmc_dma_enabled;
- uint32_t dmc_sample_timer;
+
+ uint8_t dmc_freq_index;
+ uint8_t dmc_sample_addr;
+ uint8_t dmc_sample_len;
+
+ uint16_t dmc_current_addr;
+ uint16_t dmc_bytes_remaining;
+ uint16_t dmc_sample_timer;
} __attribute__((packed, aligned(64)));
struct cpu_state {
@@ -110,7 +121,7 @@ struct nes_state {
union mapper_data mapper_data;
struct cpu_state cpu;
struct ines_state ines;
- // struct apu_state apu;
+ struct apu_state apu;
uint8_t ram[RAM_SIZE] __attribute__((aligned(4096)));
uint8_t ciram[CIRAM_SIZE] __attribute__((aligned(4096)));
diff --git a/ppu_registers.c b/ppu_registers.c
index 6217908..8423774 100644
--- a/ppu_registers.c
+++ b/ppu_registers.c
@@ -128,7 +128,7 @@ static inline void ppu_dma_4014(struct nes_state *state, uint8_t page) {
for(uint8_t i = 0; i < idle_cycles; i++) {
state->cpu.cycles++;
ppu_tick(state);
- // apu_tick(state);
+ apu_tick(state);
}
for(uint32_t i = 0; i < 256; i++) {
@@ -136,12 +136,12 @@ static inline void ppu_dma_4014(struct nes_state *state, uint8_t page) {
state->cpu.cycles++;
ppu_tick(state);
- // apu_tick(state);
+ apu_tick(state);
uint8_t value = memory_read_dma(state, addr);
state->cpu.cycles++;
ppu_tick(state);
- // apu_tick(state);
+ apu_tick(state);
ppu_write(state, 4, value);
}