summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/swp00.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/swp00.cpp')
-rw-r--r--src/devices/sound/swp00.cpp1834
1 files changed, 1789 insertions, 45 deletions
diff --git a/src/devices/sound/swp00.cpp b/src/devices/sound/swp00.cpp
index eb0d7c59107..f04161b22b3 100644
--- a/src/devices/sound/swp00.cpp
+++ b/src/devices/sound/swp00.cpp
@@ -6,12 +6,72 @@
#include "emu.h"
#include "swp00.h"
+/*
+
+ Used in the MU50, the SWP00 is the combination of a rompler called
+ AWM2 (Advanced Wave Memory 2) and an effects DSP called MEG
+ (Multiple Effects Generator). It is the simpler variant of those, a
+ simplification and integration of the SWP20/SWD/MEG/EQ combo use in
+ the MU80.
+
+ Its clock is 33.9MHz and the output is at 44100Hz stereo (768 cycles
+ per sample pair) per dac output.
+
+
+ AWM2:
+
+ The AWM2 is in charge of handling the individual channels. It
+ manages reading the rom, decoding the samples, applying volume and
+ envelopes and lfos and filtering the result. The channels are
+ volume-modulated and summed into 7 outputs which are then processed
+ by the MEG.
+
+ As all the SWPs, the sound data can be four formats (8 bits, 12
+ bits, 16 bits, and a 8-bits log format with roughly 10 bits of
+ dynamic). It's interesting to note that the 8-bits format is not
+ used by the MU50. The rom bus is 24 bits address and 8 bits data
+ wide. It applies a single, Chamberlin-configuration LPF to the
+ sample data. Envelopes are handled semi-automatically, and the
+ final result volume-modulated (global volume, pan, tremolo, dispatch
+ in dry/reverb/chorus/variation) in 7 output channels.
+
+
+ MEG:
+
+ The MEG in this case is an internal DSP with a fixed program in four
+ selectable variants. It has 192 steps of program, and can issue a
+ memory access to the effects DRAM every 3 cycles. The programs are
+ internal and as far as we know not dumpable. We managed a
+ reimplementation though.
+
+ The program does the effects "reverb", "chorus" and "variation" and
+ mixing between all those. The four variants only in practice impact
+ the variation segment, in addresses 109-191 roughly.
+
+ Each instruction is associated with a dynamically changeable 10-bit
+ constant used as a fixed point value (either 1.9 or 3.7 depending on
+ the instruction). Every third instruction (pc multiple of 3) is
+ also associated with a 16-bits offset for the potential memory
+ access.
+
+
+ Interface:
+
+ The interface is 8-bits wide but would have wanted to be 16-bits, with
+ 11 address bits. There are three address formats depending on the
+ part of the chip one speaks to:
+ 000 0sss ssss Global controls
+ 001 1ppp pppl MEG, offsets (16-bits values, l=high/low byte, pc 00-bd, divided by 3)
+ 01p pppp pppl MEG, constants (16-bits values, l=high/low byte, pc 00-bf)
+ sss sscc cccs AWM2, channel/slot combination (slot = 8-b and 20-37)
+*/
+
DEFINE_DEVICE_TYPE(SWP00, swp00_device, "swp00", "Yamaha SWP00 (TC170C120SF / XQ036A00) sound chip")
swp00_device::swp00_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, SWP00, tag, owner, clock),
device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this, 25+2, ENDIANNESS_LITTLE, 32)
+ device_rom_interface(mconfig, *this)
{
}
@@ -19,84 +79,1768 @@ void swp00_device::device_add_mconfig(machine_config &config)
{
}
+const std::array<u32, 4> swp00_device::lfo_shape_centered_saw = { 0x00000000, 0x00000000, 0xfff00000, 0xfff00000 }; // --////--
+const std::array<u32, 4> swp00_device::lfo_shape_centered_tri = { 0x00000000, 0x0007ffff, 0xfff7ffff, 0xfff00000 }; // --/\/\--
+const std::array<u32, 4> swp00_device::lfo_shape_offset_saw = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; // __////__
+const std::array<u32, 4> swp00_device::lfo_shape_offset_tri = { 0x00000000, 0x00000000, 0x000fffff, 0x000fffff }; // __/\/\__
+
+const std::array<s32, 16> swp00_device::panmap = {
+ 0x000, 0x040, 0x080, 0x0c0,
+ 0x100, 0x140, 0x180, 0x1c0,
+ 0x200, 0x240, 0x280, 0x2c0,
+ 0x300, 0x340, 0x380, 0xfff
+};
+
+const std::array<u8, 4> swp00_device::dpcm_offset = { 7, 6, 4, 0 };
+
+bool swp00_device::istep(s32 &value, s32 limit, s32 step)
+{
+ // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step);
+ if(value < limit) {
+ value += step;
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ if(value > limit) {
+ value -= step;
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+s32 swp00_device::fpadd(s32 value, s32 step)
+{
+ s32 e = value >> 24;
+ s32 m = value & 0xffffff;
+
+ m += step << e;
+ if(m & 0xfe000000)
+ return 0xfffffff;
+
+ while(m & 0x01000000) {
+ m <<= 1;
+ e ++;
+ }
+ if(e >= 16)
+ return 0xfffffff;
+ return (e << 24) | (m & 0xffffff);
+}
+
+s32 swp00_device::fpsub(s32 value, s32 step)
+{
+ s32 e = value >> 24;
+ s32 m = (value & 0xffffff) | 0xfe000000;
+ m = e < 0xc ? m - (step << e) : (m >> (e - 0xb)) - (step << 0xb);
+ if(m >= 0)
+ return 0;
+ if(e >= 0xc)
+ e = 0xb;
+ while(m < 0xfe000000) {
+ if(!e)
+ return 0;
+ e --;
+ m >>= 1;
+ }
+ while(e != 0xf && (m >= 0xff000000)) {
+ e ++;
+ m <<= 1;
+ }
+
+ return (e << 24) | (m & 0xffffff);
+}
+
+bool swp00_device::fpstep(s32 &value, s32 limit, s32 step)
+{
+ // value, limit and step are 4.24 but step has its exponent and
+ // top four bits zero
+
+ if(value == limit)
+ return true;
+ if(value < limit) {
+ value = fpadd(value, step);
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ value = fpsub(value, step);
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+}
+
+// sample is signed 24.8
+s32 swp00_device::fpapply(s32 value, s32 sample)
+{
+ if(value >= 0x10000000)
+ return 0;
+ return (s64(sample) - ((s64(sample) * ((value >> 9) & 0x7fff)) >> 16)) >> (value >> 24);
+}
+
+// sample is signed 24.8
+s32 swp00_device::lpffpapply(s32 value, s32 sample)
+{
+ return ((((value >> 7) & 0x7fff) | 0x8000) * s64(sample)) >> (31 - (value >> 22));
+}
+
+// Some tables we need. Maybe they're in roms inside the chip,
+// maybe they're logic. Probably slightly inexact too, would need
+// a complicated hardware setup to really test them.
+
+const std::array<s32, 0x80> swp00_device::attack_linear_step = {
+ 0x00027, 0x0002b, 0x0002f, 0x00033, 0x00037, 0x0003d, 0x00042, 0x00048,
+ 0x0004d, 0x00056, 0x0005e, 0x00066, 0x0006f, 0x0007a, 0x00085, 0x00090,
+ 0x0009b, 0x000ac, 0x000bd, 0x000cc, 0x000de, 0x000f4, 0x00109, 0x00120,
+ 0x00135, 0x00158, 0x00179, 0x00199, 0x001bc, 0x001e7, 0x00214, 0x00240,
+ 0x0026b, 0x002af, 0x002f2, 0x00332, 0x00377, 0x003d0, 0x0042c, 0x00480,
+ 0x004dc, 0x0055e, 0x005e9, 0x0066e, 0x006f4, 0x007a4, 0x00857, 0x0090b,
+ 0x009c3, 0x00acb, 0x00bd6, 0x00ce6, 0x00e00, 0x00f5e, 0x010d2, 0x01234,
+ 0x0139e, 0x015d0, 0x017f3, 0x01a20, 0x01c4a, 0x01f52, 0x02232, 0x0250f,
+ 0x027ff, 0x02c72, 0x03109, 0x0338b, 0x039c4, 0x04038, 0x04648, 0x04c84,
+ 0x05262, 0x05c1c, 0x065af, 0x06f5c, 0x07895, 0x0866f, 0x09470, 0x0a19e,
+ 0x0ae4c, 0x0c566, 0x0db8d, 0x0f00f, 0x10625, 0x12937, 0x14954, 0x16c17,
+ 0x1886e, 0x1c71c, 0x20000, 0x239e1, 0x2647c, 0x2aaab, 0x2ecfc, 0x3241f,
+ 0x35e51, 0x3a83b, 0x40000, 0x4325c, 0x47dc1, 0x4c8f9, 0x50505, 0x55555,
+ 0x58160, 0x5d174, 0x60606, 0x62b2e, 0x67b24, 0x6a63c, 0x6d3a0, 0x6eb3e,
+ 0x71c72, 0x73616, 0x75075, 0x76b98, 0x78788, 0x78788, 0x7a44c, 0x7a44c,
+ 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c,
+};
+
+const std::array<s32, 0x20> swp00_device::decay_linear_step = {
+ 0x15083, 0x17ad2, 0x1a41a, 0x1cbe7, 0x1f16d, 0x22ef1, 0x26a44, 0x2a1e4,
+ 0x2da35, 0x34034, 0x3a197, 0x40000, 0x45b82, 0x4b809, 0x51833, 0x57262,
+ 0x5d9f7, 0x6483f, 0x6b15c, 0x71c72, 0x77976, 0x7d119, 0x83127, 0x88889,
+ 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769,
+};
+
void swp00_device::device_start()
{
m_stream = stream_alloc(0, 2, 44100);
+
+ save_item(NAME(m_waverom_access));
+ save_item(NAME(m_waverom_val));
+ save_item(NAME(m_meg_control));
+
+ save_item(NAME(m_buffer_offset));
+ save_item(NAME(m_rev_vol));
+ save_item(NAME(m_cho_vol));
+ save_item(NAME(m_var_vol));
+
+ save_item(NAME(m_var_lfo_phase));
+ save_item(NAME(m_var_lfo_h_1));
+ save_item(NAME(m_var_lfo_h_2));
+ save_item(NAME(m_var_lfo1a));
+ save_item(NAME(m_var_lfo2a));
+ save_item(NAME(m_var_lfo3a));
+ save_item(NAME(m_var_lfo4a));
+
+ save_item(NAME(m_var_filter_1));
+ save_item(NAME(m_var_filter_2));
+ save_item(NAME(m_var_filter_3));
+
+ save_item(NAME(m_var_filter2_1));
+ save_item(NAME(m_var_filter2_2a));
+ save_item(NAME(m_var_filter2_2b));
+ save_item(NAME(m_var_filter2_3a));
+ save_item(NAME(m_var_filter2_3b));
+ save_item(NAME(m_var_filter2_4));
+
+ save_item(NAME(m_var_filterp_l_1));
+ save_item(NAME(m_var_filterp_l_2));
+ save_item(NAME(m_var_filterp_l_3));
+ save_item(NAME(m_var_filterp_l_4));
+ save_item(NAME(m_var_filterp_l_5));
+ save_item(NAME(m_var_filterp_l_6));
+ save_item(NAME(m_var_filterp_r_1));
+ save_item(NAME(m_var_filterp_r_2));
+ save_item(NAME(m_var_filterp_r_3));
+ save_item(NAME(m_var_filterp_r_4));
+ save_item(NAME(m_var_filterp_r_5));
+ save_item(NAME(m_var_filterp_r_6));
+
+ save_item(NAME(m_var_filter3_1));
+ save_item(NAME(m_var_filter3_2));
+
+ save_item(NAME(m_var_h1));
+ save_item(NAME(m_var_h2));
+ save_item(NAME(m_var_h3));
+ save_item(NAME(m_var_h4));
+
+ save_item(NAME(m_cho_lfo_phase));
+ save_item(NAME(m_cho_filter_l_1));
+ save_item(NAME(m_cho_filter_l_2));
+ save_item(NAME(m_cho_filter_l_3));
+ save_item(NAME(m_cho_filter_r_1));
+ save_item(NAME(m_cho_filter_r_2));
+ save_item(NAME(m_cho_filter_r_3));
+
+ save_item(NAME(m_rev_filter_1));
+ save_item(NAME(m_rev_filter_2));
+ save_item(NAME(m_rev_filter_3));
+ save_item(NAME(m_rev_hist_a));
+ save_item(NAME(m_rev_hist_b));
+ save_item(NAME(m_rev_hist_c));
+ save_item(NAME(m_rev_hist_d));
+
+ save_item(NAME(m_rev_buffer));
+ save_item(NAME(m_cho_buffer));
+ save_item(NAME(m_var_buffer));
+ save_item(NAME(m_offset));
+ save_item(NAME(m_const));
+ save_item(NAME(m_lpf_info));
+ save_item(NAME(m_lpf_speed));
+ save_item(NAME(m_lfo_famod_depth));
+ save_item(NAME(m_rev_level));
+ save_item(NAME(m_dry_level));
+ save_item(NAME(m_cho_level));
+ save_item(NAME(m_var_level));
+ save_item(NAME(m_glo_level));
+ save_item(NAME(m_panning));
+ save_item(NAME(m_attack_speed));
+ save_item(NAME(m_attack_level));
+ save_item(NAME(m_decay_speed));
+ save_item(NAME(m_decay_level));
+ save_item(NAME(m_pitch));
+ save_item(NAME(m_sample_start));
+ save_item(NAME(m_sample_end));
+ save_item(NAME(m_sample_dpcm_and_format));
+ save_item(NAME(m_sample_address));
+ save_item(NAME(m_lfo_step));
+ save_item(NAME(m_lfo_pmod_depth));
+
+ save_item(NAME(m_lfo_phase));
+ save_item(NAME(m_sample_pos));
+ save_item(NAME(m_envelope_level));
+
+ save_item(NAME(m_glo_level_cur));
+ save_item(NAME(m_pan_l));
+ save_item(NAME(m_pan_r));
+
+ save_item(NAME(m_lpf_feedback));
+ save_item(NAME(m_lpf_target_value));
+ save_item(NAME(m_lpf_value));
+ save_item(NAME(m_lpf_timer));
+ save_item(NAME(m_lpf_ha));
+ save_item(NAME(m_lpf_hb));
+
+ save_item(NAME(m_active));
+ save_item(NAME(m_decay));
+ save_item(NAME(m_decay_done));
+ save_item(NAME(m_lpf_done));
+
+ save_item(NAME(m_dpcm_current));
+ save_item(NAME(m_dpcm_next));
+ save_item(NAME(m_dpcm_address));
+ save_item(NAME(m_dpcm_sum));
+
+ for(int i=0; i != 128; i++) {
+ u32 v = 0;
+ switch(i >> 3) {
+ default: v = ((i & 7) + 8) << (1 + (i >> 3)); break;
+ case 0xb: v = ((i & 7) + 4) << 13; break;
+ case 0xc: v = ((i & 6) + 6) << 14; break;
+ case 0xd: v = ((i & 4) + 7) << 15; break;
+ case 0xe: v = 15 << 15; break;
+ case 0xf: v = 31 << 15; break;
+ }
+ m_global_step[i] = v;
+ }
+
+ // Delta-packed samples decompression.
+
+ for(int i=0; i<128; i++) {
+ s16 base = ((i & 0x1f) << (3+(i >> 5))) + (((1 << (i >> 5))-1) << 8);
+ m_dpcm[i | 0x80] = - base;
+ m_dpcm[i] = + base;
+ }
}
void swp00_device::device_reset()
{
+ m_waverom_access = 0;
+ m_waverom_val = 0;
+ m_meg_control = 0;
+
+ m_buffer_offset = 0;
+ m_rev_vol = 0;
+ m_cho_vol = 0;
+ m_var_vol = 0;
+
+ m_var_lfo_phase = 0;
+ m_var_lfo_h_1 = 0;
+ m_var_lfo_h_2 = 0;
+ m_var_lfo1a = 0;
+ m_var_lfo2a = 0;
+ m_var_lfo3a = 0;
+ m_var_lfo4a = 0;
+ m_var_filter_1 = 0;
+ m_var_filter_2 = 0;
+ m_var_filter_3 = 0;
+ m_var_filter2_1 = 0;
+ m_var_filter2_2a = 0;
+ m_var_filter2_2b = 0;
+ m_var_filter2_3a = 0;
+ m_var_filter2_3b = 0;
+ m_var_filter2_4 = 0;
+ m_var_filter3_1 = 0;
+ m_var_filter3_2 = 0;
+ m_var_filterp_l_1 = 0;
+ m_var_filterp_l_2 = 0;
+ m_var_filterp_l_3 = 0;
+ m_var_filterp_l_4 = 0;
+ m_var_filterp_l_5 = 0;
+ m_var_filterp_l_6 = 0;
+ m_var_filterp_r_1 = 0;
+ m_var_filterp_r_2 = 0;
+ m_var_filterp_r_3 = 0;
+ m_var_filterp_r_4 = 0;
+ m_var_filterp_r_5 = 0;
+ m_var_filterp_r_6 = 0;
+
+ m_var_h1 = 0;
+ m_var_h2 = 0;
+ m_var_h3 = 0;
+ m_var_h4 = 0;
+
+ m_cho_lfo_phase = 0;
+ m_cho_filter_l_1 = 0;
+ m_cho_filter_l_2 = 0;
+ m_cho_filter_l_3 = 0;
+ m_cho_filter_r_1 = 0;
+ m_cho_filter_r_2 = 0;
+ m_cho_filter_r_3 = 0;
+
+ m_rev_filter_1 = 0;
+ m_rev_filter_2 = 0;
+ m_rev_filter_3 = 0;
+ m_rev_hist_a = 0;
+ m_rev_hist_b = 0;
+ m_rev_hist_c = 0;
+ m_rev_hist_d = 0;
+
+ std::fill(m_rev_buffer.begin(), m_rev_buffer.end(), 0);
+ std::fill(m_cho_buffer.begin(), m_cho_buffer.end(), 0);
+ std::fill(m_var_buffer.begin(), m_var_buffer.end(), 0);
+ std::fill(m_offset.begin(), m_offset.end(), 0);
+ std::fill(m_const.begin(), m_const.end(), 0);
+ std::fill(m_lpf_info.begin(), m_lpf_info.end(), 0);
+ std::fill(m_lpf_speed.begin(), m_lpf_speed.end(), 0);
+ std::fill(m_lfo_famod_depth.begin(), m_lfo_famod_depth.end(), 0);
+ std::fill(m_rev_level.begin(), m_rev_level.end(), 0);
+ std::fill(m_dry_level.begin(), m_dry_level.end(), 0);
+ std::fill(m_cho_level.begin(), m_cho_level.end(), 0);
+ std::fill(m_var_level.begin(), m_var_level.end(), 0);
+ std::fill(m_glo_level.begin(), m_glo_level.end(), 0);
+ std::fill(m_panning.begin(), m_panning.end(), 0);
+ std::fill(m_attack_speed.begin(), m_attack_speed.end(), 0);
+ std::fill(m_attack_level.begin(), m_attack_level.end(), 0);
+ std::fill(m_decay_speed.begin(), m_decay_speed.end(), 0);
+ std::fill(m_decay_level.begin(), m_decay_level.end(), 0);
+ std::fill(m_pitch.begin(), m_pitch.end(), 0);
+ std::fill(m_sample_start.begin(), m_sample_start.end(), 0);
+ std::fill(m_sample_end.begin(), m_sample_end.end(), 0);
+ std::fill(m_sample_dpcm_and_format.begin(), m_sample_dpcm_and_format.end(), 0);
+ std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
+ std::fill(m_lfo_step.begin(), m_lfo_step.end(), 0);
+ std::fill(m_lfo_pmod_depth.begin(), m_lfo_pmod_depth.end(), 0);
+
+ std::fill(m_lfo_phase.begin(), m_lfo_phase.end(), 0);
+ std::fill(m_sample_pos.begin(), m_sample_pos.end(), 0);
+ std::fill(m_envelope_level.begin(), m_envelope_level.end(), 0);
+
+ std::fill(m_glo_level_cur.begin(), m_glo_level_cur.end(), 0);
+ std::fill(m_pan_l.begin(), m_pan_l.end(), 0);
+ std::fill(m_pan_r.begin(), m_pan_r.end(), 0);
+
+ std::fill(m_lpf_feedback.begin(), m_lpf_feedback.end(), 0);
+ std::fill(m_lpf_target_value.begin(), m_lpf_target_value.end(), 0);
+ std::fill(m_lpf_value.begin(), m_lpf_value.end(), 0);
+ std::fill(m_lpf_timer.begin(), m_lpf_timer.end(), 0);
+ std::fill(m_lpf_ha.begin(), m_lpf_ha.end(), 0);
+ std::fill(m_lpf_hb.begin(), m_lpf_hb.end(), 0);
+
+ std::fill(m_active.begin(), m_active.end(), false);
+ std::fill(m_decay.begin(), m_decay.end(), false);
+ std::fill(m_decay_done.begin(), m_decay_done.end(), false);
+ std::fill(m_lpf_done.begin(), m_lpf_done.end(), false);
+
+ std::fill(m_dpcm_current.begin(), m_dpcm_current.end(), false);
+ std::fill(m_dpcm_next.begin(), m_dpcm_next.end(), false);
+ std::fill(m_dpcm_address.begin(), m_dpcm_address.end(), false);
+ std::fill(m_dpcm_sum.begin(), m_dpcm_sum.end(), 0);
}
-void swp00_device::rom_bank_updated()
+void swp00_device::rom_bank_pre_change()
{
m_stream->update();
}
void swp00_device::map(address_map &map)
{
- map(0x0000, 0x7ff).rw(FUNC(swp00_device::snd_r), FUNC(swp00_device::snd_w));
+ map(0x000, 0x7ff).rw(FUNC(swp00_device::snd_r), FUNC(swp00_device::snd_w));
+
+ // 00-01: control
+
+ rchan(map, 0x08).w(FUNC(swp00_device::slot8_w)); // always 80
+ rchan(map, 0x09).w(FUNC(swp00_device::slot9_w)); // always 00
+ rchan(map, 0x0a).rw(FUNC(swp00_device::sample_start_r<1>), FUNC(swp00_device::sample_start_w<1>));
+ rchan(map, 0x0b).rw(FUNC(swp00_device::sample_start_r<0>), FUNC(swp00_device::sample_start_w<0>));
+
+ // 0c-0f: meg offsets
+ // 10-1b: meg values
+
+ rchan(map, 0x20).rw(FUNC(swp00_device::lpf_info_r<1>), FUNC(swp00_device::lpf_info_w<1>));
+ rchan(map, 0x21).rw(FUNC(swp00_device::lpf_info_r<0>), FUNC(swp00_device::lpf_info_w<0>));
+ rchan(map, 0x22).rw(FUNC(swp00_device::lpf_speed_r), FUNC(swp00_device::lpf_speed_w));
+ rchan(map, 0x23).rw(FUNC(swp00_device::lfo_famod_depth_r), FUNC(swp00_device::lfo_famod_depth_w));
+ rchan(map, 0x24).rw(FUNC(swp00_device::lfo_step_r), FUNC(swp00_device::lfo_step_w));
+ rchan(map, 0x25).rw(FUNC(swp00_device::lfo_pmod_depth_r), FUNC(swp00_device::lfo_pmod_depth_w));
+ rchan(map, 0x26).rw(FUNC(swp00_device::attack_speed_r), FUNC(swp00_device::attack_speed_w));
+ rchan(map, 0x27).rw(FUNC(swp00_device::attack_level_r), FUNC(swp00_device::attack_level_w));
+ rchan(map, 0x28).rw(FUNC(swp00_device::decay_speed_r), FUNC(swp00_device::decay_speed_w));
+ rchan(map, 0x29).rw(FUNC(swp00_device::decay_level_r), FUNC(swp00_device::decay_level_w));
+ rchan(map, 0x2a).rw(FUNC(swp00_device::rev_level_r), FUNC(swp00_device::rev_level_w));
+ rchan(map, 0x2b).rw(FUNC(swp00_device::dry_level_r), FUNC(swp00_device::dry_level_w));
+ rchan(map, 0x2c).rw(FUNC(swp00_device::cho_level_r), FUNC(swp00_device::cho_level_w));
+ rchan(map, 0x2d).rw(FUNC(swp00_device::var_level_r), FUNC(swp00_device::var_level_w));
+ rchan(map, 0x2e).rw(FUNC(swp00_device::glo_level_r), FUNC(swp00_device::glo_level_w));
+ rchan(map, 0x2f).rw(FUNC(swp00_device::panning_r), FUNC(swp00_device::panning_w));
+ rchan(map, 0x30).rw(FUNC(swp00_device::sample_dpcm_and_format_r), FUNC(swp00_device::sample_dpcm_and_format_w));
+ rchan(map, 0x31).rw(FUNC(swp00_device::sample_address_r<2>), FUNC(swp00_device::sample_address_w<2>));
+ rchan(map, 0x32).rw(FUNC(swp00_device::sample_address_r<1>), FUNC(swp00_device::sample_address_w<1>));
+ rchan(map, 0x33).rw(FUNC(swp00_device::sample_address_r<0>), FUNC(swp00_device::sample_address_w<0>));
+ rchan(map, 0x34).rw(FUNC(swp00_device::pitch_r<1>), FUNC(swp00_device::pitch_w<1>));
+ rchan(map, 0x35).rw(FUNC(swp00_device::pitch_r<0>), FUNC(swp00_device::pitch_w<0>));
+ rchan(map, 0x36).rw(FUNC(swp00_device::sample_end_r<1>), FUNC(swp00_device::sample_end_w<1>));
+ rchan(map, 0x37).rw(FUNC(swp00_device::sample_end_r<0>), FUNC(swp00_device::sample_end_w<0>));
+
+ rctrl(map, 0x00); // 01 at startup
+ rctrl(map, 0x01).rw(FUNC(swp00_device::state_r), FUNC(swp00_device::state_adr_w));
+ rctrl(map, 0x02).rw(FUNC(swp00_device::waverom_access_r), FUNC(swp00_device::waverom_access_w));
+ rctrl(map, 0x03).r(FUNC(swp00_device::waverom_val_r));
+ rctrl(map, 0x04).rw(FUNC(swp00_device::meg_control_r), FUNC(swp00_device::meg_control_w));
+ rctrl(map, 0x08).w(FUNC(swp00_device::keyon_w<3>));
+ rctrl(map, 0x09).w(FUNC(swp00_device::keyon_w<2>));
+ rctrl(map, 0x0a).w(FUNC(swp00_device::keyon_w<1>));
+ rctrl(map, 0x0b).w(FUNC(swp00_device::keyon_w<0>));
+ rctrl(map, 0x0c); // 00 at startup
+ rctrl(map, 0x0d); // 00 at startup
+ rctrl(map, 0x0e); // 00 at startup
+
+ map(0x180, 0x1ff).rw(FUNC(swp00_device::offset_r), FUNC(swp00_device::offset_w));
+ map(0x200, 0x37f).rw(FUNC(swp00_device::const_r), FUNC(swp00_device::const_w));
}
-static u8 rr[0x20*0x20];
+// Voice control
-u8 swp00_device::snd_r(offs_t offset)
+void swp00_device::slot8_w(offs_t offset, u8 data)
{
- if(0) {
- int chan = (offset >> 6) & 0x3f;
- int slot = offset & 0x3f;
- std::string preg = "-";
- if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
- else if(slot == 0x30 || slot == 0x31)
- preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
- else if(slot == 0x0e || slot == 0x0f)
- preg = util::string_format("ct%02x", (slot-0x0e) + 2*chan);
- else
- preg = util::string_format("%02x.%02x", chan, slot);
- logerror("snd_r [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, rr[offset]);
- }
- if(0 && offset == 0x080f)
- machine().debug_break();
- if(offset == 0x080f)
- return 0;
- return rr[offset];
+ if(data == 0x80)
+ return;
+ logerror("slot8[%02x] = %02x\n", offset >> 1, data);
}
-void swp00_device::snd_w(offs_t offset, u8 data)
+void swp00_device::slot9_w(offs_t offset, u8 data)
+{
+ if(data == 0x00)
+ return;
+ logerror("slot9[%02x] = %02x\n", offset >> 1, data);
+}
+
+template<int sel> void swp00_device::lpf_info_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ u16 old = m_lpf_info[chan];
+ m_stream->update();
+
+ m_lpf_info[chan] = (m_lpf_info[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ if(m_lpf_info[chan] == old)
+ return;
+
+ // if(!sel)
+ // logerror("lpf_info[%02x] = %04x\n", chan, m_lpf_info[chan]);
+
+ u32 fb = m_lpf_info[chan] >> 11;
+ u32 level = m_lpf_info[chan] & 0x7ff;
+ if(fb < 4 && level > 0x7c0)
+ level = 0x7c0;
+ if(level)
+ level |= 0x800;
+ m_lpf_feedback[chan] = (fb + 4) << 21;
+ m_lpf_target_value[chan] = level << 14;
+}
+
+template<int sel> u8 swp00_device::lpf_info_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lpf_info[chan] >> (8*sel);
+}
+
+void swp00_device::lpf_speed_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lpf_speed[chan] == data)
+ return;
+ m_stream->update();
+ m_lpf_speed[chan] = data;
+ // logerror("lpf_speed[%02x] = %02x\n", chan, m_lpf_speed[chan]);
+}
+
+u8 swp00_device::lpf_speed_r(offs_t offset)
{
- if(rr[offset] == data)
+ int chan = offset >> 1;
+ return m_lpf_speed[chan];
+}
+
+void swp00_device::lfo_famod_depth_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lfo_famod_depth[chan] == data)
return;
+ m_stream->update();
+ m_lfo_famod_depth[chan] = data;
+ // logerror("lfo_famod_depth[%02x] = %02x\n", chan, m_lfo_famod_depth[chan]);
+}
+
+u8 swp00_device::lfo_famod_depth_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lfo_famod_depth[chan];
+}
+
+void swp00_device::rev_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_rev_level[chan] == data)
+ return;
+ m_stream->update();
+ m_rev_level[chan] = data;
+ // logerror("rev_level[%02x] = %02x\n", chan, m_rev_level[chan]);
+}
+
+u8 swp00_device::rev_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_rev_level[chan];
+}
+
+void swp00_device::dry_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_dry_level[chan] == data)
+ return;
+ m_stream->update();
+ m_dry_level[chan] = data;
+ // logerror("dry_level[%02x] = %02x\n", chan, m_dry_level[chan]);
+}
+
+u8 swp00_device::dry_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_dry_level[chan];
+}
+
+void swp00_device::cho_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_cho_level[chan] == data)
+ return;
+ m_stream->update();
+ m_cho_level[chan] = data;
+ // logerror("cho_level[%02x] = %02x\n", chan, m_cho_level[chan]);
+}
+
+u8 swp00_device::cho_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_cho_level[chan];
+}
+
+void swp00_device::var_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_var_level[chan] == data)
+ return;
+ m_stream->update();
+ m_var_level[chan] = data;
+ // logerror("var_level[%02x] = %02x\n", chan, m_var_level[chan]);
+}
+
+u8 swp00_device::var_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_var_level[chan];
+}
+
+void swp00_device::glo_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_glo_level[chan] == data)
+ return;
+ m_glo_level[chan] = data;
+ // logerror("glo_level[%02x] = %02x\n", chan, m_glo_level[chan]);
+}
+
+u8 swp00_device::glo_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_glo_level[chan];
+}
+
+void swp00_device::panning_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_panning[chan] == data)
+ return;
+ m_stream->update();
+ m_panning[chan] = data;
+ // logerror("panning[%02x] = %02x\n", chan, m_panning[chan]);
+}
+
+u8 swp00_device::panning_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_panning[chan];
+}
+
+void swp00_device::attack_speed_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_attack_speed[chan] == data)
+ return;
+ m_stream->update();
+ m_attack_speed[chan] = data;
+ logerror("attack_speed[%02x] = %02x\n", chan, m_attack_speed[chan]);
+}
+
+u8 swp00_device::attack_speed_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_attack_speed[chan];
+}
+
+void swp00_device::attack_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_attack_level[chan] == data)
+ return;
+ m_stream->update();
+ m_attack_level[chan] = data;
+ logerror("attack_level[%02x] = %02x\n", chan, m_attack_level[chan]);
+}
+
+u8 swp00_device::attack_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_attack_level[chan];
+}
+
+void swp00_device::decay_speed_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_decay_speed[chan] == data)
+ return;
+
+ m_stream->update();
+ m_decay_speed[chan] = data;
+
+ if(data & 0x80)
+ m_decay[chan] = true;
+
+ logerror("decay_speed[%02x] = %02x\n", chan, m_decay_speed[chan]);
+}
+
+u8 swp00_device::decay_speed_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_decay_speed[chan];
+}
+
+void swp00_device::decay_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_decay_level[chan] == data)
+ return;
+ m_stream->update();
+ m_decay_level[chan] = data;
+ logerror("decay_level[%02x] = %02x\n", chan, m_decay_level[chan]);
+}
+
+u8 swp00_device::decay_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_decay_level[chan];
+}
+
+template<int sel> void swp00_device::pitch_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ u16 old = m_pitch[chan];
+ m_stream->update();
+ m_pitch[chan] = (m_pitch[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ if(m_pitch[chan] == old)
+ return;
+ // if(!sel)
+ // logerror("pitch[%02x] = %04x\n", chan, m_pitch[chan]);
+}
+
+template<int sel> u8 swp00_device::pitch_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_pitch[chan] >> (8*sel);
+}
+
+template<int sel> void swp00_device::sample_start_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_start[chan] = (m_sample_start[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_start[%02x] = %04x\n", chan, m_sample_start[chan]);
+}
+
+template<int sel> u8 swp00_device::sample_start_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_start[chan] >> (8*sel);
+}
+
+template<int sel> void swp00_device::sample_end_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_end[chan] = (m_sample_end[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_end[%02x] = %04x\n", chan, m_sample_end[chan]);
+}
+
+template<int sel> u8 swp00_device::sample_end_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_end[chan] >> (8*sel);
+}
+
+void swp00_device::sample_dpcm_and_format_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_dpcm_and_format[chan] = data;
+ // logerror("sample_dpcm_and_format[%02x] = %02x\n", chan, m_sample_dpcm_and_format[chan]);
+}
+
+u8 swp00_device::sample_dpcm_and_format_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_dpcm_and_format[chan];
+}
+
+template<int sel> void swp00_device::sample_address_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_address[chan] = (m_sample_address[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_address[%02x] = %04x\n", chan, m_sample_address[chan]);
+}
+
+template<int sel> u8 swp00_device::sample_address_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_address[chan] >> (8*sel);
+}
+
+void swp00_device::lfo_step_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lfo_step[chan] == data)
+ return;
+ m_stream->update();
+
+ m_lfo_step[chan] = data;
+ // logerror("lfo_step[%02x] = %02x\n", chan, m_lfo_step[chan]);
+}
+
+u8 swp00_device::lfo_step_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lfo_step[chan];
+}
+
+void swp00_device::lfo_pmod_depth_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lfo_pmod_depth[chan] == data)
+ return;
+ m_stream->update();
+
+ m_lfo_pmod_depth[chan] = data;
+ // logerror("lfo_pmod_depth[%02x] = %02x\n", chan, m_lfo_pmod_depth[chan]);
+}
+
+u8 swp00_device::lfo_pmod_depth_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lfo_pmod_depth[chan];
+}
+
+void swp00_device::keyon(int chan)
+{
+ m_stream->update();
+ logerror("keyon %02x a=%02x/%02x d=%02x/%02x glo=%02x pan=%02x [%x %x %x %x]\n", chan, m_attack_speed[chan], m_attack_level[chan], m_decay_speed[chan], m_decay_level[chan], m_glo_level[chan], m_panning[chan], m_sample_start[chan], m_sample_end[chan], m_sample_address[chan], m_sample_dpcm_and_format[chan]);
+ m_lfo_phase[chan] = 0;
+ m_sample_pos[chan] = -m_sample_start[chan] << 15;
+
+ m_active[chan] = true;
+ m_decay[chan] = false;
+ m_decay_done[chan] = false;
- rr[offset] = data;
+ m_dpcm_current[chan] = 0;
+ m_dpcm_next[chan] = 0;
+ m_dpcm_address[chan] = m_sample_address[chan] - m_sample_start[chan];
+ m_dpcm_sum[chan] = 0;
- int chan = (offset >> 6) & 0x1f;
- int slot = offset & 0x3f;
+ m_lpf_value[chan] = m_lpf_target_value[chan];
+ m_lpf_timer[chan] = 0x4000000;
+ m_lpf_ha[chan] = 0;
+ m_lpf_hb[chan] = 0;
- std::string preg = "-";
-#if 0
- if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
- else if(slot == 0x0e || slot == 0x0f)
- preg = util::string_format("sy%02x", (slot-0x0e) + 2*chan);
- else if(slot == 0x30 || slot == 0x31)
- preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
- else if(slot == 0x38)
- preg = util::string_format("vl%02x", chan);
- else if(slot == 0x3e || slot == 0x3f)
- preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan);
+ m_glo_level_cur[chan] = m_glo_level[chan] << 4;
+ m_pan_l[chan] = panmap[m_panning[chan] >> 4];
+ m_pan_r[chan] = panmap[m_panning[chan] & 15];
+
+ if(m_decay_speed[chan] & 0x80) {
+ m_envelope_level[chan] = 0;
+ m_decay[chan] = true;
+ } else if((m_attack_speed[chan] & 0x80) || m_attack_level[chan])
+ m_envelope_level[chan] = m_attack_level[chan] << 20;
+ else
+ m_envelope_level[chan] = 0x8000000;
+}
+
+template<int sel> void swp00_device::keyon_w(u8 data)
+{
+ for(int i=0; i < 8; i++)
+ if(BIT(data, i))
+ keyon(8*sel+i);
+}
+
+void swp00_device::offset_w(offs_t offset, u8 data)
+{
+ m_stream->update();
+
+ if(offset & 1)
+ m_offset[offset >> 1] = (m_offset[offset >> 1] & 0xff00) | data;
+ else
+ m_offset[offset >> 1] = (m_offset[offset >> 1] & 0x00ff) | (data << 8);
+ if(0)
+ if(offset & 1)
+ logerror("offset[%02x] = %04x\n", 3*(offset >> 1), m_offset[offset >> 1]);
+}
+
+u8 swp00_device::offset_r(offs_t offset)
+{
+ if(offset & 1)
+ return m_offset[offset >> 1];
+ else
+ return m_offset[offset >> 1] >> 8;
+}
+
+void swp00_device::const_w(offs_t offset, u8 data)
+{
+ m_stream->update();
+
+ if(offset & 1)
+ m_const[offset >> 1] = (m_const[offset >> 1] & 0xff00) | data;
else
-#endif
- preg = util::string_format("%02x.%02x", chan, slot);
+ m_const[offset >> 1] = (m_const[offset >> 1] & 0x00ff) | (data << 8);
+ if(0)
+ if(offset & 1)
+ logerror("const[%02x] = %04x\n", offset >> 1, m_const[offset >> 1]);
+}
+
+u8 swp00_device::const_r(offs_t offset)
+{
+ if(offset & 1)
+ return m_const[offset >> 1];
+ else
+ return m_const[offset >> 1] >> 8;
+}
+
+void swp00_device::waverom_access_w(u8 data)
+{
+ m_waverom_access = data;
+}
+
+u8 swp00_device::waverom_access_r()
+{
+ return 0x00; // non-zero = busy reading the rom
+}
+
+u8 swp00_device::waverom_val_r()
+{
+ u8 val = read_byte(m_sample_address[0x1f]);
+ logerror("waverom read adr=%08x -> %02x\n", m_sample_address[0x1f], val);
+ m_sample_address[0x1f] = (m_sample_address[0x1f] + 1) & 0xffffff;
+ return val;
+}
- logerror("snd_w [%04x %04x] %-5s, %02x\n", offset, offset*2, preg, data);
+void swp00_device::meg_control_w(u8 data)
+{
+ m_meg_control = data;
+ logerror("meg_control %02x (variation %x, %s)\n", m_meg_control, m_meg_control >> 6, m_meg_control & 2 ? "mute" : "on");
+}
+
+u8 swp00_device::meg_control_r()
+{
+ return m_meg_control;
+}
+
+// Counters state access
+u8 swp00_device::state_r()
+{
+ m_stream->update();
+
+ int chan = m_state_adr & 0x1f;
+ switch(m_state_adr & 0xe0) {
+ case 0x00: // lpf value
+ return (m_lpf_value[chan] >> 20) | (m_lpf_done[chan] ? 0x80 : 0x00);
+
+ case 0x40: { // Envelope state
+ if(!m_active[chan])
+ return 0xff;
+
+ u8 vol;
+ if(m_decay[chan] || m_attack_level[chan] || (m_attack_speed[chan] & 0x80))
+ vol = m_envelope_level[chan] >> 22;
+ else
+ vol = 0;
+
+ if(m_decay_done[chan])
+ vol |= 0x40;
+ if(m_decay[chan])
+ vol |= 0x80;
+
+ return vol;
+ }
+
+ case 0x60: // global level
+ return (m_glo_level_cur[chan] >> 6) | ((m_glo_level_cur[chan] == (m_glo_level[chan] << 4)) ? 0x80 : 0x00);
+
+ case 0x80: // panning l
+ return (m_pan_l[chan] >> 6) | ((m_pan_l[chan] == panmap[m_panning[chan] >> 4]) ? 0x80 : 0x00);
+
+ case 0xa0: // panning r
+ return (m_pan_r[chan] >> 6) | ((m_pan_r[chan] == panmap[m_panning[chan] & 15]) ? 0x80 : 0x00);
+ }
+
+ logerror("state %02x unsupported\n");
+ return 0;
+}
+
+void swp00_device::state_adr_w(u8 data)
+{
+ m_state_adr = data;
+}
+
+
+// Catch-all
+
+u8 swp00_device::snd_r(offs_t offset)
+{
+ logerror("snd_r [%03x]\n", offset);
+ return 0;
+}
+
+void swp00_device::snd_w(offs_t offset, u8 data)
+{
+ logerror("snd_w [%03x] %02x\n", offset, data);
}
// Synthesis
-void swp00_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+s32 swp00_device::rext(int reg) const
+{
+ s32 val = m_const[reg] & 0x3ff;
+ if(val > 0x200) // Not 100% a real 2-complement fixed-point, e.g. the max value is positive, not negative
+ val |= 0xfffffc00;
+ return val;
+}
+
+s32 swp00_device::m7v(s32 value, s32 mult)
+{
+ return (s64(value) * mult) >> 7;
+}
+
+s32 swp00_device::m7(s32 value, int reg) const
+{
+ return m7v(value, rext(reg));
+}
+
+s32 swp00_device::m9v(s32 value, s32 mult)
+{
+ return (s64(value) * mult) >> 9;
+}
+
+s32 swp00_device::m9(s32 value, int reg) const
+{
+ return m9v(value, rext(reg));
+}
+
+template<size_t size> swp00_device::delay_block<size>::delay_block(swp00_device *swp, std::array<s32, size> &buffer) :
+ m_swp(swp),
+ m_buffer(buffer)
+{
+}
+
+template<size_t size> s32 swp00_device::delay_block<size>::r(int offreg) const
+{
+ return m_buffer[(m_swp->m_buffer_offset + m_swp->m_offset[offreg/3]) & (size - 1)];
+}
+
+template<size_t size> void swp00_device::delay_block<size>::w(int offreg, s32 value) const
+{
+ m_buffer[(m_swp->m_buffer_offset + m_swp->m_offset[offreg/3]) & (size - 1)] = value;
+}
+
+template<size_t size> s32 swp00_device::delay_block<size>::rlfo(int offreg, u32 phase, s32 delta_phase, int levelreg) const
+{
+ // Phase is on 23 bits
+ // Delta phase is on 10 bits shifts for a maximum of a full period (e.g. left shift of 13)
+ // Phase is wrapped into a triangle on 22 bits
+ // Level register is 10 bits where 1 = 4 samples of offset, for a maximum of 4096 samples
+
+ u32 lfo_phase = lfo_wrap(phase, delta_phase);
+
+ // Offset is 12.22
+ u64 lfo_offset = lfo_phase * m_swp->rext(levelreg);
+ u32 lfo_i_offset = lfo_offset >> 22;
+ s32 lfo_i_frac = lfo_offset & 0x3fffff;
+
+ // Uses in reality offreg and offreg+3 (which are offset by 1)
+ u32 pos = m_swp->m_buffer_offset + m_swp->m_offset[offreg/3] + lfo_i_offset;
+ s32 val0 = m_buffer[pos & (size - 1)];
+ s32 val1 = m_buffer[(pos + 1) & (size - 1)];
+
+ // fprintf(stderr, "lfo %02x %x %x\n", offreg, val0, val1);
+ return s32((val1 * s64(lfo_i_frac) + val0 * s64(0x400000 - lfo_i_frac)) >> 22);
+}
+
+template<size_t size> s32 swp00_device::delay_block<size>::rlfo2(int offreg, s32 offset) const
+{
+ // Offset is 12.11
+ u32 lfo_i_offset = offset >> 11;
+ s32 lfo_i_frac = offset & 0x7ff;
+
+ // Uses in reality offreg and offreg+3 (which are offset by 1)
+ u32 pos = m_swp->m_buffer_offset + m_swp->m_offset[offreg/3] + lfo_i_offset;
+ s32 val0 = m_buffer[pos & (size - 1)];
+ s32 val1 = m_buffer[(pos + 1) & (size - 1)];
+
+ // fprintf(stderr, "lfo %02x %x %x\n", offreg, val0, val1);
+ return s32((val1 * s64(lfo_i_frac) + val0 * s64(0x800 - lfo_i_frac)) >> 11);
+}
+
+s32 swp00_device::lfo_get_step(int reg) const
+{
+ u32 e = (m_const[reg] >> 7) & 7;
+ return (m_const[reg] & 0x7f) << (e == 7 ? 15 : e);
+}
+
+void swp00_device::lfo_step(u32 &phase, int reg) const
+{
+ phase = (phase + lfo_get_step(reg)) & 0x7fffff;
+}
+
+s32 swp00_device::lfo_saturate(s32 phase)
+{
+ if(phase < -0x400000)
+ return -0x400000;
+ if(phase >= 0x400000)
+ return 0x3fffff;
+ return phase;
+}
+
+u32 swp00_device::lfo_wrap(s32 phase, s32 delta_phase)
+{
+ s32 lfo_phase = (phase - (delta_phase << 13)) & 0x7fffff;
+ if(lfo_phase & 0x400000)
+ lfo_phase ^= 0x7fffff;
+ return lfo_phase;
+}
+
+void swp00_device::filtered_lfo_step(s32 &position, s32 phase, int deltareg, int postdeltareg, int scalereg, int feedbackreg)
+{
+ s32 phase1 = lfo_saturate((deltareg == -1 ? phase : lfo_wrap(phase, deltareg)) - (rext(postdeltareg) << 13));
+ s64 phase2 = s64(lfo_get_step(scalereg)) * phase1 + s64(0x400000 - lfo_get_step(feedbackreg)) * position;
+ position = phase2 >> 22;
+}
+
+s32 swp00_device::alfo(u32 phase, s32 delta_phase, int levelreg, int offsetreg, bool sub) const
+{
+ u32 lfo_phase = lfo_wrap(phase, delta_phase);
+ s32 offset = rext(offsetreg);
+ if(sub)
+ offset = -offset;
+ s32 base = s32((s64(lfo_phase) * rext(levelreg)) >> 19) + (offset << 3);
+ s32 bamp = ((base & 0x1ff) | 0x200) << ((base >> 9) & 15);
+ bamp >>= 8;
+ if(bamp <= -0x200)
+ bamp = -0x1ff;
+ else if(bamp >= 0x200)
+ bamp = 0x200;
+ return bamp;
+}
+
+s32 swp00_device::lfo_mod(s32 phase, int scalereg) const
+{
+ return (m9(phase, scalereg) >> 13) + 0x200;
+}
+
+s32 swp00_device::lfo_scale(s32 phase, int scalereg) const
+{
+ return lfo_saturate((phase - (rext(scalereg) << 13)) * 4);
+}
+
+s32 swp00_device::lfo_wrap_reg(s32 phase, int deltareg) const
+{
+ return lfo_wrap(phase, rext(deltareg));
+}
+
+s32 swp00_device::sx(int reg) const
+{
+ s32 mult = m_const[reg];
+ if(mult & 0x200)
+ mult |= 0xfffffc00;
+ return mult;
+}
+
+double swp00_device::sx7(int reg) const
+{
+ return sx(reg) / 128.0;
+}
+
+double swp00_device::sx9(int reg) const
{
+ return sx(reg) / 512.0;
+}
+
+s32 swp00_device::saturate(s32 value)
+{
+ if(value <= -0x20000)
+ return -0x20000;
+ else if(value > 0x1ffff)
+ return 0x1ffff;
+ else
+ return value;
+}
+
+double v2f2(s32 value)
+{
+ return (1.0 - (value & 0xffffff) / 33554432.0) / (1 << (value >> 24));
+}
+
+void swp00_device::sound_stream_update(sound_stream &stream)
+{
+ const delay_block brev(this, m_rev_buffer);
+ const delay_block bcho(this, m_cho_buffer);
+ const delay_block bvar(this, m_var_buffer);
+
+ for(int i=0; i != stream.samples(); i++) {
+ s32 dry_l = 0, dry_r = 0;
+ s32 rev = 0;
+ s32 cho_l = 0, cho_r = 0;
+ s32 var_l = 0, var_r = 0;
+
+ for(int chan = 0; chan != 32; chan++) {
+ if(!m_active[chan])
+ continue;
+
+ u32 lfo_phase = m_lfo_phase[chan] >> 7;
+ s32 lfo_p_phase = lfo_phase ^ (m_lfo_step[chan] & 0x40 ? lfo_shape_centered_tri : lfo_shape_centered_saw)[lfo_phase >> 18];
+ s32 lfo_fa_phase = lfo_phase ^ (m_lfo_step[chan] & 0x40 ? lfo_shape_offset_tri : lfo_shape_offset_saw )[lfo_phase >> 18];
+
+ s16 val0, val1;
+ u32 base_address = m_sample_address[chan];
+ s32 spos = m_sample_pos[chan] >> 15;
+ switch(m_sample_dpcm_and_format[chan] >> 6) {
+ case 0: { // 16-bits linear
+ offs_t adr = base_address + (spos << 1);
+ val0 = read_word(adr);
+ val1 = read_word(adr+2);
+ break;
+ }
+
+ case 1: { // 12-bits linear
+ offs_t adr = base_address + (spos >> 2)*6;
+ switch(spos & 3) {
+ case 0: { // Cabc ..AB .... ....
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+2);
+ val0 = (w0 & 0x0fff) << 4;
+ val1 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+ break;
+ }
+ case 1: { // c... BCab ...A ....
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+2);
+ u16 w2 = read_word(adr+4);
+ val0 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+ val1 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
+ break;
+ }
+ case 2: { // .... bc.. ABCa ....
+ u16 w1 = read_word(adr+2);
+ u16 w2 = read_word(adr+4);
+ val0 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
+ val1 = w2 & 0xfff0;
+ break;
+ }
+ case 3: { // .... .... abc. .ABC
+ u16 w2 = read_word(adr+4);
+ u16 w3 = read_word(adr+6);
+ val0 = w2 & 0xfff0;
+ val1 = (w3 & 0x0fff) << 4;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 2: // 8-bits linear
+ val0 = (read_byte(base_address + spos) << 8);
+ val1 = (read_byte(base_address + spos + 1) << 8);
+ break;
+
+ case 3: { // 8-bits delta-pcm
+ u8 offset = dpcm_offset[m_sample_dpcm_and_format[chan] & 3];
+ u8 scale = (m_sample_dpcm_and_format[chan] >> 2) & 7;
+ u32 target_address = base_address + spos + 1;
+ while(m_dpcm_address[chan] <= target_address) {
+ m_dpcm_current[chan] = m_dpcm_next[chan];
+ m_dpcm_sum[chan] += m_dpcm[read_byte(m_dpcm_address[chan])] - offset;
+ s32 sample = (m_dpcm_sum[chan] << scale) >> 3;
+ m_dpcm_address[chan] ++;
+ if(sample < -0x8000)
+ sample = -0x8000;
+ else if(sample > 0x7fff)
+ sample = 0x7fff;
+ m_dpcm_next[chan] = sample;
+ }
+ val0 = m_dpcm_current[chan];
+ val1 = m_dpcm_next[chan];
+ break;
+ }
+ }
+
+ s32 mul = m_sample_pos[chan] & 0x7fff;
+ s32 sample = (val1 * mul + val0 * (0x8000 - mul)) >> 7;
+
+ s32 lpf_value = m_lpf_value[chan] + ((lfo_fa_phase * (m_lfo_famod_depth[chan] >> 5)) << (m_lfo_step[chan] & 0x40 ? 2 : 1));
+
+ m_lpf_ha[chan] += lpffpapply(lpf_value, sample - 2*fpapply(m_lpf_feedback[chan], m_lpf_ha[chan]) - m_lpf_hb[chan]);
+ m_lpf_hb[chan] += lpffpapply(lpf_value, m_lpf_ha[chan]);
+
+ sample = m_lpf_hb[chan];
+
+ s32 envelope_level;
+ if(m_decay[chan] || m_attack_level[chan] || (m_attack_speed[chan] & 0x80))
+ envelope_level = m_envelope_level[chan];
+ else
+ envelope_level = 0;
+
+ s32 tremolo_level = (lfo_fa_phase * (m_lfo_famod_depth[chan] & 0x1f)) << ((m_lfo_step[chan] & 0x40) ? 3 : 2);
+
+ dry_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_dry_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
+ dry_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_dry_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
+ rev += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_rev_level[chan] << 20), sample);
+ cho_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_cho_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
+ cho_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_cho_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
+ var_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_var_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
+ var_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_var_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
+
+ m_lfo_phase[chan] = (m_lfo_phase[chan] + m_global_step[0x20 + (m_lfo_step[chan] & 0x3f)]) & 0x7ffffff;
+
+ u32 sample_increment = ((m_pitch[chan] & 0xfff) << (8 + (s16(m_pitch[chan]) >> 12))) >> 4;
+ m_sample_pos[chan] += (sample_increment * (0x800 + ((lfo_p_phase * m_lfo_pmod_depth[chan]) >> (m_lfo_step[chan] & 0x40 ? 18 : 19)))) >> 11;
+
+ if((m_sample_pos[chan] >> 15) >= m_sample_end[chan]) {
+ if(!m_sample_end[chan])
+ m_active[chan] = false;
+ else {
+ s32 prev = m_sample_pos[chan];
+ do
+ m_sample_pos[chan] -= m_sample_end[chan] << 15;
+ while((m_sample_pos[chan] >> 15) >= m_sample_end[chan]);
+ m_dpcm_address[chan] += (m_sample_pos[chan] >> 15) - (prev >> 15);
+ m_dpcm_sum[chan] = 0;
+ }
+ }
+
+ if(m_lpf_speed[chan] & 0x80)
+ m_lpf_done[chan] = istep(m_lpf_timer[chan], 0, m_global_step[m_lpf_speed[chan] & 0x7f] >> 1);
+ else
+ m_lpf_done[chan] = istep(m_lpf_value[chan], m_lpf_target_value[chan], m_global_step[m_lpf_speed[chan]] >> 1);
+
+ istep(m_glo_level_cur[chan], m_glo_level[chan] << 4, 1);
+ istep(m_pan_l[chan], panmap[m_panning[chan] >> 4], 1);
+ istep(m_pan_r[chan], panmap[m_panning[chan] & 15], 1);
+
+ if(m_decay[chan]) {
+ if((m_decay_speed[chan] & 0x60) == 0x60)
+ m_decay_done[chan] = fpstep(m_envelope_level[chan], m_decay_level[chan] << 20, decay_linear_step[m_decay_speed[chan] & 0x1f]);
+ else
+ m_decay_done[chan] = istep(m_envelope_level[chan], m_decay_level[chan] << 20, m_global_step[m_decay_speed[chan]] << 1);
+ if(m_envelope_level[chan] & 0x8000000)
+ m_active[chan] = false;
+
+ } else if(m_attack_speed[chan] & 0x80)
+ m_decay[chan] = fpstep(m_envelope_level[chan], 0, attack_linear_step[m_attack_speed[chan] & 0x7f]);
+ else
+ m_decay[chan] = istep(m_envelope_level[chan], 0, m_global_step[m_attack_speed[chan]] << 1);
+ }
+
+ dry_l >>= 8;
+ dry_r >>= 8;
+ rev >>= 8;
+ cho_l >>= 8;
+ cho_r >>= 8;
+ var_l >>= 8;
+ var_r >>= 8;
+
+
+ // Variation block
+ // Update the output volume
+ m_var_vol = m9(m_var_vol, 0xbd) + m_const[0xbc];
+
+ // Scale the input
+ var_l = m7(var_l, 0x04);
+ var_r = m7(var_r, 0x07);
+
+ // Split depending on the variant selected
+ s32 var_out_l = 0, var_out_r = 0;
+
+ switch(m_meg_control & 0xc0) {
+ case 0x00: {
+ // Used by:
+ // - 2-band EQ
+ // - Auto Pan
+ // - Celeste
+ // - Chorus
+ // - Delays
+ // - Flanger
+ // - Rotary Speaker
+ // - Symphonic
+ // - Tremolo
+
+ // Two stages of filtering
+ s32 var_filter_l_2 = m7(m_var_filter_l_1, 0x7e) + m7(var_l, 0x7f) + m9(m_var_filter_l_2, 0x80);
+ s32 var_filtered_l = m7(m_var_filter_l_2, 0xa7) + m7(var_filter_l_2, 0xa8) + m9(m_var_filter_l_3, 0xa9);
+
+ m_var_filter_l_1 = var_l;
+ m_var_filter_l_2 = var_filter_l_2;
+ m_var_filter_l_3 = var_filtered_l;
+
+ s32 var_filter_r_2 = m7(m_var_filter_r_1, 0x98) + m7(var_r, 0x99) + m9(m_var_filter_r_2, 0x9a);
+ s32 var_filtered_r = m7(m_var_filter_r_2, 0x9b) + m7(var_filter_r_2, 0x9c) + m9(m_var_filter_r_3, 0x9d);
+
+ m_var_filter_r_1 = var_r;
+ m_var_filter_r_2 = var_filter_r_2;
+ m_var_filter_r_3 = var_filtered_r;
+
+ // Rest is like, complex and stuff
+ lfo_step(m_var_lfo_phase, 0x77);
+ s32 var_lfo_phase_2 = m7(m7(m_var_lfo_phase, 0x6d), 0x70) & 0x7fffff;
+
+ filtered_lfo_step(m_var_lfo1a, m_var_lfo_phase, 0x6e, 0x6f, 0x72, 0x71);
+ filtered_lfo_step(m_var_lfo2a, m_var_lfo_phase, 0x79, 0x7a, 0x7c, 0x7b);
+ filtered_lfo_step(m_var_lfo3a, m_var_lfo_phase, 0x88, 0x89, 0x8b, 0x8a);
+
+ s32 lfo1b = lfo_scale(m_var_lfo1a, 0x73);
+ s32 lfo2b = lfo_scale(m_var_lfo1a, 0x7d);
+ s32 lfo3b = lfo_scale(m_var_lfo1a, 0x8c);
+
+ s32 lfo1c = lfo_wrap_reg(var_lfo_phase_2, 0x74);
+ s32 lfo2c = lfo_wrap_reg(var_lfo_phase_2, 0x84);
+ s32 lfo3c = lfo_wrap_reg(var_lfo_phase_2, 0x8d);
+
+ filtered_lfo_step(m_var_lfo4a, lfo3c, -1, 0x8e, 0x90, 0x8f);
+ s32 lfo4b = lfo_scale(m_var_lfo4a, 0x91);
+
+ s32 tap1 = bvar.rlfo2(0x78, m9(lfo1b, 0x75) + m9(lfo1c, 0x76));
+ s32 tap2 = bvar.rlfo2(0x87, m9(lfo2b, 0x85) + m9(lfo2c, 0x86));
+ s32 tap3 = bvar.rlfo2(0x99, m9(lfo3b, 0x95) + m9(lfo3c, 0x96));
+ s32 tap4 = bvar.rlfo2(0xa8, m9(lfo4b, 0xa5));
+
+ s32 mod1 = lfo_mod(lfo1b, 0x83);
+ s32 mod2 = lfo_mod(lfo2b, 0x94);
+ s32 mod3 = lfo_mod(lfo3b, 0xa4);
+
+ m_var_lfo_h_1 = m9(m_var_lfo_h_1, 0x9e) + m9(tap1, 0x9f);
+ m_var_lfo_h_2 = m9(m_var_lfo_h_2, 0xa0) + m9(tap1, 0xa1);
+
+ bvar.w(0xae, var_filtered_l + m9(var_filtered_r, 0xaa) + m9(m_var_lfo_h_1, 0xab) + m9(m_var_lfo_h_2, 0xac));
+ bvar.w(0xb1, m9(var_filtered_r, 0xad) + m9(m_var_lfo_h_1, 0xae) + m9(m_var_lfo_h_2, 0xaf));
+
+ var_out_l = m9(var_filtered_l, 0xb2) + m9(var_filtered_r, 0xb3) + m9(m9v(tap2, mod1), 0xb4) + m9(m9v(tap3, mod3), 0xb5) + m9(tap4, 0xb6);
+ var_out_r = m9(var_filtered_l, 0xb7) + m9(var_filtered_r, 0xb8) + m9(m9v(tap2, mod2), 0xb9) + m9(m9v(tap3, mod3), 0xba) + m9(tap4, 0xbb);
+
+ break;
+ }
+
+ case 0x40: {
+ // Used by:
+ // - Phaser
+
+ // Two stages of filtering
+ s32 var_filter_l_2 = m7(m_var_filter_l_1, 0x6d) + m7(var_l, 0x6e) + m9(m_var_filter_l_2, 0x6f);
+ s32 var_filtered_l = m7(m_var_filter_l_2, 0x70) + m7(var_filter_l_2, 0x71) + m9(m_var_filter_l_3, 0x72);
+
+ m_var_filter_l_1 = var_l;
+ m_var_filter_l_2 = var_filter_l_2;
+ m_var_filter_l_3 = var_filtered_l;
+ s32 var_filter_r_2 = m7(m_var_filter_r_1, 0x73) + m7(var_r, 0x74) + m9(m_var_filter_r_2, 0x75);
+ s32 var_filtered_r = m7(m_var_filter_r_2, 0x76) + m7(var_filter_r_2, 0x77) + m9(m_var_filter_r_3, 0x78);
+
+ m_var_filter_r_1 = var_r;
+ m_var_filter_r_2 = var_filter_r_2;
+ m_var_filter_r_3 = var_filtered_r;
+
+ // A very funky amplitude lfo with a lot of stages
+ s32 var_raw_l = m9(m_var_filterp_l_4, 0x7b) + m9(m_var_filterp_l_5, 0x7c) + m9(m_var_filterp_l_6, 0x7d);
+ s32 var_raw_r = m9(m_var_filterp_r_4, 0x7e) + m9(m_var_filterp_r_5, 0x7f) + m9(m_var_filterp_r_6, 0x80);
+
+ s32 var_o_l = m9(var_raw_l, 0xa3) + m9(m_var_filterp_r_3, 0xa4) + m9(m_var_filterp_r_5, 0xa5);
+ s32 var_o_r = m9(var_raw_r, 0xa7);
+
+ lfo_step(m_var_lfo_phase, 0x79);
+ s32 alfo_l = 0x200 - alfo(m_var_lfo_phase, 0, 0x83, 0x82, false);
+ s32 alfo_r = 0x200 - alfo(m_var_lfo_phase, m_const[0x9c], 0x9e, 0x9d, false);
+
+ s32 var_l_1 = m9(var_filtered_l, 0x84) + m9(var_filtered_r, 0x85) + m9(var_raw_l, 0x86) + m9(var_raw_r, 0x87);
+ s32 var_l_2 = m_var_filterp_l_1 + m9v(m_var_filterp_l_2 - var_l_1, alfo_l);
+ m_var_filterp_l_1 = var_l_1;
+ s32 var_l_3 = m_var_filterp_l_2 + m9v(m_var_filterp_l_3 - var_l_2, alfo_l);
+ m_var_filterp_l_2 = var_l_2;
+ s32 var_l_4 = m_var_filterp_l_3 + m9v(m_var_filterp_l_4 - var_l_3, alfo_l);
+ m_var_filterp_l_3 = var_l_3;
+ s32 var_l_5 = m_var_filterp_l_4 + m9v(m_var_filterp_l_5 - var_l_4, alfo_l);
+ m_var_filterp_l_4 = var_l_4;
+ m_var_filterp_l_6 = m_var_filterp_l_5 + m9v(m_var_filterp_l_6 - var_l_5, alfo_l);
+ m_var_filterp_l_5 = var_l_5;
+
+ s32 var_r_1 = m9(var_filtered_r, 0x9f) + m9(var_raw_l, 0xa0) + m9(var_raw_r, 0xa1);
+ s32 var_r_2 = m_var_filterp_r_1 + m9v(m_var_filterp_r_2 - var_r_1, alfo_r);
+ m_var_filterp_r_1 = var_r_1;
+ s32 var_r_3 = m_var_filterp_r_2 + m9v(m_var_filterp_r_3 - var_r_2, alfo_r);
+ m_var_filterp_r_2 = var_r_2;
+ s32 var_r_4 = m_var_filterp_r_3 + m9v(m_var_filterp_r_4 - var_r_3, alfo_r);
+ m_var_filterp_r_3 = var_r_3;
+ s32 var_r_5 = m_var_filterp_r_4 + m9v(m_var_filterp_r_5 - var_r_4, alfo_r);
+ m_var_filterp_r_4 = var_r_4;
+ m_var_filterp_r_6 = m_var_filterp_r_5 + m9v(m_var_filterp_r_6 - var_r_5, alfo_r);
+ m_var_filterp_r_5 = var_r_5;
+
+ var_out_l = var_o_l + m9(var_filtered_l, 0xa2);
+ var_out_r = var_o_r + m9(var_filtered_r, 0xa6);
+ break;
+ }
+
+ case 0x80: {
+ // Used by:
+ // - 3-band EQ
+ // - Amp simulation
+ // - Distortion
+ // - Gating
+
+ // Compute a center value
+ s32 var_m = m9(var_l, 0x6d) + m9(var_r, 0x6e);
+
+ // Two stages of filtering on the center value
+ s32 var_filter_2 = m7(m_var_filter_1, 0x6f) + m7(var_m, 0x70) + m9(m_var_filter_2, 0x71);
+ s32 var_filtered = m7(m_var_filter_2, 0x72) + m7(var_filter_2, 0x73) + m9(m_var_filter_3, 0x74);
+
+ m_var_filter_1 = var_m;
+ m_var_filter_2 = var_filter_2;
+ m_var_filter_3 = var_filtered;
+
+ // Gating/ER reverb injection with some filtering
+ bvar.w(0x7e, m9(bvar.r(0x6c), 0x7b) + m9(var_m, 0x7c));
+ s32 tap0 = m7(bvar.r(0x6c), 0x7e) + m7(var_m, 0x7f);
+ bvar.w(0x84, m9(bvar.r(0x78), 0x81) + m9(tap0, 0x82));
+
+ s32 var_f3_1 = bvar.r(0x6f);
+ s32 var_f3_2 = m7(m_var_filter2_1, 0x77) + m7(var_f3_1, 0x78) + m9(m_var_filter3_2, 0x79);
+ bvar.w(0x87, m7(bvar.r(0x78), 0x84) + m7(tap0, 0x85) + m9(var_f3_2, 0x86));
+
+ m_var_filter3_1 = var_f3_1;
+ m_var_filter3_2 = var_f3_2;
+
+ // Multi-tap on reverb
+ s32 tap1 = m9(bvar.r(0x6f), 0x99) + m9(bvar.r(0x72), 0x9a) + m9(bvar.r(0x75), 0x9b) + m9(bvar.r(0x8d), 0x9c) + m9(bvar.r(0x90), 0x9d) + m9(bvar.r(0x93), 0x9e) + m9(bvar.r(0x96), 0x9f);
+ s32 tap2 = m9(bvar.r(0x9f), 0xb4) + m9(bvar.r(0xa2), 0xb5) + m9(bvar.r(0xa5), 0xb6) + m9(bvar.r(0xa8), 0xb7) + m9(bvar.r(0xab), 0xb8) + m9(bvar.r(0xae), 0xb9) + m9(bvar.r(0xb1), 0xba);
+
+ bvar.w(0xb7, tap1);
+ bvar.w(0xba, tap2);
+
+ s32 tap2b = tap2 + m9(brev.r(0xb4), 0xbb);
+ bvar.w(0x8a, m9(bvar.r(0x7b), 0x88) + m9(tap2b, 0x89));
+ s32 var_gate_l = m7(bvar.r(0x7b), 0x8b) + m7(tap2b, 0x8c);
+
+ s32 tap1b = tap1 + m9(brev.r(0x99), 0xa0);
+ bvar.w(0x9c, m9(bvar.r(0x81), 0x8e) + m9(tap1b, 0x8f));
+ s32 var_gate_r = m7(bvar.r(0x7b), 0x8b) + m7(tap1b, 0x8c);
+
+ // Distortion stage
+ s32 dist1 = saturate(m7(var_filtered, 0x76));
+ s32 dist2 = saturate(m7(dist1, 0x83));
+ s32 dist3 = saturate(m7(dist2, 0x87));
+ s32 dist4 = saturate(m7(dist3, 0x8a));
+ s32 dist5 = saturate(m7(dist4, 0x8d));
+ s32 dist6 = saturate(m7(dist5, 0x90));
+ s32 disto = m9(m9(dist1, 0x91) + m9(dist2, 0x92) + m9(dist3, 0x93) + m9(dist4, 0x94) + m9(dist5, 0x95) + m9(dist6, 0x96), 0xa1);
+
+ // Filtering again, 3 stages
+ s32 var_f2_2 = m7(m_var_filter2_1, 0xa2) + m7(disto, 0xa3) + m9(m_var_filter2_2a, 0xa4);
+ s32 var_f2_3 = m7(m_var_filter2_3b, 0xa5) + m7(m_var_filter2_3a, 0xa6) + m7(m_var_filter2_2b, 0xa7) + m7(m_var_filter2_2a, 0xa8) + m7(var_f2_2, 0xa9);
+ s32 var_f2_4 = m7(m_var_filter2_3a, 0xaa) + m7(var_f2_3, 0xab) + m9(m_var_filter2_4, 0xac);
+
+ m_var_filter2_1 = disto;
+ m_var_filter2_2b = m_var_filter2_2a;
+ m_var_filter2_2a = var_f2_2;
+ m_var_filter2_3b = m_var_filter2_3a;
+ m_var_filter2_3a = var_f2_3;
+ m_var_filter2_4 = var_f2_4;
+
+ // Mix in both paths
+ var_out_l = m9(var_l, 0xad) + m9(var_gate_l, 0xaf) + m9(var_f2_4, 0xb0);
+ var_out_r = m9(var_r, 0xb1) + m9(var_gate_r, 0xb2) + m9(var_f2_4, 0xb3);
+
+ break;
+ }
+
+ case 0xc0: {
+ // Used by:
+ // - Auto wah
+ // - Hall
+ // - Karaoke
+ // - Plate
+ // - Room
+ // - Stage
+
+ // Compute a center value
+ s32 var_m = m9(var_l, 0x6d) + m9(var_r, 0x6e);
+
+ // Two stages of filtering on the center value
+ s32 var_filter_2 = m7(m_var_filter_1, 0x6f) + m7(var_m, 0x70) + m9(m_var_filter_2, 0x71);
+ s32 var_filtered = m7(m_var_filter_2, 0x72) + m7(var_filter_2, 0x73) + m9(m_var_filter_3, 0x74);
+ m_var_filter_1 = var_m;
+ m_var_filter_2 = var_filter_2;
+ m_var_filter_3 = var_filtered;
+
+ // Inject in the reverb buffer and loop with filtering
+ s32 tap1a = bvar.r(0x6c); // 36 v19
+ s32 tap1b = bvar.r(0x6f); // 37 v21
+ s32 tap1c = bvar.r(0x72); // 38 v27
+
+ bvar.w(0x75, var_filtered + m9(tap1a, 0x75));
+ bvar.w(0x78, m9(tap1b, 0x76) + m9(tap1a, 0x77));
+
+ s32 tap2a = m7(tap1b, 0x78) + m7(tap1a, 0x79);
+
+ bvar.w(0x7b, m9(tap1b, 0x7a) + m9(tap2a, 0x7b));
+
+ s32 tap2b = m7(tap1c, 0x7c) + m7(tap2a, 0x7d);
+
+ s32 tap1d = bvar.r(0x9c);
+ s32 tap1e = bvar.r(0x9f);
+
+ bvar.w(0xa8, m9(m_var_h1, 0xa5) + m9(tap1d, 0xa6) + m9(tap2b, 0xa7));
+ m_var_h1 = tap1d;
+
+ bvar.w(0xae, m9(m_var_h2, 0xa8) + m9(tap1e, 0xa9) + m9(tap2b, 0xaa));
+ m_var_h2 = tap1e;
+
+ s32 tap1f = bvar.r(0xab);
+ s32 tap1g = bvar.r(0xb1);
+
+ bvar.w(0xb7, m9(m_var_h3, 0xb3) + m9(tap1f, 0xb4) + m9(tap2b, 0xb5));
+ m_var_h3 = tap1f;
+
+ bvar.w(0xba, m9(m_var_h4, 0xb6) + m9(tap1g, 0xb7) + m9(tap2b, 0xb8));
+ m_var_h4 = tap1g;
+
+ s32 tap1h = bvar.r(0x7e);
+
+ s32 tap3a = m9(bvar.r(0x81) + bvar.r(0x84) + bvar.r(0x87) + bvar.r(0x8a), 0x8f) + m9(tap1h, 0x93);
+ s32 tap3b = bvar.r(0xa5);
+ bvar.w(0xb4, m9(tap3b, 0xaf) + m9(tap3a, 0xb0));
+ s32 var_o_l = m7(tap3b, 0xb1) + m7(tap3a, 0xb2);
+
+ s32 tap4a = m9(bvar.r(0x8d) + bvar.r(0x90) + bvar.r(0x93) + bvar.r(0x96), 0x9c) + m9(tap1h, 0xa0);
+ s32 tap4b = bvar.r(0x99);
+ bvar.w(0xa2, m9(tap4b, 0xa1) + m9(tap4a, 0xa2));
+ s32 var_o_r = m7(tap4b, 0xa3) + m7(tap4a, 0xa4);
+
+ // auto-wah effect with lfo
+ // Two stages of filtering
+ s32 var_filter_l_2 = m7(m_var_filter_l_1, 0x80) + m7(var_l, 0x81) + m9(m_var_filter_l_2, 0x82);
+ s32 var_filtered_l = m7(m_var_filter_l_2, 0x83) + m7(var_filter_l_2, 0x84) + m9(m_var_filter_l_3, 0x85);
+
+ m_var_filter_l_1 = var_l;
+ m_var_filter_l_2 = var_filter_l_2;
+ m_var_filter_l_3 = var_filtered_l;
+ s32 var_filter_r_2 = m7(m_var_filter_r_1, 0x6f) + m7(var_r, 0x70) + m9(m_var_filter_r_2, 0x71);
+ s32 var_filtered_r = m7(m_var_filter_r_2, 0x72) + m7(var_filter_r_2, 0x73) + m9(m_var_filter_r_3, 0x74);
+
+ m_var_filter_r_1 = var_r;
+ m_var_filter_r_2 = var_filter_r_2;
+ m_var_filter_r_3 = var_filtered_r;
+
+ // Mixing
+ s32 var_w_l = m7(var_filtered_l, 0x94) + m7(var_filtered_r, 0x95);
+ s32 var_w_r = m7(var_filtered_r, 0x88);
+
+ // Amplitude LFO and filtering
+ lfo_step(m_var_lfo_phase, 0x7e);
+ s32 amp = alfo(m_var_lfo_phase, 0, 0x86, 0x87, true);
+
+ m_var_filterp_l_1 = m9v(m9(m_var_filterp_l_1, 0x89) + m9(m_var_filterp_l_2, 0x8a) + var_w_l, amp) + m9(m_var_filterp_l_1, 0x8b);
+ m_var_filterp_l_2 = m9v(m_var_filterp_l_1, amp) + m9(m_var_filterp_l_2, 0x8d);
+
+ m_var_filterp_r_1 = m9v(m9(m_var_filterp_r_1, 0x96) + m9(m_var_filterp_r_2, 0x97) + var_w_r, amp) + m9(m_var_filterp_r_1, 0x98);
+ m_var_filterp_r_2 = m9v(m_var_filterp_r_1, amp) + m9(m_var_filterp_r_2, 0x9a);
+
+ var_out_l = m9(var_filtered_l, 0xb9) + m9(m_var_filterp_l_1, 0xba) + m9(var_o_l, 0xbb);
+ var_out_r = m9(var_filtered_r, 0xab) + m9(var_r, 0xac) + m9(m_var_filterp_r_1, 0xad) + m9(var_o_r, 0xae);
+ break;
+ }
+ }
+
+
+ // Chorus block
+ // Update the output volume
+ m_cho_vol = m9(m_cho_vol, 0x58) + m_const[0x57];
+
+ // Scale the input
+ cho_l = m7(cho_l, 0x02);
+ cho_r = m7(cho_r, 0x05);
+
+ // Add in the other channels
+ cho_l += m9v(m7(var_out_l, 0x03), m_var_vol);
+ cho_r += m9v(m7(var_out_r, 0x06), m_var_vol);
+
+ // A LFO with (up to) three phases to pick up the reverb
+ lfo_step(m_cho_lfo_phase, 0x09);
+
+ s32 cho_lfo_1 = bcho.rlfo(0x1b, m_cho_lfo_phase, 0, 0x1a);
+ s32 cho_lfo_2 = bcho.rlfo(0x2a, m_cho_lfo_phase, m_const[0x25], 0x28);
+ s32 cho_lfo_3 = bcho.rlfo(0x39, m_cho_lfo_phase, m_const[0x34], 0x37);
+
+ // Two stages of filtering
+ s32 cho_filter_r_2 = m7(m_cho_filter_r_1, 0x3c) + m7(cho_r, 0x3d) + m9(m_cho_filter_r_2, 0x3e);
+ s32 cho_filtered_r = m7(m_cho_filter_r_2, 0x3f) + m7(cho_filter_r_2, 0x40) + m9(m_cho_filter_r_3, 0x41);
+
+ m_cho_filter_r_1 = cho_r;
+ m_cho_filter_r_2 = cho_filter_r_2;
+ m_cho_filter_r_3 = cho_filtered_r;
+
+ s32 cho_filter_l_2 = m7(m_cho_filter_l_1, 0x49) + m7(cho_l, 0x4a) + m9(m_cho_filter_l_2, 0x4b);
+ s32 cho_filtered_l = m7(m_cho_filter_l_2, 0x4c) + m7(cho_filter_l_2, 0x4d) + m9(m_cho_filter_l_3, 0x4e);
+
+ m_cho_filter_l_1 = cho_l;
+ m_cho_filter_l_2 = cho_filter_l_2;
+ m_cho_filter_l_3 = cho_filtered_l;
+
+ // Reverb feedback from there, slighly assymetric to cover more possibilities
+ bcho.w(0x42, m9(cho_lfo_2, 0x42) + cho_filtered_r);
+ bcho.w(0x51, m9(cho_lfo_1, 0x4f) + cho_filtered_l + m9(cho_filtered_r, 0x50));
+
+ // Final value by combining the LFO-ed reverbs
+ s32 cho_out_l = m9(cho_lfo_1, 0x60) + m9(cho_lfo_3, 0x61);
+ s32 cho_out_r = m9(cho_lfo_2, 0x69) + m9(cho_lfo_3, 0x6a);
+
+
+
+ // Reverb block
+ // Update the output volume
+ m_rev_vol = m9(m_rev_vol, 0x0c) + m_const[0x0b];
+
+ // Scale the input
+ rev = m7(rev, 0x11);
+
+ // Add in the other channels
+ rev += m9v(m7(cho_out_l, 0x12) + m7(cho_out_r, 0x13), m_cho_vol);
+ rev += m9v(m7(var_out_l, 0x14) + m7(var_out_r, 0x15), m_var_vol);
+
+ // Two stages of filtering (hpf then lpf)
+ s32 rev_filter_2 = m7(m_rev_filter_1, 0x2d) + m7(rev, 0x2e) + m9(m_rev_filter_2, 0x2f);
+ s32 rev_filtered = m7(m_rev_filter_2, 0x30) + m7(rev_filter_2, 0x31) + m9(m_rev_filter_3, 0x32);
+
+ m_rev_filter_1 = rev;
+ m_rev_filter_2 = rev_filter_2;
+ m_rev_filter_3 = rev_filtered;
+
+ // Main reverb
+ brev.w(0x30, m9(brev.r(0x21), 0x29) + m9(brev.r(0x18), 0x2a));
+ brev.w(0x33, m9(brev.r(0x1b), 0x33) + rev_filtered);
+
+ // Second dual reverb
+ s32 rev_1 = m7(brev.r(0x33), 0x2b) + m7(brev.r(0x18), 0x2c);
+ s32 rev_2 = m7(brev.r(0x27), 0x3a) + m7(rev_1, 0x3b);
+ brev.w(0x3f, m9(brev.r(0x39), 0x38) + m9(rev_1, 0x39));
+
+ // Four more parallel layers with filtering
+ brev.w(0x5d, m9(m_rev_hist_a, 0x59) + m9(brev.r(0x24), 0x5a) + m9(rev_2, 0x5b));
+ m_rev_hist_a = brev.r(0x24);
+ brev.w(0x63, m9(m_rev_hist_b, 0x5c) + m9(brev.r(0x54), 0x5d) + m9(rev_2, 0x5e));
+ m_rev_hist_b = brev.r(0x54);
+ brev.w(0x69, m9(m_rev_hist_c, 0x62) + m9(brev.r(0x5a), 0x63) + m9(rev_2, 0x64));
+ m_rev_hist_c = brev.r(0x63);
+ brev.w(0x6c, m9(m_rev_hist_d, 0x65) + m9(brev.r(0x60), 0x66) + m9(rev_2, 0x67));
+ m_rev_hist_d = brev.r(0x66);
+
+ // Split final pick-up and injection
+ s32 rev_base_l = m9(brev.r(0x00) + brev.r(0x03) + brev.r(0x06) + brev.r(0x09), 0x1c) + m9(brev.r(0xbd), 0x1b);
+ brev.w(0x48, m9(brev.r(0x36), 0x45) + m9(rev_base_l, 0x46));
+ s32 rev_out_l = m7(brev.r(0x36), 0x47) + m7(rev_base_l, 0x48);
+
+ s32 rev_base_r = m9(brev.r(0x0c) + brev.r(0x0f) + brev.r(0x12) + brev.r(0x15), 0x21) + m9(brev.r(0xbd), 0x20);
+ brev.w(0x48, m9(brev.r(0x36), 0x51) + m9(rev_base_r, 0x52));
+ s32 rev_out_r = m7(brev.r(0x36), 0x53) + m7(rev_base_r, 0x54);
+
+
+ // Scale the dry input
+ dry_l = m7(dry_l, 0xbe);
+ dry_r = m7(dry_r, 0x01);
+
+
+ // Add in the other channels
+ dry_l += m9v(rev_out_l, m_rev_vol) + m9v(m9(cho_out_l, 0x17), m_cho_vol) + m9v(m9(var_out_l, 0x18), m_var_vol);
+ dry_r += m9v(rev_out_r, m_rev_vol) + m9v(m9(cho_out_r, 0x0e), m_cho_vol) + m9v(m9(var_out_r, 0x0f), m_var_vol);
+
+ stream.put_int(0, i, dry_l, 32768);
+ stream.put_int(1, i, dry_r, 32768);
+
+ m_buffer_offset --;
+ }
}