mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-25 22:15:14 +00:00
Update vgsound_emu library
This commit is contained in:
parent
9a3c81d90a
commit
b461ffe411
28 changed files with 78 additions and 2705 deletions
|
@ -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
|
||||
|
|
|
@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_
|
|||
for (size_t h=start; h<start+len; h++) {
|
||||
signed int out=0;
|
||||
// K005289 part
|
||||
k005289->tick();
|
||||
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() {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <queue>
|
||||
#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);
|
||||
|
|
|
@ -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<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
rWrite(0,0x80|chan[c.chan].sample); // set phrase
|
||||
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
|
||||
} else {
|
||||
|
@ -128,7 +128,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
}
|
||||
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
chan[c.chan].sample=12*sampleBank+c.value%12;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
rWrite(0,0x80|chan[c.chan].sample); // set phrase
|
||||
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
|
||||
}
|
||||
|
@ -138,14 +138,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
|
@ -206,7 +206,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformMSM6295::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
msm.m_voice[ch].m_muted=mute;
|
||||
msm.voice_mute(ch,mute);
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::forceIns() {
|
||||
|
@ -387,7 +387,7 @@ void DivPlatformMSM6295::setFlags(unsigned int flags) {
|
|||
chipClock=COLOR_NTSC/3.0;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/3;
|
||||
rate=chipClock;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate/22;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#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) {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/n163/n163.hpp"
|
||||
#include "vgsound_emu/src/n163/n163.hpp"
|
||||
|
||||
class DivPlatformN163: public DivDispatch {
|
||||
struct Channel {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/scc/scc.hpp"
|
||||
#include "vgsound_emu/src/scc/scc.hpp"
|
||||
|
||||
class DivPlatformSCC: public DivDispatch {
|
||||
struct Channel {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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<typename T> 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
|
|
@ -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 <string.h>
|
||||
|
||||
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<u32>(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;
|
||||
}
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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<typename T> 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
|
|
@ -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<u8>(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;
|
||||
}
|
||||
}
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
#include <math.h>
|
||||
|
||||
#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<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
|
||||
// get sign extended value, sign_ext<type>(input, len)
|
||||
template<typename T> T sign_ext(T in, u8 len)
|
||||
{
|
||||
len = std::max<u8>(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<typename T, T InitWidth, u8 InitEdge = 0>
|
||||
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
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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
|
|
@ -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 <string.h>
|
||||
|
||||
// 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<u8>(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<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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<typename T> 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
|
|
@ -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 <string.h>
|
||||
|
||||
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<u32>(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);
|
||||
}
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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<typename T> 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
|
|
@ -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;
|
||||
}
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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<typename T> 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<u8[]>(0x1000);
|
||||
m_wave = std::make_unique<u8[]>(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<u8[]> m_envelope = nullptr;
|
||||
std::unique_ptr<u8[]> m_wave = nullptr;
|
||||
|
||||
// output data
|
||||
s32 m_out[2] = {0};
|
||||
|
||||
x1_010_mem_intf &m_intf;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
#include <queue>
|
||||
#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<QueuedWrite> 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();
|
||||
};
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
#include <math.h>
|
||||
|
||||
//#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; h<start+len; h++) {
|
||||
x1_010->tick();
|
||||
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) && (address<getSampleMemCapacity())) {
|
||||
return sampleMem[address];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double DivPlatformX1_010::NoteX1_010(int ch, int note) {
|
||||
if (chan[ch].pcm) { // PCM note
|
||||
double off=8192.0;
|
||||
|
@ -811,7 +818,7 @@ DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) {
|
|||
|
||||
unsigned char* DivPlatformX1_010::getRegisterPool() {
|
||||
for (int i=0; i<0x2000; i++) {
|
||||
regPool[i]=x1_010->ram_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue