diff --git a/APU.cpp b/APU.cpp new file mode 100644 index 0000000..2a462dc --- /dev/null +++ b/APU.cpp @@ -0,0 +1,112 @@ +#include "APU.h" + +namespace APU +{ + #define apu_enabled (NR52&0x80) + #define DAC1_enabled ((NR12&0xf8)!=0) + + struct channel_t + { + bool enabled = false; + uint8_t length_timer = 0; + bool length_timer_expired = true; + bool length_enable = false; + }; + + channel_t channels[4]; + + uint8_t NR10 = 0; // 0xff10 - Sound channel 1 sweep + uint8_t NR11 = 0; // 0xff11 - Sound channel 1 length timer & duty cycle + uint8_t NR12 = 0; // 0xff12 - Sound channel 1 volume & envelope + uint8_t NR13 = 0; // 0xff13 - Sound channel 1 period low + uint8_t NR14 = 0; // 0xff14 - Sound channel 1 period high & control + // 0xff15 - Not used? + uint8_t NR21 = 0; // 0xff16 - Sound channel 2 length timer & duty cycle + uint8_t NR22 = 0; // 0xff17 - Sound channel 2 volume & envelope + uint8_t NR23 = 0; // 0xff18 - Sound channel 2 period low + uint8_t NR24 = 0; // 0xff19 - Sound channel 2 period high & control + uint8_t NR30 = 0; // 0xff1a - Sound channel 3 DAC enable + uint8_t NR31 = 0; // 0xff1b - Sound channel 3 length timer + uint8_t NR32 = 0; // 0xff1c - Sound channel 3 output level + uint8_t NR33 = 0; // 0xff1d - Sound channel 3 period low + uint8_t NR34 = 0; // 0xff1e - Sound channel 3 period high & control + // 0xff1f - Not used? + uint8_t NR41 = 0; // 0xff20 - Sound channel 4 length timer + uint8_t NR42 = 0; // 0xff21 - Sound channel 4 volume & envelope + uint8_t NR43 = 0; // 0xff22 - Sound channel 4 frequency & randomness + uint8_t NR44 = 0; // 0xff23 - Sound channel 4 control + uint8_t NR50 = 0; // 0xff24 - Master volume & VIN panning + uint8_t NR51 = 0; // 0xff25 - Sound panning + uint8_t NR52 = 0; // 0xff26 - Sound on/off + // 0xff27-0xff2f - Not used? + uint8_t WaveRAM[16]; // 0xff30-0xff3f + + void reset() + { + NR10 = 0; + NR11 = 0; + NR12 = 0; + NR13 = 0; + NR14 = 0; + NR21 = 0; + NR22 = 0; + NR23 = 0; + NR24 = 0; + NR30 = 0; + NR31 = 0; + NR32 = 0; + NR33 = 0; + NR34 = 0; + NR41 = 0; + NR42 = 0; + NR43 = 0; + NR44 = 0; + NR50 = 0; + NR51 = 0; + NR52 = 0; + } + + void trigger(uint8_t channel) + { + channels[channel].enabled = true; + // length timer expired is reset + // period divider is set to the contents of NR13 and NR14 + // envelope timer is reset + // volume is set to contents of NR12 initial volume + // sweep does several things (check documentation) + } + + uint8_t readRegister(uint16_t address) + { + switch(address) + { + case 0xff10: return NR10; break; + case 0xff11: return NR11 & 0xc0; break; + case 0xff12: return NR12; break; + case 0xff13: return 0x00; break; + case 0xff14: return NR14 & 0x40; break; + + case 0xff24: return NR50; break; + case 0xff25: return NR51; break; + case 0xff26: return NR52; break; + } + } + + void writeRegister(uint16_t address, uint8_t value) + { + if (!apu_enabled && (address!=0xff26)) return; + + switch(address) + { + case 0xff10: NR10 = value; break; + case 0xff11: NR11 = value; break; + case 0xff12: NR12 = value; break; + case 0xff13: NR13 = value; break; + case 0xff14: NR14 = value; if (value&0x80) trigger(1); break; + + case 0xff24: NR50 = value; break; + case 0xff25: NR51 = value; break; + case 0xff26: if (value&0x80) reset(); NR52 = (value&0x80) | (NR52 & 0x0f); break; + } + } +} \ No newline at end of file diff --git a/APU.h b/APU.h new file mode 100644 index 0000000..3a3dd54 --- /dev/null +++ b/APU.h @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace APU +{ + uint8_t readRegister(uint16_t address); + void writeRegister(uint16_t address, uint8_t value); +}