From b461ffe41141f4b90ee4f40899bc87350783cf56 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 16 Sep 2022 23:48:06 +0900 Subject: [PATCH 01/16] Update vgsound_emu library --- CMakeLists.txt | 37 +- src/engine/platform/bubsyswsg.cpp | 12 +- src/engine/platform/bubsyswsg.h | 4 +- src/engine/platform/msm6295.cpp | 14 +- src/engine/platform/msm6295.h | 4 +- src/engine/platform/n163.cpp | 2 +- src/engine/platform/n163.h | 2 +- src/engine/platform/scc.cpp | 2 +- src/engine/platform/scc.h | 2 +- src/engine/platform/sound/k005289/k005289.cpp | 46 -- src/engine/platform/sound/k005289/k005289.hpp | 68 -- src/engine/platform/sound/n163/n163.cpp | 167 ----- src/engine/platform/sound/n163/n163.hpp | 90 --- src/engine/platform/sound/oki/msm6295.cpp | 215 ------ src/engine/platform/sound/oki/msm6295.hpp | 95 --- src/engine/platform/sound/oki/util.hpp | 159 ----- src/engine/platform/sound/oki/vox.hpp | 116 ---- src/engine/platform/sound/scc/scc.cpp | 617 ------------------ src/engine/platform/sound/scc/scc.hpp | 139 ---- src/engine/platform/sound/vrcvi/vrcvi.cpp | 324 --------- src/engine/platform/sound/vrcvi/vrcvi.hpp | 242 ------- src/engine/platform/sound/x1_010/x1_010.cpp | 225 ------- src/engine/platform/sound/x1_010/x1_010.hpp | 134 ---- src/engine/platform/vrc6.cpp | 7 +- src/engine/platform/vrc6.h | 7 +- src/engine/platform/x1_010.cpp | 28 +- src/engine/platform/x1_010.h | 23 +- src/main.cpp | 2 +- 28 files changed, 78 insertions(+), 2705 deletions(-) delete mode 100644 src/engine/platform/sound/k005289/k005289.cpp delete mode 100644 src/engine/platform/sound/k005289/k005289.hpp delete mode 100644 src/engine/platform/sound/n163/n163.cpp delete mode 100644 src/engine/platform/sound/n163/n163.hpp delete mode 100644 src/engine/platform/sound/oki/msm6295.cpp delete mode 100644 src/engine/platform/sound/oki/msm6295.hpp delete mode 100644 src/engine/platform/sound/oki/util.hpp delete mode 100644 src/engine/platform/sound/oki/vox.hpp delete mode 100644 src/engine/platform/sound/scc/scc.cpp delete mode 100644 src/engine/platform/sound/scc/scc.hpp delete mode 100644 src/engine/platform/sound/vrcvi/vrcvi.cpp delete mode 100644 src/engine/platform/sound/vrcvi/vrcvi.hpp delete mode 100644 src/engine/platform/sound/x1_010/x1_010.cpp delete mode 100644 src/engine/platform/sound/x1_010/x1_010.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7db42e..39d3321c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ else() endif() list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/SAASound/include") +list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/vgsound_emu-modified") find_package(Threads REQUIRED) list(APPEND DEPENDENCIES_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) @@ -319,6 +320,31 @@ extern/SAASound/src/SAANoise.cpp extern/SAASound/src/SAASndC.cpp extern/SAASound/src/SAASound.cpp +extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp + extern/adpcm/bs_codec.c extern/adpcm/oki_codec.c extern/adpcm/yma_codec.c @@ -398,28 +424,17 @@ src/engine/platform/sound/lynx/Mikey.cpp src/engine/platform/sound/qsound.c -src/engine/platform/sound/x1_010/x1_010.cpp - src/engine/platform/sound/swan.cpp src/engine/platform/sound/su.cpp -src/engine/platform/sound/k005289/k005289.cpp - -src/engine/platform/sound/n163/n163.cpp - src/engine/platform/sound/vic20sound.c -src/engine/platform/sound/vrcvi/vrcvi.cpp - -src/engine/platform/sound/scc/scc.cpp - src/engine/platform/sound/ymz280b.cpp src/engine/platform/sound/rf5c68.cpp src/engine/platform/sound/oki/okim6258.cpp -src/engine/platform/sound/oki/msm6295.cpp src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 89503cce..89d609ce 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_ for (size_t h=start; htick(); + k005289.tick(); // Wavetable part for (int i=0; i<2; i++) { @@ -52,7 +52,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_ oscBuf[i]->data[oscBuf[i]->needle++]=0; continue; } else { - chanOut=chan[i].waveROM[k005289->addr(i)]*(regPool[2+i]&0xf); + chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf); out+=chanOut; if (writeOscBuf==0) { oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7; @@ -122,9 +122,9 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; - k005289->load(i,chan[i].freq); + k005289.load(i,chan[i].freq); rWrite(i,chan[i].freq); - k005289->update(i); + k005289.update(i); if (chan[i].keyOn) { // ??? } @@ -295,7 +295,7 @@ void DivPlatformBubSysWSG::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } - k005289->reset(); + k005289.reset(); } bool DivPlatformBubSysWSG::isStereo() { @@ -347,7 +347,6 @@ int DivPlatformBubSysWSG::init(DivEngine* p, int channels, int sugRate, unsigned oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); - k005289=new k005289_core(); reset(); return 2; } @@ -356,7 +355,6 @@ void DivPlatformBubSysWSG::quit() { for (int i=0; i<2; i++) { delete oscBuf[i]; } - delete k005289; } DivPlatformBubSysWSG::~DivPlatformBubSysWSG() { diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index cfc41dc1..f14b94a7 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -24,7 +24,7 @@ #include #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/k005289/k005289.hpp" +#include "vgsound_emu/src/k005289/k005289.hpp" class DivPlatformBubSysWSG: public DivDispatch { struct Channel { @@ -60,7 +60,7 @@ class DivPlatformBubSysWSG: public DivDispatch { bool isMuted[2]; unsigned char writeOscBuf; - k005289_core* k005289; + k005289_core k005289; unsigned short regPool[4]; void updateWave(int ch); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 0a76a2c9..4d32f431 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -79,7 +79,7 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t updateOsc=0; // TODO: per-channel osc for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=msm.m_voice[i].m_muted?0:(msm.m_voice[i].m_out<<6); + oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<6; } } } @@ -113,7 +113,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - rWriteDelay(0,(8<getSample(12*sampleBank+c.value%12); chan[c.chan].sample=12*sampleBank+c.value%12; - rWriteDelay(0,(8<rate=rate/22; } diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index 0953bd33..33f5c85f 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -22,7 +22,7 @@ #include "../dispatch.h" #include "../macroInt.h" #include -#include "sound/oki/msm6295.hpp" +#include "vgsound_emu/src/msm6295/msm6295.hpp" class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { protected: @@ -57,7 +57,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { unsigned short addr; unsigned char val; unsigned short delay; - QueuedWrite(unsigned short a, unsigned char v, unsigned short d=32): + QueuedWrite(unsigned short a, unsigned char v, unsigned short d=96): addr(a), val(v), delay(d) {} diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index bc985b42..1597d0fa 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -117,7 +117,7 @@ void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len bufL[i]=bufR[i]=out; if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=n163.chan_out(i)<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7; } // command queue diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index c77510f5..d4a9ee35 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -24,7 +24,7 @@ #include #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/n163/n163.hpp" +#include "vgsound_emu/src/n163/n163.hpp" class DivPlatformN163: public DivDispatch { struct Channel { diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index d8859cff..725c53da 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -89,7 +89,7 @@ void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) bufL[h]=bufR[h]=out; for (int i=0; i<5; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=scc->chan_out(i)<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7; } } } diff --git a/src/engine/platform/scc.h b/src/engine/platform/scc.h index f9fa3172..b6117d4a 100644 --- a/src/engine/platform/scc.h +++ b/src/engine/platform/scc.h @@ -24,7 +24,7 @@ #include #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/scc/scc.hpp" +#include "vgsound_emu/src/scc/scc.hpp" class DivPlatformSCC: public DivDispatch { struct Channel { diff --git a/src/engine/platform/sound/k005289/k005289.cpp b/src/engine/platform/sound/k005289/k005289.cpp deleted file mode 100644 index c9bdf83a..00000000 --- a/src/engine/platform/sound/k005289/k005289.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900 - Konami K005289 emulation core - - This chip is used at infamous Konami Bubble System, for part of Wavetable sound generator. - But seriously, It is just to 2 internal 12 bit timer and address generators, rather than sound generator. - - Everything except for internal counter and address are done by external logic, the chip is only has external address, frequency registers and its update pins. - - Frequency calculation: Input clock / (4096 - Pitch input) -*/ - -#include "k005289.hpp" - -void k005289_core::tick() -{ - for (auto & elem : m_voice) - elem.tick(); -} - -void k005289_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); -} - -void k005289_core::voice_t::tick() -{ - if (bitfield(++counter, 0, 12) == 0) - { - addr = bitfield(addr + 1, 0, 5); - counter = freq; - } -} - -void k005289_core::voice_t::reset() -{ - addr = 0; - pitch = 0; - freq = 0; - counter = 0; -} diff --git a/src/engine/platform/sound/k005289/k005289.hpp b/src/engine/platform/sound/k005289/k005289.hpp deleted file mode 100644 index 575a98b8..00000000 --- a/src/engine/platform/sound/k005289/k005289.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900 - Konami K005289 emulation core - - See k005289.cpp for more info. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_K005289_HPP -#define _VGSOUND_EMU_K005289_HPP - -#pragma once - -namespace k005289 -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef signed short s16; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -} - -using namespace k005289; -class k005289_core -{ -public: - // accessors, getters, setters - u8 addr(int voice) { return m_voice[voice & 1].addr; } // 1QA...E/2QA...E pin - void load(int voice, u16 addr) { m_voice[voice & 1].load(addr); } // LD1/2 pin, A0...11 pin - void update(int voice) { m_voice[voice & 1].update(); } // TG1/2 pin - - // internal state - void reset(); - void tick(); - -private: - // k005289 voice structs - struct voice_t - { - // internal state - void reset(); - void tick(); - - // accessors, getters, setters - void load(u16 addr) { pitch = addr; } // Load pitch data (address pin) - void update() { freq = pitch; } // Replace current frequency to lastest loaded pitch - - // registers - u8 addr = 0; // external address pin - u16 pitch = 0; // pitch - u16 freq = 0; // current frequency - s16 counter = 0; // frequency counter - }; - - voice_t m_voice[2]; -}; - -#endif diff --git a/src/engine/platform/sound/n163/n163.cpp b/src/engine/platform/sound/n163/n163.cpp deleted file mode 100644 index 3fd30bc4..00000000 --- a/src/engine/platform/sound/n163/n163.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Namco 163 Sound emulation core - - This chip is one of NES mapper with sound expansion, This one is by Namco. - - It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM. - 4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM. - - But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels. - - Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality. - - Sound register layout - - Address Bit Description - 7654 3210 - - 78-7f Channel 0 - 78 xxxx xxxx Channel 0 Pitch input bit 0-7 - 79 xxxx xxxx Channel 0 Accumulator bit 0-7* - 7a xxxx xxxx Channel 0 Pitch input bit 8-15 - 7b xxxx xxxx Channel 0 Accumulator bit 8-15* - 7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4) - ---- --xx Channel 0 Pitch input bit 16-17 - 7d xxxx xxxx Channel 0 Accumulator bit 16-23* - 7e xxxx xxxx Channel 0 Waveform base offset - xxxx xxx- RAM byte (0 to 127) - ---- ---x RAM nibble - ---- ---0 Low nibble - ---- ---1 High nibble - 7f ---- xxxx Channel 0 Volume - - 7f Number of active channels - 7f -xxx ---- Number of active channels - -000 ---- Channel 0 activated - -001 ---- Channel 1 activated - -010 ---- Channel 2 activated - ... - -110 ---- Channel 6 activated - -111 ---- Channel 7 activated - - 70-77 Channel 1 (Optional if activated) - 68-6f Channel 2 (Optional if activated) - ... - 48-4f Channel 6 (Optional if activated) - 40-47 Channel 7 (Optional if activated) - - Rest of RAM area are for 4 bit Waveform and/or scratchpad. - Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next. - ---- xxxx 4 bit waveform, LSB - xxxx ---- Same as above, MSB - - Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM. - - Frequency formula: - Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536) - - There's to way for reduce N163 noises: reduce channel limit and demultiplex - - Channel limit is runtime changeable and it makes some usable effects. - - Demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered) - This core is support both, You can choose output behavior - -*/ - -#include "n163.hpp" -#include - -void n163_core::tick() -{ - if (m_multiplex) - m_out = 0; - // 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.) - if (m_disable) - { - if (!m_multiplex) - m_out = 0; - return; - } - - // tick per each clock - const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency - u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | ( u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator - const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc); - const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8); - const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8); - const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4); - - // accumulate address - accum = bitfield(accum + freq, 0, 24); - if (bitfield(accum, 16, 8) >= length) - accum = bitfield(accum, 0, 18); - - // writeback to register - m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8); - m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8); - m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8); - - const u8 prev_voice_cycle = m_voice_cycle; - - // update voice cycle - bool flush = m_multiplex ? true : false; - m_voice_cycle -= 0x8; - if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3))) - { - if (!m_multiplex) - flush = true; - m_voice_cycle = 0x78; - } - - // output 4 bit waveform and volume, multiplexed - const u8 chan_index = ((0x78-prev_voice_cycle)>>3)&7; - m_ch_out[chan_index]=wave * volume; - m_acc += m_ch_out[chan_index]; - if (flush) - { - m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1)); - m_acc = 0; - } -} - -void n163_core::reset() -{ - // reset this chip - m_disable = false; - m_multiplex = true; - memset(m_ram,0,sizeof(m_ram)); - m_voice_cycle = 0x78; - m_addr_latch.reset(); - m_out = 0; - m_acc = 0; - memset(m_ch_out,0,sizeof(m_ch_out)); -} - -// accessor -void n163_core::addr_w(u8 data) -{ - // 0xf800-0xffff Sound address, increment - m_addr_latch.addr = bitfield(data, 0, 7); - m_addr_latch.incr = bitfield(data, 7); -} - -void n163_core::data_w(u8 data, bool cpu_access) -{ - // 0x4800-0x4fff Sound data write - m_ram[m_addr_latch.addr] = data; - - // address latch increment - if (cpu_access && m_addr_latch.incr) - m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7); -} - -u8 n163_core::data_r(bool cpu_access) -{ - // 0x4800-0x4fff Sound data read - const u8 ret = m_ram[m_addr_latch.addr]; - - // address latch increment - if (cpu_access && m_addr_latch.incr) - m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7); - - return ret; -} diff --git a/src/engine/platform/sound/n163/n163.hpp b/src/engine/platform/sound/n163/n163.hpp deleted file mode 100644 index 317a33b4..00000000 --- a/src/engine/platform/sound/n163/n163.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Namco 163 Sound emulation core -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_N163_HPP -#define _VGSOUND_EMU_N163_HPP - -#pragma once - -namespace n163 -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - typedef signed short s16; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -}; - -using namespace n163; -class n163_core -{ -public: - // accessors, getters, setters - void addr_w(u8 data); - void data_w(u8 data, bool cpu_access = false); - u8 data_r(bool cpu_access = false); - - void set_disable(bool disable) { m_disable = disable; } - - // internal state - void reset(); - void tick(); - - // sound output pin - s16 out() { return m_out; } - - // get channel output - s16 chan_out(u8 ch) { return m_ch_out[ch]; } - - // get voice cycle - u8 voice_cycle() { return m_voice_cycle; } - - // register pool - u8 reg(u8 addr) { return m_ram[addr & 0x7f]; } - void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; } - -private: - // Address latch - struct addr_latch_t - { - addr_latch_t() - : addr(0) - , incr(0) - { } - - void reset() - { - addr = 0; - incr = 0; - } - - u8 addr : 7; - u8 incr : 1; - }; - - bool m_disable = false; - u8 m_ram[0x80] = {0}; // internal 128 byte RAM - u8 m_voice_cycle = 0x78; // Voice cycle for processing - addr_latch_t m_addr_latch; // address latch - s16 m_out = 0; // output - s16 m_ch_out[8] = {0}; // per channel output - // demultiplex related - bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate! - s16 m_acc = 0; // accumulated output -}; - -#endif diff --git a/src/engine/platform/sound/oki/msm6295.cpp b/src/engine/platform/sound/oki/msm6295.cpp deleted file mode 100644 index e7f39d27..00000000 --- a/src/engine/platform/sound/oki/msm6295.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - OKI MSM6295 emulation core - - It is 4 channel ADPCM playback chip from OKI semiconductor. - It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance. - - The chip itself is pretty barebone: there is no "register" in chip. - It can't control volume and pitch in currently playing channels, only stopable them. - And volume is must be determined at playback start command. - - Command format: - - Playback command (2 byte): - - Byte Bit Description - 76543210 - 0 1xxxxxxx Phrase select (Header stored in ROM) - 1 x000---- Play channel 4 - 0x00---- Play channel 3 - 00x0---- Play channel 2 - 000x---- Play channel 1 - ----xxxx Volume - ----0000 0.0dB - ----0001 -3.2dB - ----0010 -6.0dB - ----0011 -9.2dB - ----0100 -12.0dB - ----0101 -14.5dB - ----0110 -18.0dB - ----0111 -20.5dB - ----1000 -24.0dB - - Suspend command (1 byte): - - Byte Bit Description - 76543210 - 0 0x------ Suspend channel 4 - 0-x----- Suspend channel 3 - 0--x---- Suspend channel 2 - 0---x--- Suspend channel 1 - - Frequency calculation: - if (SS) then - Frequency = Input clock / 165 - else then - Frequency = Input clock / 132 - -*/ - -#include "msm6295.hpp" - -#define CORE_DIVIDER 3 - -#define CHANNEL_DELAY (15/CORE_DIVIDER) -#define MASTER_DELAY (33/CORE_DIVIDER) - -void msm6295_core::tick() -{ - // command handler - if (m_command_pending) - { - if (bitfield(m_command, 7)) // play voice - { - if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4)))) - { - m_clock = 0; - if (bitfield(m_next_command, 4, 4) != 0) - { - for (int i = 0; i < 4; i++) - { - if (bitfield(m_next_command, 4 + i)) - { - if (!m_voice[i].m_busy) - { - m_voice[i].m_command = m_command; - m_voice[i].m_volume = (bitfield(m_next_command, 0, 4) < 9) ? m_volume_table[std::min(8, bitfield(m_next_command, 0, 4))] : 0; - } - break; // voices aren't be playable simultaneously at once - } - } - } - m_command = 0; - m_command_pending = false; - } - } - else if (bitfield(m_next_command, 7)) // select phrase - { - if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4)))) - { - m_clock = 0; - m_command = m_next_command; - m_command_pending = false; - } - } - else - { - if (bitfield(m_next_command, 3, 4) != 0) // suspend voices - { - for (int i = 0; i < 4; i++) - { - if (bitfield(m_next_command, 3 + i)) - { - if (m_voice[i].m_busy) - m_voice[i].m_command = m_next_command; - } - } - m_next_command &= ~0x78; - } - m_command_pending = false; - } - } - m_out = 0; - for (int i = 0; i < 4; i++) - { - m_voice[i].tick(); - if (!m_voice[i].m_muted) m_out += m_voice[i].m_out; - } -} - -void msm6295_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); - - m_command = 0; - m_next_command = 0; - m_command_pending = false; - m_clock = 0; - m_out = 0; -} - -void msm6295_core::voice_t::tick() -{ - if (!m_busy) - { - if (bitfield(m_command, 7)) - { - // get phrase header (stored in data memory) - const u32 phrase = bitfield(m_command, 0, 7) << 3; - // Start address - m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16) - | (m_host.m_intf.read_byte(phrase | 1) << 8) - | (m_host.m_intf.read_byte(phrase | 2) << 0); - // End address - m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16) - | (m_host.m_intf.read_byte(phrase | 4) << 8) - | (m_host.m_intf.read_byte(phrase | 5) << 0); - m_nibble = 4; // MSB first, LSB second - m_command = 0; - m_busy = true; - vox_decoder_t::reset(); - } - m_out = 0; - } - else - { - // playback - if ((++m_clock) >= ((MASTER_DELAY * (m_host.m_ss ? 5 : 4)))) - { - m_clock = 0; - bool is_end = (m_command != 0); - m_curr.decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4)); - if (m_nibble <= 0) - { - m_nibble = 4; - if (++m_addr > m_end) - is_end = true; - } - else - m_nibble -= 4; - if (is_end) - { - m_command = 0; - m_busy = false; - } - m_out = (out() * m_volume) >> 7; // scale out to 12 bit output - } - } -} - -void msm6295_core::voice_t::reset() -{ - vox_decoder_t::reset(); - m_clock = 0; - m_busy = false; - m_command = 0; - m_addr = 0; - m_nibble = 0; - m_end = 0; - m_volume = 0; - m_out = 0; -} - -// accessors -u8 msm6295_core::busy_r() -{ - return (m_voice[0].m_busy ? 0x01 : 0x00) - | (m_voice[1].m_busy ? 0x02 : 0x00) - | (m_voice[2].m_busy ? 0x04 : 0x00) - | (m_voice[3].m_busy ? 0x08 : 0x00); -} - -void msm6295_core::command_w(u8 data) -{ - if (!m_command_pending) - { - m_next_command = data; - m_command_pending = true; - } -} diff --git a/src/engine/platform/sound/oki/msm6295.hpp b/src/engine/platform/sound/oki/msm6295.hpp deleted file mode 100644 index ca8b81d4..00000000 --- a/src/engine/platform/sound/oki/msm6295.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - OKI MSM6295 emulation core - - See msm6295.cpp for more info. -*/ - -#include "util.hpp" -#include "vox.hpp" -#include -#include - -#ifndef _VGSOUND_EMU_MSM6295_HPP -#define _VGSOUND_EMU_MSM6295_HPP - -#pragma once - -class msm6295_core : public vox_core -{ - friend class vgsound_emu_mem_intf; // common memory interface -public: - // constructor - msm6295_core(vgsound_emu_mem_intf &intf) - : m_voice{{*this,*this},{*this,*this},{*this,*this},{*this,*this}} - , m_intf(intf) - { - } - // accessors, getters, setters - u8 busy_r(); - void command_w(u8 data); - void ss_w(bool ss) { m_ss = ss; } // SS pin - - // internal state - void reset(); - void tick(); - - s32 out() { return m_out; } // built in 12 bit DAC - -private: - // Internal volume table, 9 step - const s32 m_volume_table[9] = { - 32/* 0.0dB */, - 22/* -3.2dB */, - 16/* -6.0dB */, - 11/* -9.2dB */, - 8/* -12.0dB */, - 6/* -14.5dB */, - 4/* -18.0dB */, - 3/* -20.5dB */, - 2/* -24.0dB */ }; // scale out to 5 bit for optimization - -public: - // msm6295 voice structs - struct voice_t : vox_decoder_t - { - // constructor - voice_t(vox_core &vox, msm6295_core &host) - : vox_decoder_t(vox) - , m_host(host) - {}; - - // internal state - virtual void reset() override; - void tick(); - - // accessors, getters, setters - // registers - msm6295_core &m_host; - u16 m_clock = 0; // clock counter - bool m_busy = false; // busy status - bool m_muted = false; // muted - u8 m_command = 0; // current command - u32 m_addr = 0; // current address - s8 m_nibble = 0; // current nibble - u32 m_end = 0; // end address - s32 m_volume = 0; // volume - s32 m_out = 0; // output - }; - voice_t m_voice[4]; -private: - vgsound_emu_mem_intf &m_intf; // common memory interface - - bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4) - u8 m_command = 0; // Command byte - u8 m_next_command = 0; // Next command - bool m_command_pending = false; // command pending flag - u16 m_clock = 0; // clock counter - s32 m_out = 0; // 12 bit output -}; - -#endif diff --git a/src/engine/platform/sound/oki/util.hpp b/src/engine/platform/sound/oki/util.hpp deleted file mode 100644 index b9c50d7b..00000000 --- a/src/engine/platform/sound/oki/util.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - Various core utilities for vgsound_emu -*/ - -#include -#include -#include - -#ifndef _VGSOUND_EMU_CORE_UTIL_HPP -#define _VGSOUND_EMU_CORE_UTIL_HPP - -#pragma once - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; -typedef float f32; -typedef double f64; - -const f64 PI = 3.1415926535897932384626433832795; - -// get bitfield, bitfield(input, position, len) -template T bitfield(T in, u8 pos, u8 len = 1) -{ - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); -} - -// get sign extended value, sign_ext(input, len) -template T sign_ext(T in, u8 len) -{ - len = std::max(0, (8 * sizeof(T)) - len); - return T(T(in) << len) >> len; -} - -// convert attenuation decibel value to gain -inline f32 dB_to_gain(f32 attenuation) -{ - return powf(10.0f, attenuation / 20.0f); -} - -class vgsound_emu_mem_intf -{ -public: - virtual u8 read_byte(u32 address) { return 0; } - virtual u16 read_word(u32 address) { return 0; } - virtual u32 read_dword(u32 address) { return 0; } - virtual u64 read_qword(u32 address) { return 0; } - virtual void write_byte(u32 address, u8 data) { } - virtual void write_word(u32 address, u16 data) { } - virtual void write_dword(u32 address, u32 data) { } - virtual void write_qword(u32 address, u64 data) { } -}; - -template -struct clock_pulse_t -{ - void reset(T init = InitWidth) - { - m_edge.reset(); - m_width = m_width_latch = m_counter = init; - m_cycle = 0; - } - - bool tick(T width = 0) - { - bool carry = ((--m_counter) <= 0); - if (carry) - { - if (!width) - m_width = m_width_latch; - else - m_width = width; // reset width - m_counter = m_width; - m_cycle = 0; - } - else - m_cycle++; - - m_edge.tick(carry); - return carry; - } - - void set_width(T width) { m_width = width; } - void set_width_latch(T width) { m_width_latch = width; } - - // Accessors - bool current_edge() { return m_edge.m_current; } - bool rising_edge() { return m_edge.m_rising; } - bool falling_edge() { return m_edge.m_rising; } - T cycle() { return m_cycle; } - - struct edge_t - { - edge_t() - : m_current(InitEdge ^ 1) - , m_previous(InitEdge) - , m_rising(0) - , m_falling(0) - , m_changed(0) - { - set(InitEdge); - } - - void tick(bool toggle) - { - u8 current = m_current; - if (toggle) - current ^= 1; - set(current); - } - - void set(u8 edge) - { - edge &= 1; - m_rising = m_falling = m_changed = 0; - if (m_current != edge) - { - m_changed = 1; - if (m_current && (!edge)) - m_falling = 1; - else if ((!m_current) && edge) - m_rising = 1; - m_current = edge; - } - m_previous = m_current; - } - - void reset() - { - m_previous = InitEdge; - m_current = InitEdge ^ 1; - set(InitEdge); - } - - u8 m_current : 1; // current edge - u8 m_previous : 1; // previous edge - u8 m_rising : 1; // rising edge - u8 m_falling : 1; // falling edge - u8 m_changed : 1; // changed flag - }; - - edge_t m_edge; - T m_width = InitWidth; // clock pulse width - T m_width_latch = InitWidth; // clock pulse width latch - T m_counter = InitWidth; // clock counter - T m_cycle = 0; // clock cycle -}; - -#endif diff --git a/src/engine/platform/sound/oki/vox.hpp b/src/engine/platform/sound/oki/vox.hpp deleted file mode 100644 index 23fbfd78..00000000 --- a/src/engine/platform/sound/oki/vox.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - Dialogic ADPCM core -*/ - -#include "util.hpp" -#include -#include - -#ifndef _VGSOUND_EMU_CORE_VOX_HPP -#define _VGSOUND_EMU_CORE_VOX_HPP - -#pragma once - -#define MODIFIED_CLAMP(x,xMin,xMax) (std::min(std::max((x),(xMin)),(xMax))) - -class vox_core -{ -protected: - struct vox_decoder_t - { - vox_decoder_t(vox_core &vox) - : m_curr(vox) - , m_loop(vox) - { }; - - virtual void reset() - { - m_curr.reset(); - m_loop.reset(); - m_loop_saved = false; - } - - void save() - { - if (!m_loop_saved) - { - m_loop.copy(m_curr); - m_loop_saved = true; - } - } - - void restore() - { - if (m_loop_saved) - m_curr.copy(m_loop); - } - - s32 out() { return m_curr.m_step; } - - struct decoder_state_t - { - decoder_state_t(vox_core &vox) - : m_vox(vox) - { }; - - void reset() - { - m_index = 0; - m_step = 16; - } - - void copy(decoder_state_t src) - { - m_index = src.m_index; - m_step = src.m_step; - } - - void decode(u8 nibble) - { - const u8 delta = bitfield(nibble, 0, 3); - s16 ss = m_vox.m_step_table[m_index]; // ss(n) - - // d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + (ss(n) / 8) - s16 d = ss >> 3; - if (bitfield(delta, 2)) - d += ss; - if (bitfield(delta, 1)) - d += (ss >> 1); - if (bitfield(delta, 0)) - d += (ss >> 2); - - // if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n) - if (bitfield(nibble, 3)) - m_step = std::max(m_step - d, -2048); - else - m_step = std::min(m_step + d, 2047); - - // adjust step index - m_index = MODIFIED_CLAMP(m_index + m_vox.m_index_table[delta], 0, 48); - } - - vox_core &m_vox; - s8 m_index = 0; - s32 m_step = 16; - }; - - decoder_state_t m_curr; - decoder_state_t m_loop; - bool m_loop_saved = false; - }; - - s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; - s32 m_step_table[49] = { - 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, - 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 - }; -}; - -#endif diff --git a/src/engine/platform/sound/scc/scc.cpp b/src/engine/platform/sound/scc/scc.cpp deleted file mode 100644 index e2ebcf20..00000000 --- a/src/engine/platform/sound/scc/scc.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst - Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw - Konami SCC emulation core - - Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator. - - It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake. - Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares. - and some Third-party MSX utilities still support this due to its market shares. - - There's 2 SCC types: - - K051649 (or simply known as SCC) - This chip is used for MSX MegaROM Mapper, some arcade machines. - Channel 4 and 5 must be share waveform, other channels has its own waveforms. - - K052539 (also known as SCC+) - This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher). - All channels can be has its own waveforms, and also has backward compatibility mode with K051649. - - Based on: - https://www.msx.org/wiki/MegaROM_Mappers - https://www.msx.org/wiki/Konami_051649 - https://www.msx.org/wiki/Konami_052539 - http://bifi.msxnet.org/msxnet/tech/scc - http://bifi.msxnet.org/msxnet/tech/soundcartridge - - K051649 Register Layout - - -------------------------------------------------------------------- - - 4000-bfff MegaROM Mapper - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 4000-5fff xxxx xxxx R Bank page 0 - c000-dfff mirror of 4000-5fff - - 6000-7fff xxxx xxxx R Bank page 1 - e000-ffff mirror of 6000-7fff - - 8000-9fff xxxx xxxx R Bank page 2 - 0000-1fff mirror of 8000-9fff - - a000-bfff xxxx xxxx R Bank page 3 - 2000-3fff mirror of a000-bfff - - -------------------------------------------------------------------- - - 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 5000 --xx xxxx W Bank select, Page 0 - 5001-57ff Mirror of 5000 - - 7000 --xx xxxx W Bank select, Page 1 - 7001-77ff Mirror of 7000 - - 9000 --xx xxxx W Bank select, Page 2 - --11 1111 W SCC Enable - 9001-97ff Mirror of 9000 - - b000 --xx xxxx W Bank select, Page 3 - b001-b7ff Mirror of b000 - - -------------------------------------------------------------------- - - 9800-9fff SCC register - - -------------------------------------------------------------------- - - 9800-987f Waveform - - Address Bit R/W Description - 7654 3210 - - 9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) - 9820-983f xxxx xxxx R/W Channel 1 "" - 9840-985f xxxx xxxx R/W Channel 2 "" - 9860-987f xxxx xxxx R/W Channel 3/4 "" - - 9880-9889 Pitch - - 9880 xxxx xxxx W Channel 0 Pitch LSB - 9881 ---- xxxx W Channel 0 Pitch MSB - 9882 xxxx xxxx W Channel 1 Pitch LSB - 9883 ---- xxxx W Channel 1 Pitch MSB - 9884 xxxx xxxx W Channel 2 Pitch LSB - 9885 ---- xxxx W Channel 2 Pitch MSB - 9886 xxxx xxxx W Channel 3 Pitch LSB - 9887 ---- xxxx W Channel 3 Pitch MSB - 9888 xxxx xxxx W Channel 4 Pitch LSB - 9889 ---- xxxx W Channel 4 Pitch MSB - - 9888-988e Volume - - 988a ---- xxxx W Channel 0 Volume - 988b ---- xxxx W Channel 1 Volume - 988c ---- xxxx W Channel 2 Volume - 988d ---- xxxx W Channel 3 Volume - 988e ---- xxxx W Channel 4 Volume - - 988f ---x ---- W Channel 4 Output enable/disable flag - ---- x--- W Channel 3 Output enable/disable flag - ---- -x-- W Channel 2 Output enable/disable flag - ---- --x- W Channel 1 Output enable/disable flag - ---- ---x W Channel 0 Output enable/disable flag - - 9890-989f Mirror of 9880-988f - - 98a0-98bf xxxx xxxx R Channel 4 Waveform - - 98e0 x--- ---- W Waveform rotate flag for channel 4 - -x-- ---- W Waveform rotate flag for all channels - --x- ---- W Reset waveform position after pitch writes - ---- --x- W 8 bit frequency - ---- --0x W 4 bit frequency - - 98e1-98ff Mirror of 98e0 - - 9900-9fff Mirror of 9800-98ff - - -------------------------------------------------------------------- - - K052539 Register Layout - - -------------------------------------------------------------------- - - 4000-bfff MegaRAM Mapper - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 4000-5fff xxxx xxxx R/W Bank page 0 - c000-dfff xxxx xxxx R/W "" - - 6000-7fff xxxx xxxx R/W Bank page 1 - e000-ffff xxxx xxxx R/W "" - - 8000-9fff xxxx xxxx R/W Bank page 2 - 0000-1fff xxxx xxxx R/W "" - - a000-bfff xxxx xxxx R/W Bank page 3 - 2000-3fff xxxx xxxx R/W "" - - -------------------------------------------------------------------- - - 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 5000 xxxx xxxx W Bank select, Page 0 - 5001-57ff Mirror of 5000 - - 7000 xxxx xxxx W Bank select, Page 1 - 7001-77ff Mirror of 7000 - - 9000 xxxx xxxx W Bank select, Page 2 - --11 1111 W SCC Enable (SCC Compatible mode) - 9001-97ff Mirror of 9000 - - b000 xxxx xxxx W Bank select, Page 3 - 1--- ---- W SCC+ Enable (SCC+ mode) - b001-b7ff Mirror of b000 - - -------------------------------------------------------------------- - - bffe-bfff Mapper configuration - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - bffe --x- ---- W SCC operation mode - --0- ---- W SCC Compatible mode - --1- ---- W SCC+ mode - ---x ---- W RAM write/Bank select toggle for all Bank pages - ---0 ---- W Bank select enable - ---1 ---- W RAM write enable - ---0 -x-- W RAM write/Bank select toggle for Bank page 2 - ---0 --x- W RAM write/Bank select toggle for Bank page 1 - ---0 ---x W RAM write/Bank select toggle for Bank page 0 - bfff Mirror of bffe - - -------------------------------------------------------------------- - - 9800-9fff SCC Compatible mode register - - -------------------------------------------------------------------- - - 9800-987f Waveform - - Address Bit R/W Description - 7654 3210 - - 9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) - 9820-983f xxxx xxxx R/W Channel 1 "" - 9840-985f xxxx xxxx R/W Channel 2 "" - 9860-987f xxxx xxxx R/W Channel 3/4 "" - - 9880-9889 Pitch - - 9880 xxxx xxxx W Channel 0 Pitch LSB - 9881 ---- xxxx W Channel 0 Pitch MSB - 9882 xxxx xxxx W Channel 1 Pitch LSB - 9883 ---- xxxx W Channel 1 Pitch MSB - 9884 xxxx xxxx W Channel 2 Pitch LSB - 9885 ---- xxxx W Channel 2 Pitch MSB - 9886 xxxx xxxx W Channel 3 Pitch LSB - 9887 ---- xxxx W Channel 3 Pitch MSB - 9888 xxxx xxxx W Channel 4 Pitch LSB - 9889 ---- xxxx W Channel 4 Pitch MSB - - 9888-988e Volume - - 988a ---- xxxx W Channel 0 Volume - 988b ---- xxxx W Channel 1 Volume - 988c ---- xxxx W Channel 2 Volume - 988d ---- xxxx W Channel 3 Volume - 988e ---- xxxx W Channel 4 Volume - - 988f ---x ---- W Channel 4 Output enable/disable flag - ---- x--- W Channel 3 Output enable/disable flag - ---- -x-- W Channel 2 Output enable/disable flag - ---- --x- W Channel 1 Output enable/disable flag - ---- ---x W Channel 0 Output enable/disable flag - - 9890-989f Mirror of 9880-988f - - 98a0-98bf xxxx xxxx R Channel 4 Waveform - - 98c0 -x-- ---- W Waveform rotate flag for all channels - --x- ---- W Reset waveform position after pitch writes - ---- --x- W 8 bit frequency - ---- --0x W 4 bit frequency - - 98c1-98df Mirror of 98c0 - - 9900-9fff Mirror of 9800-98ff - - -------------------------------------------------------------------- - - b800-bfff SCC+ mode register - - -------------------------------------------------------------------- - - b800-b89f Waveform - - Address Bit R/W Description - 7654 3210 - - b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) - b820-b83f xxxx xxxx R/W Channel 1 "" - b840-b85f xxxx xxxx R/W Channel 2 "" - b860-b87f xxxx xxxx R/W Channel 3 "" - b880-b89f xxxx xxxx R/W Channel 3 "" - - b8a0-b8a9 Pitch - - b8a0 xxxx xxxx W Channel 0 Pitch LSB - b8a1 ---- xxxx W Channel 0 Pitch MSB - b8a2 xxxx xxxx W Channel 1 Pitch LSB - b8a3 ---- xxxx W Channel 1 Pitch MSB - b8a4 xxxx xxxx W Channel 2 Pitch LSB - b8a5 ---- xxxx W Channel 2 Pitch MSB - b8a6 xxxx xxxx W Channel 3 Pitch LSB - b8a7 ---- xxxx W Channel 3 Pitch MSB - b8a8 xxxx xxxx W Channel 4 Pitch LSB - b8a9 ---- xxxx W Channel 4 Pitch MSB - - b8a8-b8ae Volume - - b8aa ---- xxxx W Channel 0 Volume - b8ab ---- xxxx W Channel 1 Volume - b8ac ---- xxxx W Channel 2 Volume - b8ad ---- xxxx W Channel 3 Volume - b8ae ---- xxxx W Channel 4 Volume - - b8af ---x ---- W Channel 4 Output enable/disable flag - ---- x--- W Channel 3 Output enable/disable flag - ---- -x-- W Channel 2 Output enable/disable flag - ---- --x- W Channel 1 Output enable/disable flag - ---- ---x W Channel 0 Output enable/disable flag - - b8b0-b8bf Mirror of b8a0-b8af - - b8c0 -x-- ---- W Waveform rotate flag for all channels - --x- ---- W Reset waveform position after pitch writes - ---- --x- W 8 bit frequency - ---- --0x W 4 bit frequency - - b8c1-b8df Mirror of b8c0 - - b900-bfff Mirror of b800-b8ff - - -------------------------------------------------------------------- - - SCC Frequency calculation: - if 8 bit frequency then - Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1) - else if 4 bit frequency then - Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1) - else - Frequency = Input clock / (Pitch input + 1) - -*/ - -#include "scc.hpp" -#include - -// shared SCC features -void scc_core::tick() -{ - m_out = 0; - for (auto & elem : m_voice) - { - elem.tick(); - m_out += elem.out; - } -} - -void scc_core::voice_t::tick() -{ - if (pitch >= 9) // or voice is halted - { - // update counter - Post decrement - u16 temp = counter; - if (m_host.m_test.freq_4bit) // 4 bit frequency mode - { - counter = (counter & ~0x0ff) | (bitfield(bitfield(counter, 0, 8) - 1, 0, 8) << 0); - counter = (counter & ~0xf00) | (bitfield(bitfield(counter, 8, 4) - 1, 0, 4) << 8); - } - else - counter = bitfield(counter - 1, 0, 12); - - // handle counter carry - bool carry = m_host.m_test.freq_8bit ? (bitfield(temp, 0, 8) == 0) : - (m_host.m_test.freq_4bit ? (bitfield(temp, 8, 4) == 0) : - (bitfield(temp, 0, 12) == 0)); - if (carry) - { - addr = bitfield(addr + 1, 0, 5); - counter = pitch; - } - } - // get output - if (enable) - out = (wave[addr] * volume) >> 4; // scale to 11 bit digital output - else - out = 0; -} - -void scc_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); - - m_test.reset(); - m_out = 0; - memset(m_reg,0,sizeof(m_reg)); -} - -void scc_core::voice_t::reset() -{ - memset(wave,0,sizeof(wave)); - enable = false; - pitch = 0; - volume = 0; - addr = 0; - counter = 0; - out = 0; -} - -// SCC accessors -u8 scc_core::wave_r(bool is_sccplus, u8 address) -{ - u8 ret = 0xff; - const u8 voice = bitfield(address, 5, 3); - if (voice > 4) - return ret; - - u8 wave_addr = bitfield(address, 0, 5); - - if (m_test.rotate) // rotate flag - wave_addr = bitfield(wave_addr + m_voice[voice].addr, 0, 5); - - if (!is_sccplus) - { - if (voice == 3) // rotate voice 3~4 flag - { - if (m_test.rotate4 || m_test.rotate) // rotate flag - wave_addr = bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate].addr, 0, 5); - } - } - ret = m_voice[voice].wave[wave_addr]; - - return ret; -} - -void scc_core::wave_w(bool is_sccplus, u8 address, u8 data) -{ - if (m_test.rotate) // write protected - return; - - const u8 voice = bitfield(address, 5, 3); - if (voice > 4) - return; - - const u8 wave_addr = bitfield(address, 0, 5); - - if (!is_sccplus) - { - if (((voice >= 3) && m_test.rotate4) || (voice >= 4)) // Ignore if write protected, or voice 4 - return; - if (voice >= 3) // voice 3, 4 shares waveform - { - m_voice[3].wave[wave_addr] = data; - m_voice[4].wave[wave_addr] = data; - } - else - m_voice[voice].wave[wave_addr] = data; - } - else - m_voice[voice].wave[wave_addr] = data; -} - -void scc_core::freq_vol_enable_w(u8 address, u8 data) -{ - const u8 voice_freq = bitfield(address, 1, 3); - const u8 voice_reg = bitfield(address, 0, 4); - // *0-*f Pitch, Volume, Enable - switch (voice_reg) - { - case 0x0: // 0x*0 Voice 0 Pitch LSB - case 0x2: // 0x*2 Voice 1 Pitch LSB - case 0x4: // 0x*4 Voice 2 Pitch LSB - case 0x6: // 0x*6 Voice 3 Pitch LSB - case 0x8: // 0x*8 Voice 4 Pitch LSB - if (m_test.resetpos) // Reset address - m_voice[voice_freq].addr = 0; - m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data; - m_voice[voice_freq].counter = m_voice[voice_freq].pitch; - break; - case 0x1: // 0x*1 Voice 0 Pitch MSB - case 0x3: // 0x*3 Voice 1 Pitch MSB - case 0x5: // 0x*5 Voice 2 Pitch MSB - case 0x7: // 0x*7 Voice 3 Pitch MSB - case 0x9: // 0x*9 Voice 4 Pitch MSB - if (m_test.resetpos) // Reset address - m_voice[voice_freq].addr = 0; - m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8); - m_voice[voice_freq].counter = m_voice[voice_freq].pitch; - break; - case 0xa: // 0x*a Voice 0 Volume - case 0xb: // 0x*b Voice 1 Volume - case 0xc: // 0x*c Voice 2 Volume - case 0xd: // 0x*d Voice 3 Volume - case 0xe: // 0x*e Voice 4 Volume - m_voice[voice_reg - 0xa].volume = bitfield(data, 0, 4); - break; - case 0xf: // 0x*f Enable/Disable flag - m_voice[0].enable = bitfield(data, 0); - m_voice[1].enable = bitfield(data, 1); - m_voice[2].enable = bitfield(data, 2); - m_voice[3].enable = bitfield(data, 3); - m_voice[4].enable = bitfield(data, 4); - break; - } -} - -void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) -{ - const u8 voice = bitfield(address, 5, 3); - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3/4 Waveform - wave_w(false, address, data); - break; - case 0b100: // 0x80-0x9f Pitch, Volume, Enable - freq_vol_enable_w(address, data); - break; - case 0b111: // 0xe0-0xff Test register - m_test.freq_4bit = bitfield(data, 0); - m_test.freq_8bit = bitfield(data, 1); - m_test.resetpos = bitfield(data, 5); - m_test.rotate = bitfield(data, 6); - m_test.rotate4 = bitfield(data, 7); - break; - } - m_reg[address] = data; -} - -void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) -{ - const u8 voice = bitfield(address, 5, 3); - if (is_sccplus) - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b100: // 0x80-0x9f Voice 4 Waveform - wave_w(true, address, data); - break; - case 0b101: // 0xa0-0xbf Pitch, Volume, Enable - freq_vol_enable_w(address, data); - break; - case 0b110: // 0xc0-0xdf Test register - m_test.freq_4bit = bitfield(data, 0); - m_test.freq_8bit = bitfield(data, 1); - m_test.resetpos = bitfield(data, 5); - m_test.rotate = bitfield(data, 6); - break; - default: - break; - } - } - else - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3/4 Waveform - wave_w(false, address, data); - break; - case 0b100: // 0x80-0x9f Pitch, Volume, Enable - freq_vol_enable_w(address, data); - break; - case 0b110: // 0xc0-0xdf Test register - m_test.freq_4bit = bitfield(data, 0); - m_test.freq_8bit = bitfield(data, 1); - m_test.resetpos = bitfield(data, 5); - m_test.rotate = bitfield(data, 6); - break; - default: - break; - } - } - m_reg[address] = data; -} - -u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address) -{ - const u8 voice = bitfield(address, 5, 3); - const u8 wave = bitfield(address, 0, 5); - u8 ret = 0xff; - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b101: // 0xa0-0xbf Voice 4 Waveform - ret = wave_r(false, (std::min(4, voice) << 5) | wave); - break; - } - return ret; -} - -u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address) -{ - const u8 voice = bitfield(address, 5, 3); - const u8 wave = bitfield(address, 0, 5); - u8 ret = 0xff; - if (is_sccplus) - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b100: // 0x80-0x9f Voice 4 Waveform - ret = wave_r(true, address); - break; - } - } - else - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b101: // 0xa0-0xbf Voice 4 Waveform - ret = wave_r(false, (std::min(4, voice) << 5) | wave); - break; - } - } - return ret; -} diff --git a/src/engine/platform/sound/scc/scc.hpp b/src/engine/platform/sound/scc/scc.hpp deleted file mode 100644 index 24a365cc..00000000 --- a/src/engine/platform/sound/scc/scc.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst - Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw - Konami SCC emulation core - - See scc.cpp for more info. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_SCC_HPP -#define _VGSOUND_EMU_SCC_HPP - -#pragma once - -namespace scc -{ - typedef unsigned char u8; - typedef signed char s8; - typedef unsigned short u16; - typedef signed short s16; - typedef unsigned int u32; - typedef signed int s32; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -} - -using namespace scc; -// shared for SCCs -class scc_core -{ -public: - // constructor - scc_core() - : m_voice{*this,*this,*this,*this,*this} - {}; - virtual ~scc_core(){}; - - // accessors - virtual u8 scc_r(bool is_sccplus, u8 address) = 0; - virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0; - - // internal state - virtual void reset(); - void tick(); - - // getters - s32 out() { return m_out; } // output to DA0...DA10 pin - s32 chan_out(u8 ch) { return m_voice[ch].out; } - u8 reg(u8 address) { return m_reg[address]; } - -protected: - // voice structs - struct voice_t - { - // constructor - voice_t(scc_core &host) : m_host(host) {}; - - // internal state - void reset(); - void tick(); - - // registers - scc_core &m_host; - s8 wave[32] = {0}; // internal waveform - bool enable = false; // output enable flag - u16 pitch = 0; // pitch - u8 volume = 0; // volume - u8 addr = 0; // waveform pointer - u16 counter = 0; // frequency counter - s32 out = 0; // current output - }; - voice_t m_voice[5]; // 5 voices - - // accessor - u8 wave_r(bool is_sccplus, u8 address); - void wave_w(bool is_sccplus, u8 address, u8 data); - void freq_vol_enable_w(u8 address, u8 data); - - struct test_t - { - // constructor - test_t() - : freq_4bit(0) - , freq_8bit(0) - , resetpos(0) - , rotate(0) - , rotate4(0) - { }; - - void reset() - { - freq_4bit = 0; - freq_8bit = 0; - resetpos = 0; - rotate = 0; - rotate4 = 0; - } - - u8 freq_4bit : 1; // 4 bit frequency - u8 freq_8bit : 1; // 8 bit frequency - u8 resetpos : 1; // reset counter after pitch writes - u8 rotate : 1; // rotate and write protect waveform for all channels - u8 rotate4 : 1; // same as above but for channel 4 only - }; - - test_t m_test; // test register - s32 m_out = 0; // output to DA0...10 - - u8 m_reg[256] = {0}; // register pool -}; - -// SCC core -class k051649_scc_core : public scc_core -{ -public: - // accessors - virtual u8 scc_r(bool is_sccplus, u8 address) override; - virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; -}; - -class k052539_scc_core : public k051649_scc_core -{ -public: - // accessors - virtual u8 scc_r(bool is_sccplus, u8 address) override; - virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; -}; - -#endif diff --git a/src/engine/platform/sound/vrcvi/vrcvi.cpp b/src/engine/platform/sound/vrcvi/vrcvi.cpp deleted file mode 100644 index a811c2f4..00000000 --- a/src/engine/platform/sound/vrcvi/vrcvi.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Konami VRC VI sound emulation core - - It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.) - - It's also DACless like other sound chip and mapper-with-sound manufactured by konami, - the Chips 6 bit digital sound output is needs converted to analog sound output when you it want to make some sounds, or send to sound mixer. - - Its are used for Akumajou Densetsu (Japan release of Castlevania III), Madara, Esper Dream 2. - - The chip is installed in 351951 PCB and 351949A PCB. - - 351951 PCB is used exclusivly for Akumajou Densetsu, Small board has VRC VI, PRG and CHR ROM. - - It's configuration also calls VRC6a, iNES mapper 024. - - 351949A PCB is for Last 2 titles with VRC VI, Bigger board has VRC VI, PRG and CHR ROM, and Battery Backed 8K x 8 bit SRAM. - - Additionally, It's PRG A0 and A1 bit to VRC VI input is swapped, compare to above. - - It's configuration also calls VRC6b, iNES mapper 026. - - The chip itself has 053328, 053329, 053330 Revision, but Its difference between revision is unknown. - - Like other mappers for NES, It has internal timer - Its timer can be sync with scanline like other Konami mapper in this era. - - Register layout (Sound and Timer only; 351951 PCB case, 351949A swaps xxx1 and xxx2): - - Address Bits Description - 7654 3210 - - 9000-9002 Pulse 1 - - 9000 x--- ---- Pulse 1 Duty ignore - -xxx ---- Pulse 1 Duty cycle - ---- xxxx Pulse 1 Volume - 9001 xxxx xxxx Pulse 1 Pitch bit 0-7 - 9002 x--- ---- Pulse 1 Enable - ---- xxxx Pulse 1 Pitch bit 8-11 - - 9003 Sound control - - 9003 ---- -x-- 4 bit Frequency mode - ---- -0x- 8 bit Frequency mode - ---- ---x Halt - - a000-a002 Pulse 2 - - a000 x--- ---- Pulse 2 Duty ignore - -xxx ---- Pulse 2 Duty cycle - ---- xxxx Pulse 2 Volume - a001 xxxx xxxx Pulse 2 Pitch bit 0-7 - a002 x--- ---- Pulse 2 Enable - ---- xxxx Pulse 2 Pitch bit 8-11 - - b000-b002 Sawtooth - - b000 --xx xxxx Sawtooth Accumulate Rate - b001 xxxx xxxx Sawtooth Pitch bit 0-7 - b002 x--- ---- Sawtooth Enable - ---- xxxx Sawtooth Pitch bit 8-11 - - f000-f002 IRQ Timer - - f000 xxxx xxxx IRQ Timer latch - f001 ---- -0-- Sync with scanline - ---- --x- Enable timer - ---- ---x Enable timer after IRQ Acknowledge - f002 ---- ---- IRQ Acknowledge - - Frequency calculations: - - if 4 bit Frequency Mode then - Frequency: Input clock / (bit 8 to 11 of Pitch + 1) - end else if 8 bit Frequency Mode then - Frequency: Input clock / (bit 4 to 11 of Pitch + 1) - end else then - Frequency: Input clock / (Pitch + 1) - end -*/ - -#include "vrcvi.hpp" -#include - -void vrcvi_core::tick() -{ - m_out = 0; - if (!m_control.m_halt) // Halt flag - { - // tick per each clock - int elemIndex=0; - for (auto & elem : m_pulse) - { - if (elem.tick()) { - m_out += elem.m_control.m_volume; // add 4 bit pulse output - m_ch_out[elemIndex]=elem.m_control.m_volume; - } else { - m_ch_out[elemIndex]=0; - } - elemIndex++; - } - if (m_sawtooth.tick()) { - m_out += bitfield(m_sawtooth.m_accum, 3, 5); // add 5 bit sawtooth output - m_ch_out[2]=bitfield(m_sawtooth.m_accum, 3, 5); - } else { - m_ch_out[2]=0; - } - } - if (m_timer.tick()) - m_timer.counter_tick(); -} - -void vrcvi_core::reset() -{ - for (auto & elem : m_pulse) - elem.reset(); - - m_sawtooth.reset(); - m_timer.reset(); - m_control.reset(); - m_out = 0; - memset(m_ch_out,0,sizeof(m_ch_out)); -} - -bool vrcvi_core::alu_t::tick() -{ - if (m_divider.m_enable) - { - const u16 temp = m_counter; - // post decrement - if (bitfield(m_host.m_control.m_shift, 1)) - { - m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); - m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); - } - else if (bitfield(m_host.m_control.m_shift, 0)) - { - m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4); - m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0); - } - else - m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12); - - // carry handling - bool carry = bitfield(m_host.m_control.m_shift, 1) ? (bitfield(temp, 8, 4) == 0) : - (bitfield(m_host.m_control.m_shift, 0) ? (bitfield(temp, 4, 8) == 0) : - (bitfield(temp, 0, 12) == 0)); - if (carry) - m_counter = m_divider.m_divider; - - return carry; - } - return false; -} - -bool vrcvi_core::pulse_t::tick() -{ - if (!m_divider.m_enable) - return false; - - if (vrcvi_core::alu_t::tick()) - m_cycle = bitfield(m_cycle + 1, 0, 4); - - return m_control.m_mode ? true : ((m_cycle > m_control.m_duty) ? true : false); -} - -bool vrcvi_core::sawtooth_t::tick() -{ - if (!m_divider.m_enable) - return false; - - if (vrcvi_core::alu_t::tick()) - { - if (bitfield(m_cycle++, 0)) // Even step only - m_accum += m_rate; - if (m_cycle >= 14) // Reset accumulator at every 14 cycles - { - m_accum = 0; - m_cycle = 0; - } - } - return (m_accum == 0) ? false : true; -} - -void vrcvi_core::alu_t::reset() -{ - m_divider.reset(); - m_counter = 0; - m_cycle = 0; -} - -void vrcvi_core::pulse_t::reset() -{ - vrcvi_core::alu_t::reset(); - m_control.reset(); -} - -void vrcvi_core::sawtooth_t::reset() -{ - vrcvi_core::alu_t::reset(); - m_rate = 0; - m_accum = 0; -} - -bool vrcvi_core::timer_t::tick() -{ - if (m_timer_control.m_enable) - { - if (!m_timer_control.m_sync) // scanline sync mode - { - m_prescaler -= 3; - if (m_prescaler <= 0) - { - m_prescaler += 341; - return true; - } - } - } - return (m_timer_control.m_enable && m_timer_control.m_sync) ? true : false; -} - -void vrcvi_core::timer_t::counter_tick() -{ - if (bitfield(++m_counter, 0, 8) == 0) - { - m_counter = m_counter_latch; - irq_set(); - } -} - -void vrcvi_core::timer_t::reset() -{ - m_timer_control.reset(); - m_prescaler = 341; - m_counter = m_counter_latch = 0; - irq_clear(); -} - -// Accessors - -void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data) -{ - if (msb) - { - m_divider = (m_divider & ~0xf00) | (bitfield(data, 0, 4) << 8); - m_enable = bitfield(data, 7); - } - else - m_divider = (m_divider & ~0x0ff) | data; -} - - -void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data) -{ - pulse_t &v = m_pulse[voice]; - switch (address) - { - case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2) - v.m_control.m_mode = bitfield(data, 7); - v.m_control.m_duty = bitfield(data, 4, 3); - v.m_control.m_volume = bitfield(data, 0, 4); - break; - case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2) - v.m_divider.write(false, data); - break; - case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2) - v.m_divider.write(true, data); - if (!v.m_divider.m_enable) // Reset duty cycle - v.m_cycle = 0; - break; - } -} - -void vrcvi_core::saw_w(u8 address, u8 data) -{ - switch (address) - { - case 0x00: // Sawtooth Accumulate - 0xb000 - m_sawtooth.m_rate = bitfield(data, 0, 6); - break; - case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth) - m_sawtooth.m_divider.write(false, data); - break; - case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth) - m_sawtooth.m_divider.write(true, data); - if (!m_sawtooth.m_divider.m_enable) // Reset accumulator - m_sawtooth.m_accum = 0; - break; - } -} - -void vrcvi_core::timer_w(u8 address, u8 data) -{ - switch (address) - { - case 0x00: // Timer latch - 0xf000 - m_timer.m_counter_latch = data; - break; - case 0x01: // Timer control - 0xf001/0xf002 - m_timer.m_timer_control.m_sync = bitfield(data, 2); - m_timer.m_timer_control.m_enable = bitfield(data, 1); - m_timer.m_timer_control.m_enable_ack = bitfield(data, 0); - if (m_timer.m_timer_control.m_enable) - { - m_timer.m_counter = m_timer.m_counter_latch; - m_timer.m_prescaler = 341; - } - m_timer.irq_clear(); - break; - case 0x02: // IRQ Acknowledge - 0xf002/0xf001 - m_timer.irq_clear(); - m_timer.m_timer_control.m_enable = m_timer.m_timer_control.m_enable_ack; - break; - } -} - -void vrcvi_core::control_w(u8 data) -{ - // Global control - 0x9003 - m_control.m_halt = bitfield(data, 0); - m_control.m_shift = bitfield(data, 1, 2); -} diff --git a/src/engine/platform/sound/vrcvi/vrcvi.hpp b/src/engine/platform/sound/vrcvi/vrcvi.hpp deleted file mode 100644 index 4a80f757..00000000 --- a/src/engine/platform/sound/vrcvi/vrcvi.hpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Konami VRC VI sound emulation core - - See vrcvi.cpp to more infos. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_VRCVI_HPP -#define _VGSOUND_EMU_VRCVI_HPP - -#pragma once - -namespace vrcvi -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - typedef signed char s8; - typedef signed short s16; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -}; - -class vrcvi_intf -{ -public: - virtual void irq_w(bool irq) { } -}; - -using namespace vrcvi; -class vrcvi_core -{ -public: - friend class vrcvi_intf; - // constructor - vrcvi_core(vrcvi_intf &intf) - : m_pulse{*this,*this} - , m_sawtooth(*this) - , m_timer(*this) - , m_intf(intf) - { - } - // accessors, getters, setters - void pulse_w(u8 voice, u8 address, u8 data); - void saw_w(u8 address, u8 data); - void timer_w(u8 address, u8 data); - void control_w(u8 data); - - // internal state - void reset(); - void tick(); - - // 6 bit output - s8 out() { return m_out; } - // channel output - s16 chan_out(u8 ch) { return m_ch_out[ch]; } -private: - // Common ALU for sound channels - struct alu_t - { - alu_t(vrcvi_core &host) - : m_host(host) - { }; - - - virtual void reset(); - virtual bool tick(); - - struct divider_t - { - divider_t() - : m_divider(0) - , m_enable(0) - { }; - - void reset() - { - m_divider = 0; - m_enable = 0; - } - - void write(bool msb, u8 data); - - u16 m_divider : 12; // divider (pitch) - u16 m_enable : 1; // channel enable flag - }; - - vrcvi_core &m_host; - divider_t m_divider; - u16 m_counter = 0; // clock counter - u8 m_cycle = 0; // clock cycle - }; - - // 2 Pulse channels - struct pulse_t : alu_t - { - pulse_t(vrcvi_core &host) - : alu_t(host) - { }; - - virtual void reset() override; - virtual bool tick() override; - - // Control bits - struct pulse_control_t - { - pulse_control_t() - : m_mode(0) - , m_duty(0) - , m_volume(0) - { }; - - void reset() - { - m_mode = 0; - m_duty = 0; - m_volume = 0; - } - - u8 m_mode : 1; // duty toggle flag - u8 m_duty : 3; // 3 bit duty cycle - u8 m_volume : 4; // 4 bit volume - }; - - pulse_control_t m_control; - }; - - // 1 Sawtooth channel - struct sawtooth_t : alu_t - { - sawtooth_t(vrcvi_core &host) - : alu_t(host) - { }; - - virtual void reset() override; - virtual bool tick() override; - - u8 m_rate = 0; // sawtooth accumulate rate - u8 m_accum = 0; // sawtooth accumulator, high 5 bit is accumulated to output - }; - - // Internal timer - struct timer_t - { - timer_t(vrcvi_core &host) - : m_host(host) - { }; - - void reset(); - bool tick(); - void counter_tick(); - - // IRQ update - void update() { m_host.m_intf.irq_w(m_timer_control.m_irq_trigger); } - void irq_set() - { - if (!m_timer_control.m_irq_trigger) - { - m_timer_control.m_irq_trigger = 1; - update(); - } - } - void irq_clear() - { - if (m_timer_control.m_irq_trigger) - { - m_timer_control.m_irq_trigger = 0; - update(); - } - } - - // Control bits - struct timer_control_t - { - timer_control_t() - : m_irq_trigger(0) - , m_enable_ack(0) - , m_enable(0) - , m_sync(0) - { }; - - void reset() - { - m_irq_trigger = 0; - m_enable_ack = 0; - m_enable = 0; - m_sync = 0; - } - - u8 m_irq_trigger : 1; - u8 m_enable_ack : 1; - u8 m_enable : 1; - u8 m_sync : 1; - }; - - vrcvi_core &m_host; // host core - timer_control_t m_timer_control; // timer control bits - s16 m_prescaler = 341; // prescaler - u8 m_counter = 0; // clock counter - u8 m_counter_latch = 0; // clock counter latch - }; - - struct global_control_t - { - global_control_t() - : m_halt(0) - , m_shift(0) - { }; - - void reset() - { - m_halt = 0; - m_shift = 0; - } - - u8 m_halt : 1; // halt sound - u8 m_shift : 2; // 4/8 bit right shift - }; - - pulse_t m_pulse[2]; // 2 pulse channels - sawtooth_t m_sawtooth; // sawtooth channel - timer_t m_timer; // internal timer - global_control_t m_control; // control - - vrcvi_intf &m_intf; - - s8 m_out = 0; // 6 bit output - s8 m_ch_out[3] = {0}; // per-channel output -}; - -#endif diff --git a/src/engine/platform/sound/x1_010/x1_010.cpp b/src/engine/platform/sound/x1_010/x1_010.cpp deleted file mode 100644 index 6b0041ba..00000000 --- a/src/engine/platform/sound/x1_010/x1_010.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Seta/Allumer X1-010 Emulation core - - the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode. - It has also 2 output channels, but no known hardware using this feature for stereo sound. - - Wavetable needs to paired with envelope, it's always enabled and similar as AY PSG's one - but its shape is stored at RAM. - - PCM volume is stored by each register. - - Both volume is 4bit per output. - - Everything except PCM sample is stored at paired 8 bit RAM. - - RAM layout (common case: Address bit 12 is swapped when RAM is shared with CPU) - - ----------------------------- - 0000...007f Voice Registers - - 0000...0007 Voice 0 Register - - Address Bits Description - 7654 3210 - 0 x--- ---- Frequency divider* - ---- -x-- Envelope one-shot mode - ---- --x- Sound format - ---- --0- PCM - ---- --1- Wavetable - ---- ---x Keyon/off - PCM case: - 1 xxxx xxxx Volume (Each nibble is for each output) - - 2 xxxx xxxx Frequency* - - 4 xxxx xxxx Start address / 4096 - - 5 xxxx xxxx 0x100 - (End address / 4096) - Wavetable case: - 1 ---x xxxx Wavetable data select - - 2 xxxx xxxx Frequency LSB* - 3 xxxx xxxx "" MSB - - 4 xxxx xxxx Envelope period (.10 fixed point, Low 8 bit) - - 5 ---x xxxx Envelope shape select (!= 0 : Reserved for Voice registers) - - 0008...000f Voice 1 Register - ... - 0078...007f Voice 15 Register - ----------------------------- - 0080...0fff Envelope shape data (Same as volume; Each nibble is for each output) - - 0080...00ff Envelope shape data 1 - 0100...017f Envelope shape data 2 - ... - 0f80...0fff Envelope shape data 31 - ----------------------------- - 1000...1fff Wavetable data - - 1000...107f Wavetable data 0 - 1080...10ff Wavetable data 1 - ... - 1f80...1fff Wavetable data 31 - ----------------------------- - - * Frequency is 4.4 fixed point for PCM, - 6.10 for Wavetable. - Frequency divider is higher precision or just right shift? - needs verification. -*/ - -#include "x1_010.hpp" - -void x1_010_core::tick() -{ - // reset output - m_out[0] = m_out[1] = 0; - for (int i = 0; i < 16; i++) - { - voice_t &v = m_voice[i]; - v.tick(); - m_out[0] += v.data * v.vol_out[0]; - m_out[1] += v.data * v.vol_out[1]; - } -} - -void x1_010_core::voice_t::tick() -{ - data = vol_out[0] = vol_out[1] = 0; - if (flag.keyon) - { - if (flag.wavetable) // Wavetable - { - // envelope, each nibble is for each output - u8 vol = m_host.m_envelope[(bitfield(end_envshape, 0, 5) << 7) | bitfield(env_acc, 10, 7)]; - vol_out[0] = bitfield(vol, 4, 4); - vol_out[1] = bitfield(vol, 0, 4); - env_acc += start_envfreq; - if (flag.env_oneshot && bitfield(env_acc, 17)) - flag.keyon = false; - else - env_acc = bitfield(env_acc, 0, 17); - // get wavetable data - data = m_host.m_wave[(bitfield(vol_wave, 0, 5) << 7) | bitfield(acc, 10, 7)]; - acc = bitfield(acc + (freq >> flag.div), 0, 17); - } - else // PCM sample - { - // volume register, each nibble is for each output - vol_out[0] = bitfield(vol_wave, 4, 4); - vol_out[1] = bitfield(vol_wave, 0, 4); - // get PCM sample - data = m_host.m_intf.read_byte(bitfield(acc, 4, 20)); - acc += bitfield(freq, 0, 8) >> flag.div; - if ((acc >> 16) > (0xff ^ end_envshape)) - flag.keyon = false; - } - } -} - -u8 x1_010_core::ram_r(u16 offset) -{ - if (offset & 0x1000) // wavetable data - return m_wave[offset & 0xfff]; - else if (offset & 0xf80) // envelope shape data - return m_envelope[offset & 0xfff]; - else // channel register - return m_voice[bitfield(offset, 3, 4)].reg_r(offset & 0x7); -} - -void x1_010_core::ram_w(u16 offset, u8 data) -{ - if (offset & 0x1000) // wavetable data - m_wave[offset & 0xfff] = data; - else if (offset & 0xf80) // envelope shape data - m_envelope[offset & 0xfff] = data; - else // channel register - m_voice[bitfield(offset, 3, 4)].reg_w(offset & 0x7, data); -} - -u8 x1_010_core::voice_t::reg_r(u8 offset) -{ - switch (offset & 0x7) - { - case 0x00: return (flag.div << 7) - | (flag.env_oneshot << 2) - | (flag.wavetable << 1) - | (flag.keyon << 0); - case 0x01: return vol_wave; - case 0x02: return bitfield(freq, 0, 8); - case 0x03: return bitfield(freq, 8, 8); - case 0x04: return start_envfreq; - case 0x05: return end_envshape; - default: break; - } - return 0; -} - -void x1_010_core::voice_t::reg_w(u8 offset, u8 data) -{ - switch (offset & 0x7) - { - case 0x00: - { - const bool prev_keyon = flag.keyon; - flag.div = bitfield(data, 7); - flag.env_oneshot = bitfield(data, 2); - flag.wavetable = bitfield(data, 1); - flag.keyon = bitfield(data, 0); - if (!prev_keyon && flag.keyon) // Key on - { - acc = flag.wavetable ? 0 : (u32(start_envfreq) << 16); - env_acc = 0; - } - break; - } - case 0x01: - vol_wave = data; - break; - case 0x02: - freq = (freq & 0xff00) | data; - break; - case 0x03: - freq = (freq & 0x00ff) | (u16(data) << 8); - break; - case 0x04: - start_envfreq = data; - break; - case 0x05: - end_envshape = data; - break; - default: - break; - } -} - -void x1_010_core::voice_t::reset() -{ - flag.reset(); - vol_wave = 0; - freq = 0; - start_envfreq = 0; - end_envshape = 0; - acc = 0; - env_acc = 0; - data = 0; - vol_out[0] = vol_out[1] = 0; -} - -void x1_010_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); - - std::fill_n(&m_envelope[0], 0x1000, 0); - std::fill_n(&m_wave[0], 0x1000, 0); - m_out[0] = m_out[1] = 0; -} diff --git a/src/engine/platform/sound/x1_010/x1_010.hpp b/src/engine/platform/sound/x1_010/x1_010.hpp deleted file mode 100644 index b533b66d..00000000 --- a/src/engine/platform/sound/x1_010/x1_010.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Seta/Allumer X1-010 Emulation core - - See x1_010.cpp for more info. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_X1_010_HPP -#define _VGSOUND_EMU_X1_010_HPP - -#pragma once - -namespace x1_010 -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - typedef signed char s8; - typedef signed int s32; - - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -} - -using namespace x1_010; -class x1_010_mem_intf -{ -public: - virtual u8 read_byte(u32 address) { return 0; } -}; - -using namespace x1_010; -class x1_010_core -{ - friend class x1_010_mem_intf; -public: - // constructor - x1_010_core(x1_010_mem_intf &intf) - : m_voice{*this,*this,*this,*this, - *this,*this,*this,*this, - *this,*this,*this,*this, - *this,*this,*this,*this} - , m_intf(intf) - { - m_envelope = std::make_unique(0x1000); - m_wave = std::make_unique(0x1000); - - std::fill_n(&m_envelope[0], 0x1000, 0); - std::fill_n(&m_wave[0], 0x1000, 0); - } - - // register accessor - u8 ram_r(u16 offset); - void ram_w(u16 offset, u8 data); - - // getters - s32 output(u8 channel) { return m_out[channel & 1]; } - s32 chan_out(u8 channel) { return (m_voice[channel].data * (m_voice[channel].vol_out[0]+m_voice[channel].vol_out[1]))<<2; } - - // internal state - void reset(); - void tick(); - -private: - // 16 voices in chip - struct voice_t - { - // constructor - voice_t(x1_010_core &host) : m_host(host) {} - - // internal state - void reset(); - void tick(); - - // register accessor - u8 reg_r(u8 offset); - void reg_w(u8 offset, u8 data); - - // registers - x1_010_core &m_host; - struct flag_t - { - u8 div : 1; - u8 env_oneshot : 1; - u8 wavetable : 1; - u8 keyon : 1; - void reset() - { - div = 0; - env_oneshot = 0; - wavetable = 0; - keyon = 0; - } - flag_t() - : div(0) - , env_oneshot(0) - , wavetable(0) - , keyon(0) - { } - }; - flag_t flag; - u8 vol_wave = 0; - u16 freq = 0; - u8 start_envfreq = 0; - u8 end_envshape = 0; - - // internal registers - u32 acc = 0; - u32 env_acc = 0; - s8 data = 0; - u8 vol_out[2] = {0}; - }; - voice_t m_voice[16]; - - // RAM - std::unique_ptr m_envelope = nullptr; - std::unique_ptr m_wave = nullptr; - - // output data - s32 m_out[2] = {0}; - - x1_010_mem_intf &m_intf; -}; - -#endif diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 2500ce19..449ee159 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -86,9 +86,10 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len // Oscilloscope buffer part if (++writeOscBuf>=32) { writeOscBuf=0; - for (int i=0; i<3; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.chan_out(i)<<10; + for (int i=0; i<2; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<10; } + oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10; } // Command part @@ -195,7 +196,7 @@ void DivPlatformVRC6::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOff) { chWrite(i,2,0); - } else { + } else if (chan[i].active) { chWrite(i,1,chan[i].freq&0xff); chWrite(i,2,0x80|((chan[i].freq>>8)&0xf)); } diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 608baa10..81c81016 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -23,10 +23,10 @@ #include #include "../dispatch.h" #include "../macroInt.h" -#include "sound/vrcvi/vrcvi.hpp" +#include "vgsound_emu/src/vrcvi/vrcvi.hpp" -class DivPlatformVRC6: public DivDispatch { +class DivPlatformVRC6: public DivDispatch, public vrcvi_intf { struct Channel { int freq, baseFreq, pitch, pitch2, note; int dacPeriod, dacRate, dacOut; @@ -75,7 +75,6 @@ class DivPlatformVRC6: public DivDispatch { std::queue writes; unsigned char sampleBank; unsigned char writeOscBuf; - vrcvi_intf intf; vrcvi_core vrc6; unsigned char regPool[13]; @@ -101,7 +100,7 @@ class DivPlatformVRC6: public DivDispatch { const char** getRegisterSheet(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); - DivPlatformVRC6() : vrc6(intf) {}; + DivPlatformVRC6() : vrc6(*this) {}; ~DivPlatformVRC6(); }; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 0f989ac9..87c7a91c 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -23,9 +23,9 @@ #include //#define rWrite(a,v) pendingWrites[a]=v; -#define rWrite(a,v) if (!skipRegisterWrites) { x1_010->ram_w(a,v); if (dumpWrites) { addWrite(a,v); } } +#define rWrite(a,v) if (!skipRegisterWrites) { x1_010.ram_w(a,v); if (dumpWrites) { addWrite(a,v); } } -#define chRead(c,a) x1_010->ram_r((c<<3)|(a&7)) +#define chRead(c,a) x1_010.ram_r((c<<3)|(a&7)) #define chWrite(c,a,v) rWrite((c<<3)|(a&7),v) #define waveWrite(c,a,v) rWrite(0x1000|(chan[c].waveBank<<11)|(c<<7)|(a&0x7f),(v-128)&0xff) #define envFill(c,a) rWrite(0x800|(c<<7)|(a&0x7f),(chan[c].lvol<<4)|chan[c].rvol) @@ -207,10 +207,10 @@ const char** DivPlatformX1_010::getRegisterSheet() { void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t h=start; htick(); + x1_010.tick(); - signed int tempL=x1_010->output(0); - signed int tempR=x1_010->output(1); + signed int tempL=x1_010.output(0); + signed int tempR=x1_010.output(1); if (tempL<-32768) tempL=-32768; if (tempL>32767) tempL=32767; @@ -222,11 +222,18 @@ void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t l bufR[h]=stereo?tempR:bufL[h]; for (int i=0; i<16; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=x1_010->chan_out(i); + oscBuf[i]->data[oscBuf[i]->needle++]=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))>>1; } } } +u8 DivPlatformX1_010::read_byte(u32 address) { + if ((sampleMem!=NULL) && (addressram_r(i); + regPool[i]=x1_010.ram_r(i); } return regPool; } @@ -829,7 +836,7 @@ void DivPlatformX1_010::reset() { chan[i].ws.setEngine(parent); chan[i].ws.init(NULL,128,255,false); } - x1_010->reset(); + x1_010.reset(); sampleBank=0; // set per-channel initial panning for (int i=0; i<16; i++) { @@ -942,9 +949,7 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in setFlags(flags); sampleMem=new unsigned char[getSampleMemCapacity()]; sampleMemLen=0; - intf.memory=sampleMem; - x1_010=new x1_010_core(intf); - x1_010->reset(); + x1_010.reset(); reset(); return 16; } @@ -953,7 +958,6 @@ void DivPlatformX1_010::quit() { for (int i=0; i<16; i++) { delete oscBuf[i]; } - delete x1_010; delete[] sampleMem; } diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 178a8938..84d1040e 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -24,20 +24,9 @@ #include "../engine.h" #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/x1_010/x1_010.hpp" +#include "vgsound_emu/src/x1_010/x1_010.hpp" -class DivX1_010Interface: public x1_010_mem_intf { - public: - unsigned char* memory; - int sampleBank; - virtual u8 read_byte(u32 address) override { - if (memory==NULL) return 0; - return memory[address & 0xfffff]; - } - DivX1_010Interface(): memory(NULL), sampleBank(0) {} -}; - -class DivPlatformX1_010: public DivDispatch { +class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { struct Channel { struct Envelope { struct EnvFlag { @@ -118,14 +107,14 @@ class DivPlatformX1_010: public DivDispatch { unsigned char* sampleMem; size_t sampleMemLen; unsigned char sampleBank; - DivX1_010Interface intf; - x1_010_core* x1_010; + x1_010_core x1_010; unsigned char regPool[0x2000]; double NoteX1_010(int ch, int note); void updateWave(int ch); void updateEnvelope(int ch); friend void putDispatchChan(void*,int,int); public: + u8 read_byte(u32 address); void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); @@ -151,6 +140,10 @@ class DivPlatformX1_010: public DivDispatch { const char** getRegisterSheet(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); + DivPlatformX1_010(): + DivDispatch(), + vgsound_emu_mem_intf(), + x1_010(*this) {} ~DivPlatformX1_010(); }; diff --git a/src/main.cpp b/src/main.cpp index a1856092..885a4c49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,7 +183,7 @@ TAParamResult pVersion(String) { printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - printf("- vgsound_emu (first version) by cam900 (BSD 3-clause)\n"); + printf("- vgsound_emu (second version) by cam900 (zlib)\n"); return TA_PARAM_QUIT; } From bf2ec8f1c4138f079277954501a8c3f7b694cc17 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 00:05:54 +0900 Subject: [PATCH 02/16] Temporary workaround for accidently removed libs --- extern/vgsound_emu-modified | 1 + vgsound_emu-modified/.clang-format | 154 ++++ vgsound_emu-modified/.gitignore | 18 + vgsound_emu-modified/CHANGELOG.md | 17 + vgsound_emu-modified/CMakeLists.txt | 161 ++++ vgsound_emu-modified/LICENSE | 19 + vgsound_emu-modified/MODIFIED.md | 7 + vgsound_emu-modified/README.md | 131 +++ .../vgsound_emu/src/core/util.hpp | 263 ++++++ .../vgsound_emu/src/core/vox/vox.cpp | 75 ++ .../vgsound_emu/src/core/vox/vox.hpp | 115 +++ .../vgsound_emu/src/es550x/README.md | 88 ++ .../vgsound_emu/src/es550x/es5504.cpp | 456 +++++++++ .../vgsound_emu/src/es550x/es5504.hpp | 117 +++ .../vgsound_emu/src/es550x/es5505.cpp | 652 +++++++++++++ .../vgsound_emu/src/es550x/es5505.hpp | 304 ++++++ .../vgsound_emu/src/es550x/es5506.cpp | 870 ++++++++++++++++++ .../vgsound_emu/src/es550x/es5506.hpp | 385 ++++++++ .../vgsound_emu/src/es550x/es550x.cpp | 34 + .../vgsound_emu/src/es550x/es550x.hpp | 610 ++++++++++++ .../vgsound_emu/src/es550x/es550x_alu.cpp | 131 +++ .../vgsound_emu/src/es550x/es550x_filter.cpp | 72 ++ .../vgsound_emu/src/k005289/README.md | 23 + .../vgsound_emu/src/k005289/k005289.cpp | 42 + .../vgsound_emu/src/k005289/k005289.hpp | 83 ++ .../vgsound_emu/src/k007232/README.md | 75 ++ .../vgsound_emu/src/k007232/k007232.cpp | 169 ++++ .../vgsound_emu/src/k007232/k007232.hpp | 115 +++ .../vgsound_emu/src/k053260/README.md | 109 +++ .../vgsound_emu/src/k053260/k053260.cpp | 290 ++++++ .../vgsound_emu/src/k053260/k053260.hpp | 262 ++++++ .../vgsound_emu/src/msm6295/README.md | 67 ++ .../vgsound_emu/src/msm6295/msm6295.cpp | 179 ++++ .../vgsound_emu/src/msm6295/msm6295.hpp | 142 +++ .../vgsound_emu/src/n163/README.md | 88 ++ .../vgsound_emu/src/n163/n163.cpp | 120 +++ .../vgsound_emu/src/n163/n163.hpp | 109 +++ .../vgsound_emu/src/scc/README.md | 314 +++++++ .../vgsound_emu/src/scc/scc.cpp | 461 ++++++++++ .../vgsound_emu/src/scc/scc.hpp | 320 +++++++ .../vgsound_emu/src/template/template.cpp | 33 + .../vgsound_emu/src/template/template.hpp | 88 ++ .../vgsound_emu/src/vrcvi/README.md | 97 ++ .../vgsound_emu/src/vrcvi/vrcvi.cpp | 260 ++++++ .../vgsound_emu/src/vrcvi/vrcvi.hpp | 407 ++++++++ .../vgsound_emu/src/x1_010/README.md | 97 ++ .../vgsound_emu/src/x1_010/x1_010.cpp | 163 ++++ .../vgsound_emu/src/x1_010/x1_010.hpp | 179 ++++ 48 files changed, 8972 insertions(+) create mode 160000 extern/vgsound_emu-modified create mode 100644 vgsound_emu-modified/.clang-format create mode 100644 vgsound_emu-modified/.gitignore create mode 100644 vgsound_emu-modified/CHANGELOG.md create mode 100644 vgsound_emu-modified/CMakeLists.txt create mode 100644 vgsound_emu-modified/LICENSE create mode 100644 vgsound_emu-modified/MODIFIED.md create mode 100644 vgsound_emu-modified/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/core/util.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k005289/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k007232/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k053260/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/msm6295/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/n163/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/scc/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/template/template.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/template/template.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/x1_010/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp diff --git a/extern/vgsound_emu-modified b/extern/vgsound_emu-modified new file mode 160000 index 00000000..7b988a67 --- /dev/null +++ b/extern/vgsound_emu-modified @@ -0,0 +1 @@ +Subproject commit 7b988a6714ebf61e8a5fad5c9ccbda2b85853fe1 diff --git a/vgsound_emu-modified/.clang-format b/vgsound_emu-modified/.clang-format new file mode 100644 index 00000000..edce098d --- /dev/null +++ b/vgsound_emu-modified/.clang-format @@ -0,0 +1,154 @@ +# +# License: Zlib +# see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details +# +# Copyright holder(s): cam900 +# Clang Format setting for vgsound_emu +# +--- +BasedOnStyle: Microsoft +UseCRLF: true +IndentWidth: 4 +ColumnLimit: 0 +--- +Language: Proto +DisableFormat: true +--- +Language: TableGen +DisableFormat: true +--- +Language: TextProto +DisableFormat: true +--- +Language: Cpp +TabWidth: 4 +UseTab: Always +AccessModifierOffset: 4 +ColumnLimit: 100 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignEscapedNewlines: Right +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BreakBeforeBraces: Allman +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +BreakStringLiterals: false +CompactNamespaces: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +EmptyLineAfterAccessModifier: Leave +EmptyLineBeforeAccessModifier: Always +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentAccessModifiers: true +IndentCaseBlocks: true +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentRequiresClause: true +IndentRequires: true +IndentWrappedFunctionNames: false +InsertBraces: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PPIndentWidth: 1 +PackConstructorInitializers: Never +PointerAlignment: Right +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: Microsoft +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RequiresClausePosition: OwnLine +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 0 +SortIncludes: CaseSensitive +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: After +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: false + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: [] diff --git a/vgsound_emu-modified/.gitignore b/vgsound_emu-modified/.gitignore new file mode 100644 index 00000000..6a46037b --- /dev/null +++ b/vgsound_emu-modified/.gitignore @@ -0,0 +1,18 @@ +.vs/* +.vscode/* +node_modules/* +package.json +package-lock.json + +build/* +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps diff --git a/vgsound_emu-modified/CHANGELOG.md b/vgsound_emu-modified/CHANGELOG.md new file mode 100644 index 00000000..bf1e6677 --- /dev/null +++ b/vgsound_emu-modified/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelogs + +## Important changes + +### V 2.1.0 (2022-09-08) + +Move source folder into vgsound_emu folder +CMake support +Move each readmes into README.md each folders + +## Details + +See [here](https://gitlab.com/cam900/vgsound_emu/-/commits/main). + +### Previous changelogs + +See [here](https://github.com/cam900/vgsound_emu/commits/main). diff --git a/vgsound_emu-modified/CMakeLists.txt b/vgsound_emu-modified/CMakeLists.txt new file mode 100644 index 00000000..17e14291 --- /dev/null +++ b/vgsound_emu-modified/CMakeLists.txt @@ -0,0 +1,161 @@ +# +# License: Zlib +# see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details +# +# Copyright holder(s): cam900 +# CMake for vgsound_emu +# + +cmake_minimum_required(VERSION 3.0) +project(vgsound_emu + VERSION 2.1.0 + LANGUAGES CXX) + +option(VGSOUND_EMU_ES5504 "Use ES5504 core" ON) +option(VGSOUND_EMU_ES5505 "Use ES5505 core" ON) +option(VGSOUND_EMU_ES5506 "Use ES5506 core" ON) +option(VGSOUND_EMU_K005289 "Use K005289 core" ON) +option(VGSOUND_EMU_K007232 "Use K007232 core" ON) +option(VGSOUND_EMU_K053260 "Use K053260 core" ON) +option(VGSOUND_EMU_MSM6295 "Use MSM6295 core" ON) +option(VGSOUND_EMU_NAMCO_163 "Use Namco 163 core" ON) +option(VGSOUND_EMU_SCC "Use SCC core" ON) +option(VGSOUND_EMU_VRCVI "Use VRC VI core" ON) +option(VGSOUND_EMU_X1_010 "Use X1-010 core" ON) + +message(STATUS "Host: ${CMAKE_HOST_SYSTEM_NAME}, ${CMAKE_HOST_SYSTEM_PROCESSOR}") +message(STATUS "Target: ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}") +message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "CMake version: ${CMAKE_VERSION}") +message(STATUS "Generator: ${CMAKE_GENERATOR}") +message(STATUS "Extra generator: ${CMAKE_EXTRA_GENERATOR}") +message(STATUS "Make program: ${CMAKE_MAKE_PROGRAM}") + +set(CORE_SOURCE "") +set(EMU_SOURCE "") + +# Core functions +list(APPEND CORE_SOURCE + vgsound_emu/src/core/util.hpp +) + +# Dialogic ADPCM +if(VGSOUND_EMU_MSM6295) + list(APPEND CORE_SOURCE + vgsound_emu/src/core/vox/vox.cpp + vgsound_emu/src/core/vox/vox.hpp + ) + message(STATUS "Using Dialogic ADPCM core") +endif() + +# ES5504, ES5505, ES5506 +if(VGSOUND_EMU_ES5504 OR VGSOUND_EMU_ES5505 OR VGSOUND_EMU_ES5506) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es550x.hpp + + vgsound_emu/src/es550x/es550x.cpp + vgsound_emu/src/es550x/es550x_alu.cpp + vgsound_emu/src/es550x/es550x_filter.cpp + ) + + if(VGSOUND_EMU_ES5504) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es5504.hpp + vgsound_emu/src/es550x/es5504.cpp + ) + message(STATUS "Using ES5504 core") + endif() + + if(VGSOUND_EMU_ES5505) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es5505.hpp + vgsound_emu/src/es550x/es5505.cpp + ) + message(STATUS "Using ES5505 core") + endif() + + if(VGSOUND_EMU_ES5506) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es5506.hpp + vgsound_emu/src/es550x/es5506.cpp + ) + message(STATUS "Using ES5506 core") + endif() +endif() + +# K005289 +if(VGSOUND_EMU_K005289) + list(APPEND EMU_SOURCE + vgsound_emu/src/k005289/k005289.hpp + vgsound_emu/src/k005289/k005289.cpp + ) + message(STATUS "Using K005289 core") +endif() + +# K007232 +if(VGSOUND_EMU_K007232) + list(APPEND EMU_SOURCE + vgsound_emu/src/k007232/k007232.hpp + vgsound_emu/src/k007232/k007232.cpp + ) + message(STATUS "Using K007232 core") +endif() + +# K053260 +if(VGSOUND_EMU_K053260) + list(APPEND EMU_SOURCE + vgsound_emu/src/k053260/k053260.hpp + vgsound_emu/src/k053260/k053260.cpp + ) + message(STATUS "Using K053260 core") +endif() + +# MSM6295 +if(VGSOUND_EMU_MSM6295) + list(APPEND EMU_SOURCE + vgsound_emu/src/msm6295/msm6295.hpp + vgsound_emu/src/msm6295/msm6295.cpp + ) + message(STATUS "Using MSM6295 core") +endif() + +# Namco 163 +if(VGSOUND_EMU_NAMCO_163) + list(APPEND EMU_SOURCE + vgsound_emu/src/n163/n163.hpp + vgsound_emu/src/n163/n163.cpp + ) + message(STATUS "Using Namco 163 core") +endif() + +# SCC +if(VGSOUND_EMU_SCC) + list(APPEND EMU_SOURCE + vgsound_emu/src/scc/scc.hpp + vgsound_emu/src/scc/scc.cpp + ) + message(STATUS "Using SCC core") +endif() + +# VRC VI +if(VGSOUND_EMU_VRCVI) + list(APPEND EMU_SOURCE + vgsound_emu/src/vrcvi/vrcvi.hpp + vgsound_emu/src/vrcvi/vrcvi.cpp + ) + message(STATUS "Using VRC VI core") +endif() + +# X1-010 +if(VGSOUND_EMU_X1_010) + list(APPEND EMU_SOURCE + vgsound_emu/src/x1_010/x1_010.hpp + vgsound_emu/src/x1_010/x1_010.cpp + ) + message(STATUS "Using X1-010 core") +endif() + +add_library(vgsound_emu STATIC ${CORE_SOURCE} ${EMU_SOURCE}) +target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# target_compile_options(vgsound_emu PRIVATE -Wall -Werror) \ No newline at end of file diff --git a/vgsound_emu-modified/LICENSE b/vgsound_emu-modified/LICENSE new file mode 100644 index 00000000..92814468 --- /dev/null +++ b/vgsound_emu-modified/LICENSE @@ -0,0 +1,19 @@ +zlib License + +(C) 2022-present cam900 and contributors + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/vgsound_emu-modified/MODIFIED.md b/vgsound_emu-modified/MODIFIED.md new file mode 100644 index 00000000..432d014f --- /dev/null +++ b/vgsound_emu-modified/MODIFIED.md @@ -0,0 +1,7 @@ +# modification disclaimer + +this is a modified version of vgsound_emu emulation core library tailored for Furnace. + +it should not and shall NOT be mistaken for the original, authentic or actual version and revision of the library. + +you can get original software from [here](https://gitlab.com/cam900/vgsound_emu/). diff --git a/vgsound_emu-modified/README.md b/vgsound_emu-modified/README.md new file mode 100644 index 00000000..b24e2888 --- /dev/null +++ b/vgsound_emu-modified/README.md @@ -0,0 +1,131 @@ +# vgsound_emu V2 (modified) + +This is a library of video game sound chip emulation cores. useful for emulators, chiptune trackers, or players. + +This is a modified version of vgsound_emu, tailored for Furnace. + +## Important + +License is now changed to zlib license in vgsound_emu V2, now you must notify your all modifications. + +but [vgsound_emu V1 (pre-V2)](https://gitlab.com/cam900/vgsound_emu/-/tree/V1) is still exists, and it's still distributed under [BSD-3-Clause license](https://spdx.org/licenses/BSD-3-Clause.html).([details](https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE)) + +## V2 revision changes + +formatting codes with clang-format, Encapsulation for Maintenance, Fix GCC 12, Change license to zlib license for notify modifications in derived works from this cores. + +## Changelog + +See [here](https://gitlab.com/cam900/vgsound_emu/-/blob/main/CHANGELOG.md). + +## License + +This software is distributed under [zlib License](https://spdx.org/licenses/Zlib.html), unlike [vgsound_emu V1](https://gitlab.com/cam900/vgsound_emu/-/tree/V1)([standard BSD-3-Clause license](https://spdx.org/licenses/BSD-3-Clause.html)([details](https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE))). +You must notify your modifications at all files you have modified! + +See [here](https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE) for details. + +## Folders + +- vgsound_emu: base folder + - src: source codes for emulation cores + - core: core files used in most of emulation cores + - vox: Dialogic ADPCM core + - es550x: Ensoniq ES5504, ES5505, ES5506 PCM sound chip families, 25/32 voices with 16/4 stereo/6 stereo output channels + - k005289: Konami K005289, 2 timers + - k007232: Konami K007232, 2 PCM channels + - k053260: Konami K053260, 4 PCM or ADPCM channels with CPU to CPU communication feature + - msm6295: OKI MSM6295, 4 ADPCM channels + - n163: Namco 163, NES Mapper with up to 8 Wavetable channels + - scc: Konami SCC, MSX Mappers with 5 Wavetable channels + - vrcvi: Konami VRC VI, NES Mapper with 2 Pulse channels and 1 Sawtooth channel + - x1_010: Seta/Allumer X1-010, 16 Wavetable/PCM channels + - template: Template for sound emulation core + +## Build instruction + +### dependencies + +- CMake +- git (for source repository management) +- MSVC or GCC or Clang (for compile) + +### Clone repository + +type the following on a terminal/console/shell/whatever: + +``` +git clone https://gitlab.com/cam900/vgsound_emu.git +cd vgsound_emu +``` + +### Compile + +#### MSVC + +type the following on a developer tools command prompt: + +``` +mkdir build +cd build +cmake .. +msbuild ALL_BUILD.vcxproj +``` + +#### MinGW, GCC, Clang with MakeFile + +type the following on a terminal/console/shell/whatever: + +``` +mkdir build +cd build +cmake .. +make +``` + +### CMake options + +To add an CMake option from the command line: ```cmake -D= ..``` +You can add multiple option with CMake. + +#### Available options + +| Options | Available Value | Default | Descriptions | +| :-: | :-: | :-: | :-: | +| VGSOUND_EMU_ES5504 | ON/OFF | ON | Use ES5504 core | +| VGSOUND_EMU_ES5505 | ON/OFF | ON | Use ES5505 core | +| VGSOUND_EMU_ES5506 | ON/OFF | ON | Use ES5506 core | +| VGSOUND_EMU_K005289 | ON/OFF | ON | Use K005289 core | +| VGSOUND_EMU_K007232 | ON/OFF | ON | Use K007232 core | +| VGSOUND_EMU_K053260 | ON/OFF | ON | Use K053260 core | +| VGSOUND_EMU_MSM6295 | ON/OFF | ON | Use MSM6295 core | +| VGSOUND_EMU_NAMCO_163 | ON/OFF | ON | Use Namco 163 core | +| VGSOUND_EMU_SCC | ON/OFF | ON | Use SCC core | +| VGSOUND_EMU_VRCVI | ON/OFF | ON | Use VRC VI core | +| VGSOUND_EMU_X1_010 | ON/OFF | ON | Use X1-010 core | + +### Link at another project + +Copy this repository as submodule first, type the following on a terminal/console/shell/whatever: + +``` +git submodule add https://gitlab.com/cam900/vgsound_emu.git +``` + +Then, Add following options at your CMakeLists.txt file. + +example: + +``` +add_subdirectory( [EXCLUDE_FROM_ALL]) +... +target_include_directories( SYSTEM PRIVATE ) +target_link_libraries( PRIVATE vgsound_emu) +``` + +## Contributors + +- [cam900](https://gitlab.com/cam900) +- [Natt Akuma](https://github.com/akumanatt) +- [James Alan Nguyen](https://github.com/djtuBIG-MaliceX) +- [Laurens Holst](https://github.com/Grauw) diff --git a/vgsound_emu-modified/vgsound_emu/src/core/util.hpp b/vgsound_emu-modified/vgsound_emu/src/core/util.hpp new file mode 100644 index 00000000..a6a9d6a2 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/core/util.hpp @@ -0,0 +1,263 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Various core utilities for vgsound_emu +*/ + +#ifndef _VGSOUND_EMU_SRC_CORE_UTIL_HPP +#define _VGSOUND_EMU_SRC_CORE_UTIL_HPP + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace vgsound_emu +{ + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + typedef unsigned long long u64; + typedef signed char s8; + typedef signed short s16; + typedef signed int s32; + typedef signed long long s64; + typedef float f32; + typedef double f64; + + class vgsound_emu_core + { + public: + // constructors + vgsound_emu_core(std::string tag) + : m_tag(tag) + { + } + + // getters + std::string tag() { return m_tag; } + + protected: + const f64 PI = 3.1415926535897932384626433832795; + + // std::clamp is only for C++17 or later; I use my own code + template + T clamp(T in, T min, T max) + { +#if defined(_HAS_CXX17) && _HAS_CXX17 + // just use std::clamp if C++17 or above + return std::clamp(in, min, max); +#else + // otherwise, use my own implementation of std::clamp + return std::min(std::max(in, min), max); +#endif + } + + // get bitfield, bitfield(input, position, len) + template + T bitfield(T in, u8 pos, u8 len = 1) + { + return (in >> pos) & (len ? (T(1 << len) - 1) : 1); + } + + // get sign extended value, sign_ext(input, len) + template + T sign_ext(T in, u8 len) + { + len = std::max(0, (8 * sizeof(T)) - len); + return T(T(in) << len) >> len; + } + + // convert attenuation decibel value to gain + inline f32 dB_to_gain(f32 attenuation) { return std::pow(10.0f, attenuation / 20.0f); } + + private: + std::string m_tag = ""; // core tags + }; + + class vgsound_emu_mem_intf : public vgsound_emu_core + { + public: + // constructor + vgsound_emu_mem_intf() + : vgsound_emu_core("mem_intf") + { + } + + virtual u8 read_byte(u32 address) { return 0; } + + virtual u16 read_word(u32 address) { return 0; } + + virtual u32 read_dword(u32 address) { return 0; } + + virtual u64 read_qword(u32 address) { return 0; } + + virtual void write_byte(u32 address, u8 data) {} + + virtual void write_word(u32 address, u16 data) {} + + virtual void write_dword(u32 address, u32 data) {} + + virtual void write_qword(u32 address, u64 data) {} + }; + + template + class clock_pulse_t : public vgsound_emu_core + { + private: + const T m_init_width = 1; + + class edge_t : public vgsound_emu_core + { + private: + const u8 m_init_edge = 1; + + public: + edge_t(u8 init_edge = 0) + : vgsound_emu_core("clock_pulse_edge") + , m_init_edge(init_edge) + , m_current(init_edge ^ 1) + , m_previous(init_edge) + , m_rising(0) + , m_falling(0) + , m_changed(0) + { + set(init_edge); + } + + // internal states + void reset() + { + m_previous = m_init_edge; + m_current = m_init_edge ^ 1; + set(m_init_edge); + } + + void tick(bool toggle) + { + u8 current = m_current; + if (toggle) + { + current ^= 1; + } + set(current); + } + + void set(u8 edge) + { + edge &= 1; + m_rising = m_falling = m_changed = 0; + if (m_current != edge) + { + m_changed = 1; + if (m_current && (!edge)) + { + m_falling = 1; + } + else if ((!m_current) && edge) + { + m_rising = 1; + } + m_current = edge; + } + m_previous = m_current; + } + + // getters + inline bool current() { return m_current; } + + inline bool rising() { return m_rising; } + + inline bool falling() { return m_falling; } + + inline bool changed() { return m_changed; } + + private: + u8 m_current : 1; // current edge + u8 m_previous : 1; // previous edge + u8 m_rising : 1; // rising edge + u8 m_falling : 1; // falling edge + u8 m_changed : 1; // changed flag + }; + + public: + clock_pulse_t(T init_width, u8 init_edge = 0) + : vgsound_emu_core("clock_pulse") + , m_init_width(init_width) + , m_edge(edge_t(init_edge & 1)) + , m_width(init_width) + , m_width_latch(init_width) + , m_counter(init_width) + , m_cycle(0) + { + } + + void reset(T init) + { + m_edge.reset(); + m_width = m_width_latch = m_counter = init; + m_cycle = 0; + } + + inline void reset() { reset(m_init_width); } + + bool tick(T width = 0) + { + bool carry = ((--m_counter) <= 0); + if (carry) + { + if (!width) + { + m_width = m_width_latch; + } + else + { + m_width = width; // reset width + } + m_counter = m_width; + m_cycle = 0; + } + else + { + m_cycle++; + } + + m_edge.tick(carry); + return carry; + } + + inline void set_width(T width) { m_width = width; } + + inline void set_width_latch(T width) { m_width_latch = width; } + + // Accessors + inline bool current_edge() { return m_edge.current(); } + + inline bool rising_edge() { return m_edge.rising(); } + + inline bool falling_edge() { return m_edge.falling(); } + + // getters + edge_t &edge() { return m_edge; } + + inline T cycle() { return m_cycle; } + + private: + edge_t m_edge; + T m_width = 1; // clock pulse width + T m_width_latch = 1; // clock pulse width latch + T m_counter = 1; // clock counter + T m_cycle = 0; // clock cycle + }; +}; // namespace vgsound_emu + +using namespace vgsound_emu; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp new file mode 100644 index 00000000..ef06c151 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp @@ -0,0 +1,75 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Dialogic ADPCM core +*/ + +#include "vox.hpp" + +// reset decoder +void vox_core::vox_decoder_t::decoder_state_t::reset() +{ + m_index = 0; + m_step = 16; +} + +// copy from source +void vox_core::vox_decoder_t::decoder_state_t::copy(decoder_state_t src) +{ + m_index = src.index(); + m_step = src.step(); +} + +// decode single nibble +void vox_core::vox_decoder_t::decoder_state_t::decode(u8 nibble) +{ + const u8 delta = bitfield(nibble, 0, 3); + const s16 ss = m_vox.m_step_table[m_index]; // ss(n) + + // d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + // + (ss(n) / 8) + s16 d = ss >> 3; + if (bitfield(delta, 2)) + { + d += ss; + } + if (bitfield(delta, 1)) + { + d += (ss >> 1); + } + if (bitfield(delta, 0)) + { + d += (ss >> 2); + } + + // if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n) + if (bitfield(nibble, 3)) + { + m_step -= d; + } + else + { + m_step += d; + } + + if (m_wraparound) // wraparound (MSM5205) + { + if (m_step < -2048) + { + m_step &= 0x7ff; + } + else if (m_step > 2047) + { + m_step |= ~0x7ff; + } + } + else + { + m_step = clamp(m_step, -2048, 2047); + } + + // adjust step index + m_index = clamp(m_index + m_vox.m_index_table[delta], 0, 48); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp new file mode 100644 index 00000000..f3ae6d4c --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -0,0 +1,115 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Dialogic ADPCM core +*/ + +#ifndef _VGSOUND_EMU_SRC_CORE_VOX_VOX_HPP +#define _VGSOUND_EMU_SRC_CORE_VOX_VOX_HPP + +#pragma once + +#include "../util.hpp" + +class vox_core : public vgsound_emu_core +{ + protected: + class vox_decoder_t : public vgsound_emu_core + { + private: + class decoder_state_t : public vgsound_emu_core + { + public: + decoder_state_t(vox_core &vox, bool wraparound) + : vgsound_emu_core("vox_decoder_state") + , m_wraparound(wraparound) + , m_vox(vox) + , m_index(0) + , m_step(16) + { + } + + // internal states + void reset(); + void decode(u8 nibble); + + // getters + s8 index() { return m_index; } + + s32 step() { return m_step; } + + decoder_state_t &operator=(decoder_state_t src) + { + copy(src); + return *this; + } + + private: + const bool m_wraparound = false; // wraparound or clamp? + + void copy(decoder_state_t src); + + vox_core &m_vox; + s8 m_index = 0; + s32 m_step = 16; + }; + + public: + vox_decoder_t(vox_core &vox, bool wraparound) + : vgsound_emu_core("vox_decoder") + , m_curr(vox, wraparound) + , m_loop(vox, wraparound) + , m_loop_saved(false) + { + } + + virtual void reset() + { + m_curr.reset(); + m_loop.reset(); + m_loop_saved = false; + } + + void save() + { + if (!m_loop_saved) + { + m_loop = m_curr; + m_loop_saved = true; + } + } + + void restore() + { + if (m_loop_saved) + { + m_curr = m_loop; + } + } + + void decode(u8 nibble) { m_curr.decode(nibble); } + + s32 step() { return m_curr.step(); } + + private: + decoder_state_t m_curr; + decoder_state_t m_loop; + bool m_loop_saved = false; + }; + + const s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; + const s32 m_step_table[49] = { + 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, + 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, + 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552}; + + public: + vox_core(std::string tag) + : vgsound_emu_core(tag) + { + } +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/README.md b/vgsound_emu-modified/vgsound_emu/src/es550x/README.md new file mode 100644 index 00000000..6f410e7d --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/README.md @@ -0,0 +1,88 @@ +# Ensoniq ES5504 DOCII, ES5505 OTIS, ES5506 OTTO + +## Summary + +- 32 voice of PCMs + - per-voice features: + - 4 pole, 4 mode filter (2 filter coefficient, 12 bit per them) + - max 16 output, 12 bit volume (ES5504) + - max 4 stereo output channels, 8 bit logarithmic volume per left/right output (ES5505) + - max 6 stereo output channels, 12 bit logarithmic volume per left/right output (ES5506) + - Hardware envelope for volume and filter (ES5506) + - 16 bit linear PCM + - 8 bit compressed PCM (ES5506) + - total accessible memory: 1 MWord (2MByte) per bank (ES5504, ES5505) or 2 MWord (4MByte) per bank (ES5506) + - CA flag is also usable for bank + - 1 bit BS flag for bank - 2 bank total (ES5505) + - 2 bit BS flag for bank - 4 bank total (ES5506) + +## Source code + +- es550x.hpp: Base header + - es550x.cpp: Emulation core for common shared features + - es550x_alu.cpp: Emulation core for shared ALU function + - es550x_filter.cpp: Emulation core for shared filter function + - es5504.hpp: ES5504 header + - es5504.cpp: Emulation core for ES5504 specific features + - es5505.hpp: ES5505 header + - es5505.cpp: Emulation core for ES5505 specific features + - es5506.hpp: ES5506 header + - es5506.cpp: Emulation core for ES5506 specific features + +## Description + +After ES5503 DOC's appeared, Ensoniq announces ES5504 DOC II, ES5505 OTIS, ES5506 OTTO. + +These are not just PCM chip; but with built-in 4 pole filters and variable voice limits. + +It can be trades higher sample rate and finer frequency and Tons of voices, or vice-versa. + +These are mainly used with their synthesizers, musical stuffs. It's also mainly paired with ES5510 ESP/ES5511 ESP2 for post processing. ES5506 can be paired with itself, It's called Dual chip configuration and Both chips are can be shares same memory spaces. + +ES5505 was also mainly used on Taito's early- to late-90s arcade hardware for their PCM sample based sound system, paired with ES5510 ESP for post processing. It's configuration is borrowed from Ensoniq's 32 Voice synths powered by these chips. It's difference is external logic to adds per-voice bankswitching looks like what Konami doing on K007232. + +Atari Panther was will be use ES5505, but finally canceled. + +Ensoniq's ISA Sound Card for PC, Soundscape used ES5506, "Elite" model has optional daughterboard with ES5510 for digital effects. + +## Related chips + +- ES5530 "OPUS" variant is 2-in-one chip with built-in ES5506 and Sequoia. + +- ES5540 "OTTOFX" variant is ES5506 and ES5510 merged in single package. + +- ES5548 "OTTO48" variant is used at late-90s ensoniq synths and musical instruments, 2 ES5506s are merged in single package, or with 48 voices in chip? + +## Chip difference + +### ES5504 to ES5505 + +- Total voice amount is expanded to 32, rather than 25. + +- ADC and DAC is completely redesigned. + +- it's has now voice-independent 10 bit and Sony/Burr-Brown format DAC. + +- Output channel and Volume is changed to 16 mono to 4 stereo, 12 bit Analog to 8 bit Stereo digital, also Floating point-ish format and independent per left and right output. + +- Channel 3 is can be Input/Output. + +- Channel output is can be accessible at host for test purpose. + +- Max sample memory is expanded to 2MWords (1MWords * 2 Banks) + +### ES5505 to ES5506 + +- Frequency is more finer now: 11 bit fraction rather than 9 bit. + +- Output channel and Volume is changed to 4 stereo to 6 stereo, 8 bit to 16 bit, but only 12 bit is used for calculation; 4 LSB is used for envelope ramping. + +- Transwave flag is added - its helpful for transwave process, with interrupt per voices. Hardware envelope is added - K1, K2, Volume value is can be modified in run-time. also K1, K2 is expanded to 16 bit for finer envelope ramping. + +- Filter calculation resolution is expanded to 18 bit. + +- All channels are output, Serial output is now partially programmable. + +- Max sample memory is expanded to 8MWords (2MWords * 4 Banks) + +- Register format between these chips are incompatible. diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp new file mode 100644 index 00000000..586f15f3 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp @@ -0,0 +1,456 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504 emulation core +*/ + +#include "es5504.hpp" + +// Internal functions +void es5504_core::tick() +{ + m_voice_update = false; + m_voice_end = false; + // /CAS, E + if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock + { + // /CAS + if (m_cas.tick()) + { + // /CAS high, E low: get sample address + if (m_cas.falling_edge()) + { + // /CAS low, E low: fetch sample + if (!m_e.current_edge()) + { + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + } + // E + if (m_clkin.falling_edge()) // falling edge triggers E clock + { + if (m_e.tick()) + { + m_intf.e_pin(m_e.current_edge()); + if (m_e.rising_edge()) // Host access + { + m_host_intf.update_strobe(); + voice_tick(); + } + if (m_e.falling_edge()) // Voice memory + { + m_host_intf.clear_host_access(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + if (m_e.current_edge()) // Host interface + { + if (m_host_intf.host_access()) + { + if (m_host_intf.rw() && (m_e.cycle() == 2)) // Read + { + m_hd = read(m_ha); + m_host_intf.clear_host_access(); + } + else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) + { // Write + write(m_ha, m_hd); + } + } + } + else if (!m_e.current_edge()) + { + if (m_e.cycle() == 2) + { + // reset host access state + m_hd = 0; + m_host_intf.clear_strobe(); + } + } + } + } +} + +// less cycle accurate, but less CPU heavy routine +void es5504_core::tick_perf() +{ + m_voice_update = false; + m_voice_end = false; + // update + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); +} + +void es5504_core::voice_tick() +{ + // Voice updates every 2 E clock cycle (= 1 CHSTRB cycle or 4 BCLK clock cycle) + m_voice_update = bitfield(m_voice_fetch++, 0); + if (m_voice_update) + { + // Update voice + m_voice[m_voice_cycle].tick(m_voice_cycle); + + // Refresh output (Multiplexed analog output) + m_out[m_voice[m_voice_cycle].cr().ca()] = m_voice[m_voice_cycle].out(); + + if ((++m_voice_cycle) > std::min(24, m_active)) // ~ 25 voices + { + m_voice_end = true; + m_voice_cycle = 0; + } + + m_voice_fetch = 0; + } +} + +void es5504_core::voice_t::fetch(u8 voice, u8 cycle) +{ + m_alu.set_sample( + cycle, + m_host.m_intf.read_sample(voice, + bitfield(m_cr.ca(), 0, 3), + bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); +} + +void es5504_core::voice_t::tick(u8 voice) +{ + m_out = 0; + + // Filter execute + m_filter.tick(m_alu.interpolation()); + + if (m_alu.busy()) + { + // Send to output + m_out = ((sign_ext(m_filter.o4_1(), 16) >> 3) * m_volume) >> + 12; // Analog multiplied in real chip, 13/12 bit ladder DAC + + // ALU execute + if (m_alu.tick()) + { + m_alu.loop_exec(); + } + + // ADC check + adc_exec(); + } + + // Update IRQ + m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); +} + +// ADC; Correct? +void es5504_core::voice_t::adc_exec() +{ + if (m_cr.adc()) + { + m_host.m_adc = m_host.m_intf.adc_r() & ~0x7; + } +} + +void es5504_core::reset() +{ + es550x_shared_core::reset(); + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_adc = 0; + std::fill(m_out.begin(), m_out.end(), 0); +} + +void es5504_core::voice_t::reset() +{ + es550x_shared_core::es550x_voice_t::reset(); + m_volume = 0; + m_out = 0; +} + +// Accessors +u16 es5504_core::host_r(u8 address) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + if (m_e.rising_edge()) + { // update directly + m_hd = read(m_ha, true); + } + else + { + m_host_intf.set_strobe(true); + } + } + return m_hd; +} + +void es5504_core::host_w(u8 address, u16 data) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + m_hd = data; + if (m_e.rising_edge()) + { // update directly + write(m_ha, m_hd, true); + } + else + { + m_host_intf.set_strobe(false); + } + } +} + +u16 es5504_core::read(u8 address, bool cpu_access) { return regs_r(m_page, address, cpu_access); } + +void es5504_core::write(u8 address, u16 data, bool cpu_access) +{ + regs_w(m_page, address, data, cpu_access); +} + +u16 es5504_core::regs_r(u8 page, u8 address, bool cpu_access) +{ + u16 ret = 0xffff; + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 12) // Global registers + { + switch (address) + { + case 12: // A/D (A to D Convert/Test) + ret = (ret & ~0xfffb) | (m_adc & 0xfffb); + break; + case 13: // ACT (Number of voices) + ret = (ret & ~0x1f) | bitfield(m_active, 0, 5); + break; + case 14: // IRQV (Interrupting voice vector) + ret = (ret & ~0x9f) | m_irqv.get(); + if (cpu_access) + { + m_irqv.clear(); + if (bool(bitfield(ret, 7)) != m_irqv.irqb()) + { + m_voice[m_irqv.voice()].alu().irq_update(m_intf, m_irqv); + } + } + break; + case 15: // PAGE (Page select register) + ret = (ret & ~0x3f) | bitfield(m_page, 0, 6); + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + if (voice < 25) + { + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 56 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + ret = v.filter().o4_1(); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + ret = v.filter().o3_2(); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + ret = v.filter().o3_1(); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + ret = v.filter().o2_2(); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + ret = v.filter().o2_1(); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + ret = v.filter().o1_1(); + break; + } + } + else // Page 0 - 24 + { + switch (address) + { + case 0: // CR (Control Register) + ret = (ret & ~0xff) | (v.alu().stop() ? 0x01 : 0x00) | + (v.cr().adc() ? 0x04 : 0x00) | (v.alu().lpe() ? 0x08 : 0x00) | + (v.alu().ble() ? 0x10 : 0x00) | (v.alu().irqe() ? 0x20 : 0x00) | + (v.alu().dir() ? 0x40 : 0x00) | (v.alu().irq() ? 0x80 : 0x00); + break; + case 1: // FC (Frequency Control) + ret = (ret & ~0xfffe) | (v.alu().fc() << 1); + break; + case 2: // STRT-H (Loop Start Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().start(), 16, 13); + break; + case 3: // STRT-L (Loop Start Register Low) + ret = (ret & ~0xffe0) | (v.alu().start() & 0xffe0); + break; + case 4: // END-H (Loop End Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().end(), 16, 13); + break; + case 5: // END-L (Loop End Register Low) + ret = (ret & ~0xffe0) | (v.alu().end() & 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + ret = (ret & ~0xfff0) | (v.filter().k2() & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + ret = (ret & ~0xfff0) | (v.filter().k1() & 0xfff0); + break; + case 8: // Volume + ret = (ret & ~0xfff0) | ((v.volume() << 4) & 0xfff0); + break; + case 9: // CA (Filter Config, Channel Assign) + ret = (ret & ~0x3f) | bitfield(v.cr().ca(), 0, 4) | + (bitfield(v.filter().lp(), 0, 2) << 4); + break; + case 10: // ACCH (Accumulator High) + ret = (ret & ~0x1fff) | bitfield(v.alu().accum(), 16, 13); + break; + case 11: // ACCL (Accumulator Low) + ret = bitfield(v.alu().accum(), 0, 16); + break; + } + } + } + } + + return ret; +} + +void es5504_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) +{ + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 12) // Global registers + { + switch (address) + { + case 12: // A/D (A to D Convert/Test) + if (bitfield(m_adc, 0)) // Writam_ble ADC + { + m_adc = (m_adc & 7) | (data & ~7); + m_intf.adc_w(m_adc & ~7); + } + m_adc = (m_adc & ~3) | (data & 3); + break; + case 13: // ACT (Number of voices) + m_active = std::min(24, bitfield(data, 0, 5)); + break; + case 14: // IRQV (Interrupting voice vector) + // Read only + break; + case 15: // PAGE (Page select register) + m_page = bitfield(data, 0, 6); + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + if (voice < 25) + { + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 56 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + v.filter().set_o4_1(sign_ext(data, 16)); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + v.filter().set_o3_2(sign_ext(data, 16)); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + v.filter().set_o3_1(sign_ext(data, 16)); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + v.filter().set_o2_2(sign_ext(data, 16)); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + v.filter().set_o2_1(sign_ext(data, 16)); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + v.filter().set_o1_1(sign_ext(data, 16)); + break; + } + } + else // Page 0 - 24 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.cr().set_adc(bitfield(data, 2)); + v.alu().set_lpe(bitfield(data, 3)); + v.alu().set_ble(bitfield(data, 4)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + break; + case 1: // FC (Frequency Control) + v.alu().set_fc(bitfield(data, 1, 15)); + break; + case 2: // STRT-H (Loop Start Register High) + v.alu().set_start(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 3: // STRT-L (Loop Start Register Low) + v.alu().set_start(data & 0xffe0, 0xffe0); + break; + case 4: // END-H (Loop End Register High) + v.alu().set_end(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 5: // END-L (Loop End Register Low) + v.alu().set_end(data & 0xffe0, 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + v.filter().set_k2(data & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + v.filter().set_k1(data & 0xfff0); + break; + case 8: // Volume + v.set_volume(bitfield(data, 4, 12)); + break; + case 9: // CA (Filter Config, Channel Assign) + v.cr().set_ca(bitfield(data, 0, 4)); + v.filter().set_lp(bitfield(data, 4, 2)); + break; + case 10: // ACCH (Accumulator High) + v.alu().set_accum(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 11: // ACCL (Accumulator Low) + v.alu().set_accum(data, 0xffff); + break; + } + } + } + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp new file mode 100644 index 00000000..d37ef36d --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp @@ -0,0 +1,117 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES5504_HPP +#define _VGSOUND_EMU_SRC_ES5504_HPP + +#pragma once + +#include "es550x.hpp" + +// ES5504 specific +class es5504_core : public es550x_shared_core +{ + private: + // es5504 voice classes + class voice_t : public es550x_voice_t + { + public: + // constructor + voice_t(es5504_core &host) + : es550x_voice_t("es5504_voice", 20, 9, false) + , m_host(host) + , m_volume(0) + , m_out(0) + { + } + + // internal state + virtual void reset() override; + virtual void fetch(u8 voice, u8 cycle) override; + virtual void tick(u8 voice) override; + + // setters + inline void set_volume(u16 volume) { m_volume = volume; } + + // getters + inline u16 volume() { return m_volume; } + + inline s32 out() { return m_out; } + + private: + void adc_exec(); + + // registers + es5504_core &m_host; + u16 m_volume = 0; // 12 bit Volume + s32 m_out = 0; // channel outputs + }; + + public: + // constructor + es5504_core(es550x_intf &intf) + : es550x_shared_core("es5504", 25, intf) + , m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this} + , m_adc(0) + , m_out{0} + { + } + + // host interface + u16 host_r(u8 address); + void host_w(u8 address, u16 data); + + // internal state + virtual void reset() override; + virtual void tick() override; + + // less cycle accurate, but also less cpu heavy update routine + void tick_perf(); + + // 16 analog output channels + inline s32 out(u8 ch) { return m_out[ch & 0xf]; } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // bypass chips host interface for debug purpose only + u16 read(u8 address, bool cpu_access = false); + void write(u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address, bool cpu_access = false); + void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address) + { + u8 prev = m_page; + m_page = page; + u16 ret = read(address, false); + m_page = prev; + return ret; + } + + // per-voice outputs + inline s32 voice_out(u8 voice) { return (voice < 25) ? m_voice[voice].out() : 0; } + + protected: + virtual inline u8 max_voices() override { return 25; } + + virtual void voice_tick() override; + + private: + std::array m_voice; // 25 voices + u16 m_adc = 0; // ADC register + std::array m_out = {0}; // 16 channel outputs +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp new file mode 100644 index 00000000..07a7b704 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp @@ -0,0 +1,652 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5505 emulation core +*/ + +#include "es5505.hpp" + +// Internal functions +void es5505_core::tick() +{ + m_voice_update = false; + m_voice_end = false; + // CLKIN + if (m_clkin.tick()) + { + // SERBCLK + if (m_clkin.edge().changed()) // BCLK is freely running clock + { + if (m_bclk.tick()) + { + m_intf.bclk(m_bclk.current_edge()); + // Serial output + if (m_bclk.falling_edge()) + { + // SERLRCLK + if (m_lrclk.tick()) + { + m_intf.lrclk(m_lrclk.current_edge()); + } + } + // SERWCLK + if (m_lrclk.edge().changed()) + { + m_wclk = 0; + } + if (m_bclk.falling_edge()) + { + if (m_wclk == ((m_sermode.sony_bb()) ? 1 : 0)) + { + if (m_lrclk.current_edge()) + { + for (int i = 0; i < 4; i++) + { + // copy output + m_output[i] = m_output_temp[i]; + m_output_latch[i] = m_ch[i]; + m_output_temp[i].reset(); + // clamp to 16 bit (upper 5 bits are overflow + // guard bits) + m_output_latch[i].clamp16(); + // set signed + if (m_output_latch[i].left() < 0) + { + m_output_temp[i].set_left(-1); + } + if (m_output_latch[i].right() < 0) + { + m_output_temp[i].set_right(-1); + } + } + } + m_wclk_lr = m_lrclk.current_edge(); + m_output_bit = 16; + } + s8 output_bit = --m_output_bit; + if (m_output_bit >= 0) + { + for (int i = 0; i < 4; i++) + { + if (m_wclk_lr) + { // Right output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].right(), output_bit)); + } + else + { // Left output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].left(), output_bit)); + } + } + } + m_wclk++; + } + } + } + // /CAS, E + if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock + { + // /CAS + if (m_cas.tick()) + { + // /CAS high, E low: get sample address + if (m_cas.falling_edge()) + { + // /CAS low, E low: fetch sample + if (!m_e.current_edge()) + { + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + } + // E + if (m_e.tick()) + { + m_intf.e_pin(m_e.current_edge()); + if (m_e.rising_edge()) // Host access + { + m_host_intf.update_strobe(); + voice_tick(); + } + else if (m_e.falling_edge()) // Voice memory + { + m_host_intf.clear_host_access(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + if (m_e.current_edge()) // Host interface + { + if (m_host_intf.host_access()) + { + if (m_host_intf.rw() && (m_e.cycle() == 2)) // Read + { + m_hd = read(m_ha); + m_host_intf.clear_host_access(); + } + else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) + { // Write + write(m_ha, m_hd); + } + } + } + else if (!m_e.current_edge()) + { + if (m_e.cycle() == 2) + { + // reset host access state + m_hd = 0; + m_host_intf.clear_strobe(); + } + } + } + } + } +} + +// less cycle accurate, but less CPU heavy routine +void es5505_core::tick_perf() +{ + m_voice_update = false; + m_voice_end = false; + // output + for (int c = 0; c < 4; c++) + { + m_output[c] = m_ch[c]; + } + + // update + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); +} + +void es5505_core::voice_tick() +{ + // Voice updates every 2 E clock cycle (or 4 BCLK clock cycle) + m_voice_update = bitfield(m_voice_fetch++, 0); + if (m_voice_update) + { + // Update voice + m_voice[m_voice_cycle].tick(m_voice_cycle); + + // Refresh output + if ((++m_voice_cycle) > clamp(m_active, 7, 31)) // 8 ~ 32 voices + { + m_voice_end = true; + m_voice_cycle = 0; + for (auto &elem : m_ch) + { + elem.reset(); + } + + for (auto &elem : m_voice) + { + m_ch[bitfield(elem.cr().ca(), 0, 2)] += elem.ch(); + elem.ch().reset(); + } + } + m_voice_fetch = 0; + } +} + +void es5505_core::voice_t::fetch(u8 voice, u8 cycle) +{ + m_alu.set_sample( + cycle, + m_host.m_intf.read_sample(voice, + bitfield(m_cr.bs(), 0), + bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); +} + +void es5505_core::voice_t::tick(u8 voice) +{ + m_ch.reset(); + + // Filter execute + m_filter.tick(m_alu.interpolation()); + + if (m_alu.busy()) + { + // Send to output + m_ch.set_left(volume_calc(m_lvol, sign_ext(m_filter.o4_1(), 16))); + m_ch.set_right(volume_calc(m_rvol, sign_ext(m_filter.o4_1(), 16))); + + // ALU execute + if (m_alu.tick()) + { + m_alu.loop_exec(); + } + } + + // Update IRQ + m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); +} + +// volume calculation +s32 es5505_core::voice_t::volume_calc(u8 volume, s32 in) +{ + u8 exponent = bitfield(volume, 4, 4); + u8 mantissa = bitfield(volume, 0, 4); + return exponent ? (in * s32(0x10 | mantissa)) >> (19 - exponent) : 0; +} + +void es5505_core::reset() +{ + es550x_shared_core::reset(); + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_sermode.reset(); + m_bclk.reset(); + m_lrclk.reset(); + m_wclk = 0; + m_wclk_lr = false; + m_output_bit = 0; + for (auto &elem : m_ch) + { + elem.reset(); + } + for (auto &elem : m_output) + { + elem.reset(); + } + for (auto &elem : m_output_temp) + { + elem.reset(); + } + for (auto &elem : m_output_latch) + { + elem.reset(); + } +} + +void es5505_core::voice_t::reset() +{ + es550x_shared_core::es550x_voice_t::reset(); + m_lvol = 0; + m_rvol = 0; + m_ch.reset(); +} + +// Accessors +u16 es5505_core::host_r(u8 address) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + if (m_e.rising_edge()) + { // update directly + m_hd = read(m_ha, true); + } + else + { + m_host_intf.set_strobe(true); + } + } + return m_hd; +} + +void es5505_core::host_w(u8 address, u16 data) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + m_hd = data; + if (m_e.rising_edge()) + { // update directly + write(m_ha, m_hd, true); + } + else + { + m_host_intf.set_strobe(false); + } + } +} + +u16 es5505_core::read(u8 address, bool cpu_access) { return regs_r(m_page, address, cpu_access); } + +void es5505_core::write(u8 address, u16 data, bool cpu_access) +{ + regs_w(m_page, address, data, cpu_access); +} + +u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access) +{ + u16 ret = 0xffff; + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 13) // Global registers + { + switch (address) + { + case 13: // ACT (Number of voices) + ret = (ret & ~0x1f) | bitfield(m_active, 0, 5); + break; + case 14: // IRQV (Interrupting voice vector) + ret = (ret & ~0x9f) | m_irqv.get(); + if (cpu_access) + { + m_irqv.clear(); + if (bool(bitfield(ret, 7)) != m_irqv.irqb()) + { + m_voice[m_irqv.voice()].alu().irq_update(m_intf, m_irqv); + } + } + break; + case 15: // PAGE (Page select register) + ret = (ret & ~0x7f) | bitfield(m_page, 0, 7); + break; + } + } + else + { + if (bitfield(page, 6)) // Channel registers + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + case 2: // CH1L (Channel 1 Left) + case 4: // CH2L (Channel 2 Left) + if (!cpu_access) + { // CPU can't read here + ret = m_ch[bitfield(address, 0, 2)].left(); + } + break; + case 1: // CH0R (Channel 0 Right) + case 3: // CH1R (Channel 1 Right) + case 5: // CH2R (Channel 2 Right) + if (!cpu_access) + { // CPU can't read here + ret = m_ch[bitfield(address, 0, 2)].right(); + } + break; + case 6: // CH3L (Channel 3 Left) + if ((!cpu_access) || m_sermode.adc()) + { + ret = m_ch[3].left(); + } + break; + case 7: // CH3R (Channel 3 Right) + if ((!cpu_access) || m_sermode.adc()) + { + ret = m_ch[3].right(); + } + break; + case 8: // SERMODE (Serial Mode) + ret = (ret & ~0xf807) | (m_sermode.adc() ? 0x01 : 0x00) | + (m_sermode.test() ? 0x02 : 0x00) | (m_sermode.sony_bb() ? 0x04 : 0x00) | + (bitfield(m_sermode.msb(), 0, 5) << 11); + break; + case 9: // PAR (Port A/D Register) + ret = (ret & ~0x3f) | (m_intf.adc_r() & ~0x3f); + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 63 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + ret = v.filter().o4_1(); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + ret = v.filter().o3_2(); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + ret = v.filter().o3_1(); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + ret = v.filter().o2_2(); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + ret = v.filter().o2_1(); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + ret = v.filter().o1_1(); + break; + } + } + else // Page 0 - 31 + { + switch (address) + { + case 0: // CR (Control Register) + ret = (ret & ~0xfff) | (v.alu().stop() << 0) | + (bitfield(v.cr().bs(), 0) ? 0x04 : 0x00) | + (v.alu().lpe() ? 0x08 : 0x00) | (v.alu().ble() ? 0x10 : 0x00) | + (v.alu().irqe() ? 0x20 : 0x00) | (v.alu().dir() ? 0x40 : 0x00) | + (v.alu().irq() ? 0x80 : 0x00) | (bitfield(v.cr().ca(), 0, 2) << 8) | + (bitfield(v.filter().lp(), 0, 2) << 10); + break; + case 1: // FC (Frequency Control) + ret = (ret & ~0xfffe) | (bitfield(v.alu().fc(), 0, 15) << 1); + break; + case 2: // STRT-H (Loop Start Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().start(), 16, 13); + break; + case 3: // STRT-L (Loop Start Register Low) + ret = (ret & ~0xffe0) | (v.alu().start() & 0xffe0); + break; + case 4: // END-H (Loop End Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().end(), 16, 13); + break; + case 5: // END-L (Loop End Register Low) + ret = (ret & ~0xffe0) | (v.alu().end() & 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + ret = (ret & ~0xfff0) | (v.filter().k2() & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + ret = (ret & ~0xfff0) | (v.filter().k1() & 0xfff0); + break; + case 8: // LVOL (Left Volume) + ret = (ret & ~0xff00) | ((v.lvol() << 8) & 0xff00); + break; + case 9: // RVOL (Right Volume) + ret = (ret & ~0xff00) | ((v.rvol() << 8) & 0xff00); + break; + case 10: // ACCH (Accumulator High) + ret = (ret & ~0x1fff) | bitfield(v.alu().accum(), 16, 13); + break; + case 11: // ACCL (Accumulator Low) + ret = bitfield(v.alu().accum(), 0, 16); + break; + } + } + } + } + + return ret; +} + +void es5505_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) +{ + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 12) // Global registers + { + switch (address) + { + case 13: // ACT (Number of voices) + m_active = std::max(7, bitfield(data, 0, 5)); + break; + case 14: // IRQV (Interrupting voice vector) + // Read only + break; + case 15: // PAGE (Page select register) + m_page = bitfield(data, 0, 7); + break; + } + } + else // Voice specific registers + { + if (bitfield(page, 6)) // Channel registers + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + if (m_sermode.test()) + { + m_ch[0].set_left(data); + } + break; + case 1: // CH0R (Channel 0 Right) + if (m_sermode.test()) + { + m_ch[0].set_right(data); + } + break; + case 2: // CH1L (Channel 1 Left) + if (m_sermode.test()) + { + m_ch[1].set_left(data); + } + break; + case 3: // CH1R (Channel 1 Right) + if (m_sermode.test()) + { + m_ch[1].set_right(data); + } + break; + case 4: // CH2L (Channel 2 Left) + if (m_sermode.test()) + { + m_ch[2].set_left(data); + } + break; + case 5: // CH2R (Channel 2 Right) + if (m_sermode.test()) + { + m_ch[2].set_right(data); + } + break; + case 6: // CH3L (Channel 3 Left) + if (m_sermode.test()) + { + m_ch[3].set_left(data); + } + break; + case 7: // CH3R (Channel 3 Right) + if (m_sermode.test()) + { + m_ch[3].set_right(data); + } + break; + case 8: // SERMODE (Serial Mode) + m_sermode.write(data); + break; + case 9: // PAR (Port A/D Register) + // Read only + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 56 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + v.filter().set_o4_1(sign_ext(data, 16)); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + v.filter().set_o3_2(sign_ext(data, 16)); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + v.filter().set_o3_1(sign_ext(data, 16)); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + v.filter().set_o2_2(sign_ext(data, 16)); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + v.filter().set_o2_1(sign_ext(data, 16)); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + v.filter().set_o1_1(sign_ext(data, 16)); + break; + } + } + else // Page 0 - 24 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.cr().set_bs(bitfield(data, 2)); + v.alu().set_lpe(bitfield(data, 3)); + v.alu().set_ble(bitfield(data, 4)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + v.cr().set_ca(bitfield(data, 8, 2)); + v.filter().set_lp(bitfield(data, 10, 2)); + break; + case 1: // FC (Frequency Control) + v.alu().set_fc(bitfield(data, 1, 15)); + break; + case 2: // STRT-H (Loop Start Register High) + v.alu().set_start(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 3: // STRT-L (Loop Start Register Low) + v.alu().set_start(data & 0xffe0, 0xffe0); + break; + case 4: // END-H (Loop End Register High) + v.alu().set_end(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 5: // END-L (Loop End Register Low) + v.alu().set_end(data & 0xffe0, 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + v.filter().set_k2(data & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + v.filter().set_k1(data & 0xfff0); + break; + case 8: // LVOL (Left Volume) + v.set_lvol(bitfield(data, 8, 8)); + break; + case 9: // RVOL (Right Volume) + v.set_rvol(bitfield(data, 8, 8)); + break; + case 10: // ACCH (Accumulator High) + v.alu().set_accum(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 11: // ACCL (Accumulator Low) + v.alu().set_accum(data, 0xffff); + break; + } + } + } + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp new file mode 100644 index 00000000..34782f10 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -0,0 +1,304 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES5505_HPP +#define _VGSOUND_EMU_SRC_ES5505_HPP + +#pragma once + +#include "es550x.hpp" + +// ES5505 specific +class es5505_core : public es550x_shared_core +{ + private: + class output_t : public vgsound_emu_core + { + public: + output_t(s32 left = 0, s32 right = 0) + : vgsound_emu_core("es5505_output") + , m_left(left) + , m_right(right) + { + } + + void reset() + { + m_left = 0; + m_right = 0; + }; + + inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); } + + inline void clamp16(output_t src) + { + m_left = clamp16(src.left()); + m_right = clamp16(src.right()); + } + + inline void clamp16() + { + m_left = clamp16(m_left); + m_right = clamp16(m_right); + } + + // setters + inline void set_left(s32 left) { m_left = left; } + + inline void set_right(s32 right) { m_right = right; } + + inline void serial_in(bool ch, u8 in) + { + if (ch) // Right output + { + m_right = (m_right << 1) | (in ? 1 : 0); + } + else // Left output + { + m_left = (m_left << 1) | (in ? 1 : 0); + } + } + + // getters + inline u32 left() { return m_left; } + + inline u32 right() { return m_right; } + + output_t &operator+=(output_t &src) + { + m_left = clamp16(m_left + src.left()); + m_right = clamp16(m_right + src.right()); + return *this; + } + + output_t &operator=(output_t src) + { + clamp16(src); + return *this; + } + + output_t &operator=(s32 val) + { + m_left = m_right = clamp16(val); + return *this; + } + + output_t &operator>>(s32 shift) + { + m_left >>= shift; + m_right >>= shift; + return *this; + } + + private: + s32 m_left = 0; + s32 m_right = 0; + }; + + // es5505 voice classes + class voice_t : public es550x_voice_t + { + public: + // constructor + voice_t(es5505_core &host) + : es550x_voice_t("es5505_voice", 20, 9, false) + , m_host(host) + , m_lvol(0) + , m_rvol(0) + , m_ch(output_t()) + { + } + + // internal state + virtual void reset() override; + virtual void fetch(u8 voice, u8 cycle) override; + virtual void tick(u8 voice) override; + + // setters + inline void set_lvol(u8 lvol) { m_lvol = lvol; } + + inline void set_rvol(u8 rvol) { m_rvol = rvol; } + + // getters + inline u8 lvol() { return m_lvol; } + + inline u8 rvol() { return m_rvol; } + + output_t &ch() { return m_ch; } + + private: + s32 volume_calc(u8 volume, s32 in); + + // registers + es5505_core &m_host; + u8 m_lvol = 0; // Left volume + u8 m_rvol = 0; // Right volume + output_t m_ch; // channel output + }; + + class sermode_t : public vgsound_emu_core + { + public: + sermode_t() + : vgsound_emu_core("es5505_sermode") + , m_adc(0) + , m_test(0) + , m_sony_bb(0) + , m_msb(0) + { + } + + void reset() + { + m_adc = 0; + m_test = 0; + m_sony_bb = 0; + m_msb = 0; + } + + // setters + void write(u16 data) + { + m_adc = (data >> 0) & 1; + m_test = (data >> 1) & 1; + m_sony_bb = (data >> 2) & 1; + m_msb = (data >> 11) & 0x1f; + } + + void set_adc(bool adc) { m_adc = adc ? 1 : 0; } + + void set_test(bool test) { m_test = test ? 1 : 0; } + + void set_sony_bb(bool sony_bb) { m_sony_bb = sony_bb ? 1 : 0; } + + void set_msb(u8 msb) { m_msb = msb & 0x1f; } + + // getters + bool adc() { return m_adc; } + + bool test() { return m_test; } + + bool sony_bb() { return m_sony_bb; } + + u8 msb() { return m_msb; } + + private: + u8 m_adc : 1; // A/D + u8 m_test : 1; // Test + u8 m_sony_bb : 1; // Sony/BB format serial output + u8 m_msb : 5; // Serial output MSB + }; + + public: + // constructor + es5505_core(es550x_intf &intf) + : es550x_shared_core("es5505", 32, intf) + , m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this} + , m_sermode(sermode_t()) + , m_bclk(clock_pulse_t(4, 0)) + , m_lrclk(clock_pulse_t(16, 1)) + , m_wclk(0) + , m_wclk_lr(false) + , m_output_bit(0) + , m_ch{output_t()} + , m_output{output_t()} + , m_output_temp{output_t()} + , m_output_latch{output_t()} + { + } + + // host interface + u16 host_r(u8 address); + void host_w(u8 address, u16 data); + + // internal state + virtual void reset() override; + virtual void tick() override; + + // less cycle accurate, but also less cpu heavy update routine + void tick_perf(); + + // clock outputs + inline bool bclk() { return m_bclk.current_edge(); } + + inline bool bclk_rising_edge() { return m_bclk.rising_edge(); } + + inline bool bclk_falling_edge() { return m_bclk.falling_edge(); } + + // Input mode for Channel 3 + inline void lin(s32 in) + { + if (m_sermode.adc()) + { + m_ch[3].set_left(in); + } + } + + inline void rin(s32 in) + { + if (m_sermode.adc()) + { + m_ch[3].set_right(in); + } + } + + // 4 stereo output channels + inline s32 lout(u8 ch) { return m_ch[ch & 0x3].left(); } + + inline s32 rout(u8 ch) { return m_ch[ch & 0x3].right(); } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // bypass chips host interface for debug purpose only + u16 read(u8 address, bool cpu_access = false); + void write(u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address, bool cpu_access = false); + void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address) + { + u8 prev = m_page; + m_page = page; + u16 ret = read(address, false); + m_page = prev; + return ret; + } + + // per-voice outputs + inline s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].ch().left() : 0; } + + inline s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].ch().right() : 0; } + + protected: + virtual inline u8 max_voices() override { return 32; } + + virtual void voice_tick() override; + + private: + std::array m_voice; // 32 voices + // Serial related stuffs + sermode_t m_sermode; // Serial mode register + clock_pulse_t m_bclk; // BCLK clock (CLKIN / 4), freely running clock + clock_pulse_t m_lrclk; // LRCLK + s16 m_wclk = 0; // WCLK + bool m_wclk_lr = false; // WCLK, L/R output select + s8 m_output_bit = 0; // Bit position in output + std::array m_ch; // 4 stereo output channels + std::array m_output; // Serial outputs + std::array m_output_temp; // temporary signal for serial output + std::array m_output_latch; // output latch +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp new file mode 100644 index 00000000..ebb9a62d --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp @@ -0,0 +1,870 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5506 emulation core +*/ + +#include "es5506.hpp" + +// Internal functions +void es5506_core::tick() +{ + m_voice_update = false; + m_voice_end = false; + // CLKIN + if (m_clkin.tick()) + { + // BCLK + if (m_clkin.edge().changed() && (!m_mode.bclk_en())) // BCLK is freely running clock + { + if (m_bclk.tick()) + { + m_intf.bclk(m_bclk.current_edge()); + // Serial output + if (!m_mode.lrclk_en()) + { + if (m_bclk.falling_edge()) + { + // LRCLK + if (m_lrclk.tick()) + { + m_intf.lrclk(m_lrclk.current_edge()); + if (m_lrclk.rising_edge()) + { + m_w_st_curr = m_w_st; + m_w_end_curr = m_w_end; + } + if (m_lrclk.falling_edge()) + { // update width + m_lrclk.set_width_latch(m_lr_end); + } + } + } + } + // WCLK + if (!m_mode.wclk_en()) + { + if (!m_mode.lrclk_en()) + { + if (m_lrclk.edge().changed()) + { + m_wclk = 0; + } + } + if (m_bclk.falling_edge()) + { + if (m_wclk == m_w_st_curr) + { + m_intf.wclk(true); + if (m_lrclk.current_edge()) + { + for (int i = 0; i < 6; i++) + { + // copy output + m_output[i] = m_output_temp[i]; + m_output_latch[i] = m_ch[i]; + m_output_temp[i].reset(); + // clamp to 20 bit (upper 3 bits are + // overflow guard bits) + m_output_latch[i].clamp20(); + // set signed + if (m_output_latch[i].left() < 0) + { + m_output_temp[i].set_left(-1); + } + if (m_output_latch[i].right() < 0) + { + m_output_temp[i].set_right(-1); + } + } + } + m_wclk_lr = m_lrclk.current_edge(); + m_output_bit = 20; + } + if (m_wclk < m_w_end_curr) + { + s8 output_bit = --m_output_bit; + if (m_output_bit >= 0) + { + for (int i = 0; i < 6; i++) + { + if (m_wclk_lr) + { + // Right output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].right(), output_bit)); + } + else + { + // Left output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].left(), output_bit)); + } + } + } + } + if (m_wclk == m_w_end_curr) + { + m_intf.wclk(false); + } + + m_wclk++; + } + } + } + } + // /CAS, E + if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock + { + // /CAS + if (m_cas.tick()) + { + // single OTTO master mode, /CAS high, E low: get sample address + // single OTTO early mode, /CAS falling, E high: get sample + // address + if (m_cas.falling_edge()) + { + if (!m_e.current_edge()) + { + // single OTTO master mode, /CAS low, E low: fetch + // sample + if (m_mode.master()) + { + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + else if (m_e.current_edge()) + { + // dual OTTO slave mode, /CAS low, E high: fetch sample + if (m_mode.dual() && (!m_mode.master())) + { // Dual OTTO, slave mode + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + } + } + // E + if (m_e.tick()) + { + m_intf.e_pin(m_e.current_edge()); + if (m_e.rising_edge()) + { + m_host_intf.update_strobe(); + } + else if (m_e.falling_edge()) + { + m_host_intf.clear_host_access(); + voice_tick(); + } + if (m_e.current_edge()) // Host interface + { + if (m_host_intf.host_access()) + { + if (m_host_intf.rw() && (m_e.cycle() == 0)) // Read + { + m_hd = read(m_ha); + m_host_intf.clear_host_access(); + } + else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) + { // Write + write(m_ha, m_hd); + } + } + } + else if (!m_e.current_edge()) + { + if (m_e.cycle() == 2) + { + // reset host access state + m_hd = 0; + m_host_intf.clear_strobe(); + } + } + } + } + } +} + +// less cycle accurate, but less CPU heavy routine +void es5506_core::tick_perf() +{ + m_voice_update = false; + m_voice_end = false; + // output + if (((!m_mode.lrclk_en()) && (!m_mode.bclk_en()) && (!m_mode.wclk_en())) && (m_w_st < m_w_end)) + { + const int output_bits = 20 - (m_w_end - m_w_st); + if (output_bits < 20) + { + for (int c = 0; c < 6; c++) + { + m_output[c] = m_ch[c] >> output_bits; + } + } + } + else + { + for (int c = 0; c < 6; c++) + { + m_output[c] = 0; + } + } + + // update + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); +} + +void es5506_core::voice_tick() +{ + // Voice updates every 2 E clock cycle (or 4 BCLK clock cycle) + m_voice_update = bitfield(m_voice_fetch++, 0); + if (m_voice_update) + { + // Update voice + m_voice[m_voice_cycle].tick(m_voice_cycle); + + // Refresh output + if ((++m_voice_cycle) > clamp(m_active, 4, 31)) // 5 ~ 32 voices + { + m_voice_end = true; + m_voice_cycle = 0; + for (output_t &elem : m_ch) + { + elem.reset(); + } + + for (voice_t &elem : m_voice) + { + const u8 ca = bitfield(elem.cr().ca(), 0, 3); + if (ca < 6) + { + m_ch[ca] += elem.ch(); + } + elem.ch().reset(); + } + } + m_voice_fetch = 0; + } +} + +void es5506_core::voice_t::fetch(u8 voice, u8 cycle) +{ + m_alu.set_sample( + cycle, + m_host.m_intf.read_sample(voice, + m_cr.bs(), + bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); + if (m_cr.cmpd()) + { // Decompress (Upper 8 bit is used for compressed format) + m_alu.set_sample(cycle, decompress(bitfield(m_alu.sample(cycle), 8, 8))); + } +} + +void es5506_core::voice_t::tick(u8 voice) +{ + m_ch.reset(); + + // Filter execute + m_filter.tick(m_alu.interpolation()); + + if (m_alu.busy()) + { + // Send to output + m_ch.set_left(volume_calc(m_lvol, sign_ext(m_filter.o4_1(), 16))); + m_ch.set_right(volume_calc(m_rvol, sign_ext(m_filter.o4_1(), 16))); + + // ALU execute + if (m_alu.tick()) + { + m_alu.loop_exec(); + } + } + // Envelope + if (m_ecount != 0) + { + // Left and Right volume + if (bitfield(m_lvramp, 0, 8) != 0) + { + m_lvol = clamp(m_lvol + sign_ext(bitfield(m_lvramp, 0, 8), 8), 0, 0xffff); + } + if (bitfield(m_rvramp, 0, 8) != 0) + { + m_rvol = clamp(m_rvol + sign_ext(bitfield(m_rvramp, 0, 8), 8), 0, 0xffff); + } + + // Filter coeffcient + if ((m_k1ramp.ramp() != 0) && + ((m_k1ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0))) + { + m_filter.set_k1( + clamp(m_filter.k1() + sign_ext(m_k1ramp.ramp(), 8), 0, 0xffff)); + } + if ((m_k2ramp.ramp() != 0) && + ((m_k2ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0))) + { + m_filter.set_k2( + clamp(m_filter.k2() + sign_ext(m_k2ramp.ramp(), 8), 0, 0xffff)); + } + + m_ecount--; + } + m_filtcount = bitfield(m_filtcount + 1, 0, 3); + + // Update IRQ + m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); +} + +// Compressed format +s16 es5506_core::voice_t::decompress(u8 sample) +{ + u8 exponent = bitfield(sample, 5, 3); + u8 mantissa = bitfield(sample, 0, 5); + return (exponent > 0) + ? s16(((bitfield(mantissa, 4) ? 0x10 : ~0x1f) | bitfield(mantissa, 0, 4)) + << (4 + (exponent - 1))) + : s16(((bitfield(mantissa, 4) ? ~0xf : 0) | bitfield(mantissa, 0, 4)) << 4); +} + +// volume calculation +s32 es5506_core::voice_t::volume_calc(u16 volume, s32 in) +{ + u8 exponent = bitfield(volume, 12, 4); + u8 mantissa = bitfield(volume, 4, 8); + return (in * s32(0x100 | mantissa)) >> (20 - exponent); +} + +void es5506_core::reset() +{ + es550x_shared_core::reset(); + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_read_latch = 0xffffffff; + m_write_latch = 0xffffffff; + m_w_st = 0; + m_w_end = 0; + m_lr_end = 0; + m_w_st_curr = 0; + m_w_end_curr = 0; + m_mode.reset(); + m_bclk.reset(); + m_lrclk.reset(32); + m_wclk = 0; + m_wclk_lr = false; + m_output_bit = 0; + for (auto &elem : m_ch) + { + elem.reset(); + } + for (auto &elem : m_output) + { + elem.reset(); + } + for (auto &elem : m_output_temp) + { + elem.reset(); + } + for (auto &elem : m_output_latch) + { + elem.reset(); + } +} + +void es5506_core::voice_t::reset() +{ + es550x_shared_core::es550x_voice_t::reset(); + m_lvol = 0; + m_rvol = 0; + m_lvramp = 0; + m_rvramp = 0; + m_ecount = 0; + m_k2ramp.reset(); + m_k1ramp.reset(); + m_filtcount = 0; + m_ch.reset(); + m_mute = false; +} + +// Accessors +u8 es5506_core::host_r(u8 address) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + if (m_e.rising_edge()) + { // update directly + m_hd = read(m_ha, true); + } + else + { + m_host_intf.set_strobe(true); + } + } + return m_hd; +} + +void es5506_core::host_w(u8 address, u8 data) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + m_hd = data; + if (m_e.rising_edge()) + { // update directly + write(m_ha, m_hd, true); + } + else + { + m_host_intf.set_strobe(false); + } + } +} + +u8 es5506_core::read(u8 address, bool cpu_access) +{ + const u8 byte = bitfield(address, 0, 2); // byte select + const u8 shift = 24 - (byte << 3); + if (byte != 0) + { // Return already latched register if not highest byte is accessing + return bitfield(m_read_latch, shift, 8); + } + + address = bitfield(address, 2, 4); // 4 bit address for CPU access + + // get read register + m_read_latch = regs_r(m_page, address, cpu_access); + + return bitfield(m_read_latch, 24, 8); +} + +void es5506_core::write(u8 address, u8 data, bool cpu_access) +{ + const u8 byte = bitfield(address, 0, 2); // byte select + const u8 shift = 24 - (byte << 3); + address = bitfield(address, 2, 4); // 4 bit address for CPU access + + // Update register latch + m_write_latch = (m_write_latch & ~(0xff << shift)) | (u32(data) << shift); + + if (byte != 3) + { // Wait until lowest byte is writed + return; + } + + regs_w(m_page, address, m_write_latch, cpu_access); + + // Reset latch + m_write_latch = 0; +} + +u32 es5506_core::regs_r(u8 page, u8 address, bool cpu_access) +{ + u32 read_latch = 0xffffffff; + // Global registers + if (address >= 13) + { + switch (address) + { + case 13: // POT (Pot A/D Register) + read_latch = (read_latch & ~0x3ff) | bitfield(m_intf.adc_r(), 0, 10); + break; + case 14: // IRQV (Interrupting voice vector) + read_latch = (read_latch & ~0x9f) | (m_irqv.irqb() ? 0x80 : 0) | + bitfield(m_irqv.voice(), 0, 5); + if (cpu_access) + { + m_irqv.clear(); + if (bool(bitfield(read_latch, 7)) != m_irqv.irqb()) + { + m_voice[m_irqv.voice()].irq_update(m_intf, m_irqv); + } + } + break; + case 15: // PAGE (Page select register) + read_latch = (read_latch & ~0x7f) | bitfield(m_page, 0, 7); + break; + } + } + else + { + // Channel registers are Write only + if (bitfield(page, 6)) + { + if (!cpu_access) // CPU can't read here + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + case 2: // CH1L (Channel 1 Left) + case 4: // CH2L (Channel 2 Left) + case 6: // CH3L (Channel 3 Left) + case 8: // CH4L (Channel 4 Left) + case 10: // CH5L (Channel 5 Left) + read_latch = m_ch[bitfield(address, 1, 3)].left(); + break; + case 1: // CH0R (Channel 0 Right) + case 3: // CH1R (Channel 1 Right) + case 5: // CH2R (Channel 2 Right) + case 7: // CH3R (Channel 3 Right) + case 9: // CH4R (Channel 4 Right) + case 11: // CH5R (Channel 5 Right) + read_latch = m_ch[bitfield(address, 1, 3)].right(); + break; + } + } + } + else + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 63 + { + switch (address) + { + case 0: // CR (Control Register) + read_latch = (read_latch & ~0xffff) | (v.alu().stop() << 0) | + (v.alu().lei() ? 0x0004 : 0x0000) | (v.alu().loop() << 3) | + (v.alu().irqe() ? 0x0020 : 0x0000) | + (v.alu().dir() ? 0x0040 : 0x0000) | + (v.alu().irq() ? 0x0080 : 0x0000) | + (bitfield(v.filter().lp(), 0, 2) << 8) | (v.cr().ca() << 10) | + (v.cr().cmpd() ? 0x2000 : 0x0000) | (v.cr().bs() << 14); + break; + case 1: // START (Loop Start Register) + read_latch = (read_latch & ~0xfffff800) | (v.alu().start() & 0xfffff800); + break; + case 2: // END (Loop End Register) + read_latch = (read_latch & ~0xffffff80) | (v.alu().end() & 0xffffff80); + break; + case 3: // ACCUM (Accumulator Register) + read_latch = v.alu().accum(); + break; + case 4: // O4(n-1) (Filter 4 Temp Register) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o4_1(), 0, 18); + } + else + { + read_latch = v.filter().o4_1(); + } + break; + case 5: // O3(n-2) (Filter 3 Temp Register #2) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o3_2(), 0, 18); + } + else + { + read_latch = v.filter().o3_2(); + } + break; + case 6: // O3(n-1) (Filter 3 Temp Register #1) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o3_1(), 0, 18); + } + else + { + read_latch = v.filter().o3_1(); + } + break; + case 7: // O2(n-2) (Filter 2 Temp Register #2) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o2_2(), 0, 18); + } + else + { + read_latch = v.filter().o2_2(); + } + break; + case 8: // O2(n-1) (Filter 2 Temp Register #1) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o2_1(), 0, 18); + } + else + { + read_latch = v.filter().o2_1(); + } + break; + case 9: // O1(n-1) (Filter 1 Temp Register) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o1_1(), 0, 18); + } + else + { + read_latch = v.filter().o1_1(); + } + break; + case 10: // W_ST (Word Clock Start Register) + read_latch = (read_latch & ~0x7f) | bitfield(m_w_st, 0, 7); + break; + case 11: // W_END (Word Clock End Register) + read_latch = (read_latch & ~0x7f) | bitfield(m_w_end, 0, 7); + break; + case 12: // LR_END (Left/Right Clock End Register) + read_latch = (read_latch & ~0x7f) | bitfield(m_lr_end, 0, 7); + break; + } + } + else // Page 0 - 31 + { + switch (address) + { + case 0: // CR (Control Register) + read_latch = (read_latch & ~0xffff) | (v.alu().stop() << 0) | + (v.alu().lei() ? 0x0004 : 0x0000) | (v.alu().loop() << 3) | + (v.alu().irqe() ? 0x0020 : 0x0000) | + (v.alu().dir() ? 0x0040 : 0x0000) | + (v.alu().irq() ? 0x0080 : 0x0000) | + (bitfield(v.filter().lp(), 0, 2) << 8) | (v.cr().ca() << 10) | + (v.cr().cmpd() ? 0x2000 : 0x0000) | (v.cr().bs() << 14); + break; + case 1: // FC (Frequency Control) + read_latch = (read_latch & ~0x1ffff) | bitfield(v.alu().fc(), 0, 17); + break; + case 2: // LVOL (Left Volume) + read_latch = (read_latch & ~0xffff) | bitfield(v.lvol(), 0, 16); + break; + case 3: // LVRAMP (Left Volume Ramp) + read_latch = (read_latch & ~0xff00) | (bitfield(v.lvramp(), 0, 8) << 8); + break; + case 4: // RVOL (Right Volume) + read_latch = (read_latch & ~0xffff) | bitfield(v.rvol(), 0, 16); + break; + case 5: // RVRAMP (Right Volume Ramp) + read_latch = (read_latch & ~0xff00) | (bitfield(v.rvramp(), 0, 8) << 8); + break; + case 6: // ECOUNT (Envelope Counter) + read_latch = (read_latch & ~0x01ff) | bitfield(v.ecount(), 0, 9); + break; + case 7: // K2 (Filter Cutoff Coefficient #2) + read_latch = (read_latch & ~0xffff) | bitfield(v.filter().k2(), 0, 16); + break; + case 8: // K2RAMP (Filter Cutoff Coefficient #2 Ramp) + read_latch = (read_latch & ~0xff01) | + (bitfield(v.k2ramp().ramp(), 0, 8) << 8) | + (v.k2ramp().slow() ? 0x0001 : 0x0000); + break; + case 9: // K1 (Filter Cutoff Coefficient #1) + read_latch = (read_latch & ~0xffff) | bitfield(v.filter().k1(), 0, 16); + break; + case 10: // K1RAMP (Filter Cutoff Coefficient #1 Ramp) + read_latch = (read_latch & ~0xff01) | + (bitfield(v.k1ramp().ramp(), 0, 8) << 8) | + (v.k1ramp().slow() ? 0x0001 : 0x0000); + break; + case 11: // ACT (Number of voices) + read_latch = (read_latch & ~0x1f) | bitfield(m_active, 0, 5); + break; + case 12: // MODE (Global Mode) + read_latch = + (read_latch & ~0x1f) | (m_mode.lrclk_en() ? 0x01 : 0x00) | + (m_mode.wclk_en() ? 0x02 : 0x00) | (m_mode.bclk_en() ? 0x04 : 0x00) | + (m_mode.master() ? 0x08 : 0x00) | (m_mode.dual() ? 0x10 : 0x00); + break; + } + } + } + } + + return read_latch; +} + +void es5506_core::regs_w(u8 page, u8 address, u32 data, bool cpu_access) +{ + // Global registers + if (address >= 13) + { + switch (address) + { + case 13: // POT (Pot A/D Register) + // Read only + break; + case 14: // IRQV (Interrupting voice vector) + // Read only + break; + case 15: // PAGE (Page select register) + m_page = bitfield(data, 0, 7); + break; + } + } + else + { + // Channel registers are Write only, and for test purposes + if (bitfield(page, 6)) + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + case 2: // CH1L (Channel 1 Left) + case 4: // CH2L (Channel 2 Left) + case 6: // CH3L (Channel 3 Left) + case 8: // CH4L (Channel 4 Left) + case 10: // CH5L (Channel 5 Left) + m_ch[bitfield(address, 1, 3)].set_left( + sign_ext(bitfield(data, 0, 23), 23)); + break; + case 1: // CH0R (Channel 0 Right) + case 3: // CH1R (Channel 1 Right) + case 5: // CH2R (Channel 2 Right) + case 7: // CH3R (Channel 3 Right) + case 9: // CH4R (Channel 4 Right) + case 11: // CH5R (Channel 5 Right) + m_ch[bitfield(address, 1, 3)].set_right( + sign_ext(bitfield(data, 0, 23), 23)); + break; + } + } + else + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 63 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.alu().set_lei(bitfield(data, 2)); + v.alu().set_loop(bitfield(data, 3, 2)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + v.filter().set_lp(bitfield(data, 8, 2)); + v.cr().set_ca(std::min(5, bitfield(data, 10, 3))); + v.cr().set_cmpd(bitfield(data, 13)); + v.cr().set_bs(bitfield(data, 14, 2)); + break; + case 1: // START (Loop Start Register) + v.alu().set_start(data & 0xfffff800); + break; + case 2: // END (Loop End Register) + v.alu().set_end(data & 0xffffff80); + break; + case 3: // ACCUM (Accumulator Register) + v.alu().set_accum(data); + break; + case 4: // O4(n-1) (Filter 4 Temp Register) + v.filter().set_o4_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 5: // O3(n-2) (Filter 3 Temp Register #2) + v.filter().set_o3_2(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 6: // O3(n-1) (Filter 3 Temp Register #1) + v.filter().set_o3_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 7: // O2(n-2) (Filter 2 Temp Register #2) + v.filter().set_o2_2(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 8: // O2(n-1) (Filter 2 Temp Register #1) + v.filter().set_o2_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 9: // O1(n-1) (Filter 1 Temp Register) + v.filter().set_o1_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 10: // W_ST (Word Clock Start Register) + m_w_st = bitfield(data, 0, 7); + break; + case 11: // W_END (Word Clock End Register) + m_w_end = bitfield(data, 0, 7); + break; + case 12: // LR_END (Left/Right Clock End Register) + m_lr_end = bitfield(data, 0, 7); + m_lrclk.set_width(m_lr_end); + break; + } + } + else // Page 0 - 31 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.alu().set_lei(bitfield(data, 2)); + v.alu().set_loop(bitfield(data, 3, 2)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + v.filter().set_lp(bitfield(data, 8, 2)); + v.cr().set_ca(std::min(5, bitfield(data, 10, 3))); + v.cr().set_cmpd(bitfield(data, 13)); + v.cr().set_bs(bitfield(data, 14, 2)); + break; + case 1: // FC (Frequency Control) + v.alu().set_fc(bitfield(data, 0, 17)); + break; + case 2: // LVOL (Left Volume) + v.set_lvol(bitfield(data, 0, 16)); + break; + case 3: // LVRAMP (Left Volume Ramp) + v.set_lvramp(bitfield(data, 8, 8)); + break; + case 4: // RVOL (Right Volume) + v.set_rvol(bitfield(data, 0, 16)); + break; + case 5: // RVRAMP (Right Volume Ramp) + v.set_rvramp(bitfield(data, 8, 8)); + break; + case 6: // ECOUNT (Envelope Counter) + v.set_ecount(bitfield(data, 0, 9)); + break; + case 7: // K2 (Filter Cutoff Coefficient #2) + v.filter().set_k2(bitfield(data, 0, 16)); + break; + case 8: // K2RAMP (Filter Cutoff Coefficient #2 Ramp) + v.k2ramp().write(data); + break; + case 9: // K1 (Filter Cutoff Coefficient #1) + v.filter().set_k1(bitfield(data, 0, 16)); + break; + case 10: // K1RAMP (Filter Cutoff Coefficient #1 Ramp) + v.k1ramp().write(data); + break; + case 11: // ACT (Number of voices) + m_active = std::max(4, bitfield(data, 0, 5)); + break; + case 12: // MODE (Global Mode) + m_mode.write(data); + break; + } + } + } + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp new file mode 100644 index 00000000..84a6077d --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -0,0 +1,385 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5506 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES5506_HPP +#define _VGSOUND_EMU_SRC_ES5506_HPP + +#pragma once + +#include "es550x.hpp" + +// ES5506 specific +class es5506_core : public es550x_shared_core +{ + private: + class output_t : public vgsound_emu_core + { + public: + output_t(s32 left = 0, s32 right = 0) + : vgsound_emu_core("es5506_output") + , m_left(left) + , m_right(right) + { + } + + void reset() + { + m_left = 0; + m_right = 0; + }; + + inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } + + inline void clamp20(output_t src) + { + m_left = clamp20(src.left()); + m_right = clamp20(src.right()); + } + + inline void clamp20() + { + m_left = clamp20(m_left); + m_right = clamp20(m_right); + } + + // setters + inline void set_left(s32 left) { m_left = clamp20(left); } + + inline void set_right(s32 right) { m_right = clamp20(right); } + + void serial_in(bool ch, u8 in) + { + if (ch) // Right output + { + m_right = (m_right << 1) | (in ? 1 : 0); + } + else // Left output + { + m_left = (m_left << 1) | (in ? 1 : 0); + } + } + + // getters + inline u32 left() { return m_left; } + + inline u32 right() { return m_right; } + + output_t &operator+=(output_t &src) + { + m_left = clamp20(m_left + src.left()); + m_right = clamp20(m_right + src.right()); + return *this; + } + + output_t &operator=(output_t src) + { + clamp20(src); + return *this; + } + + output_t &operator=(s32 val) + { + m_left = m_right = clamp20(val); + return *this; + } + + output_t &operator>>(s32 shift) + { + m_left >>= shift; + m_right >>= shift; + return *this; + } + + private: + s32 m_left = 0; + s32 m_right = 0; + }; + + // es5506 voice classes + class voice_t : public es550x_voice_t + { + private: + // es5506 Filter ramp class + class filter_ramp_t : public vgsound_emu_core + { + public: + filter_ramp_t() + : vgsound_emu_core("es5506_filter_ramp") + , m_slow(0) + , m_ramp(0) + { + } + + void reset() + { + m_slow = 0; + m_ramp = 0; + }; + + // Setters + inline void write(u16 data) + { + m_slow = data & 1; + m_ramp = (data >> 8) & 0xff; + } + + // Getters + inline bool slow() { return m_slow; } + + inline u16 ramp() { return m_ramp; } + + private: + u16 m_slow : 1; // Slow mode flag + u16 m_ramp = 8; // Ramp value + }; + + public: + // constructor + voice_t(es5506_core &host) + : es550x_voice_t("es5506_voice", 21, 11, true) + , m_host(host) + , m_lvol(0) + , m_rvol(0) + , m_lvramp(0) + , m_rvramp(0) + , m_ecount(0) + , m_k2ramp(filter_ramp_t()) + , m_k1ramp(filter_ramp_t()) + , m_filtcount(0) + , m_ch(output_t()) + , m_mute(false) + { + } + + // internal state + virtual void reset() override; + virtual void fetch(u8 voice, u8 cycle) override; + virtual void tick(u8 voice) override; + + // Setters + inline void set_lvol(s32 lvol) { m_lvol = lvol; } + + inline void set_rvol(s32 rvol) { m_rvol = rvol; } + + inline void set_lvramp(s32 lvramp) { m_lvramp = lvramp; } + + inline void set_rvramp(s32 rvramp) { m_rvramp = rvramp; } + + inline void set_ecount(s16 ecount) { m_ecount = ecount; } + + // Getters + inline s32 lvol() { return m_lvol; } + + inline s32 rvol() { return m_rvol; } + + inline s32 lvramp() { return m_lvramp; } + + inline s32 rvramp() { return m_rvramp; } + + inline s16 ecount() { return m_ecount; } + + inline filter_ramp_t &k2ramp() { return m_k2ramp; } + + inline filter_ramp_t &k1ramp() { return m_k1ramp; } + + output_t &ch() { return m_ch; } + + // for debug/preview only + inline void set_mute(bool mute) { m_mute = mute; } + + inline s32 left_out() { return m_mute ? 0 : m_ch.left(); } + + inline s32 right_out() { return m_mute ? 0 : m_ch.right(); } + + private: + // accessors, getters, setters + s16 decompress(u8 sample); + s32 volume_calc(u16 volume, s32 in); + + // registers + es5506_core &m_host; + // Volume register: 4 bit exponent, 8 bit mantissa + // 4 LSBs are used for fine control of ramp increment for hardware envelope + s32 m_lvol = 0; // Left volume + s32 m_rvol = 0; // Right volume + // Envelope + s32 m_lvramp = 0; // Left volume ramp + s32 m_rvramp = 0; // Righr volume ramp + s16 m_ecount = 0; // Envelope counter + filter_ramp_t m_k2ramp; // Filter coefficient 2 Ramp + filter_ramp_t m_k1ramp; // Filter coefficient 1 Ramp + u8 m_filtcount = 0; // Internal counter for slow mode + output_t m_ch; // channel output + bool m_mute = false; // mute flag (for debug purpose) + }; + + // 5 bit mode + class mode_t : public vgsound_emu_core + { + public: + mode_t() + : vgsound_emu_core("es5506_mode") + , m_lrclk_en(1) + , m_wclk_en(1) + , m_bclk_en(1) + , m_master(0) + , m_dual(0) + { + } + + // internal states + void reset() + { + m_lrclk_en = 1; + m_wclk_en = 1; + m_bclk_en = 1; + m_master = 0; + m_dual = 0; + } + + // accessors + void write(u8 data) + { + m_lrclk_en = (data >> 0) & 1; + m_wclk_en = (data >> 1) & 1; + m_bclk_en = (data >> 2) & 1; + m_master = (data >> 3) & 1; + m_dual = (data >> 4) & 1; + } + + // getters + bool lrclk_en() { return m_lrclk_en; } + + bool wclk_en() { return m_wclk_en; } + + bool bclk_en() { return m_bclk_en; } + + bool master() { return m_master; } + + bool dual() { return m_dual; } + + private: + u8 m_lrclk_en : 1; // Set LRCLK to output + u8 m_wclk_en : 1; // Set WCLK to output + u8 m_bclk_en : 1; // Set BCLK to output + u8 m_master : 1; // Set memory mode to master + u8 m_dual : 1; // Set dual chip config + }; + + public: + // constructor + es5506_core(es550x_intf &intf) + : es550x_shared_core("es5506", 32, intf) + , m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this} + , m_read_latch(0) + , m_write_latch(0) + , m_w_st(0) + , m_w_end(0) + , m_lr_end(0) + , m_mode(mode_t()) + , m_w_st_curr(0) + , m_w_end_curr(0) + , m_bclk(clock_pulse_t(4, 0)) + , m_lrclk(clock_pulse_t(32, 1)) + , m_wclk(0) + , m_wclk_lr(false) + , m_output_bit(0) + , m_ch{output_t()} + , m_output{output_t()} + , m_output_temp{output_t()} + , m_output_latch{output_t()} + { + } + + // host interface + u8 host_r(u8 address); + void host_w(u8 address, u8 data); + + // internal state + virtual void reset() override; + virtual void tick() override; + + // less cycle accurate, but also less cpu heavy update routine + void tick_perf(); + + // clock outputs + inline bool bclk() { return m_bclk.current_edge(); } + + inline bool bclk_rising_edge() { return m_bclk.rising_edge(); } + + inline bool bclk_falling_edge() { return m_bclk.falling_edge(); } + + // 6 stereo output channels + inline s32 lout(u8 ch) { return m_output[std::min(5, ch & 0x7)].left(); } + + inline s32 rout(u8 ch) { return m_output[std::min(5, ch & 0x7)].right(); } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // bypass chips host interface for debug purpose only + u8 read(u8 address, bool cpu_access = false); + void write(u8 address, u8 data, bool cpu_access = false); + + u32 regs_r(u8 page, u8 address, bool cpu_access = false); + void regs_w(u8 page, u8 address, u32 data, bool cpu_access = false); + + u8 regs8_r(u8 page, u8 address) + { + u8 prev = m_page; + m_page = page; + u8 ret = read(address, false); + m_page = prev; + return ret; + } + + inline void set_mute(u8 ch, bool mute) { m_voice[ch & 0x1f].set_mute(mute); } + + // per-voice outputs + inline s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].left_out() : 0; } + + inline s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].right_out() : 0; } + + protected: + virtual inline u8 max_voices() override { return 32; } + + virtual void voice_tick() override; + + private: + std::array m_voice; // 32 voices + + // Host interfaces + u32 m_read_latch = 0; // 32 bit register latch for host read + u32 m_write_latch = 0; // 32 bit register latch for host write + + // Serial register + u8 m_w_st = 0; // Word clock start register + u8 m_w_end = 0; // Word clock end register + u8 m_lr_end = 0; // Left/Right clock end register + mode_t m_mode; // Global mode + + // Serial related stuffs + u8 m_w_st_curr = 0; // Word clock start, current status + u8 m_w_end_curr = 0; // Word clock end register + clock_pulse_t m_bclk; // BCLK clock (CLKIN / 4), freely running clock + clock_pulse_t m_lrclk; // LRCLK + s16 m_wclk = 0; // WCLK + bool m_wclk_lr = false; // WCLK, L/R output select + s8 m_output_bit = 0; // Bit position in output + std::array m_ch; // 6 stereo output channels + std::array m_output; // Serial outputs + std::array m_output_temp; // temporary signal for serial output + std::array m_output_latch; // output latch +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp new file mode 100644 index 00000000..7de7f049 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp @@ -0,0 +1,34 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 emulation core +*/ + +#include "es550x.hpp" + +// Shared functions +void es550x_shared_core::reset() +{ + m_host_intf.reset(); + m_ha = 0; + m_hd = 0; + m_page = 0; + m_irqv.reset(); + m_active = max_voices() - 1; + m_voice_cycle = 0; + m_voice_fetch = 0; + m_voice_update = false; + m_voice_end = false; + m_clkin.reset(); + m_cas.reset(); + m_e.reset(); +} + +void es550x_shared_core::es550x_voice_t::reset() +{ + m_cr.reset(); + m_alu.reset(); + m_filter.reset(); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp new file mode 100644 index 00000000..4457c244 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp @@ -0,0 +1,610 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES550X_HPP +#define _VGSOUND_EMU_SRC_ES550X_HPP + +#pragma once + +#include "../core/util.hpp" + +// ES5504/ES5505/ES5506 interface +class es550x_intf : public vgsound_emu_core +{ + public: + es550x_intf() + : vgsound_emu_core("es550x_intf") + { + } + + virtual void e_pin(bool state) {} // E output + + virtual void bclk(bool state) {} // BCLK output (serial specific) + + virtual void lrclk(bool state) {} // LRCLK output (serial specific) + + virtual void wclk(bool state) {} // WCLK output (serial specific) + + virtual void irqb(bool state) {} // irqb output + + virtual u16 adc_r() { return 0; } // ADC input + + virtual void adc_w(u16 data) {} // ADC output + + virtual s16 read_sample(u8 voice, u8 bank, u32 address) { return 0; } +}; + +// Shared functions for ES5504/ES5505/ES5506 +class es550x_shared_core : public vgsound_emu_core +{ + friend class es550x_intf; // es550x specific memory interface + + private: + const u8 m_max_voices = 32; + + protected: + // Interrupt bits + class es550x_irq_t : public vgsound_emu_core + { + public: + es550x_irq_t() + : vgsound_emu_core("es550x_irq") + , m_voice(0) + , m_irqb(1) + { + } + + void reset() + { + m_voice = 0; + m_irqb = 1; + } + + // setter + void set(u8 index) + { + m_irqb = 0; + m_voice = index & 0x1f; + } + + void clear() + { + m_irqb = 1; + m_voice = 0; + } + + // getter + inline bool irqb() { return m_irqb; } + + inline u8 voice() { return m_voice; } + + inline u8 get() { return (m_irqb << 7) | (m_voice & 0x1f); } + + u8 m_voice : 5; + u8 m_irqb : 1; + }; + + // Common voice class + class es550x_voice_t : public vgsound_emu_core + { + private: + // Common control bits + class es550x_control_t : public vgsound_emu_core + { + public: + es550x_control_t() + : vgsound_emu_core("es550x_voice_control") + , m_ca(0) + , m_adc(0) + , m_bs(0) + , m_cmpd(0) + { + } + + void reset() + { + m_ca = 0; + m_adc = 0; + m_bs = 0; + m_cmpd = 0; + } + + // setters + inline void set_ca(u8 ca) { m_ca = ca & 0xf; } + + inline void set_adc(bool adc) { m_adc = adc ? 1 : 0; } + + inline void set_bs(u8 bs) { m_bs = bs & 0x3; } + + inline void set_cmpd(bool cmpd) { m_cmpd = cmpd ? 1 : 0; } + + // getters + inline u8 ca() { return m_ca; } + + inline bool adc() { return m_adc; } + + inline u8 bs() { return m_bs; } + + inline bool cmpd() { return m_cmpd; } + + protected: + // Channel assign - + // 4 bit (16 channel or Bank) for ES5504 + // 2 bit (4 stereo channels) for ES5505 + // 3 bit (6 stereo channels) for ES5506 + u8 m_ca : 4; + + // ES5504 Specific + u8 m_adc : 1; // Start ADC + + // ES5505/ES5506 Specific + u8 m_bs : 2; // Bank bit (1 bit for ES5505, 2 bit for ES5506) + u8 m_cmpd : 1; // Use compressed sample format (ES5506) + }; + + // Accumulator + class es550x_alu_t : public vgsound_emu_core + { + public: + es550x_alu_t(u8 integer, u8 fraction, bool transwave) + : vgsound_emu_core("es550x_voice_alu") + , m_integer(integer) + , m_fraction(fraction) + , m_total_bits(integer + fraction) + , m_accum_mask( + u32(std::min(~0, u64(u64(1) << u64(integer + fraction)) - 1))) + , m_transwave(transwave) + , m_fc(0) + , m_start(0) + , m_end(0) + , m_accum(0) + , m_sample({0}) + { + } + + // configurations + const u8 m_integer; + const u8 m_fraction; + const u8 m_total_bits; + const u32 m_accum_mask; + const bool m_transwave; + + // internal states + void reset(); + bool tick(); + + void loop_exec(); + bool busy(); + s32 interpolation(); + u32 get_accum_integer(); + + void irq_exec(es550x_intf &intf, es550x_irq_t &irqv, u8 index); + + void irq_update(es550x_intf &intf, es550x_irq_t &irqv) + { + intf.irqb(irqv.irqb() ? false : true); + } + + // setters + inline void set_stop0(bool stop0) { m_cr.set_stop0(stop0); } + + inline void set_stop1(bool stop1) { m_cr.set_stop1(stop1); } + + inline void set_lpe(bool lpe) { m_cr.set_lpe(lpe); } + + inline void set_ble(bool ble) { m_cr.set_ble(ble); } + + inline void set_irqe(bool irqe) { m_cr.set_irqe(irqe); } + + inline void set_dir(bool dir) { m_cr.set_dir(dir); } + + inline void set_irq(bool irq) { m_cr.set_irq(irq); } + + inline void set_lei(bool lei) { m_cr.set_lei(lei); } + + inline void set_stop(u8 stop) { m_cr.set_stop(stop); } + + inline void set_loop(u8 loop) { m_cr.set_loop(loop); } + + inline void set_fc(u32 fc) { m_fc = fc; } + + inline void set_start(u32 start, u32 mask = ~0) + { + m_start = (m_start & ~mask) | (start & mask); + } + + inline void set_end(u32 end, u32 mask = ~0) + { + m_end = (m_end & ~mask) | (end & mask); + } + + inline void set_accum(u32 accum, u32 mask = ~0) + { + m_accum = (m_accum & ~mask) | (accum & mask); + } + + inline void set_sample(u8 slot, s32 sample) { m_sample[slot & 1] = sample; } + + // getters + inline bool stop0() { return m_cr.stop0(); } + + inline bool stop1() { return m_cr.stop1(); } + + inline bool lpe() { return m_cr.lpe(); } + + inline bool ble() { return m_cr.ble(); } + + inline bool irqe() { return m_cr.irqe(); } + + inline bool dir() { return m_cr.dir(); } + + inline bool irq() { return m_cr.irq(); } + + inline bool lei() { return m_cr.lei(); } + + inline u8 stop() { return m_cr.stop(); } + + inline u8 loop() { return m_cr.loop(); } + + inline u32 fc() { return m_fc; } + + inline u32 start() { return m_start; } + + inline u32 end() { return m_end; } + + inline u32 accum() { return m_accum; } + + inline s32 sample(u8 slot) { return m_sample[slot & 1]; } + + private: + class es550x_alu_cr_t : public vgsound_emu_core + { + public: + es550x_alu_cr_t() + : vgsound_emu_core("es550x_voice_alu_cr") + , m_stop0(0) + , m_stop1(0) + , m_lpe(0) + , m_ble(0) + , m_irqe(0) + , m_dir(0) + , m_irq(0) + , m_lei(0) + { + } + + void reset() + { + m_stop0 = 0; + m_stop1 = 0; + m_lpe = 0; + m_ble = 0; + m_irqe = 0; + m_dir = 0; + m_irq = 0; + m_lei = 0; + } + + // setters + inline void set_stop0(bool stop0) { m_stop0 = stop0 ? 1 : 0; } + + inline void set_stop1(bool stop1) { m_stop1 = stop1 ? 1 : 0; } + + inline void set_lpe(bool lpe) { m_lpe = lpe ? 1 : 0; } + + inline void set_ble(bool ble) { m_ble = ble ? 1 : 0; } + + inline void set_irqe(bool irqe) { m_irqe = irqe ? 1 : 0; } + + inline void set_dir(bool dir) { m_dir = dir ? 1 : 0; } + + inline void set_irq(bool irq) { m_irq = irq ? 1 : 0; } + + inline void set_lei(bool lei) { m_lei = lei ? 1 : 0; } + + inline void set_stop(u8 stop) + { + m_stop0 = (stop >> 0) & 1; + m_stop1 = (stop >> 1) & 1; + } + + inline void set_loop(u8 loop) + { + m_lpe = (loop >> 0) & 1; + m_ble = (loop >> 1) & 1; + } + + // getters + inline bool stop0() { return m_stop0; } + + inline bool stop1() { return m_stop1; } + + inline bool lpe() { return m_lpe; } + + inline bool ble() { return m_ble; } + + inline bool irqe() { return m_irqe; } + + inline bool dir() { return m_dir; } + + inline bool irq() { return m_irq; } + + inline bool lei() { return m_lei; } + + inline u8 stop() { return (m_stop0 << 0) | (m_stop1 << 1); } + + inline u8 loop() { return (m_lpe << 0) | (m_ble << 1); } + + private: + u8 m_stop0 : 1; // Stop with ALU + u8 m_stop1 : 1; // Stop with processor + u8 m_lpe : 1; // Loop enable + u8 m_ble : 1; // Bidirectional loop enable + u8 m_irqe : 1; // IRQ enable + u8 m_dir : 1; // Playback direction + u8 m_irq : 1; // IRQ bit + u8 m_lei : 1; // Loop end ignore (ES5506 specific) + }; + + es550x_alu_cr_t m_cr; + // Frequency - + // 6 integer, 9 fraction for ES5504/ES5505 + // 6 integer, 11 fraction for ES5506 + u32 m_fc = 0; + u32 m_start = 0; // Start register + u32 m_end = 0; // End register + + // Accumulator - + // 20 integer, 9 fraction for ES5504/ES5505 + // 21 integer, 11 fraction for ES5506 + u32 m_accum = 0; + // Samples + std::array m_sample = {0}; + }; + + // Filter + class es550x_filter_t : public vgsound_emu_core + { + public: + es550x_filter_t() + : vgsound_emu_core("es550x_voice_filter") + , m_lp(0) + , m_k2(0) + , m_k1(0) + { + for (std::array &elem : m_o) + { + std::fill(elem.begin(), elem.end(), 0); + } + } + + void reset(); + void tick(s32 in); + + // setters + inline void set_lp(u8 lp) { m_lp = lp & 3; } + + inline void set_k2(s32 k2) { m_k2 = k2; } + + inline void set_k1(s32 k1) { m_k1 = k1; } + + inline void set_o1_1(s32 o1_1) { m_o[1][0] = o1_1; } + + inline void set_o2_1(s32 o2_1) { m_o[2][0] = o2_1; } + + inline void set_o2_2(s32 o2_2) { m_o[2][1] = o2_2; } + + inline void set_o3_1(s32 o3_1) { m_o[3][0] = o3_1; } + + inline void set_o3_2(s32 o3_2) { m_o[3][1] = o3_2; } + + inline void set_o4_1(s32 o4_1) { m_o[4][0] = o4_1; } + + // getters + inline u8 lp() { return m_lp; } + + inline s32 k2() { return m_k2; } + + inline s32 k1() { return m_k1; } + + inline s32 o1_1() { return m_o[1][0]; } + + inline s32 o2_1() { return m_o[2][0]; } + + inline s32 o2_2() { return m_o[2][1]; } + + inline s32 o3_1() { return m_o[3][0]; } + + inline s32 o3_2() { return m_o[3][1]; } + + inline s32 o4_1() { return m_o[4][0]; } + + private: + void lp_exec(s32 coeff, s32 in, s32 out); + void hp_exec(s32 coeff, s32 in, s32 out); + + // Registers + u8 m_lp = 0; // Filter mode + // Filter coefficient registers + // 12 bit for filter calculation, 4 + // LSBs are used for fine control of ramp increment for + // hardware envelope (ES5506) + s32 m_k2 = 0; // Filter coefficient 2 + s32 m_k1 = 0; // Filter coefficient 1 + + // Filter storage registers + std::array, 5> m_o; + }; + + public: + es550x_voice_t(std::string tag, u8 integer, u8 fraction, bool transwave) + : vgsound_emu_core(tag) + , m_cr(es550x_control_t()) + , m_alu(integer, fraction, transwave) + , m_filter(es550x_filter_t()) + { + } + + // internal state + virtual void reset(); + virtual void fetch(u8 voice, u8 cycle) = 0; + virtual void tick(u8 voice) = 0; + + void irq_update(es550x_intf &intf, es550x_irq_t &irqv) + { + m_alu.irq_update(intf, irqv); + } + + // Getters + es550x_control_t &cr() { return m_cr; } + + es550x_alu_t &alu() { return m_alu; } + + es550x_filter_t &filter() { return m_filter; } + + protected: + es550x_control_t m_cr; + es550x_alu_t m_alu; + es550x_filter_t m_filter; + }; + + // Host interfaces + class host_interface_flag_t : public vgsound_emu_core + { + public: + host_interface_flag_t() + : vgsound_emu_core("es550x_host_interface_flag") + , m_host_access(0) + , m_host_access_strobe(0) + , m_rw(0) + , m_rw_strobe(0) + { + } + + // Accessors + void reset() + { + m_host_access = 0; + m_host_access_strobe = 0; + m_rw = 0; + m_rw_strobe = 0; + } + + // Setters + void set_strobe(bool rw) + { + m_rw_strobe = rw ? 1 : 0; + m_host_access_strobe = 1; + } + + void clear_strobe() { m_host_access_strobe = 0; } + + void clear_host_access() { m_host_access = 0; } + + void update_strobe() + { + m_rw = m_rw_strobe; + m_host_access = m_host_access_strobe; + } + + // Getters + bool host_access() { return m_host_access; } + + bool rw() { return m_rw; } + + private: + u8 m_host_access : 1; // Host access trigger + u8 m_host_access_strobe : 1; // Host access strobe + u8 m_rw : 1; // R/W state + u8 m_rw_strobe : 1; // R/W strobe + }; + + public: + // internal state + virtual void reset(); + + virtual void tick() {} + + // clock outputs + inline bool _cas() { return m_cas.current_edge(); } + + inline bool _cas_rising_edge() { return m_cas.rising_edge(); } + + inline bool _cas_falling_edge() { return m_cas.falling_edge(); } + + inline bool e() { return m_e.current_edge(); } + + inline bool e_rising_edge() { return m_e.rising_edge(); } + + inline bool e_falling_edge() { return m_e.falling_edge(); } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // voice cycle + inline u8 voice_cycle() { return m_voice_cycle; } + + // voice update flag + inline bool voice_update() { return m_voice_update; } + + inline bool voice_end() { return m_voice_end; } + + protected: + // constructor + es550x_shared_core(std::string tag, const u8 voice, es550x_intf &intf) + : vgsound_emu_core(tag) + , m_max_voices(voice) + , m_intf(intf) + , m_host_intf(host_interface_flag_t()) + , m_ha(0) + , m_hd(0) + , m_page(0) + , m_irqv(es550x_irq_t()) + , m_active(m_max_voices - 1) + , m_voice_cycle(0) + , m_voice_fetch(0) + , m_voice_update(0) + , m_voice_end(0) + , m_clkin(clock_pulse_t(1, 0)) + , m_cas(clock_pulse_t(2, 1)) + , m_e(clock_pulse_t(4, 0)) + { + } + + // Constants + virtual inline u8 max_voices() { return m_max_voices; } + + // Shared registers, functions + virtual void voice_tick() {} // voice tick + + es550x_intf &m_intf; // es550x specific memory interface + host_interface_flag_t m_host_intf; // Host interface flag + u8 m_ha = 0; // Host address (4 bit) + u16 m_hd = 0; // Host data (16 bit for ES5504/ES5505, 8 bit for ES5506) + u8 m_page = 0; // Page + es550x_irq_t m_irqv; // Voice interrupt vector registers + + // Internal states + u8 m_active = 31; // Activated voices + // -1, ~25 for ES5504, + // ~32 for ES5505/ES5506 + u8 m_voice_cycle = 0; // Voice cycle + u8 m_voice_fetch = 0; // Voice fetch cycle + bool m_voice_update = false; // Voice update flag + bool m_voice_end = false; // End of one voice cycle flag + clock_pulse_t m_clkin; // CLKIN clock + clock_pulse_t m_cas; // /CAS clock (CLKIN / 4), falling edge of + // CLKIN trigger this clock + clock_pulse_t m_e; // E clock (CLKIN / 8), + // falling edge of CLKIN trigger this clock +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp new file mode 100644 index 00000000..f058f054 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp @@ -0,0 +1,131 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 Shared Accumulator emulation core +*/ + +#include "es550x.hpp" + +// Accumulator functions +void es550x_shared_core::es550x_voice_t::es550x_alu_t::reset() +{ + m_cr.reset(); + m_fc = 0; + m_start = 0; + m_end = 0; + m_accum = 0; + m_sample[0] = m_sample[1] = 0; +} + +bool es550x_shared_core::es550x_voice_t::es550x_alu_t::busy() { return m_cr.stop() == 0; } + +bool es550x_shared_core::es550x_voice_t::es550x_alu_t::tick() +{ + if (m_cr.dir()) + { + m_accum -= m_fc; + } + else + { + m_accum += m_fc; + } + + m_accum &= m_accum_mask; + return ((!m_cr.lei()) && + (((m_cr.dir()) && (m_accum < m_start)) || ((!m_cr.dir()) && (m_accum > m_end)))) + ? true + : false; +} + +void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec() +{ + if (m_cr.irqe()) + { // Set IRQ + m_cr.set_irq(true); + } + + if (m_cr.dir()) // Reverse playback + { + if (m_cr.lpe()) // Loop enable + { + if (m_cr.ble()) // Bidirectional + { + m_cr.set_dir(false); + m_accum = m_start + (m_start - m_accum); + } + else + { // Normal + m_accum = m_end - (m_start - m_accum); + } + } + else if (m_cr.ble() && m_transwave) // m_transwave + { + m_cr.set_loop(0); + m_cr.set_lei(true); // Loop end ignore + m_accum = m_end - (m_start - m_accum); + } + else + { // Stop + m_cr.set_stop0(true); + } + } + else + { + if (m_cr.lpe()) // Loop enable + { + if (m_cr.ble()) // Bidirectional + { + m_cr.set_dir(true); + m_accum = m_end - (m_end - m_accum); + } + else + { // Normal + m_accum = (m_accum - m_end) + m_start; + } + } + else if (m_cr.ble() && m_transwave) // m_transwave + { + m_cr.set_loop(0); + m_cr.set_lei(true); // Loop end ignore + m_accum = (m_accum - m_end) + m_start; + } + else + { // Stop + m_cr.set_stop0(true); + } + } +} + +s32 es550x_shared_core::es550x_voice_t::es550x_alu_t::interpolation() +{ + // SF = S1 + ACCfr * (S2 - S1) + return m_sample[0] + ((bitfield(m_accum, std::max(0, m_fraction - 9), 9) * + (m_sample[1] - m_sample[0])) >> + 9); +} + +u32 es550x_shared_core::es550x_voice_t::es550x_alu_t::get_accum_integer() +{ + return bitfield(m_accum, m_fraction, m_integer); +} + +void es550x_shared_core::es550x_voice_t::es550x_alu_t::irq_exec(es550x_intf &intf, + es550x_irq_t &irqv, + u8 index) +{ + const bool prev = irqv.irqb(); + if (m_cr.irq()) + { + if (irqv.irqb()) + { + irqv.set(index); + m_cr.set_irq(false); + } + } + if (prev != irqv.irqb()) + { + irq_update(intf, irqv); + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp new file mode 100644 index 00000000..360cab81 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp @@ -0,0 +1,72 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 Shared Filter emulation core +*/ + +#include "es550x.hpp" + +// Filter functions +void es550x_shared_core::es550x_voice_t::es550x_filter_t::reset() +{ + m_lp = 0; + m_k2 = 0; + m_k1 = 0; + for (std::array &elem : m_o) + { + std::fill(elem.begin(), elem.end(), 0); + } +} + +void es550x_shared_core::es550x_voice_t::es550x_filter_t::tick(s32 in) +{ + // set sample input + m_o[0][0] = in; + + s32 coeff_k1 = s32(bitfield(m_k1, 4, 12)); // 12 MSB used + s32 coeff_k2 = s32(bitfield(m_k2, 4, 12)); // 12 MSB used + + // First and second stage: LP/K1, LP/K1 Fixed + lp_exec(coeff_k1, 0, 1); + lp_exec(coeff_k1, 1, 2); + switch (m_lp) + { + case 0: // LP3 = 0, LP4 = 0: HP/K2, HP/K2 + default: + hp_exec(coeff_k2, 2, 3); + hp_exec(coeff_k2, 3, 4); + break; + case 1: // LP3 = 0, LP4 = 1: HP/K2, LP/K1 + lp_exec(coeff_k1, 2, 3); + hp_exec(coeff_k2, 3, 4); + break; + case 2: // LP3 = 1, LP4 = 0: LP/K2, LP/K2 + lp_exec(coeff_k2, 2, 3); + lp_exec(coeff_k2, 3, 4); + break; + case 3: // LP3 = 1, LP4 = 1: LP/K2, LP/K1 + lp_exec(coeff_k1, 2, 3); + lp_exec(coeff_k2, 3, 4); + break; + } +} + +void es550x_shared_core::es550x_voice_t::es550x_filter_t::lp_exec(s32 coeff, s32 in, s32 out) +{ + // Store previous filter data + m_o[out][1] = m_o[out][0]; + + // Yn = K*(Xn - Yn-1) + Yn-1 + m_o[out][0] = ((coeff * (m_o[in][0] - m_o[out][0])) / 4096) + m_o[out][0]; +} + +void es550x_shared_core::es550x_voice_t::es550x_filter_t::hp_exec(s32 coeff, s32 in, s32 out) +{ + // Store previous filter data + m_o[out][1] = m_o[out][0]; + + // Yn = Xn - Xn-1 + K*Yn-1 + m_o[out][0] = m_o[in][0] - m_o[in][1] + ((coeff * m_o[out][0]) / 8192) + (m_o[out][0] / 2); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/README.md b/vgsound_emu-modified/vgsound_emu/src/k005289/README.md new file mode 100644 index 00000000..cc297b2f --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k005289/README.md @@ -0,0 +1,23 @@ +# Konami K005289 + +## Summary + +- 2 12 bit timer +- Address generator + +## Source code + +- k005289.hpp: Base header + - k005289.cpp: Source emulation core + +## Description + +This chip is used at infamous Konami Bubble System, for part of Wavetable sound generator. But seriously, It is just to 2 internal 12 bit timer and address generators, rather than sound generator. + +Everything except for internal counter and address are done by external logic, the chip is only has external address, frequency registers and its update pins. + +## Frequency calculation + +``` +Input clock / (4096 - Pitch input) +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp new file mode 100644 index 00000000..464ce5bc --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp @@ -0,0 +1,42 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K005289 emulation core +*/ + +#include "k005289.hpp" + +void k005289_core::tick() +{ + for (timer_t &elem : m_timer) + { + elem.tick(); + } +} + +void k005289_core::reset() +{ + for (timer_t &elem : m_timer) + { + elem.reset(); + } +} + +void k005289_core::timer_t::tick() +{ + if (bitfield(++m_counter, 0, 12) == 0) + { + m_addr = bitfield(m_addr + 1, 0, 5); + m_counter = m_freq; + } +} + +void k005289_core::timer_t::reset() +{ + m_addr = 0; + m_pitch = 0; + m_freq = 0; + m_counter = 0; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp new file mode 100644 index 00000000..6fae1f2c --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp @@ -0,0 +1,83 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K005289 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_K005289_HPP +#define _VGSOUND_EMU_SRC_K005289_HPP + +#pragma once + +#include "../core/util.hpp" + +class k005289_core : public vgsound_emu_core +{ + private: + // k005289 timer classes + class timer_t : public vgsound_emu_core + { + public: + timer_t() + : vgsound_emu_core("k005289_timer") + , m_addr(0) + , m_pitch(0) + , m_freq(0) + , m_counter(0) + { + } + + // internal state + void reset(); + void tick(); + + // accessors + // Replace current frequency to lastest loaded pitch + inline void update() { m_freq = m_pitch; } + + // setters + // Load pitch data (address pin) + inline void load(u16 pitch) { m_pitch = pitch; } + + // getters + inline u8 addr() { return m_addr; } + + private: + // registers + u8 m_addr = 0; // external address pin + u16 m_pitch = 0; // pitch + u16 m_freq = 0; // current frequency + s16 m_counter = 0; // frequency counter + }; + + public: + // constructor + k005289_core() + : vgsound_emu_core("k005289") + , m_timer{timer_t()} + { + } + + // internal state + void reset(); + void tick(); + + // accessors + // TG1/2 pin + inline void update(int voice) { m_timer[voice & 1].update(); } + + // setters + // LD1/2 pin, A0...11 pin + inline void load(int voice, u16 addr) { m_timer[voice & 1].load(addr); } + + // getters + // 1QA...E/2QA...E pin + inline u8 addr(int voice) { return m_timer[voice & 1].addr(); } + + private: + std::array m_timer; +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/README.md b/vgsound_emu-modified/vgsound_emu/src/k007232/README.md new file mode 100644 index 00000000..790d3107 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k007232/README.md @@ -0,0 +1,75 @@ +# Konami K007232 + +## Summary + +- 2 voice PCM + - 7 bit unsigned, with end marker + - total accessible memory: 128 Kbyte per bank + - Per-voice bankswitchable via E clock +- External 8 bit I/O (usually for volume) + +## Source code + +- k007232.hpp: Base header + - k007232.cpp: Source emulation core + +## Description + +It's Konami's one of custom PCM sound chip, Used at their arcade hardware at mid-80s to early-90s. + +It has 2 channel of PCM, these are has its own output pins...just 7 LSB of currently fetched data. + +PCM Sample format is unique, 1 MSB is end marker and 7 LSB is actually output. (unsigned format) + +The chip itself is DACless, so Sound output and mixing control needs external logics and sound DAC. + +## Register layout + +``` + Address Bits R/W Description + 7654 3210 + + 0x0 xxxx xxxx W Channel 0 Pitch bit 0-7 + 0x1 --x- ---- W Channel 0 4 bit Frequency mode + ---x ---- W Channel 0 8 bit Frequency mode + ---- xxxx W Channel 0 Pitch bit 8-11 + + 0x2 xxxx xxxx W Channel 0 Start address bit 0-7 + 0x3 xxxx xxxx W Channel 0 Start address bit 8-15 + 0x4 ---- ---x W Channel 0 Start address bit 16 + + 0x5 R/W Channel 0 Key on trigger (R/W) + + 0x6 xxxx xxxx W Channel 1 Pitch bit 0-7 + 0x7 --x- ---- W Channel 1 4 bit Frequency mode + ---x ---- W Channel 1 8 bit Frequency mode + ---- xxxx W Channel 1 Pitch bit 8-11 + + 0x8 xxxx xxxx W Channel 1 Start address bit 0-7 + 0x9 xxxx xxxx W Channel 1 Start address bit 8-15 + 0xa ---- ---x W Channel 1 Start address bit 16 + + 0xb R/W Channel 1 Key on trigger (R/W) + + 0xc xxxx xxxx W External port write (w/SLEV pin, Usually for volume control) + + 0xd ---- --x- W Channel 1 Loop enable + ---- ---x W Channel 0 Loop enable +``` + +## Frequency calculation + +(Guesswork in 8/4 bit Frequency mode) + +``` + if 8 bit Frequency mode then + Frequency: Input clock / 4 * (256 - (Pitch bit 0 - 7)) + else if 4 bit Frequency mode then + Frequency: Input clock / 4 * (16 - (Pitch bit 8 - 11)) + else + Frequency: Input clock / 4 * (4096 - (Pitch bit 0 - 11)) +``` + +## Reference + + diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp new file mode 100644 index 00000000..3798f4c8 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp @@ -0,0 +1,169 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K007232 core +*/ + +#include "k007232.hpp" + +void k007232_core::tick() +{ + for (int i = 0; i < 2; i++) + { + m_voice[i].tick(i); + } +} + +void k007232_core::voice_t::tick(u8 ne) +{ + if (m_busy) + { + const bool is4bit = bitfield(m_pitch, 13); // 4 bit frequency divider flag + const bool is8bit = bitfield(m_pitch, 12); // 8 bit frequency divider flag + + // update counter + if (is4bit) + { + m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) + 1, 0, 8) << 0); + m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) + 1, 0, 4) << 8); + } + else + { + m_counter++; + } + + // handle counter carry + bool carry = + is8bit ? (bitfield(m_counter, 0, 8) == 0) + : (is4bit ? (bitfield(m_counter, 8, 4) == 0) : (bitfield(m_counter, 0, 12) == 0)); + if (carry) + { + m_counter = bitfield(m_pitch, 0, 12); + if (is4bit) // 4 bit frequency has different behavior for address + { + m_addr = (m_addr & ~0x0000f) | (bitfield(bitfield(m_addr, 0, 4) + 1, 0, 4) << 0); + m_addr = (m_addr & ~0x000f0) | (bitfield(bitfield(m_addr, 4, 4) + 1, 0, 4) << 4); + m_addr = (m_addr & ~0x00f00) | (bitfield(bitfield(m_addr, 8, 4) + 1, 0, 4) << 8); + m_addr = (m_addr & ~0x1f000) | (bitfield(bitfield(m_addr, 12, 5) + 1, 0, 5) << 12); + } + else + { + m_addr = bitfield(m_addr + 1, 0, 17); + } + } + + m_data = m_host.m_intf.read_sample(ne, bitfield(m_addr, 0, 17)); // fetch ROM + if (bitfield(m_data, 7)) // check end marker + { + if (m_loop) + { + m_addr = m_start; + } + else + { + m_busy = false; + } + } + + m_out = s8(m_data) - 0x40; // send to output (ASD/BSD) pin + } + else + { + m_out = 0; + } +} + +void k007232_core::write(u8 address, u8 data) +{ + address &= 0xf; // 4 bit for CPU write + + switch (address) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: // voice 0 + case 0x6: + case 0x7: + case 0x8: + case 0x9: + case 0xa: + case 0xb: // voice 1 + m_voice[(address / 6) & 1].write(address % 6, data); + break; + case 0xc: // external register with SLEV pin + m_intf.write_slev(data); + break; + case 0xd: // loop flag + m_voice[0].set_loop(bitfield(data, 0)); + m_voice[1].set_loop(bitfield(data, 1)); + break; + default: break; + } + + m_reg[address] = data; +} + +// write registers on each voices +void k007232_core::voice_t::write(u8 address, u8 data) +{ + switch (address) + { + case 0: // pitch LSB + m_pitch = (m_pitch & ~0x00ff) | data; + break; + case 1: // pitch MSB, divider + m_pitch = (m_pitch & ~0x3f00) | (u16(bitfield(data, 0, 6)) << 8); + break; + case 2: // start address bit 0-7 + m_start = (m_start & ~0x000ff) | data; + break; + case 3: // start address bit 8-15 + m_start = (m_start & ~0x0ff00) | (u32(data) << 8); + break; + case 4: // start address bit 16 + m_start = (m_start & ~0x10000) | (u32(bitfield(data, 16)) << 16); + break; + case 5: // keyon trigger + keyon(); + break; + } +} + +// key on trigger (write OR read 0x05/0x11 register) +void k007232_core::voice_t::keyon() +{ + m_busy = true; + m_counter = bitfield(m_pitch, 0, 12); + m_addr = m_start; +} + +// reset chip +void k007232_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_intf.write_slev(0); + + std::fill(m_reg.begin(), m_reg.end(), 0); +} + +// reset voice +void k007232_core::voice_t::reset() +{ + m_busy = false; + m_loop = false; + m_pitch = 0; + m_start = 0; + m_counter = 0; + m_addr = 0; + m_data = 0; + m_out = 0; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp new file mode 100644 index 00000000..d500f091 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp @@ -0,0 +1,115 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K007232 core +*/ + +#ifndef _VGSOUND_EMU_SRC_K007232_HPP +#define _VGSOUND_EMU_SRC_K007232_HPP + +#pragma once + +#include "../core/util.hpp" + +class k007232_intf : public vgsound_emu_core +{ + public: + k007232_intf() + : vgsound_emu_core("k007232_intf") + { + } + + // NE pin is executing voice number, and used for per-voice sample bank. + virtual u8 read_sample(u8 ne, u32 address) { return 0; } + + // SLEV pin actived when 0x0c register accessed + virtual void write_slev(u8 out) {} +}; + +class k007232_core : public vgsound_emu_core +{ + friend class k007232_intf; // k007232 specific interface + + private: + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(k007232_core &host) + : vgsound_emu_core("k007232_voice") + , m_host(host) + , m_busy(false) + , m_loop(false) + , m_pitch(0) + , m_start(0) + , m_counter(0) + , m_addr(0) + , m_data(0) + , m_out(0) + { + } + + // internal state + void reset(); + void tick(u8 ne); + + // accessors + void write(u8 address, u8 data); + void keyon(); + + // setters + inline void set_loop(bool loop) { m_loop = loop; } + + // getters + inline s8 out() { return m_out; } + + private: + // registers + k007232_core &m_host; + bool m_busy = false; // busy status + bool m_loop = false; // loop flag + u16 m_pitch = 0; // pitch, frequency divider + u32 m_start = 0; // start position when keyon or loop start position at + // when reach end marker if loop enabled + u16 m_counter = 0; // frequency counter + u32 m_addr = 0; // current address + u8 m_data = 0; // current data + s8 m_out = 0; // current output (7 bit unsigned) + }; + + public: + // constructor + k007232_core(k007232_intf &intf) + : vgsound_emu_core("k007232") + , m_voice{*this, *this} + , m_intf(intf) + , m_reg{0} + { + } + + // host accessors + void keyon(u8 voice) { m_voice[voice & 1].keyon(); } + + void write(u8 address, u8 data); + + // internal state + void reset(); + void tick(); + + // output for each voices, ASD/BSD pin + inline s32 output(u8 voice) { return m_voice[voice & 1].out(); } + + // getters for debug, trackers, etc + inline u8 reg_r(u8 address) { return m_reg[address & 0xf]; } + + private: + std::array m_voice; + + k007232_intf &m_intf; // common memory interface + + std::array m_reg = {0}; // register pool +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/README.md b/vgsound_emu-modified/vgsound_emu/src/k053260/README.md new file mode 100644 index 00000000..e34947f9 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k053260/README.md @@ -0,0 +1,109 @@ +# Konami K053260 + +## Summary + +- 4 voice DPCM or PCM + - 8 bit signed PCM or 4 bit DPCM (unique type) + - total accessible memory: 2 MByte, 64 KByte per sample +- CPU to CPU Communication +- CPU can be accessible k053260 memory through voice register +- 2 Stereo sound input, 1 Stereo output (YM3012 DAC compatible) + +## Source code + +- k053260.hpp: Base header + - k053260.cpp: Source emulation core + +## Description + +It's one of Konami's custom PCM playback chip with CPU to CPU communication feature, and built in timer. + +It's architecture is successed from K007232, but it features various enhancements: + +it's expanded to 4 channels, Supports more memory space, 4 bit DPCM, Built in volume and stereo panning support, and Dual chip configurations. + +There's 2 stereo inputs and single stereo output, Both format is YM3012 compatible. + +## Register layout + +``` +Address Bits R/W Description + 7654 3210 + +00...03 Communication Registers + +00 xxxx xxxx R Answer from host CPU LSB +01 xxxx xxxx R Answer from host CPU MSB + +02 xxxx xxxx W Reply to host CPU LSB +03 xxxx xxxx W Reply to host CPU MSB + +08...0f Voice 0 Register + +08 xxxx xxxx W Pitch bit 0-7 +09 ---- xxxx W Pitch bit 8-11 + +0a xxxx xxxx W Source length bit 0-7 (byte wide) +0b xxxx xxxx W Source length bit 8-15 (byte wide) + +0c xxxx xxxx W Start address/ROM readback base bit 0-7 +0d xxxx xxxx W Start address/ROM readback base bit 8-15 +0e ---x xxxx W Start address/ROM readback base bit 16-20 + +0f -xxx xxxx W Volume + +10...17 Voice 1 Register +18...1f Voice 2 Register +20...27 Voice 3 Register + +28 ---- x--- W Voice 3 Key on/off trigger + ---- -x-- W Voice 2 Key on/off trigger + ---- --x- W Voice 1 Key on/off trigger + ---- ---x W Voice 0 Key on/off trigger + +29 ---- x--- R Voice 3 busy + ---- -x-- R Voice 2 busy + ---- --x- R Voice 1 busy + ---- ---x R Voice 0 busy + +2a x--- ---- W Voice 3 source format + 0--- ---- 8 bit signed PCM + 1--- ---- 4 bit ADPCM + -x-- ---- W Voice 2 source format + --x- ---- W Voice 1 source format + ---x ---- W Voice 0 source format + + ---- x--- W Voice 3 Loop enable + ---- -x-- W Voice 2 Loop enable + ---- --x- W Voice 1 Loop enable + ---- ---x W Voice 0 Loop enable + +2c --xx x--- W Voice 1 Pan angle in degrees*1 + --00 0--- Mute + --00 1--- 0 degrees + --01 0--- 24 degrees + --01 1--- 35 degrees + --10 0--- 45 degrees + --10 1--- 55 degrees + --11 0--- 66 degrees + --11 1--- 90 degrees + ---- -xxx W Voice 0 Pan angle in degrees*1 + +2d --xx x--- W Voice 3 Pan angle in degrees*1 + ---- -xxx W Voice 2 Pan angle in degrees*1 + +2e xxxx xxxx R ROM readback (use Voice 0 register) + +2f ---- x--- W AUX2 input enable + ---- -x-- W AUX1 input enable + ---- --x- W Sound enable + ---- ---x W ROM readbank enable + +*1 Actually fomula unknown, Use floating point type until explained that. +``` + +## Frequency calculation + +``` + Frequency: Input clock / (4096 - Pitch) +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp new file mode 100644 index 00000000..6fe759ac --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp @@ -0,0 +1,290 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K053260 core +*/ + +#include "k053260.hpp" + +void k053260_core::tick() +{ + m_out[0] = m_out[1] = 0; + if (m_ctrl.sound_en()) + { + for (int i = 0; i < 4; i++) + { + m_voice[i].tick(i); + m_out[0] += m_voice[i].out(0); + m_out[1] += m_voice[i].out(1); + } + } + // dac clock (YM3012 format) + u8 dac_clock = m_dac.clock(); + if (bitfield(++dac_clock, 0, 4) == 0) + { + m_intf.write_int(m_dac.state()); + u8 dac_state = m_dac.state(); + if (bitfield(++dac_state, 0) == 0) + { + m_ym3012.tick(bitfield(dac_state, 1), m_out[bitfield(dac_state, 1) ^ 1]); + } + + m_dac.set_state(bitfield(dac_state, 0, 2)); + } + m_dac.set_clock(bitfield(dac_clock, 0, 4)); +} + +void k053260_core::voice_t::tick(u8 ne) +{ + if (m_enable && m_busy) + { + bool update = false; + // update counter + if (bitfield(++m_counter, 0, 12) == 0) + { + if (m_bitpos < 8) + { + m_bitpos += 8; + m_addr = bitfield(m_addr + 1, 0, 21); + m_remain--; + } + if (m_adpcm) + { + m_bitpos -= 4; + update = true; + } + else + { + m_bitpos -= 8; + } + } + m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM + if (update) + { + const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM + if (nibble) + { + m_adpcm_buf += bitfield(nibble, 3) ? s8(0x80 >> bitfield(nibble, 0, 3)) + : (1 << bitfield(nibble - 1, 0, 3)); + } + } + + if (m_remain < 0) // check end flag + { + if (m_loop) + { + m_addr = m_start; + m_adpcm_buf = 0; + } + else + { + m_busy = false; + } + } + // calculate output + s32 output = m_adpcm ? m_adpcm_buf : sign_ext(m_data, 8) * s32(m_volume); + // use math for now; actually fomula unknown + m_out[0] = (m_pan >= 0) ? s32(output * cos(f64(m_pan) * PI / 180)) : 0; + m_out[1] = (m_pan >= 0) ? s32(output * sin(f64(m_pan) * PI / 180)) : 0; + } + else + { + m_out[0] = m_out[1] = 0; + } +} + +u8 k053260_core::read(u8 address) +{ + address &= 0x3f; // 6 bit for CPU read + + switch (address) + { + case 0x0: + case 0x1: // Answer from host + return m_host2snd[address & 1]; + break; + case 0x29: // Voice playing status + return (m_voice[0].busy() ? 0x1 : 0x0) | (m_voice[1].busy() ? 0x2 : 0x0) | + (m_voice[2].busy() ? 0x4 : 0x0) | (m_voice[3].busy() ? 0x8 : 0x0); + case 0x2e: // ROM readback + { + if (!m_ctrl.rom_read()) + { + return 0xff; + } + + const u32 rom_addr = m_voice[0].start() + m_voice[0].length(); + m_voice[0].length_inc(); + return m_intf.read_sample(rom_addr); + } + } + return 0xff; +} + +void k053260_core::write(u8 address, u8 data) +{ + address &= 0x3f; // 6 bit for CPU write + + switch (address) + { + case 0x2: + case 0x3: // Reply to host + m_snd2host[address & 1] = data; + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: // voice 0 + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: // voice 1 + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: // voice 2 + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: // voice 3 + m_voice[bitfield(address - 0x8, 3, 2)].write(bitfield(address, 0, 3), data); + break; + case 0x28: // keyon/off toggle + for (int i = 0; i < 4; i++) + { + if (bitfield(data, i) && (!m_voice[i].enable())) + { // rising edge (keyon) + m_voice[i].keyon(); + } + else if ((!bitfield(data, i)) && m_voice[i].enable()) + { // falling edge (keyoff) + m_voice[i].keyoff(); + } + } + break; + case 0x2a: // loop/adpcm flag + for (int i = 0; i < 4; i++) + { + m_voice[i].set_loop(bitfield(data, i)); + m_voice[i].set_adpcm(bitfield(data, i + 4)); + } + break; + case 0x2c: + m_voice[0].set_pan(bitfield(data, 0, 3)); + m_voice[1].set_pan(bitfield(data, 3, 3)); + break; + case 0x2d: + m_voice[2].set_pan(bitfield(data, 0, 3)); + m_voice[3].set_pan(bitfield(data, 3, 3)); + break; + case 0x2f: m_ctrl.write(data); break; + default: break; + } + + m_reg[address] = data; +} + +// write registers on each voices +void k053260_core::voice_t::write(u8 address, u8 data) +{ + switch (address) + { + case 0: // pitch LSB + m_pitch = (m_pitch & ~0x00ff) | data; + break; + case 1: // pitch MSB + m_pitch = (m_pitch & ~0x0f00) | (u16(bitfield(data, 0, 4)) << 8); + break; + case 2: // source length LSB + m_length = (m_length & ~0x000ff) | data; + break; + case 3: // source length MSB + m_length = (m_length & ~0x0ff00) | (u16(data) << 8); + break; + case 4: // start address bit 0-7 + m_start = (m_start & ~0x0000ff) | data; + break; + case 5: // start address bit 8-15 + m_start = (m_start & ~0x00ff00) | (u32(data) << 8); + break; + case 6: // start address bit 16-20 + m_start = (m_start & ~0x1f0000) | (u32(bitfield(data, 16, 5)) << 16); + break; + case 7: // volume + m_volume = bitfield(data, 0, 7); + break; + } +} + +// key on trigger +void k053260_core::voice_t::keyon() +{ + m_enable = m_busy = 1; + m_counter = bitfield(m_pitch, 0, 12); + m_addr = m_start; + m_remain = m_length; + m_bitpos = 4; + m_adpcm_buf = 0; + std::fill(m_out.begin(), m_out.end(), 0); +} + +// key off trigger +void k053260_core::voice_t::keyoff() { m_enable = m_busy = 0; } + +// reset chip +void k053260_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_intf.write_int(0); + + std::fill(m_host2snd.begin(), m_host2snd.end(), 0); + std::fill(m_snd2host.begin(), m_snd2host.end(), 0); + m_ctrl.reset(); + m_dac.reset(); + + std::fill(m_reg.begin(), m_reg.end(), 0); + std::fill(m_out.begin(), m_out.end(), 0); +} + +// reset voice +void k053260_core::voice_t::reset() +{ + m_enable = 0; + m_busy = 0; + m_loop = 0; + m_adpcm = 0; + m_pitch = 0; + m_start = 0; + m_length = 0; + m_volume = 0; + m_pan = -1; + m_counter = 0; + m_addr = 0; + m_remain = 0; + m_bitpos = 4; + m_data = 0; + m_adpcm_buf = 0; + m_out[0] = m_out[1] = 0; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp new file mode 100644 index 00000000..de39e35e --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp @@ -0,0 +1,262 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K053260 core +*/ + +#ifndef _VGSOUND_EMU_SRC_K053260_HPP +#define _VGSOUND_EMU_SRC_K053260_HPP + +#pragma once + +#include "../core/util.hpp" + +class k053260_intf : public vgsound_emu_core +{ + public: + k053260_intf() + : vgsound_emu_core("k053260_intf") + { + } + + virtual u8 read_sample(u32 address) { return 0; } // sample fetch + + virtual void write_int(u8 out) {} // timer interrupt +}; + +class k053260_core : public vgsound_emu_core +{ + friend class k053260_intf; // k053260 specific interface + + private: + const int pan_dir[8] = {-1, 0, 24, 35, 45, 55, 66, 90}; // pan direction + + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(k053260_core &host) + : vgsound_emu_core("k053260_voice") + , m_host(host) + , m_enable(0) + , m_busy(0) + , m_loop(0) + , m_adpcm(0) + , m_pitch(0) + , m_start(0) + , m_length(0) + , m_volume(0) + , m_pan(-1) + , m_counter(0) + , m_addr(0) + , m_remain(0) + , m_bitpos(4) + , m_data(0) + , m_adpcm_buf(0) + , m_out{0} + { + } + + // internal state + void reset(); + void tick(u8 ne); + + // accessors + void write(u8 address, u8 data); + void keyon(); + void keyoff(); + + // setters + inline void set_enable(bool enable) { m_enable = enable ? 1 : 0; } + + inline void set_busy(bool busy) { m_busy = busy ? 1 : 0; } + + inline void set_loop(bool loop) { m_loop = loop ? 1 : 0; } + + inline void set_adpcm(bool adpcm) { m_adpcm = adpcm ? 1 : 0; } + + inline void length_inc() { m_length = (m_length + 1) & 0xffff; } + + inline void set_pan(u8 pan) { m_pan = m_host.pan_dir[pan & 7]; } + + // getters + inline bool enable() { return m_enable; } + + inline bool busy() { return m_busy; } + + inline u32 start() { return m_start; } + + inline u16 length() { return m_length; } + + inline s32 out(u8 ch) { return m_out[ch & 1]; } + + private: + // registers + k053260_core &m_host; + u16 m_enable : 1; // enable flag + u16 m_busy : 1; // busy status + u16 m_loop : 1; // loop flag + u16 m_adpcm : 1; // ADPCM flag + u16 m_pitch : 12; // pitch + u32 m_start = 0; // start position + u16 m_length = 0; // source length + u8 m_volume = 0; // master volume + int m_pan = -1; // master pan + u16 m_counter = 0; // frequency counter + u32 m_addr = 0; // current address + s32 m_remain = 0; // remain for end sample + u8 m_bitpos = 4; // bit position for ADPCM decoding + u8 m_data = 0; // current data + s8 m_adpcm_buf = 0; // ADPCM buffer + std::array m_out = {0}; // current output + }; + + class ctrl_t + { + public: + ctrl_t() + : m_rom_read(0) + , m_sound_en(0) + , m_input_en(0) + { + } + + void reset() + { + m_rom_read = 0; + m_sound_en = 0; + m_input_en = 0; + } + + void write(u8 data) + { + m_rom_read = (data >> 0) & 1; + m_sound_en = (data >> 1) & 1; + m_input_en = (data >> 2) & 3; + } + + // getters + inline bool rom_read() { return m_rom_read; } + + inline bool sound_en() { return m_sound_en; } + + inline u8 input_en() { return m_input_en; } + + private: + u8 m_rom_read : 1; // ROM readback + u8 m_sound_en : 1; // Sound enable + u8 m_input_en : 2; // Input enable + }; + + class ym3012_t + { + public: + ym3012_t() + : m_in{0} + , m_out{0} + { + } + + void reset() + { + std::fill(m_in.begin(), m_in.end(), 0); + std::fill(m_out.begin(), m_out.end(), 0); + } + + void tick(u8 ch, s32 in) + { + m_out[(ch & 1)] = m_in[(ch & 1)]; + m_in[(ch & 1) ^ 1] = in; + } + + private: + std::array m_in = {0}; + std::array m_out = {0}; + }; + + class dac_t + { + public: + dac_t() + : m_clock(0) + , m_state(0) + { + } + + void reset() + { + m_clock = 0; + m_state = 0; + } + + inline void set_clock(u8 clock) { m_clock = clock; } + + inline void set_state(u8 state) { m_state = state; } + + inline u8 clock() { return m_clock; } + + inline u8 state() { return m_state; } + + private: + u8 m_clock : 4; // DAC clock (16 clock) + u8 m_state : 2; // DAC state (4 state - SAM1, SAM2) + }; + + public: + // constructor + k053260_core(k053260_intf &intf) + : vgsound_emu_core("k053260") + , m_voice{*this, *this, *this, *this} + , m_intf(intf) + , m_host2snd{0} + , m_snd2host{0} + , m_ctrl(ctrl_t()) + , m_ym3012(ym3012_t()) + , m_dac(dac_t()) + , m_reg{0} + , m_out{0} + { + } + + // communications + inline u8 snd2host_r(u8 address) { return m_snd2host[address & 1]; } + + inline void host2snd_w(u8 address, u8 data) { m_host2snd[address & 1] = data; } + + // sound accessors + u8 read(u8 address); + void write(u8 address, u8 data); + + // internal state + void reset(); + void tick(); + + // getters for debug, trackers, etc + inline s32 output(u8 ch) { return m_out[ch & 1]; } // output for each channels + + inline u8 reg_r(u8 address) { return m_reg[address & 0x3f]; } + + inline s32 voice_out(u8 voice, u8 ch) + { + return (voice < 4) ? m_voice[voice].out(ch & 1) : 0; + } + + private: + std::array m_voice; + k053260_intf &m_intf; // common memory interface + + std::array m_host2snd = {0}; + std::array m_snd2host = {0}; + + ctrl_t m_ctrl; // chip control + + ym3012_t m_ym3012; // YM3012 output + dac_t m_dac; // YM3012 interface + + std::array m_reg = {0}; // register pool + std::array m_out = {0}; // stereo output +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md b/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md new file mode 100644 index 00000000..626b6918 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md @@ -0,0 +1,67 @@ +# OKI MSM6295 + +## Summary + +- 4 voice Dialogic ADPCM + - total accessible memory: 256 KByte +- Clock divider via SS pin + +## Source code + +- msm6295.hpp: Base header + - msm6295.cpp: Source emulation core + +### Dependencies + +- vox.hpp: Dialogic ADPCM decoder header + - vox.cpp: Dialogic ADPCM decoder source + +## Description + +It is 4 channel ADPCM playback chip from OKI semiconductor. It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance. + +The chip itself is pretty barebone: there is no "register" in chip. It can't control volume and pitch in currently playing channels, only stopable them. And volume is must be determined at playback start command. + +## Command format + + Playback command (2 byte): + +``` +Byte Bit Description + 76543210 +0 1xxxxxxx Phrase select (Header stored in ROM) +1 x000---- Play channel 4 + 0x00---- Play channel 3 + 00x0---- Play channel 2 + 000x---- Play channel 1 + ----xxxx Volume + ----0000 0.0dB + ----0001 -3.2dB + ----0010 -6.0dB + ----0011 -9.2dB + ----0100 -12.0dB + ----0101 -14.5dB + ----0110 -18.0dB + ----0111 -20.5dB + ----1000 -24.0dB +``` + + Suspend command (1 byte): + +``` +Byte Bit Description + 76543210 +0 0x------ Suspend channel 4 + 0-x----- Suspend channel 3 + 0--x---- Suspend channel 2 + 0---x--- Suspend channel 1 +``` + +## Frequency calculation + +``` + if (SS) then + Frequency = Input clock / 165 + else then + Frequency = Input clock / 132 +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp new file mode 100644 index 00000000..e09c45d9 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp @@ -0,0 +1,179 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + OKI MSM6295 emulation core +*/ + +#include "msm6295.hpp" + +void msm6295_core::tick() +{ + if (m_counter < 4) + { + m_voice[m_counter].tick(); + m_out_temp += m_voice[m_counter].out(); + } + if ((++m_counter) >= (m_ss ? 5 : 4)) + { + // command handler + if (m_command_pending) + { + if (bitfield(m_command, 7)) // play voice + { + if ((++m_clock) >= 15) + { + if (bitfield(m_next_command, 4, 4) != 0) + { + for (int i = 0; i < 4; i++) + { + if (bitfield(m_next_command, 4 + i)) + { + if (!m_voice[i].busy()) + { + m_voice[i].set_command(m_command); + m_voice[i].set_volume(bitfield(m_next_command, 0, 4)); + } + // voices aren't be playable simultaneously at once + break; + } + } + } + m_command = 0; + m_command_pending = false; + m_clock = 0; + } + } + else if (bitfield(m_next_command, 7)) // select phrase + { + if ((++m_clock) >= 15) + { + m_command = m_next_command; + m_command_pending = false; + m_clock = 0; + } + } + else + { + if (bitfield(m_next_command, 3, 4) != 0) // suspend voices + { + for (int i = 0; i < 4; i++) + { + if (bitfield(m_next_command, 3 + i)) + { + if (m_voice[i].busy()) + { + m_voice[i].set_command(m_next_command); + } + } + } + m_next_command &= ~0x78; + } + m_command_pending = false; + } + } + m_out = m_out_temp; + m_out_temp = 0; + m_counter = 0; + } +} + +void msm6295_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_command = 0; + m_next_command = 0; + m_command_pending = false; + m_clock = 0; + m_counter = 0; + m_out = 0; + m_out_temp = 0; +} + +void msm6295_core::voice_t::tick() +{ + if (!m_busy) + { + if (bitfield(m_command, 7)) + { + // get phrase header (stored in data memory) + const u32 phrase = bitfield(m_command, 0, 7) << 3; + // Start address + m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16) | + (m_host.m_intf.read_byte(phrase | 1) << 8) | + (m_host.m_intf.read_byte(phrase | 2) << 0); + // End address + m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16) | + (m_host.m_intf.read_byte(phrase | 4) << 8) | + (m_host.m_intf.read_byte(phrase | 5) << 0); + m_nibble = 4; // MSB first, LSB second + m_command = 0; + m_busy = true; + vox_decoder_t::reset(); + } + m_out = 0; + } + else + { + // playback + if ((++m_clock) >= 33) + { + bool is_end = (m_command != 0); // suspend + decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4)); + if (m_nibble <= 0) + { + m_nibble = 4; + if (++m_addr > m_end) + { + is_end = true; + } + } + else + { + m_nibble -= 4; + } + if (is_end) + { + m_command = 0; + m_busy = false; + } + m_out = (step() * m_volume) >> 7; // scale out to 12 bit output + m_clock = 0; + } + } +} + +void msm6295_core::voice_t::reset() +{ + vox_decoder_t::reset(); + m_clock = 0; + m_busy = false; + m_command = 0; + m_addr = 0; + m_nibble = 0; + m_end = 0; + m_volume = 0; + m_out = 0; + m_mute = false; +} + +// accessors +u8 msm6295_core::busy_r() +{ + return (m_voice[0].busy() ? 0x01 : 0x00) | (m_voice[1].busy() ? 0x02 : 0x00) | + (m_voice[2].busy() ? 0x04 : 0x00) | (m_voice[3].busy() ? 0x08 : 0x00); +} + +void msm6295_core::command_w(u8 data) +{ + if (!m_command_pending) + { + m_next_command = data; + m_command_pending = true; + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp new file mode 100644 index 00000000..04326ae6 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp @@ -0,0 +1,142 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + OKI MSM6295 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_MSM6295_HPP +#define _VGSOUND_EMU_SRC_MSM6295_HPP + +#pragma once + +#include "../core/util.hpp" +#include "../core/vox/vox.hpp" + +class msm6295_core : public vox_core +{ + friend class vgsound_emu_mem_intf; // common memory interface + + private: + // Internal volume table, 9 step + const s32 m_volume_table[9] = {32 /* 0.0dB */, + 22 /* -3.2dB */, + 16 /* -6.0dB */, + 11 /* -9.2dB */, + 8 /* -12.0dB */, + 6 /* -14.5dB */, + 4 /* -18.0dB */, + 3 /* -20.5dB */, + 2 /* -24.0dB */}; // scale out to 5 bit for optimization + + // msm6295 voice classes + class voice_t : vox_decoder_t + { + public: + // constructor + voice_t(msm6295_core &host) + : vox_decoder_t(host, false) + , m_host(host) + , m_clock(0) + , m_busy(false) + , m_command(0) + , m_addr(0) + , m_nibble(0) + , m_end(0) + , m_volume(0) + , m_out(0) + , m_mute(false) + { + } + + // internal state + virtual void reset() override; + void tick(); + + // Setters + inline void set_command(u8 command) { m_command = command; } + + inline void set_volume(s32 volume) + { + m_volume = (volume < 9) ? m_host.m_volume_table[volume] : 0; + } + + inline void set_mute(bool mute) { m_mute = mute; } + + // Getters + inline bool busy() { return m_busy; } + + inline s32 out() { return m_mute ? 0 : m_out; } + + private: + // accessors, getters, setters + // registers + msm6295_core &m_host; // host core + u16 m_clock = 0; // clock counter + bool m_busy = false; // busy status + u8 m_command = 0; // current command + u32 m_addr = 0; // current address + s8 m_nibble = 0; // current nibble + u32 m_end = 0; // end address + s32 m_volume = 0; // volume + s32 m_out = 0; // output + // for preview only + bool m_mute = false; // mute flag + }; + + public: + // constructor + msm6295_core(vgsound_emu_mem_intf &intf) + : vox_core("msm6295") + , m_voice{*this, *this, *this, *this} + , m_intf(intf) + , m_ss(false) + , m_command(0) + , m_next_command(0) + , m_command_pending(false) + , m_clock(0) + , m_counter(0) + , m_out(0) + , m_out_temp(0) + { + } + + // accessors, getters, setters + u8 busy_r(); + void command_w(u8 data); + + inline void ss_w(bool ss) { m_ss = ss; } // SS pin + + // internal state + void reset(); + void tick(); + + inline s32 out() { return m_out; } // built in 12 bit DAC + + // for preview + inline void voice_mute(u8 voice, bool mute) + { + if (voice < 4) + { + m_voice[voice].set_mute(mute); + } + } + + inline s32 voice_out(u8 voice) { return (voice < 4) ? m_voice[voice].out() : 0; } + + private: + std::array m_voice; + vgsound_emu_mem_intf &m_intf; // common memory interface + + bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4) + u8 m_command = 0; // Command byte + u8 m_next_command = 0; // Next command + bool m_command_pending = false; // command pending flag + u16 m_clock = 0; // clock counter + u16 m_counter = 0; // another clock counter + s32 m_out = 0; // 12 bit output + s32 m_out_temp = 0; // temporary buffer of above +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/README.md b/vgsound_emu-modified/vgsound_emu/src/n163/README.md new file mode 100644 index 00000000..984356be --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/n163/README.md @@ -0,0 +1,88 @@ +# Namco 163 + +## Summary + +- 1 to 8 voice wavetable + - 4 bit unsigned + - each waveforms are can be placed to anywhere in internal RAM, and its size is can be variable. +- activated voice count can be changed any time, multiplexed output + +## Source code + +- n163.hpp: Base header + - n163.cpp: Source emulation core + +## Description + +This chip is one of NES mapper with sound expansion, This one is by Namco. + +It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM. 4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM. + +But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels. + +Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality. + +## Sound register layout + +``` +Address Bit Description + 7654 3210 + +78-7f Channel 0 +78 xxxx xxxx Channel 0 Pitch input bit 0-7 +79 xxxx xxxx Channel 0 Accumulator bit 0-7 +7a xxxx xxxx Channel 0 Pitch input bit 8-15 +7b xxxx xxxx Channel 0 Accumulator bit 8-15 +7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4) + ---- --xx Channel 0 Pitch input bit 16-17 +7d xxxx xxxx Channel 0 Accumulator bit 16-23 +7e xxxx xxxx Channel 0 Waveform base offset + xxxx xxx- RAM byte (0 to 127) + ---- ---x RAM nibble + ---- ---0 Low nibble + ---- ---1 High nibble +7f ---- xxxx Channel 0 Volume + +7f Number of active channels +7f -xxx ---- Number of active channels + -000 ---- Channel 0 activated + -001 ---- Channel 1 activated + -010 ---- Channel 2 activated + ... + -110 ---- Channel 6 activated + -111 ---- Channel 7 activated + +70-77 Channel 1 (Optional if activated) +68-6f Channel 2 (Optional if activated) +... +48-4f Channel 6 (Optional if activated) +40-47 Channel 7 (Optional if activated) +``` + +Rest of RAM area are for 4 bit Waveform and/or scratchpad. + +## Waveform format + +Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next. + +``` + ---- xxxx 4 bit waveform, LSB + xxxx ---- Same as above, MSB +``` + +Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM. + +## Frequency calculation + +``` + Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536) +``` + +## Technical notice + +This core only outputs raw output from chip (or accumulated output, see below); any kind of off-chip stuff needs to implemented outside core. + +There's to way for reduce N163 noises: reduce channel limit and demultiplex: + +- Channel limit is runtime changeable and it makes some usable effects. +- Demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered) This core is support both, You can choose output behavior diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp b/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp new file mode 100644 index 00000000..783bf2df --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp @@ -0,0 +1,120 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Namco 163 Sound emulation core +*/ + +#include "n163.hpp" + +void n163_core::tick() +{ + if (m_multiplex) + { + m_out = 0; + } + // 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank + // 0x8000-0x9fff select.) + if (m_disable) + { + if (!m_multiplex) + { + m_out = 0; + } + return; + } + + // tick per each clock + const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | + (bitfield(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency + + u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | + (u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator + + const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc); + const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8); + const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8); + const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4); + + // get per-voice output + const s16 voice_out = (wave * volume); + m_voice_out[(m_voice_cycle >> 3) & 7] = voice_out; + + // accumulate address + accum = bitfield(accum + freq, 0, 24); + if (bitfield(accum, 16, 8) >= length) + { + accum = bitfield(accum, 0, 18); + } + + // writeback to register + m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8); + m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8); + m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8); + + // update voice cycle + bool flush = m_multiplex ? true : false; + m_voice_cycle -= 0x8; + if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3))) + { + if (!m_multiplex) + { + flush = true; + } + m_voice_cycle = 0x78; + } + + // output 4 bit waveform and volume, multiplexed + m_acc += voice_out; + if (flush) + { + m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1)); + m_acc = 0; + } +} + +void n163_core::reset() +{ + // reset this chip + m_disable = false; + m_multiplex = true; + std::fill(m_ram.begin(), m_ram.end(), 0); + m_voice_cycle = 0x78; + m_addr_latch.reset(); + m_out = 0; + m_acc = 0; +} + +// accessor +void n163_core::addr_w(u8 data) +{ + // 0xf800-0xffff Sound address, increment + m_addr_latch.write(data); +} + +void n163_core::data_w(u8 data, bool cpu_access) +{ + // 0x4800-0x4fff Sound data write + m_ram[m_addr_latch.addr()] = data; + + // address latch increment + if (cpu_access && m_addr_latch.incr()) + { + m_addr_latch.addr_inc(); + } +} + +u8 n163_core::data_r(bool cpu_access) +{ + // 0x4800-0x4fff Sound data read + const u8 ret = m_ram[m_addr_latch.addr()]; + + // address latch increment + if (cpu_access && m_addr_latch.incr()) + { + m_addr_latch.addr_inc(); + } + + return ret; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp b/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp new file mode 100644 index 00000000..1c04d1f3 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp @@ -0,0 +1,109 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Namco 163 Sound emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_N163_HPP +#define _VGSOUND_EMU_SRC_N163_HPP + +#pragma once + +#include "../core/util.hpp" + +class n163_core : public vgsound_emu_core +{ + private: + // Address latch + class addr_latch_t : public vgsound_emu_core + { + public: + addr_latch_t() + : vgsound_emu_core("namco_163_addr_latch") + , m_addr(0) + , m_incr(0) + { + } + + void reset() + { + m_addr = 0; + m_incr = 0; + } + + // accessors + inline void write(u8 data) + { + m_addr = (data >> 0) & 0x7f; + m_incr = (data >> 7) & 0x01; + } + + inline void addr_inc() { m_addr = (m_addr + 1) & 0x7f; } + + // getters + inline u8 addr() { return m_addr; } + + inline bool incr() { return m_incr; } + + private: + u8 m_addr : 7; + u8 m_incr : 1; + }; + + public: + n163_core() + : vgsound_emu_core("namco_163") + , m_disable(false) + , m_ram{0} + , m_voice_cycle(0x78) + , m_addr_latch(addr_latch_t()) + , m_out(0) + , m_voice_out{0} + , m_multiplex(true) + , m_acc(0) + { + } + + // accessors, getters, setters + void addr_w(u8 data); + void data_w(u8 data, bool cpu_access = false); + u8 data_r(bool cpu_access = false); + + inline void set_disable(bool disable) { m_disable = disable; } + + // internal state + void reset(); + void tick(); + + // sound output pin + inline s16 out() { return m_out; } + + // register pool + inline u8 reg(u8 addr) { return m_ram[addr & 0x7f]; } + + inline void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; } + + // preview only + inline u8 voice_cycle() { return m_voice_cycle; } + + inline s16 voice_out(u8 voice) + { + return (voice <= ((m_ram[0x7f] >> 4) & 7)) ? m_voice_out[7 - voice] : 0; + } + + private: + bool m_disable = false; + std::array m_ram = {0}; // internal 128 byte RAM + u8 m_voice_cycle = 0x78; // Voice cycle for processing + addr_latch_t m_addr_latch; // address latch + s16 m_out = 0; // output + + std::array m_voice_out = {0}; // per-voice output, for preview only + // demultiplex related + bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate! + s16 m_acc = 0; // accumulated output +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/README.md b/vgsound_emu-modified/vgsound_emu/src/scc/README.md new file mode 100644 index 00000000..2a66bd8c --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/scc/README.md @@ -0,0 +1,314 @@ +# Konami SCC + +## Summary + +- 5 voice wavetable + - 8 bit signed, 32 width long for each voice + - each voice has separated waveform space + - 4th and 5th voice shares waveform (K051649) +- MegaROM mapper (K051649) or MegaRAM mapper (K052539) + +## Source code + +- scc.hpp: Base header + - scc.cpp: Source emulation core + +## Description + +Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator. + +It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake. Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares. and some Third-party MSX utilities still support this due to its market shares. + +There's 2 SCC types: + +- K051649 (or simply known as SCC) + This chip is used for MSX MegaROM Mapper, some arcade machines. + Channel 4 and 5 must be share waveform, other channels has its own waveforms. + +- K052539 (also known as SCC+) + This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher). All channels can be has its own waveforms, and also has backward compatibility mode with K051649. + +## Register layout + +### K051649 + +- 4000-bfff MegaROM Mapper + +``` +Address Bit R/W Description + 7654 3210 + +4000-5fff xxxx xxxx R Bank page 0 +c000-dfff mirror of 4000-5fff + +6000-7fff xxxx xxxx R Bank page 1 +e000-ffff mirror of 6000-7fff + +8000-9fff xxxx xxxx R Bank page 2 +0000-1fff mirror of 8000-9fff + +a000-bfff xxxx xxxx R Bank page 3 +2000-3fff mirror of a000-bfff +``` + +- 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select + +``` +Address Bit R/W Description + 7654 3210 + +5000 --xx xxxx W Bank select, Page 0 +5001-57ff Mirror of 5000 + +7000 --xx xxxx W Bank select, Page 1 +7001-77ff Mirror of 7000 + +9000 --xx xxxx W Bank select, Page 2 + --11 1111 W SCC Enable +9001-97ff Mirror of 9000 + +b000 --xx xxxx W Bank select, Page 3 +b001-b7ff Mirror of b000 +``` + +- 9800-9fff SCC register + +``` +9800-987f Waveform + +Address Bit R/W Description + 7654 3210 + +9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) +9820-983f xxxx xxxx R/W Channel 1 "" +9840-985f xxxx xxxx R/W Channel 2 "" +9860-987f xxxx xxxx R/W Channel 3/4 "" + +9880-9889 Pitch + +9880 xxxx xxxx W Channel 0 Pitch LSB +9881 ---- xxxx W Channel 0 Pitch MSB +9882 xxxx xxxx W Channel 1 Pitch LSB +9883 ---- xxxx W Channel 1 Pitch MSB +9884 xxxx xxxx W Channel 2 Pitch LSB +9885 ---- xxxx W Channel 2 Pitch MSB +9886 xxxx xxxx W Channel 3 Pitch LSB +9887 ---- xxxx W Channel 3 Pitch MSB +9888 xxxx xxxx W Channel 4 Pitch LSB +9889 ---- xxxx W Channel 4 Pitch MSB + +9888-988e Volume + +988a ---- xxxx W Channel 0 Volume +988b ---- xxxx W Channel 1 Volume +988c ---- xxxx W Channel 2 Volume +988d ---- xxxx W Channel 3 Volume +988e ---- xxxx W Channel 4 Volume + +988f ---x ---- W Channel 4 Output enable/disable flag + ---- x--- W Channel 3 Output enable/disable flag + ---- -x-- W Channel 2 Output enable/disable flag + ---- --x- W Channel 1 Output enable/disable flag + ---- ---x W Channel 0 Output enable/disable flag + +9890-989f Mirror of 9880-988f + +98a0-98bf xxxx xxxx R Channel 4 Waveform + +98e0 x--- ---- W Waveform rotate flag for channel 4 + -x-- ---- W Waveform rotate flag for all channels + --x- ---- W Reset waveform position after pitch writes + ---- --x- W 8 bit frequency + ---- --0x W 4 bit frequency + +98e1-98ff Mirror of 98e0 + +9900-9fff Mirror of 9800-98ff +``` + +### K052539 + +- 4000-bfff MegaRAM Mapper + +``` +Address Bit R/W Description + 7654 3210 + +4000-5fff xxxx xxxx R/W Bank page 0 +c000-dfff xxxx xxxx R/W "" + +6000-7fff xxxx xxxx R/W Bank page 1 +e000-ffff xxxx xxxx R/W "" + +8000-9fff xxxx xxxx R/W Bank page 2 +0000-1fff xxxx xxxx R/W "" + +a000-bfff xxxx xxxx R/W Bank page 3 +2000-3fff xxxx xxxx R/W "" +``` + +- 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select + +``` +Address Bit R/W Description + 7654 3210 + +5000 xxxx xxxx W Bank select, Page 0 +5001-57ff Mirror of 5000 + +7000 xxxx xxxx W Bank select, Page 1 +7001-77ff Mirror of 7000 + +9000 xxxx xxxx W Bank select, Page 2 + --11 1111 W SCC Enable (SCC Compatible mode) +9001-97ff Mirror of 9000 + +b000 xxxx xxxx W Bank select, Page 3 + 1--- ---- W SCC+ Enable (SCC+ mode) +b001-b7ff Mirror of b000 +``` + +- bffe-bfff Mapper configuration + +``` +Address Bit R/W Description + 7654 3210 + +bffe --x- ---- W SCC operation mode + --0- ---- W SCC Compatible mode + --1- ---- W SCC+ mode + ---x ---- W RAM write/Bank select toggle for all Bank pages + ---0 ---- W Bank select enable + ---1 ---- W RAM write enable + ---0 -x-- W RAM write/Bank select toggle for Bank page 2 + ---0 --x- W RAM write/Bank select toggle for Bank page 1 + ---0 ---x W RAM write/Bank select toggle for Bank page 0 + +bfff Mirror of bffe +``` + +- 9800-9fff SCC Compatible mode register + +``` +9800-987f Waveform + +Address Bit R/W Description + 7654 3210 + +9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) +9820-983f xxxx xxxx R/W Channel 1 "" +9840-985f xxxx xxxx R/W Channel 2 "" +9860-987f xxxx xxxx R/W Channel 3/4 "" + +9880-9889 Pitch + +9880 xxxx xxxx W Channel 0 Pitch LSB +9881 ---- xxxx W Channel 0 Pitch MSB +9882 xxxx xxxx W Channel 1 Pitch LSB +9883 ---- xxxx W Channel 1 Pitch MSB +9884 xxxx xxxx W Channel 2 Pitch LSB +9885 ---- xxxx W Channel 2 Pitch MSB +9886 xxxx xxxx W Channel 3 Pitch LSB +9887 ---- xxxx W Channel 3 Pitch MSB +9888 xxxx xxxx W Channel 4 Pitch LSB +9889 ---- xxxx W Channel 4 Pitch MSB + +9888-988e Volume + +988a ---- xxxx W Channel 0 Volume +988b ---- xxxx W Channel 1 Volume +988c ---- xxxx W Channel 2 Volume +988d ---- xxxx W Channel 3 Volume +988e ---- xxxx W Channel 4 Volume + +988f ---x ---- W Channel 4 Output enable/disable flag + ---- x--- W Channel 3 Output enable/disable flag + ---- -x-- W Channel 2 Output enable/disable flag + ---- --x- W Channel 1 Output enable/disable flag + ---- ---x W Channel 0 Output enable/disable flag + +9890-989f Mirror of 9880-988f + +98a0-98bf xxxx xxxx R Channel 4 Waveform + +98c0 -x-- ---- W Waveform rotate flag for all channels + --x- ---- W Reset waveform position after pitch writes + ---- --x- W 8 bit frequency + ---- --0x W 4 bit frequency + +98c1-98df Mirror of 98c0 + + 9900-9fff Mirror of 9800-98ff +``` + +- b800-bfff SCC+ mode register + +``` +b800-b89f Waveform + +Address Bit R/W Description + 7654 3210 + +b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) +b820-b83f xxxx xxxx R/W Channel 1 "" +b840-b85f xxxx xxxx R/W Channel 2 "" +b860-b87f xxxx xxxx R/W Channel 3 "" +b880-b89f xxxx xxxx R/W Channel 3 "" + +b8a0-b8a9 Pitch + +b8a0 xxxx xxxx W Channel 0 Pitch LSB +b8a1 ---- xxxx W Channel 0 Pitch MSB +b8a2 xxxx xxxx W Channel 1 Pitch LSB +b8a3 ---- xxxx W Channel 1 Pitch MSB +b8a4 xxxx xxxx W Channel 2 Pitch LSB +b8a5 ---- xxxx W Channel 2 Pitch MSB +b8a6 xxxx xxxx W Channel 3 Pitch LSB +b8a7 ---- xxxx W Channel 3 Pitch MSB +b8a8 xxxx xxxx W Channel 4 Pitch LSB +b8a9 ---- xxxx W Channel 4 Pitch MSB + +b8a8-b8ae Volume + +b8aa ---- xxxx W Channel 0 Volume +b8ab ---- xxxx W Channel 1 Volume +b8ac ---- xxxx W Channel 2 Volume +b8ad ---- xxxx W Channel 3 Volume +b8ae ---- xxxx W Channel 4 Volume + +b8af ---x ---- W Channel 4 Output enable/disable flag + ---- x--- W Channel 3 Output enable/disable flag + ---- -x-- W Channel 2 Output enable/disable flag + ---- --x- W Channel 1 Output enable/disable flag + ---- ---x W Channel 0 Output enable/disable flag + +b8b0-b8bf Mirror of b8a0-b8af + +b8c0 -x-- ---- W Waveform rotate flag for all channels + --x- ---- W Reset waveform position after pitch writes + ---- --x- W 8 bit frequency + ---- --0x W 4 bit frequency + +b8c1-b8df Mirror of b8c0 + +b900-bfff Mirror of b800-b8ff +``` + +## Frequency calculation + +``` + if 8 bit frequency then + Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1) + else if 4 bit frequency then + Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1) + else + Frequency = Input clock / (Pitch input + 1) +``` + +## Reference + + + + + + diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp b/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp new file mode 100644 index 00000000..07cbb60e --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp @@ -0,0 +1,461 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst + Konami SCC emulation core +*/ + +#include "scc.hpp" + +// shared SCC features +void scc_core::tick() +{ + m_out = 0; + for (auto &elem : m_voice) + { + elem.tick(); + m_out += elem.out(); + } +} + +void scc_core::voice_t::tick() +{ + if (m_pitch >= 9) // or voice is halted + { + // update counter - Post decrement + const u16 temp = m_counter; + if (m_host.m_test.freq_4bit()) // 4 bit frequency mode + { + m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); + m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); + } + else + { + m_counter = bitfield(m_counter - 1, 0, 12); + } + + // handle counter carry + const bool carry = m_host.m_test.freq_8bit() + ? (bitfield(temp, 0, 8) == 0) + : (m_host.m_test.freq_4bit() ? (bitfield(temp, 8, 4) == 0) + : (bitfield(temp, 0, 12) == 0)); + if (carry) + { + m_addr = bitfield(m_addr + 1, 0, 5); + m_counter = m_pitch; + } + } + // get output + if (m_enable) + { + m_out = (m_wave[m_addr] * m_volume) >> 4; // scale to 11 bit digital output + } + else + { + m_out = 0; + } +} + +void scc_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_test.reset(); + m_out = 0; + std::fill(m_reg.begin(), m_reg.end(), 0); +} + +void scc_core::voice_t::reset() +{ + std::fill(m_wave.begin(), m_wave.end(), 0); + m_enable = false; + m_pitch = 0; + m_volume = 0; + m_addr = 0; + m_counter = 0; + m_out = 0; +} + +// SCC accessors +u8 scc_core::wave_r(bool is_sccplus, u8 address) +{ + u8 ret = 0xff; + const u8 voice = bitfield(address, 5, 3); + if (voice > 4) + { + return ret; + } + + u8 wave_addr = bitfield(address, 0, 5); + + if (m_test.rotate()) + { // rotate flag + wave_addr = bitfield(wave_addr + m_voice[voice].addr(), 0, 5); + } + + if (!is_sccplus) + { + if (voice == 3) // rotate voice 3~4 flag + { + if (m_test.rotate4() || m_test.rotate()) + { // rotate flag + wave_addr = + bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate()].addr(), 0, 5); + } + } + } + ret = m_voice[voice].wave(wave_addr); + + return ret; +} + +void scc_core::wave_w(bool is_sccplus, u8 address, u8 data) +{ + if (m_test.rotate()) + { // write protected + return; + } + + const u8 voice = bitfield(address, 5, 3); + if (voice > 4) + { + return; + } + + const u8 wave_addr = bitfield(address, 0, 5); + + if (!is_sccplus) + { + if (((voice >= 3) && m_test.rotate4()) || (voice >= 4)) + { // Ignore if write protected, or voice 4 + return; + } + if (voice >= 3) // voice 3, 4 shares waveform + { + m_voice[3].set_wave(wave_addr, data); + m_voice[4].set_wave(wave_addr, data); + } + else + { + m_voice[voice].set_wave(wave_addr, data); + } + } + else + { + m_voice[voice].set_wave(wave_addr, data); + } +} + +void scc_core::freq_vol_enable_w(u8 address, u8 data) +{ + // *0-*f Pitch, Volume, Enable + address = bitfield(address, 0, 4); // mask address to 4 bit + const u8 voice_freq = bitfield(address, 1, 3); + switch (address) + { + case 0x0: // 0x*0 Voice 0 Pitch LSB + case 0x2: // 0x*2 Voice 1 Pitch LSB + case 0x4: // 0x*4 Voice 2 Pitch LSB + case 0x6: // 0x*6 Voice 3 Pitch LSB + case 0x8: // 0x*8 Voice 4 Pitch LSB + if (m_test.resetpos()) + { // Reset address + m_voice[voice_freq].reset_addr(); + } + m_voice[voice_freq].set_pitch(data, 0x0ff); + break; + case 0x1: // 0x*1 Voice 0 Pitch MSB + case 0x3: // 0x*3 Voice 1 Pitch MSB + case 0x5: // 0x*5 Voice 2 Pitch MSB + case 0x7: // 0x*7 Voice 3 Pitch MSB + case 0x9: // 0x*9 Voice 4 Pitch MSB + if (m_test.resetpos()) + { // Reset address + m_voice[voice_freq].reset_addr(); + } + m_voice[voice_freq].set_pitch(u16(bitfield(data, 0, 4)) << 8, 0xf00); + break; + case 0xa: // 0x*a Voice 0 Volume + case 0xb: // 0x*b Voice 1 Volume + case 0xc: // 0x*c Voice 2 Volume + case 0xd: // 0x*d Voice 3 Volume + case 0xe: // 0x*e Voice 4 Volume + m_voice[address - 0xa].set_volume(bitfield(data, 0, 4)); + break; + case 0xf: // 0x*f Enable/Disable flag + m_voice[0].set_enable(bitfield(data, 0)); + m_voice[1].set_enable(bitfield(data, 1)); + m_voice[2].set_enable(bitfield(data, 2)); + m_voice[3].set_enable(bitfield(data, 3)); + m_voice[4].set_enable(bitfield(data, 4)); + break; + } +} + +void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) +{ + const u8 voice = bitfield(address, 5, 3); + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3/4 Waveform + wave_w(false, address, data); + break; + case 0b100: // 0x80-0x9f Pitch, Volume, Enable + freq_vol_enable_w(address, data); + break; + case 0b111: // 0xe0-0xff Test register + m_test.set_freq_4bit(bitfield(data, 0)); + m_test.set_freq_8bit(bitfield(data, 1)); + m_test.set_resetpos(bitfield(data, 5)); + m_test.set_rotate(bitfield(data, 6)); + m_test.set_rotate4(bitfield(data, 7)); + break; + } + m_reg[address] = data; +} + +void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) +{ + const u8 voice = bitfield(address, 5, 3); + if (is_sccplus) + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b100: // 0x80-0x9f Voice 4 Waveform + wave_w(true, address, data); + break; + case 0b101: // 0xa0-0xbf Pitch, Volume, Enable + freq_vol_enable_w(address, data); + break; + case 0b110: // 0xc0-0xdf Test register + m_test.set_freq_4bit(bitfield(data, 0)); + m_test.set_freq_8bit(bitfield(data, 1)); + m_test.set_resetpos(bitfield(data, 5)); + m_test.set_rotate(bitfield(data, 6)); + break; + default: break; + } + } + else + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3/4 Waveform + wave_w(false, address, data); + break; + case 0b100: // 0x80-0x9f Pitch, Volume, Enable + freq_vol_enable_w(address, data); + break; + case 0b110: // 0xc0-0xdf Test register + m_test.set_freq_4bit(bitfield(data, 0)); + m_test.set_freq_8bit(bitfield(data, 1)); + m_test.set_resetpos(bitfield(data, 5)); + m_test.set_rotate(bitfield(data, 6)); + break; + default: break; + } + } + m_reg[address] = data; +} + +u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address) +{ + const u8 voice = bitfield(address, 5, 3); + const u8 wave = bitfield(address, 0, 5); + u8 ret = 0xff; + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b101: // 0xa0-0xbf Voice 4 Waveform + ret = wave_r(false, (std::min(4, voice) << 5) | wave); + break; + } + return ret; +} + +u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address) +{ + const u8 voice = bitfield(address, 5, 3); + const u8 wave = bitfield(address, 0, 5); + u8 ret = 0xff; + if (is_sccplus) + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b100: // 0x80-0x9f Voice 4 Waveform + ret = wave_r(true, address); + break; + } + } + else + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b101: // 0xa0-0xbf Voice 4 Waveform + ret = wave_r(false, (std::min(4, voice) << 5) | wave); + break; + } + } + return ret; +} + +// Mapper +void k051649_core::reset() +{ + k051649_scc_core::reset(); + m_mapper.reset(); + m_scc_enable = false; +} + +void k052539_core::reset() +{ + k052539_scc_core::reset(); + m_mapper.reset(); + m_scc_enable = false; + m_is_sccplus = false; +} + +void k051649_core::k051649_mapper_t::reset() +{ + m_bank[0] = 0; + m_bank[1] = 1; + m_bank[2] = 2; + m_bank[3] = 3; +} + +void k052539_core::k052539_mapper_t::reset() +{ + m_bank[0] = 0; + m_bank[1] = 1; + m_bank[2] = 2; + m_bank[3] = 3; + std::fill(m_ram_enable.begin(), m_ram_enable.end(), false); +} + +// Mapper accessors +u8 k051649_core::read(u16 address) +{ + if ((bitfield(address, 11, 5) == 0b10011) && m_scc_enable) + { + return scc_r(false, u8(address)); + } + + return m_intf.read_byte((u32(m_mapper.bank(bitfield(address, 13, 2) ^ 2)) << 13) | + bitfield(address, 0, 13)); +} + +u8 k052539_core::read(u16 address) +{ + if ((bitfield(address, 11, 5) == 0b10011) && m_scc_enable && (!m_is_sccplus)) + { + return scc_r(false, u8(address)); + } + + if ((bitfield(address, 11, 5) == 0b10111) && m_scc_enable && m_is_sccplus) + { + return scc_r(true, u8(address)); + } + + return m_intf.read_byte((u32(m_mapper.bank(bitfield(address, 13, 2) ^ 2)) << 13) | + bitfield(address, 0, 13)); +} + +void k051649_core::write(u16 address, u8 data) +{ + const u16 bank = bitfield(address, 13, 2) ^ 2; + switch (bitfield(address, 11, 5)) + { + case 0b01010: // 0x5000-0x57ff Bank 0 + case 0b01110: // 0x7000-0x77ff Bank 1 + case 0b10010: // 0x9000-0x97ff Bank 2 + case 0b10110: // 0xb000-0xb7ff Bank 3 + m_mapper.set_bank(bank, data); + m_scc_enable = (bitfield(m_mapper.bank(2), 0, 6) == 0x3f); + break; + case 0b10011: // 0x9800-9fff SCC + if (m_scc_enable) + { + scc_w(false, u8(address), data); + } + break; + } +} + +void k052539_core::write(u16 address, u8 data) +{ + u8 prev = 0; + bool update = false; + const u16 bank = bitfield(address, 13, 2) ^ 2; + const bool ram_enable = m_mapper.ram_enable(bank); + if (ram_enable) + { + m_intf.write_byte((u32(m_mapper.bank(bank)) << 13) | bitfield(address, 0, 13), data); + } + switch (bitfield(address, 11, 5)) + { + case 0b01010: // 0x5000-0x57ff Bank 0 + case 0b01110: // 0x7000-0x77ff Bank 1 + case 0b10010: // 0x9000-0x97ff Bank 2 + case 0b10110: // 0xb000-0xb7ff Bank 3 + if (!ram_enable) + { + prev = m_mapper.bank(bank); + m_mapper.set_bank(bank, data); + update = prev ^ m_mapper.bank(bank); + } + break; + case 0b10011: // 0x9800-0x9fff SCC + if ((!ram_enable) && m_scc_enable && (!m_is_sccplus)) + { + scc_w(false, u8(address), data); + } + break; + case 0b10111: // 0xb800-0xbfff SCC+, Mapper configuration + if (bitfield(address, 1, 10) == 0x3ff) + { + m_mapper.set_ram_enable(0, bitfield(data, 4) || bitfield(data, 0)); + m_mapper.set_ram_enable(1, bitfield(data, 4) || bitfield(data, 1)); + m_mapper.set_ram_enable(2, bitfield(data, 4) || bitfield(data, 2)); + m_mapper.set_ram_enable(3, bitfield(data, 4)); + prev = (m_is_sccplus ? 1 : 0); + m_is_sccplus = bitfield(data, 5); + update = prev ^ (m_is_sccplus ? 1 : 0); + } + else if ((!ram_enable) && m_scc_enable && m_is_sccplus) + { + scc_w(true, u8(address), data); + } + break; + } + if (update) + { + m_scc_enable = + m_is_sccplus ? bitfield(m_mapper.bank(3), 7) : (bitfield(m_mapper.bank(2), 0, 6) == 0x3f); + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp b/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp new file mode 100644 index 00000000..8824d253 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp @@ -0,0 +1,320 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst + Konami SCC emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_SCC_HPP +#define _VGSOUND_EMU_SRC_SCC_HPP + +#pragma once + +#include "../core/util.hpp" + +// shared for SCCs +class scc_core : public vgsound_emu_core +{ + private: + // classes + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(scc_core &host) + : vgsound_emu_core("scc_voice") + , m_host(host) + , m_wave{0} + , m_enable(false) + , m_pitch(0) + , m_volume(0) + , m_addr(0) + , m_counter(0) + , m_out(0) + { + } + + // internal state + void reset(); + void tick(); + + // accessors + inline void reset_addr() { m_addr = 0; } + + // setters + inline void set_wave(u8 addr, s8 wave) { m_wave[addr & 0x1f] = wave; } + + inline void set_enable(bool enable) { m_enable = enable; } + + inline void set_pitch(u16 pitch, u16 mask = 0xfff) + { + m_pitch = (m_pitch & ~(mask & 0xfff)) | (pitch & (mask & 0xfff)); + m_counter = m_pitch; + } + + inline void set_volume(u8 volume) { m_volume = volume & 0xf; } + + // getters + inline s8 wave(u8 addr) { return m_wave[addr & 0x1f]; } + + inline u8 addr() { return m_addr; } + + inline s32 out() { return m_out; } + + private: + // registers + scc_core &m_host; + std::array m_wave = {0}; // internal waveform + bool m_enable = false; // output enable flag + u16 m_pitch : 12; // pitch + u16 m_volume : 4; // volume + u8 m_addr = 0; // waveform pointer + u16 m_counter = 0; // frequency counter + s32 m_out = 0; // current output + }; + + class test_t : public vgsound_emu_core + { + public: + // constructor + test_t() + : vgsound_emu_core("scc_test") + , m_freq_4bit(0) + , m_freq_8bit(0) + , m_resetpos(0) + , m_rotate(0) + , m_rotate4(0) + { + } + + void reset() + { + m_freq_4bit = 0; + m_freq_8bit = 0; + m_resetpos = 0; + m_rotate = 0; + m_rotate4 = 0; + } + + // setters + inline void set_freq_4bit(bool freq_4bit) { m_freq_4bit = freq_4bit; } + + inline void set_freq_8bit(bool freq_8bit) { m_freq_8bit = freq_8bit; } + + inline void set_resetpos(bool resetpos) { m_resetpos = resetpos; } + + inline void set_rotate(bool rotate) { m_rotate = rotate; } + + inline void set_rotate4(bool rotate4) { m_rotate4 = rotate4; } + + // getters + inline bool freq_4bit() { return m_freq_4bit; } + + inline bool freq_8bit() { return m_freq_8bit; } + + inline bool resetpos() { return m_resetpos; } + + inline bool rotate() { return m_rotate; } + + inline bool rotate4() { return m_rotate4; } + + private: + u8 m_freq_4bit : 1; // 4 bit frequency + u8 m_freq_8bit : 1; // 8 bit frequency + u8 m_resetpos : 1; // reset counter after pitch writes + u8 m_rotate : 1; // rotate and write protect waveform for all channels + u8 m_rotate4 : 1; // same as above but for channel 4 only + }; + + public: + // constructor + scc_core(std::string tag) + : vgsound_emu_core(tag) + , m_voice{*this, *this, *this, *this, *this} + , m_test(test_t()) + , m_out(0) + , m_reg{0} + { + } + + // destructor + virtual ~scc_core() {} + + // accessors + virtual u8 scc_r(bool is_sccplus, u8 address) = 0; + virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0; + + // internal state + virtual void reset(); + void tick(); + + // getters + inline s32 out() { return m_out; } // output to DA0...DA10 pin + + inline u8 reg(u8 address) { return m_reg[address]; } + + // for preview + inline s32 voice_out(u8 voice) { return (voice < 5) ? m_voice[voice].out() : 0; } + + protected: + // accessor + u8 wave_r(bool is_sccplus, u8 address); + void wave_w(bool is_sccplus, u8 address, u8 data); + void freq_vol_enable_w(u8 address, u8 data); + + // internal values + std::array m_voice; // 5 voices + + test_t m_test; // test register + s32 m_out = 0; // output to DA0...10 + + std::array m_reg = {0}; // register pool +}; + +// SCC core +class k051649_scc_core : public scc_core +{ + public: + // constructor + k051649_scc_core(std::string tag = "k051649_scc") + : scc_core(tag) + { + } + + // accessors + virtual u8 scc_r(bool is_sccplus, u8 address) override; + virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; +}; + +class k052539_scc_core : public k051649_scc_core +{ + public: + // constructor + k052539_scc_core(std::string tag = "k052539_scc") + : k051649_scc_core(tag) + { + } + + // accessors + virtual u8 scc_r(bool is_sccplus, u8 address) override; + virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; +}; + +// MegaROM Mapper with SCC +class k051649_core : public k051649_scc_core +{ + friend class vgsound_emu_mem_intf; // for megaROM mapper + + private: + // mapper classes + class k051649_mapper_t : public vgsound_emu_core + { + public: + k051649_mapper_t() + : vgsound_emu_core("k051649_mapper") + , m_bank{0, 1, 2, 3} + { + } + + // internal state + void reset(); + + // setters + inline void set_bank(u8 slot, u8 bank) { m_bank[slot & 3] = bank; } + + // getters + inline u8 bank(u8 slot) { return m_bank[slot & 3]; } + + private: + // registers + u8 m_bank[4] = {0, 1, 2, 3}; + }; + + public: + // constructor + k051649_core(vgsound_emu_mem_intf &intf) + : k051649_scc_core("k051649") + , m_intf(intf) + , m_mapper(k051649_mapper_t()) + , m_scc_enable(false) + { + } + + // accessors + u8 read(u16 address); + void write(u16 address, u8 data); + + virtual void reset() override; + + private: + vgsound_emu_mem_intf m_intf; + k051649_mapper_t m_mapper; + bool m_scc_enable = false; +}; + +// MegaRAM Mapper with SCC +class k052539_core : public k052539_scc_core +{ + friend class vgsound_emu_mem_intf; // for megaRAM mapper + + private: + // mapper classes + class k052539_mapper_t : public vgsound_emu_core + { + public: + k052539_mapper_t() + : vgsound_emu_core("k052539_mapper") + , m_bank{0, 1, 2, 3} + , m_ram_enable{false} + { + } + + // internal state + void reset(); + + // setters + inline void set_bank(u8 slot, u8 bank) { m_bank[slot & 3] = bank; } + + inline void set_ram_enable(u8 slot, bool ram_enable) + { + m_ram_enable[slot & 3] = ram_enable; + } + + // getters + inline u8 bank(u8 slot) { return m_bank[slot & 3]; } + + inline bool ram_enable(u8 slot) { return m_ram_enable[slot & 3]; } + + private: + // registers + std::array m_bank = {0, 1, 2, 3}; + std::array m_ram_enable = {false}; + }; + + public: + // constructor + k052539_core(vgsound_emu_mem_intf &intf) + : k052539_scc_core("k052539") + , m_intf(intf) + , m_mapper(k052539_mapper_t()) + , m_scc_enable(false) + , m_is_sccplus(false) + { + } + + // accessors + u8 read(u16 address); + void write(u16 address, u8 data); + + virtual void reset() override; + + private: + vgsound_emu_mem_intf m_intf; + k052539_mapper_t m_mapper; + bool m_scc_enable = false; + bool m_is_sccplus = false; +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.cpp b/vgsound_emu-modified/vgsound_emu/src/template/template.cpp new file mode 100644 index 00000000..d927860a --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/template/template.cpp @@ -0,0 +1,33 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): (Author name) + Template for sound emulation core +*/ + +#include "template.hpp" + +void template_core::tick() +{ + // tick per each clock +} + +void template_core::reset() +{ + // reset this chip + std::fill(m_array.begin(), m_array.end(), 0); // std::fill() for fill std::array, std::vector +} + +/* +template voice function +void template_core::voice_t::tick() +{ + // tick per each voice +} + +void template_core::voice_t::reset() +{ + // reset this voice +} +*/ diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.hpp b/vgsound_emu-modified/vgsound_emu/src/template/template.hpp new file mode 100644 index 00000000..ca8f91b5 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/template/template.hpp @@ -0,0 +1,88 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): (Author name) + Template for sound emulation core, also guideline +*/ + +#ifndef _VGSOUND_EMU_SRC_TEMPLATE_HPP // _VGSOUND_EMU_ABSOLUTE_PATH_OF_THIS_FILE +#define _VGSOUND_EMU_SRC_TEMPLATE_HPP + +#pragma once + +#include "../core/util.hpp" + +class template_core : public vgsound_emu_core +{ + friend class vgsound_emu_mem_intf; // common memory interface if exists + + private: // protected: if shares between inheritances + // place classes and local constants here if exists + + // template voice classes + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(template_core &host) + : vgsound_emu_core("your_voice_tag_here") + , m_host(host) + , m_something(0) + { + } + + // internal state + void reset(); + void tick(); + + // accessors, getters, setters + + // setters + void set_something(s32 something) { m_something = something; } + + // getters + s32 something() { return m_something; } + + private: + // registers + template_core &m_host; + s32 m_something = 0; // register + }; + + public: + // place constructor and destructor, getter and setter for local variables, + // off-chip interfaces, update routine here only if can't be local + + // constructor + template_core(vgsound_emu_mem_intf &intf) + : vgsound_emu_core("your_core_tag_here") + // initialize all variables in constructor, because constructor is also executable + // anywhere, and it works as initializer. + , m_voice{*this} + , m_intf(intf) + , m_array{0} + , m_vector{0} + { + } + + // accessors, getters, setters + + // internal state + void reset(); + void tick(); + + protected: + // place local variables and functions here if shares between inheritances + + private: + // place local variables and functions here + + std::array m_voice; // voice classes + vgsound_emu_mem_intf &m_intf; // common memory interface + std::array m_array = { + 0}; // std::array for static size array + std::vector m_vector = {0}; // std::vector for variable size array +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md b/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md new file mode 100644 index 00000000..5b2db051 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md @@ -0,0 +1,97 @@ + +# Konami VRC VI + +## Summary + +- 2 voice pulse wave + - 8 level duty or volume only mode +- 1 voice sawtooth wave +- Internal mapper and timer + +## Source code + +- vrcvi.hpp: Base header + - vrcvi.cpp: Source emulation core + +## Description + +It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.) + +It's also DACless like other sound chip and mapper-with-sound manufactured by konami, the Chips 6 bit digital sound output is needs converted to analog sound output when you it want to make some sounds, or send to sound mixer. + +Its are used for Akumajou Densetsu (Japan release of Castlevania III), Madara, Esper Dream 2. + +The chip is installed in 351951 PCB and 351949A PCB. + +351951 PCB is used exclusivly for Akumajou Densetsu, Small board has VRC VI, PRG and CHR ROM. + +- It's configuration also calls VRC6a, iNES mapper 024. + +351949A PCB is for Last 2 titles with VRC VI, Bigger board has VRC VI, PRG and CHR ROM, and Battery Backed 8K x 8 bit SRAM. + +- Additionally, It's PRG A0 and A1 bit to VRC VI input is swapped, compare to above. +- It's configuration also calls VRC6b, iNES mapper 026. + +The chip itself has 053328, 053329, 053330 Revision, but Its difference between revision is unknown. + +Like other mappers for NES, It has internal timer - Its timer can be sync with scanline like other Konami mapper in this era. + +## Register layout + +- Sound and Timer only; 351951 PCB case, 351949A swaps xxx1 and xxx2 + +``` +Address Bits Description + 7654 3210 + +9000-9002 Pulse 1 + +9000 x--- ---- Pulse 1 Duty ignore + -xxx ---- Pulse 1 Duty cycle + ---- xxxx Pulse 1 Volume +9001 xxxx xxxx Pulse 1 Pitch bit 0-7 +9002 x--- ---- Pulse 1 Enable + ---- xxxx Pulse 1 Pitch bit 8-11 + +9003 Sound control + +9003 ---- -x-- 4 bit Frequency mode + ---- -0x- 8 bit Frequency mode + ---- ---x Halt + +a000-a002 Pulse 2 + +a000 x--- ---- Pulse 2 Duty ignore + -xxx ---- Pulse 2 Duty cycle + ---- xxxx Pulse 2 Volume +a001 xxxx xxxx Pulse 2 Pitch bit 0-7 +a002 x--- ---- Pulse 2 Enable + ---- xxxx Pulse 2 Pitch bit 8-11 + +b000-b002 Sawtooth + +b000 --xx xxxx Sawtooth Accumulate Rate +b001 xxxx xxxx Sawtooth Pitch bit 0-7 +b002 x--- ---- Sawtooth Enable + ---- xxxx Sawtooth Pitch bit 8-11 + +f000-f002 IRQ Timer + +f000 xxxx xxxx IRQ Timer latch +f001 ---- -0-- Sync with scanline + ---- --x- Enable timer + ---- ---x Enable timer after IRQ Acknowledge +f002 ---- ---- IRQ Acknowledge +``` + +# Frequency calculation + +``` + if 4 bit Frequency Mode then + Frequency: Input clock / (bit 8 to 11 of Pitch + 1) + end else if 8 bit Frequency Mode then + Frequency: Input clock / (bit 4 to 11 of Pitch + 1) + end else then + Frequency: Input clock / (Pitch + 1) + end +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp new file mode 100644 index 00000000..dc59120f --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp @@ -0,0 +1,260 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami VRC VI sound emulation core +*/ + +#include "vrcvi.hpp" + +void vrcvi_core::tick() +{ + m_out = 0; + if (!m_control.halt()) // Halt flag + { + // tick per each clock + for (auto &elem : m_pulse) + { + m_out += elem.get_output(); // add 4 bit pulse output + } + m_out += m_sawtooth.get_output(); // add 5 bit sawtooth output + } + if (m_timer.tick()) + { + m_timer.counter_tick(); + } +} + +void vrcvi_core::reset() +{ + for (auto &elem : m_pulse) + { + elem.reset(); + } + + m_sawtooth.reset(); + m_timer.reset(); + m_control.reset(); + m_out = 0; +} + +bool vrcvi_core::alu_t::tick() +{ + if (m_divider.enable()) + { + const u16 temp = m_counter; + // post decrement + if (bitfield(m_host.m_control.shift(), 1)) + { + m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); + m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); + } + else if (bitfield(m_host.m_control.shift(), 0)) + { + m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4); + m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0); + } + else + { + m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12); + } + + // carry handling + bool carry = bitfield(m_host.m_control.shift(), 1) + ? (bitfield(temp, 8, 4) == 0) + : (bitfield(m_host.m_control.shift(), 0) ? (bitfield(temp, 4, 8) == 0) + : (bitfield(temp, 0, 12) == 0)); + if (carry) + { + m_counter = m_divider.divider(); + } + + return carry; + } + return false; +} + +bool vrcvi_core::pulse_t::tick() +{ + if (!m_divider.enable()) + { + return false; + } + + if (vrcvi_core::alu_t::tick()) + { + m_cycle = bitfield(m_cycle + 1, 0, 4); + } + + return m_control.mode() ? true : ((m_cycle > m_control.duty()) ? true : false); +} + +bool vrcvi_core::sawtooth_t::tick() +{ + if (!m_divider.enable()) + { + return false; + } + + if (vrcvi_core::alu_t::tick()) + { + if (bitfield(m_cycle++, 0)) + { // Even step only + m_accum += m_rate; + } + if (m_cycle >= 14) // Reset accumulator at every 14 cycles + { + m_accum = 0; + m_cycle = 0; + } + } + return (m_accum == 0) ? false : true; +} + +s8 vrcvi_core::pulse_t::get_output() +{ + // add 4 bit pulse output + m_out = tick() ? m_control.volume() : 0; + return m_out; +} + +s8 vrcvi_core::sawtooth_t::get_output() +{ + // add 5 bit sawtooth output + m_out = tick() ? bitfield(m_accum, 3, 5) : 0; + return m_out; +} + +void vrcvi_core::alu_t::reset() +{ + m_divider.reset(); + m_counter = 0; + m_cycle = 0; + m_out = 0; +} + +void vrcvi_core::pulse_t::reset() +{ + vrcvi_core::alu_t::reset(); + m_control.reset(); +} + +void vrcvi_core::sawtooth_t::reset() +{ + vrcvi_core::alu_t::reset(); + m_rate = 0; + m_accum = 0; +} + +bool vrcvi_core::timer_t::tick() +{ + if (m_timer_control.enable()) + { + if (!m_timer_control.sync()) // scanline sync mode + { + m_prescaler -= 3; + if (m_prescaler <= 0) + { + m_prescaler += 341; + return true; + } + } + } + return (m_timer_control.enable() && m_timer_control.sync()) ? true : false; +} + +void vrcvi_core::timer_t::counter_tick() +{ + if (bitfield(++m_counter, 0, 8) == 0) + { + m_counter = m_counter_latch; + irq_set(); + } +} + +void vrcvi_core::timer_t::reset() +{ + m_timer_control.reset(); + m_prescaler = 341; + m_counter = m_counter_latch = 0; + irq_clear(); +} + +// Accessors + +void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data) +{ + if (msb) + { + m_divider = (m_divider & ~0xf00) | (bitfield(data, 0, 4) << 8); + m_enable = bitfield(data, 7); + } + else + { + m_divider = (m_divider & ~0x0ff) | data; + } +} + +void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data) +{ + pulse_t &v = m_pulse[voice]; + switch (address) + { + case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2) + v.control().write(data); + break; + case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2) + v.divider().write(false, data); + break; + case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2) + v.divider().write(true, data); + if (!v.divider().enable()) + { // Reset duty cycle + v.clear_cycle(); + } + break; + } +} + +void vrcvi_core::saw_w(u8 address, u8 data) +{ + switch (address) + { + case 0x00: // Sawtooth Accumulate - 0xb000 + m_sawtooth.set_rate(bitfield(data, 0, 6)); + break; + case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth) + m_sawtooth.divider().write(false, data); + break; + case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth) + m_sawtooth.divider().write(true, data); + if (!m_sawtooth.divider().enable()) + { // Reset accumulator + m_sawtooth.clear_accum(); + } + break; + } +} + +void vrcvi_core::timer_w(u8 address, u8 data) +{ + switch (address) + { + case 0x00: // Timer latch - 0xf000 + m_timer.set_counter_latch(data); + break; + case 0x01: // Timer control - 0xf001/0xf002 + m_timer.timer_control_w(data); + break; + case 0x02: // IRQ Acknowledge - 0xf002/0xf001 + m_timer.irq_ack(); + break; + } +} + +void vrcvi_core::control_w(u8 data) +{ + // Global control - 0x9003 + m_control.write(data); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp new file mode 100644 index 00000000..f163114c --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp @@ -0,0 +1,407 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami VRC VI sound emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_VRCVI_HPP +#define _VGSOUND_EMU_SRC_VRCVI_HPP + +#pragma once + +#include "../core/util.hpp" + +class vrcvi_intf : public vgsound_emu_core +{ + public: + vrcvi_intf() + : vgsound_emu_core("vrc_vi_intf") + { + } + + virtual void irq_w(bool irq) {} +}; + +class vrcvi_core : public vgsound_emu_core +{ + friend class vrcvi_intf; + + private: + // Common ALU for sound channels + class alu_t : public vgsound_emu_core + { + private: + class divider_t : public vgsound_emu_core + { + public: + divider_t() + : vgsound_emu_core("vrc_vi_frequency_divider") + , m_divider(0) + , m_enable(0) + { + } + + void reset() + { + m_divider = 0; + m_enable = 0; + } + + void write(bool msb, u8 data); + + // getters + inline u16 divider() { return m_divider; } + + inline bool enable() { return m_enable; } + + private: + u16 m_divider : 12; // divider (pitch) + u16 m_enable : 1; // channel disable flag + }; + + public: + alu_t(std::string tag, vrcvi_core &host) + : vgsound_emu_core(tag) + , m_host(host) + , m_divider(divider_t()) + , m_counter(0) + , m_cycle(0) + , m_out(0) + { + } + + virtual void reset(); + virtual bool tick(); + + virtual s8 get_output() + { + m_out = 0; + return 0; + } + + // accessors + inline void clear_cycle() { m_cycle = 0; } + + // getters + divider_t ÷r() { return m_divider; } + + inline u16 counter() { return m_counter; } + + inline u8 cycle() { return m_cycle; } + + // for previwe/debug only + inline s8 out() { return m_out; } + + protected: + vrcvi_core &m_host; + divider_t m_divider; + u16 m_counter = 0; // clock counter + u8 m_cycle = 0; // clock cycle + s8 m_out = 0; // output per channel + }; + + // 2 Pulse channels + class pulse_t : public alu_t + { + private: + // Control bits + class pulse_control_t + { + public: + pulse_control_t() + : m_mode(0) + , m_duty(0) + , m_volume(0) + { + } + + void reset() + { + m_mode = 0; + m_duty = 0; + m_volume = 0; + } + + // accessors + inline void write(u8 data) + { + m_mode = (data >> 7) & 0x1; + m_duty = (data >> 4) & 0x7; + m_volume = (data >> 0) & 0xf; + } + + // getters + inline bool mode() { return m_mode; } + + inline u8 duty() { return m_duty; } + + inline u8 volume() { return m_volume; } + + private: + u8 m_mode : 1; // duty toggle flag + u8 m_duty : 3; // 3 bit duty cycle + u8 m_volume : 4; // 4 bit volume + }; + + public: + pulse_t(vrcvi_core &host) + : alu_t("vrc_vi_pulse", host) + , m_control(pulse_control_t()) + { + } + + virtual void reset() override; + virtual bool tick() override; + virtual s8 get_output() override; + + // getters + pulse_control_t &control() { return m_control; } + + private: + pulse_control_t m_control; + }; + + // 1 Sawtooth channel + class sawtooth_t : public alu_t + { + public: + sawtooth_t(vrcvi_core &host) + : alu_t("vrc_vi_sawtooth", host) + , m_rate(0) + , m_accum(0) + { + } + + virtual void reset() override; + virtual bool tick() override; + virtual s8 get_output() override; + + // accessors + inline void clear_accum() { m_accum = 0; } + + // setters + inline void set_rate(u8 rate) { m_rate = rate; } + + // getters + inline u8 rate() { return m_rate; } + + inline u8 accum() { return m_accum; } + + private: + u8 m_rate = 0; // sawtooth accumulate rate + u8 m_accum = 0; // sawtooth accumulator, high 5 bit is accumulated to output + }; + + // Internal timer + class timer_t : public vgsound_emu_core + { + private: + // Control bits + class timer_control_t : public vgsound_emu_core + { + public: + timer_control_t() + : vgsound_emu_core("vrc_vi_timer_control") + , m_irq_trigger(0) + , m_enable_ack(0) + , m_enable(0) + , m_sync(0) + { + } + + void reset() + { + m_irq_trigger = 0; + m_enable_ack = 0; + m_enable = 0; + m_sync = 0; + } + + // accessors + inline void irq_set(bool irq) { m_irq_trigger = irq ? 1 : 0; } + + // setters + inline void set_enable_ack(bool enable_ack) + { + m_enable_ack = enable_ack ? 1 : 0; + } + + inline void set_enable(bool enable) { m_enable = enable ? 1 : 0; } + + inline void set_sync(bool sync) { m_sync = sync ? 1 : 0; } + + // getters + inline bool irq_trigger() { return m_irq_trigger; } + + inline bool enable_ack() { return m_enable_ack; } + + inline bool enable() { return m_enable; } + + inline bool sync() { return m_sync; } + + private: + u8 m_irq_trigger : 1; + u8 m_enable_ack : 1; + u8 m_enable : 1; + u8 m_sync : 1; + }; + + public: + timer_t(vrcvi_core &host) + : vgsound_emu_core("vrc_vi_timer") + , m_host(host) + , m_timer_control(timer_control_t()) + , m_prescaler(341) + , m_counter(0) + , m_counter_latch(0) + { + } + + void reset(); + bool tick(); + void counter_tick(); + + // IRQ update + void update() { m_host.m_intf.irq_w(m_timer_control.irq_trigger()); } + + void irq_set() + { + if (!m_timer_control.irq_trigger()) + { + m_timer_control.irq_set(true); + update(); + } + } + + void irq_clear() + { + if (m_timer_control.irq_trigger()) + { + m_timer_control.irq_set(false); + update(); + } + } + + // accessors + void reset_counter() + { + m_counter = m_counter_latch; + m_prescaler = 341; + } + + void timer_control_w(u8 data) + { + m_timer_control.set_enable_ack((data >> 0) & 1); + m_timer_control.set_enable((data >> 1) & 1); + m_timer_control.set_sync((data >> 2) & 1); + if (m_timer_control.enable()) + { + reset_counter(); + } + irq_clear(); + } + + void irq_ack() + { + irq_clear(); + m_timer_control.set_enable(m_timer_control.enable_ack()); + } + + // setters + inline void set_counter_latch(u8 counter_latch) { m_counter_latch = counter_latch; } + + // getters + timer_control_t &timer_control() { return m_timer_control; } + + inline s16 prescaler() { return m_prescaler; } + + inline u8 counter() { return m_counter; } + + inline u8 counter_latch() { return m_counter_latch; } + + private: + vrcvi_core &m_host; // host core + timer_control_t m_timer_control; // timer control bits + s16 m_prescaler = 341; // prescaler + u8 m_counter = 0; // clock counter + u8 m_counter_latch = 0; // clock counter latch + }; + + class global_control_t : public vgsound_emu_core + { + public: + global_control_t() + : vgsound_emu_core("vrc_vi_global_control") + , m_halt(0) + , m_shift(0) + { + } + + void reset() + { + m_halt = 0; + m_shift = 0; + } + + // accessors + inline void write(u8 data) + { + m_halt = (data >> 0) & 1; + m_shift = (data >> 1) & 3; + } + + // getters + inline bool halt() { return m_halt; } + + inline u8 shift() { return m_shift; } + + private: + u8 m_halt : 1; // halt sound + u8 m_shift : 2; // 4/8 bit right shift + }; + + public: + // constructor + vrcvi_core(vrcvi_intf &intf) + : vgsound_emu_core("vrc_vi") + , m_intf(intf) + , m_pulse{*this, *this} + , m_sawtooth(*this) + , m_timer(*this) + , m_control(global_control_t()) + , m_out(0) + { + } + + // accessors, getters, setters + void pulse_w(u8 voice, u8 address, u8 data); + void saw_w(u8 address, u8 data); + void timer_w(u8 address, u8 data); + void control_w(u8 data); + + // internal state + void reset(); + void tick(); + + // 6 bit output + inline s8 out() { return m_out; } + + // for debug/preview only + inline s8 pulse_out(u8 pulse) { return (pulse < 2) ? m_pulse[pulse].out() : 0; } + + inline s8 sawtooth_out() { return m_sawtooth.out(); } + + private: + vrcvi_intf &m_intf; + + std::array m_pulse; // 2 pulse channels + sawtooth_t m_sawtooth; // sawtooth channel + timer_t m_timer; // internal timer + global_control_t m_control; // control + + s8 m_out = 0; // 6 bit output +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md b/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md new file mode 100644 index 00000000..656e5c60 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md @@ -0,0 +1,97 @@ + +# Seta/Allumer X1-010 + +## Summary + +- 16 voice wavetable or PCM + - 8 bit signed for both wavetable and PCM + - 128 width long waveform + - wavetable playback must be paired with envelope + - envelope shape is 4 bit stereo, 128 width long waveform + - waveform and envelope shape stored at each half area on RAM space + - total accessible memory for PCM: 1 MByte + +## Source code + +- x1_010.hpp: Base header + - x1_010.cpp: Source emulation core + +## Description + +the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode. It has also 2 output channels, but no known hardware using this feature for stereo sound. + +Wavetable needs to paired with envelope, it's always enabled and similar as AY PSG's one but its shape is stored at RAM. + +PCM volume is stored by each register. + +Both volume is 4bit per output. + +Everything except PCM sample is stored at paired 8 bit RAM. + +## RAM layout + +common case: Address bit 12 is swapped when RAM is shared with CPU + +### Voice registers (0000...007f) + +0000...0007 Voice #0 Register + +``` +Address Bits Description + 7654 3210 +0 x--- ---- Frequency divider* + ---- -x-- Envelope one-shot mode + ---- --x- Sound format + ---- --0- PCM + ---- --1- Wavetable + ---- ---x Keyon/off +PCM case: +1 xxxx xxxx Volume (Each nibble is for each output) + +2 xxxx xxxx Frequency + +4 xxxx xxxx Start address / 4096 + +5 xxxx xxxx 0x100 - (End address / 4096) +Wavetable case: +1 ---x xxxx Wavetable data select + +2 xxxx xxxx Frequency LSB +3 xxxx xxxx "" MSB + +4 xxxx xxxx Envelope period + +5 ---x xxxx Envelope shape select (!= 0 : Reserved for Voice registers) +``` + +0008...000f Voice #1 Register +... +0078...007f Voice #15 Register + +### Envelope shape data (0080...0fff) + +Same format as volume; Each nibble is for each output + +0080...00ff Envelope shape #1 data +0100...017f Envelope shape #2 data +... +0f80...0fff Envelope shape #31 data + +### Waveform data (1000...1fff) + +1000...107f Waveform #0 data +1080...10ff Waveform #1 data +... +1f80...1fff Waveform #31 data + +## Frequency calculation + +``` +Wavetable, Divider Clear: Frequency value * (Input clock / 524288) +Wavetable, Divider Set: Frequency value * (Input clock / 1048576) +PCM, Divider Clear: Frequency value * (Input clock / 8192) +PCM, Divider Set: Frequency value * (Input clock / 16384) +Envelope: Envelope period * (Input clock / 524288) - Frequency divider not affected? +``` + +Frequency divider is higher precision or just right shift? needs verification. diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp new file mode 100644 index 00000000..c249e554 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp @@ -0,0 +1,163 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Seta/Allumer X1-010 Emulation core +*/ + +#include "x1_010.hpp" + +void x1_010_core::tick() +{ + // reset output + m_out[0] = m_out[1] = 0; + for (voice_t &elem : m_voice) + { + elem.tick(); + m_out[0] += elem.out(0); + m_out[1] += elem.out(1); + } +} + +void x1_010_core::voice_t::tick() +{ + m_out[0] = m_out[1] = 0; + if (m_flag.keyon()) + { + if (m_flag.wavetable()) // Wavetable + { + // envelope, each nibble is for each output + u8 vol = + m_host.m_envelope[(bitfield(m_end_envshape, 0, 5) << 7) | bitfield(m_env_acc, 10, 7)]; + m_vol_out[0] = bitfield(vol, 4, 4); + m_vol_out[1] = bitfield(vol, 0, 4); + m_env_acc += m_start_envfreq; + if (m_flag.env_oneshot() && bitfield(m_env_acc, 17)) + { + m_flag.set_keyon(false); + } + else + { + m_env_acc = bitfield(m_env_acc, 0, 17); + } + // get wavetable data + m_data = m_host.m_wave[(bitfield(m_vol_wave, 0, 5) << 7) | bitfield(m_acc, 11, 7)]; + m_acc = bitfield(m_acc + (m_freq << (1 - m_flag.div())), 0, 18); + } + else // PCM sample + { + // volume register, each nibble is for each output + m_vol_out[0] = bitfield(m_vol_wave, 4, 4); + m_vol_out[1] = bitfield(m_vol_wave, 0, 4); + // get PCM sample + m_data = m_host.m_intf.read_byte(bitfield(m_acc, 5, 20)); + m_acc += u32(bitfield(m_freq, 0, 8)) << (1 - m_flag.div()); + if ((m_acc >> 17) > u32(0xff ^ m_end_envshape)) + { + m_flag.set_keyon(false); + } + } + m_out[0] = m_data * m_vol_out[0]; + m_out[1] = m_data * m_vol_out[1]; + } +} + +u8 x1_010_core::ram_r(u16 offset) +{ + if (offset & 0x1000) + { // wavetable data + return m_wave[offset & 0xfff]; + } + else if (offset & 0xf80) + { // envelope shape data + return m_envelope[offset & 0xfff]; + } + else + { // channel register + return m_voice[bitfield(offset, 3, 4)].reg_r(offset & 0x7); + } +} + +void x1_010_core::ram_w(u16 offset, u8 data) +{ + if (offset & 0x1000) + { // wavetable data + m_wave[offset & 0xfff] = data; + } + else if (offset & 0xf80) + { // envelope shape data + m_envelope[offset & 0xfff] = data; + } + else + { // channel register + m_voice[bitfield(offset, 3, 4)].reg_w(offset & 0x7, data); + } +} + +u8 x1_010_core::voice_t::reg_r(u8 offset) +{ + switch (offset & 0x7) + { + case 0x00: + return (m_flag.div() << 7) | (m_flag.env_oneshot() << 2) | (m_flag.wavetable() << 1) | + (m_flag.keyon() << 0); + case 0x01: return m_vol_wave; + case 0x02: return bitfield(m_freq, 0, 8); + case 0x03: return bitfield(m_freq, 8, 8); + case 0x04: return m_start_envfreq; + case 0x05: return m_end_envshape; + default: break; + } + return 0; +} + +void x1_010_core::voice_t::reg_w(u8 offset, u8 data) +{ + switch (offset & 0x7) + { + case 0x00: + { + const bool prev_keyon = m_flag.keyon(); + m_flag.write(data); + if (!prev_keyon && m_flag.keyon()) // Key on + { + m_acc = m_flag.wavetable() ? 0 : (u32(m_start_envfreq) << 16); + m_env_acc = 0; + } + break; + } + case 0x01: m_vol_wave = data; break; + case 0x02: m_freq = (m_freq & 0xff00) | data; break; + case 0x03: m_freq = (m_freq & 0x00ff) | (u16(data) << 8); break; + case 0x04: m_start_envfreq = data; break; + case 0x05: m_end_envshape = data; break; + default: break; + } +} + +void x1_010_core::voice_t::reset() +{ + m_flag.reset(); + m_vol_wave = 0; + m_freq = 0; + m_start_envfreq = 0; + m_end_envshape = 0; + m_acc = 0; + m_env_acc = 0; + m_data = 0; + m_vol_out.fill(0); + m_out.fill(0); +} + +void x1_010_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_envelope.fill(0); + m_wave.fill(0); + m_out.fill(0); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp new file mode 100644 index 00000000..34a4ab99 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp @@ -0,0 +1,179 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Seta/Allumer X1-010 Emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_X1_010_HPP +#define _VGSOUND_EMU_SRC_X1_010_HPP + +#pragma once + +#include "../core/util.hpp" + +class x1_010_core : public vgsound_emu_core +{ + friend class vgsound_emu_mem_intf; + + private: + // 16 voices in chip + class voice_t : public vgsound_emu_core + { + private: + class flag_t : public vgsound_emu_core + { + public: + flag_t() + : vgsound_emu_core("x1_010_voice_flag") + , m_div(0) + , m_env_oneshot(0) + , m_wavetable(0) + , m_keyon(0) + { + } + + // internal state + void reset() + { + m_div = 0; + m_env_oneshot = 0; + m_wavetable = 0; + m_keyon = 0; + } + + // register accessor + inline void write(u8 data) + { + m_div = (data >> 7) & 1; + m_env_oneshot = (data >> 2) & 1; + m_wavetable = (data >> 1) & 1; + m_keyon = (data >> 0) & 1; + } + + // Setters + inline void set_keyon(bool keyon) { m_keyon = keyon; } + + // Getters + inline bool div() { return m_div; } + + inline bool env_oneshot() { return m_env_oneshot; } + + inline bool wavetable() { return m_wavetable; } + + inline bool keyon() { return m_keyon; } + + private: + u8 m_div : 1; + u8 m_env_oneshot : 1; + u8 m_wavetable : 1; + u8 m_keyon : 1; + }; + + public: + // constructor + voice_t(x1_010_core &host) + : vgsound_emu_core("x1_010_voice") + , m_host(host) + , m_flag(flag_t()) + , m_vol_wave(0) + , m_freq(0) + , m_start_envfreq(0) + , m_end_envshape(0) + , m_acc(0) + , m_env_acc(0) + , m_data(0) + , m_vol_out{0} + , m_out{0} + { + } + + // internal state + void reset(); + void tick(); + + // register accessor + u8 reg_r(u8 offset); + void reg_w(u8 offset, u8 data); + + // getters + inline s32 out(u8 ch) { return m_out[ch & 1]; } + + private: + // host flag + x1_010_core &m_host; + // registers + flag_t m_flag; + u8 m_vol_wave = 0; + u16 m_freq = 0; + u8 m_start_envfreq = 0; + u8 m_end_envshape = 0; + + // internal registers + u32 m_acc = 0; + u32 m_env_acc = 0; + s8 m_data = 0; + std::array m_vol_out = {0}; + + // for preview only + std::array m_out = {0}; + }; + + public: + // constructor + x1_010_core(vgsound_emu_mem_intf &intf) + : vgsound_emu_core("x1_010") + , m_voice{*this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this} + , m_intf(intf) + , m_envelope{0} + , m_wave{0} + , m_out{0} + { + } + + // register accessor + u8 ram_r(u16 offset); + void ram_w(u16 offset, u8 data); + + // getters + inline s32 output(u8 ch) { return m_out[ch & 1]; } + + // internal state + void reset(); + void tick(); + + // for preview only + inline s32 voice_out(u8 voice, u8 ch) + { + return (voice < 16) ? m_voice[voice].out(ch & 1) : 0; + } + + private: + std::array m_voice; + vgsound_emu_mem_intf &m_intf; + + // RAM + std::array m_envelope = {0}; + std::array m_wave = {0}; + + // output data + std::array m_out = {0}; +}; + +#endif From 2582ec17ed082cb7ef1ff5655ab251fe93739565 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 00:15:04 +0900 Subject: [PATCH 03/16] Temporary workaround for accidently removed libs --- extern/vgsound_emu-modified | 1 - 1 file changed, 1 deletion(-) delete mode 160000 extern/vgsound_emu-modified diff --git a/extern/vgsound_emu-modified b/extern/vgsound_emu-modified deleted file mode 160000 index 7b988a67..00000000 --- a/extern/vgsound_emu-modified +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7b988a6714ebf61e8a5fad5c9ccbda2b85853fe1 From 5034b3623d8721722a3bb4832a2fc7205586d29d Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 00:16:20 +0900 Subject: [PATCH 04/16] Take 2 --- .../vgsound_emu-modified}/.clang-format | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/.gitignore | 0 .../vgsound_emu-modified}/CHANGELOG.md | 0 .../vgsound_emu-modified}/CMakeLists.txt | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/LICENSE | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/MODIFIED.md | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/core/util.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es550x_alu.cpp | 0 .../vgsound_emu/src/es550x/es550x_filter.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k005289/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k007232/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k053260/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/msm6295/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/n163/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/n163/n163.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/n163/n163.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/scc/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/scc/scc.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/scc/scc.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/template/template.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/template/template.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/vrcvi/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/x1_010/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.hpp | 0 47 files changed, 0 insertions(+), 0 deletions(-) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/.clang-format (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/.gitignore (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/CHANGELOG.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/CMakeLists.txt (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/LICENSE (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/MODIFIED.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/core/util.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x_alu.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x_filter.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k005289/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k007232/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k053260/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/msm6295/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/n163/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/n163/n163.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/n163/n163.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/scc/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/scc/scc.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/scc/scc.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/template/template.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/template/template.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/vrcvi/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/x1_010/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.hpp (100%) diff --git a/vgsound_emu-modified/.clang-format b/extern/vgsound_emu-modified/.clang-format similarity index 100% rename from vgsound_emu-modified/.clang-format rename to extern/vgsound_emu-modified/.clang-format diff --git a/vgsound_emu-modified/.gitignore b/extern/vgsound_emu-modified/.gitignore similarity index 100% rename from vgsound_emu-modified/.gitignore rename to extern/vgsound_emu-modified/.gitignore diff --git a/vgsound_emu-modified/CHANGELOG.md b/extern/vgsound_emu-modified/CHANGELOG.md similarity index 100% rename from vgsound_emu-modified/CHANGELOG.md rename to extern/vgsound_emu-modified/CHANGELOG.md diff --git a/vgsound_emu-modified/CMakeLists.txt b/extern/vgsound_emu-modified/CMakeLists.txt similarity index 100% rename from vgsound_emu-modified/CMakeLists.txt rename to extern/vgsound_emu-modified/CMakeLists.txt diff --git a/vgsound_emu-modified/LICENSE b/extern/vgsound_emu-modified/LICENSE similarity index 100% rename from vgsound_emu-modified/LICENSE rename to extern/vgsound_emu-modified/LICENSE diff --git a/vgsound_emu-modified/MODIFIED.md b/extern/vgsound_emu-modified/MODIFIED.md similarity index 100% rename from vgsound_emu-modified/MODIFIED.md rename to extern/vgsound_emu-modified/MODIFIED.md diff --git a/vgsound_emu-modified/README.md b/extern/vgsound_emu-modified/README.md similarity index 100% rename from vgsound_emu-modified/README.md rename to extern/vgsound_emu-modified/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/core/util.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/util.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/core/util.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/core/util.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k005289/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/k005289/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/k007232/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k007232/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/k007232/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k053260/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/k053260/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/msm6295/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/n163/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/n163/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/n163/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/scc/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/template/template.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/template/template.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/template/template.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/template/template.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/template/template.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/template/template.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/x1_010/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp From 74bf0171f0668e03a83a952897d0ef96a9d46b0c Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 12:36:36 +0900 Subject: [PATCH 05/16] Modifier disclaimer --- extern/vgsound_emu-modified/MODIFIED.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extern/vgsound_emu-modified/MODIFIED.md b/extern/vgsound_emu-modified/MODIFIED.md index 432d014f..00a7e727 100644 --- a/extern/vgsound_emu-modified/MODIFIED.md +++ b/extern/vgsound_emu-modified/MODIFIED.md @@ -5,3 +5,7 @@ this is a modified version of vgsound_emu emulation core library tailored for Fu it should not and shall NOT be mistaken for the original, authentic or actual version and revision of the library. you can get original software from [here](https://gitlab.com/cam900/vgsound_emu/). + +## Modifier + +- [cam900](https://gitlab.com/cam900) \ No newline at end of file From c9813192953959a6f821012cdf7df27c5b4a8e70 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 12:48:03 +0900 Subject: [PATCH 06/16] Sync with master --- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp | 2 +- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp index ef06c151..0eb97ab0 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp @@ -16,7 +16,7 @@ void vox_core::vox_decoder_t::decoder_state_t::reset() } // copy from source -void vox_core::vox_decoder_t::decoder_state_t::copy(decoder_state_t src) +void vox_core::vox_decoder_t::decoder_state_t::copy_state(decoder_state_t src) { m_index = src.index(); m_step = src.step(); diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp index f3ae6d4c..167d0fed 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -42,14 +42,14 @@ class vox_core : public vgsound_emu_core decoder_state_t &operator=(decoder_state_t src) { - copy(src); + copy_state(src); return *this; } private: const bool m_wraparound = false; // wraparound or clamp? - void copy(decoder_state_t src); + void copy_state(decoder_state_t src); vox_core &m_vox; s8 m_index = 0; From 4eaf5ce9a6a74c8f2a59ccfc4980f47a7ff74ea8 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 12:55:05 +0900 Subject: [PATCH 07/16] Sync with master --- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp | 2 +- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp index 0eb97ab0..ec16ca2b 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp @@ -16,7 +16,7 @@ void vox_core::vox_decoder_t::decoder_state_t::reset() } // copy from source -void vox_core::vox_decoder_t::decoder_state_t::copy_state(decoder_state_t src) +void vox_core::vox_decoder_t::decoder_state_t::copy_state(decoder_state_t &src) { m_index = src.index(); m_step = src.step(); diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp index 167d0fed..ead2ca75 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -40,7 +40,7 @@ class vox_core : public vgsound_emu_core s32 step() { return m_step; } - decoder_state_t &operator=(decoder_state_t src) + decoder_state_t &operator=(decoder_state_t &src) { copy_state(src); return *this; @@ -49,7 +49,7 @@ class vox_core : public vgsound_emu_core private: const bool m_wraparound = false; // wraparound or clamp? - void copy_state(decoder_state_t src); + void copy_state(decoder_state_t &src); vox_core &m_vox; s8 m_index = 0; From e9bdd356da4c9e7cc6742884cb9872b777f1c7f1 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:02:10 +0900 Subject: [PATCH 08/16] Sync with master --- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp | 4 ++-- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index 34782f10..4c8454c8 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -35,7 +35,7 @@ class es5505_core : public es550x_shared_core inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); } - inline void clamp16(output_t src) + inline void clamp16(output_t &src) { m_left = clamp16(src.left()); m_right = clamp16(src.right()); @@ -76,7 +76,7 @@ class es5505_core : public es550x_shared_core return *this; } - output_t &operator=(output_t src) + output_t &operator=(output_t &src) { clamp16(src); return *this; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index 84a6077d..53713cfb 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -35,7 +35,7 @@ class es5506_core : public es550x_shared_core inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } - inline void clamp20(output_t src) + inline void clamp20(output_t &src) { m_left = clamp20(src.left()); m_right = clamp20(src.right()); @@ -76,7 +76,7 @@ class es5506_core : public es550x_shared_core return *this; } - output_t &operator=(output_t src) + output_t &operator=(output_t &src) { clamp20(src); return *this; From 146da2ce7649fcea31f76502b4307461b1a70f23 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:05:58 +0900 Subject: [PATCH 09/16] Forgot to sync --- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp | 4 ++-- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index 4c8454c8..d1c70614 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -65,9 +65,9 @@ class es5505_core : public es550x_shared_core } // getters - inline u32 left() { return m_left; } + inline s32 left() { return m_left; } - inline u32 right() { return m_right; } + inline s32 right() { return m_right; } output_t &operator+=(output_t &src) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index 53713cfb..524e754e 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -65,9 +65,9 @@ class es5506_core : public es550x_shared_core } // getters - inline u32 left() { return m_left; } + inline s32 left() { return m_left; } - inline u32 right() { return m_right; } + inline s32 right() { return m_right; } output_t &operator+=(output_t &src) { From 5af02d068e62a589f83fd787a646a3746f4d9a40 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:33:21 +0900 Subject: [PATCH 10/16] Sync with master --- .../vgsound_emu/src/es550x/es5504.cpp | 5 ++++- .../vgsound_emu/src/es550x/es5505.cpp | 8 ++++---- .../vgsound_emu/src/es550x/es5505.hpp | 4 ++-- .../vgsound_emu/src/es550x/es5506.cpp | 8 ++++---- .../vgsound_emu/src/es550x/es5506.hpp | 4 ++-- .../vgsound_emu/src/k053260/k053260.cpp | 4 ++-- .../vgsound_emu/src/k053260/k053260.hpp | 2 +- 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp index 586f15f3..b31a13b2 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp @@ -357,7 +357,10 @@ void es5504_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) if (bitfield(m_adc, 0)) // Writam_ble ADC { m_adc = (m_adc & 7) | (data & ~7); - m_intf.adc_w(m_adc & ~7); + if (cpu_access) + { + m_intf.adc_w(m_adc & ~7); + } } m_adc = (m_adc & ~3) | (data & 3); break; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp index 07a7b704..6b271224 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp @@ -319,7 +319,7 @@ void es5505_core::host_w(u8 address, u16 data) m_hd = data; if (m_e.rising_edge()) { // update directly - write(m_ha, m_hd, true); + write(m_ha, m_hd); } else { @@ -330,9 +330,9 @@ void es5505_core::host_w(u8 address, u16 data) u16 es5505_core::read(u8 address, bool cpu_access) { return regs_r(m_page, address, cpu_access); } -void es5505_core::write(u8 address, u16 data, bool cpu_access) +void es5505_core::write(u8 address, u16 data) { - regs_w(m_page, address, data, cpu_access); + regs_w(m_page, address, data); } u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access) @@ -488,7 +488,7 @@ u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access) return ret; } -void es5505_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) +void es5505_core::regs_w(u8 page, u8 address, u16 data) { address = bitfield(address, 0, 4); // 4 bit address for CPU access diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index d1c70614..4cbd2581 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -262,10 +262,10 @@ class es5505_core : public es550x_shared_core // bypass chips host interface for debug purpose only u16 read(u8 address, bool cpu_access = false); - void write(u8 address, u16 data, bool cpu_access = false); + void write(u8 address, u16 data); u16 regs_r(u8 page, u8 address, bool cpu_access = false); - void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false); + void regs_w(u8 page, u8 address, u16 data); u16 regs_r(u8 page, u8 address) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp index ebb9a62d..b0f887da 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp @@ -438,7 +438,7 @@ void es5506_core::host_w(u8 address, u8 data) m_hd = data; if (m_e.rising_edge()) { // update directly - write(m_ha, m_hd, true); + write(m_ha, m_hd); } else { @@ -464,7 +464,7 @@ u8 es5506_core::read(u8 address, bool cpu_access) return bitfield(m_read_latch, 24, 8); } -void es5506_core::write(u8 address, u8 data, bool cpu_access) +void es5506_core::write(u8 address, u8 data) { const u8 byte = bitfield(address, 0, 2); // byte select const u8 shift = 24 - (byte << 3); @@ -478,7 +478,7 @@ void es5506_core::write(u8 address, u8 data, bool cpu_access) return; } - regs_w(m_page, address, m_write_latch, cpu_access); + regs_w(m_page, address, m_write_latch); // Reset latch m_write_latch = 0; @@ -707,7 +707,7 @@ u32 es5506_core::regs_r(u8 page, u8 address, bool cpu_access) return read_latch; } -void es5506_core::regs_w(u8 page, u8 address, u32 data, bool cpu_access) +void es5506_core::regs_w(u8 page, u8 address, u32 data) { // Global registers if (address >= 13) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index 524e754e..f54a03c3 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -329,10 +329,10 @@ class es5506_core : public es550x_shared_core // bypass chips host interface for debug purpose only u8 read(u8 address, bool cpu_access = false); - void write(u8 address, u8 data, bool cpu_access = false); + void write(u8 address, u8 data); u32 regs_r(u8 page, u8 address, bool cpu_access = false); - void regs_w(u8 page, u8 address, u32 data, bool cpu_access = false); + void regs_w(u8 page, u8 address, u32 data); u8 regs8_r(u8 page, u8 address) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp index 6fe759ac..cccdbbf5 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp @@ -15,7 +15,7 @@ void k053260_core::tick() { for (int i = 0; i < 4; i++) { - m_voice[i].tick(i); + m_voice[i].tick(); m_out[0] += m_voice[i].out(0); m_out[1] += m_voice[i].out(1); } @@ -36,7 +36,7 @@ void k053260_core::tick() m_dac.set_clock(bitfield(dac_clock, 0, 4)); } -void k053260_core::voice_t::tick(u8 ne) +void k053260_core::voice_t::tick() { if (m_enable && m_busy) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp index de39e35e..bca78a1f 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp @@ -61,7 +61,7 @@ class k053260_core : public vgsound_emu_core // internal state void reset(); - void tick(u8 ne); + void tick(); // accessors void write(u8 address, u8 data); From 81f812b2160063ce40f54571c8dd69c0c0f85914 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:56:09 +0900 Subject: [PATCH 11/16] Sync with master --- .../vgsound_emu/src/es550x/es5505.cpp | 9 ++++----- .../vgsound_emu/src/es550x/es5505.hpp | 20 +++++++------------ .../vgsound_emu/src/es550x/es5506.cpp | 10 +++++----- .../vgsound_emu/src/es550x/es5506.hpp | 20 +++++++------------ 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp index 6b271224..6172a866 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp @@ -45,12 +45,11 @@ void es5505_core::tick() for (int i = 0; i < 4; i++) { // copy output - m_output[i] = m_output_temp[i]; - m_output_latch[i] = m_ch[i]; - m_output_temp[i].reset(); + m_output[i].copy_output(m_output_temp[i]); // clamp to 16 bit (upper 5 bits are overflow // guard bits) - m_output_latch[i].clamp16(); + m_output_latch[i].clamp16(m_ch[i]); + m_output_temp[i].reset(); // set signed if (m_output_latch[i].left() < 0) { @@ -155,7 +154,7 @@ void es5505_core::tick_perf() // output for (int c = 0; c < 4; c++) { - m_output[c] = m_ch[c]; + m_output[c].clamp16(m_ch[c]); } // update diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index 4cbd2581..52a3b5f3 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -31,7 +31,13 @@ class es5505_core : public es550x_shared_core { m_left = 0; m_right = 0; - }; + } + + inline void copy_output(output_t &src) + { + m_left = src.left(); + m_right = src.right(); + } inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); } @@ -76,18 +82,6 @@ class es5505_core : public es550x_shared_core return *this; } - output_t &operator=(output_t &src) - { - clamp16(src); - return *this; - } - - output_t &operator=(s32 val) - { - m_left = m_right = clamp16(val); - return *this; - } - output_t &operator>>(s32 shift) { m_left >>= shift; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp index b0f887da..83e43cac 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp @@ -63,11 +63,11 @@ void es5506_core::tick() for (int i = 0; i < 6; i++) { // copy output - m_output[i] = m_output_temp[i]; - m_output_latch[i] = m_ch[i]; - m_output_temp[i].reset(); + m_output[i].copy_output(m_output_temp[i]); // clamp to 20 bit (upper 3 bits are // overflow guard bits) + m_output_latch[i].clamp20(m_ch[i]); + m_output_temp[i].reset(); m_output_latch[i].clamp20(); // set signed if (m_output_latch[i].left() < 0) @@ -202,7 +202,7 @@ void es5506_core::tick_perf() { for (int c = 0; c < 6; c++) { - m_output[c] = m_ch[c] >> output_bits; + m_output[c].clamp20(m_ch[c] >> output_bits); } } } @@ -210,7 +210,7 @@ void es5506_core::tick_perf() { for (int c = 0; c < 6; c++) { - m_output[c] = 0; + m_output[c].reset(); } } diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index f54a03c3..b59a96dc 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -31,7 +31,13 @@ class es5506_core : public es550x_shared_core { m_left = 0; m_right = 0; - }; + } + + inline void copy_output(output_t &src) + { + m_left = src.left(); + m_right = src.right(); + } inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } @@ -76,18 +82,6 @@ class es5506_core : public es550x_shared_core return *this; } - output_t &operator=(output_t &src) - { - clamp20(src); - return *this; - } - - output_t &operator=(s32 val) - { - m_left = m_right = clamp20(val); - return *this; - } - output_t &operator>>(s32 shift) { m_left >>= shift; From eeb09c703147a0845c7eb4886dfe60caac23b45b Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 14:02:43 +0900 Subject: [PATCH 12/16] Sync with master --- .../vgsound_emu/src/core/vox/vox.hpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp index ead2ca75..231f9b63 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -40,17 +40,11 @@ class vox_core : public vgsound_emu_core s32 step() { return m_step; } - decoder_state_t &operator=(decoder_state_t &src) - { - copy_state(src); - return *this; - } + void copy_state(decoder_state_t &src); private: const bool m_wraparound = false; // wraparound or clamp? - void copy_state(decoder_state_t &src); - vox_core &m_vox; s8 m_index = 0; s32 m_step = 16; @@ -76,7 +70,7 @@ class vox_core : public vgsound_emu_core { if (!m_loop_saved) { - m_loop = m_curr; + m_loop.copy_state(m_curr); m_loop_saved = true; } } @@ -85,7 +79,7 @@ class vox_core : public vgsound_emu_core { if (m_loop_saved) { - m_curr = m_loop; + m_curr.copy_state(m_loop); } } From 8ba53999bc710e51594c42e0c1d844f77474248c Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 18 Sep 2022 12:09:54 +0900 Subject: [PATCH 13/16] Less confused naming --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 885a4c49..634cf584 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,7 +183,7 @@ TAParamResult pVersion(String) { printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - printf("- vgsound_emu (second version) by cam900 (zlib)\n"); + printf("- vgsound_emu (second version) by cam900 (zlib license)\n"); return TA_PARAM_QUIT; } From 635bcf1c6d461d938f667a6dc210cba21ddd5756 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 18 Sep 2022 13:19:08 +0900 Subject: [PATCH 14/16] Fix disclaimer --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 634cf584..142454b8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,7 +183,7 @@ TAParamResult pVersion(String) { printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - printf("- vgsound_emu (second version) by cam900 (zlib license)\n"); + printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n"); return TA_PARAM_QUIT; } From 02ef001eb8104cb1255ea26f36bf228641c162f0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 19:41:36 -0500 Subject: [PATCH 15/16] MSM6295: fix mute issue --- src/engine/platform/msm6295.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 4d32f431..bb002d1a 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -255,6 +255,7 @@ void DivPlatformMSM6295::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformMSM6295::Channel(); chan[i].std.setEngine(parent); + msm.voice_mute(i,isMuted[i]); } for (int i=0; i<4; i++) { chan[i].vol=8; From ed7a48884464d1762c6a86216a19f764ee882459 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 19:46:17 -0500 Subject: [PATCH 16/16] MSM6295: reduce some CPU usage --- src/engine/platform/msm6295.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index bb002d1a..e6e3d8a7 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -68,9 +68,11 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t delay=w.delay; } } else { - delay--; + delay-=3; } + msm.tick(); + msm.tick(); msm.tick(); bufL[h]=msm.out()<<4; @@ -388,7 +390,7 @@ void DivPlatformMSM6295::setFlags(unsigned int flags) { chipClock=COLOR_NTSC/3.0; break; } - rate=chipClock; + rate=chipClock/3; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate/22; }