Update to v098r13 release.

byuu says:

Changelog:
- nall/dsp returns with new iir/biquad.hpp and resampler/cubic.hpp files
- nall/queue.hpp added (simple ring buffer ... nall/vector wouldn't
  cause too many moves with FIFO)
- audio streams now only buffer 20ms; so even if multiple audio streams
  desync, latency can never exceed 20ms
- replaced blackman windwed sinc FIR hermite audio filter with transposed
  direct form II biquadratic sixth-order IIR butterworth filter (better
  attenuation of frequencies above 20KHz, faster, no need for decimation,
  less code)
- put in experimental eight-tap echo filter (a lot better than what I
  had before, but still rather weak)
- substantial cleanups to the SuperFX GSU processor core (slightly
  faster, 479KB->100KB object file, 42.7KB->33.4KB source code size,
  way less code duplication)

We'll definitely want to test the whole SuperFX library (not many games)
just to make sure there's no regressions caused by this one.

Not sure what I want to do with audio processing effects yet. I've always
really wanted lots of fun controls to customize audio, and now finally
with this new biquad filter, I can finally start implementing real
effects. For instance, an equalizer wouldn't be too complicated anymore.

The new reverb effect is still a poor man's version. I need to find human
readable source for implementing a comb-filter properly. I'm pretty sure
I can already treat nall::queue as an all-pass filter since all that
does is phase shift (fancy audio term for "delay audio"). What's really
going to be hard is figuring out how to expose user-friendly settings for
controlling it. It looks like you need a bunch of coprime coefficients,
and I don't think casual users are going to be able to hand-enter coprime
values to get the echo effect they want. I uh ... don't even know how
to calculate coprime values dynamically right now >_> But we're going
to have to, as they are correlated to the output sampling rate.

We'll definitely want to make some audio profiles so that users can
quickly select pre-configured themes that sound nice, but expose the
underlying coefficients so that they can tweak stuff to their liking. This
isn't just about higan, this is about me trying to learn digital signal
processing, so please don't be too upset about feature creep or anything
on this.

Anyway ... I'm having some difficulties with my audio right now. When
the reverb effect is enabled, there's a bunch of static on system
reset for just a moment. But this should not be possible. nall::queue
is initializing all previous reverb sample elements to 0.0. I don't
understand where static is coming in from. Further, we have the same
issue with both the windowed sinc and the biquad filters ... a bit of
a popping sound when starting a game. Any help tracking this down would
be appreciated.

There's also one really annoying issue ... I can't seem to do reverb
or volume adjustments with normalized samples. If I say "volume *= 0.5"
in higan/audio/audio.cpp line 68, it doesn't just halve the volume, it
adds a whole bunch of distortion. This makes absolutely zero sense to
me. The sample values are between 0.0 (mute) and 1.0 (full volume) here,
so multiplying a double by 0.5 shouldn't cause distortion. So right now,
I'm doing these adjustments with less precision after denormalizing back
to int16. Anyone ever see something like that? :/
This commit is contained in:
Tim Allen 2016-06-01 08:29:36 +10:00
parent 7f3cfa17b9
commit 839813d0f1
18 changed files with 657 additions and 955 deletions

View File

@ -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<Stream> {
@ -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));
}
}

View File

@ -1,5 +1,8 @@
#pragma once
#include <nall/dsp/iir/biquad.hpp>
#include <nall/dsp/resampler/cubic.hpp>
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<Stream>;
@ -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<int16> reverbLeft;
vector<int16> reverbRight;
bool reverbEnable = false;
vector<vector<queue<int16>>> reverb;
friend class Stream;
};
@ -55,23 +56,9 @@ private:
double outputFrequency = 0.0;
double cutoffFrequency = 0.0;
vector<double> taps;
uint decimationRate = 0;
uint decimationOffset = 0;
vector<vector<double>> input;
uint inputOffset = 0;
double resamplerFrequency = 0.0;
double resamplerFraction = 0.0;
double resamplerStep = 0.0;
vector<vector<double>> queue;
vector<vector<double>> output;
uint outputs = 0;
uint outputReadOffset = 0;
uint outputWriteOffset = 0;
const uint iirPasses = 3; //6th-order filter
vector<vector<DSP::IIR::Biquad>> iir;
vector<DSP::Resampler::Cubic> resampler;
friend class Audio;
};

View File

@ -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;
resampler.resize(channels);
for(auto c : range(channels)) {
resampler[c].reset(inputFrequency, outputFrequency);
}
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;
}
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;
}
}

View File

@ -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/";

View File

@ -8,7 +8,7 @@
namespace Processor {
#include "instructions.cpp"
#include "table.cpp"
#include "switch.cpp"
#include "serialization.cpp"
#include "disassembler.cpp"

View File

@ -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<int> auto op_adc_i();
template<int> auto op_adc_r();
template<int> auto op_add_i();
template<int> auto op_add_r();
auto op_add_adc(uint n);
auto op_alt1();
auto op_alt2();
auto op_alt3();
template<int> auto op_and_i();
template<int> auto op_and_r();
auto op_asr();
auto op_bge();
auto op_bcc();
auto op_bcs();
auto op_beq();
template<int> auto op_bic_i();
template<int> 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<int> auto op_cmp_r();
auto op_color();
template<int> auto op_dec_r();
auto op_div2();
auto op_fmult();
template<int> 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<int> auto op_ibt_r();
template<int> auto op_inc_r();
template<int> auto op_iwt_r();
template<int> auto op_jmp_r();
template<int> auto op_ldb_ir();
template<int> auto op_ldw_ir();
template<int> auto op_link();
template<int> auto op_ljmp_r();
template<int> auto op_lm_r();
template<int> 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<int> auto op_mult_i();
template<int> auto op_mult_r();
auto op_mult_umult(uint n);
auto op_nop();
auto op_not();
template<int> auto op_or_i();
template<int> 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<int> auto op_sbc_r();
auto op_sbk();
auto op_sex();
template<int> auto op_sm_r();
template<int> auto op_sms_r();
template<int> auto op_stb_ir();
auto op_store(uint n);
auto op_stop();
template<int> auto op_stw_ir();
template<int> auto op_sub_i();
template<int> auto op_sub_r();
auto op_sub_sbc_cmp(uint n);
auto op_swap();
template<int> auto op_to_r();
template<int> auto op_umult_i();
template<int> auto op_umult_r();
template<int> auto op_with_r();
template<int> auto op_xor_i();
template<int> 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;

View File

@ -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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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() {
//$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]++;
regs.reset();
}
//$4c(alt1): rpix
auto GSU::op_rpix() {
} else {
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() {
//$4e(alt0) color
//$4e(alt1) cmode
auto GSU::op_color_cmode() {
if(!regs.sfr.alt1) {
regs.colr = color(regs.sr());
regs.reset();
}
//$4e(alt1): cmode
auto GSU::op_cmode() {
} else {
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
auto GSU::op_jmp_r() {
//$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];
regs.reset();
}
//$98-9d(alt1): ljmp rN
template<int n>
auto GSU::op_ljmp_r() {
} else {
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<int n>
auto GSU::op_ibt_r() {
regs.r[n] = (int8)pipe();
regs.reset();
}
//$a0-af(alt1): lms rN,(yy)
template<int n>
auto GSU::op_lms_r() {
//$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;
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<int n>
auto GSU::op_sms_r() {
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();
}
//$b0-bf(b0): from rN
//$b0-bf(b1): moves rN
template<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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<int n>
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() {
//$df(alt0) getc
//$df(alt2) ramb
//$df(alt3) romb
auto GSU::op_getc_ramb_romb() {
if(!regs.sfr.alt2) {
regs.colr = color(rombuffer_read());
regs.reset();
}
//$df(alt2): ramb
auto GSU::op_ramb() {
} else if(!regs.sfr.alt1) {
rambuffer_sync();
regs.rambr = regs.sr() & 0x01;
regs.reset();
}
//$df(alt3): romb
auto GSU::op_romb() {
} else {
rombuffer_sync();
regs.rombr = regs.sr() & 0x7f;
}
regs.reset();
}
//$e0-ee: dec rN
template<int n>
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<int n>
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<int n>
auto GSU::op_lm_r() {
//$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;
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<int n>
auto GSU::op_sm_r() {
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();
}

View File

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

View File

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

View File

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

View File

@ -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");

View File

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

View File

@ -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();
}

View File

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

146
nall/dsp/iir/biquad.hpp Normal file
View File

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

View File

@ -0,0 +1,56 @@
#pragma once
#include <nall/queue.hpp>
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<double> 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;
}
}}}

View File

@ -38,6 +38,7 @@
#include <nall/path.hpp>
#include <nall/primitives.hpp>
#include <nall/property.hpp>
#include <nall/queue.hpp>
#include <nall/random.hpp>
#include <nall/range.hpp>
#include <nall/run.hpp>

101
nall/queue.hpp Normal file
View File

@ -0,0 +1,101 @@
#pragma once
//simple circular ring buffer
namespace nall {
template<typename T>
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;
};
}