diff --git a/Components/KonamiSCC/KonamiSCC.cpp b/Components/KonamiSCC/KonamiSCC.cpp new file mode 100644 index 000000000..dc7187df8 --- /dev/null +++ b/Components/KonamiSCC/KonamiSCC.cpp @@ -0,0 +1,38 @@ +// +// KonamiSCC.cpp +// Clock Signal +// +// Created by Thomas Harte on 06/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "KonamiSCC.hpp" + +#include + +using namespace Konami; + +SCC::SCC(Concurrency::DeferringAsyncTaskQueue &task_queue) : + task_queue_(task_queue) {} + +bool SCC::is_silent() { + return !(channel_enable_ & 0x1f); +} + +void SCC::get_samples(std::size_t number_of_samples, std::int16_t *target) { + if(is_silent()) { + std::memset(target, 0, sizeof(std::int16_t) * number_of_samples); + return; + } + + // TODO +} + +void SCC::write(uint16_t address, uint8_t value) { + // TODO +} + +uint8_t SCC::read(uint16_t address) { + // TODO + return 0xff; +} diff --git a/Components/KonamiSCC/KonamiSCC.hpp b/Components/KonamiSCC/KonamiSCC.hpp new file mode 100644 index 000000000..0bcd7b51b --- /dev/null +++ b/Components/KonamiSCC/KonamiSCC.hpp @@ -0,0 +1,59 @@ +// +// KonamiSCC.hpp +// Clock Signal +// +// Created by Thomas Harte on 06/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef KonamiSCC_hpp +#define KonamiSCC_hpp + +#include "../../Outputs/Speaker/Implementation/SampleSource.hpp" +#include "../../Concurrency/AsyncTaskQueue.hpp" + +namespace Konami { + +/*! + Provides an emulation of Konami's Sound Creative Chip ('SCC'). + + The SCC is a primitive wavetable synthesis chip, offering 32-sample tables, + and five channels of output. The original SCC uses the same wave for channels + four and five, the SCC+ supports different waves for the two channels. +*/ +class SCC: public ::Outputs::Speaker::SampleSource { + public: + /// Creates a new SCC. + SCC(Concurrency::DeferringAsyncTaskQueue &task_queue); + + /// As per ::SampleSource; provides a broadphase test for silence. + bool is_silent(); + + /// As per ::SampleSource; provides audio output. + void get_samples(std::size_t number_of_samples, std::int16_t *target); + + /// Writes to the SCC. + void write(uint16_t address, uint8_t value); + + /// Reads from the SCC. + uint8_t read(uint16_t address); + + private: + struct Channel { + int period = 0; + int amplitude = 0; + } channels_[5]; + + struct Wavetable { + std::int16_t samples[32]; + } waves_[4]; + + std::uint8_t channel_enable_ = 0; + std::uint8_t test_register_ = 0; + + Concurrency::DeferringAsyncTaskQueue &task_queue_; +}; + +} + +#endif /* KonamiSCC_hpp */ diff --git a/Machines/MSX/Cartridges/KonamiWithSCC.hpp b/Machines/MSX/Cartridges/KonamiWithSCC.hpp index f57d33249..d640b4d70 100644 --- a/Machines/MSX/Cartridges/KonamiWithSCC.hpp +++ b/Machines/MSX/Cartridges/KonamiWithSCC.hpp @@ -10,16 +10,17 @@ #define KonamiWithSCC_hpp #include "../ROMSlotHandler.hpp" +#include "../../../Components/KonamiSCC/KonamiSCC.hpp" namespace MSX { namespace Cartridge { class KonamiWithSCCROMSlotHandler: public ROMSlotHandler { public: - KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot) : - map_(map), slot_(slot) {} + KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot, Konami::SCC &scc) : + map_(map), slot_(slot), scc_(scc) {} - void write(uint16_t address, uint8_t value) { + void write(uint16_t address, uint8_t value) override { switch(address >> 11) { default: break; case 0x0a: @@ -29,7 +30,16 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler { map_.map(slot_, value * 8192, 0x6000, 0x2000); break; case 0x12: - map_.map(slot_, value * 8192, 0x8000, 0x2000); + if((value&0x3f) == 0x3f) { + scc_is_visible_ = true; + map_.unmap(slot_, 0x8000, 0x2000); + } else { + scc_is_visible_ = false; + map_.map(slot_, value * 8192, 0x8000, 0x2000); + } + break; + case 0x13: + if(scc_is_visible_) scc_.write(address, value); break; case 0x16: map_.map(slot_, value * 8192, 0xa000, 0x2000); @@ -37,9 +47,18 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler { } } + uint8_t read(uint16_t address) override { + if(scc_is_visible_ && address >= 0x9800 && address < 0xa000) { + return scc_.read(address); + } + return 0xff; + } + private: MSX::MemoryMap &map_; int slot_; + Konami::SCC &scc_; + bool scc_is_visible_ = false; }; } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 5ba77f305..f3a493871 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -22,6 +22,7 @@ #include "../../Components/9918/9918.hpp" #include "../../Components/8255/i8255.hpp" #include "../../Components/AY38910/AY38910.hpp" +#include "../../Components/KonamiSCC/KonamiSCC.hpp" #include "../../Storage/Tape/Parsers/MSX.hpp" #include "../../Storage/Tape/Tape.hpp" @@ -120,7 +121,8 @@ class ConcreteMachine: i8255_(i8255_port_handler_), ay_(audio_queue_), audio_toggle_(audio_queue_), - mixer_(ay_, audio_toggle_), + scc_(audio_queue_), + mixer_(ay_, audio_toggle_, scc_), speaker_(mixer_), tape_player_(3579545 * 2), i8255_port_handler_(*this, audio_toggle_, tape_player_), @@ -167,7 +169,7 @@ class ConcreteMachine: break; case StaticAnalyser::MSXCartridgeType::KonamiWithSCC: // TODO: enable an SCC. - memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1)); + memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1, scc_)); break; case StaticAnalyser::MSXCartridgeType::ASCII8kb: memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1)); @@ -317,6 +319,7 @@ class ConcreteMachine: int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; if(memory_slots_[slot_hit].handler) { + update_audio(); memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); memory_slots_[slot_hit].handler->write(address, *cycle.value); } @@ -568,8 +571,9 @@ class ConcreteMachine: Concurrency::DeferringAsyncTaskQueue audio_queue_; GI::AY38910::AY38910 ay_; AudioToggle audio_toggle_; - Outputs::Speaker::CompoundSource mixer_; - Outputs::Speaker::LowpassSpeaker> speaker_; + Konami::SCC scc_; + Outputs::Speaker::CompoundSource mixer_; + Outputs::Speaker::LowpassSpeaker> speaker_; Storage::Tape::BinaryTapePlayer tape_player_; bool use_fast_tape_ = false; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3ac05131c..b5f02182b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -205,6 +205,8 @@ 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; }; 4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; }; 4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; }; + 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; + 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; }; 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; }; 4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */; }; @@ -797,6 +799,8 @@ 4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = ""; }; 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AY38910.cpp; path = AY38910/AY38910.cpp; sourceTree = ""; }; 4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AY38910.hpp; path = AY38910/AY38910.hpp; sourceTree = ""; }; + 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KonamiSCC.cpp; sourceTree = ""; }; + 4B4B1A3B200198C900A0F866 /* KonamiSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KonamiSCC.hpp; sourceTree = ""; }; 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Vic20.cpp; sourceTree = ""; }; 4B4DC8201D2C2425003C5BF8 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Vic20.hpp; sourceTree = ""; }; 4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = ""; }; @@ -1801,6 +1805,15 @@ name = AY38910; sourceTree = ""; }; + 4B4B1A39200198C900A0F866 /* KonamiSCC */ = { + isa = PBXGroup; + children = ( + 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */, + 4B4B1A3B200198C900A0F866 /* KonamiSCC.hpp */, + ); + path = KonamiSCC; + sourceTree = ""; + }; 4B4DC81D1D2C2425003C5BF8 /* Commodore */ = { isa = PBXGroup; children = ( @@ -2607,11 +2620,12 @@ 4BC9DF4B1D04691600F44158 /* 6522 */, 4B1E85791D174DEC001EF87D /* 6532 */, 4BC9DF4C1D04691600F44158 /* 6560 */, - 4B4A762D1DB1A35C007AAE2E /* AY38910 */, 4BE845221F2FF7F400A5EA22 /* 6845 */, 4BD9137C1F3115AC009BCF85 /* 8255 */, 4BBC951F1F368D87008F4C34 /* 8272 */, 4B0E04F71FC9F2C800F43484 /* 9918 */, + 4B4A762D1DB1A35C007AAE2E /* AY38910 */, + 4B4B1A39200198C900A0F866 /* KonamiSCC */, ); name = Components; path = ../../Components; @@ -3319,6 +3333,7 @@ 4B055AA31FAE85DF0060FFFF /* ImplicitSectors.cpp in Sources */, 4B055AAE1FAE85FD0060FFFF /* TrackSerialiser.cpp in Sources */, 4B055A981FAE85C50060FFFF /* Drive.cpp in Sources */, + 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4B055AE21FAE9B6F0060FFFF /* CRTOpenGL.cpp in Sources */, 4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */, 4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */, @@ -3445,6 +3460,7 @@ 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, 4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */, + 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */, 4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */, 4B4518811F75E91A00926311 /* PCMPatchedTrack.cpp in Sources */, diff --git a/OSBindings/SDL/SConstruct b/OSBindings/SDL/SConstruct index c97ce506a..3219db008 100644 --- a/OSBindings/SDL/SConstruct +++ b/OSBindings/SDL/SConstruct @@ -17,6 +17,7 @@ SOURCES += glob.glob('../../Components/8272/*.cpp') SOURCES += glob.glob('../../Components/9918/*.cpp') SOURCES += glob.glob('../../Components/9918/Implementation/*.cpp') SOURCES += glob.glob('../../Components/AY38910/*.cpp') +SOURCES += glob.glob('../../Components/KonamiSCC/*.cpp') SOURCES += glob.glob('../../Concurrency/*.cpp')