/* S-DSP emulator 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 namespace SNES { #define REG(n) state.regs[r_##n] #define VREG(n) state.regs[v.vidx + v_##n] #if !defined(DSP_STATE_MACHINE) #define phase_start() while(true) { #define phase(n) #define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp() #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= 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() { } };