diff --git a/LR35902/inc/Audio.h b/LR35902/inc/Audio.h new file mode 100644 index 0000000..d9d983d --- /dev/null +++ b/LR35902/inc/Audio.h @@ -0,0 +1,310 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace EightBit { + namespace GameBoy { + + class Envelope { + public: + Envelope() {} + + enum Direction { Attenuate, Amplify }; + + int default() const { return m_defaultValue; } + void setDefault(int value) { m_defaultValue = value; } + + Direction direction() const { return (Direction)m_direction; } + void setDirection(int value) { m_direction = value; } + void setDirection(Direction value) { setDirection((int)value); } + + int stepLength() const { return m_stepLength; } + void setStepLength(int value) { m_stepLength = value; } + + void dump() const { + std::cout << "Envelope: default=" << default() << ",direction=" << direction() << ",step length=" << stepLength() << std::endl; + } + + private: + int m_defaultValue; + int m_direction; + int m_stepLength; + }; + + class Sweep { + public: + Sweep() {} + + enum Direction { Addition, Subtraction }; + + int time() const { return m_time; } + void setTime(int value) { m_time = value; } + + Direction direction() const { return (Direction)m_direction; } + void setDirection(int value) { m_direction = value; } + void setDirection(Direction value) { setDirection((int)value); } + + int shift() const { return m_shift; } + void setShift(int value) { m_shift = value; } + + void dump() const { + std::cout << "Sweep: time=" << time() << ",direction=" << direction() << ",shift=" << shift() << std::endl; + } + + private: + int m_time; + int m_direction; + int m_shift; + }; + + class AudioVoice { + public: + AudioVoice() {} + + enum Type { Continuous, Counter }; + + Type type() const { return (Type)m_counterContinuous; } + void setType(int value) { m_counterContinuous = value; } + void setType(Type value) { setType((int)value); } + + bool initialise() const { return !!m_initialise; } + void setInitialise(bool value) { m_initialise = value; } + + virtual void dump() const { + std::cout << "Audio Voice: type=" << type() << ",initialise=" << initialise() << std::endl; + } + + private: + int m_counterContinuous; + int m_initialise; + }; + + class WaveVoice : public AudioVoice { + public: + WaveVoice() {} + + int frequencyLowOrder() const { return m_frequencyLowOrder; } + + void setFrequencyLowOrder(int value) { + assert(value < Processor::Bit8); + m_frequencyLowOrder = value; + } + + int frequencyHighOrder() const { return m_frequencyHighOrder; } + + void setFrequencyHighOrder(int value) { + assert(value < Processor::Bit3); + m_frequencyHighOrder = value; + } + + int frequency() const { + return (m_frequencyHighOrder >> 8) | m_frequencyLowOrder; + } + + void setFrequency(int value) { + assert(value < Processor::Bit11); + m_frequencyHighOrder = (value >> 8) & Processor::Mask3; + m_frequencyLowOrder = value & Processor::Mask8; + } + + virtual void dump() const override { + AudioVoice::dump(); + std::cout << "Wave Voice: frequency=" << frequency() << std::endl; + } + + private: + int m_frequencyLowOrder; // 8 bits + int m_frequencyHighOrder; // 3 bits + }; + + class RectangularVoice : public WaveVoice { + public: + RectangularVoice() {} + + int waveFormDutyCycle() const { return m_waveFormDutyCycle; } + void setWaveFormDutyCycle(int value) { m_waveFormDutyCycle = value; } + + int length() const { return m_soundLength; } + void setLength(int value) { m_soundLength = value; } + + virtual void dump() const override { + WaveVoice::dump(); + std::cout << "Rectangular Voice: wave form duty=" << waveFormDutyCycle() << ",length=" << length() << std::endl; + } + + private: + int m_waveFormDutyCycle; + int m_soundLength; + }; + + // NR2X + class EnvelopedRectangularVoice : public RectangularVoice { + public: + EnvelopedRectangularVoice() {} + + Envelope& envelope() { return m_envelope; } + + virtual void dump() const override { + RectangularVoice::dump(); + m_envelope.dump(); + } + + private: + Envelope m_envelope; + }; + + // NR1X + class SweptEnvelopedRectangularVoice : public EnvelopedRectangularVoice { + public: + SweptEnvelopedRectangularVoice() {} + + Sweep& sweep() { return m_sweep; } + + virtual void dump() const override { + EnvelopedRectangularVoice::dump(); + m_sweep.dump(); + } + + private: + Sweep m_sweep; + }; + + // NR3X + class UserDefinedWaveVoice : public WaveVoice { + public: + UserDefinedWaveVoice() {} + + bool enabled() const { return !!m_enabled; } + void setEnabled(bool value) { m_enabled = value; } + + int length() const { return m_soundLength; } + void setLength(int value) { m_soundLength = value; } + + int level() const { return m_outputLevel; } + void setLevel(int value) { m_outputLevel = value; } + + int packedWaveDatum(int i) const { + assert(i < 16); + return m_waveData[i]; + } + + void setPackedWaveDatum(int i, uint8_t value) { + assert(i < 16); + m_waveData[i] = value; + } + + int waveDatum(int i) const { + assert(i < 32); + const auto packed = packedWaveDatum(i >> 1); + return i & 1 ? Processor::lowNibble(packed) : Processor::highNibble(packed); + } + + private: + int m_enabled; + int m_soundLength; + int m_outputLevel; + std::array m_waveData; + }; + + // NR4X + class WhiteNoiseWaveVoice : public AudioVoice { + public: + WhiteNoiseWaveVoice() {} + + Envelope& envelope() { return m_envelope; } + + int length() const { return m_soundLength; } + void setLength(int value) { m_soundLength = value; } + + int polynomialShiftClockFrequency() const { return m_polynomialShiftClockFrequency; } + void setPolynomialShiftClockFrequency(int value) { m_polynomialShiftClockFrequency = value; } + + int polynomialCounterSteps() const { return m_polynomialCounterSteps; } + void setPolynomialCounterSteps(int value) { m_polynomialCounterSteps = value; } + + int frequencyDivisionRatio() const { return m_frequencyDivisionRatio; } + void setFrequencyDivisionRatio(int value) { m_frequencyDivisionRatio = value; } + + private: + Envelope m_envelope; + int m_soundLength; + int m_polynomialShiftClockFrequency; + int m_polynomialCounterSteps; + int m_frequencyDivisionRatio; + }; + + class OutputChannel { + public: + OutputChannel() {} + + bool vin() const { return m_vin; } + void setVin(bool value) { m_vin = value; } + + int outputLevel() const { return m_outputLevel; } + void setOutputLevel(int value) { m_outputLevel = value; } + + bool& outputVoice(int voice) { return m_outputVoice[voice]; } + bool& outputVoice1() { return m_outputVoice[0]; } + bool& outputVoice2() { return m_outputVoice[1]; } + bool& outputVoice3() { return m_outputVoice[2]; } + bool& outputVoice4() { return m_outputVoice[3]; } + + private: + bool m_vin; + int m_outputLevel; + std::array m_outputVoice; + }; + + class Audio { + public: + Audio() { + m_voices[0] = std::make_shared(); + m_voices[1] = std::make_shared(); + m_voices[2] = std::make_shared(); + m_voices[3] = std::make_shared(); + } + + std::shared_ptr voice(int i) { return m_voices[i]; } + + SweptEnvelopedRectangularVoice* voice1() { + return (SweptEnvelopedRectangularVoice*)voice(0).get(); + } + + EnvelopedRectangularVoice* voice2() { + return (EnvelopedRectangularVoice*)voice(1).get(); + } + + UserDefinedWaveVoice* voice3() { + return (UserDefinedWaveVoice*)voice(2).get(); + } + + WhiteNoiseWaveVoice* voice4() { + return (WhiteNoiseWaveVoice*)voice(3).get(); + } + + OutputChannel& channel(int i) { return m_channels[i]; } + OutputChannel& channel1() { return channel(0); } + OutputChannel& channel2() { return channel(1); } + + bool enabled() const { return m_enabled; } + void setEnabled(bool value) { m_enabled = value; } + + void dump() const { + for (int i = 0; i < 4; ++i) { + std::cout << "** Voice " << i + 1 << std::endl; + m_voices[i]->dump(); + } + } + + private: + std::array, 4> m_voices; + std::array m_channels; + bool m_enabled; + }; + } +} \ No newline at end of file diff --git a/LR35902/inc/GameBoyBus.h b/LR35902/inc/GameBoyBus.h index c1eaaa2..e983c81 100644 --- a/LR35902/inc/GameBoyBus.h +++ b/LR35902/inc/GameBoyBus.h @@ -9,6 +9,8 @@ #include #include +#include "Audio.h" + namespace EightBit { namespace GameBoy { class Bus : public EightBit::Bus { @@ -118,29 +120,7 @@ namespace EightBit { Bus(); - Signal> Audio_SweepTimeModified; - Signal> Audio_SweepDirectionModified; - Signal> Audio_SweepShiftModified; - Signal> Audio_WavePatternDutyModified; - Signal> Audio_SoundLengthModified; - Signal> Audio_DefaultEnvelopeVolumeModified; - Signal> Audio_EnvelopeDirectionModified; - Signal> Audio_EnvelopeStepLengthModified; - Signal> Audio_FrequencyLoModified; - Signal> Audio_InitialiseModified; - Signal> Audio_CounterContinuousSelectionModified; - Signal> Audio_FrequencyHiModified; - Signal> Audio_SoundOnOffModified; - Signal> Audio_OutputLevelModified; - Signal> Audio_PolynomialShiftClockFrequencyModified; - Signal> Audio_PolynomialCounterStepModified; - Signal> Audio_FrequencyDivisionRatioModified; - Signal> Audio_SO_VinOnOffModified; - Signal> Audio_SO_OutputLevelModified; - Signal> Audio_SO_SoundOutputModified; - Signal Audio_AllSoundOnOffModified; - Signal> Audio_ChannelOnOffModified; - + Audio& audio() { return m_audio; } void reset(); @@ -337,6 +317,8 @@ namespace EightBit { bool m_p11; // left/b bool m_p10; // right/a + Audio m_audio; + void checkTimer(int cycles); void validateCartridgeType(); diff --git a/LR35902/src/GameBoyBus.cpp b/LR35902/src/GameBoyBus.cpp index 164626e..5800d7e 100644 --- a/LR35902/src/GameBoyBus.cpp +++ b/LR35902/src/GameBoyBus.cpp @@ -246,120 +246,116 @@ void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) { break; case BASE + NR10: // Sound mode 1 register: Sweep - Audio_SweepTimeModified.fire(std::make_tuple(1, (value >> 4) & Processor::Mask3)); // Bits 4-6 - Audio_SweepDirectionModified.fire(std::make_tuple(1, (value >> 3) & Processor::Mask1)); // Bits 3 - Audio_SweepShiftModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2 + audio().voice1()->sweep().setTime((value >> 4) & Processor::Mask3); // Bits 4-6 + audio().voice1()->sweep().setDirection((value >> 3) & Processor::Mask1); // Bit 3 + audio().voice1()->sweep().setShift(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR11: // Sound mode 1 register: Sound length / Wave pattern duty - Audio_WavePatternDutyModified.fire(std::make_tuple(1, (value >> 6) & Processor::Mask2));// Bits 6-7 - Audio_SoundLengthModified.fire(std::make_tuple(1, value & Processor::Mask6)); // Bits 0-5 + audio().voice1()->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7 + audio().voice1()->setLength(value & Processor::Mask6); // Bits 0-5 break; case BASE + NR12: // Sound mode 1 register: Envelope - Audio_DefaultEnvelopeVolumeModified.fire(std::make_tuple(1, (value >> 4) & Processor::Mask4)); // Bits 4-7 - Audio_EnvelopeDirectionModified.fire(std::make_tuple(1, (value >> 3) & Processor::Mask1)); // Bits 3 - Audio_EnvelopeStepLengthModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2 + audio().voice1()->envelope().setDefault((value >> 4) & Processor::Mask4); // Bits 4-7 + audio().voice1()->envelope().setDirection((value >> 3) & Processor::Mask1); // Bit 3 + audio().voice1()->envelope().setStepLength(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR13: // Sound mode 1 register: Frequency lo - Audio_FrequencyLoModified.fire(std::make_tuple(1, value)); + audio().voice1()->setFrequencyLowOrder(value); break; case BASE + NR14: // Sound mode 1 register: Frequency hi - Audio_InitialiseModified.fire(std::make_tuple(1, (value >> 7) & Processor::Mask1)); // Bits 7 - Audio_CounterContinuousSelectionModified.fire(std::make_tuple(1, (value >> 6) & Processor::Mask1));// Bits 6 - Audio_FrequencyHiModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2 + audio().voice1()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7 + audio().voice1()->setType((value >> 6) & Processor::Mask1); // Bits 6 + audio().voice1()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR21: // Sound mode 2 register: Sound length / Wave pattern duty - Audio_WavePatternDutyModified.fire(std::make_tuple(2, (value >> 6) & Processor::Mask2));// Bits 6-7 - Audio_SoundLengthModified.fire(std::make_tuple(2, Processor::Mask6)); // Bits 0-5 + audio().voice2()->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7 + audio().voice2()->setLength(value & Processor::Mask6); // Bits 0-5 break; case BASE + NR22: // Sound mode 2 register: Envelope - Audio_DefaultEnvelopeVolumeModified.fire(std::make_tuple(2, (value >> 4) & Processor::Mask4)); // Bits 4-7 - Audio_EnvelopeDirectionModified.fire(std::make_tuple(2, (value >> 3) & Processor::Mask1)); // Bits 3 - Audio_EnvelopeStepLengthModified.fire(std::make_tuple(2, value & Processor::Mask3)); // Bits 0-2 + audio().voice2()->envelope().setDefault((value >> 4) & Processor::Mask4); // Bits 4-7 + audio().voice2()->envelope().setDirection((value >> 3) & Processor::Mask1); // Bit 3 + audio().voice2()->envelope().setStepLength(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR23: // Sound mode 2 register: Frequency lo - Audio_FrequencyLoModified.fire(std::make_tuple(2, value)); + audio().voice2()->setFrequencyLowOrder(value); break; case BASE + NR24: // Sound mode 2 register: Frequency hi - Audio_InitialiseModified.fire(std::make_tuple(2, (value >> 7) & Processor::Mask1)); // Bits 7 - Audio_CounterContinuousSelectionModified.fire(std::make_tuple(2, (value >> 6) & Processor::Mask1));// Bits 6 - Audio_FrequencyHiModified.fire(std::make_tuple(2, value & Processor::Mask3)); // Bits 0-2 + audio().voice2()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7 + audio().voice2()->setType((value >> 6) & Processor::Mask1); // Bit 6 + audio().voice2()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR30: // Sound mode 3 register: Sound on/off - Audio_SoundOnOffModified.fire(std::make_tuple(3, (value >> 7) & Processor::Mask1)); // Bits 0-2 + audio().voice3()->setEnabled((value >> 7) & Processor::Mask1); // Bit 7 break; case BASE + NR31: // Sound mode 3 register: Sound length - Audio_SoundLengthModified.fire(std::make_tuple(3, value)); + audio().voice3()->setLength(value); break; case BASE + NR32: // Sound mode 3 register: Select output level - Audio_OutputLevelModified.fire(std::make_tuple(3, value)); + audio().voice3()->setLevel(value); break; case BASE + NR33: // Sound mode 3 register: Frequency lo - Audio_FrequencyLoModified.fire(std::make_tuple(3, value)); + audio().voice3()->setFrequencyLowOrder(value); break; case BASE + NR34: // Sound mode 3 register: Frequency hi - Audio_InitialiseModified.fire(std::make_tuple(3, (value >> 7) & Processor::Mask1)); // Bits 7 - Audio_CounterContinuousSelectionModified.fire(std::make_tuple(3, (value >> 6) & Processor::Mask1));// Bits 6 - Audio_FrequencyHiModified.fire(std::make_tuple(3, value & Processor::Mask3)); // Bits 0-2 + audio().voice3()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7 + audio().voice3()->setType((value >> 6) & Processor::Mask1); // Bits 6 + audio().voice3()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR41: // Sound mode 4 register: Sound length - Audio_SoundLengthModified.fire(std::make_tuple(4, value & Processor::Mask6)); // Bits 0-5 + audio().voice4()->setLength(value & Processor::Mask6); // Bits 0-5 break; case BASE + NR42: // Sound mode 4 register: Envelope - Audio_DefaultEnvelopeVolumeModified.fire(std::make_tuple(4, (value >> 4) & Processor::Mask4)); // Bits 4-7 - Audio_EnvelopeDirectionModified.fire(std::make_tuple(4, (value >> 3) & Processor::Mask1)); // Bits 3 - Audio_EnvelopeStepLengthModified.fire(std::make_tuple(4, value & Processor::Mask3)); // Bits 0-2 + audio().voice4()->envelope().setDefault((value >> 4) & Processor::Mask4); // Bits 4-7 + audio().voice4()->envelope().setDirection((value >> 3) & Processor::Mask1); // Bit 3 + audio().voice4()->envelope().setStepLength(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR43: // Sound mode 4 register: Polynomial counter - Audio_PolynomialShiftClockFrequencyModified.fire(std::make_tuple(4, (value >> 4) & Processor::Mask4)); // Bits 4-7 - Audio_PolynomialCounterStepModified.fire(std::make_tuple(4, (value >> 3) & Processor::Mask1)); // Bit 3 - Audio_FrequencyDivisionRatioModified.fire(std::make_tuple(4, value & Processor::Mask3)); // Bits 0-2 + audio().voice4()->setPolynomialShiftClockFrequency((value >> 4) & Processor::Mask4); // Bits 4-7 + audio().voice4()->setPolynomialCounterSteps((value >> 3) & Processor::Mask1); // Bit 3 + audio().voice4()->setFrequencyDivisionRatio(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR44: // Sound mode 4 register: counter/consecutive; inital - Audio_InitialiseModified.fire(std::make_tuple(4, (value >> 7) & Processor::Mask1)); // Bit 7 - Audio_CounterContinuousSelectionModified.fire(std::make_tuple(4, (value >> 6) & Processor::Mask1)); // Bits 6 + audio().voice4()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7 + audio().voice4()->setType((value >> 6) & Processor::Mask1); // Bit 6 break; - case BASE + NR50: // Channel control/on-off/volume - Audio_SO_VinOnOffModified.fire(std::make_tuple(2, (value >> 7) & Processor::Mask1)); // Bit 7 - Audio_SO_OutputLevelModified.fire(std::make_tuple(2, (value >> 4) & Processor::Mask3)); // Bits 4-6 - Audio_SO_VinOnOffModified.fire(std::make_tuple(1, (value >> 3) & Processor::Mask1)); // Bit 3 - Audio_SO_OutputLevelModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2 + case BASE + NR50: // Channel control: on-off/volume + audio().channel2().setVin((value >> 7) & Processor::Mask1); // Bit 7 + audio().channel2().setOutputLevel((value >> 4) & Processor::Mask3); // Bits 4-6 + audio().channel1().setVin((value >> 3) & Processor::Mask1); // Bit 3 + audio().channel1().setOutputLevel(value & Processor::Mask3); // Bits 0-2 break; case BASE + NR51: - Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 4, (value >> 7) & Processor::Mask1)); // Bit 7 - Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 3, (value >> 6) & Processor::Mask1)); // Bit 6 - Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 2, (value >> 5) & Processor::Mask1)); // Bit 5 - Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 1, (value >> 4) & Processor::Mask1)); // Bit 4 - Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 4, (value >> 3) & Processor::Mask1)); // Bit 3 - Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 3, (value >> 2) & Processor::Mask1)); // Bit 2 - Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 2, (value >> 1) & Processor::Mask1)); // Bit 1 - Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 1, value & Processor::Mask1)); // Bit 0 + audio().channel2().outputVoice4() = (value >> 7) & Processor::Mask1; // Bit 7 + audio().channel2().outputVoice3() = (value >> 6) & Processor::Mask1; // Bit 6 + audio().channel2().outputVoice2() = (value >> 5) & Processor::Mask1; // Bit 5 + audio().channel2().outputVoice1() = (value >> 4) & Processor::Mask1; // Bit 4 + audio().channel1().outputVoice4() = (value >> 3) & Processor::Mask1; // Bit 3 + audio().channel1().outputVoice3() = (value >> 2) & Processor::Mask1; // Bit 2 + audio().channel1().outputVoice2() = (value >> 1) & Processor::Mask1; // Bit 1 + audio().channel1().outputVoice1() = value & Processor::Mask1; // Bit 0 break; case BASE + NR52: // Sound on/off - Audio_AllSoundOnOffModified.fire((value >> 7) & Processor::Mask1); // Bit 7 - Audio_ChannelOnOffModified.fire(std::make_tuple(4, (value >> 3) & Processor::Mask1)); // Bit 3 - Audio_ChannelOnOffModified.fire(std::make_tuple(3, (value >> 2) & Processor::Mask1)); // Bit 2 - Audio_ChannelOnOffModified.fire(std::make_tuple(2, (value >> 1) & Processor::Mask1)); // Bit 1 - Audio_ChannelOnOffModified.fire(std::make_tuple(1, value & Processor::Mask1)); // Bit 0 + audio().setEnabled((value >> 7) & Processor::Mask1); // Bit 7 break; case BASE + LCDC: diff --git a/LR35902/src/LR35902.vcxproj b/LR35902/src/LR35902.vcxproj index 141fb76..1838a19 100644 --- a/LR35902/src/LR35902.vcxproj +++ b/LR35902/src/LR35902.vcxproj @@ -140,6 +140,7 @@ + diff --git a/LR35902/src/LR35902.vcxproj.filters b/LR35902/src/LR35902.vcxproj.filters index ed7430e..200a16b 100644 --- a/LR35902/src/LR35902.vcxproj.filters +++ b/LR35902/src/LR35902.vcxproj.filters @@ -38,6 +38,9 @@ Header Files + + Header Files + diff --git a/LR35902/src/stdafx.h b/LR35902/src/stdafx.h index 831f62d..83e44c1 100644 --- a/LR35902/src/stdafx.h +++ b/LR35902/src/stdafx.h @@ -10,8 +10,11 @@ #include #include #include +#include +#include #include +#include #include #include