summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/sound/swp00.cpp931
-rw-r--r--src/devices/sound/swp00.h80
-rw-r--r--src/mame/yamaha/ymmu50.cpp41
3 files changed, 933 insertions, 119 deletions
diff --git a/src/devices/sound/swp00.cpp b/src/devices/sound/swp00.cpp
index 54ab5ca3d6e..9a522275cea 100644
--- a/src/devices/sound/swp00.cpp
+++ b/src/devices/sound/swp00.cpp
@@ -6,6 +6,66 @@
#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)
@@ -78,7 +138,6 @@ 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);
- // fprintf(stderr, "%07x %05x -> %x %08x\n", value, step, e, m);
if(m >= 0)
return 0;
if(e >= 0xc)
@@ -99,8 +158,6 @@ s32 swp00_device::fpsub(s32 value, s32 step)
bool swp00_device::fpstep(s32 &value, s32 limit, s32 step)
{
- // fprintf(stderr, "fpstep(%x, %x, %x)\n", value, limit, step);
-
// value, limit and step are 4.24 but step has its exponent and
// top four bits zero
@@ -175,8 +232,72 @@ void swp00_device::device_start()
save_item(NAME(m_waverom_val));
save_item(NAME(m_meg_control));
- save_item(NAME(m_off));
- save_item(NAME(m_fp));
+ 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));
@@ -259,8 +380,68 @@ void swp00_device::device_reset()
m_waverom_val = 0;
m_meg_control = 0;
- std::fill(m_off.begin(), m_off.end(), 0);
- std::fill(m_fp.begin(), m_fp.end(), 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);
@@ -361,8 +542,8 @@ void swp00_device::map(address_map &map)
rctrl(map, 0x0d); // 00 at startup
rctrl(map, 0x0e); // 00 at startup
- map(0x180, 0x1ff).rw(FUNC(swp00_device::off_r), FUNC(swp00_device::off_w));
- map(0x200, 0x37f).rw(FUNC(swp00_device::fp_r), FUNC(swp00_device::fp_w));
+ 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));
}
@@ -392,8 +573,8 @@ template<int sel> void swp00_device::lpf_info_w(offs_t offset, u8 data)
if(m_lpf_info[chan] == old)
return;
- if(!sel)
- logerror("lpf_info[%02x] = %04x\n", chan, m_lpf_info[chan]);
+ // 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;
@@ -418,7 +599,7 @@ void swp00_device::lpf_speed_w(offs_t offset, u8 data)
return;
m_stream->update();
m_lpf_speed[chan] = data;
- logerror("lpf_speed[%02x] = %02x\n", chan, m_lpf_speed[chan]);
+ // logerror("lpf_speed[%02x] = %02x\n", chan, m_lpf_speed[chan]);
}
u8 swp00_device::lpf_speed_r(offs_t offset)
@@ -434,7 +615,7 @@ void swp00_device::lfo_famod_depth_w(offs_t offset, u8 data)
return;
m_stream->update();
m_lfo_famod_depth[chan] = data;
- logerror("lfo_famod_depth[%02x] = %02x\n", chan, m_lfo_famod_depth[chan]);
+ // logerror("lfo_famod_depth[%02x] = %02x\n", chan, m_lfo_famod_depth[chan]);
}
u8 swp00_device::lfo_famod_depth_r(offs_t offset)
@@ -450,7 +631,7 @@ void swp00_device::rev_level_w(offs_t offset, u8 data)
return;
m_stream->update();
m_rev_level[chan] = data;
- logerror("rev_level[%02x] = %02x\n", chan, m_rev_level[chan]);
+ // logerror("rev_level[%02x] = %02x\n", chan, m_rev_level[chan]);
}
u8 swp00_device::rev_level_r(offs_t offset)
@@ -466,7 +647,7 @@ void swp00_device::dry_level_w(offs_t offset, u8 data)
return;
m_stream->update();
m_dry_level[chan] = data;
- logerror("dry_level[%02x] = %02x\n", chan, m_dry_level[chan]);
+ // logerror("dry_level[%02x] = %02x\n", chan, m_dry_level[chan]);
}
u8 swp00_device::dry_level_r(offs_t offset)
@@ -482,7 +663,7 @@ void swp00_device::cho_level_w(offs_t offset, u8 data)
return;
m_stream->update();
m_cho_level[chan] = data;
- logerror("cho_level[%02x] = %02x\n", chan, m_cho_level[chan]);
+ // logerror("cho_level[%02x] = %02x\n", chan, m_cho_level[chan]);
}
u8 swp00_device::cho_level_r(offs_t offset)
@@ -498,7 +679,7 @@ void swp00_device::var_level_w(offs_t offset, u8 data)
return;
m_stream->update();
m_var_level[chan] = data;
- logerror("var_level[%02x] = %02x\n", chan, m_var_level[chan]);
+ // logerror("var_level[%02x] = %02x\n", chan, m_var_level[chan]);
}
u8 swp00_device::var_level_r(offs_t offset)
@@ -513,7 +694,7 @@ void swp00_device::glo_level_w(offs_t offset, u8 data)
if(m_glo_level[chan] == data)
return;
m_glo_level[chan] = data;
- logerror("glo_level[%02x] = %02x\n", chan, m_glo_level[chan]);
+ // logerror("glo_level[%02x] = %02x\n", chan, m_glo_level[chan]);
}
u8 swp00_device::glo_level_r(offs_t offset)
@@ -529,7 +710,7 @@ void swp00_device::panning_w(offs_t offset, u8 data)
return;
m_stream->update();
m_panning[chan] = data;
- logerror("panning[%02x] = %02x\n", chan, m_panning[chan]);
+ // logerror("panning[%02x] = %02x\n", chan, m_panning[chan]);
}
u8 swp00_device::panning_r(offs_t offset)
@@ -545,7 +726,7 @@ void swp00_device::attack_speed_w(offs_t offset, u8 data)
return;
m_stream->update();
m_attack_speed[chan] = data;
- logerror("attack_speed[%02x] = %02x\n", chan, m_attack_speed[chan]);
+ // logerror("attack_speed[%02x] = %02x\n", chan, m_attack_speed[chan]);
}
u8 swp00_device::attack_speed_r(offs_t offset)
@@ -561,7 +742,7 @@ void swp00_device::attack_level_w(offs_t offset, u8 data)
return;
m_stream->update();
m_attack_level[chan] = data;
- logerror("attack_level[%02x] = %02x\n", chan, m_attack_level[chan]);
+ // logerror("attack_level[%02x] = %02x\n", chan, m_attack_level[chan]);
}
u8 swp00_device::attack_level_r(offs_t offset)
@@ -582,7 +763,7 @@ void swp00_device::decay_speed_w(offs_t offset, u8 data)
if(data & 0x80)
m_decay[chan] = true;
- logerror("decay_speed[%02x] = %02x\n", chan, m_decay_speed[chan]);
+ // logerror("decay_speed[%02x] = %02x\n", chan, m_decay_speed[chan]);
}
u8 swp00_device::decay_speed_r(offs_t offset)
@@ -598,7 +779,7 @@ void swp00_device::decay_level_w(offs_t offset, u8 data)
return;
m_stream->update();
m_decay_level[chan] = data;
- logerror("decay_level[%02x] = %02x\n", chan, m_decay_level[chan]);
+ // logerror("decay_level[%02x] = %02x\n", chan, m_decay_level[chan]);
}
u8 swp00_device::decay_level_r(offs_t offset)
@@ -615,8 +796,8 @@ template<int sel> void swp00_device::pitch_w(offs_t offset, u8 data)
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]);
+ // if(!sel)
+ // logerror("pitch[%02x] = %04x\n", chan, m_pitch[chan]);
}
template<int sel> u8 swp00_device::pitch_r(offs_t offset)
@@ -631,8 +812,8 @@ template<int sel> void swp00_device::sample_start_w(offs_t offset, u8 data)
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]);
+ // 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)
@@ -647,8 +828,8 @@ template<int sel> void swp00_device::sample_end_w(offs_t offset, u8 data)
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]);
+ // 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)
@@ -663,7 +844,7 @@ void swp00_device::sample_dec_and_format_w(offs_t offset, u8 data)
m_stream->update();
m_sample_dec_and_format[chan] = data;
- logerror("sample_dec_and_format[%02x] = %02x\n", chan, m_sample_dec_and_format[chan]);
+ // logerror("sample_dec_and_format[%02x] = %02x\n", chan, m_sample_dec_and_format[chan]);
}
u8 swp00_device::sample_dec_and_format_r(offs_t offset)
@@ -678,8 +859,8 @@ template<int sel> void swp00_device::sample_address_w(offs_t offset, u8 data)
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]);
+ // 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)
@@ -696,7 +877,7 @@ void swp00_device::lfo_step_w(offs_t offset, u8 data)
m_stream->update();
m_lfo_step[chan] = data;
- logerror("lfo_step[%02x] = %02x\n", chan, m_lfo_step[chan]);
+ // logerror("lfo_step[%02x] = %02x\n", chan, m_lfo_step[chan]);
}
u8 swp00_device::lfo_step_r(offs_t offset)
@@ -713,7 +894,7 @@ void swp00_device::lfo_pmod_depth_w(offs_t offset, u8 data)
m_stream->update();
m_lfo_pmod_depth[chan] = data;
- logerror("lfo_pmod_depth[%02x] = %02x\n", chan, m_lfo_pmod_depth[chan]);
+ // logerror("lfo_pmod_depth[%02x] = %02x\n", chan, m_lfo_pmod_depth[chan]);
}
u8 swp00_device::lfo_pmod_depth_r(offs_t offset)
@@ -725,7 +906,7 @@ u8 swp00_device::lfo_pmod_depth_r(offs_t offset)
void swp00_device::keyon(int chan)
{
m_stream->update();
- logerror("keyon %02x a=%02x/%02x d=%02x/%02x\n", chan, m_attack_speed[chan], m_attack_level[chan], m_decay_speed[chan], m_decay_level[chan]);
+ // logerror("keyon %02x a=%02x/%02x d=%02x/%02x\n", chan, m_attack_speed[chan], m_attack_level[chan], m_decay_speed[chan], m_decay_level[chan]);
m_lfo_phase[chan] = 0;
m_sample_pos[chan] = -m_sample_start[chan] << 15;
@@ -758,46 +939,46 @@ template<int sel> void swp00_device::keyon_w(u8 data)
keyon(8*sel+i);
}
-void swp00_device::off_w(offs_t offset, u8 data)
+void swp00_device::offset_w(offs_t offset, u8 data)
{
m_stream->update();
if(offset & 1)
- m_off[offset >> 1] = (m_off[offset >> 1] & 0xff00) | data;
+ m_offset[offset >> 1] = (m_offset[offset >> 1] & 0xff00) | data;
else
- m_off[offset >> 1] = (m_off[offset >> 1] & 0x00ff) | (data << 8);
- if(1)
+ m_offset[offset >> 1] = (m_offset[offset >> 1] & 0x00ff) | (data << 8);
+ if(0)
if(offset & 1)
- logerror("off[%02x] = %04x\n", 3*(offset >> 1), m_off[offset >> 1]);
+ logerror("offset[%02x] = %04x\n", 3*(offset >> 1), m_offset[offset >> 1]);
}
-u8 swp00_device::off_r(offs_t offset)
+u8 swp00_device::offset_r(offs_t offset)
{
if(offset & 1)
- return m_off[offset >> 1];
+ return m_offset[offset >> 1];
else
- return m_off[offset >> 1] >> 8;
+ return m_offset[offset >> 1] >> 8;
}
-void swp00_device::fp_w(offs_t offset, u8 data)
+void swp00_device::const_w(offs_t offset, u8 data)
{
m_stream->update();
if(offset & 1)
- m_fp[offset >> 1] = (m_fp[offset >> 1] & 0xff00) | data;
+ m_const[offset >> 1] = (m_const[offset >> 1] & 0xff00) | data;
else
- m_fp[offset >> 1] = (m_fp[offset >> 1] & 0x00ff) | (data << 8);
- if(1)
+ m_const[offset >> 1] = (m_const[offset >> 1] & 0x00ff) | (data << 8);
+ if(0)
if(offset & 1)
- logerror("fp[%02x] = %04x\n", offset >> 1, m_fp[offset >> 1]);
+ logerror("const[%02x] = %04x\n", offset >> 1, m_const[offset >> 1]);
}
-u8 swp00_device::fp_r(offs_t offset)
+u8 swp00_device::const_r(offs_t offset)
{
if(offset & 1)
- return m_fp[offset >> 1];
+ return m_const[offset >> 1];
else
- return m_fp[offset >> 1] >> 8;
+ return m_const[offset >> 1] >> 8;
}
void swp00_device::waverom_access_w(u8 data)
@@ -821,7 +1002,7 @@ u8 swp00_device::waverom_val_r()
void swp00_device::meg_control_w(u8 data)
{
m_meg_control = data;
- logerror("meg_control %02x (variation %x, %s)\n", m_meg_control >> 6, m_meg_control & 2 ? "mute" : "on");
+ 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()
@@ -834,7 +1015,6 @@ u8 swp00_device::state_r()
{
m_stream->update();
- // logerror("state_r %x.%02x\n", m_state_adr >> 5, m_state_adr & 0x1f);
int chan = m_state_adr & 0x1f;
switch(m_state_adr & 0xe0) {
case 0x00: // lpf value
@@ -880,64 +1060,203 @@ void swp00_device::state_adr_w(u8 data)
// Catch-all
-static u8 rr[0x20*0x40];
-
u8 swp00_device::snd_r(offs_t offset)
{
- if(1) {
- int chan = (offset >> 1) & 0x1f;
- int slot = ((offset >> 5) & 0x3e) | (offset & 1);
- 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 == 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
-#endif
- preg = util::string_format("%02x.%02x", chan, slot);
- logerror("snd_r [%03x] %-5s, %02x\n", offset, preg, rr[offset]);
- }
- return rr[offset];
+ logerror("snd_r [%03x]\n", offset);
+ return 0;
}
void swp00_device::snd_w(offs_t offset, u8 data)
{
- // if(rr[offset] == data)
- // return;
-
- rr[offset] = data;
-
- int chan = (offset >> 1) & 0x1f;
- int slot = ((offset >> 5) & 0x3e) | (offset & 1);
-
- 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);
- else
-#endif
- preg = util::string_format("%02x.%02x", chan, slot);
-
- logerror("snd_w [%03x] %-5s, %02x\n", offset, preg, data);
+ logerror("snd_w [%03x] %02x\n", offset, data);
}
// Synthesis
+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;
+}
+
void swp00_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
+ 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 != outputs[0].samples(); i++) {
s32 dry_l = 0, dry_r = 0;
s32 rev = 0;
@@ -1081,12 +1400,420 @@ void swp00_device::sound_stream_update(sound_stream &stream, std::vector<read_st
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, var_out_r;
+
+ 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);
+
outputs[0].put_int(i, dry_l, 32768);
outputs[1].put_int(i, dry_r, 32768);
- (void)rev;
- (void)cho_l;
- (void)cho_r;
- (void)var_l;
- (void)var_r;
+
+ m_buffer_offset --;
}
}
diff --git a/src/devices/sound/swp00.h b/src/devices/sound/swp00.h
index 4a29fedc4c2..36dfba1677d 100644
--- a/src/devices/sound/swp00.h
+++ b/src/devices/sound/swp00.h
@@ -26,7 +26,16 @@ protected:
virtual void device_add_mconfig(machine_config &config) override;
private:
- // required_device<meg_embedded_device> m_meg;
+ template<size_t size> struct delay_block {
+ swp00_device *m_swp;
+ std::array<s32, size> &m_buffer;
+
+ delay_block(swp00_device *swp, std::array<s32, size> &buffer);
+ s32 r(int offreg) const;
+ s32 rlfo(int offreg, u32 phase, s32 delta_phase, int levelreg) const;
+ s32 rlfo2(int offreg, s32 offset) const;
+ void w(int offreg, s32 value) const;
+ };
sound_stream *m_stream;
@@ -41,9 +50,14 @@ private:
static const std::array<u32, 4> lfo_shape_offset_saw;
static const std::array<u32, 4> lfo_shape_offset_tri;
+ // MEG reverb memory
+ std::array<s32, 0x20000> m_rev_buffer;
+ std::array<s32, 0x8000> m_cho_buffer;
+ std::array<s32, 0x20000> m_var_buffer;
+
// MEG registers
- std::array<u16, 0x40> m_off;
- std::array<u16, 0xc0> m_fp;
+ std::array<u16, 0x40> m_offset;
+ std::array<u16, 0xc0> m_const;
// AWM registers
std::array<u16, 0x20> m_lpf_info;
@@ -87,6 +101,36 @@ private:
u8 m_state_adr;
u8 m_meg_control;
+ // MEG state
+ u32 m_buffer_offset;
+ s32 m_rev_vol, m_cho_vol, m_var_vol;
+
+ u32 m_var_lfo_phase;
+
+ s32 m_var_lfo_h_1, m_var_lfo_h_2;
+ s32 m_var_lfo1a, m_var_lfo2a, m_var_lfo3a, m_var_lfo4a;
+
+ s32 m_var_filter_1, m_var_filter_2, m_var_filter_3;
+ s32 m_var_filter_l_1, m_var_filter_l_2, m_var_filter_l_3;
+ s32 m_var_filter_r_1, m_var_filter_r_2, m_var_filter_r_3;
+ s32 m_var_filter2_1, m_var_filter2_2a, m_var_filter2_2b, m_var_filter2_3a, m_var_filter2_3b, m_var_filter2_4;
+ s32 m_var_filter3_1, m_var_filter3_2;
+
+ s32 m_var_filterp_l_1, m_var_filterp_l_2, m_var_filterp_l_3;
+ s32 m_var_filterp_l_4, m_var_filterp_l_5, m_var_filterp_l_6;
+ s32 m_var_filterp_r_1, m_var_filterp_r_2, m_var_filterp_r_3;
+ s32 m_var_filterp_r_4, m_var_filterp_r_5, m_var_filterp_r_6;
+
+ s32 m_var_h1, m_var_h2, m_var_h3, m_var_h4;
+
+ u32 m_cho_lfo_phase;
+ s32 m_cho_filter_l_1, m_cho_filter_l_2, m_cho_filter_l_3;
+ s32 m_cho_filter_r_1, m_cho_filter_r_2, m_cho_filter_r_3;
+
+ s32 m_rev_filter_1, m_rev_filter_2, m_rev_filter_3;
+ s32 m_rev_hist_a, m_rev_hist_b, m_rev_hist_c, m_rev_hist_d;
+
+
// Voice control
template<int sel> void lpf_info_w(offs_t offset, u8 data);
@@ -138,10 +182,10 @@ private:
void state_adr_w(u8 data);
// MEG
- void off_w(offs_t offset, u8 data);
- u8 off_r(offs_t offset);
- void fp_w(offs_t offset, u8 data);
- u8 fp_r(offs_t offset);
+ void offset_w(offs_t offset, u8 data);
+ u8 offset_r(offs_t offset);
+ void const_w(offs_t offset, u8 data);
+ u8 const_r(offs_t offset);
void meg_control_w(u8 data);
u8 meg_control_r();
@@ -173,6 +217,28 @@ private:
static s32 fpsub(s32 value, s32 step);
static s32 fpapply(s32 value, s32 sample);
static s32 lpffpapply(s32 value, s32 sample);
+
+ s32 rext(int reg) const;
+ static s32 m7v(s32 value, s32 mult);
+ s32 m7(s32 value, int reg) const;
+ static s32 m9v(s32 value, s32 mult);
+ s32 m9(s32 value, int reg) const;
+ s32 lfo_get_step(int reg) const;
+ void lfo_step(u32 &phase, int reg) const;
+ static u32 lfo_wrap(s32 phase, s32 delta);
+ static s32 lfo_saturate(s32 phase);
+ s32 lfo_wrap_reg(s32 phase, int deltareg) const;
+ void filtered_lfo_step(s32 &position, s32 phase, int deltareg, int postdeltareg, int scalereg, int feedbackreg);
+ s32 lfo_mod(s32 phase, int scalereg) const;
+ s32 lfo_scale(s32 phase, int scalereg) const;
+
+ s32 alfo(u32 phase, s32 delta_phase, int levelreg, int offsetreg, bool sub) const;
+
+ s32 sx(int reg) const;
+ double sx7(int reg) const;
+ double sx9(int reg) const;
+
+ static s32 saturate(s32 value);
};
DECLARE_DEVICE_TYPE(SWP00, swp00_device)
diff --git a/src/mame/yamaha/ymmu50.cpp b/src/mame/yamaha/ymmu50.cpp
index e6192a05c43..27c1b9dcc6b 100644
--- a/src/mame/yamaha/ymmu50.cpp
+++ b/src/mame/yamaha/ymmu50.cpp
@@ -2,8 +2,16 @@
// copyright-holders:R. Belmont, Olivier Galibert
/*************************************************************************************
- Yamaha MU-50 : 16-voice polyphonic/multitimbral General MIDI/GS/XG tone modules
- Preliminary driver by R. Belmont and O. Galibert
+ Yamaha MU-50 : 16-part, 32-note polyphonic/multitimbral General MIDI/GS/XG
+ tone module
+ Driver by R. Belmont and O. Galibert
+
+ Cost-reduced version of the MU80, uses the SWP00 which is a single-chip
+ integrated version of the multi-chip SWP20. As a consequence has half the
+ voices, loses the parametric EQ and the AD inputs. The sample roms are also
+ smaller, and it only has one midi input.
+
+ A wavetable version exists as the DB50XG.
**************************************************************************************/
@@ -14,6 +22,7 @@
#include "cpu/h8/h83003.h"
#include "mulcd.h"
#include "sound/swp00.h"
+#include "machine/nvram.h"
#include "debugger.h"
#include "speaker.h"
@@ -53,11 +62,13 @@ public:
mu50_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_mu50cpu(*this, "mu50cpu")
+ , m_nvram(*this, "ram")
, m_swp00(*this, "swp00")
, m_lcd(*this, "lcd")
, m_ioport_o0(*this, "O0")
, m_ioport_o1(*this, "O1")
, m_ioport_o2(*this, "O2")
+ , m_ram(*this, "ram")
{ }
void mu50(machine_config &config);
@@ -70,11 +81,13 @@ private:
};
required_device<h83003_device> m_mu50cpu;
+ required_device<nvram_device> m_nvram;
required_device<swp00_device> m_swp00;
required_device<mulcd_device> m_lcd;
required_ioport m_ioport_o0;
required_ioport m_ioport_o1;
required_ioport m_ioport_o2;
+ required_shared_ptr<u16> m_ram;
u8 cur_p6, cur_pa, cur_pb, cur_pc;
@@ -112,7 +125,7 @@ void mu50_state::machine_reset()
void mu50_state::mu50_map(address_map &map)
{
map(0x000000, 0x07ffff).rom().region("mu50cpu", 0);
- map(0x200000, 0x20ffff).ram(); // 64K work RAM
+ map(0x200000, 0x20ffff).ram().share(m_ram); // 64K work RAM
map(0x400000, 0x4007ff).m(m_swp00, FUNC(swp00_device::map));
}
@@ -131,7 +144,6 @@ u16 mu50_state::adc_al_r()
// Put the host switch to pure midi
u16 mu50_state::adc_midisw_r()
{
- // -> 20948a
// 000-0bf: midi
// 0c0-1ff: pc2
// 200-37f: pc1
@@ -236,6 +248,8 @@ void mu50_state::mu50(machine_config &config)
m_mu50cpu->read_port7().set_constant(0);
m_mu50cpu->read_port9().set_constant(0);
+ NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);
+
MULCD(config, m_lcd);
SPEAKER(config, "lspeaker").front_left();
@@ -254,16 +268,23 @@ void mu50_state::mu50(machine_config &config)
m_mu50cpu->write_sci_tx<1>().set(mdout, FUNC(midi_port_device::write_txd));
}
+#define ROM_LOAD16_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
+ ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(bios))
+
ROM_START( mu50 )
ROM_REGION( 0x80000, "mu50cpu", 0 )
- ROM_LOAD16_WORD_SWAP( "yamaha_mu50.bin", 0x000000, 0x080000, CRC(507168ad) SHA1(58c41f10d292cac35ef0e8f93029fbc4685df586) )
-
- ROM_REGION( 0x400000, "swp00", ROMREGION_ERASE00 )
- ROM_LOAD( "xq730b0.ic9", 0x000000, 0x200000, CRC(d4adbc7e) SHA1(32f653c7644d060f5a6d63a435ae3a7412386d92) BAD_DUMP) // Use the db50xg roms for now
- ROM_LOAD( "xq731b0.ic10", 0x200000, 0x200000, CRC(7b68f475) SHA1(adf68689b4842ec5bc9b0ea1bb99cf66d2dec4de) BAD_DUMP) // Note that they may be identical to the mu50 ones
+ ROM_SYSTEM_BIOS( 0, "bios0", "xr174c0 (v1.05, Aug. 21, 1995)" )
+ ROM_LOAD16_WORD_SWAP_BIOS( 0, "xr174c0.ic7", 0x000000, 0x080000, CRC(902520a4) SHA1(9ca892920598f9fdf08544dac4c0e54e7d46ee3c) )
+ ROM_SYSTEM_BIOS( 1, "bios1", "? (v1.04, May 22, 1995)" )
+ ROM_LOAD16_WORD_SWAP_BIOS( 1, "yamaha_mu50.bin", 0x000000, 0x080000, CRC(507168ad) SHA1(58c41f10d292cac35ef0e8f93029fbc4685df586) )
+
+ ROM_REGION( 0x400000, "swp00", 0 )
+ // Identical to the db50xg roms
+ ROM_LOAD( "xq057c0.ic18", 0x000000, 0x200000, CRC(d4adbc7e) SHA1(32f653c7644d060f5a6d63a435ae3a7412386d92) )
+ ROM_LOAD( "xq058c0.ic19", 0x200000, 0x200000, CRC(7b68f475) SHA1(adf68689b4842ec5bc9b0ea1bb99cf66d2dec4de) )
ROM_END
} // anonymous namespace
-CONS( 1995, mu50, 0, 0, mu50, mu50, mu50_state, empty_init, "Yamaha", "MU50", MACHINE_NOT_WORKING )
+CONS( 1995, mu50, 0, 0, mu50, mu50, mu50_state, empty_init, "Yamaha", "MU50", 0 )