o add bsnes

This commit is contained in:
optixx
2009-04-08 21:29:36 +02:00
parent bcb4a055e9
commit 27b58a09f2
413 changed files with 71887 additions and 0 deletions

588
bsnes/dsp/adsp/adsp.cpp Executable file
View File

@@ -0,0 +1,588 @@
#include <../base.hpp>
#define ADSP_CPP
#include "adsp_tables.cpp"
void aDSP::enter() { loop:
run();
goto loop;
}
uint8 aDSP::readb(uint16 addr) {
return spcram[addr];
}
void aDSP::writeb(uint16 addr, uint8 data) {
spcram[addr] = data;
}
uint16 aDSP::readw(uint16 addr) {
return (readb(addr + 0)) | (readb(addr + 1) << 8);
}
void aDSP::writew(uint16 addr, uint16 data) {
writeb(addr + 0, data);
writeb(addr + 1, data >> 8);
}
uint8 aDSP::read(uint8 addr) {
addr &= 127;
int v = addr >> 4;
int n = addr & 15;
switch(addr) {
case 0x00: case 0x10: case 0x20: case 0x30:
case 0x40: case 0x50: case 0x60: case 0x70:
return voice[v].VOLL;
case 0x01: case 0x11: case 0x21: case 0x31:
case 0x41: case 0x51: case 0x61: case 0x71:
return voice[v].VOLR;
case 0x02: case 0x12: case 0x22: case 0x32:
case 0x42: case 0x52: case 0x62: case 0x72:
return voice[v].PITCH;
case 0x03: case 0x13: case 0x23: case 0x33:
case 0x43: case 0x53: case 0x63: case 0x73:
return voice[v].PITCH >> 8;
case 0x04: case 0x14: case 0x24: case 0x34:
case 0x44: case 0x54: case 0x64: case 0x74:
return voice[v].SRCN;
case 0x05: case 0x15: case 0x25: case 0x35:
case 0x45: case 0x55: case 0x65: case 0x75:
return voice[v].ADSR1;
case 0x06: case 0x16: case 0x26: case 0x36:
case 0x46: case 0x56: case 0x66: case 0x76:
return voice[v].ADSR2;
case 0x07: case 0x17: case 0x27: case 0x37:
case 0x47: case 0x57: case 0x67: case 0x77:
return voice[v].GAIN;
case 0x08: case 0x18: case 0x28: case 0x38:
case 0x48: case 0x58: case 0x68: case 0x78:
return voice[v].ENVX;
case 0x09: case 0x19: case 0x29: case 0x39:
case 0x49: case 0x59: case 0x69: case 0x79:
return voice[v].OUTX;
case 0x0f: case 0x1f: case 0x2f: case 0x3f:
case 0x4f: case 0x5f: case 0x6f: case 0x7f:
return status.FIR[v];
case 0x0c: return status.MVOLL;
case 0x1c: return status.MVOLR;
case 0x2c: return status.EVOLL;
case 0x3c: return status.EVOLR;
case 0x4c: return status.KON;
case 0x5c: return status.KOFF;
case 0x6c: return status.FLG;
case 0x7c: return status.ENDX;
case 0x0d: return status.EFB;
case 0x2d: return status.PMON;
case 0x3d: return status.NON;
case 0x4d: return status.EON;
case 0x5d: return status.DIR;
case 0x6d: return status.ESA;
case 0x7d: return status.EDL;
}
return dspram[addr];
}
void aDSP::write(uint8 addr, uint8 data) {
//0x80-0xff is a read-only mirror of 0x00-0x7f
if(addr & 0x80)return;
int v = addr >> 4;
int n = addr & 15;
switch(addr) {
case 0x00: case 0x10: case 0x20: case 0x30:
case 0x40: case 0x50: case 0x60: case 0x70:
voice[v].VOLL = data;
break;
case 0x01: case 0x11: case 0x21: case 0x31:
case 0x41: case 0x51: case 0x61: case 0x71:
voice[v].VOLR = data;
break;
case 0x02: case 0x12: case 0x22: case 0x32:
case 0x42: case 0x52: case 0x62: case 0x72:
voice[v].PITCH &= 0xff00;
voice[v].PITCH |= data;
break;
case 0x03: case 0x13: case 0x23: case 0x33:
case 0x43: case 0x53: case 0x63: case 0x73:
voice[v].PITCH &= 0x00ff;
voice[v].PITCH |= data << 8;
break;
case 0x04: case 0x14: case 0x24: case 0x34:
case 0x44: case 0x54: case 0x64: case 0x74:
voice[v].SRCN = data;
break;
case 0x05: case 0x15: case 0x25: case 0x35:
case 0x45: case 0x55: case 0x65: case 0x75:
voice[v].ADSR1 = data;
voice[v].AdjustEnvelope();
break;
case 0x06: case 0x16: case 0x26: case 0x36:
case 0x46: case 0x56: case 0x66: case 0x76:
voice[v].ADSR2 = data;
//sustain_level = 0-7, 7 is a special case handled by ATTACK envx mode
voice[v].env_sustain = (voice[v].ADSR_sus_level() + 1) << 8;
voice[v].AdjustEnvelope();
break;
case 0x07: case 0x17: case 0x27: case 0x37:
case 0x47: case 0x57: case 0x67: case 0x77:
voice[v].GAIN = data;
voice[v].AdjustEnvelope();
break;
case 0x08: case 0x18: case 0x28: case 0x38:
case 0x48: case 0x58: case 0x68: case 0x78:
voice[v].ENVX = data;
break;
case 0x09: case 0x19: case 0x29: case 0x39:
case 0x49: case 0x59: case 0x69: case 0x79:
voice[v].OUTX = data;
break;
case 0x0f: case 0x1f: case 0x2f: case 0x3f:
case 0x4f: case 0x5f: case 0x6f: case 0x7f:
status.FIR[v] = data;
break;
case 0x0c: status.MVOLL = data; break;
case 0x1c: status.MVOLR = data; break;
case 0x2c: status.EVOLL = data; break;
case 0x3c: status.EVOLR = data; break;
case 0x4c:
status.KON = data;
status.kon = data;
break;
case 0x5c:
status.KOFF = data;
break;
case 0x6c:
status.FLG = data;
status.noise_rate = rate_table[data & 0x1f];
break;
case 0x7c:
//read-only register, writes clear all bits of ENDX
status.ENDX = 0;
break;
case 0x0d: status.EFB = data; break;
case 0x2d: status.PMON = data; break;
case 0x3d: status.NON = data; break;
case 0x4d: status.EON = data; break;
case 0x5d: status.DIR = data; break;
case 0x6d: status.ESA = data; break;
case 0x7d: status.EDL = data; break;
}
dspram[addr] = data;
}
void aDSP::power() {
spcram = r_smp->get_spcram_handle();
memset(dspram, 0x00, 128);
for(int v = 0; v < 8; v++) {
voice[v].VOLL = 0;
voice[v].VOLR = 0;
voice[v].PITCH = 0;
voice[v].SRCN = 0;
voice[v].ADSR1 = 0;
voice[v].ADSR2 = 0;
voice[v].GAIN = 0;
status.FIR[v] = 0;
}
status.FLG = 0xe0;
status.MVOLL = status.MVOLR = 0;
status.EVOLL = status.EVOLR = 0;
status.ENDX = 0;
status.EFB = 0;
status.PMON = 0;
status.NON = 0;
status.EON = 0;
status.DIR = 0;
status.ESA = 0;
status.EDL = 0;
status.echo_length = 0;
reset();
}
void aDSP::reset() {
status.KON = 0x00;
status.KOFF = 0x00;
status.FLG |= 0xe0;
status.kon = 0x00;
status.esa = 0x00;
status.noise_ctr = 0;
status.noise_rate = 0;
status.noise_sample = 0x4000;
status.echo_index = 0;
status.fir_buffer_index = 0;
for(int v = 0; v < 8; v++) {
voice[v].ENVX = 0;
voice[v].OUTX = 0;
voice[v].pitch_ctr = 0;
voice[v].brr_index = 0;
voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2));
voice[v].brr_looped = false;
voice[v].brr_data[0] = 0;
voice[v].brr_data[1] = 0;
voice[v].brr_data[2] = 0;
voice[v].brr_data[3] = 0;
voice[v].brr_data_index = 0;
voice[v].envx = 0;
voice[v].env_ctr = 0;
voice[v].env_rate = 0;
voice[v].env_state = SILENCE;
voice[v].env_mode = DIRECT;
status.fir_buffer[0][v] = 0;
status.fir_buffer[1][v] = 0;
}
dsp_counter = 0;
}
void aDSP::run() {
uint8 pmon;
int32 sample;
int32 msamplel, msampler;
int32 esamplel, esampler;
int32 fir_samplel, fir_sampler;
pmon = status.PMON & ~status.NON & ~1;
if((dsp_counter++ & 1) == 0) {
for(uint v = 0; v < 8; v++) {
if(status.soft_reset()) {
if(voice[v].env_state != SILENCE) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
}
if(status.KOFF & (1 << v)) {
if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) {
voice[v].env_state = RELEASE;
voice[v].AdjustEnvelope();
}
}
if(status.kon & (1 << v)) {
voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2));
voice[v].brr_index = -9;
voice[v].brr_looped = false;
voice[v].brr_data[0] = 0;
voice[v].brr_data[1] = 0;
voice[v].brr_data[2] = 0;
voice[v].brr_data[3] = 0;
voice[v].envx = 0;
voice[v].env_state = ATTACK;
voice[v].AdjustEnvelope();
}
}
status.ENDX &= ~status.kon;
status.kon = 0;
}
/*****
* update noise
*****/
status.noise_ctr += status.noise_rate;
if(status.noise_ctr >= 0x7800) {
status.noise_ctr -= 0x7800;
status.noise_sample = (status.noise_sample >> 1) | (((status.noise_sample << 14) ^ (status.noise_sample << 13)) & 0x4000);
}
msamplel = msampler = 0;
esamplel = esampler = 0;
/*****
* process voice channels
*****/
for(int v = 0; v < 8; v++) {
if(voice[v].brr_index < -1) {
voice[v].brr_index++;
voice[v].OUTX = voice[v].outx = 0;
voice[v].ENVX = 0;
continue;
}
if(voice[v].brr_index >= 0) {
if(pmon & (1 << v)) {
voice[v].pitch_ctr += (voice[v].pitch_rate() * (voice[v - 1].outx + 0x8000)) >> 15;
} else {
voice[v].pitch_ctr += voice[v].pitch_rate();
}
} else {
voice[v].pitch_ctr = 0x3000;
voice[v].brr_index = 0;
}
/*****
* decode BRR samples
*****/
while(voice[v].pitch_ctr >= 0) {
voice[v].pitch_ctr -= 0x1000;
voice[v].brr_data_index++;
voice[v].brr_data_index &= 3;
if(voice[v].brr_index == 0) {
voice[v].brr_header = readb(voice[v].brr_ptr);
if(voice[v].brr_header_flags() == BRR_END) {
status.ENDX |= (1 << v);
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
}
#define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3]
if(voice[v].env_state != SILENCE) {
sample = readb(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1));
if(voice[v].brr_index & 1) {
sample = sclip<4>(sample);
} else {
sample = sclip<4>(sample >> 4);
}
if(voice[v].brr_header_shift() <= 12) {
sample = (sample << voice[v].brr_header_shift() >> 1);
} else {
sample &= ~0x7ff;
}
switch(voice[v].brr_header_filter()) {
case 0: //direct
break;
case 1: //15/16
sample += S(-1) + ((-S(-1)) >> 4);
break;
case 2: //61/32 - 15/16
sample += (S(-1) << 1) + ((-((S(-1) << 1) + S(-1))) >> 5)
- S(-2) + (S(-2) >> 4);
break;
case 3: //115/64 - 13/16
sample += (S(-1) << 1) + ((-(S(-1) + (S(-1) << 2) + (S(-1) << 3))) >> 6)
- S(-2) + (((S(-2) << 1) + S(-2)) >> 4);
break;
}
S(0) = sample = sclip<15>(sclamp<16>(sample));
} else {
S(0) = sample = 0;
}
if(++voice[v].brr_index > 15) {
voice[v].brr_index = 0;
if(voice[v].brr_header_flags() & BRR_END) {
if(voice[v].brr_header_flags() & BRR_LOOP) {
status.ENDX |= (1 << v);
}
voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2) + 2);
voice[v].brr_looped = true;
} else {
voice[v].brr_ptr += 9;
}
}
}
/*****
* volume envelope adjust
*****/
voice[v].env_ctr += voice[v].env_rate;
if(voice[v].env_ctr >= 0x7800) {
voice[v].env_ctr -= 0x7800;
switch(voice[v].env_mode) {
case DIRECT:
voice[v].env_rate = 0;
break;
case LINEAR_DEC:
voice[v].envx -= 32;
if(voice[v].envx <= 0) {
voice[v].envx = 0;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
break;
case LINEAR_INC:
voice[v].envx += 32;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
if(voice[v].ADSR_enabled() && voice[v].env_state == ATTACK) {
voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY);
voice[v].AdjustEnvelope();
}
}
break;
case EXP_DEC:
//multiply by 255/256ths
voice[v].envx -= ((voice[v].envx - 1) >> 8) + 1;
if(voice[v].ADSR_enabled() && voice[v].env_state == DECAY && voice[v].envx <= voice[v].env_sustain) {
voice[v].env_state = SUSTAIN;
voice[v].AdjustEnvelope();
} else if(voice[v].envx <= 0) {
voice[v].envx = 0;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
break;
case BENT_INC:
if(voice[v].envx < 0x600) {
voice[v].envx += 32;
} else {
voice[v].envx += 8;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
}
break;
case FAST_ATTACK:
voice[v].envx += 0x400;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
//attack raises to max envx. if sustain is also set to max envx, skip decay phase
voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY);
voice[v].AdjustEnvelope();
}
break;
case RELEASE_DEC:
voice[v].envx -= 8;
if(voice[v].envx <= 0) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
break;
}
}
voice[v].ENVX = voice[v].envx >> 4;
/*****
* gaussian interpolation / noise
*****/
if(status.NON & (1 << v)) {
sample = sclip<15>(status.noise_sample);
} else {
int32 d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1
sample = ((gaussian_table[ -1 - d] * S(-3)) >> 11);
sample += ((gaussian_table[255 - d] * S(-2)) >> 11);
sample += ((gaussian_table[512 + d] * S(-1)) >> 11);
sample = sclip <15>(sample);
sample += ((gaussian_table[256 + d] * S( 0)) >> 11);
sample = sclamp<15>(sample);
}
#undef S
/*****
* envelope / volume adjust
*****/
sample = (sample * voice[v].envx) >> 11;
voice[v].outx = sample << 1;
voice[v].OUTX = sample >> 7;
if(!status.mute()) {
msamplel += ((sample * voice[v].VOLL) >> 7) << 1;
msampler += ((sample * voice[v].VOLR) >> 7) << 1;
}
if((status.EON & (1 << v)) && status.echo_write()) {
esamplel += ((sample * voice[v].VOLL) >> 7) << 1;
esampler += ((sample * voice[v].VOLR) >> 7) << 1;
}
}
/*****
* echo (FIR) adjust
*****/
#define F(c,x) status.fir_buffer[c][(status.fir_buffer_index + (x)) & 7]
status.fir_buffer_index++;
F(0,0) = readw((status.esa << 8) + status.echo_index + 0);
F(1,0) = readw((status.esa << 8) + status.echo_index + 2);
fir_samplel = (F(0,-0) * status.FIR[7] +
F(0,-1) * status.FIR[6] +
F(0,-2) * status.FIR[5] +
F(0,-3) * status.FIR[4] +
F(0,-4) * status.FIR[3] +
F(0,-5) * status.FIR[2] +
F(0,-6) * status.FIR[1] +
F(0,-7) * status.FIR[0]);
fir_sampler = (F(1,-0) * status.FIR[7] +
F(1,-1) * status.FIR[6] +
F(1,-2) * status.FIR[5] +
F(1,-3) * status.FIR[4] +
F(1,-4) * status.FIR[3] +
F(1,-5) * status.FIR[2] +
F(1,-6) * status.FIR[1] +
F(1,-7) * status.FIR[0]);
#undef F
/*****
* update echo buffer
*****/
if(status.echo_write()) {
esamplel += (fir_samplel * status.EFB) >> 14;
esampler += (fir_sampler * status.EFB) >> 14;
esamplel = sclamp<16>(esamplel);
esampler = sclamp<16>(esampler);
writew((status.esa << 8) + status.echo_index + 0, esamplel);
writew((status.esa << 8) + status.echo_index + 2, esampler);
}
status.echo_index += 4;
if(status.echo_index >= status.echo_length) {
status.echo_index = 0;
status.echo_length = (status.EDL & 0x0f) << 11;
}
//ESA read occurs at roughly 22/32th sample
//ESA fetch occurs at roughly 29/32th sample
//as this is not a subsample-level S-DSP emulator,
//simulate ~25/32th delay by caching ESA for one
//complete sample ...
status.esa = status.ESA;
/*****
* main output adjust
*****/
if(!status.mute()) {
msamplel = (msamplel * status.MVOLL) >> 7;
msampler = (msampler * status.MVOLR) >> 7;
msamplel += (fir_samplel * status.EVOLL) >> 14;
msampler += (fir_sampler * status.EVOLR) >> 14;
msamplel = sclamp<16>(msamplel);
msampler = sclamp<16>(msampler);
}
snes.audio.update(msamplel, msampler);
scheduler.addclocks_dsp(32 * 3 * 8);
}
aDSP::aDSP() {}
aDSP::~aDSP() {}

172
bsnes/dsp/adsp/adsp.hpp Executable file
View File

@@ -0,0 +1,172 @@
class aDSP : public DSP {
private:
uint8 dspram[128];
uint8 *spcram;
uint32 dsp_counter;
enum { BRR_END = 1, BRR_LOOP = 2 };
uint8 readb (uint16 addr);
uint16 readw (uint16 addr);
void writeb(uint16 addr, uint8 data);
void writew(uint16 addr, uint16 data);
public:
static const uint16 rate_table[32];
static const int16 gaussian_table[512];
enum EnvelopeStates {
ATTACK,
DECAY,
SUSTAIN,
RELEASE,
SILENCE
};
enum EnvelopeModes {
DIRECT,
LINEAR_DEC,
EXP_DEC,
LINEAR_INC,
BENT_INC,
FAST_ATTACK,
RELEASE_DEC
};
private:
struct Status {
//$0c,$1c
int8 MVOLL, MVOLR;
//$2c,$3c
int8 EVOLL, EVOLR;
//$4c,$5c
uint8 KON, KOFF;
//$6c
uint8 FLG;
//$7c
uint8 ENDX;
//$0d
int8 EFB;
//$2d,$3d,$4d
uint8 PMON, NON, EON;
//$5d
uint8 DIR;
//$6d,$7d
uint8 ESA, EDL;
//$xf
int8 FIR[8];
//internal variables
uint8 kon, esa;
int16 noise_ctr, noise_rate;
uint16 noise_sample;
uint16 echo_index, echo_length;
int16 fir_buffer[2][8];
uint8 fir_buffer_index;
//functions
bool soft_reset() { return !!(FLG & 0x80); }
bool mute() { return !!(FLG & 0x40); }
bool echo_write() { return !(FLG & 0x20); }
} status;
struct Voice {
//$x0-$x1
int8 VOLL, VOLR;
//$x2-$x3
int16 PITCH;
//$x4
uint8 SRCN;
//$x5-$x7
uint8 ADSR1, ADSR2, GAIN;
//$x8-$x9
uint8 ENVX, OUTX;
//internal variables
int16 pitch_ctr;
int8 brr_index;
uint16 brr_ptr;
uint8 brr_header;
bool brr_looped;
int16 brr_data[4];
uint8 brr_data_index;
int16 envx;
uint16 env_ctr, env_rate, env_sustain;
enum EnvelopeStates env_state;
enum EnvelopeModes env_mode;
int16 outx;
//functions
int16 pitch_rate() { return PITCH & 0x3fff; }
uint8 brr_header_shift() { return brr_header >> 4; }
uint8 brr_header_filter() { return (brr_header >> 2) & 3; }
uint8 brr_header_flags() { return brr_header & 3; }
bool ADSR_enabled() { return !!(ADSR1 & 0x80); }
uint8 ADSR_decay() { return (ADSR1 >> 4) & 7; }
uint8 ADSR_attack() { return ADSR1 & 15; }
uint8 ADSR_sus_level() { return ADSR2 >> 5; }
uint8 ADSR_sus_rate() { return ADSR2 & 31; }
void AdjustEnvelope() {
if(env_state == SILENCE) {
env_mode = DIRECT;
env_rate = 0;
envx = 0;
} else if(env_state == RELEASE) {
env_mode = RELEASE_DEC;
env_rate = 0x7800;
} else if(ADSR_enabled()) {
switch(env_state) {
case ATTACK:
env_rate = rate_table[(ADSR_attack() << 1) + 1];
env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC;
break;
case DECAY:
env_rate = rate_table[(ADSR_decay() << 1) + 0x10];
env_mode = EXP_DEC;
break;
case SUSTAIN:
env_rate = rate_table[ADSR_sus_rate()];
env_mode = (env_rate == 0) ? DIRECT : EXP_DEC;
break;
}
} else if(GAIN & 0x80) {
switch(GAIN & 0x60) {
case 0x00: env_mode = LINEAR_DEC; break;
case 0x20: env_mode = EXP_DEC; break;
case 0x40: env_mode = LINEAR_INC; break;
case 0x60: env_mode = BENT_INC; break;
}
env_rate = rate_table[GAIN & 0x1f];
} else {
env_mode = DIRECT;
env_rate = 0;
envx = (GAIN & 0x7f) << 4;
}
}
} voice[8];
public:
void enter();
void run();
uint8 read (uint8 addr);
void write(uint8 addr, uint8 data);
void power();
void reset();
aDSP();
~aDSP();
};

77
bsnes/dsp/adsp/adsp_tables.cpp Executable file
View File

@@ -0,0 +1,77 @@
#ifdef ADSP_CPP
const uint16 aDSP::rate_table[32] = {
0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
};
const int16 aDSP::gaussian_table[512] = {
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002,
0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004,
0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005,
0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008,
0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A,
0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E,
0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011,
0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016,
0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B,
0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021,
0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028,
0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030,
0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038,
0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042,
0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D,
0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059,
0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066,
0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075,
0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084,
0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096,
0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8,
0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC,
0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2,
0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9,
0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101,
0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B,
0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137,
0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153,
0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172,
0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191,
0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2,
0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5,
0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8,
0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C,
0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241,
0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267,
0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E,
0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5,
0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC,
0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304,
0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B,
0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353,
0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379,
0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F,
0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5,
0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9,
0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C,
0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E,
0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E,
0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C,
0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488,
0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2,
0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA,
0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0,
0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3,
0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3,
0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500,
0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B,
0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512,
0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517,
0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519
};
#endif //ifdef ADSP_CPP

13
bsnes/dsp/dsp.hpp Executable file
View File

@@ -0,0 +1,13 @@
class DSP {
public:
virtual void enter() = 0;
virtual uint8 read(uint8 addr) = 0;
virtual void write(uint8 addr, uint8 data) = 0;
virtual void power() = 0;
virtual void reset() = 0;
DSP() {}
virtual ~DSP() {}
};

62
bsnes/dsp/sdsp/brr.cpp Executable file
View File

@@ -0,0 +1,62 @@
#ifdef SDSP_CPP
void sDSP::brr_decode(voice_t &v) {
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
int nybbles = (state.t_brr_byte << 8) + memory::apuram[(uint16)(v.brr_addr + v.brr_offset + 1)];
const int filter = (state.t_brr_header >> 2) & 3;
const int scale = (state.t_brr_header >> 4);
//decode four samples
for(unsigned i = 0; i < 4; i++) {
//bits 12-15 = current nybble; sign extend, then shift right to 4-bit precision
//result: s = 4-bit sign-extended sample value
int s = (int16)nybbles >> 12;
nybbles <<= 4; //slide nybble so that on next loop iteration, bits 12-15 = current nybble
if(scale <= 12) {
s <<= scale;
s >>= 1;
} else {
s &= ~0x7ff;
}
//apply IIR filter (2 is the most commonly used)
const int p1 = v.buffer[v.buf_pos - 1];
const int p2 = v.buffer[v.buf_pos - 2] >> 1;
switch(filter) {
case 0: break; //no filter
case 1: {
//s += p1 * 0.46875
s += p1 >> 1;
s += (-p1) >> 5;
} break;
case 2: {
//s += p1 * 0.953125 - p2 * 0.46875
s += p1;
s -= p2;
s += p2 >> 4;
s += (p1 * -3) >> 6;
} break;
case 3: {
//s += p1 * 0.8984375 - p2 * 0.40625
s += p1;
s -= p2;
s += (p1 * -13) >> 7;
s += (p2 * 3) >> 4;
} break;
}
//adjust and write sample
s = sclamp<16>(s);
s = (int16)(s << 1);
v.buffer.write(v.buf_pos++, s);
if(v.buf_pos >= brr_buf_size) v.buf_pos = 0;
}
}
#endif //ifdef SDSP_CPP

52
bsnes/dsp/sdsp/counter.cpp Executable file
View File

@@ -0,0 +1,52 @@
#ifdef SDSP_CPP
//counter_rate = number of samples per counter event
//all rates are evenly divisible by counter_range (0x7800, 30720, or 2048 * 5 * 3)
//note that rate[0] is a special case, which never triggers
const uint16 sDSP::counter_rate[32] = {
0, 2048, 1536,
1280, 1024, 768,
640, 512, 384,
320, 256, 192,
160, 128, 96,
80, 64, 48,
40, 32, 24,
20, 16, 12,
10, 8, 6,
5, 4, 3,
2,
1,
};
//counter_offset = counter offset from zero
//counters do not appear to be aligned at zero for all rates
const uint16 sDSP::counter_offset[32] = {
0, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
0,
0,
};
inline void sDSP::counter_tick() {
state.counter--;
if(state.counter < 0) state.counter = counter_range - 1;
}
//return true if counter event should trigger
inline bool sDSP::counter_poll(unsigned rate) {
if(rate == 0) return false;
return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0;
}
#endif //ifdef SDSP_CPP

135
bsnes/dsp/sdsp/echo.cpp Executable file
View File

@@ -0,0 +1,135 @@
#ifdef SDSP_CPP
int sDSP::calc_fir(int i, bool channel) {
int s = state.echo_hist[channel][state.echo_hist_pos + i + 1];
return (s * (int8)REG(fir + i * 0x10)) >> 6;
}
int sDSP::echo_output(bool channel) {
int output = (int16)((state.t_main_out[channel] * (int8)REG(mvoll + channel * 0x10)) >> 7)
+ (int16)((state.t_echo_in [channel] * (int8)REG(evoll + channel * 0x10)) >> 7);
return sclamp<16>(output);
}
void sDSP::echo_read(bool channel) {
unsigned addr = state.t_echo_ptr + channel * 2;
uint8 lo = memory::apuram[(uint16)(addr + 0)];
uint8 hi = memory::apuram[(uint16)(addr + 1)];
int s = (int16)((hi << 8) + lo);
state.echo_hist[channel].write(state.echo_hist_pos, s >> 1);
}
void sDSP::echo_write(bool channel) {
if(!(state.t_echo_disabled & 0x20)) {
unsigned addr = state.t_echo_ptr + channel * 2;
int s = state.t_echo_out[channel];
memory::apuram[(uint16)(addr + 0)] = s;
memory::apuram[(uint16)(addr + 1)] = s >> 8;
}
state.t_echo_out[channel] = 0;
}
void sDSP::echo_22() {
//history
state.echo_hist_pos++;
if(state.echo_hist_pos >= echo_hist_size) state.echo_hist_pos = 0;
state.t_echo_ptr = (uint16)((state.t_esa << 8) + state.echo_offset);
echo_read(0);
//FIR
int l = calc_fir(0, 0);
int r = calc_fir(0, 1);
state.t_echo_in[0] = l;
state.t_echo_in[1] = r;
}
void sDSP::echo_23() {
int l = calc_fir(1, 0) + calc_fir(2, 0);
int r = calc_fir(1, 1) + calc_fir(2, 1);
state.t_echo_in[0] += l;
state.t_echo_in[1] += r;
echo_read(1);
}
void sDSP::echo_24() {
int l = calc_fir(3, 0) + calc_fir(4, 0) + calc_fir(5, 0);
int r = calc_fir(3, 1) + calc_fir(4, 1) + calc_fir(5, 1);
state.t_echo_in[0] += l;
state.t_echo_in[1] += r;
}
void sDSP::echo_25() {
int l = state.t_echo_in[0] + calc_fir(6, 0);
int r = state.t_echo_in[1] + calc_fir(6, 1);
l = (int16)l;
r = (int16)r;
l += (int16)calc_fir(7, 0);
r += (int16)calc_fir(7, 1);
state.t_echo_in[0] = sclamp<16>(l) & ~1;
state.t_echo_in[1] = sclamp<16>(r) & ~1;
}
void sDSP::echo_26() {
//left output volumes
//(save sample for next clock so we can output both together)
state.t_main_out[0] = echo_output(0);
//echo feedback
int l = state.t_echo_out[0] + (int16)((state.t_echo_in[0] * (int8)REG(efb)) >> 7);
int r = state.t_echo_out[1] + (int16)((state.t_echo_in[1] * (int8)REG(efb)) >> 7);
state.t_echo_out[0] = sclamp<16>(l) & ~1;
state.t_echo_out[1] = sclamp<16>(r) & ~1;
}
void sDSP::echo_27() {
//output
int outl = state.t_main_out[0];
int outr = echo_output(1);
state.t_main_out[0] = 0;
state.t_main_out[1] = 0;
//TODO: global muting isn't this simple
//(turns DAC on and off or something, causing small ~37-sample pulse when first muted)
if(REG(flg) & 0x40) {
outl = 0;
outr = 0;
}
//output sample to DAC
snes.audio.update(outl, outr);
}
void sDSP::echo_28() {
state.t_echo_disabled = REG(flg);
}
void sDSP::echo_29() {
state.t_esa = REG(esa);
if(!state.echo_offset) state.echo_length = (REG(edl) & 0x0f) << 11;
state.echo_offset += 4;
if(state.echo_offset >= state.echo_length) state.echo_offset = 0;
//write left echo
echo_write(0);
state.t_echo_disabled = REG(flg);
}
void sDSP::echo_30() {
//write right echo
echo_write(1);
}
#endif //ifdef SDSP_CPP

62
bsnes/dsp/sdsp/envelope.cpp Executable file
View File

@@ -0,0 +1,62 @@
#ifdef SDSP_CPP
void sDSP::envelope_run(voice_t &v) {
int env = v.env;
if(v.env_mode == env_release) { //60%
env -= 0x8;
if(env < 0) env = 0;
v.env = env;
return;
}
int rate;
int env_data = VREG(adsr1);
if(state.t_adsr0 & 0x80) { //99% ADSR
if(v.env_mode >= env_decay) { //99%
env--;
env -= env >> 8;
rate = env_data & 0x1f;
if(v.env_mode == env_decay) { //1%
rate = ((state.t_adsr0 >> 3) & 0x0e) + 0x10;
}
} else { //env_attack
rate = ((state.t_adsr0 & 0x0f) << 1) + 1;
env += rate < 31 ? 0x20 : 0x400;
}
} else { //GAIN
env_data = VREG(gain);
int mode = env_data >> 5;
if(mode < 4) { //direct
env = env_data << 4;
rate = 31;
} else {
rate = env_data & 0x1f;
if(mode == 4) { //4: linear decrease
env -= 0x20;
} else if(mode < 6) { //5: exponential decrease
env--;
env -= env >> 8;
} else { //6, 7: linear increase
env += 0x20;
if(mode > 6 && (unsigned)v.hidden_env >= 0x600) {
env += 0x8 - 0x20; //7: two-slope linear increase
}
}
}
}
//sustain level
if((env >> 8) == (env_data >> 5) && v.env_mode == env_decay) v.env_mode = env_sustain;
v.hidden_env = env;
//unsigned cast because linear decrease underflowing also triggers this
if((unsigned)env > 0x7ff) {
env = (env < 0 ? 0 : 0x7ff);
if(v.env_mode == env_attack) v.env_mode = env_decay;
}
if(counter_poll(rate) == true) v.env = env;
}
#endif //ifdef SDSP_CPP

54
bsnes/dsp/sdsp/gaussian.cpp Executable file
View File

@@ -0,0 +1,54 @@
#ifdef SDSP_CPP
const int16 sDSP::gaussian_table[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,
28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,
78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,
104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,
134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,
171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,
212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,
260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,
314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,
374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,
439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,
508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,
582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,
659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,
737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,
816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,
894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,
969, 974, 978, 983, 988, 992, 997, 1001, 1005, 1010, 1014, 1019, 1023, 1027, 1032, 1036,
1040, 1045, 1049, 1053, 1057, 1061, 1066, 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102,
1106, 1109, 1113, 1117, 1121, 1125, 1128, 1132, 1136, 1139, 1143, 1146, 1150, 1153, 1157, 1160,
1164, 1167, 1170, 1174, 1177, 1180, 1183, 1186, 1190, 1193, 1196, 1199, 1202, 1205, 1207, 1210,
1213, 1216, 1219, 1221, 1224, 1227, 1229, 1232, 1234, 1237, 1239, 1241, 1244, 1246, 1248, 1251,
1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280,
1282, 1283, 1284, 1286, 1287, 1288, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1297, 1298,
1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305,
};
int sDSP::gaussian_interpolate(const voice_t &v) {
//make pointers into gaussian table based on fractional position between samples
int offset = (v.interp_pos >> 4) & 0xff;
const int16 *fwd = gaussian_table + 255 - offset;
const int16 *rev = gaussian_table + offset; //mirror left half of gaussian table
offset = v.buf_pos + (v.interp_pos >> 12);
int output;
output = (fwd[ 0] * v.buffer[offset + 0]) >> 11;
output += (fwd[256] * v.buffer[offset + 1]) >> 11;
output += (rev[256] * v.buffer[offset + 2]) >> 11;
output = (int16)output;
output += (rev[ 0] * v.buffer[offset + 3]) >> 11;
return sclamp<16>(output) & ~1;
}
#endif //ifdef SDSP_CPP

35
bsnes/dsp/sdsp/misc.cpp Executable file
View File

@@ -0,0 +1,35 @@
#ifdef SDSP_CPP
void sDSP::misc_27() {
state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON
}
void sDSP::misc_28() {
state.t_non = REG(non);
state.t_eon = REG(eon);
state.t_dir = REG(dir);
}
void sDSP::misc_29() {
state.every_other_sample ^= 1;
if(state.every_other_sample) {
state.new_kon &= ~state.kon; //clears KON 63 clocks after it was last read
}
}
void sDSP::misc_30() {
if(state.every_other_sample) {
state.kon = state.new_kon;
state.t_koff = REG(koff);
}
counter_tick();
//noise
if(counter_poll(REG(flg) & 0x1f) == true) {
int feedback = (state.noise << 13) ^ (state.noise << 14);
state.noise = (feedback & 0x4000) ^ (state.noise >> 1);
}
}
#endif //ifdef SDSP_CPP

326
bsnes/dsp/sdsp/sdsp.cpp Executable file
View File

@@ -0,0 +1,326 @@
/*
S-DSP emulator
license: LGPLv2
Note: this is basically a C++ cothreaded implementation of Shay Green's (blargg's) S-DSP emulator.
The actual algorithms, timing information, tables, variable names, etc were all from him.
*/
#include <../base.hpp>
#define SDSP_CPP
#define REG(n) state.regs[r_##n]
#define VREG(n) state.regs[v.vidx + v_##n]
#if !defined(USE_STATE_MACHINE)
#define phase_start() while(true) {
#define phase(n)
#define tick() scheduler.addclocks_dsp(3 * 8)
#define phase_end() }
#else
#define phase_start() switch(phase_index) {
#define phase(n) case n:
#define tick() scheduler.addclocks_dsp(3 * 8); break
#define phase_end() } phase_index = (phase_index + 1) & 31;
#endif
#include "gaussian.cpp"
#include "counter.cpp"
#include "envelope.cpp"
#include "brr.cpp"
#include "misc.cpp"
#include "voice.cpp"
#include "echo.cpp"
/* timing */
void sDSP::enter() {
phase_start()
phase(0)
voice_5(voice[0]);
voice_2(voice[1]);
tick();
phase(1)
voice_6(voice[0]);
voice_3(voice[1]);
tick();
phase(2)
voice_7(voice[0]);
voice_4(voice[1]);
voice_1(voice[3]);
tick();
phase(3)
voice_8(voice[0]);
voice_5(voice[1]);
voice_2(voice[2]);
tick();
phase(4)
voice_9(voice[0]);
voice_6(voice[1]);
voice_3(voice[2]);
tick();
phase(5)
voice_7(voice[1]);
voice_4(voice[2]);
voice_1(voice[4]);
tick();
phase(6)
voice_8(voice[1]);
voice_5(voice[2]);
voice_2(voice[3]);
tick();
phase(7)
voice_9(voice[1]);
voice_6(voice[2]);
voice_3(voice[3]);
tick();
phase(8)
voice_7(voice[2]);
voice_4(voice[3]);
voice_1(voice[5]);
tick();
phase(9)
voice_8(voice[2]);
voice_5(voice[3]);
voice_2(voice[4]);
tick();
phase(10)
voice_9(voice[2]);
voice_6(voice[3]);
voice_3(voice[4]);
tick();
phase(11)
voice_7(voice[3]);
voice_4(voice[4]);
voice_1(voice[6]);
tick();
phase(12)
voice_8(voice[3]);
voice_5(voice[4]);
voice_2(voice[5]);
tick();
phase(13)
voice_9(voice[3]);
voice_6(voice[4]);
voice_3(voice[5]);
tick();
phase(14)
voice_7(voice[4]);
voice_4(voice[5]);
voice_1(voice[7]);
tick();
phase(15)
voice_8(voice[4]);
voice_5(voice[5]);
voice_2(voice[6]);
tick();
phase(16)
voice_9(voice[4]);
voice_6(voice[5]);
voice_3(voice[6]);
tick();
phase(17)
voice_1(voice[0]);
voice_7(voice[5]);
voice_4(voice[6]);
tick();
phase(18)
voice_8(voice[5]);
voice_5(voice[6]);
voice_2(voice[7]);
tick();
phase(19)
voice_9(voice[5]);
voice_6(voice[6]);
voice_3(voice[7]);
tick();
phase(20)
voice_1(voice[1]);
voice_7(voice[6]);
voice_4(voice[7]);
tick();
phase(21)
voice_8(voice[6]);
voice_5(voice[7]);
voice_2(voice[0]);
tick();
phase(22)
voice_3a(voice[0]);
voice_9(voice[6]);
voice_6(voice[7]);
echo_22();
tick();
phase(23)
voice_7(voice[7]);
echo_23();
tick();
phase(24)
voice_8(voice[7]);
echo_24();
tick();
phase(25)
voice_3b(voice[0]);
voice_9(voice[7]);
echo_25();
tick();
phase(26)
echo_26();
tick();
phase(27)
misc_27();
echo_27();
tick();
phase(28)
misc_28();
echo_28();
tick();
phase(29)
misc_29();
echo_29();
tick();
phase(30)
misc_30();
voice_3c(voice[0]);
echo_30();
tick();
phase(31)
voice_4(voice[0]);
voice_1(voice[2]);
tick();
phase_end()
}
/* register interface for S-SMP $00f2,$00f3 */
uint8 sDSP::read(uint8 addr) {
return state.regs[addr];
}
void sDSP::write(uint8 addr, uint8 data) {
state.regs[addr] = data;
if((addr & 0x0f) == v_envx) {
state.envx_buf = data;
} else if((addr & 0x0f) == v_outx) {
state.outx_buf = data;
} else if(addr == r_kon) {
state.new_kon = data;
} else if(addr == r_endx) {
//always cleared, regardless of data written
state.endx_buf = 0;
state.regs[r_endx] = 0;
}
}
/* initialization */
void sDSP::power() {
memset(&state.regs, 0, sizeof state.regs);
state.echo_hist_pos = 0;
state.every_other_sample = false;
state.kon = 0;
state.noise = 0;
state.counter = 0;
state.echo_offset = 0;
state.echo_length = 0;
state.new_kon = 0;
state.endx_buf = 0;
state.envx_buf = 0;
state.outx_buf = 0;
state.t_pmon = 0;
state.t_non = 0;
state.t_eon = 0;
state.t_dir = 0;
state.t_koff = 0;
state.t_brr_next_addr = 0;
state.t_adsr0 = 0;
state.t_brr_header = 0;
state.t_brr_byte = 0;
state.t_srcn = 0;
state.t_esa = 0;
state.t_echo_disabled = 0;
state.t_dir_addr = 0;
state.t_pitch = 0;
state.t_output = 0;
state.t_looped = 0;
state.t_echo_ptr = 0;
state.t_main_out[0] = state.t_main_out[1] = 0;
state.t_echo_out[0] = state.t_echo_out[1] = 0;
state.t_echo_in[0] = state.t_echo_in[1] = 0;
for(unsigned i = 0; i < 8; i++) {
voice[i].buf_pos = 0;
voice[i].interp_pos = 0;
voice[i].brr_addr = 0;
voice[i].brr_offset = 1;
voice[i].vbit = 1 << i;
voice[i].vidx = i * 0x10;
voice[i].kon_delay = 0;
voice[i].env_mode = env_release;
voice[i].env = 0;
voice[i].t_envx_out = 0;
voice[i].hidden_env = 0;
}
reset();
}
void sDSP::reset() {
REG(flg) = 0xe0;
state.noise = 0x4000;
state.echo_hist_pos = 0;
state.every_other_sample = 1;
state.echo_offset = 0;
state.counter = 0;
phase_index = 0;
}
sDSP::sDSP() {
static_assert<sizeof(int) >= 32 / 8>(); //int >= 32-bits
static_assert<(int8_t)0x80 == -0x80>(); //8-bit sign extension
static_assert<(int16_t)0x8000 == -0x8000>(); //16-bit sign extension
static_assert<(uint16_t)0xffff0000 == 0>(); //16-bit unsigned clip
static_assert<(-1 >> 1) == -1>(); //arithmetic shift right
//-0x8000 <= n <= +0x7fff
assert(sclamp<16>(+0x8000) == +0x7fff);
assert(sclamp<16>(-0x8001) == -0x8000);
}
sDSP::~sDSP() {
}

166
bsnes/dsp/sdsp/sdsp.hpp Executable file
View File

@@ -0,0 +1,166 @@
class sDSP : public DSP {
public:
void enter();
uint8 read(uint8 addr);
void write(uint8 addr, uint8 data);
void power();
void reset();
sDSP();
~sDSP();
private:
//USE_STATE_MACHINE variable
unsigned phase_index;
//global registers
enum global_reg_t {
r_mvoll = 0x0c, r_mvolr = 0x1c,
r_evoll = 0x2c, r_evolr = 0x3c,
r_kon = 0x4c, r_koff = 0x5c,
r_flg = 0x6c, r_endx = 0x7c,
r_efb = 0x0d, r_pmon = 0x2d,
r_non = 0x3d, r_eon = 0x4d,
r_dir = 0x5d, r_esa = 0x6d,
r_edl = 0x7d, r_fir = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f
};
//voice registers
enum voice_reg_t {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09,
};
//internal envelope modes
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
//internal constants
enum { echo_hist_size = 8 };
enum { brr_buf_size = 12 };
enum { brr_block_size = 9 };
//global state
struct state_t {
uint8 regs[128];
modulo_array<int, echo_hist_size> echo_hist[2]; //echo history keeps most recent 8 samples
int echo_hist_pos;
bool every_other_sample; //toggles every sample
int kon; //KON value when last checked
int noise;
int counter;
int echo_offset; //offset from ESA in echo buffer
int echo_length; //number of bytes that echo_offset will stop at
//hidden registers also written to when main register is written to
int new_kon;
int endx_buf;
int envx_buf;
int outx_buf;
//temporary state between clocks
//read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
//read a few clocks ahead before used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_disabled;
//internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
//left/right sums
int t_main_out[2];
int t_echo_out[2];
int t_echo_in [2];
} state;
//voice state
struct voice_t {
modulo_array<int, brr_buf_size> buffer; //decoded samples
int buf_pos; //place in buffer where next samples will be decoded
int interp_pos; //relative fractional position in sample (0x1000 = 1.0)
int brr_addr; //address of current BRR block
int brr_offset; //current decoding offset in BRR block
int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
int kon_delay; //KON delay/current setup phase
env_mode_t env_mode;
int env; //current envelope level
int t_envx_out;
int hidden_env; //used by GAIN mode 7, very obscure quirk
} voice[8];
//gaussian
static const int16 gaussian_table[512];
int gaussian_interpolate(const voice_t &v);
//counter
enum { counter_range = 2048 * 5 * 3 }; //30720 (0x7800)
static const uint16 counter_rate[32];
static const uint16 counter_offset[32];
void counter_tick();
bool counter_poll(unsigned rate);
//envelope
void envelope_run(voice_t &v);
//brr
void brr_decode(voice_t &v);
//misc
void misc_27();
void misc_28();
void misc_29();
void misc_30();
//voice
void voice_output(voice_t &v, bool channel);
void voice_1 (voice_t &v);
void voice_2 (voice_t &v);
void voice_3 (voice_t &v);
void voice_3a(voice_t &v);
void voice_3b(voice_t &v);
void voice_3c(voice_t &v);
void voice_4 (voice_t &v);
void voice_5 (voice_t &v);
void voice_6 (voice_t &v);
void voice_7 (voice_t &v);
void voice_8 (voice_t &v);
void voice_9 (voice_t &v);
//echo
int calc_fir(int i, bool channel);
int echo_output(bool channel);
void echo_read(bool channel);
void echo_write(bool channel);
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
};

174
bsnes/dsp/sdsp/voice.cpp Executable file
View File

@@ -0,0 +1,174 @@
#ifdef SDSP_CPP
inline void sDSP::voice_output(voice_t &v, bool channel) {
//apply left/right volume
int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7;
//add to output total
state.t_main_out[channel] += amp;
state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]);
//optionally add to echo total
if(state.t_eon & v.vbit) {
state.t_echo_out[channel] += amp;
state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]);
}
}
void sDSP::voice_1(voice_t &v) {
state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2);
state.t_srcn = VREG(srcn);
}
void sDSP::voice_2(voice_t &v) {
//read sample pointer (ignored if not needed)
uint16 addr = state.t_dir_addr;
if(!v.kon_delay) addr += 2;
uint8 lo = memory::apuram[(uint16)(addr + 0)];
uint8 hi = memory::apuram[(uint16)(addr + 1)];
state.t_brr_next_addr = ((hi << 8) + lo);
state.t_adsr0 = VREG(adsr0);
//read pitch, spread over two clocks
state.t_pitch = VREG(pitchl);
}
void sDSP::voice_3(voice_t &v) {
voice_3a(v);
voice_3b(v);
voice_3c(v);
}
void sDSP::voice_3a(voice_t &v) {
state.t_pitch += (VREG(pitchh) & 0x3f) << 8;
}
void sDSP::voice_3b(voice_t &v) {
state.t_brr_byte = memory::apuram[(uint16)(v.brr_addr + v.brr_offset)];
state.t_brr_header = memory::apuram[(uint16)(v.brr_addr)];
}
void sDSP::voice_3c(voice_t &v) {
//pitch modulation using previous voice's output
if(state.t_pmon & v.vbit) {
state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10;
}
if(v.kon_delay) {
//get ready to start BRR decoding on next sample
if(v.kon_delay == 5) {
v.brr_addr = state.t_brr_next_addr;
v.brr_offset = 1;
v.buf_pos = 0;
state.t_brr_header = 0; //header is ignored on this sample
}
//envelope is never run during KON
v.env = 0;
v.hidden_env = 0;
//disable BRR decoding until last three samples
v.interp_pos = 0;
v.kon_delay--;
if(v.kon_delay & 3) v.interp_pos = 0x4000;
//pitch is never added during KON
state.t_pitch = 0;
}
//gaussian interpolation
int output = gaussian_interpolate(v);
//noise
if(state.t_non & v.vbit) {
output = (int16)(state.noise << 1);
}
//apply envelope
state.t_output = ((output * v.env) >> 11) & ~1;
v.t_envx_out = v.env >> 4;
//immediate silence due to end of sample or soft reset
if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) {
v.env_mode = env_release;
v.env = 0;
}
if(state.every_other_sample) {
//KOFF
if(state.t_koff & v.vbit) {
v.env_mode = env_release;
}
//KON
if(state.kon & v.vbit) {
v.kon_delay = 5;
v.env_mode = env_attack;
}
}
//run envelope for next sample
if(!v.kon_delay) envelope_run(v);
}
void sDSP::voice_4(voice_t &v) {
//decode BRR
state.t_looped = 0;
if(v.interp_pos >= 0x4000) {
brr_decode(v);
v.brr_offset += 2;
if(v.brr_offset >= 9) {
//start decoding next BRR block
v.brr_addr = (uint16)(v.brr_addr + 9);
if(state.t_brr_header & 1) {
v.brr_addr = state.t_brr_next_addr;
state.t_looped = v.vbit;
}
v.brr_offset = 1;
}
}
//apply pitch
v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch;
//keep from getting too far ahead (when using pitch modulation)
if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff;
//output left
voice_output(v, 0);
}
void sDSP::voice_5(voice_t &v) {
//output right
voice_output(v, 1);
//ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier
state.endx_buf = REG(endx) | state.t_looped;
//clear bit in ENDX if KON just began
if(v.kon_delay == 5) state.endx_buf &= ~v.vbit;
}
void sDSP::voice_6(voice_t &v) {
state.outx_buf = state.t_output >> 8;
}
void sDSP::voice_7(voice_t &v) {
//update ENDX
REG(endx) = (uint8)state.endx_buf;
state.envx_buf = v.t_envx_out;
}
void sDSP::voice_8(voice_t &v) {
//update OUTX
VREG(outx) = (uint8)state.outx_buf;
}
void sDSP::voice_9(voice_t &v) {
//update ENVX
VREG(envx) = (uint8)state.envx_buf;
}
#endif //ifdef SDSP_CPP