diff --git a/Analyser/Static/Acorn/Target.hpp b/Analyser/Static/Acorn/Target.hpp index 1303784b7..273c31839 100644 --- a/Analyser/Static/Acorn/Target.hpp +++ b/Analyser/Static/Acorn/Target.hpp @@ -44,7 +44,7 @@ struct BBCMicroTarget: public ::Analyser::Static::Target, public Reflection::Str bool has_1770dfs = false; bool has_adfs = false; bool has_sideways_ram = true; - bool has_beebsid = true; + bool has_beebsid = true; // TODO: false is a much better default. ReflectableEnum(TubeProcessor, None, WDC65C02, Z80); TubeProcessor tube_processor = TubeProcessor::None; diff --git a/Components/SID/SID.cpp b/Components/SID/SID.cpp index 8f65af947..6a0b39c3c 100644 --- a/Components/SID/SID.cpp +++ b/Components/SID/SID.cpp @@ -12,6 +12,30 @@ using namespace MOS::SID; SID::SID(Concurrency::AsyncTaskQueue &audio_queue) : audio_queue_(audio_queue) {} + +void SID::write(const Numeric::SizedInt<5> address, const uint8_t value) { + switch(address.get()) { + case 0x00: case 0x07: case 0x0e: voices_[address.get() / 7].frequency.load<0>(value); break; + case 0x01: case 0x08: case 0x0f: voices_[address.get() / 7].frequency.load<8>(value); break; + case 0x02: case 0x09: case 0x10: voices_[address.get() / 7].pulse_width.load<0>(value); break; + case 0x03: case 0x0a: case 0x11: voices_[address.get() / 7].pulse_width.load<8>(value); break; + case 0x04: case 0x0b: case 0x12: voices_[address.get() / 7].control = value; break; + case 0x05: case 0x0c: case 0x13: + voices_[address.get() / 7].attack = value >> 4; + voices_[address.get() / 7].decay = value; + break; + case 0x06: case 0x0d: case 0x14: + voices_[address.get() / 7].sustain = value >> 4; + voices_[address.get() / 7].release = value; + break; + } +} + +uint8_t SID::read(const Numeric::SizedInt<5> address) { + (void)address; + return 0xff; +} + void SID::set_sample_volume_range(const std::int16_t range) { (void)range; } diff --git a/Components/SID/SID.hpp b/Components/SID/SID.hpp index 775d708b6..7a1ccf2ae 100644 --- a/Components/SID/SID.hpp +++ b/Components/SID/SID.hpp @@ -8,6 +8,8 @@ #pragma once +#include "Numeric/SizedInt.hpp" + #include "Concurrency/AsyncTaskQueue.hpp" #include "Outputs/Speaker/Implementation/BufferSource.hpp" @@ -17,6 +19,10 @@ class SID: public Outputs::Speaker::BufferSource { public: SID(Concurrency::AsyncTaskQueue &audio_queue); + void write(Numeric::SizedInt<5> address, uint8_t value); + uint8_t read(Numeric::SizedInt<5> address); + + // Outputs::Speaker::BufferSource. template void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); bool is_zero_level() const; @@ -24,6 +30,18 @@ public: private: Concurrency::AsyncTaskQueue &audio_queue_; + + struct Voice { + Numeric::SizedInt<16> frequency; + Numeric::SizedInt<12> pulse_width; + Numeric::SizedInt<8> control; + Numeric::SizedInt<4> attack; + Numeric::SizedInt<4> decay; + Numeric::SizedInt<4> sustain; + Numeric::SizedInt<4> release; + }; + + Voice voices_[3]; }; } diff --git a/Machines/Acorn/BBCMicro/BBCMicro.cpp b/Machines/Acorn/BBCMicro/BBCMicro.cpp index 48daa3d4a..7e043a2e1 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.cpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.cpp @@ -109,6 +109,8 @@ private: /*! Combines an SN76489 with an appropriate asynchronous queue and filtering speaker. */ + +// TODO: generalise the below and clean up across the project. template struct Audio { private: @@ -147,7 +149,7 @@ public: return sn76489_; } if constexpr (std::is_same_v) { - return sn76489_; + return sid_; } } @@ -1027,6 +1029,12 @@ public: } break; } + } else if(has_beebsid && address >= 0xfc20 && address < 0xfc40) { + if constexpr (is_read(operation)) { + value = audio_.template get().read(+address); + } else { + audio_.template get().write(+address, value); + } } else { Logger::error() .append("Unhandled IO %s at %04x", is_read(operation) ? "read" : "write", address)