diff --git a/Makefile b/Makefile index e0a80f1..c6481cf 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CFLAGS = -O2 -Wall -Wextra -pedantic -Wno-unused-parameter nes: - ${CC} ${CFLAGS} -o nes cpu.c rom.c ppu.c + ${CC} ${CFLAGS} -o nes cpu.c rom.c ppu.c apu.c test: nes ./nes ${HOME}/src/other/nes-test-roms/other/nestest.nes diff --git a/apu.c b/apu.c new file mode 100644 index 0000000..3c0393c --- /dev/null +++ b/apu.c @@ -0,0 +1,55 @@ +#include +#include + +#include "apu.h" + +struct apu apu = {0}; + +void +apu_init(void) +{ + memset(&apu, 0, sizeof(apu)); +} + +uint8_t +apu_read(uint16_t addr) +{ + switch (addr) { + /* pulse1 */ + case 0x4000: + return (apu.pulse1.duty << 6) | (apu.pulse1.envelope << 5) + | (apu.pulse1.const_vol << 4) | apu.pulse1.envelope_vol; + case 0x4001: + return (apu.pulse1.sweep_enable << 7) | (apu.pulse1.sweep_period << 4) + | (apu.pulse1.sweep_negative << 3) | apu.pulse1.sweep_shift_count; + case 0x4002: + return apu.pulse1.timer_low; + case 0x4003: + return (apu.pulse1.length_counter_load << 3) | apu.pulse1.timer_high; + /* pulse2 */ + case 0x4004: + return (apu.pulse2.duty << 6) | (apu.pulse2.envelope << 5) + | (apu.pulse2.const_vol << 4) | apu.pulse2.envelope_vol; + case 0x4005: + return (apu.pulse1.sweep_enable << 7) | (apu.pulse1.sweep_period << 4) + | (apu.pulse1.sweep_negative << 3) | apu.pulse1.sweep_shift_count; + case 0x4006: + return apu.pulse1.timer_low; + case 0x4007: + return (apu.pulse1.length_counter_load << 3) | apu.pulse1.timer_high; + /* status */ + case 0x4015: + return (apu.status.dmc_int << 7) | (apu.status.frame_int << 6) + | (apu.status.dmc_active << 4) | (apu.status.lc_noise << 3) + | (apu.status.lc_triangle << 2) | (apu.status.lc_pulse2 << 1) + | apu.status.lc_pulse1; + default: + fprintf(stderr, "Invalid APU read at $%04X!\n", addr); + return 0; + }; +} + +void +apu_write(uint16_t addr, uint8_t byte) +{ +} diff --git a/apu.h b/apu.h new file mode 100644 index 0000000..4c39a82 --- /dev/null +++ b/apu.h @@ -0,0 +1,105 @@ +#ifndef APU_H +#define APU_H + +#include + +struct pulse { + /* $4000/$4004 */ + uint8_t duty : 2; + uint8_t envelope : 1; + uint8_t const_vol : 1; + uint8_t envelope_vol : 4; + /* $4001/$4005 */ + uint8_t sweep_enable : 1; + uint8_t sweep_period : 3; + uint8_t sweep_negative : 1; + uint8_t sweep_shift_count : 3; + /* $4002/$4006 */ + uint8_t timer_low; + /* $4003/$4007 */ + uint8_t length_counter_load : 5; + uint8_t timer_high : 3; +}; + +struct triangle { + /* $4008 */ + uint8_t counter_disable : 1; + uint8_t counter_reload : 7; + /* $400A */ + uint8_t timer_low; + /* $400B */ + uint8_t counter_load : 5; + uint8_t timer_high : 3; +}; + +struct noise { + /* $400C */ + uint8_t padding1 : 2; + uint8_t loop_envelope : 1; + uint8_t constant_volume : 1; + uint8_t envelope_volume : 4; + /* $400E */ + uint8_t loop_noise : 1; + uint8_t padding2 : 3; + uint8_t noise_period : 4; + /* $400F */ + uint8_t length_counter_load : 5; + uint8_t padding3 : 3; +}; + +struct dmc { + /* $4010 */ + uint8_t irq_enable : 1; + uint8_t loop : 1; + uint8_t padding1 : 2; + uint8_t frequency : 4; + /* $4011 */ + uint8_t padding2 : 1; + uint8_t load_counter : 7; + /* $4012 */ + uint8_t sample_addr; + /* $4013 */ + uint8_t sample_length; +}; + +struct apu { + /* $4000-$4013 (write) */ + struct pulse pulse1; + struct pulse pulse2; + struct triangle triangle; + struct noise noise; + struct dmc dmc; + /* $4015 (write) */ + struct control { + uint8_t padding : 3; + uint8_t dmc_enable : 1; + uint8_t noise_enable : 1; + uint8_t triangle_enable : 1; + uint8_t pulse2_enable : 1; + uint8_t pulse1_enable : 1; + } control; + /* $4015 (read) */ + struct status { + uint8_t dmc_int : 1; + uint8_t frame_int : 1; + uint8_t padding : 1; + uint8_t dmc_active : 1; + uint8_t lc_noise : 1; + uint8_t lc_triangle : 1; + uint8_t lc_pulse2 : 1; + uint8_t lc_pulse1 : 1; + } status; + /* $4017 (write) */ + struct frame_counter { + uint8_t mode : 1; + uint8_t irq_inhibit : 1; + uint8_t padding : 6; + } frame_counter; +}; +extern struct apu apu; + +void apu_tick(void); +uint8_t apu_read(uint16_t addr); +void apu_write(uint16_t addr, uint8_t byte); + +#endif /* APU_H */ diff --git a/cpu.c b/cpu.c index 509f7a8..97ece60 100644 --- a/cpu.c +++ b/cpu.c @@ -5,10 +5,11 @@ #include #include +#include "apu.h" #include "cpu.h" #include "opcodes.h" -#include "rom.h" #include "ppu.h" +#include "rom.h" #define MAX(a, b) ((a > b) ? a : b) @@ -53,9 +54,10 @@ peek(uint16_t addr) return rom.prg_rom[addr - 0x8000]; else fprintf(stderr, "PRG ROG size is not 0x4000 nor 0x8000\n"), exit(1); - } else { + } else if ((addr >= 0x4000 && addr <= 0x4013) || addr == 0x4015 || addr == 0x4017) + return apu_read(addr); + else return memory[addr]; - } } static uint16_t @@ -68,6 +70,11 @@ peek16(uint16_t addr) static void memwrite(uint16_t addr, uint8_t byte) { + if (addr >= 0x4000 && addr <= 0x4017) { + apu_write(addr, byte); + return; + } + MEMORY_MIRROR(addr); memory[addr] = byte;