new MemoryMap architecture

starting to working of voices and so on BRR, Envelopes, Gauss and Timers as they use each others
This commit is contained in:
Melefo
2021-02-04 00:06:39 +01:00
parent 61ef40e968
commit b0fb1e3351
13 changed files with 429 additions and 54 deletions

View File

@@ -105,7 +105,7 @@ add_executable(unit_tests
sources/CPU/DMA/DMA.hpp
sources/APU/DSP/Voice.cpp
sources/APU/DSP/Echo.cpp
)
sources/APU/DSP/Gauss.cpp sources/APU/DSP/Timer.cpp sources/APU/DSP/BRR.cpp)
# include criterion & coverage
target_link_libraries(unit_tests criterion -lgcov)
@@ -227,7 +227,7 @@ add_executable(ComSquare
sources/CPU/DMA/DMA.hpp
sources/APU/DSP/Voice.cpp
sources/APU/DSP/Echo.cpp
)
sources/APU/DSP/Gauss.cpp sources/APU/DSP/Envelope.cpp sources/APU/DSP/Timer.cpp sources/APU/DSP/BRR.cpp)
target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED)

View File

@@ -10,11 +10,11 @@
namespace ComSquare::APU
{
APU::APU(std::shared_ptr<MemoryMap> &map, Renderer::IRenderer &renderer) :
APU::APU(Renderer::IRenderer &renderer) :
_renderer(renderer),
_map(map),
_map(new MemoryMap()),
_soundBuffer(),
_dsp(new DSP::DSP(_soundBuffer, APU::bufferSize / 2))
_dsp(_soundBuffer, APU::bufferSize / 2, _map)
{
this->reset();
}
@@ -43,7 +43,7 @@ namespace ComSquare::APU
case 0xF2:
return this->_registers.dspregAddr;
case 0xF3:
return this->_dsp->read(this->_registers.dspregAddr);
return this->_dsp.read(this->_registers.dspregAddr);
case 0xF4:
return this->_registers.port0;
case 0xF5:
@@ -88,7 +88,7 @@ namespace ComSquare::APU
this->_registers.dspregAddr = data;
break;
case 0xF3:
this->_dsp->write(this->_registers.dspregAddr, data);
this->_dsp.write(this->_registers.dspregAddr, data);
break;
case 0xF4:
this->_registers.port0 = data;
@@ -718,8 +718,8 @@ namespace ComSquare::APU
if (this->_state == Running)
this->_paddingCycles = total - cycles;
this->_dsp->update();
samples = this->_dsp->getSamplesCount();
this->_dsp.update();
samples = this->_dsp.getSamplesCount();
if (samples > 0)
this->_renderer.playAudio(this->_soundBuffer, samples / 2);
}

View File

@@ -149,7 +149,7 @@ namespace ComSquare::APU
int16_t _soundBuffer[bufferSize];
//! @brief The DSP component used to produce sound
std::shared_ptr<DSP::DSP> _dsp;
DSP::DSP _dsp;
//! @brief Read from the APU ram.
//! @param addr The address to read from. The address 0x0000 should refer to the first byte of the register.
@@ -373,7 +373,7 @@ namespace ComSquare::APU
int MOV(uint24_t memFrom, uint8_t &regTo, int cycles, bool incrementX = false);
int MOV(uint24_t memTo, uint24_t memFrom);
public:
explicit APU(std::shared_ptr<MemoryMap> &map, Renderer::IRenderer &renderer);
explicit APU(Renderer::IRenderer &renderer);
APU(const APU &) = default;
APU &operator=(const APU &) = default;
~APU() override = default;

66
sources/APU/DSP/BRR.cpp Normal file
View File

@@ -0,0 +1,66 @@
//
// Created by melefo on 2/3/21.
//
#include <algorithm>
#include "DSP.hpp"
namespace ComSquare::APU::DSP
{
void DSP::decodeBRR(Voice &voice)
{
int32_t value = this->_brr.value << 8 | this->_readRAM(voice.brrAddress + voice.brrOffset + 1);
int32_t filter = this->_brr.header >> 2 & 0b11;
int32_t range = this->_brr.header >> 4 & 0b1111;
for (int i = 0; i < 4; i++) {
int32_t sample = value >> 12;
value <<= 4;
if (range <= 12) {
sample <<= range;
sample >>= 1;
}
else
sample &= ~0x7FF;
int offset = voice.sampleOffset;
int lastSample;
int afterLastSample;
if (--offset < 0)
offset = 11;
lastSample = voice.samples[offset];
if (--offset < 0)
offset = 11;
afterLastSample = voice.samples[offset];
switch (filter) {
case 0:
break;
case 1:
sample += lastSample;
sample += (lastSample) >> 4;
break;
case 2:
sample += lastSample << 1;
sample += -((lastSample << 1) + lastSample) >> 5;
sample -= afterLastSample;
sample += afterLastSample >> 4;
break;
case 3:
sample += lastSample << 1;
sample += -(lastSample + (lastSample << 2) + (lastSample << 3)) >> 6;
sample -= afterLastSample;
sample += ((afterLastSample << 1) + afterLastSample) >> 4;
break;
}
sample = std::clamp(sample, 0, 16);
sample <<= 1;
voice.samples[voice.sampleOffset] = sample;
if (++voice.sampleOffset >= 12)
voice.sampleOffset = 0;
}
}
}

View File

@@ -3,11 +3,12 @@
//
#include "DSP.hpp"
#include "../APU.hpp"
#include "../../Exceptions/InvalidAddress.hpp"
namespace ComSquare::APU::DSP
{
DSP::DSP(int16_t *buffer, int32_t size)
DSP::DSP(int16_t *buffer, int32_t size, std::weak_ptr<MemoryMap> map) : _map(map)
{
this->_state.buffer = buffer;
this->_state.bufferStart = buffer;
@@ -568,6 +569,40 @@ namespace ComSquare::APU::DSP
}
}
uint8_t DSP::_readRAM(uint24_t addr) {
switch (addr) {
case 0x0000 ... 0x00EF:
return this->_map.lock()->Page0.read_internal(addr);
case 0x0100 ... 0x01FF:
return this->_map.lock()->Page1.read_internal(addr - 0x0100);
case 0x0200 ... 0xFFBF:
return this->_map.lock()->Memory.read_internal(addr - 0x200);
case 0xFFC0 ... 0xFFFF:
return this->_map.lock()->IPL.read(addr - 0xFFC0);
default:
throw InvalidAddress("DSP read", addr);
}
}
void DSP::_writeRAM(uint24_t addr, uint8_t data) {
switch (addr) {
case 0x0000 ... 0x00EF:
this->_map.lock()->Page0.write_internal(addr, data);
break;
case 0x0100 ... 0x01FF:
this->_map.lock()->Page1.write_internal(addr - 0x0100, data);
break;
case 0x0200 ... 0xFFBF:
this->_map.lock()->Memory.write_internal(addr - 0x200, data);
break;
case 0xFFC0 ... 0xFFFF:
this->_map.lock()->IPL.write(addr - 0xFFC0, data);
break;
default:
throw InvalidAddress("DSP write", addr);
}
}
void DSP::update()
{
switch (this->_state.voice) {
@@ -760,14 +795,4 @@ namespace ComSquare::APU::DSP
{
return this->_state.buffer - this->_state.bufferStart;
}
std::string DSP::getName()
{
return "DSP";
}
Component DSP::getComponent()
{
return Apu;
}
}

View File

@@ -9,8 +9,21 @@
#include <array>
#include "../../Memory/AMemory.hpp"
namespace ComSquare::APU
{
class APU;
struct MemoryMap;
}
namespace ComSquare::APU::DSP
{
enum Envelope : uint {
Release,
Attack,
Decay,
Sustain
};
struct Master {
//! @brief Main Volume register (MVOL)
std::array<uint8_t, 2> volume;
@@ -55,6 +68,18 @@ namespace ComSquare::APU::DSP
struct BRR {
//! @brief Offset pointing to sample directory in external RAM (DIR)
uint8_t offset;
//! @brief Address of the offset
uint8_t offsetAddr;
//! @brief Current address of the BRR in APU's RAM
uint16_t address;
//! @brief Next address of the BRR in APU's RAM
uint16_t nextAddress;
//! @brief Current value inside BRR
uint8_t value;
//! @brief Current header of BRR
uint8_t header;
//! @brief Current value of Voice ADSR1 loaded
uint8_t source;
};
struct Latch {
@@ -92,7 +117,7 @@ namespace ComSquare::APU::DSP
//! @brief Envelope controllers register (ADSR)
uint8_t adsr2;
};
uint16_t envelope;
uint16_t adsr;
};
//! @brief Gain register (GAIN)
uint8_t gain;
@@ -118,6 +143,30 @@ namespace ComSquare::APU::DSP
bool echo;
//! @brief Check if this voice will be looped
bool loop;
//! @brief Current BRR associated with this voice
uint16_t brrAddress;
//! @brief Current Offset in the BRR block
uint8_t brrOffset = 1;
//! @brief Previous modulation
bool prevPmon : 1;
//! @brief temporary NON register value
bool tempNon : 1;
//! @brief temporary Key On register value
bool tempKon : 1;
//! @brief temporary Key Off register value
bool tempKof : 1;
//! @brief all samples Decoded from BRR
std::array<uint16_t, 12> samples;
//! @brief Offset of current sample in samples buffer
uint8_t sampleOffset;
//! @brief Current envelope level
uint16_t envelope;
//! @brief Second envelope level used to make "special" waveforms
uint16_t hiddenEnvelope;
//! @brief current envelope Mode
Envelope envelopeMode;
//! @brief Relative fractional position in sample
uint16_t gaussOffset;
};
//! @brief Current state of the DSP
@@ -132,6 +181,14 @@ namespace ComSquare::APU::DSP
//! @brief Beginning of the buffer
int16_t *bufferStart;
};
struct Timer {
//! @brief Ticks remaining in the timer
uint16_t counter;
//! @brief output every samples
bool sample = true;
};
/*//! @brief All the registers of the DSP
struct Registers {
//! @brief Main Volume register
@@ -249,8 +306,28 @@ namespace ComSquare::APU::DSP
uint16_t output;
};*/
class DSP : public Memory::AMemory {
class DSP {
private:
//! @brief Number of samples per counter event
std::array<uint16_t, 32> _rateModulus = {
0, 2048, 1536, 1280, 1024, 768,
640, 512, 384, 320, 256, 192,
160, 128, 96, 80, 64, 48,
40, 32, 24, 20, 16, 12,
10, 8, 6, 5, 4, 3,
2, 1
};
//! @brief Counter offset
std::array<uint16_t, 32> _counterOffset = {
0, 0, 1040, 536, 0, 1040,
536, 0, 1040, 536, 0, 1040,
536, 0, 1040, 536, 0, 1040,
536, 0, 1040, 536, 0, 1040,
536, 0, 1040, 536, 0, 1040,
0,0
};
//! @brief Gaussian table used for making waves
std::array<int16_t, 512> _gauss = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -295,6 +372,7 @@ namespace ComSquare::APU::DSP
BRR _brr {};
Latch _latch {};
State _state {};
Timer _timer {};
void voiceOutput(Voice &voice, bool channel);
void voice1(Voice &voice);
@@ -322,11 +400,24 @@ namespace ComSquare::APU::DSP
void misc28();
void misc29();
void misc30();
int32_t interpolate(const Voice &voice);
void runEnvelope(Voice &voice);
void timerTick();
bool timerPoll(uint32_t rate);
void decodeBRR(Voice &voice);
std::weak_ptr<MemoryMap> _map;
uint8_t _readRAM(uint24_t addr);
void _writeRAM(uint24_t addr, uint8_t data);
public:
DSP(int16_t *buffer, int32_t size);
DSP(int16_t *buffer, int32_t size, std::weak_ptr<MemoryMap> map);
DSP(const DSP &) = default;
DSP &operator=(const DSP &) = default;
~DSP() override = default;
~DSP() = default;
//! @brief Return all 8 voices from DSP
const std::array<Voice, 8> &getVoices();
@@ -340,24 +431,18 @@ namespace ComSquare::APU::DSP
//! @param addr The address to read from. The address 0x0 should refer to the first byte of the register.
//! @throw InvalidAddress will be thrown if the address is more than $7F (the number of register).
//! @return Return the value of the register.
uint8_t read(uint24_t addr) override;
uint8_t read(uint24_t addr);
//! @brief Write data to the internal DSP register.
//! @param addr The address to write to. The address 0x0 should refer to the first byte of register.
//! @param data The new value of the register.
//! @throw InvalidAddress will be thrown if the address is more than $7F (the number of register).
void write(uint24_t addr, uint8_t data) override;
void write(uint24_t addr, uint8_t data);
//! @brief Execute current voice transformation
void update();
//! @brief Return the number of samples written
int32_t getSamplesCount() const;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() override;
};
}

View File

@@ -0,0 +1,78 @@
//
// Created by melefo on 2/3/21.
//
#include "DSP.hpp"
namespace ComSquare::APU::DSP
{
void DSP::runEnvelope(Voice &voice)
{
int32_t envelope = voice.envelope;
if (voice.envelopeMode == Envelope::Release) {
envelope -= 0x08;
if (envelope < 0)
envelope = 0;
voice.envelope = envelope;
return;
}
int32_t rate;
int32_t mode;
int32_t data = voice.adsr2;
if (this->_latch.adsr1 & 0b10000000) {
if (voice.envelopeMode >= Envelope::Decay) {
envelope -= 1;
envelope -= envelope >> 8;
rate = data & 0b11111;
if (voice.envelopeMode == Envelope::Decay)
rate = ((this->_latch.adsr1 >> 3) & 0x0E) + 0x10;
}
else {
rate = ((this->_latch.adsr1 & 0b1111) << 1) + 1;
if (rate < 0b11111)
envelope += 0x20;
else
envelope += 0x400;
}
}
else {
data = voice.gain;
mode = data >> 5;
if (mode < 4) {
envelope = data << 4;
rate = 0b11111;
}
else {
rate = data & 0b11111;
if (mode == 4)
envelope -= 0x20;
else if (mode < 6) {
envelope -= 1;
envelope -= envelope >> 8;
}
else {
envelope += 0x20;
if (mode > 6 && voice.hiddenEnvelope >= 0x600)
envelope += 0x08 - 0x20;
}
}
}
if (envelope >> 8 == (data >> 5) && voice.envelopeMode == Envelope::Decay)
voice.envelopeMode = Envelope::Sustain;
voice.hiddenEnvelope = envelope;
if (static_cast<uint32_t>(envelope) > 0x7FF) {
if (envelope < 0)
envelope = 0;
else
envelope = 0x7FF;
if (voice.envelopeMode == Envelope::Attack)
voice.envelopeMode = Envelope::Decay;
}
if (this->timerPoll(rate))
voice.envelope = envelope;
}
}

30
sources/APU/DSP/Gauss.cpp Normal file
View File

@@ -0,0 +1,30 @@
//
// Created by melefo on 2/3/21.
//
#include <algorithm>
#include <cstdint>
#include "DSP.hpp"
namespace ComSquare::APU::DSP
{
int32_t DSP::interpolate(const Voice &voice)
{
int32_t interpolated;
uint8_t offset = voice.gaussOffset >> 4;
int forward = 255 - offset;
int reverse = offset;
offset = (voice.sampleOffset + (voice.gaussOffset >> 12)) % 12;
interpolated = this->_gauss[forward] * voice.samples[offset++] >> 11;
offset %= 12;
interpolated += this->_gauss[forward + 256] * voice.samples[offset++] >> 11;
offset %= 12;
interpolated += this->_gauss[reverse + 256] * voice.samples[offset++] >> 11;
offset %= 12;
interpolated = static_cast<int16_t>(interpolated);
interpolated += this->_gauss[reverse] * voice.samples[offset] >> 11;
return std::clamp(interpolated, 0, 16) & ~1;
}
}

22
sources/APU/DSP/Timer.cpp Normal file
View File

@@ -0,0 +1,22 @@
//
// Created by melefo on 2/3/21.
//
#include "DSP.hpp"
namespace ComSquare::APU::DSP
{
void DSP::timerTick()
{
if (!this->_timer.counter)
this->_timer.counter = 0x7800;
this->_timer.counter -= 1;
}
bool DSP::timerPoll(uint32_t rate)
{
if (!rate)
return false;
return (this->_timer.counter + this->_counterOffset[rate]) % this->_rateModulus[rate] == 0;
}
}

View File

@@ -3,6 +3,7 @@
//
#include "DSP.hpp"
#include "../APU.hpp"
#include <algorithm>
namespace ComSquare::APU::DSP
@@ -19,38 +20,109 @@ namespace ComSquare::APU::DSP
void DSP::voice1(Voice &voice)
{
this->_brr.address = (this->_brr.offsetAddr << 8) + (this->_brr.source << 2);
this->_brr.source = voice.srcn;
}
void DSP::voice2(Voice &voice)
{
/*uint16_t addr = this->_brr.
this->_latch.adsr1 = voice.adsr1;*/
uint16_t addr = this->_brr.address;
if (!voice.konDelay)
addr += 2;
this->_brr.nextAddress = this->_readRAM(addr++);
this->_brr.nextAddress += this->_readRAM(addr++) << 8;
this->_latch.adsr1 = voice.adsr1;
this->_latch.pitch = voice.pitch & 0xFF;
}
void DSP::voice3(Voice &voice)
{
this->voice3a(voice);
this->voice3b(voice);
this->voice3c(voice);
}
void DSP::voice3a(Voice &voice)
{
this->_latch.pitch |= (voice.pitchH & 0x3F) << 8;
}
void DSP::voice3b(Voice &voice)
{
this->_brr.header = this->_readRAM(this->_brr.address);
this->_brr.value = this->_readRAM(this->_brr.address + voice.brrOffset);
}
void DSP::voice3c(Voice &voice)
{
if (voice.prevPmon)
this->_latch.pitch += (this->_latch.output >> 5) * this->_latch.pitch >> 10;
if (voice.konDelay) {
if (voice.konDelay == 5) {
voice.brrAddress = this->_brr.nextAddress;
voice.brrOffset = 1;
voice.sampleOffset = 0;
this->_brr.header = 0;
}
voice.envelope = 0;
voice.hiddenEnvelope = 0;
voice.gaussOffset = 0;
voice.konDelay -= 1;
if (voice.konDelay & 3)
voice.gaussOffset = 0x4000;
this->_latch.pitch = 0;
}
int32_t interpolated = this->interpolate(voice);
if (voice.tempNon)
interpolated = this->_noise.lfsr << 1;
this->_latch.output = interpolated * voice.envelope >> 11 & ~1;
voice.envx = voice.envelope >> 4;
if (this->_master.reset || (this->_brr.header & 3) == 1) {
voice.envelope = 0;
voice.envelopeMode = Envelope::Release;
}
if (this->_timer.sample)
{
if (voice.tempKof)
voice.envelopeMode = Envelope::Release;
if (voice.tempKon) {
voice.konDelay = 5;
voice.envelopeMode = Envelope::Attack;
}
}
if (!voice.konDelay)
this->runEnvelope(voice);
}
void DSP::voice4(Voice &voice)
{
voice.loop = false;
if (voice.gaussOffset >= 0x4000) {
this->decodeBRR(voice);
voice.brrOffset += 2;
if (voice.brrOffset >= 9) {
voice.brrOffset = voice.brrAddress + 9;
if (this->_brr.header & 0b1) {
voice.brrAddress = this->_brr.nextAddress;
voice.loop = true;
}
voice.brrOffset = 1;
}
}
voice.gaussOffset = (voice.gaussOffset & 0x3FFF) + this->_latch.pitch;
if (voice.gaussOffset > 0x7FFF)
voice.gaussOffset = 0x7FFF;
this->voiceOutput(voice, 0);
}
void DSP::voice5(Voice &voice)
@@ -62,9 +134,9 @@ namespace ComSquare::APU::DSP
voice.endx &= ~voice.endx;
}
void DSP::voice6(Voice &voice)
void DSP::voice6(Voice &)
{
this->_latch.outx = this->_latch.output >> 8;
}
void DSP::voice7(Voice &voice)

View File

@@ -47,8 +47,8 @@ namespace ComSquare::Debugger
this->_ui.dSPRegAddresshexaLineEdit->setText(Utility::to_hex(this->_registers.dspregAddr).c_str());
this->_ui.dSPRegAddressLineEdit->setText(Utility::to_binary(this->_registers.dspregAddr).c_str());
this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_dsp->read(this->_registers.dspregAddr)).c_str());
this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_dsp->read(this->_registers.dspregAddr)).c_str());
this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_dsp.read(this->_registers.dspregAddr)).c_str());
this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_dsp.read(this->_registers.dspregAddr)).c_str());
this->_ui.timer0hexaLineEdit->setText(Utility::to_hex(this->_registers.timer0).c_str());
this->_ui.timer0LineEdit->setText(Utility::to_binary(this->_registers.timer0).c_str());
@@ -84,12 +84,12 @@ namespace ComSquare::Debugger
this->_ui.programCounterLineEdit->setText(Utility::to_hex(this->_internalRegisters.pc + 0x0001u).c_str());
this->_ui.programStatusWordLineEdit->setText(this->_getPSWString().c_str());
auto voices = this->_dsp->getVoices();
auto master = this->_dsp->getMaster();
auto echo = this->_dsp->getEcho();
auto noise = this->_dsp->getNoise();
auto brr = this->_dsp->getBrr();
auto latch = this->_dsp->getLatch();
auto voices = this->_dsp.getVoices();
auto master = this->_dsp.getMaster();
auto echo = this->_dsp.getEcho();
auto noise = this->_dsp.getNoise();
auto brr = this->_dsp.getBrr();
auto latch = this->_dsp.getLatch();
this->_ui.mvolLprogressBar->setValue(master.volume[0]);
this->_ui.mvolRprogressBar->setValue(master.volume[1]);

View File

@@ -19,10 +19,9 @@ namespace ComSquare
cartridge(new Cartridge::Cartridge(romPath)),
wram(new Ram::Ram(16384, WRam, "WRam")),
sram(new Ram::Ram(this->cartridge->header.sramSize, SRam, "SRam")),
apuRam(new APU::MemoryMap()),
cpu(new CPU::CPU(this->_bus, cartridge->header)),
ppu(new PPU::PPU(renderer)),
apu(new APU::APU(this->apuRam, renderer))
apu(new APU::APU(renderer))
{
this->_bus->mapComponents(*this);
}

View File

@@ -41,8 +41,6 @@ namespace ComSquare
std::shared_ptr<Ram::Ram> wram;
//! @brief Save Ram residing inside the Cartridge in a real SNES.
std::shared_ptr<Ram::Ram> sram;
//! @brief External Ram used only by the Audio Processing Unit
std::shared_ptr<APU::MemoryMap> apuRam;
//! @brief Central Processing Unit of the SNES.
std::shared_ptr<CPU::CPU> cpu;
//! @brief Picture Processing Unit of the SNES