Prepare the LR35902 to allow use of the Blargg gb sound library.

Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
This commit is contained in:
Adrian.Conlon 2017-09-23 22:56:11 +01:00
parent 053d649124
commit dbe5f7f8e5
9 changed files with 11 additions and 1417 deletions

View File

@ -1,388 +0,0 @@
#pragma once
#include <array>
#include <memory>
#include <iostream>
#include <cassert>
#include "AudioFrame.h"
namespace EightBit {
namespace GameBoy {
class Envelope final {
public:
Envelope();
enum Direction { Attenuate, Amplify };
void reset();
bool zeroed() const;
int volume() const;
void setVolume(int value);
Direction direction() const;
void setDirection(int value);
void setDirection(Direction value);
int period() const;
void setPeriod(int value);
void step();
private:
int m_volume;
int m_direction;
int m_period;
int m_position;
};
class Sweep final {
public:
Sweep();
enum Direction { Addition, Subtraction };
void reset();
bool zeroed() const;
int time() const;
void setTime(int value);
Direction direction() const;
void setDirection(int value);
void setDirection(Direction value);
int shift() const;
void setShift(int value);
private:
int m_time;
int m_direction;
int m_shift;
};
class AudioVoice {
public:
AudioVoice();
virtual void reset();
virtual bool zeroed() const;
enum Type { Continuous, Counter };
Type type() const;
void setType(int value);
void setType(Type value);
bool initialise() const;
void setInitialise(bool value);
private:
int m_counterContinuous;
int m_initialise;
};
class WaveVoice : public AudioVoice {
public:
WaveVoice(int cyclesPerSecond);
virtual void reset() override;
virtual bool zeroed() const override;
int frequencyLowOrder() const;
void setFrequencyLowOrder(int value);
int frequencyHighOrder() const;
void setFrequencyHighOrder(int value);
int frequency() const;
void setFrequency(int value);
int hertz() const;
private:
const int m_cyclesPerSecond;
int m_frequencyLowOrder; // 8 bits
int m_frequencyHighOrder; // 3 bits
};
class RectangularVoice : public WaveVoice {
public:
RectangularVoice(int cyclesPerSecond);
virtual void reset() override;
virtual bool zeroed() const override;
int waveFormDutyCycle() const;
void setWaveFormDutyCycle(int value);
int length() const;
void setLength(int value);
private:
int m_waveFormDutyCycle;
int m_soundLength;
};
// NR2X
class EnvelopedRectangularVoice : public RectangularVoice {
public:
EnvelopedRectangularVoice(int cyclesPerSecond);
virtual void reset() override;
virtual bool zeroed() const override;
Envelope& envelope();
private:
Envelope m_envelope;
};
// NR1X
class SweptEnvelopedRectangularVoice : public EnvelopedRectangularVoice {
public:
SweptEnvelopedRectangularVoice(int cyclesPerSecond);
virtual void reset() override;
virtual bool zeroed() const override;
Sweep& sweep();
private:
Sweep m_sweep;
};
// NR3X
class UserDefinedWaveVoice : public WaveVoice {
public:
UserDefinedWaveVoice(int cyclesPerSecond);
virtual void reset() override;
virtual bool zeroed() const override;
bool enabled() const;
void setEnabled(bool value);
int length() const;
void setLength(int value);
int level() const;
void setLevel(int value);
int packedWaveDatum(int i) const;
void setPackedWaveDatum(int i, uint8_t value);
int waveDatum(int i) const;
private:
int m_enabled;
int m_soundLength;
int m_outputLevel;
std::array<uint8_t, 16> m_waveData;
};
// NR4X
class WhiteNoiseWaveVoice : public AudioVoice {
public:
WhiteNoiseWaveVoice();
virtual void reset() override;
virtual bool zeroed() const override;
Envelope& envelope();
int length() const;
void setLength(int value);
int polynomialShiftClockFrequency() const;
void setPolynomialShiftClockFrequency(int value);
int polynomialCounterSteps() const;
void setPolynomialCounterSteps(int value);
int frequencyDivisionRatio() const;
void setFrequencyDivisionRatio(int value);
private:
Envelope m_envelope;
int m_soundLength;
int m_polynomialShiftClockFrequency;
int m_polynomialCounterSteps;
int m_frequencyDivisionRatio;
};
class OutputChannel final {
public:
OutputChannel();
void reset();
bool zeroed() const;
bool vin() const;
void setVin(bool value);
int outputLevel() const;
void setOutputLevel(int value);
bool& outputVoice(int voice);
bool& outputVoice1();
bool& outputVoice2();
bool& outputVoice3();
bool& outputVoice4();
private:
bool m_vin;
int m_outputLevel;
std::array<bool, 4> m_outputVoices;
};
class Audio final {
public:
Audio(int cyclesPerSecond);
std::shared_ptr<AudioVoice> voice(int i);
SweptEnvelopedRectangularVoice* voice1();
EnvelopedRectangularVoice* voice2();
UserDefinedWaveVoice* voice3();
WhiteNoiseWaveVoice* voice4();
OutputChannel& channel(int i);
OutputChannel& channel1();
OutputChannel& channel2();
bool enabled() const;
void setEnabled(bool value);
void reset();
bool zeroed() const;
//
bool voice1On() const;
bool voice2On() const;
bool voice3On() const;
bool voice4On() const;
//
uint8_t toNRx1(int i);
void fromNRx1(int i, uint8_t value);
uint8_t toNRx2(int i);
void fromNRx2(int i, uint8_t value);
uint8_t toNRx3(int i);
void fromNRx3(int i, uint8_t value);
// Sound mode 1 register: Sweep
uint8_t toNR10();
void fromNR10(uint8_t value);
// Sound mode 1 register: Sound length / Wave pattern duty
uint8_t toNR11();
void fromNR11(uint8_t value);
// Sound mode 1 register: Envelope
uint8_t toNR12();
void fromNR12(uint8_t value);
// Sound mode 1 register: Frequency lo
uint8_t toNR13();
void fromNR13(uint8_t value);
// Sound mode 1 register: Frequency hi
uint8_t toNR14();
void fromNR14(uint8_t value);
// Sound mode 2 register: Sound length / Wave pattern duty
uint8_t toNR21();
void fromNR21(uint8_t value);
// Sound mode 2 register: Envelope
uint8_t toNR22();
void fromNR22(uint8_t value);
// Sound mode 2 register: Frequency lo
uint8_t toNR23();
void fromNR23(uint8_t value);
// Sound mode 2 register: Frequency hi
uint8_t toNR24();
void fromNR24(uint8_t value);
// Sound mode 3 register: Sound on/off
uint8_t toNR30();
void fromNR30(uint8_t value);
// Sound mode 3 register: Sound length
uint8_t toNR31();
void fromNR31(uint8_t value);
// Sound mode 3 register: Select output level
uint8_t toNR32();
void fromNR32(uint8_t value);
// Sound mode 3 register: Frequency lo
uint8_t toNR33();
void fromNR33(uint8_t value);
// Sound mode 3 register: Frequency hi
uint8_t toNR34();
void fromNR34(uint8_t value);
// Sound mode 4 register: Sound length
uint8_t toNR41();
void fromNR41(uint8_t value);
// Sound mode 4 register: Envelope
uint8_t toNR42();
void fromNR42(uint8_t value);
// Sound mode 4 register: Polynomial counter
uint8_t toNR43();
void fromNR43(uint8_t value);
// Sound mode 4 register: counter/consecutive; inital
uint8_t toNR44();
void fromNR44(uint8_t value);
// Channel control: on-off/volume
uint8_t toNR50();
void fromNR50(uint8_t value);
// Selection of Sound output terminal
uint8_t toNR51();
void fromNR51(uint8_t value);
// Sound on/off
uint8_t toNR52();
void fromNR52(uint8_t value);
void setPackedWaveDatum(int i, uint8_t value);
uint8_t packedWaveDatum(int i);
void stepFrame(int cycles);
void Sequencer_FrameStep(int step);
void Sequencer_LengthStep(int step);
void Sequencer_VolumeStep(int step);
void Sequencer_SweepStep(int step);
private:
AudioFrame m_frameSequencer;
std::array<std::shared_ptr<AudioVoice>, 4> m_voices;
std::array<OutputChannel, 2> m_channels;
bool m_enabled;
template<class T>
static void stepLength(T* voice) {
if (voice->length() > 0) {
auto current = voice->length();
voice->setLength(--current);
}
}
};
}
}

View File

@ -1,47 +0,0 @@
#pragma once
#include <Signal.h>
namespace EightBit {
namespace GameBoy {
class AudioFrame final {
private:
enum { StepCycle = 7, Frequency = 512 };
int m_cyclesPerTick;
int m_currentStep;
int m_currentCycles;
public:
AudioFrame(int cyclesPerSecond)
: m_cyclesPerTick(cyclesPerSecond / Frequency),
m_currentStep(0),
m_currentCycles(m_cyclesPerTick) {
}
Signal<int> FrameStep;
Signal<int> LengthStep;
Signal<int> VolumeStep;
Signal<int> SweepStep;
void step() {
m_currentStep = (m_currentStep + 1) % StepCycle;
FrameStep.fire(m_currentStep);
if ((m_currentStep % 2) == 0)
LengthStep.fire(m_currentStep);
if (m_currentStep == 7)
VolumeStep.fire(m_currentStep);
if ((m_currentStep == 2) || (m_currentStep == 6))
SweepStep.fire(m_currentStep);
}
void step(int cycles) {
m_currentCycles -= cycles;
if (m_currentCycles < 0) {
step();
m_currentCycles += m_cyclesPerTick;
}
}
};
}
}

View File

@ -9,8 +9,6 @@
#include <Processor.h>
#include <Signal.h>
#include "Audio.h"
namespace EightBit {
namespace GameBoy {
class Bus : public EightBit::Bus {
@ -124,8 +122,6 @@ namespace EightBit {
Bus();
Audio& audio() { return m_audio; }
void reset();
void triggerInterrupt(int cause) {
@ -321,8 +317,6 @@ namespace EightBit {
bool m_p11; // left/b
bool m_p10; // right/a
Audio m_audio;
void checkTimer(int cycles);
void validateCartridgeType();

View File

@ -22,6 +22,11 @@ namespace EightBit {
LR35902(Bus& memory);
Signal<LR35902> ExecutingInstruction;
Signal<LR35902> ExecutedInstruction;
int clockCycles() const {
return cycles * 4;
}
virtual register16_t& AF() override {
af.low &= 0xf0;

View File

@ -1,800 +0,0 @@
#include "stdafx.h"
#include "Audio.h"
//
EightBit::GameBoy::Envelope::Envelope() {}
void EightBit::GameBoy::Envelope::reset() {
m_position = m_volume = m_direction = m_period = 0;
}
bool EightBit::GameBoy::Envelope::zeroed() const {
return (volume() == 0) && (period() == 0) && (direction() == Attenuate);
}
int EightBit::GameBoy::Envelope::volume() const {
return m_volume;
}
void EightBit::GameBoy::Envelope::setVolume(int value) {
m_volume = value;
}
EightBit::GameBoy::Envelope::Direction EightBit::GameBoy::Envelope::direction() const {
return (Direction)m_direction;
}
void EightBit::GameBoy::Envelope::setDirection(int value) {
m_direction = value;
}
void EightBit::GameBoy::Envelope::setDirection(Direction value) {
setDirection((int)value);
}
int EightBit::GameBoy::Envelope::period() const {
return m_period;
}
void EightBit::GameBoy::Envelope::setPeriod(int value) {
m_position = m_period = value;
}
void EightBit::GameBoy::Envelope::step() {
if (m_period != 0) {
if (--m_position == 0) {
auto volume = m_volume + (m_direction == Amplify ? +1 : -1);
if (volume >= 0 && volume <= 15)
m_volume = volume;
m_position = m_period;
}
}
}
//
EightBit::GameBoy::Sweep::Sweep() {}
void EightBit::GameBoy::Sweep::reset() {
m_time = m_direction = m_shift = 0;
}
bool EightBit::GameBoy::Sweep::zeroed() const {
return (time() == 0) && (shift() == 0) && (direction() == Addition);
}
int EightBit::GameBoy::Sweep::time() const {
return m_time;
}
void EightBit::GameBoy::Sweep::setTime(int value) {
m_time = value;
}
EightBit::GameBoy::Sweep::Direction EightBit::GameBoy::Sweep::direction() const {
return (Direction)m_direction;
}
void EightBit::GameBoy::Sweep::setDirection(int value) {
m_direction = value;
}
void EightBit::GameBoy::Sweep::setDirection(Direction value) {
setDirection((int)value);
}
int EightBit::GameBoy::Sweep::shift() const {
return m_shift;
}
void EightBit::GameBoy::Sweep::setShift(int value) {
m_shift = value;
}
//
EightBit::GameBoy::AudioVoice::AudioVoice() {}
void EightBit::GameBoy::AudioVoice::reset() {
m_counterContinuous = m_initialise = 0;
}
bool EightBit::GameBoy::AudioVoice::zeroed() const {
return !initialise() && (type() == Continuous);
}
EightBit::GameBoy::AudioVoice::Type EightBit::GameBoy::AudioVoice::type() const {
return (Type)m_counterContinuous;
}
void EightBit::GameBoy::AudioVoice::setType(int value) {
m_counterContinuous = value;
}
void EightBit::GameBoy::AudioVoice::setType(Type value) {
setType((int)value);
}
bool EightBit::GameBoy::AudioVoice::initialise() const {
return !!m_initialise;
}
void EightBit::GameBoy::AudioVoice::setInitialise(bool value) {
m_initialise = value;
}
//
EightBit::GameBoy::WaveVoice::WaveVoice(int cyclesPerSecond)
: m_cyclesPerSecond(cyclesPerSecond) {}
void EightBit::GameBoy::WaveVoice::reset() {
AudioVoice::reset();
m_frequencyLowOrder = m_frequencyHighOrder = 0;
}
bool EightBit::GameBoy::WaveVoice::zeroed() const {
return AudioVoice::zeroed() && (frequency() == 0);
}
int EightBit::GameBoy::WaveVoice::frequencyLowOrder() const {
return m_frequencyLowOrder;
}
void EightBit::GameBoy::WaveVoice::setFrequencyLowOrder(int value) {
assert(value < Processor::Bit8);
m_frequencyLowOrder = value;
}
int EightBit::GameBoy::WaveVoice::frequencyHighOrder() const {
return m_frequencyHighOrder;
}
void EightBit::GameBoy::WaveVoice::setFrequencyHighOrder(int value) {
assert(value < Processor::Bit3);
m_frequencyHighOrder = value;
}
int EightBit::GameBoy::WaveVoice::frequency() const {
return (m_frequencyHighOrder << 8) | m_frequencyLowOrder;
}
int EightBit::GameBoy::WaveVoice::hertz() const {
// f = 4194304 / (4 x 8 x (2048 - X)) Hz
auto division = 4 * 8 * (2048 - frequency());
return m_cyclesPerSecond / division;
}
void EightBit::GameBoy::WaveVoice::setFrequency(int value) {
assert(value < Processor::Bit11);
m_frequencyHighOrder = (value >> 8) & Processor::Mask3;
m_frequencyLowOrder = value & Processor::Mask8;
}
//
EightBit::GameBoy::RectangularVoice::RectangularVoice(int cyclesPerSecond)
: WaveVoice(cyclesPerSecond) {}
void EightBit::GameBoy::RectangularVoice::reset() {
WaveVoice::reset();
m_waveFormDutyCycle = m_soundLength = 0;
}
bool EightBit::GameBoy::RectangularVoice::zeroed() const {
return WaveVoice::zeroed() && (waveFormDutyCycle() == 0) && (length() == 0);
}
int EightBit::GameBoy::RectangularVoice::waveFormDutyCycle() const {
return m_waveFormDutyCycle;
}
void EightBit::GameBoy::RectangularVoice::setWaveFormDutyCycle(int value) {
m_waveFormDutyCycle = value;
}
int EightBit::GameBoy::RectangularVoice::length() const {
return m_soundLength;
}
void EightBit::GameBoy::RectangularVoice::setLength(int value) {
m_soundLength = value;
}
//
EightBit::GameBoy::EnvelopedRectangularVoice::EnvelopedRectangularVoice(int cyclesPerSecond)
: RectangularVoice(cyclesPerSecond) {}
void EightBit::GameBoy::EnvelopedRectangularVoice::reset() {
RectangularVoice::reset();
m_envelope.reset();
}
bool EightBit::GameBoy::EnvelopedRectangularVoice::zeroed() const {
return RectangularVoice::zeroed() && m_envelope.zeroed();
}
EightBit::GameBoy::Envelope& EightBit::GameBoy::EnvelopedRectangularVoice::envelope() {
return m_envelope;
}
//
EightBit::GameBoy::SweptEnvelopedRectangularVoice::SweptEnvelopedRectangularVoice(int cyclesPerSecond)
: EnvelopedRectangularVoice(cyclesPerSecond) {}
void EightBit::GameBoy::SweptEnvelopedRectangularVoice::reset() {
EnvelopedRectangularVoice::reset();
m_sweep.reset();
}
bool EightBit::GameBoy::SweptEnvelopedRectangularVoice::zeroed() const {
return EnvelopedRectangularVoice::zeroed() && m_sweep.zeroed();
}
EightBit::GameBoy::Sweep& EightBit::GameBoy::SweptEnvelopedRectangularVoice::sweep() {
return m_sweep;
}
//
EightBit::GameBoy::UserDefinedWaveVoice::UserDefinedWaveVoice(int cyclesPerSecond)
: WaveVoice(cyclesPerSecond) {}
void EightBit::GameBoy::UserDefinedWaveVoice::reset() {
WaveVoice::reset();
m_enabled = m_soundLength = m_outputLevel = 0;
for (auto& datum : m_waveData)
datum = 0;
}
bool EightBit::GameBoy::UserDefinedWaveVoice::zeroed() const {
bool dataZeroed = true;
for (const auto& datum : m_waveData) {
if (datum != 0) {
dataZeroed = false;
break;
}
}
return WaveVoice::zeroed() && dataZeroed && !enabled() && (length() == 0) && (level() == 0);
}
bool EightBit::GameBoy::UserDefinedWaveVoice::enabled() const {
return !!m_enabled;
}
void EightBit::GameBoy::UserDefinedWaveVoice::setEnabled(bool value) {
m_enabled = value;
}
int EightBit::GameBoy::UserDefinedWaveVoice::length() const {
return m_soundLength;
}
void EightBit::GameBoy::UserDefinedWaveVoice::setLength(int value) {
m_soundLength = value;
}
int EightBit::GameBoy::UserDefinedWaveVoice::level() const {
return m_outputLevel;
}
void EightBit::GameBoy::UserDefinedWaveVoice::setLevel(int value) {
m_outputLevel = value;
}
int EightBit::GameBoy::UserDefinedWaveVoice::packedWaveDatum(int i) const {
assert(i < 16);
return m_waveData[i];
}
void EightBit::GameBoy::UserDefinedWaveVoice::setPackedWaveDatum(int i, uint8_t value) {
assert(i < 16);
m_waveData[i] = value;
}
int EightBit::GameBoy::UserDefinedWaveVoice::waveDatum(int i) const {
assert(i < 32);
const auto packed = packedWaveDatum(i >> 1);
return i & 1 ? Processor::lowNibble(packed) : Processor::highNibble(packed);
}
//
EightBit::GameBoy::WhiteNoiseWaveVoice::WhiteNoiseWaveVoice() {}
void EightBit::GameBoy::WhiteNoiseWaveVoice::reset() {
AudioVoice::reset();
m_envelope.reset();
m_soundLength = m_polynomialShiftClockFrequency = m_polynomialCounterSteps = m_frequencyDivisionRatio = 0;
}
bool EightBit::GameBoy::WhiteNoiseWaveVoice::zeroed() const {
return
AudioVoice::zeroed()
&& m_envelope.zeroed()
&& (length() == 0)
&& (polynomialShiftClockFrequency() == 0)
&& (polynomialCounterSteps() == 0)
&& (frequencyDivisionRatio() == 0);
}
EightBit::GameBoy::Envelope& EightBit::GameBoy::WhiteNoiseWaveVoice::envelope() {
return m_envelope;
}
int EightBit::GameBoy::WhiteNoiseWaveVoice::length() const {
return m_soundLength;
}
void EightBit::GameBoy::WhiteNoiseWaveVoice::setLength(int value) {
m_soundLength = value;
}
int EightBit::GameBoy::WhiteNoiseWaveVoice::polynomialShiftClockFrequency() const {
return m_polynomialShiftClockFrequency;
}
void EightBit::GameBoy::WhiteNoiseWaveVoice::setPolynomialShiftClockFrequency(int value) {
m_polynomialShiftClockFrequency = value;
}
int EightBit::GameBoy::WhiteNoiseWaveVoice::polynomialCounterSteps() const {
return m_polynomialCounterSteps;
}
void EightBit::GameBoy::WhiteNoiseWaveVoice::setPolynomialCounterSteps(int value) {
m_polynomialCounterSteps = value;
}
int EightBit::GameBoy::WhiteNoiseWaveVoice::frequencyDivisionRatio() const {
return m_frequencyDivisionRatio;
}
void EightBit::GameBoy::WhiteNoiseWaveVoice::setFrequencyDivisionRatio(int value) {
m_frequencyDivisionRatio = value;
}
//
EightBit::GameBoy::OutputChannel::OutputChannel() {}
void EightBit::GameBoy::OutputChannel::reset() {
m_vin = false;
m_outputLevel = 0;
for (auto& outputVoice : m_outputVoices)
outputVoice = false;
}
bool EightBit::GameBoy::OutputChannel::zeroed() const {
return
!vin()
&& outputLevel() == 0
&& !m_outputVoices[0] && !m_outputVoices[1] && !m_outputVoices[2] && !m_outputVoices[3];
}
bool EightBit::GameBoy::OutputChannel::vin() const {
return m_vin;
}
void EightBit::GameBoy::OutputChannel::setVin(bool value) {
m_vin = value;
}
int EightBit::GameBoy::OutputChannel::outputLevel() const {
return m_outputLevel;
}
void EightBit::GameBoy::OutputChannel::setOutputLevel(int value) {
m_outputLevel = value;
}
bool& EightBit::GameBoy::OutputChannel::outputVoice(int voice) {
return m_outputVoices[voice];
}
bool& EightBit::GameBoy::OutputChannel::outputVoice1() {
return m_outputVoices[0];
}
bool& EightBit::GameBoy::OutputChannel::outputVoice2() {
return m_outputVoices[1];
}
bool& EightBit::GameBoy::OutputChannel::outputVoice3() {
return m_outputVoices[2];
}
bool& EightBit::GameBoy::OutputChannel::outputVoice4() {
return m_outputVoices[3];
}
EightBit::GameBoy::Audio::Audio(int cyclesPerSecond)
: m_frameSequencer(cyclesPerSecond),
m_enabled(false) {
m_voices[0] = std::make_shared<SweptEnvelopedRectangularVoice>(cyclesPerSecond);
m_voices[1] = std::make_shared<EnvelopedRectangularVoice>(cyclesPerSecond);
m_voices[2] = std::make_shared<UserDefinedWaveVoice>(cyclesPerSecond);
m_voices[3] = std::make_shared<WhiteNoiseWaveVoice>();
m_frameSequencer.FrameStep.connect(std::bind(&Audio::Sequencer_FrameStep, this, std::placeholders::_1));
m_frameSequencer.LengthStep.connect(std::bind(&Audio::Sequencer_LengthStep, this, std::placeholders::_1));
m_frameSequencer.VolumeStep.connect(std::bind(&Audio::Sequencer_VolumeStep, this, std::placeholders::_1));
m_frameSequencer.SweepStep.connect(std::bind(&Audio::Sequencer_SweepStep, this, std::placeholders::_1));
}
std::shared_ptr<EightBit::GameBoy::AudioVoice> EightBit::GameBoy::Audio::voice(int i) {
return m_voices[i];
}
EightBit::GameBoy::SweptEnvelopedRectangularVoice* EightBit::GameBoy::Audio::voice1() {
return (SweptEnvelopedRectangularVoice*)voice(0).get();
}
EightBit::GameBoy::EnvelopedRectangularVoice* EightBit::GameBoy::Audio::voice2() {
return (EnvelopedRectangularVoice*)voice(1).get();
}
EightBit::GameBoy::UserDefinedWaveVoice* EightBit::GameBoy::Audio::voice3() {
return (UserDefinedWaveVoice*)voice(2).get();
}
EightBit::GameBoy::WhiteNoiseWaveVoice* EightBit::GameBoy::Audio::voice4() {
return (WhiteNoiseWaveVoice*)voice(3).get();
}
EightBit::GameBoy::OutputChannel& EightBit::GameBoy::Audio::channel(int i) {
return m_channels[i];
}
EightBit::GameBoy::OutputChannel& EightBit::GameBoy::Audio::channel1() {
return channel(0);
}
EightBit::GameBoy::OutputChannel& EightBit::GameBoy::Audio::channel2() {
return channel(1);
}
bool EightBit::GameBoy::Audio::enabled() const {
return m_enabled;
}
void EightBit::GameBoy::Audio::setEnabled(bool value) {
m_enabled = value;
if (!enabled())
reset();
}
void EightBit::GameBoy::Audio::reset() {
m_enabled = false;
for (auto voice : m_voices)
voice->reset();
for (auto& channel : m_channels)
channel.reset();
}
bool EightBit::GameBoy::Audio::zeroed() const {
auto channelsZeroed = m_channels[0].zeroed() && m_channels[1].zeroed();
auto voice1Zero = m_voices[0]->zeroed();
auto voice2Zero = m_voices[1]->zeroed();
auto voice3Zero = m_voices[2]->zeroed();
auto voice4Zero = m_voices[3]->zeroed();
auto voicesZeroed = voice1Zero && voice2Zero && voice3Zero && voice4Zero;
return !enabled() && channelsZeroed && voicesZeroed;
}
//
bool EightBit::GameBoy::Audio::voice1On() const {
return true;
}
bool EightBit::GameBoy::Audio::voice2On() const {
return true;
}
bool EightBit::GameBoy::Audio::voice3On() const {
return true;
}
bool EightBit::GameBoy::Audio::voice4On() const {
return true;
}
//
uint8_t EightBit::GameBoy::Audio::toNRx1(int i) {
auto voice = (RectangularVoice*)m_voices[i].get();
return (voice->waveFormDutyCycle() << 6) | Processor::Mask6;
}
void EightBit::GameBoy::Audio::fromNRx1(int i, uint8_t value) {
auto voice = (RectangularVoice*)m_voices[i].get();
voice->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7
voice->setLength(value & Processor::Mask6); // Bits 0-5
}
uint8_t EightBit::GameBoy::Audio::toNRx2(int i) {
auto voice = (EnvelopedRectangularVoice*)m_voices[i].get();
auto& envelope = voice->envelope();
return (envelope.volume() << 4) | (envelope.direction() << 3) | envelope.period();
}
void EightBit::GameBoy::Audio::fromNRx2(int i, uint8_t value) {
auto voice = (EnvelopedRectangularVoice*)m_voices[i].get();
auto& envelope = voice->envelope();
envelope.setVolume((value >> 4) & Processor::Mask4); // Bits 4-7
envelope.setDirection((value >> 3) & Processor::Mask1); // Bit 3
envelope.setPeriod(value & Processor::Mask3); // Bits 0-2
}
uint8_t EightBit::GameBoy::Audio::toNRx3(int i) {
return Processor::Mask8;
}
void EightBit::GameBoy::Audio::fromNRx3(int i, uint8_t value) {
auto voice = (WaveVoice*)m_voices[i].get();
voice->setFrequencyLowOrder(value);
}
// Sound mode 1 register: Sweep
uint8_t EightBit::GameBoy::Audio::toNR10() {
auto& sweep = voice1()->sweep();
return
Processor::Bit7
| (sweep.time() << 4)
| (sweep.direction() << 3)
| sweep.shift();
}
void EightBit::GameBoy::Audio::fromNR10(uint8_t value) {
auto& sweep = voice1()->sweep();
sweep.setTime((value >> 4) & Processor::Mask3); // Bits 4-6
sweep.setDirection((value >> 3) & Processor::Mask1); // Bit 3
sweep.setShift(value & Processor::Mask3); // Bits 0-2
}
// Sound mode 1 register: Sound length / Wave pattern duty
uint8_t EightBit::GameBoy::Audio::toNR11() {
return toNRx1(0);
}
void EightBit::GameBoy::Audio::fromNR11(uint8_t value) {
fromNRx1(0, value);
}
// Sound mode 1 register: Envelope
uint8_t EightBit::GameBoy::Audio::toNR12() {
return toNRx2(0);
}
void EightBit::GameBoy::Audio::fromNR12(uint8_t value) {
fromNRx2(0, value);
}
// Sound mode 1 register: Frequency lo
uint8_t EightBit::GameBoy::Audio::toNR13() {
return toNRx3(0);
}
void EightBit::GameBoy::Audio::fromNR13(uint8_t value) {
fromNRx3(0, value);
}
// Sound mode 1 register: Frequency hi
uint8_t EightBit::GameBoy::Audio::toNR14() {
return Processor::Bit7 | (voice1()->type() << 6) | Processor::Mask6;
}
void EightBit::GameBoy::Audio::fromNR14(uint8_t value) {
voice1()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
voice1()->setType((value >> 6) & Processor::Mask1); // Bits 6
voice1()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
}
// Sound mode 2 register: Sound length / Wave pattern duty
uint8_t EightBit::GameBoy::Audio::toNR21() {
return toNRx1(1);
}
void EightBit::GameBoy::Audio::fromNR21(uint8_t value) {
fromNRx1(1, value);
}
// Sound mode 2 register: Envelope
uint8_t EightBit::GameBoy::Audio::toNR22() {
return toNRx2(1);
}
void EightBit::GameBoy::Audio::fromNR22(uint8_t value) {
fromNRx2(1, value);
}
// Sound mode 2 register: Frequency lo
uint8_t EightBit::GameBoy::Audio::toNR23() {
return toNRx3(1);
}
void EightBit::GameBoy::Audio::fromNR23(uint8_t value) {
fromNRx3(1, value);
}
// Sound mode 2 register: Frequency hi
uint8_t EightBit::GameBoy::Audio::toNR24() {
return Processor::Bit7 | (voice2()->type() << 6) | Processor::Mask6;
}
void EightBit::GameBoy::Audio::fromNR24(uint8_t value) {
voice2()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
voice2()->setType((value >> 6) & Processor::Mask1); // Bits 6
voice2()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
}
// Sound mode 3 register: Sound on/off
uint8_t EightBit::GameBoy::Audio::toNR30() {
return (voice3()->enabled() << 7) | Processor::Mask7;
}
void EightBit::GameBoy::Audio::fromNR30(uint8_t value) {
voice3()->setEnabled((value >> 7) & Processor::Mask1); // Bit 7
}
// Sound mode 3 register: Sound length
uint8_t EightBit::GameBoy::Audio::toNR31() {
return voice3()->length();
}
void EightBit::GameBoy::Audio::fromNR31(uint8_t value) {
voice3()->setLength(value);
}
// Sound mode 3 register: Select output level
uint8_t EightBit::GameBoy::Audio::toNR32() {
return Processor::Bit7 | Processor::Bit6 | voice3()->level() << 5 | Processor::Mask5;
}
void EightBit::GameBoy::Audio::fromNR32(uint8_t value) {
voice3()->setLevel((value >> 5) & Processor::Mask2); // Bits 6-5
}
// Sound mode 3 register: Frequency lo
uint8_t EightBit::GameBoy::Audio::toNR33() {
return toNRx3(2);
}
void EightBit::GameBoy::Audio::fromNR33(uint8_t value) {
fromNRx3(2, value);
}
// Sound mode 3 register: Frequency hi
uint8_t EightBit::GameBoy::Audio::toNR34() {
return Processor::Bit7 | (voice3()->type() << 6) | Processor::Mask6;
}
void EightBit::GameBoy::Audio::fromNR34(uint8_t value) {
voice3()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
voice3()->setType((value >> 6) & Processor::Mask1); // Bits 6
voice3()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
}
// Sound mode 4 register: Sound length
uint8_t EightBit::GameBoy::Audio::toNR41() {
return Processor::Bit7 | Processor::Bit6 | voice4()->length();
}
void EightBit::GameBoy::Audio::fromNR41(uint8_t value) {
voice4()->setLength(value);
}
// Sound mode 4 register: Envelope
uint8_t EightBit::GameBoy::Audio::toNR42() {
return toNRx2(3);
}
void EightBit::GameBoy::Audio::fromNR42(uint8_t value) {
fromNRx2(3, value);
}
// Sound mode 4 register: Polynomial counter
uint8_t EightBit::GameBoy::Audio::toNR43() {
return
(voice4()->polynomialShiftClockFrequency() << 4)
| voice4()->polynomialCounterSteps() << 3
| voice4()->frequencyDivisionRatio();
}
void EightBit::GameBoy::Audio::fromNR43(uint8_t value) {
voice4()->setPolynomialShiftClockFrequency((value >> 4) & Processor::Mask4); // Bits 4-7
voice4()->setPolynomialCounterSteps((value >> 3) & Processor::Mask1); // Bit 3
voice4()->setFrequencyDivisionRatio(value & Processor::Mask3); // Bits 0-2
}
// Sound mode 4 register: counter/consecutive; inital
uint8_t EightBit::GameBoy::Audio::toNR44() {
return Processor::Bit7 | (voice4()->type() << 6) | Processor::Mask6;
}
void EightBit::GameBoy::Audio::fromNR44(uint8_t value) {
voice4()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7
voice4()->setType((value >> 6) & Processor::Mask1); // Bit 6
}
// Channel control: on-off/volume
uint8_t EightBit::GameBoy::Audio::toNR50() {
return
(channel2().vin() << 7)
| (channel2().outputLevel() << 4)
| (channel2().vin() << 3)
| channel2().outputLevel();
}
void EightBit::GameBoy::Audio::fromNR50(uint8_t value) {
channel2().setVin((value >> 7) & Processor::Mask1); // Bit 7
channel2().setOutputLevel((value >> 4) & Processor::Mask3); // Bits 4-6
channel1().setVin((value >> 3) & Processor::Mask1); // Bit 3
channel1().setOutputLevel(value & Processor::Mask3); // Bits 0-2
}
// Selection of Sound output terminal
uint8_t EightBit::GameBoy::Audio::toNR51() {
return
(channel2().outputVoice4() << 7)
| (channel2().outputVoice3() << 6)
| (channel2().outputVoice2() << 5)
| (channel2().outputVoice1() << 4)
| (channel1().outputVoice4() << 3)
| (channel1().outputVoice3() << 2)
| (channel1().outputVoice2() << 1)
| (int)channel1().outputVoice1();
}
void EightBit::GameBoy::Audio::fromNR51(uint8_t value) {
channel2().outputVoice4() = (value >> 7) & Processor::Mask1; // Bit 7
channel2().outputVoice3() = (value >> 6) & Processor::Mask1; // Bit 6
channel2().outputVoice2() = (value >> 5) & Processor::Mask1; // Bit 5
channel2().outputVoice1() = (value >> 4) & Processor::Mask1; // Bit 4
channel1().outputVoice4() = (value >> 3) & Processor::Mask1; // Bit 3
channel1().outputVoice3() = (value >> 2) & Processor::Mask1; // Bit 2
channel1().outputVoice2() = (value >> 1) & Processor::Mask1; // Bit 1
channel1().outputVoice1() = value & Processor::Mask1; // Bit 0
}
// Sound on/off
uint8_t EightBit::GameBoy::Audio::toNR52() {
return
(enabled() << 7)
| Processor::Bit6 | Processor::Bit5 | Processor::Bit4
| (voice4On() << 3)
| (voice3On() << 2)
| (voice2On() << 1)
| (int)voice1On();
}
void EightBit::GameBoy::Audio::fromNR52(uint8_t value) {
setEnabled((value >> 7) & Processor::Mask1); // Bit 7
}
void EightBit::GameBoy::Audio::setPackedWaveDatum(int i, uint8_t value) {
voice3()->setPackedWaveDatum(i, value);
}
uint8_t EightBit::GameBoy::Audio::packedWaveDatum(int i) {
return voice3()->packedWaveDatum(i);
}
void EightBit::GameBoy::Audio::stepFrame(int cycles) {
m_frameSequencer.step(cycles);
}
void EightBit::GameBoy::Audio::Sequencer_FrameStep(const int step) {
}
void EightBit::GameBoy::Audio::Sequencer_LengthStep(const int step) {
stepLength(voice1());
stepLength(voice2());
stepLength(voice3());
stepLength(voice4());
}
void EightBit::GameBoy::Audio::Sequencer_VolumeStep(const int step) {
voice1()->envelope().step();
voice2()->envelope().step();
voice4()->envelope().step();
}
void EightBit::GameBoy::Audio::Sequencer_SweepStep(const int step) {
}

View File

@ -30,8 +30,7 @@ EightBit::GameBoy::Bus::Bus()
m_p13(true),
m_p12(true),
m_p11(true),
m_p10(true),
m_audio(CyclesPerSecond) {
m_p10(true) {
ReadingByte.connect(std::bind(&GameBoy::Bus::Bus_ReadingByte, this, std::placeholders::_1));
WrittenByte.connect(std::bind(&GameBoy::Bus::Bus_WrittenByte, this, std::placeholders::_1));
m_divCounter.word = 0xabcc;
@ -40,7 +39,6 @@ EightBit::GameBoy::Bus::Bus()
void EightBit::GameBoy::Bus::reset() {
audio().reset();
assert(audio().zeroed());
poke(BASE + NR52, 0xf1);
@ -112,71 +110,6 @@ void EightBit::GameBoy::Bus::Bus_ReadingByte(const uint16_t address) {
// but all are available for use.
break;
// Sound Registers
case NR10:
poke(address, audio().toNR10());
break;
case NR11:
poke(address, audio().toNR11());
break;
case NR12:
poke(address, audio().toNR12());
break;
case NR13:
poke(address, audio().toNR13());
break;
case NR14:
poke(address, audio().toNR14());
break;
case NR21:
poke(address, audio().toNR21());
break;
case NR22:
poke(address, audio().toNR22());
break;
case NR23:
poke(address, audio().toNR23());
break;
case NR24:
poke(address, audio().toNR24());
break;
case NR30:
poke(address, audio().toNR30());
break;
case NR31:
poke(address, audio().toNR31());
break;
case NR32:
poke(address, audio().toNR32());
break;
case NR33:
poke(address, audio().toNR33());
break;
case NR34:
poke(address, audio().toNR34());
break;
case NR41:
poke(address, audio().toNR41());
break;
case NR42:
poke(address, audio().toNR42());
break;
case NR43:
poke(address, audio().toNR43());
break;
case NR44:
poke(address, audio().toNR44());
break;
case NR50:
poke(address, audio().toNR50());
break;
case NR51:
poke(address, audio().toNR51());
break;
case NR52:
poke(address, audio().toNR52());
break;
// LCD Display Registers
case LCDC:
break;
@ -196,9 +129,6 @@ void EightBit::GameBoy::Bus::Bus_ReadingByte(const uint16_t address) {
break;
default:
if ((address >= (BASE + WAVE_PATTERN_RAM_START)) && (address <= (BASE + WAVE_PATTERN_RAM_END)))
poke(address, audio().packedWaveDatum(address - WAVE_PATTERN_RAM_START));
else
mask(0);
break;
}
@ -278,91 +208,6 @@ void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
case BASE + IF: // R/W
break;
case BASE + NR10: // Sound mode 1 register: Sweep
audio().fromNR10(value);
break;
case BASE + NR11: // Sound mode 1 register: Sound length / Wave pattern duty
audio().fromNR11(value);
break;
case BASE + NR12: // Sound mode 1 register: Envelope
audio().fromNR12(value);
break;
case BASE + NR13: // Sound mode 1 register: Frequency lo
audio().fromNR13(value);
break;
case BASE + NR14: // Sound mode 1 register: Frequency hi
audio().fromNR14(value);
std::cout << "Voice one frequency: " << audio().voice1()->hertz() << std::endl;
break;
case BASE + NR21: // Sound mode 2 register: Sound length / Wave pattern duty
audio().fromNR21(value);
break;
case BASE + NR22: // Sound mode 2 register: Envelope
audio().fromNR22(value);
break;
case BASE + NR23: // Sound mode 2 register: Frequency lo
audio().fromNR23(value);
break;
case BASE + NR24: // Sound mode 2 register: Frequency hi
audio().fromNR24(value);
break;
case BASE + NR30: // Sound mode 3 register: Sound on/off
audio().fromNR30(value);
break;
case BASE + NR31: // Sound mode 3 register: Sound length
audio().fromNR31(value);
break;
case BASE + NR32: // Sound mode 3 register: Select output level
audio().fromNR32(value);
break;
case BASE + NR33: // Sound mode 3 register: Frequency lo
audio().fromNR33(value);
break;
case BASE + NR34: // Sound mode 3 register: Frequency hi
audio().fromNR34(value);
break;
case BASE + NR41: // Sound mode 4 register: Sound length
audio().fromNR41(value);
break;
case BASE + NR42: // Sound mode 4 register: Envelope
audio().fromNR42(value);
break;
case BASE + NR43: // Sound mode 4 register: Polynomial counter
audio().fromNR43(value);
break;
case BASE + NR44: // Sound mode 4 register: counter/consecutive; inital
audio().fromNR44(value);
break;
case BASE + NR50: // Channel control: on-off/volume
audio().fromNR50(value);
break;
case BASE + NR51: // Selection of Sound output terminal
audio().fromNR51(value);
break;
case BASE + NR52: // Sound on/off
audio().fromNR52(value);
break;
case BASE + LCDC:
case BASE + STAT:
case BASE + SCY:
@ -386,10 +231,6 @@ void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
case BASE + BOOT_DISABLE:
m_disableBootRom = value != 0;
break;
default:
if ((address >= (BASE + WAVE_PATTERN_RAM_START)) && (address <= (BASE + WAVE_PATTERN_RAM_END)))
audio().setPackedWaveDatum(address - WAVE_PATTERN_RAM_START, value);
}
}
}

View File

@ -343,7 +343,6 @@ int EightBit::GameBoy::LR35902::runRasterLine() {
if ((m_bus.peekRegister(Bus::STAT) & Bit6) && (m_bus.peekRegister(Bus::LYC) == m_bus.peekRegister(Bus::LY)))
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
}
m_bus.audio().stepFrame(count);
return count;
}
@ -397,7 +396,9 @@ int EightBit::GameBoy::LR35902::step() {
ExecutingInstruction.fire(*this);
m_prefixCB = false;
cycles = 0;
return fetchExecute();
auto ran = fetchExecute();
ExecutedInstruction.fire(*this);
return ran;
}
#pragma endregion Controlled instruction execution
@ -423,7 +424,7 @@ int EightBit::GameBoy::LR35902::execute(uint8_t opcode) {
if (cycles == 0)
throw std::logic_error("Unhandled opcode");
return cycles * 4;
return clockCycles();
}
void EightBit::GameBoy::LR35902::executeCB(int x, int y, int z, int p, int q) {

View File

@ -140,8 +140,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\inc\AbstractColourPalette.h" />
<ClInclude Include="..\inc\Audio.h" />
<ClInclude Include="..\inc\AudioFrame.h" />
<ClInclude Include="..\inc\GameBoyBus.h" />
<ClInclude Include="..\inc\CharacterDefinition.h" />
<ClInclude Include="..\inc\Disassembler.h" />
@ -152,7 +150,6 @@
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Audio.cpp" />
<ClCompile Include="GameBoyBus.cpp" />
<ClCompile Include="Disassembler.cpp" />
<ClCompile Include="Display.cpp" />

View File

@ -38,12 +38,6 @@
<ClInclude Include="..\inc\ObjectAttribute.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Audio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\AudioFrame.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -64,8 +58,5 @@
<ClCompile Include="GameBoyBus.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Audio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>