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; }