mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-01-11 17:29:57 +00:00
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:
parent
053d649124
commit
dbe5f7f8e5
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user