mirror of
https://github.com/zoriya/ComSquare.git
synced 2025-12-20 14:15:11 +00:00
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:
@@ -105,7 +105,7 @@ add_executable(unit_tests
|
|||||||
sources/CPU/DMA/DMA.hpp
|
sources/CPU/DMA/DMA.hpp
|
||||||
sources/APU/DSP/Voice.cpp
|
sources/APU/DSP/Voice.cpp
|
||||||
sources/APU/DSP/Echo.cpp
|
sources/APU/DSP/Echo.cpp
|
||||||
)
|
sources/APU/DSP/Gauss.cpp sources/APU/DSP/Timer.cpp sources/APU/DSP/BRR.cpp)
|
||||||
|
|
||||||
# include criterion & coverage
|
# include criterion & coverage
|
||||||
target_link_libraries(unit_tests criterion -lgcov)
|
target_link_libraries(unit_tests criterion -lgcov)
|
||||||
@@ -227,7 +227,7 @@ add_executable(ComSquare
|
|||||||
sources/CPU/DMA/DMA.hpp
|
sources/CPU/DMA/DMA.hpp
|
||||||
sources/APU/DSP/Voice.cpp
|
sources/APU/DSP/Voice.cpp
|
||||||
sources/APU/DSP/Echo.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)
|
target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED)
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
namespace ComSquare::APU
|
namespace ComSquare::APU
|
||||||
{
|
{
|
||||||
APU::APU(std::shared_ptr<MemoryMap> &map, Renderer::IRenderer &renderer) :
|
APU::APU(Renderer::IRenderer &renderer) :
|
||||||
_renderer(renderer),
|
_renderer(renderer),
|
||||||
_map(map),
|
_map(new MemoryMap()),
|
||||||
_soundBuffer(),
|
_soundBuffer(),
|
||||||
_dsp(new DSP::DSP(_soundBuffer, APU::bufferSize / 2))
|
_dsp(_soundBuffer, APU::bufferSize / 2, _map)
|
||||||
{
|
{
|
||||||
this->reset();
|
this->reset();
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ namespace ComSquare::APU
|
|||||||
case 0xF2:
|
case 0xF2:
|
||||||
return this->_registers.dspregAddr;
|
return this->_registers.dspregAddr;
|
||||||
case 0xF3:
|
case 0xF3:
|
||||||
return this->_dsp->read(this->_registers.dspregAddr);
|
return this->_dsp.read(this->_registers.dspregAddr);
|
||||||
case 0xF4:
|
case 0xF4:
|
||||||
return this->_registers.port0;
|
return this->_registers.port0;
|
||||||
case 0xF5:
|
case 0xF5:
|
||||||
@@ -88,7 +88,7 @@ namespace ComSquare::APU
|
|||||||
this->_registers.dspregAddr = data;
|
this->_registers.dspregAddr = data;
|
||||||
break;
|
break;
|
||||||
case 0xF3:
|
case 0xF3:
|
||||||
this->_dsp->write(this->_registers.dspregAddr, data);
|
this->_dsp.write(this->_registers.dspregAddr, data);
|
||||||
break;
|
break;
|
||||||
case 0xF4:
|
case 0xF4:
|
||||||
this->_registers.port0 = data;
|
this->_registers.port0 = data;
|
||||||
@@ -718,8 +718,8 @@ namespace ComSquare::APU
|
|||||||
if (this->_state == Running)
|
if (this->_state == Running)
|
||||||
this->_paddingCycles = total - cycles;
|
this->_paddingCycles = total - cycles;
|
||||||
|
|
||||||
this->_dsp->update();
|
this->_dsp.update();
|
||||||
samples = this->_dsp->getSamplesCount();
|
samples = this->_dsp.getSamplesCount();
|
||||||
if (samples > 0)
|
if (samples > 0)
|
||||||
this->_renderer.playAudio(this->_soundBuffer, samples / 2);
|
this->_renderer.playAudio(this->_soundBuffer, samples / 2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ namespace ComSquare::APU
|
|||||||
int16_t _soundBuffer[bufferSize];
|
int16_t _soundBuffer[bufferSize];
|
||||||
|
|
||||||
//! @brief The DSP component used to produce sound
|
//! @brief The DSP component used to produce sound
|
||||||
std::shared_ptr<DSP::DSP> _dsp;
|
DSP::DSP _dsp;
|
||||||
|
|
||||||
//! @brief Read from the APU ram.
|
//! @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.
|
//! @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 ®To, int cycles, bool incrementX = false);
|
int MOV(uint24_t memFrom, uint8_t ®To, int cycles, bool incrementX = false);
|
||||||
int MOV(uint24_t memTo, uint24_t memFrom);
|
int MOV(uint24_t memTo, uint24_t memFrom);
|
||||||
public:
|
public:
|
||||||
explicit APU(std::shared_ptr<MemoryMap> &map, Renderer::IRenderer &renderer);
|
explicit APU(Renderer::IRenderer &renderer);
|
||||||
APU(const APU &) = default;
|
APU(const APU &) = default;
|
||||||
APU &operator=(const APU &) = default;
|
APU &operator=(const APU &) = default;
|
||||||
~APU() override = default;
|
~APU() override = default;
|
||||||
|
|||||||
66
sources/APU/DSP/BRR.cpp
Normal file
66
sources/APU/DSP/BRR.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,11 +3,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "DSP.hpp"
|
#include "DSP.hpp"
|
||||||
|
#include "../APU.hpp"
|
||||||
#include "../../Exceptions/InvalidAddress.hpp"
|
#include "../../Exceptions/InvalidAddress.hpp"
|
||||||
|
|
||||||
namespace ComSquare::APU::DSP
|
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.buffer = buffer;
|
||||||
this->_state.bufferStart = 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()
|
void DSP::update()
|
||||||
{
|
{
|
||||||
switch (this->_state.voice) {
|
switch (this->_state.voice) {
|
||||||
@@ -760,14 +795,4 @@ namespace ComSquare::APU::DSP
|
|||||||
{
|
{
|
||||||
return this->_state.buffer - this->_state.bufferStart;
|
return this->_state.buffer - this->_state.bufferStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DSP::getName()
|
|
||||||
{
|
|
||||||
return "DSP";
|
|
||||||
}
|
|
||||||
|
|
||||||
Component DSP::getComponent()
|
|
||||||
{
|
|
||||||
return Apu;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,21 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include "../../Memory/AMemory.hpp"
|
#include "../../Memory/AMemory.hpp"
|
||||||
|
|
||||||
|
namespace ComSquare::APU
|
||||||
|
{
|
||||||
|
class APU;
|
||||||
|
struct MemoryMap;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ComSquare::APU::DSP
|
namespace ComSquare::APU::DSP
|
||||||
{
|
{
|
||||||
|
enum Envelope : uint {
|
||||||
|
Release,
|
||||||
|
Attack,
|
||||||
|
Decay,
|
||||||
|
Sustain
|
||||||
|
};
|
||||||
|
|
||||||
struct Master {
|
struct Master {
|
||||||
//! @brief Main Volume register (MVOL)
|
//! @brief Main Volume register (MVOL)
|
||||||
std::array<uint8_t, 2> volume;
|
std::array<uint8_t, 2> volume;
|
||||||
@@ -55,6 +68,18 @@ namespace ComSquare::APU::DSP
|
|||||||
struct BRR {
|
struct BRR {
|
||||||
//! @brief Offset pointing to sample directory in external RAM (DIR)
|
//! @brief Offset pointing to sample directory in external RAM (DIR)
|
||||||
uint8_t offset;
|
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 {
|
struct Latch {
|
||||||
@@ -92,7 +117,7 @@ namespace ComSquare::APU::DSP
|
|||||||
//! @brief Envelope controllers register (ADSR)
|
//! @brief Envelope controllers register (ADSR)
|
||||||
uint8_t adsr2;
|
uint8_t adsr2;
|
||||||
};
|
};
|
||||||
uint16_t envelope;
|
uint16_t adsr;
|
||||||
};
|
};
|
||||||
//! @brief Gain register (GAIN)
|
//! @brief Gain register (GAIN)
|
||||||
uint8_t gain;
|
uint8_t gain;
|
||||||
@@ -118,6 +143,30 @@ namespace ComSquare::APU::DSP
|
|||||||
bool echo;
|
bool echo;
|
||||||
//! @brief Check if this voice will be looped
|
//! @brief Check if this voice will be looped
|
||||||
bool loop;
|
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
|
//! @brief Current state of the DSP
|
||||||
@@ -132,6 +181,14 @@ namespace ComSquare::APU::DSP
|
|||||||
//! @brief Beginning of the buffer
|
//! @brief Beginning of the buffer
|
||||||
int16_t *bufferStart;
|
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
|
/*//! @brief All the registers of the DSP
|
||||||
struct Registers {
|
struct Registers {
|
||||||
//! @brief Main Volume register
|
//! @brief Main Volume register
|
||||||
@@ -249,8 +306,28 @@ namespace ComSquare::APU::DSP
|
|||||||
uint16_t output;
|
uint16_t output;
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
class DSP : public Memory::AMemory {
|
class DSP {
|
||||||
private:
|
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
|
//! @brief Gaussian table used for making waves
|
||||||
std::array<int16_t, 512> _gauss = {
|
std::array<int16_t, 512> _gauss = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
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 {};
|
BRR _brr {};
|
||||||
Latch _latch {};
|
Latch _latch {};
|
||||||
State _state {};
|
State _state {};
|
||||||
|
Timer _timer {};
|
||||||
|
|
||||||
void voiceOutput(Voice &voice, bool channel);
|
void voiceOutput(Voice &voice, bool channel);
|
||||||
void voice1(Voice &voice);
|
void voice1(Voice &voice);
|
||||||
@@ -322,11 +400,24 @@ namespace ComSquare::APU::DSP
|
|||||||
void misc28();
|
void misc28();
|
||||||
void misc29();
|
void misc29();
|
||||||
void misc30();
|
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:
|
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(const DSP &) = default;
|
||||||
DSP &operator=(const DSP &) = default;
|
DSP &operator=(const DSP &) = default;
|
||||||
~DSP() override = default;
|
~DSP() = default;
|
||||||
|
|
||||||
//! @brief Return all 8 voices from DSP
|
//! @brief Return all 8 voices from DSP
|
||||||
const std::array<Voice, 8> &getVoices();
|
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.
|
//! @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).
|
//! @throw InvalidAddress will be thrown if the address is more than $7F (the number of register).
|
||||||
//! @return Return the value of the 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.
|
//! @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 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.
|
//! @param data The new value of the register.
|
||||||
//! @throw InvalidAddress will be thrown if the address is more than $7F (the number of 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
|
//! @brief Execute current voice transformation
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
//! @brief Return the number of samples written
|
//! @brief Return the number of samples written
|
||||||
int32_t getSamplesCount() const;
|
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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
sources/APU/DSP/Envelope.cpp
Normal file
78
sources/APU/DSP/Envelope.cpp
Normal 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
30
sources/APU/DSP/Gauss.cpp
Normal 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
22
sources/APU/DSP/Timer.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "DSP.hpp"
|
#include "DSP.hpp"
|
||||||
|
#include "../APU.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace ComSquare::APU::DSP
|
namespace ComSquare::APU::DSP
|
||||||
@@ -19,38 +20,109 @@ namespace ComSquare::APU::DSP
|
|||||||
|
|
||||||
void DSP::voice1(Voice &voice)
|
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)
|
void DSP::voice2(Voice &voice)
|
||||||
{
|
{
|
||||||
/*uint16_t addr = this->_brr.
|
uint16_t addr = this->_brr.address;
|
||||||
this->_latch.adsr1 = voice.adsr1;*/
|
|
||||||
|
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)
|
void DSP::voice3(Voice &voice)
|
||||||
{
|
{
|
||||||
|
this->voice3a(voice);
|
||||||
|
this->voice3b(voice);
|
||||||
|
this->voice3c(voice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::voice3a(Voice &voice)
|
void DSP::voice3a(Voice &voice)
|
||||||
{
|
{
|
||||||
|
this->_latch.pitch |= (voice.pitchH & 0x3F) << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::voice3b(Voice &voice)
|
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)
|
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)
|
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)
|
void DSP::voice5(Voice &voice)
|
||||||
@@ -62,9 +134,9 @@ namespace ComSquare::APU::DSP
|
|||||||
voice.endx &= ~voice.endx;
|
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)
|
void DSP::voice7(Voice &voice)
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ namespace ComSquare::Debugger
|
|||||||
this->_ui.dSPRegAddresshexaLineEdit->setText(Utility::to_hex(this->_registers.dspregAddr).c_str());
|
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.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.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.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.timer0hexaLineEdit->setText(Utility::to_hex(this->_registers.timer0).c_str());
|
||||||
this->_ui.timer0LineEdit->setText(Utility::to_binary(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.programCounterLineEdit->setText(Utility::to_hex(this->_internalRegisters.pc + 0x0001u).c_str());
|
||||||
this->_ui.programStatusWordLineEdit->setText(this->_getPSWString().c_str());
|
this->_ui.programStatusWordLineEdit->setText(this->_getPSWString().c_str());
|
||||||
|
|
||||||
auto voices = this->_dsp->getVoices();
|
auto voices = this->_dsp.getVoices();
|
||||||
auto master = this->_dsp->getMaster();
|
auto master = this->_dsp.getMaster();
|
||||||
auto echo = this->_dsp->getEcho();
|
auto echo = this->_dsp.getEcho();
|
||||||
auto noise = this->_dsp->getNoise();
|
auto noise = this->_dsp.getNoise();
|
||||||
auto brr = this->_dsp->getBrr();
|
auto brr = this->_dsp.getBrr();
|
||||||
auto latch = this->_dsp->getLatch();
|
auto latch = this->_dsp.getLatch();
|
||||||
|
|
||||||
this->_ui.mvolLprogressBar->setValue(master.volume[0]);
|
this->_ui.mvolLprogressBar->setValue(master.volume[0]);
|
||||||
this->_ui.mvolRprogressBar->setValue(master.volume[1]);
|
this->_ui.mvolRprogressBar->setValue(master.volume[1]);
|
||||||
|
|||||||
@@ -19,10 +19,9 @@ namespace ComSquare
|
|||||||
cartridge(new Cartridge::Cartridge(romPath)),
|
cartridge(new Cartridge::Cartridge(romPath)),
|
||||||
wram(new Ram::Ram(16384, WRam, "WRam")),
|
wram(new Ram::Ram(16384, WRam, "WRam")),
|
||||||
sram(new Ram::Ram(this->cartridge->header.sramSize, SRam, "SRam")),
|
sram(new Ram::Ram(this->cartridge->header.sramSize, SRam, "SRam")),
|
||||||
apuRam(new APU::MemoryMap()),
|
|
||||||
cpu(new CPU::CPU(this->_bus, cartridge->header)),
|
cpu(new CPU::CPU(this->_bus, cartridge->header)),
|
||||||
ppu(new PPU::PPU(renderer)),
|
ppu(new PPU::PPU(renderer)),
|
||||||
apu(new APU::APU(this->apuRam, renderer))
|
apu(new APU::APU(renderer))
|
||||||
{
|
{
|
||||||
this->_bus->mapComponents(*this);
|
this->_bus->mapComponents(*this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ namespace ComSquare
|
|||||||
std::shared_ptr<Ram::Ram> wram;
|
std::shared_ptr<Ram::Ram> wram;
|
||||||
//! @brief Save Ram residing inside the Cartridge in a real SNES.
|
//! @brief Save Ram residing inside the Cartridge in a real SNES.
|
||||||
std::shared_ptr<Ram::Ram> sram;
|
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.
|
//! @brief Central Processing Unit of the SNES.
|
||||||
std::shared_ptr<CPU::CPU> cpu;
|
std::shared_ptr<CPU::CPU> cpu;
|
||||||
//! @brief Picture Processing Unit of the SNES
|
//! @brief Picture Processing Unit of the SNES
|
||||||
|
|||||||
Reference in New Issue
Block a user