diff --git a/higan/audio/audio.cpp b/higan/audio/audio.cpp index 223b8ae9..cc441d0b 100644 --- a/higan/audio/audio.cpp +++ b/higan/audio/audio.cpp @@ -7,7 +7,18 @@ Audio audio; auto Audio::reset() -> void { streams.reset(); - setReverbDelay(reverbDelay); + reverb.reset(); + reverb.resize(2); + for(auto c : range(2)) { + reverb[c].resize(7); + reverb[c][0].resize(1229); + reverb[c][1].resize(1559); + reverb[c][2].resize(1907); + reverb[c][3].resize(4057); + reverb[c][4].resize(8117); + reverb[c][5].resize(8311); + reverb[c][6].resize(9931); + } } auto Audio::setInterface(Interface* interface) -> void { @@ -27,16 +38,8 @@ auto Audio::setBalance(double balance) -> void { this->balance = balance; } -auto Audio::setReverbDelay(uint reverbDelay) -> void { - this->reverbDelay = reverbDelay; - reverbLeft.resize(frequency * reverbDelay / 1000.0); - reverbRight.resize(frequency * reverbDelay / 1000.0); - memory::fill(reverbLeft.data(), reverbLeft.size() * sizeof(int16)); - memory::fill(reverbRight.data(), reverbRight.size() * sizeof(int16)); -} - -auto Audio::setReverbLevel(double reverbLevel) -> void { - this->reverbLevel = reverbLevel; +auto Audio::setReverb(bool enabled) -> void { + this->reverbEnable = enabled; } auto Audio::createStream(uint channels, double frequency) -> shared_pointer { @@ -70,16 +73,21 @@ auto Audio::poll() -> void { int ileft = (left * 65535.0) - 32768.0; int iright = (right * 65535.0) - 32768.0; + if(reverbEnable) { + ileft *= 0.125; + for(auto n : range(7)) ileft += 0.125 * reverb[0][n].last(); + for(auto n : range(7)) reverb[0][n].write(ileft); + ileft *= 8.000; + + iright *= 0.125; + for(auto n : range(7)) iright += 0.125 * reverb[1][n].last(); + for(auto n : range(7)) reverb[1][n].write(iright); + iright *= 8.000; + } + ileft *= volume; iright *= volume; - if(reverbDelay) { - reverbLeft.append(ileft); - reverbRight.append(iright); - ileft += reverbLeft.takeLeft() * reverbLevel; - iright += reverbRight.takeLeft() * reverbLevel; - } - interface->audioSample(sclamp<16>(ileft), sclamp<16>(iright)); } } diff --git a/higan/audio/audio.hpp b/higan/audio/audio.hpp index ccb33d9b..eb22049c 100644 --- a/higan/audio/audio.hpp +++ b/higan/audio/audio.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + namespace Emulator { struct Interface; @@ -13,8 +16,7 @@ struct Audio { auto setFrequency(double frequency) -> void; auto setVolume(double volume) -> void; auto setBalance(double balance) -> void; - auto setReverbDelay(uint milliseconds) -> void; - auto setReverbLevel(double level) -> void; + auto setReverb(bool enabled) -> void; auto createStream(uint channels, double frequency) -> shared_pointer; @@ -26,10 +28,9 @@ private: double frequency = 0.0; double volume = 1.0; double balance = 0.0; - uint reverbDelay = 0; //0 = disabled - double reverbLevel = 0.0; - vector reverbLeft; - vector reverbRight; + + bool reverbEnable = false; + vector>> reverb; friend class Stream; }; @@ -55,23 +56,9 @@ private: double outputFrequency = 0.0; double cutoffFrequency = 0.0; - vector taps; - - uint decimationRate = 0; - uint decimationOffset = 0; - - vector> input; - uint inputOffset = 0; - - double resamplerFrequency = 0.0; - double resamplerFraction = 0.0; - double resamplerStep = 0.0; - vector> queue; - - vector> output; - uint outputs = 0; - uint outputReadOffset = 0; - uint outputWriteOffset = 0; + const uint iirPasses = 3; //6th-order filter + vector> iir; + vector resampler; friend class Audio; }; diff --git a/higan/audio/stream.cpp b/higan/audio/stream.cpp index e5f20aed..3363126f 100644 --- a/higan/audio/stream.cpp +++ b/higan/audio/stream.cpp @@ -1,137 +1,49 @@ -//Emulator::Stream implements advanced audio resampling -//First, a lowpass sinc filter is used (with a Blackman window to reduce rippling) in order to remove aliasing -//Second, a decimator is used to reduce the CPU overhead of the sinc function -//Finally, a hermite resampler is used to resample to the exact requested output frequency -//Note: when the cutoff frequency is >= 0.5; only the hermite resampler is used - Stream::Stream(uint channels, double inputFrequency) : channels(channels), inputFrequency(inputFrequency) { } auto Stream::reset() -> void { - taps.reset(); - input.reset(); - queue.reset(); - output.reset(); + iir.reset(); + resampler.reset(); } auto Stream::setFrequency(double outputFrequency_) -> void { reset(); - const double pi = 3.141592; - auto sinc = [&](double x) -> double { - if(x == 0) return 1; - return sin(pi * x) / (pi * x); - }; - outputFrequency = outputFrequency_; cutoffFrequency = outputFrequency / inputFrequency; - if(cutoffFrequency < 0.5) { - double transitionBandwidth = 0.008; //lower = higher quality; more taps (slower) - taps.resize((uint)ceil(4.0 / transitionBandwidth) | 1); + iir.resize(channels); - double sum = 0.0; - for(uint t : range(taps)) { - //sinc filter - double s = sinc(2.0 * cutoffFrequency * (t - (taps.size() - 1) / 2.0)); - - //blackman window - double b = 0.42 - 0.5 * cos(2.0 * pi * t / (taps.size() - 1)) + 0.08 * cos(4.0 * pi * t / (taps.size() - 1)); - - taps[t] = s * b; - sum += taps[t]; + if(cutoffFrequency <= 0.5) { + for(auto c : range(channels)) { + iir[c].resize(iirPasses); + for(auto p : range(iirPasses)) { + //attenuates frequencies that exceed the limits of human hearing (20KHz) to prevent aliasing + iir[c][p].reset(DSP::IIR::Biquad::Type::LowPass, 20000.0 / inputFrequency); + } } - - //normalize so that the sum of all coefficients is 1.0 - for(auto& tap : taps) tap /= sum; - } else { - taps.resize(1); - taps[0] = 1.0; } - decimationRate = max(1, (uint)floor(inputFrequency / outputFrequency)); - decimationOffset = 0; - - input.resize(channels); - for(auto c : range(channels)) input[c].resize(taps.size() * 2); - inputOffset = 0; - - resamplerFrequency = inputFrequency / decimationRate; - resamplerFraction = 0.0; - resamplerStep = resamplerFrequency / outputFrequency; - queue.resize(channels); - for(auto c : range(channels)) queue[c].resize(4); - - output.resize(channels); - outputs = inputFrequency * 0.02; - for(auto c : range(channels)) output[c].resize(outputs); - outputReadOffset = 0; - outputWriteOffset = 0; + resampler.resize(channels); + for(auto c : range(channels)) { + resampler[c].reset(inputFrequency, outputFrequency); + } } auto Stream::pending() const -> bool { - return outputReadOffset != outputWriteOffset; + return resampler && resampler[0].pending(); } auto Stream::read(double* samples) -> void { - for(auto c : range(channels)) { - samples[c] = output[c][outputReadOffset]; - } + for(auto c : range(channels)) samples[c] = resampler[c].read(); if(channels == 1) samples[1] = samples[0]; //monaural->stereo hack - if(++outputReadOffset >= outputs) outputReadOffset = 0; } auto Stream::write(int16* samples) -> void { - inputOffset = !inputOffset ? taps.size() - 1 : inputOffset - 1; for(auto c : range(channels)) { - auto sample = (samples[c] + 32768.0) / 65535.0; //normalize - input[c][inputOffset] = input[c][inputOffset + taps.size()] = sample; + double sample = (samples[c] + 32768.0) / 65535.0; //normalize + for(auto& p : iir[c]) sample = p.process(sample); + resampler[c].write(sample); } - if(++decimationOffset >= decimationRate) { - decimationOffset = 0; - - for(auto c : range(channels)) { - double sample = 0.0; - for(auto t : range(taps)) sample += input[c][inputOffset + t] * taps[t]; - - auto& q = queue[c]; - q[0] = q[1]; - q[1] = q[2]; - q[2] = q[3]; - q[3] = sample; - } - - //4-tap hermite - auto& mu = resamplerFraction; - while(mu <= 1.0) { - for(auto c : range(channels)) { - auto& q = queue[c]; - - const double tension = 0.0; //-1 = low, 0 = normal, +1 = high - const double bias = 0.0; //-1 = left, 0 = even, +1 = right - - double mu1 = mu; - double mu2 = mu * mu; - double mu3 = mu * mu * mu; - - double m0 = (q[1] - q[0]) * (1.0 + bias) * (1.0 - tension) / 2.0 - + (q[2] - q[1]) * (1.0 - bias) * (1.0 - tension) / 2.0; - double m1 = (q[2] - q[1]) * (1.0 + bias) * (1.0 - tension) / 2.0 - + (q[3] - q[2]) * (1.0 - bias) * (1.0 - tension) / 2.0; - - double a0 = +2 * mu3 - 3 * mu2 + 1; - double a1 = mu3 - 2 * mu2 + mu1; - double a2 = mu3 - mu2; - double a3 = -2 * mu3 + 3 * mu2; - - output[c][outputWriteOffset] = (a0 * q[1]) + (a1 * m0) + (a2 * m1) + (a3 * q[2]); - } - - if(++outputWriteOffset >= outputs) outputWriteOffset = 0; - mu += resamplerStep; - audio.poll(); - } - - mu -= 1.0; - } + audio.poll(); } diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 09704f2f..be02c33e 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -9,7 +9,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "098.12"; + static const string Version = "098.13"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/processor/gsu/gsu.cpp b/higan/processor/gsu/gsu.cpp index 03bbb0d3..6210ba79 100644 --- a/higan/processor/gsu/gsu.cpp +++ b/higan/processor/gsu/gsu.cpp @@ -8,7 +8,7 @@ namespace Processor { #include "instructions.cpp" -#include "table.cpp" +#include "switch.cpp" #include "serialization.cpp" #include "disassembler.cpp" diff --git a/higan/processor/gsu/gsu.hpp b/higan/processor/gsu/gsu.hpp index 3c517b78..47e7601c 100644 --- a/higan/processor/gsu/gsu.hpp +++ b/higan/processor/gsu/gsu.hpp @@ -5,7 +5,7 @@ namespace Processor { struct GSU { #include "registers.hpp" - virtual auto step(unsigned clocks) -> void = 0; + virtual auto step(uint clocks) -> void = 0; virtual auto stop() -> void = 0; virtual auto color(uint8 source) -> uint8 = 0; @@ -28,91 +28,49 @@ struct GSU { auto reset() -> void; //instructions.cpp - template auto op_adc_i(); - template auto op_adc_r(); - template auto op_add_i(); - template auto op_add_r(); + auto op_add_adc(uint n); auto op_alt1(); auto op_alt2(); auto op_alt3(); - template auto op_and_i(); - template auto op_and_r(); - auto op_asr(); - auto op_bge(); - auto op_bcc(); - auto op_bcs(); - auto op_beq(); - template auto op_bic_i(); - template auto op_bic_r(); - auto op_blt(); - auto op_bmi(); - auto op_bne(); - auto op_bpl(); - auto op_bra(); - auto op_bvc(); - auto op_bvs(); + auto op_and_bic(uint n); + auto op_asr_div2(); + auto op_branch(bool c); auto op_cache(); - auto op_cmode(); - template auto op_cmp_r(); - auto op_color(); - template auto op_dec_r(); - auto op_div2(); - auto op_fmult(); - template auto op_from_r(); + auto op_color_cmode(); + auto op_dec(uint n); + auto op_fmult_lmult(); + auto op_from_moves(uint n); auto op_getb(); - auto op_getbl(); - auto op_getbh(); - auto op_getbs(); - auto op_getc(); + auto op_getc_ramb_romb(); auto op_hib(); - template auto op_ibt_r(); - template auto op_inc_r(); - template auto op_iwt_r(); - template auto op_jmp_r(); - template auto op_ldb_ir(); - template auto op_ldw_ir(); - template auto op_link(); - template auto op_ljmp_r(); - template auto op_lm_r(); - template auto op_lms_r(); - auto op_lmult(); + auto op_ibt_lms_sms(uint n); + auto op_inc(uint n); + auto op_iwt_lm_sm(uint n); + auto op_jmp_ljmp(uint n); + auto op_link(uint n); + auto op_load(uint n); auto op_lob(); auto op_loop(); auto op_lsr(); auto op_merge(); - template auto op_mult_i(); - template auto op_mult_r(); + auto op_mult_umult(uint n); auto op_nop(); auto op_not(); - template auto op_or_i(); - template auto op_or_r(); - auto op_plot(); - auto op_ramb(); + auto op_or_xor(uint n); + auto op_plot_rpix(); auto op_rol(); - auto op_romb(); auto op_ror(); - auto op_rpix(); - template auto op_sbc_r(); auto op_sbk(); auto op_sex(); - template auto op_sm_r(); - template auto op_sms_r(); - template auto op_stb_ir(); + auto op_store(uint n); auto op_stop(); - template auto op_stw_ir(); - template auto op_sub_i(); - template auto op_sub_r(); + auto op_sub_sbc_cmp(uint n); auto op_swap(); - template auto op_to_r(); - template auto op_umult_i(); - template auto op_umult_r(); - template auto op_with_r(); - template auto op_xor_i(); - template auto op_xor_r(); + auto op_to_move(uint n); + auto op_with(uint n); - //table.cpp - auto (GSU::*opcode_table[1024])() -> void; - auto initialize_opcode_table() -> void; + //switch.cpp + auto instruction(uint8 opcode) -> void; //serialization.cpp auto serialize(serializer&) -> void; diff --git a/higan/processor/gsu/instructions.cpp b/higan/processor/gsu/instructions.cpp index 4a525ea8..caa5ba8d 100644 --- a/higan/processor/gsu/instructions.cpp +++ b/higan/processor/gsu/instructions.cpp @@ -4,9 +4,8 @@ auto GSU::op_stop() { regs.sfr.irq = 1; stop(); } - regs.sfr.g = 0; - regs.pipeline = 0x01; + regs.pipeline = 0x01; //nop regs.reset(); } @@ -44,75 +43,25 @@ auto GSU::op_rol() { } //$05 bra e -auto GSU::op_bra() { - regs.r[15] += (int8)pipe(); -} - //$06 blt e -auto GSU::op_blt() { - int e = (int8)pipe(); - if((regs.sfr.s ^ regs.sfr.ov) == 0) regs.r[15] += e; -} - //$07 bge e -auto GSU::op_bge() { - int e = (int8)pipe(); - if((regs.sfr.s ^ regs.sfr.ov) == 1) regs.r[15] += e; -} - //$08 bne e -auto GSU::op_bne() { - int e = (int8)pipe(); - if(regs.sfr.z == 0) regs.r[15] += e; -} - //$09 beq e -auto GSU::op_beq() { - int e = (int8)pipe(); - if(regs.sfr.z == 1) regs.r[15] += e; -} - //$0a bpl e -auto GSU::op_bpl() { - int e = (int8)pipe(); - if(regs.sfr.s == 0) regs.r[15] += e; -} - //$0b bmi e -auto GSU::op_bmi() { - int e = (int8)pipe(); - if(regs.sfr.s == 1) regs.r[15] += e; -} - //$0c bcc e -auto GSU::op_bcc() { - int e = (int8)pipe(); - if(regs.sfr.cy == 0) regs.r[15] += e; -} - //$0d bcs e -auto GSU::op_bcs() { - int e = (int8)pipe(); - if(regs.sfr.cy == 1) regs.r[15] += e; -} - //$0e bvc e -auto GSU::op_bvc() { - int e = (int8)pipe(); - if(regs.sfr.ov == 0) regs.r[15] += e; -} - //$0f bvs e -auto GSU::op_bvs() { - int e = (int8)pipe(); - if(regs.sfr.ov == 1) regs.r[15] += e; +auto GSU::op_branch(bool c) { + auto d = (int8)pipe(); + if(c) regs.r[15] += d; } -//$10-1f(b0): to rN -//$10-1f(b1): move rN -template -auto GSU::op_to_r() { - if(regs.sfr.b == 0) { +//$10-1f(b0) to rN +//$10-1f(b1) move rN +auto GSU::op_to_move(uint n) { + if(!regs.sfr.b) { regs.dreg = n; } else { regs.r[n] = regs.sr(); @@ -120,28 +69,19 @@ auto GSU::op_to_r() { } } -//$20-2f: with rN -template -auto GSU::op_with_r() { +//$20-2f with rN +auto GSU::op_with(uint n) { regs.sreg = n; regs.dreg = n; regs.sfr.b = 1; } -//$30-3b(alt0): stw (rN) -template -auto GSU::op_stw_ir() { - regs.ramaddr = regs.r[n]; - rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0); - rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); - regs.reset(); -} - -//$30-3b(alt1): stb (rN) -template -auto GSU::op_stb_ir() { +//$30-3b(alt0) stw (rN) +//$30-3b(alt1) stb (rN) +auto GSU::op_store(uint n) { regs.ramaddr = regs.r[n]; rambuffer_write(regs.ramaddr, regs.sr()); + if(!regs.sfr.alt1) rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); regs.reset(); } @@ -173,41 +113,30 @@ auto GSU::op_alt3() { regs.sfr.alt2 = 1; } -//$40-4b(alt0): ldw (rN) -template -auto GSU::op_ldw_ir() { - regs.ramaddr = regs.r[n]; - uint16_t data; - data = rambuffer_read(regs.ramaddr ^ 0) << 0; - data |= rambuffer_read(regs.ramaddr ^ 1) << 8; - regs.dr() = data; - regs.reset(); -} - -//$40-4b(alt1): ldb (rN) -template -auto GSU::op_ldb_ir() { +//$40-4b(alt0) ldw (rN) +//$40-4b(alt1) ldb (rN) +auto GSU::op_load(uint n) { regs.ramaddr = regs.r[n]; regs.dr() = rambuffer_read(regs.ramaddr); + if(!regs.sfr.alt1) regs.dr() |= rambuffer_read(regs.ramaddr ^ 1) << 8; regs.reset(); } -//$4c(alt0): plot -auto GSU::op_plot() { - plot(regs.r[1], regs.r[2]); - regs.r[1]++; +//$4c(alt0) plot +//$4c(alt1) rpix +auto GSU::op_plot_rpix() { + if(!regs.sfr.alt1) { + plot(regs.r[1], regs.r[2]); + regs.r[1]++; + } else { + regs.dr() = rpix(regs.r[1], regs.r[2]); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + } regs.reset(); } -//$4c(alt1): rpix -auto GSU::op_rpix() { - regs.dr() = rpix(regs.r[1], regs.r[2]); - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$4d: swap +//$4d swap auto GSU::op_swap() { regs.dr() = (regs.sr() >> 8) | (regs.sr() << 8); regs.sfr.s = (regs.dr() & 0x8000); @@ -215,19 +144,18 @@ auto GSU::op_swap() { regs.reset(); } -//$4e(alt0): color -auto GSU::op_color() { - regs.colr = color(regs.sr()); +//$4e(alt0) color +//$4e(alt1) cmode +auto GSU::op_color_cmode() { + if(!regs.sfr.alt1) { + regs.colr = color(regs.sr()); + } else { + regs.por = regs.sr(); + } regs.reset(); } -//$4e(alt1): cmode -auto GSU::op_cmode() { - regs.por = regs.sr(); - regs.reset(); -} - -//$4f: not +//$4f not auto GSU::op_not() { regs.dr() = ~regs.sr(); regs.sfr.s = (regs.dr() & 0x8000); @@ -235,102 +163,37 @@ auto GSU::op_not() { regs.reset(); } -//$50-5f(alt0): add rN -template -auto GSU::op_add_r() { - int r = regs.sr() + regs.r[n]; - regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000; - regs.sfr.s = (r & 0x8000); - regs.sfr.cy = (r >= 0x10000); - regs.sfr.z = ((uint16_t)r == 0); - regs.dr() = r; - regs.reset(); -} - -//$50-5f(alt1): adc rN -template -auto GSU::op_adc_r() { - int r = regs.sr() + regs.r[n] + regs.sfr.cy; - regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000; - regs.sfr.s = (r & 0x8000); - regs.sfr.cy = (r >= 0x10000); - regs.sfr.z = ((uint16_t)r == 0); - regs.dr() = r; - regs.reset(); -} - -//$50-5f(alt2): add #N -template -auto GSU::op_add_i() { - int r = regs.sr() + n; +//$50-5f(alt0) add rN +//$50-5f(alt1) adc rN +//$50-5f(alt2) add #N +//$50-5f(alt3) adc #N +auto GSU::op_add_adc(uint n) { + if(!regs.sfr.alt2) n = regs.r[n]; + int r = regs.sr() + n + (regs.sfr.alt1 ? regs.sfr.cy : 0); regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000; regs.sfr.s = (r & 0x8000); regs.sfr.cy = (r >= 0x10000); - regs.sfr.z = ((uint16_t)r == 0); + regs.sfr.z = ((uint16)r == 0); regs.dr() = r; regs.reset(); } -//$50-5f(alt3): adc #N -template -auto GSU::op_adc_i() { - int r = regs.sr() + n + regs.sfr.cy; - regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000; - regs.sfr.s = (r & 0x8000); - regs.sfr.cy = (r >= 0x10000); - regs.sfr.z = ((uint16_t)r == 0); - regs.dr() = r; - regs.reset(); -} - -//$60-6f(alt0): sub rN -template -auto GSU::op_sub_r() { - int r = regs.sr() - regs.r[n]; - regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; - regs.sfr.s = (r & 0x8000); - regs.sfr.cy = (r >= 0); - regs.sfr.z = ((uint16_t)r == 0); - regs.dr() = r; - regs.reset(); -} - -//$60-6f(alt1): sbc rN -template -auto GSU::op_sbc_r() { - int r = regs.sr() - regs.r[n] - !regs.sfr.cy; - regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; - regs.sfr.s = (r & 0x8000); - regs.sfr.cy = (r >= 0); - regs.sfr.z = ((uint16_t)r == 0); - regs.dr() = r; - regs.reset(); -} - -//$60-6f(alt2): sub #N -template -auto GSU::op_sub_i() { - int r = regs.sr() - n; +//$60-6f(alt0) sub rN +//$60-6f(alt1) sbc rN +//$60-6f(alt2) sub #N +//$60-6f(alt3) cmp rN +auto GSU::op_sub_sbc_cmp(uint n) { + if(!regs.sfr.alt2 || regs.sfr.alt1) n = regs.r[n]; + int r = regs.sr() - n - (!regs.sfr.alt2 && regs.sfr.alt1 ? !regs.sfr.cy : 0); regs.sfr.ov = (regs.sr() ^ n) & (regs.sr() ^ r) & 0x8000; regs.sfr.s = (r & 0x8000); regs.sfr.cy = (r >= 0); - regs.sfr.z = ((uint16_t)r == 0); - regs.dr() = r; + regs.sfr.z = ((uint16)r == 0); + if(!regs.sfr.alt2 || !regs.sfr.alt1) regs.dr() = r; regs.reset(); } -//$60-6f(alt3): cmp rN -template -auto GSU::op_cmp_r() { - int r = regs.sr() - regs.r[n]; - regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; - regs.sfr.s = (r & 0x8000); - regs.sfr.cy = (r >= 0); - regs.sfr.z = ((uint16_t)r == 0); - regs.reset(); -} - -//$70: merge +//$70 merge auto GSU::op_merge() { regs.dr() = (regs.r[7] & 0xff00) | (regs.r[8] >> 8); regs.sfr.ov = (regs.dr() & 0xc0c0); @@ -340,97 +203,45 @@ auto GSU::op_merge() { regs.reset(); } -//$71-7f(alt0): and rN -template -auto GSU::op_and_r() { - regs.dr() = regs.sr() & regs.r[n]; +//$71-7f(alt0) and rN +//$71-7f(alt1) bic rN +//$71-7f(alt2) and #N +//$71-7f(alt3) bic #N +auto GSU::op_and_bic(uint n) { + if(!regs.sfr.alt2) n = regs.r[n]; + regs.dr() = regs.sr() & (regs.sfr.alt1 ? ~n : n); regs.sfr.s = (regs.dr() & 0x8000); regs.sfr.z = (regs.dr() == 0); regs.reset(); } -//$71-7f(alt1): bic rN -template -auto GSU::op_bic_r() { - regs.dr() = regs.sr() & ~regs.r[n]; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$71-7f(alt2): and #N -template -auto GSU::op_and_i() { - regs.dr() = regs.sr() & n; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$71-7f(alt3): bic #N -template -auto GSU::op_bic_i() { - regs.dr() = regs.sr() & ~n; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$80-8f(alt0): mult rN -template -auto GSU::op_mult_r() { - regs.dr() = (int8)regs.sr() * (int8)regs.r[n]; +//$80-8f(alt0) mult rN +//$80-8f(alt1) umult rN +//$80-8f(alt2) mult #N +//$80-8f(alt3) umult #N +auto GSU::op_mult_umult(uint n) { + if(!regs.sfr.alt2) n = regs.r[n]; + regs.dr() = (!regs.sfr.alt1 ? ((int8)regs.sr() * (int8)n) : ((uint8)regs.sr() * (uint8)n)); regs.sfr.s = (regs.dr() & 0x8000); regs.sfr.z = (regs.dr() == 0); regs.reset(); if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2); } -//$80-8f(alt1): umult rN -template -auto GSU::op_umult_r() { - regs.dr() = (uint8)regs.sr() * (uint8)regs.r[n]; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); - if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2); -} - -//$80-8f(alt2): mult #N -template -auto GSU::op_mult_i() { - regs.dr() = (int8)regs.sr() * (int8)n; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); - if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2); -} - -//$80-8f(alt3): umult #N -template -auto GSU::op_umult_i() { - regs.dr() = (uint8)regs.sr() * (uint8)n; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); - if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2); -} - -//$90: sbk +//$90 sbk auto GSU::op_sbk() { rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0); rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); regs.reset(); } -//$91-94: link #N -template -auto GSU::op_link() { +//$91-94 link #N +auto GSU::op_link(uint n) { regs.r[11] = regs.r[15] + n; regs.reset(); } -//$95: sex +//$95 sex auto GSU::op_sex() { regs.dr() = (int8)regs.sr(); regs.sfr.s = (regs.dr() & 0x8000); @@ -438,25 +249,17 @@ auto GSU::op_sex() { regs.reset(); } -//$96(alt0): asr -auto GSU::op_asr() { +//$96(alt0) asr +//$96(alt1) div2 +auto GSU::op_asr_div2() { regs.sfr.cy = (regs.sr() & 1); - regs.dr() = (int16_t)regs.sr() >> 1; + regs.dr() = ((int16)regs.sr() >> 1) + (regs.sfr.alt1 ? ((regs.sr() + 1) >> 16) : 0); regs.sfr.s = (regs.dr() & 0x8000); regs.sfr.z = (regs.dr() == 0); regs.reset(); } -//$96(alt1): div2 -auto GSU::op_div2() { - regs.sfr.cy = (regs.sr() & 1); - regs.dr() = ((int16_t)regs.sr() >> 1) + ((regs.sr() + 1) >> 16); - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$97: ror +//$97 ror auto GSU::op_ror() { bool carry = (regs.sr() & 1); regs.dr() = (regs.sfr.cy << 15) | (regs.sr() >> 1); @@ -466,24 +269,21 @@ auto GSU::op_ror() { regs.reset(); } -//$98-9d(alt0): jmp rN -template -auto GSU::op_jmp_r() { - regs.r[15] = regs.r[n]; +//$98-9d(alt0) jmp rN +//$98-9d(alt1) ljmp rN +auto GSU::op_jmp_ljmp(uint n) { + if(!regs.sfr.alt1) { + regs.r[15] = regs.r[n]; + } else { + regs.pbr = regs.r[n] & 0x7f; + regs.r[15] = regs.sr(); + regs.cbr = regs.r[15] & 0xfff0; + cache_flush(); + } regs.reset(); } -//$98-9d(alt1): ljmp rN -template -auto GSU::op_ljmp_r() { - regs.pbr = regs.r[n] & 0x7f; - regs.r[15] = regs.sr(); - regs.cbr = regs.r[15] & 0xfff0; - cache_flush(); - regs.reset(); -} - -//$9e: lob +//$9e lob auto GSU::op_lob() { regs.dr() = regs.sr() & 0xff; regs.sfr.s = (regs.dr() & 0x80); @@ -491,9 +291,11 @@ auto GSU::op_lob() { regs.reset(); } -//$9f(alt0): fmult -auto GSU::op_fmult() { - uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6]; +//$9f(alt0) fmult +//$9f(alt1) lmult +auto GSU::op_fmult_lmult() { + uint32 result = (int16)regs.sr() * (int16)regs.r[6]; + if(regs.sfr.alt1) regs.r[4] = result; regs.dr() = result >> 16; regs.sfr.s = (regs.dr() & 0x8000); regs.sfr.cy = (result & 0x8000); @@ -502,50 +304,28 @@ auto GSU::op_fmult() { step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2)); } -//$9f(alt1): lmult -auto GSU::op_lmult() { - uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6]; - regs.r[4] = result; - regs.dr() = result >> 16; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.cy = (result & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); - step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2)); -} - -//$a0-af(alt0): ibt rN,#pp -template -auto GSU::op_ibt_r() { - regs.r[n] = (int8)pipe(); +//$a0-af(alt0) ibt rN,#pp +//$a0-af(alt1) lms rN,(yy) +//$a0-af(alt2) sms (yy),rN +auto GSU::op_ibt_lms_sms(uint n) { + if(regs.sfr.alt1) { + regs.ramaddr = pipe() << 1; + uint8 lo = rambuffer_read(regs.ramaddr ^ 0) << 0; + regs.r[n] = rambuffer_read(regs.ramaddr ^ 1) << 8 | lo; + } else if(regs.sfr.alt2) { + regs.ramaddr = pipe() << 1; + rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); + } else { + regs.r[n] = (int8)pipe(); + } regs.reset(); } -//$a0-af(alt1): lms rN,(yy) -template -auto GSU::op_lms_r() { - regs.ramaddr = pipe() << 1; - uint16_t data; - data = rambuffer_read(regs.ramaddr ^ 0) << 0; - data |= rambuffer_read(regs.ramaddr ^ 1) << 8; - regs.r[n] = data; - regs.reset(); -} - -//$a0-af(alt2): sms (yy),rN -template -auto GSU::op_sms_r() { - regs.ramaddr = pipe() << 1; - rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); - rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); - regs.reset(); -} - -//$b0-bf(b0): from rN -//$b0-bf(b1): moves rN -template -auto GSU::op_from_r() { - if(regs.sfr.b == 0) { +//$b0-bf(b0) from rN +//$b0-bf(b1) moves rN +auto GSU::op_from_moves(uint n) { + if(!regs.sfr.b) { regs.sreg = n; } else { regs.dr() = regs.r[n]; @@ -556,7 +336,7 @@ auto GSU::op_from_r() { } } -//$c0: hib +//$c0 hib auto GSU::op_hib() { regs.dr() = regs.sr() >> 8; regs.sfr.s = (regs.dr() & 0x80); @@ -564,132 +344,81 @@ auto GSU::op_hib() { regs.reset(); } -//$c1-cf(alt0): or rN -template -auto GSU::op_or_r() { - regs.dr() = regs.sr() | regs.r[n]; +//$c1-cf(alt0) or rN +//$c1-cf(alt1) xor rN +//$c1-cf(alt2) or #N +//$c1-cf(alt3) xor #N +auto GSU::op_or_xor(uint n) { + if(!regs.sfr.alt2) n = regs.r[n]; + regs.dr() = (!regs.sfr.alt1 ? (regs.sr() | n) : (regs.sr() ^ n)); regs.sfr.s = (regs.dr() & 0x8000); regs.sfr.z = (regs.dr() == 0); regs.reset(); } -//$c1-cf(alt1): xor rN -template -auto GSU::op_xor_r() { - regs.dr() = regs.sr() ^ regs.r[n]; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$c1-cf(alt2): or #N -template -auto GSU::op_or_i() { - regs.dr() = regs.sr() | n; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$c1-cf(alt3): xor #N -template -auto GSU::op_xor_i() { - regs.dr() = regs.sr() ^ n; - regs.sfr.s = (regs.dr() & 0x8000); - regs.sfr.z = (regs.dr() == 0); - regs.reset(); -} - -//$d0-de: inc rN -template -auto GSU::op_inc_r() { +//$d0-de inc rN +auto GSU::op_inc(uint n) { regs.r[n]++; regs.sfr.s = (regs.r[n] & 0x8000); regs.sfr.z = (regs.r[n] == 0); regs.reset(); } -//$df(alt0): getc -auto GSU::op_getc() { - regs.colr = color(rombuffer_read()); +//$df(alt0) getc +//$df(alt2) ramb +//$df(alt3) romb +auto GSU::op_getc_ramb_romb() { + if(!regs.sfr.alt2) { + regs.colr = color(rombuffer_read()); + } else if(!regs.sfr.alt1) { + rambuffer_sync(); + regs.rambr = regs.sr() & 0x01; + } else { + rombuffer_sync(); + regs.rombr = regs.sr() & 0x7f; + } regs.reset(); } -//$df(alt2): ramb -auto GSU::op_ramb() { - rambuffer_sync(); - regs.rambr = regs.sr() & 0x01; - regs.reset(); -} - -//$df(alt3): romb -auto GSU::op_romb() { - rombuffer_sync(); - regs.rombr = regs.sr() & 0x7f; - regs.reset(); -} - -//$e0-ee: dec rN -template -auto GSU::op_dec_r() { +//$e0-ee dec rN +auto GSU::op_dec(uint n) { regs.r[n]--; regs.sfr.s = (regs.r[n] & 0x8000); regs.sfr.z = (regs.r[n] == 0); regs.reset(); } -//$ef(alt0): getb +//$ef(alt0) getb +//$ef(alt1) getbh +//$ef(alt2) getbl +//$ef(alt3) getbs auto GSU::op_getb() { - regs.dr() = rombuffer_read(); + switch(regs.sfr.alt2 << 1 | regs.sfr.alt1 << 0) { + case 0: regs.dr() = rombuffer_read(); break; + case 1: regs.dr() = rombuffer_read() << 8 | (uint8)regs.sr(); break; + case 2: regs.dr() = (regs.sr() & 0xff00) | rombuffer_read(); break; + case 3: regs.dr() = (int8)rombuffer_read(); break; + } regs.reset(); } -//$ef(alt1): getbh -auto GSU::op_getbh() { - regs.dr() = (rombuffer_read() << 8) | (regs.sr() & 0x00ff); - regs.reset(); -} - -//$ef(alt2): getbl -auto GSU::op_getbl() { - regs.dr() = (regs.sr() & 0xff00) | (rombuffer_read() << 0); - regs.reset(); -} - -//$ef(alt3): getbs -auto GSU::op_getbs() { - regs.dr() = (int8)rombuffer_read(); - regs.reset(); -} - -//$f0-ff(alt0): iwt rN,#xx -template -auto GSU::op_iwt_r() { - uint16_t data; - data = pipe() << 0; - data |= pipe() << 8; - regs.r[n] = data; - regs.reset(); -} - -//$f0-ff(alt1): lm rN,(xx) -template -auto GSU::op_lm_r() { - regs.ramaddr = pipe() << 0; - regs.ramaddr |= pipe() << 8; - uint16_t data; - data = rambuffer_read(regs.ramaddr ^ 0) << 0; - data |= rambuffer_read(regs.ramaddr ^ 1) << 8; - regs.r[n] = data; - regs.reset(); -} - -//$f0-ff(alt2): sm (xx),rN -template -auto GSU::op_sm_r() { - regs.ramaddr = pipe() << 0; - regs.ramaddr |= pipe() << 8; - rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); - rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); +//$f0-ff(alt0) iwt rN,#xx +//$f0-ff(alt1) lm rN,(xx) +//$f0-ff(alt2) sm (xx),rN +auto GSU::op_iwt_lm_sm(uint n) { + if(regs.sfr.alt1) { + regs.ramaddr = pipe() << 0; + regs.ramaddr |= pipe() << 8; + uint8 lo = rambuffer_read(regs.ramaddr ^ 0) << 0; + regs.r[n] = rambuffer_read(regs.ramaddr ^ 1) << 8 | lo; + } else if(regs.sfr.alt2) { + regs.ramaddr = pipe() << 0; + regs.ramaddr |= pipe() << 8; + rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); + } else { + uint8 lo = pipe(); + regs.r[n] = pipe() << 8 | lo; + } regs.reset(); } diff --git a/higan/processor/gsu/switch.cpp b/higan/processor/gsu/switch.cpp new file mode 100644 index 00000000..7aba9a5e --- /dev/null +++ b/higan/processor/gsu/switch.cpp @@ -0,0 +1,94 @@ +auto GSU::instruction(uint8 opcode) -> void { + #define op(id, name, ...) \ + case id: return op_##name(__VA_ARGS__); \ + + #define op4(id, name) \ + case id+ 0: return op_##name((uint4)opcode); \ + case id+ 1: return op_##name((uint4)opcode); \ + case id+ 2: return op_##name((uint4)opcode); \ + case id+ 3: return op_##name((uint4)opcode); \ + + #define op6(id, name) \ + op4(id, name) \ + case id+ 4: return op_##name((uint4)opcode); \ + case id+ 5: return op_##name((uint4)opcode); \ + + #define op12(id, name) \ + op6(id, name) \ + case id+ 6: return op_##name((uint4)opcode); \ + case id+ 7: return op_##name((uint4)opcode); \ + case id+ 8: return op_##name((uint4)opcode); \ + case id+ 9: return op_##name((uint4)opcode); \ + case id+10: return op_##name((uint4)opcode); \ + case id+11: return op_##name((uint4)opcode); \ + + #define op15(id, name) \ + op12(id, name) \ + case id+12: return op_##name((uint4)opcode); \ + case id+13: return op_##name((uint4)opcode); \ + case id+14: return op_##name((uint4)opcode); \ + + #define op16(id, name) \ + op15(id, name) \ + case id+15: return op_##name((uint4)opcode); \ + + switch(opcode) { + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, branch, 1) //bra + op (0x06, branch, (regs.sfr.s ^ regs.sfr.ov) == 0) //blt + op (0x07, branch, (regs.sfr.s ^ regs.sfr.ov) == 1) //bge + op (0x08, branch, regs.sfr.z == 0) //bne + op (0x09, branch, regs.sfr.z == 1) //beq + op (0x0a, branch, regs.sfr.s == 0) //bpl + op (0x0b, branch, regs.sfr.s == 1) //bmi + op (0x0c, branch, regs.sfr.cy == 0) //bcc + op (0x0d, branch, regs.sfr.cy == 1) //bcs + op (0x0e, branch, regs.sfr.ov == 0) //bvc + op (0x0f, branch, regs.sfr.ov == 1) //bvs + op16(0x10, to_move) + op16(0x20, with) + op12(0x30, store) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12(0x40, load) + op (0x4c, plot_rpix) + op (0x4d, swap) + op (0x4e, color_cmode) + op (0x4f, not) + op16(0x50, add_adc) + op16(0x60, sub_sbc_cmp) + op (0x70, merge) + op15(0x71, and_bic) + op16(0x80, mult_umult) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, asr_div2) + op (0x97, ror) + op6 (0x98, jmp_ljmp) + op (0x9e, lob) + op (0x9f, fmult_lmult) + op16(0xa0, ibt_lms_sms) + op16(0xb0, from_moves) + op (0xc0, hib) + op15(0xc1, or_xor) + op15(0xd0, inc) + op15(0xe0, dec) + op (0xdf, getc_ramb_romb) + op (0xef, getb) + op16(0xf0, iwt_lm_sm) + } + + #undef op + #undef op4 + #undef op6 + #undef op12 + #undef op15 + #undef op16 +} diff --git a/higan/processor/gsu/table.cpp b/higan/processor/gsu/table.cpp deleted file mode 100644 index 2bc51f05..00000000 --- a/higan/processor/gsu/table.cpp +++ /dev/null @@ -1,266 +0,0 @@ -auto GSU::initialize_opcode_table() -> void { - #define op4(id, name) \ - op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) - - #define op6(id, name) \ - op(id+ 0, name< 8>) op(id+ 1, name< 9>) op(id+ 2, name<10>) op(id+ 3, name<11>) \ - op(id+ 4, name<12>) op(id+ 5, name<13>) - - #define op12(id, name) \ - op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ - op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ - op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) - - #define op15l(id, name) \ - op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ - op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ - op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \ - op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) - - #define op15h(id, name) \ - op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) \ - op(id+ 4, name< 5>) op(id+ 5, name< 6>) op(id+ 6, name< 7>) op(id+ 7, name< 8>) \ - op(id+ 8, name< 9>) op(id+ 9, name<10>) op(id+10, name<11>) op(id+11, name<12>) \ - op(id+12, name<13>) op(id+13, name<14>) op(id+14, name<15>) - - #define op16(id, name) \ - op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ - op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ - op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \ - op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) op(id+15, name<15>) - - //====== - // ALT0 - //====== - - #define op(id, name) opcode_table[ 0 + id] = &GSU::op_##name; - op (0x00, stop) - op (0x01, nop) - op (0x02, cache) - op (0x03, lsr) - op (0x04, rol) - op (0x05, bra) - op (0x06, blt) - op (0x07, bge) - op (0x08, bne) - op (0x09, beq) - op (0x0a, bpl) - op (0x0b, bmi) - op (0x0c, bcc) - op (0x0d, bcs) - op (0x0e, bvc) - op (0x0f, bvs) - op16 (0x10, to_r) - op16 (0x20, with_r) - op12 (0x30, stw_ir) - op (0x3c, loop) - op (0x3d, alt1) - op (0x3e, alt2) - op (0x3f, alt3) - op12 (0x40, ldw_ir) - op (0x4c, plot) - op (0x4d, swap) - op (0x4e, color) - op (0x4f, not) - op16 (0x50, add_r) - op16 (0x60, sub_r) - op (0x70, merge) - op15h(0x71, and_r) - op16 (0x80, mult_r) - op (0x90, sbk) - op4 (0x91, link) - op (0x95, sex) - op (0x96, asr) - op (0x97, ror) - op6 (0x98, jmp_r) - op (0x9e, lob) - op (0x9f, fmult) - op16 (0xa0, ibt_r) - op16 (0xb0, from_r) - op (0xc0, hib) - op15h(0xc1, or_r) - op15l(0xd0, inc_r) - op (0xdf, getc) - op15l(0xe0, dec_r) - op (0xef, getb) - op16 (0xf0, iwt_r) - #undef op - - //====== - // ALT1 - //====== - - #define op(id, name) opcode_table[256 + id] = &GSU::op_##name; - op (0x00, stop) - op (0x01, nop) - op (0x02, cache) - op (0x03, lsr) - op (0x04, rol) - op (0x05, bra) - op (0x06, blt) - op (0x07, bge) - op (0x08, bne) - op (0x09, beq) - op (0x0a, bpl) - op (0x0b, bmi) - op (0x0c, bcc) - op (0x0d, bcs) - op (0x0e, bvc) - op (0x0f, bvs) - op16 (0x10, to_r) - op16 (0x20, with_r) - op12 (0x30, stb_ir) - op (0x3c, loop) - op (0x3d, alt1) - op (0x3e, alt2) - op (0x3f, alt3) - op12 (0x40, ldb_ir) - op (0x4c, rpix) - op (0x4d, swap) - op (0x4e, cmode) - op (0x4f, not) - op16 (0x50, adc_r) - op16 (0x60, sbc_r) - op (0x70, merge) - op15h(0x71, bic_r) - op16 (0x80, umult_r) - op (0x90, sbk) - op4 (0x91, link) - op (0x95, sex) - op (0x96, div2) - op (0x97, ror) - op6 (0x98, ljmp_r) - op (0x9e, lob) - op (0x9f, lmult) - op16 (0xa0, lms_r) - op16 (0xb0, from_r) - op (0xc0, hib) - op15h(0xc1, xor_r) - op15l(0xd0, inc_r) - op (0xdf, getc) - op15l(0xe0, dec_r) - op (0xef, getbh) - op16 (0xf0, lm_r) - #undef op - - //====== - // ALT2 - //====== - - #define op(id, name) opcode_table[512 + id] = &GSU::op_##name; - op (0x00, stop) - op (0x01, nop) - op (0x02, cache) - op (0x03, lsr) - op (0x04, rol) - op (0x05, bra) - op (0x06, blt) - op (0x07, bge) - op (0x08, bne) - op (0x09, beq) - op (0x0a, bpl) - op (0x0b, bmi) - op (0x0c, bcc) - op (0x0d, bcs) - op (0x0e, bvc) - op (0x0f, bvs) - op16 (0x10, to_r) - op16 (0x20, with_r) - op12 (0x30, stw_ir) - op (0x3c, loop) - op (0x3d, alt1) - op (0x3e, alt2) - op (0x3f, alt3) - op12 (0x40, ldw_ir) - op (0x4c, plot) - op (0x4d, swap) - op (0x4e, color) - op (0x4f, not) - op16 (0x50, add_i) - op16 (0x60, sub_i) - op (0x70, merge) - op15h(0x71, and_i) - op16 (0x80, mult_i) - op (0x90, sbk) - op4 (0x91, link) - op (0x95, sex) - op (0x96, asr) - op (0x97, ror) - op6 (0x98, jmp_r) - op (0x9e, lob) - op (0x9f, fmult) - op16 (0xa0, sms_r) - op16 (0xb0, from_r) - op (0xc0, hib) - op15h(0xc1, or_i) - op15l(0xd0, inc_r) - op (0xdf, ramb) - op15l(0xe0, dec_r) - op (0xef, getbl) - op16 (0xf0, sm_r) - #undef op - - //====== - // ALT3 - //====== - - #define op(id, name) opcode_table[768 + id] = &GSU::op_##name; - op (0x00, stop) - op (0x01, nop) - op (0x02, cache) - op (0x03, lsr) - op (0x04, rol) - op (0x05, bra) - op (0x06, blt) - op (0x07, bge) - op (0x08, bne) - op (0x09, beq) - op (0x0a, bpl) - op (0x0b, bmi) - op (0x0c, bcc) - op (0x0d, bcs) - op (0x0e, bvc) - op (0x0f, bvs) - op16 (0x10, to_r) - op16 (0x20, with_r) - op12 (0x30, stb_ir) - op (0x3c, loop) - op (0x3d, alt1) - op (0x3e, alt2) - op (0x3f, alt3) - op12 (0x40, ldb_ir) - op (0x4c, rpix) - op (0x4d, swap) - op (0x4e, cmode) - op (0x4f, not) - op16 (0x50, adc_i) - op16 (0x60, cmp_r) - op (0x70, merge) - op15h(0x71, bic_i) - op16 (0x80, umult_i) - op (0x90, sbk) - op4 (0x91, link) - op (0x95, sex) - op (0x96, div2) - op (0x97, ror) - op6 (0x98, ljmp_r) - op (0x9e, lob) - op (0x9f, lmult) - op16 (0xa0, lms_r) - op16 (0xb0, from_r) - op (0xc0, hib) - op15h(0xc1, xor_i) - op15l(0xd0, inc_r) - op (0xdf, romb) - op15l(0xe0, dec_r) - op (0xef, getbs) - op16 (0xf0, lm_r) - #undef op - - #undef op4 - #undef op6 - #undef op12 - #undef op15l - #undef op15h - #undef op16 -} diff --git a/higan/sfc/coprocessor/superfx/superfx.cpp b/higan/sfc/coprocessor/superfx/superfx.cpp index 4d56f935..aecf7e4c 100644 --- a/higan/sfc/coprocessor/superfx/superfx.cpp +++ b/higan/sfc/coprocessor/superfx/superfx.cpp @@ -17,14 +17,11 @@ auto SuperFX::Enter() -> void { auto SuperFX::main() -> void { if(regs.sfr.g == 0) return step(6); - - uint opcode = regs.sfr.alt2 << 9 | regs.sfr.alt1 << 8 | peekpipe(); - (this->*opcode_table[opcode])(); + instruction(peekpipe()); if(!r15_modified) regs.r[15]++; } auto SuperFX::init() -> void { - initialize_opcode_table(); regs.r[14].modify = {&SuperFX::r14_modify, this}; regs.r[15].modify = {&SuperFX::r15_modify, this}; } diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index 7d680960..4e8c5544 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -38,8 +38,7 @@ Settings::Settings() { set("Audio/Mute", false); set("Audio/Volume", 100); set("Audio/Balance", 50); - set("Audio/Reverb/Delay", 0); - set("Audio/Reverb/Level", 0); + set("Audio/Reverb/Enable", false); set("Audio/Latency", 60); set("Audio/Resampler", "Sinc"); diff --git a/higan/target-tomoko/program/utility.cpp b/higan/target-tomoko/program/utility.cpp index afc50368..ecbecf29 100644 --- a/higan/target-tomoko/program/utility.cpp +++ b/higan/target-tomoko/program/utility.cpp @@ -87,9 +87,6 @@ auto Program::updateAudioEffects() -> void { auto balance = max(-1.0, min(1.0, (settings["Audio/Balance"].integer() - 50) / 50.0)); Emulator::audio.setBalance(balance); - auto reverbDelay = settings["Audio/Reverb/Delay"].natural(); - Emulator::audio.setReverbDelay(reverbDelay); - - auto reverbLevel = settings["Audio/Reverb/Level"].natural() / 100.0; - Emulator::audio.setReverbLevel(reverbLevel); + auto reverbEnable = settings["Audio/Reverb/Enable"].boolean(); + Emulator::audio.setReverb(reverbEnable); } diff --git a/higan/target-tomoko/settings/audio.cpp b/higan/target-tomoko/settings/audio.cpp index 5067c5e3..5c59207f 100644 --- a/higan/target-tomoko/settings/audio.cpp +++ b/higan/target-tomoko/settings/audio.cpp @@ -46,13 +46,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) { balanceValue.setAlignment(0.5); balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] { updateEffects(); }); - reverbDelayLabel.setText("Reverb Delay:"); - reverbDelayValue.setAlignment(0.5); - reverbDelaySlider.setLength(201).setPosition(settings["Audio/Reverb/Delay"].natural()).onChange([&] { updateEffects(); }); - - reverbLevelLabel.setText("Reverb Level:"); - reverbLevelValue.setAlignment(0.5); - reverbLevelSlider.setLength(101).setPosition(settings["Audio/Reverb/Level"].natural()).onChange([&] { updateEffects(); }); + reverbEnable.setText("Reverb").setChecked(settings["Audio/Reverb/Enable"].boolean()).onToggle([&] { updateEffects(); }); updateDriver(); updateEffects(); @@ -81,11 +75,7 @@ auto AudioSettings::updateEffects() -> void { settings["Audio/Balance"].setValue(balanceSlider.position()); balanceValue.setText({balanceSlider.position(), "%"}); - settings["Audio/Reverb/Delay"].setValue(reverbDelaySlider.position()); - reverbDelayValue.setText({reverbDelaySlider.position(), "ms"}); - - settings["Audio/Reverb/Level"].setValue(reverbLevelSlider.position()); - reverbLevelValue.setText({reverbLevelSlider.position(), "%"}); + settings["Audio/Reverb/Enable"].setValue(reverbEnable.checked()); program->updateAudioEffects(); } diff --git a/higan/target-tomoko/settings/settings.hpp b/higan/target-tomoko/settings/settings.hpp index f5432d4e..351ad696 100644 --- a/higan/target-tomoko/settings/settings.hpp +++ b/higan/target-tomoko/settings/settings.hpp @@ -51,14 +51,7 @@ struct AudioSettings : TabFrameItem { Label balanceLabel{&balanceLayout, Size{80, 0}}; Label balanceValue{&balanceLayout, Size{50, 0}}; HorizontalSlider balanceSlider{&balanceLayout, Size{~0, 0}}; - HorizontalLayout reverbDelayLayout{&layout, Size{~0, 0}}; - Label reverbDelayLabel{&reverbDelayLayout, Size{80, 0}}; - Label reverbDelayValue{&reverbDelayLayout, Size{50, 0}}; - HorizontalSlider reverbDelaySlider{&reverbDelayLayout, Size{~0, 0}}; - HorizontalLayout reverbLevelLayout{&layout, Size{~0, 0}}; - Label reverbLevelLabel{&reverbLevelLayout, Size{80, 0}}; - Label reverbLevelValue{&reverbLevelLayout, Size{50, 0}}; - HorizontalSlider reverbLevelSlider{&reverbLevelLayout, Size{~0, 0}}; + CheckLabel reverbEnable{&layout, Size{~0, 0}}; auto updateDriver() -> void; auto updateEffects() -> void; diff --git a/nall/dsp/iir/biquad.hpp b/nall/dsp/iir/biquad.hpp new file mode 100644 index 00000000..f20cedfa --- /dev/null +++ b/nall/dsp/iir/biquad.hpp @@ -0,0 +1,146 @@ +#pragma once + +//transposed direct form II biquadratic second-order IIR filter (butterworth) + +namespace nall { namespace DSP { namespace IIR { + +struct Biquad { + enum class Type : uint { + LowPass, + HighPass, + BandPass, + Notch, + Peak, + LowShelf, + HighShelf, + }; + + inline auto reset(Type type, double cutoff, double quality = 0.707107, double gain = 0.0) -> void; + inline auto process(double in) -> double; //normalized sample + +private: + Type type; //filter type + double cutoff; //frequency cutoff + double quality; //frequency response quality + double gain; //peak gain + double a0, a1, a2, b1, b2; //coefficients + double z1, z2; //second-order IIR +}; + +auto Biquad::reset(Type type, double cutoff, double quality, double gain) -> void { + this->type = type; + this->cutoff = cutoff; + this->quality = quality; + this->gain = gain; + + z1 = 0.0; + z2 = 0.0; + + double v = pow(10, fabs(gain) / 20.0); + double k = tan(3.141592 * cutoff); + double q = quality; + double n = 0.0; + + switch(type) { + + case Type::LowPass: + n = 1 / (1 + k / q + k * k); + a0 = k * k * n; + a1 = 2 * a0; + a2 = a0; + b1 = 2 * (k * k - 1) * n; + b2 = (1 - k / q + k * k) * n; + break; + + case Type::HighPass: + n = 1 / (1 + k / q + k * k); + a0 = 1 * n; + a1 = -2 * a0; + a2 = a0; + b1 = 2 * (k * k - 1) * n; + b2 = (1 - k / q + k * k) * n; + break; + + case Type::BandPass: + n = 1 / (1 + k / q + k * k); + a0 = k / q * n; + a1 = 0; + a2 = -a0; + b1 = 2 * (k * k - 1) * n; + b2 = (1 - k / q + k * k) * n; + break; + + case Type::Notch: + n = 1 / (1 + k / q + k * k); + a0 = (1 + k * k) * n; + a1 = 2 * (k * k - 1) * n; + a2 = a0; + b1 = a1; + b2 = (1 - k / q + k * k) * n; + break; + + case Type::Peak: + if(gain >= 0) { + n = 1 / (1 + 1 / q * k + k * k); + a0 = (1 + v / q * k + k * k) * n; + a1 = 2 * (k * k - 1) * n; + a2 = (1 - v / q * k + k * k) * n; + b1 = a1; + b2 = (1 - 1 / q * k + k * k) * n; + } else { + n = 1 / (1 + v / q * k + k * k); + a0 = (1 + 1 / q * k + k * k) * n; + a1 = 2 * (k * k - 1) * n; + a2 = (1 - 1 / q * k + k * k) * n; + b1 = a1; + b2 = (1 - v / q * k + k * k) * n; + } + break; + + case Type::LowShelf: + if(gain >= 0) { + n = 1 / (1 + sqrt(2) * k + k * k); + a0 = (1 + sqrt(2 * v) * k + v * k * k) * n; + a1 = 2 * (v * k * k - 1) * n; + a2 = (1 - sqrt(2 * v) * k + v * k * k) * n; + b1 = 2 * (k * k - 1) * n; + b2 = (1 - sqrt(2) * k + k * k) * n; + } else { + n = 1 / (1 + sqrt(2 * v) * k + v * k * k); + a0 = (1 + sqrt(2) * k + k * k) * n; + a1 = 2 * (k * k - 1) * n; + a2 = (1 - sqrt(2) * k + k * k) * n; + b1 = 2 * (v * k * k - 1) * n; + b2 = (1 - sqrt(2 * v) * k + v * k * k) * n; + } + break; + + case Type::HighShelf: + if(gain >= 0) { + n = 1 / (1 + sqrt(2) * k + k * k); + a0 = (v + sqrt(2 * v) * k + k * k) * n; + a1 = 2 * (k * k - v) * n; + a2 = (v - sqrt(2 * v) * k + k * k) * n; + b1 = 2 * (k * k - 1) * n; + b2 = (1 - sqrt(2) * k + k * k) * n; + } else { + n = 1 / (v + sqrt(2 * v) * k + k * k); + a0 = (1 + sqrt(2) * k + k * k) * n; + a1 = 2 * (k * k - 1) * n; + a2 = (1 - sqrt(2) * k + k * k) * n; + b1 = 2 * (k * k - v) * n; + b2 = (v - sqrt(2 * v) * k + k * k) * n; + } + break; + + } +} + +auto Biquad::process(double in) -> double { + double out = in * a0 + z1; + z1 = in * a1 + z2 - b1 * out; + z2 = in * a2 - b2 * out; + return out; +} + +}}} diff --git a/nall/dsp/resampler/cubic.hpp b/nall/dsp/resampler/cubic.hpp new file mode 100644 index 00000000..df379776 --- /dev/null +++ b/nall/dsp/resampler/cubic.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include + +namespace nall { namespace DSP { namespace Resampler { + +struct Cubic { + inline auto reset(double inputFrequency, double outputFrequency, uint queueSize = 0) -> void; + inline auto pending() const -> bool { return samples.pending(); } + inline auto read() -> double { return samples.read(); } + inline auto write(double sample) -> void; + +private: + double inputFrequency; + double outputFrequency; + + double ratio; + double fraction; + double history[4]; + queue samples; +}; + +auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize) -> void { + this->inputFrequency = inputFrequency; + this->outputFrequency = outputFrequency; + if(!queueSize) queueSize = outputFrequency * 0.02; //20ms + + ratio = inputFrequency / outputFrequency; + fraction = 0.0; + for(auto& sample: history) sample = 0.0; + samples.resize(queueSize); +} + +auto Cubic::write(double sample) -> void { + auto& mu = fraction; + auto& s = history; + + s[0] = s[1]; + s[1] = s[2]; + s[2] = s[3]; + s[3] = sample; + + while(mu <= 1.0) { + double A = s[3] - s[2] - s[0] + s[1]; + double B = s[0] - s[1] - A; + double C = s[2] - s[0]; + double D = s[1]; + + samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D); + mu += ratio; + } + + mu -= 1.0; +} + +}}} diff --git a/nall/nall.hpp b/nall/nall.hpp index cdcb5cbb..b64ffe59 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/nall/queue.hpp b/nall/queue.hpp new file mode 100644 index 00000000..244aec3a --- /dev/null +++ b/nall/queue.hpp @@ -0,0 +1,101 @@ +#pragma once + +//simple circular ring buffer + +namespace nall { + +template +struct queue { + queue() = default; + + queue(const queue& source) { + operator=(source); + } + + queue(queue&& source) { + operator=(move(source)); + } + + auto operator=(const queue& source) -> queue& { + if(this == &source) return *this; + reset(); + _size = source._size; + _data = new T[_size]; + for(auto n : range(_size)) _data[n] = source._data[n]; + _read = source._read; + _write = source._write; + return *this; + } + + auto operator=(queue&& source) -> queue& { + if(this == &source) return *this; + _data = source._data; + _size = source._size; + _read = source._read; + _write = source._write; + source._data = nullptr; + source.reset(); + } + + ~queue() { + reset(); + } + + explicit operator bool() const { + return _size; + } + + auto size() const -> uint { + return _size; + } + + auto data() -> T* { + return _data; + } + + auto data() const -> const T* { + return _data; + } + + auto reset() { + delete[] _data; + _data = nullptr; + _size = 0; + _read = 0; + _write = 0; + } + + auto resize(uint size, const T& value = {}) -> void { + reset(); + _size = size; + _data = new T[_size]; + for(auto n : range(_size)) _data[n] = value; + } + + auto pending() const -> bool { + return _read != _write; + } + + auto read() -> T { + T result = _data[_read]; + if(++_read >= _size) _read = 0; + return result; + } + + auto last() const -> T { + return _data[_write]; + } + + auto write(const T& value) -> void { + _data[_write] = value; + if(++_write >= _size) _write = 0; + } + +private: + T* _data = nullptr; + uint _size = 0; + uint _read = 0; + uint _write = 0; +}; + +}