summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/swp30.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/swp30.cpp')
-rw-r--r--src/devices/sound/swp30.cpp756
1 files changed, 454 insertions, 302 deletions
diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp
index ebae8eb73e8..6f02329993f 100644
--- a/src/devices/sound/swp30.cpp
+++ b/src/devices/sound/swp30.cpp
@@ -7,91 +7,121 @@
#include "swp30.h"
/*
- mode hall 1 reverb 0.3/2.0/2.1/30.0
- 5ab/b56: 1ff2
- 5e1/bc2: 1ff1
- 5e3/bc6, 521/a42, 527/a4e, 563/ac6, 56b/ad6, 5a7/b4e, 5e3/bc6: 1d
- 529/a52: 1a21/13d2/134f/ 70c
- 4e9/9d2, 4eb/9d6: 5/14d0/1585/1ec3
- 565/aca: 1a21/12cb/1247/ 684
- 523/a46, 525/a4a: 10/1631/16d4/1eef
- 5a1/b42: 1a21/11c2/113d/ 606
- 561/ac2, 52b/ac2: a2/176f/1801/1f15
- 5a9/b5a: 1a20/10fb/1077/ 5ae
- 567/ace, 569/ad2: 4d/1847/18cd/1f2e
- 5e5/bca: 1a1f/ f74/ ef5/ 50b
- 5a3/b46, 5a5/b46: d2/19c2/1a31/1f58
- 623/c46: 1a21/130d/1289/ 6a5
- 5ab/b56, 5e1/bc2: c/15dc/1683/1ee4
-
-
-72814: move e0 to reg r0
-
-62fcc: compute stuff
+ The SWP30 is the combination of a rompler called AWM2 (Advanced Wave
+ Memory 2) and an effects DSP called MEG (Multiple Effects
+ Generator). It also includes some routing/mixing capabilities,
+ moving data between AWM2, MEG and serial inputs and outputs with
+ volume management capabilities everywhere. Its clock is 33.9MHz and
+ the output is at 44100Hz stereo (768 cycles per sample pair) per dac
+ output.
+
+ I/O wise, the chip has 8 generic audio serial inputs and 8 outputs
+ for external plugins, and two dac outputs. The DAC output is
+ stereo, and so is the first generic input. It's unclear whether the
+ outputs and the other inputs are stereo. The MU100 connects a stereo ADC to the fist input, and routes
+
+ In practice the chip has the pin for a second DAC, so is
+ probably 4-channel capable on output.
+
+ The
+
+
+ The AWM2 manages 64 channels internally, and has inputs for 8
+ external sources, one at least being stereo (they probably all are).
+ 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). The rom bus is
+ 25 bits address and 32 bits data wide. It applies four filters to
+ the sample data, two of fixed type (low pass then highpass) and two
+ free 3-point FIR filters (used for yet another lowpass and
+ highpass). Envelopes are handled automatically, and the final
+ panned result is accumulated on four stereo accumulators which will
+ be passed to the MEG. Two of the channels (that includes the
+ external ones) can also have their value sent to the MEG.
+
+
+
+
+ The MEG is a DSP with 384 program steps. Instructions are 64 bits
+ wide, and to each instruction is associated a 2.14 fixed point value
+ and, for every third instruction a 16-bit integer memory offset
+ value. In addition 24 LFOs are available, and possibly more. It is
+ connected to a dram of 262144 samples, theorically 18 bits but in
+ practice only the top 16 bits are connected.
+
+ The chip interface presents 4096 16-bits registers in a 64x64 grid.
+ Some of this grid is for per-channel values for AWM2, but parts are
+ isolated and renumbered for MEG regisrers or for general control
+ functions.
+
+ General register address: (64 * channel + slot) * 2 (16-bits values)
+ MEG fixed point constants (n=0-383) : channel = n/6, slot = 0x21 + 2*(n%6)
+ MEG integer constants (n=0-127): channel = n/2, slot = 0x30 + (n%2)
+ MEG LFOs (n=0..23): channel = n/2, slot = 0x3e + (n%2)
+
+ Control registers (n=0..127): channel = n/2, slot = 0x
+
+ Note that the LFOs may be 128 instead of 24, but the mu100 code only
+ reserves 24 values in its structures. OTOH, the mu100 code never
+ uses channel >= 12 slot 3e-3f either.
+
+
+ AWM2 (per-channel) registers:
+ slot(s) function
+ 00 fixed LPF frequency cutoff index
+ 01 fixed LPF frequency cutoff index increment?
+ 02 fixed HPF frequency cutoff
+ 03 40ff at startup, 5010 always afterwards?
+ 04 fixed LPF resonane level
+ 05 unknown
+ 06-09 envelope information, not understood yet
+ 0a-0d unknown, probably something to do with vibrato
+ 10 unknown
+ 11 channel replay frequency, signed 4.10 fixed point, log2 scale, positive is higher resulting frequency
+ 12-13 number of samples before the loop point
+ 14-15 number of samples in the loop
+ 16-17 bit 31-30 = sample format, bits 29-25 = loop samples decimal part, 24-0 = loop start address in rom
+ 20,22,24 first FIR coefficients
+ 26,28,2a second FIR coefficients
+ 2c-2f unknown
+ 32 pan left/right, 2x8 bits of attenuation
+ 33-34 attenuation levels to add to the four accumulators (dry, reverb, chorus, variation for the mu100)
+ 35-37 routing, in particular for the taps. Rather unclear
+
+ Slots e-f are system control, 21,23,25,27,29,2b MEG fixed point
+ registers, 30,31 MEG offset registers, 3e,3f MEG LFO registers.
+ 38-3d are special, not per-channel.
+
+ Known system registers:
+ number function
+ 02 internal register selector, msb = 0 or 6, lsb = channel
+ 03 internal register read port, used for envelope/keyoff management
+ 0c-0f keyon mask
+ 10 write something to trigger a keyon
+ 21 MEG program write address
+ 22-25 MEG program opcode, writing to 25 triggers an auto-increment
+ 30-3e even slots only, MEG buffer mappings
+
+
+ The LFO registers internal counters are 22 bits wide. The LSB of
+ the register gives the increment per sample, encoded in a special
+ 3.5 format.
+ With scale = 3bits and v = 5bits, step = base[scale] + (v << shift[scale])
+ base = { 0, 32, 64, 128, 256, 512, 1024, 2048 }
+ shift = { 0, 0, 1, 2, 3, 4, 5, 6 }
+
+ The 21th bit of the counter inverts bits 20-0 on read, those are
+ interpreted as a 0-1 value, giving a sawtooth wave.
+
+
+ 8 mappings can be setup, which allow to manage rotating buffers in
+ the MEG-attached ram easily by automating masking and offset adding.
+ The register format is: tttttsss oooooooo. 't' is not understood
+ yet. 's' is the sub-buffer size, defined as 1 << (10+s). The base
+ offset is o << 10. There are no alignment issues, e.g. you can have
+ a buffer at 0x28000 which is 0x10000 samples long.
-*/
-#if 0
-// 929a6: 00 2e 30 32 33 34 35 36 37 3a 3c
-
-// 3fb80: list of reverb times as text, 4chars/entry
-
-// 84268: 7c 76 77 78
-// 80 79 7a 7b
-// 84 7e 7d 7f
-// 88 81 82 83
-// 8c 85 86 87
-// 91 89 8a 8b
-
-
-void f_62fcc(e0, r0, e1, r1l, er5) // 8 (11/12) 1 6 84268
-{
- // 929b1
- // 00 00 00 00 00 00
- // 38 3c 40 43 49 3b
- // 46 4a 4e 51 55 43
- // 3e 3f 41 43 48 3c
- // 4b 51 59 5c 00 4e
-
- // 92560
- // 00 01 12 1c 23 28 2d 30 34 37 39 3c 3e 40 42 43
- // 45 46 48 49 4a 4c 4d 4e 4f 50 51 52 53 54 54 55
- // 56 57 58 58 59 5a 5a 5b 5c 5c 5d 5d 5e 5e 5f 60
- // 60 61 61 63 66 68 69 6b 6d 6e 70 71 72 75 77 79
- // 7b 7c 7e 7f 81 82
-
- l8 = 929b1 + 6*e1;
- r6 = r1l == 6 ? 0x15 : r1l == 4 ? 0xd : r1l == 3 ? 0x9 : e1;
- f_64a34(0x01, 01, t929a6[e0].b << 8, 0x40, 0, er5 + 2*r6); // 0101 3700 40 0 84292 - writes 1ff2/1ff1
- pr0 = r0;
- for(r3h=0; r3h<6; r3h++) {
- r0l = *l8++;
- if(r0l) {
- r6 = clamp(r0l + l92560[pr0] - 0x38, 0, 0xff);
- r6, e6 = l925a6[r6].l; // 13d2 5368
- } else
- r6, e6 = 0, 0;
-
- }
- er0 = *er5.w++;
- g214ca4[0x10 + er0].w = e6;
- lc = &g214ca4[0x20 + er0].w
- er0 = float(r6);
-}
-// 80914 = convert unsigned short to float
-
-void write_regs(r0b, er1) // 728c0, r0=3, er1=84292
-{
- if(!g214c98)
- return;
- for(r5=0; r5<r0b; r5++) {
- r6 = er1[r5].w; // 89 8a 8b
- e5 = (r6 % 6) + 0x21;
- r0 = r6 / 6;
- write_reg(r6/6, (r6 % 6)*2 + 0x21, g214cc2[r6].w);
- }
-}
-#endif
+*/
DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip")
@@ -107,23 +137,26 @@ void swp30_device::device_start()
{
m_stream = stream_alloc(0, 2, 44100);
- // Attenuantion for panning is logarithmic on one byte where 0x10
- // means divide by two. It means 0 to -96dB with steps of
- // 0.375dB. Since it's a nice scale, we assume it's the same for
- // other attenuation values.
+ // Attenuantion for panning is 4.4 floating point. That means 0
+ // to -96.3dB. Since it's a nice range, we assume it's the same
+ // for other attenuation values. Computed value is is 1.16
+ // format, to avoid overflow
+
for(int i=0; i<256; i++)
- m_linear_attenuation[i] = 65536.0/pow(2, i/16.0);
+ m_linear_attenuation[i] = ((32 - (i & 15)) << (0xf ^ (i >> 4))) >> 4;
// Relative playback frequency of a sample is encoded on signed 14
// bits. The scale is logarithmic, with 0x400 = 1 octave (e.g. *2
// or /2).
+
for(int i=-0x20000; i<0x2000; i++)
m_sample_increment[i & 0x3fff] = 256 * pow(2, i/1024.0);
// Log to linear 8-bits sample decompression. Statistics say
// that's what it should look like. Note that 0 can be encoded
// both as 0x00 and 0x80, and as it happens 0x80 is never used in
- // these samples.
+ // these samples. Ends up with a 55dB dynamic range, to compare
+ // with 8bits 48dB, 12bits 72dB and 16bits 96dB.
// Rescale so that it's roughly 16 bits. Range ends up being +/- 78c0.
@@ -138,10 +171,40 @@ void swp30_device::device_start()
m_sample_log8[i | 0x80] = -m_sample_log8[i];
}
+ save_item(NAME(m_program));
+
+ save_item(NAME(m_keyon_mask));
+ save_item(NAME(m_active_mask));
+
+ save_item(NAME(m_pre_size));
+ save_item(NAME(m_post_size));
+ save_item(NAME(m_address));
+
+ save_item(NAME(m_sample_pos));
+ save_item(NAME(m_sample_history));
+
+ save_item(NAME(m_program_pfp));
+ save_item(NAME(m_program_pint));
+ save_item(NAME(m_program_plfo));
+
+ save_item(NAME(m_volume));
+ save_item(NAME(m_freq));
+ save_item(NAME(m_pan));
+ save_item(NAME(m_envelope));
+ save_item(NAME(m_lpf_cutoff));
+ save_item(NAME(m_lpf_cutoff_inc));
+ save_item(NAME(m_lpf_reso));
+ save_item(NAME(m_hpf_cutoff));
+ save_item(NAME(m_eq_filter));
+ save_item(NAME(m_routing));
+
+ save_item(NAME(m_program_address));
}
void swp30_device::device_reset()
{
+ memset(m_program, 0, sizeof(m_program));
+
m_keyon_mask = 0;
m_active_mask = 0;
@@ -152,25 +215,22 @@ void swp30_device::device_reset()
memset(m_sample_pos, 0, sizeof(m_sample_pos));
memset(m_sample_history, 0, sizeof(m_sample_history));
+ memset(m_program_pfp, 0, sizeof(m_program_pfp));
+ memset(m_program_pint, 0, sizeof(m_program_pint));
+ memset(m_program_plfo, 0, sizeof(m_program_plfo));
+
memset(m_volume, 0, sizeof(m_volume));
memset(m_freq, 0, sizeof(m_freq));
memset(m_pan, 0, sizeof(m_pan));
+ memset(m_envelope, 0, sizeof(m_envelope));
memset(m_lpf_cutoff, 0, sizeof(m_lpf_cutoff));
+ memset(m_lpf_cutoff_inc, 0, sizeof(m_lpf_cutoff_inc));
memset(m_lpf_reso, 0, sizeof(m_lpf_reso));
memset(m_hpf_cutoff, 0, sizeof(m_hpf_cutoff));
memset(m_eq_filter, 0, sizeof(m_eq_filter));
+ memset(m_routing, 0, sizeof(m_routing));
- memset(m_variation_delay, 0, sizeof(m_variation_delay));
- memset(m_variation_fb_level, 0, sizeof(m_variation_fb_level));
- memset(m_variation_final_level, 0, sizeof(m_variation_final_level));
- memset(m_variation_high_damp, 0, sizeof(m_variation_high_damp));
- memset(m_variation_inv_high_damp, 0, sizeof(m_variation_inv_high_damp));
-
- memset(m_insertion_delay, 0, sizeof(m_insertion_delay));
- memset(m_insertion_fb_level, 0, sizeof(m_insertion_fb_level));
- memset(m_insertion_final_level, 0, sizeof(m_insertion_final_level));
- memset(m_insertion_high_damp, 0, sizeof(m_insertion_high_damp));
- memset(m_insertion_inv_high_damp, 0, sizeof(m_insertion_inv_high_damp));
+ m_program_address = 0;
}
void swp30_device::rom_bank_updated()
@@ -181,78 +241,79 @@ void swp30_device::rom_bank_updated()
void swp30_device::map(address_map &map)
{
map(0x0000, 0x1fff).rw(FUNC(swp30_device::snd_r), FUNC(swp30_device::snd_w));
- map(0x0000, 0x0001).select(0x1f80).rw(FUNC(swp30_device::lpf_cutoff_r), FUNC(swp30_device::lpf_cutoff_w));
- map(0x0004, 0x0005).select(0x1f80).rw(FUNC(swp30_device::hpf_cutoff_r), FUNC(swp30_device::hpf_cutoff_w));
- map(0x0008, 0x0009).select(0x1f80).rw(FUNC(swp30_device::lpf_reso_r), FUNC(swp30_device::lpf_reso_w));
- map(0x0012, 0x0013).select(0x1f80).rw(FUNC(swp30_device::volume_r), FUNC(swp30_device::volume_w));
- map(0x0022, 0x0023).select(0x1f80).rw(FUNC(swp30_device::freq_r), FUNC(swp30_device::freq_w));
- map(0x0024, 0x0027).select(0x1f80).rw(FUNC(swp30_device::pre_size_r), FUNC(swp30_device::pre_size_w));
- map(0x0028, 0x002b).select(0x1f80).rw(FUNC(swp30_device::post_size_r), FUNC(swp30_device::post_size_w));
- map(0x002c, 0x002f).select(0x1f80).rw(FUNC(swp30_device::address_r), FUNC(swp30_device::address_w));
- map(0x0040, 0x0041).select(0x1f80).rw(&swp30_device::eq_filter_r<0, 0>, "swp30_device::eq_filter_r<0, 0>", &swp30_device::eq_filter_w<0, 0>, "swp30_device::eq_filter_w<0, 0>");
- map(0x0044, 0x0045).select(0x1f80).rw(&swp30_device::eq_filter_r<0, 1>, "swp30_device::eq_filter_r<0, 1>", &swp30_device::eq_filter_w<0, 1>, "swp30_device::eq_filter_w<0, 1>");
- map(0x0048, 0x0049).select(0x1f80).rw(&swp30_device::eq_filter_r<0, 2>, "swp30_device::eq_filter_r<0, 2>", &swp30_device::eq_filter_w<0, 2>, "swp30_device::eq_filter_w<0, 2>");
- map(0x004c, 0x004d).select(0x1f80).rw(&swp30_device::eq_filter_r<1, 0>, "swp30_device::eq_filter_r<1, 0>", &swp30_device::eq_filter_w<1, 0>, "swp30_device::eq_filter_w<1, 0>");
- map(0x0050, 0x0051).select(0x1f80).rw(&swp30_device::eq_filter_r<1, 1>, "swp30_device::eq_filter_r<1, 1>", &swp30_device::eq_filter_w<1, 1>, "swp30_device::eq_filter_w<1, 1>");
- map(0x0054, 0x0055).select(0x1f80).rw(&swp30_device::eq_filter_r<1, 2>, "swp30_device::eq_filter_r<1, 2>", &swp30_device::eq_filter_w<1, 2>, "swp30_device::eq_filter_w<1, 2>");
- map(0x0064, 0x0065).select(0x1f80).rw(FUNC(swp30_device::pan_r), FUNC(swp30_device::pan_w));
- map(0x031c, 0x031d). rw(FUNC(swp30_device::keyon_mask_r<3>), FUNC(swp30_device::keyon_mask_w<3>));
- map(0x031e, 0x031f). rw(FUNC(swp30_device::keyon_mask_r<2>), FUNC(swp30_device::keyon_mask_w<2>));
- map(0x039c, 0x039d). rw(FUNC(swp30_device::keyon_mask_r<1>), FUNC(swp30_device::keyon_mask_w<1>));
- map(0x039e, 0x039f). rw(FUNC(swp30_device::keyon_mask_r<0>), FUNC(swp30_device::keyon_mask_w<0>));
- map(0x041c, 0x041d). rw(FUNC(swp30_device::keyon_r), FUNC(swp30_device::keyon_w));
-
- map(0x1060, 0x1061). rw(&swp30_device::insertion_delay_r<0, 0>, "swp30_device::insertion_delay_r<0, 0>", &swp30_device::insertion_delay_w<0, 0>, "swp30_device::insertion_delay_w<0, 0>");
- map(0x1062, 0x1063). rw(&swp30_device::insertion_delay_r<0, 1>, "swp30_device::insertion_delay_r<0, 1>", &swp30_device::insertion_delay_w<0, 1>, "swp30_device::insertion_delay_w<0, 1>");
- map(0x10e0, 0x10e1). rw(&swp30_device::insertion_delay_r<0, 2>, "swp30_device::insertion_delay_r<0, 2>", &swp30_device::insertion_delay_w<0, 2>, "swp30_device::insertion_delay_w<0, 2>");
- map(0x10e2, 0x10e3). rw(&swp30_device::insertion_delay_r<0, 3>, "swp30_device::insertion_delay_r<0, 3>", &swp30_device::insertion_delay_w<0, 3>, "swp30_device::insertion_delay_w<0, 3>");
- map(0x1152, 0x1153). rw(&swp30_device::insertion_inv_high_damp_r<0, 0>, "swp30_device::insertion_inv_high_damp_r<0, 0>", &swp30_device::insertion_inv_high_damp_w<0, 0>, "swp30_device::insertion_inv_high_damp_w<0, 0>");
- map(0x1156, 0x1157). rw(&swp30_device::insertion_high_damp_r<0, 0>, "swp30_device::insertion_high_damp_r<0, 0>", &swp30_device::insertion_high_damp_w<0, 0>, "swp30_device::insertion_high_damp_w<0, 0>");
- map(0x11ca, 0x11cb). rw(&swp30_device::insertion_inv_high_damp_r<0, 1>, "swp30_device::insertion_inv_high_damp_r<0, 1>", &swp30_device::insertion_inv_high_damp_w<0, 1>, "swp30_device::insertion_inv_high_damp_w<0, 1>");
- map(0x11ce, 0x11cf). rw(&swp30_device::insertion_high_damp_r<0, 1>, "swp30_device::insertion_high_damp_r<0, 1>", &swp30_device::insertion_high_damp_w<0, 1>, "swp30_device::insertion_high_damp_w<0, 1>");
- map(0x1242, 0x1243). rw(&swp30_device::insertion_fb_level_r<0, 0>, "swp30_device::insertion_fb_level_r<0, 0>", &swp30_device::insertion_fb_level_w<0, 0>, "swp30_device::insertion_fb_level_w<0, 0>");
- map(0x1246, 0x1247). rw(&swp30_device::insertion_fb_level_r<0, 1>, "swp30_device::insertion_fb_level_r<0, 1>", &swp30_device::insertion_fb_level_w<0, 1>, "swp30_device::insertion_fb_level_w<0, 1>");
- map(0x124e, 0x124f). rw(&swp30_device::insertion_fb_level_r<0, 2>, "swp30_device::insertion_fb_level_r<0, 2>", &swp30_device::insertion_fb_level_w<0, 2>, "swp30_device::insertion_fb_level_w<0, 2>");
- map(0x12d6, 0x12d7). rw(&swp30_device::insertion_final_level_r<0, 0>, "swp30_device::insertion_final_level_r<0, 0>", &swp30_device::insertion_final_level_w<0, 0>, "swp30_device::insertion_final_level_w<0, 0>");
- map(0x1342, 0x1343). rw(&swp30_device::insertion_final_level_r<0, 1>, "swp30_device::insertion_final_level_r<0, 1>", &swp30_device::insertion_final_level_w<0, 1>, "swp30_device::insertion_final_level_w<0, 1>");
- map(0x1346, 0x1347). rw(&swp30_device::insertion_final_level_r<0, 2>, "swp30_device::insertion_final_level_r<0, 2>", &swp30_device::insertion_final_level_w<0, 2>, "swp30_device::insertion_final_level_w<0, 2>");
- map(0x134a, 0x134b). rw(&swp30_device::insertion_final_level_r<0, 3>, "swp30_device::insertion_final_level_r<0, 3>", &swp30_device::insertion_final_level_w<0, 3>, "swp30_device::insertion_final_level_w<0, 3>");
-
- map(0x1460, 0x1461). rw(&swp30_device::insertion_delay_r<1, 0>, "swp30_device::insertion_delay_r<1, 0>", &swp30_device::insertion_delay_w<1, 0>, "swp30_device::insertion_delay_w<1, 0>");
- map(0x1462, 0x1463). rw(&swp30_device::insertion_delay_r<1, 1>, "swp30_device::insertion_delay_r<1, 1>", &swp30_device::insertion_delay_w<1, 1>, "swp30_device::insertion_delay_w<1, 1>");
- map(0x14e0, 0x14e1). rw(&swp30_device::insertion_delay_r<1, 2>, "swp30_device::insertion_delay_r<1, 2>", &swp30_device::insertion_delay_w<1, 2>, "swp30_device::insertion_delay_w<1, 2>");
- map(0x14e2, 0x14e3). rw(&swp30_device::insertion_delay_r<1, 3>, "swp30_device::insertion_delay_r<1, 3>", &swp30_device::insertion_delay_w<1, 3>, "swp30_device::insertion_delay_w<1, 3>");
- map(0x1552, 0x1553). rw(&swp30_device::insertion_inv_high_damp_r<1, 0>, "swp30_device::insertion_inv_high_damp_r<1, 0>", &swp30_device::insertion_inv_high_damp_w<1, 0>, "swp30_device::insertion_inv_high_damp_w<1, 0>");
- map(0x1556, 0x1557). rw(&swp30_device::insertion_high_damp_r<1, 0>, "swp30_device::insertion_high_damp_r<1, 0>", &swp30_device::insertion_high_damp_w<1, 0>, "swp30_device::insertion_high_damp_w<1, 0>");
- map(0x15ca, 0x15cb). rw(&swp30_device::insertion_inv_high_damp_r<1, 1>, "swp30_device::insertion_inv_high_damp_r<1, 1>", &swp30_device::insertion_inv_high_damp_w<1, 1>, "swp30_device::insertion_inv_high_damp_w<1, 1>");
- map(0x15ce, 0x15cf). rw(&swp30_device::insertion_high_damp_r<1, 1>, "swp30_device::insertion_high_damp_r<1, 1>", &swp30_device::insertion_high_damp_w<1, 1>, "swp30_device::insertion_high_damp_w<1, 1>");
- map(0x1642, 0x1643). rw(&swp30_device::insertion_fb_level_r<1, 0>, "swp30_device::insertion_fb_level_r<1, 0>", &swp30_device::insertion_fb_level_w<1, 0>, "swp30_device::insertion_fb_level_w<1, 0>");
- map(0x1646, 0x1647). rw(&swp30_device::insertion_fb_level_r<1, 1>, "swp30_device::insertion_fb_level_r<1, 1>", &swp30_device::insertion_fb_level_w<1, 1>, "swp30_device::insertion_fb_level_w<1, 1>");
- map(0x164e, 0x164f). rw(&swp30_device::insertion_fb_level_r<1, 2>, "swp30_device::insertion_fb_level_r<1, 2>", &swp30_device::insertion_fb_level_w<1, 2>, "swp30_device::insertion_fb_level_w<1, 2>");
- map(0x16d6, 0x16d7). rw(&swp30_device::insertion_final_level_r<1, 0>, "swp30_device::insertion_final_level_r<1, 0>", &swp30_device::insertion_final_level_w<1, 0>, "swp30_device::insertion_final_level_w<1, 0>");
- map(0x1742, 0x1743). rw(&swp30_device::insertion_final_level_r<1, 1>, "swp30_device::insertion_final_level_r<1, 1>", &swp30_device::insertion_final_level_w<1, 1>, "swp30_device::insertion_final_level_w<1, 1>");
- map(0x1746, 0x1747). rw(&swp30_device::insertion_final_level_r<1, 2>, "swp30_device::insertion_final_level_r<1, 2>", &swp30_device::insertion_final_level_w<1, 2>, "swp30_device::insertion_final_level_w<1, 2>");
- map(0x174a, 0x174b). rw(&swp30_device::insertion_final_level_r<1, 3>, "swp30_device::insertion_final_level_r<1, 3>", &swp30_device::insertion_final_level_w<1, 3>, "swp30_device::insertion_final_level_w<1, 3>");
-
- map(0x18e0, 0x18e1). rw(FUNC(swp30_device::variation_delay_r<0>), FUNC(swp30_device::variation_delay_w<0>));
- map(0x1960, 0x1961). rw(FUNC(swp30_device::variation_delay_r<1>), FUNC(swp30_device::variation_delay_w<1>));
- map(0x19e0, 0x19e1). rw(FUNC(swp30_device::variation_delay_r<2>), FUNC(swp30_device::variation_delay_w<2>));
- map(0x1ae0, 0x1ae1). rw(FUNC(swp30_device::variation_delay_r<3>), FUNC(swp30_device::variation_delay_w<3>));
- map(0x1bd6, 0x1bd7). rw(FUNC(swp30_device::variation_inv_high_damp_r<0>), FUNC(swp30_device::variation_inv_high_damp_w<0>));
- map(0x1c46, 0x1c47). rw(FUNC(swp30_device::variation_high_damp_r<0>), FUNC(swp30_device::variation_high_damp_w<0>));
- map(0x1c52, 0x1c53). rw(FUNC(swp30_device::variation_inv_high_damp_r<1>), FUNC(swp30_device::variation_inv_high_damp_w<1>));
- map(0x1cc2, 0x1cc3). rw(FUNC(swp30_device::variation_high_damp_r<1>), FUNC(swp30_device::variation_high_damp_w<1>));
- map(0x1cce, 0x1ccf). rw(FUNC(swp30_device::variation_fb_level_r<0>), FUNC(swp30_device::variation_fb_level_w<0>));
- map(0x1cd2, 0x1cd3). rw(FUNC(swp30_device::variation_fb_level_r<1>), FUNC(swp30_device::variation_fb_level_w<1>));
- map(0x1d46, 0x1d47). rw(FUNC(swp30_device::variation_fb_level_r<2>), FUNC(swp30_device::variation_fb_level_w<2>));
- map(0x1f4a, 0x1f4b). rw(FUNC(swp30_device::variation_final_level_r<0>), FUNC(swp30_device::variation_final_level_w<0>));
- map(0x1f52, 0x1f53). rw(FUNC(swp30_device::variation_final_level_r<1>), FUNC(swp30_device::variation_final_level_w<1>));
- map(0x1f56, 0x1f57). rw(FUNC(swp30_device::variation_final_level_r<2>), FUNC(swp30_device::variation_final_level_w<2>));
- map(0x1fc6, 0x1fc7). rw(FUNC(swp30_device::variation_final_level_r<3>), FUNC(swp30_device::variation_final_level_w<3>));
-
-}
-
+ rchan(map, 0x00).rw(FUNC(swp30_device::lpf_cutoff_r), FUNC(swp30_device::lpf_cutoff_w));
+ rchan(map, 0x01).rw(FUNC(swp30_device::lpf_cutoff_inc_r), FUNC(swp30_device::lpf_cutoff_inc_w));
+ rchan(map, 0x02).rw(FUNC(swp30_device::hpf_cutoff_r), FUNC(swp30_device::hpf_cutoff_w));
+ // 03 seems to always get 5010 except at startup where it's 40ff
+ rchan(map, 0x04).rw(FUNC(swp30_device::lpf_reso_r), FUNC(swp30_device::lpf_reso_w));
+ // 05 missing
+ rchan(map, 0x06).rw(FUNC(swp30_device::envelope_r<0>), FUNC(swp30_device::envelope_w<0>));
+ rchan(map, 0x07).rw(FUNC(swp30_device::envelope_r<1>), FUNC(swp30_device::envelope_w<1>));
+ rchan(map, 0x08).rw(FUNC(swp30_device::envelope_r<2>), FUNC(swp30_device::envelope_w<2>));
+ rchan(map, 0x09).rw(FUNC(swp30_device::volume_r), FUNC(swp30_device::volume_w));
+ // 0a-0d missing
+ // 10 missing
+ rchan(map, 0x11).rw(FUNC(swp30_device::freq_r), FUNC(swp30_device::freq_w));
+ rchan(map, 0x12).rw(FUNC(swp30_device::pre_size_h_r), FUNC(swp30_device::pre_size_h_w));
+ rchan(map, 0x13).rw(FUNC(swp30_device::pre_size_l_r), FUNC(swp30_device::pre_size_l_w));
+ rchan(map, 0x14).rw(FUNC(swp30_device::post_size_h_r), FUNC(swp30_device::post_size_h_w));
+ rchan(map, 0x15).rw(FUNC(swp30_device::post_size_l_r), FUNC(swp30_device::post_size_l_w));
+ rchan(map, 0x16).rw(FUNC(swp30_device::address_h_r), FUNC(swp30_device::address_h_w));
+ rchan(map, 0x17).rw(FUNC(swp30_device::address_l_r), FUNC(swp30_device::address_l_w));
+ rchan(map, 0x20).rw(FUNC(swp30_device::eq_filter_r<0>), FUNC(swp30_device::eq_filter_w<0>));
+ rchan(map, 0x22).rw(FUNC(swp30_device::eq_filter_r<1>), FUNC(swp30_device::eq_filter_w<1>));
+ rchan(map, 0x24).rw(FUNC(swp30_device::eq_filter_r<2>), FUNC(swp30_device::eq_filter_w<2>));
+ rchan(map, 0x26).rw(FUNC(swp30_device::eq_filter_r<3>), FUNC(swp30_device::eq_filter_w<3>));
+ rchan(map, 0x28).rw(FUNC(swp30_device::eq_filter_r<4>), FUNC(swp30_device::eq_filter_w<4>));
+ rchan(map, 0x2a).rw(FUNC(swp30_device::eq_filter_r<5>), FUNC(swp30_device::eq_filter_w<5>));
+ // 2c-2f missing
+ rchan(map, 0x32).rw(FUNC(swp30_device::pan_r), FUNC(swp30_device::pan_w));
+ rchan(map, 0x33).rw(FUNC(swp30_device::dry_rev_r), FUNC(swp30_device::dry_rev_w));
+ rchan(map, 0x34).rw(FUNC(swp30_device::cho_var_r), FUNC(swp30_device::cho_var_w));
+ rchan(map, 0x35).rw(FUNC(swp30_device::routing_r<0>), FUNC(swp30_device::routing_w<0>));
+ rchan(map, 0x36).rw(FUNC(swp30_device::routing_r<1>), FUNC(swp30_device::routing_w<1>));
+ rchan(map, 0x37).rw(FUNC(swp30_device::routing_r<2>), FUNC(swp30_device::routing_w<2>));
+ // 38-3d missing, are special
+
+ // Control registers
+ // These appear as channel slots 0x0e and 0x0f
+ // 00-0b missing
+ rctrl(map, 0x0c).rw(FUNC(swp30_device::keyon_mask_r<3>), FUNC(swp30_device::keyon_mask_w<3>));
+ rctrl(map, 0x0d).rw(FUNC(swp30_device::keyon_mask_r<2>), FUNC(swp30_device::keyon_mask_w<2>));
+ rctrl(map, 0x0e).rw(FUNC(swp30_device::keyon_mask_r<1>), FUNC(swp30_device::keyon_mask_w<1>));
+ rctrl(map, 0x0f).rw(FUNC(swp30_device::keyon_mask_r<0>), FUNC(swp30_device::keyon_mask_w<0>));
+ rctrl(map, 0x10).rw(FUNC(swp30_device::keyon_r), FUNC(swp30_device::keyon_w));
+ // 11-20 missing
+ rctrl(map, 0x21).rw(FUNC(swp30_device::prg_address_r), FUNC(swp30_device::prg_address_w));
+ rctrl(map, 0x22).rw(FUNC(swp30_device::prg_r<0>), FUNC(swp30_device::prg_w<0>));
+ rctrl(map, 0x23).rw(FUNC(swp30_device::prg_r<1>), FUNC(swp30_device::prg_w<1>));
+ rctrl(map, 0x24).rw(FUNC(swp30_device::prg_r<2>), FUNC(swp30_device::prg_w<2>));
+ rctrl(map, 0x25).rw(FUNC(swp30_device::prg_r<3>), FUNC(swp30_device::prg_w<3>));
+ // 26-7f missing
+ rctrl(map, 0x30).rw(FUNC(swp30_device::map_r<0>), FUNC(swp30_device::map_w<0>));
+ rctrl(map, 0x32).rw(FUNC(swp30_device::map_r<1>), FUNC(swp30_device::map_w<1>));
+ rctrl(map, 0x34).rw(FUNC(swp30_device::map_r<2>), FUNC(swp30_device::map_w<2>));
+ rctrl(map, 0x36).rw(FUNC(swp30_device::map_r<3>), FUNC(swp30_device::map_w<3>));
+ rctrl(map, 0x38).rw(FUNC(swp30_device::map_r<4>), FUNC(swp30_device::map_w<4>));
+ rctrl(map, 0x3a).rw(FUNC(swp30_device::map_r<5>), FUNC(swp30_device::map_w<5>));
+ rctrl(map, 0x3c).rw(FUNC(swp30_device::map_r<6>), FUNC(swp30_device::map_w<6>));
+ rctrl(map, 0x3e).rw(FUNC(swp30_device::map_r<7>), FUNC(swp30_device::map_w<7>));
+
+ // MEG registers
+ rchan(map, 0x21).rw(FUNC(swp30_device::prg_fp_r<0>), FUNC(swp30_device::prg_fp_w<0>));
+ rchan(map, 0x23).rw(FUNC(swp30_device::prg_fp_r<1>), FUNC(swp30_device::prg_fp_w<1>));
+ rchan(map, 0x25).rw(FUNC(swp30_device::prg_fp_r<2>), FUNC(swp30_device::prg_fp_w<2>));
+ rchan(map, 0x27).rw(FUNC(swp30_device::prg_fp_r<3>), FUNC(swp30_device::prg_fp_w<3>));
+ rchan(map, 0x29).rw(FUNC(swp30_device::prg_fp_r<4>), FUNC(swp30_device::prg_fp_w<4>));
+ rchan(map, 0x2b).rw(FUNC(swp30_device::prg_fp_r<5>), FUNC(swp30_device::prg_fp_w<5>));
+ rchan(map, 0x30).rw(FUNC(swp30_device::prg_int_r<0>), FUNC(swp30_device::prg_int_w<0>));
+ rchan(map, 0x31).rw(FUNC(swp30_device::prg_int_r<1>), FUNC(swp30_device::prg_int_w<1>));
+ rchan(map, 0x3e).rw(FUNC(swp30_device::prg_lfo_r<0>), FUNC(swp30_device::prg_lfo_w<0>));
+ rchan(map, 0x3f).rw(FUNC(swp30_device::prg_lfo_r<1>), FUNC(swp30_device::prg_lfo_w<1>));
+}
+
+// Control registers
template<int sel> u16 swp30_device::keyon_mask_r()
{
return m_keyon_mask >> (16*sel);
@@ -268,7 +329,6 @@ u16 swp30_device::keyon_r()
return 0;
}
-static int ncount = 0;
void swp30_device::keyon_w(u16)
{
m_stream->update();
@@ -276,14 +336,60 @@ void swp30_device::keyon_w(u16)
u64 mask = u64(1) << i;
if((m_keyon_mask & mask) && !(m_active_mask & mask) && !(m_volume[i] & 0x8000)) {
m_sample_pos[i] = -s32(m_pre_size[i] << 8);
- logerror("sample count %3d %02x %08x %08x %08x vol %04x pan %04x\n", ncount, i, m_pre_size[i], m_post_size[i], m_address[i], m_volume[i], m_pan[i]);
+ logerror("keyon %02x %08x %08x %08x vol %04x pan %04x\n", i, m_pre_size[i], m_post_size[i], m_address[i], m_volume[i], m_pan[i]);
m_active_mask |= mask;
- ncount++;
}
}
m_keyon_mask = 0;
}
+
+u16 swp30_device::prg_address_r()
+{
+ return m_program_address;
+}
+
+void swp30_device::prg_address_w(u16 data)
+{
+ m_program_address = data;
+ if(m_program_address >= 0x180)
+ m_program_address = 0;
+}
+
+template<int sel> u16 swp30_device::prg_r()
+{
+ constexpr offs_t shift = 48-16*sel;
+ return m_program[m_program_address] >> shift;
+}
+
+template<int sel> void swp30_device::prg_w(u16 data)
+{
+ constexpr offs_t shift = 48-16*sel;
+ constexpr u64 mask = ~(u64(0xffff) << shift);
+ m_program[m_program_address] = (m_program[m_program_address] & mask) | (u64(data) << shift);
+
+ if(sel == 3) {
+ logerror("program %03x %016x\n", m_program_address, m_program[m_program_address]);
+ m_program_address ++;
+ if(m_program_address == 0x180)
+ m_program_address = 0;
+ }
+}
+
+
+template<int sel> u16 swp30_device::map_r()
+{
+ return m_map[sel];
+}
+
+template<int sel> void swp30_device::map_w(u16 data)
+{
+ m_map[sel] = data;
+ logerror("map %d: type=%02x offset=%05x size=%05x\n", sel, data >> 11, (data & 0xff) << 10, 0x400 << ((data >> 8) & 7));
+}
+
+
+// AWM2 per-channel registers
u16 swp30_device::lpf_cutoff_r(offs_t offset)
{
return m_lpf_cutoff[offset >> 6];
@@ -293,11 +399,25 @@ void swp30_device::lpf_cutoff_w(offs_t offset, u16 data)
{
m_stream->update();
u8 chan = offset >> 6;
- if(m_lpf_cutoff[chan] != data)
+ if(m_lpf_cutoff[chan] != data && 0)
logerror("chan %02x lpf cutoff %04x\n", chan, data);
m_lpf_cutoff[chan] = data;
}
+u16 swp30_device::lpf_cutoff_inc_r(offs_t offset)
+{
+ return m_lpf_cutoff_inc[offset >> 6];
+}
+
+void swp30_device::lpf_cutoff_inc_w(offs_t offset, u16 data)
+{
+ m_stream->update();
+ u8 chan = offset >> 6;
+ if(m_lpf_cutoff_inc[chan] != data && 0)
+ logerror("chan %02x lpf cutoff increment %04x\n", chan, data);
+ m_lpf_cutoff_inc[chan] = data;
+}
+
u16 swp30_device::hpf_cutoff_r(offs_t offset)
{
return m_hpf_cutoff[offset >> 6];
@@ -326,17 +446,27 @@ void swp30_device::lpf_reso_w(offs_t offset, u16 data)
m_lpf_reso[chan] = data;
}
-template<int filter, int coef> u16 swp30_device::eq_filter_r(offs_t offset)
+template<int coef> u16 swp30_device::eq_filter_r(offs_t offset)
{
- return m_eq_filter[offset >> 6][filter][coef];
+ return m_eq_filter[offset >> 6][coef];
}
-template<int filter, int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data)
+template<int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data)
{
m_stream->update();
- m_eq_filter[offset >> 6][filter][coef] = data;
+ m_eq_filter[offset >> 6][coef] = data;
}
+template<int sel> u16 swp30_device::routing_r(offs_t offset)
+{
+ return m_routing[offset >> 6][sel];
+}
+
+template<int sel> void swp30_device::routing_w(offs_t offset, u16 data)
+{
+ m_stream->update();
+ m_routing[offset >> 6][sel] = data;
+}
u16 swp30_device::volume_r(offs_t offset)
{
@@ -351,8 +481,12 @@ void swp30_device::volume_w(offs_t offset, u16 data)
if(m_volume[chan] != data)
logerror("snd chan %02x volume %02x %02x\n", chan, data >> 8, data & 0xff);
m_volume[chan] = data;
- if(data & 0x8000)
- m_active_mask &= ~(u64(1) << chan);
+ if(data & 0x8000) {
+ if(m_active_mask & (u64(1) << chan)) {
+ if(m_post_size[chan])
+ m_active_mask &= ~(u64(1) << chan);
+ }
+ }
}
@@ -364,213 +498,211 @@ u16 swp30_device::pan_r(offs_t offset)
void swp30_device::pan_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- double p1 = pow(2, -(data >> 8)/16.0);
- double p2 = pow(2, -(data & 0xff)/16.0);
if(m_pan[chan] != data)
- logerror("snd chan %02x pan l %02x r %02x (%g %g %g)\n", chan, data >> 8, data & 0xff, p1, p2, sqrt(p1*p1+p2*p2));
+ logerror("snd chan %02x pan l %02x r %02x\n", chan, data >> 8, data & 0xff);
m_pan[chan] = data;
}
-u16 swp30_device::freq_r(offs_t offset)
+u16 swp30_device::dry_rev_r(offs_t offset)
{
- return m_freq[offset >> 6];
+ return m_dry_rev[offset >> 6];
}
-void swp30_device::freq_w(offs_t offset, u16 data)
+void swp30_device::dry_rev_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- // delta is 4*256 per octave, positive means higher freq, e.g 4.10 format.
- s16 v = data & 0x2000 ? data | 0xc000 : data;
- if(m_freq[chan] != data)
- logerror("snd chan %02x freq %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
- m_freq[chan] = data;
+ if(m_dry_rev[chan] != data)
+ logerror("snd chan %02x dry %02x rev %02x\n", chan, data >> 8, data & 0xff);
+ m_dry_rev[chan] = data;
}
-u16 swp30_device::pre_size_r(offs_t offset)
+u16 swp30_device::cho_var_r(offs_t offset)
{
- if(offset & 1)
- return m_pre_size[offset >> 6];
- else
- return m_pre_size[offset >> 6] >> 16;
+ return m_dry_rev[offset >> 6];
}
-void swp30_device::pre_size_w(offs_t offset, u16 data)
+void swp30_device::cho_var_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(offset & 1) {
- m_pre_size[chan] = (m_pre_size[chan] & 0xffff0000) | data;
- logerror("snd chan %02x pre-size %02x %06x\n", chan, m_pre_size[chan] >> 24, m_pre_size[chan] & 0xffffff);
- } else
- m_pre_size[chan] = (m_pre_size[chan] & 0x0000ffff) | (data << 16);
+ if(m_dry_rev[chan] != data)
+ logerror("snd chan %02x cho %02x var %02x\n", chan, data >> 8, data & 0xff);
+ m_dry_rev[chan] = data;
}
-u16 swp30_device::post_size_r(offs_t offset)
+u16 swp30_device::freq_r(offs_t offset)
{
- if(offset & 1)
- return m_post_size[offset >> 6];
- else
- return m_post_size[offset >> 6] >> 16;
+ return m_freq[offset >> 6];
}
-void swp30_device::post_size_w(offs_t offset, u16 data)
+void swp30_device::freq_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(offset & 1) {
- m_post_size[chan] = (m_post_size[chan] & 0xffff0000) | data;
- logerror("snd chan %02x post-size %02x %06x\n", chan, m_post_size[chan] >> 24, m_post_size[chan] & 0xffffff);
- } else
- m_post_size[chan] = (m_post_size[chan] & 0x0000ffff) | (data << 16);
+ // delta is 4*256 per octave, positive means higher freq, e.g 4.10 format.
+ s16 v = data & 0x2000 ? data | 0xc000 : data;
+ if(m_freq[chan] != data)
+ logerror("snd chan %02x freq %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
+ m_freq[chan] = data;
}
-u16 swp30_device::address_r(offs_t offset)
+template<int sel> u16 swp30_device::envelope_r(offs_t offset)
{
- if(offset & 1)
- return m_address[offset >> 6];
- else
- return m_address[offset >> 6] >> 16;
+ return m_envelope[offset >> 6][sel];
}
-void swp30_device::address_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::envelope_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(offset & 1) {
- // The address may be 25 bits
- static const char *const formats[4] = { "l16", "l12", "l8", "x8" };
- m_address[chan] = (m_address[chan] & 0xffff0000) | data;
- logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff);
- } else
- m_address[chan] = (m_address[chan] & 0x0000ffff) | (data << 16);
+ bool ch = m_envelope[chan][sel] != data;
+ m_envelope[chan][sel] = data;
+ if(ch)
+ logerror("snd chan %02x envelopes %04x %04x %04x\n", chan, m_envelope[chan][0], m_envelope[chan][1], m_envelope[chan][2]);
}
-template<int id> void swp30_device::variation_delay_w(u16 data)
+u16 swp30_device::pre_size_h_r(offs_t offset)
{
- m_variation_delay[id] = data;
- logerror("variation delay %d: %04x (%fms)\n", id, data, data/44.100);
+ return m_pre_size[offset >> 6] >> 16;
}
-template<int id> u16 swp30_device::variation_delay_r()
+u16 swp30_device::pre_size_l_r(offs_t offset)
{
- return m_variation_delay[id];
+ return m_pre_size[offset >> 6];
}
-template<int id> void swp30_device::variation_fb_level_w(u16 data)
+void swp30_device::pre_size_h_w(offs_t offset, u16 data)
{
- m_variation_fb_level[id] = data;
- logerror("variation feedback level %d: %04x (%+f)\n", id, data, s16(data)/500.0);
+ u8 chan = offset >> 6;
+ m_pre_size[chan] = (m_pre_size[chan] & 0x0000ffff) | (data << 16);
}
-template<int id> u16 swp30_device::variation_fb_level_r()
+void swp30_device::pre_size_l_w(offs_t offset, u16 data)
{
- return m_variation_fb_level[id];
+ u8 chan = offset >> 6;
+ m_pre_size[chan] = (m_pre_size[chan] & 0xffff0000) | data;
+ logerror("snd chan %02x pre-size %02x %06x\n", chan, m_pre_size[chan] >> 24, m_pre_size[chan] & 0xffffff);
}
-template<int id> void swp30_device::variation_final_level_w(u16 data)
+u16 swp30_device::post_size_h_r(offs_t offset)
{
- m_variation_final_level[id] = data;
- logerror("variation final level %s %s: %04x (%f)\n", id & 1 ? "wet" : "dry", id & 2 ? "left" : "right", data, data/16383.0);
+ return m_post_size[offset >> 6] >> 16;
}
-template<int id> u16 swp30_device::variation_final_level_r()
+u16 swp30_device::post_size_l_r(offs_t offset)
{
- return m_variation_final_level[id];
+ return m_post_size[offset >> 6];
}
-template<int id> void swp30_device::variation_high_damp_w(u16 data)
+void swp30_device::post_size_h_w(offs_t offset, u16 data)
{
- m_variation_high_damp[id] = data;
- logerror("variation high damp %d: %04x (%f)\n", id, data, 0.1+double(data-0x2000)/0x2000*0.9);
+ u8 chan = offset >> 6;
+ m_post_size[chan] = (m_post_size[chan] & 0x0000ffff) | (data << 16);
}
-template<int id> u16 swp30_device::variation_high_damp_r()
+void swp30_device::post_size_l_w(offs_t offset, u16 data)
{
- return m_variation_high_damp[id];
+ u8 chan = offset >> 6;
+ m_post_size[chan] = (m_post_size[chan] & 0xffff0000) | data;
+ logerror("snd chan %02x post-size %02x %06x\n", chan, m_post_size[chan] >> 24, m_post_size[chan] & 0xffffff);
}
-template<int id> void swp30_device::variation_inv_high_damp_w(u16 data)
+u16 swp30_device::address_h_r(offs_t offset)
{
- m_variation_inv_high_damp[id] = data;
- logerror("variation inverse high damp %d: %04x (%f)\n", id, data, 0.1+double(0x2000-data)/0x2000*0.9);
+ return m_address[offset >> 6] >> 16;
}
-template<int id> u16 swp30_device::variation_inv_high_damp_r()
+u16 swp30_device::address_l_r(offs_t offset)
{
- return m_variation_inv_high_damp[id];
+ return m_address[offset >> 6];
}
-
-template<int ins, int id> void swp30_device::insertion_delay_w(u16 data)
+void swp30_device::address_h_w(offs_t offset, u16 data)
{
- m_insertion_delay[ins][id] = data;
- logerror("insertion %d delay %d: %04x (%fms)\n", ins, id, data, data/44.100);
+ u8 chan = offset >> 6;
+ m_address[chan] = (m_address[chan] & 0x0000ffff) | (data << 16);
}
-template<int ins, int id> u16 swp30_device::insertion_delay_r()
+void swp30_device::address_l_w(offs_t offset, u16 data)
{
- return m_insertion_delay[ins][id];
+ u8 chan = offset >> 6;
+ // The address probably is 25 bits
+ static const char *const formats[4] = { "l16", "l12", "l8", "x8" };
+ m_address[chan] = (m_address[chan] & 0xffff0000) | data;
+ logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff);
}
-template<int ins, int id> void swp30_device::insertion_fb_level_w(u16 data)
-{
- m_insertion_fb_level[ins][id] = data;
- logerror("insertion %d feedback level %d: %04x (%+f)\n", ins, id, data, s16(data)/500.0);
-}
-template<int ins, int id> u16 swp30_device::insertion_fb_level_r()
-{
- return m_insertion_fb_level[ins][id];
-}
+// MEG registers (Multiple Effects Generator)
-template<int ins, int id> void swp30_device::insertion_final_level_w(u16 data)
+template<int sel> u16 swp30_device::prg_fp_r(offs_t offset)
{
- m_insertion_final_level[ins][id] = data;
- logerror("insertion %d final level %s %s: %04x (%f)\n", ins, id & 1 ? "wet" : "dry", id & 2 ? "left" : "right", data, data/8191.0);
+ offs_t adr = (offset >> 6)*6 + sel;
+ return m_program_pfp[adr];
}
-template<int ins, int id> u16 swp30_device::insertion_final_level_r()
+template<int sel> void swp30_device::prg_fp_w(offs_t offset, u16 data)
{
- return m_insertion_final_level[ins][id];
+ offs_t adr = (offset >> 6)*6 + sel;
+ m_program_pfp[adr] = data;
+ logerror("prg_fp_w %03x, %04x\n", adr, data);
}
-template<int ins, int id> void swp30_device::insertion_high_damp_w(u16 data)
+template<int sel> u16 swp30_device::prg_int_r(offs_t offset)
{
- m_insertion_high_damp[ins][id] = data;
- logerror("insertion %d high damp %d: %04x (%f)\n", ins, id, data, 0.1+double(data-0x4000)/0x4000*0.9);
+ offs_t adr = (offset >> 6)*2 + sel;
+ return m_program_pint[adr];
}
-template<int ins, int id> u16 swp30_device::insertion_high_damp_r()
+template<int sel> void swp30_device::prg_int_w(offs_t offset, u16 data)
{
- return m_insertion_high_damp[ins][id];
+ offs_t adr = (offset >> 6)*2 + sel;
+ m_program_pint[adr] = data;
+ logerror("prg_int_w %02x, %04x\n", adr, data);
}
-template<int ins, int id> void swp30_device::insertion_inv_high_damp_w(u16 data)
+template<int sel> u16 swp30_device::prg_lfo_r(offs_t offset)
{
- m_insertion_inv_high_damp[ins][id] = data;
- logerror("insertion %s inverse high damp %d: %04x (%f)\n", ins, id, data, 0.1+double(0x4000-data)/0x4000*0.9);
+ offs_t adr = (offset >> 6)*2 + sel;
+ return m_program_plfo[adr];
}
-template<int ins, int id> u16 swp30_device::insertion_inv_high_damp_r()
+template<int sel> void swp30_device::prg_lfo_w(offs_t offset, u16 data)
{
- return m_insertion_inv_high_damp[ins][id];
+ offs_t adr = (offset >> 6)*2 + sel;
+ m_program_plfo[adr] = data;
+
+ static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 };
+ static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 };
+
+ int scale = (data >> 5) & 7;
+ int step = ((data & 31) << sh[scale]) + dt[scale];
+ logerror("prg_lfo_w %02x freq=%5.2f phase=%6.4f\n", adr, step * 44100.0/4194304, (data >> 8)/256.0);
}
+// Catch-all
+
static u16 rr[0x40*0x40];
u16 swp30_device::snd_r(offs_t offset)
{
- if(0) {
+ if(1) {
int chan = (offset >> 6) & 0x3f;
int slot = offset & 0x3f;
std::string preg = "-";
if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("%03x", (slot-0x21)/2 + 6*chan);
- logerror("snd_r [%04x %04x - %-4s] %02x.%02x %04x\n", offset, offset*2, preg, chan, slot, rr[offset]);
- }
- if(offset == 0x080f) {
- return rr[offset] & ~8;
- // return chan == 0x20 && slot == 0xf ? 0 : 0xffff;
+ 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];
}
@@ -578,25 +710,45 @@ void swp30_device::snd_w(offs_t offset, u16 data)
{
if(rr[offset] == data)
return;
- if(offset == 0x4e && (data ^ rr[offset]) == 0x400) {
- rr[offset] = data;
- return;
- }
+
rr[offset] = data;
- if(offset == 0x4e)
- return;
+
int chan = (offset >> 6) & 0x3f;
int slot = offset & 0x3f;
+
+ if(offset == 0x04e)
+ return;
+
+ if(0 && slot == 0x03)
+ machine().debug_break();
+
std::string preg = "-";
if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("%03x", (slot-0x21)/2 + 6*chan);
- logerror("snd_w [%04x %04x - %-4s] %02x.%02x, %04x\n", offset, offset*2, preg, chan, slot, data);
+ 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
+ preg = util::string_format("%02x.%02x", chan, slot);
+ if((slot >= 0xa && slot <= 0xd) || (slot >= 0x2c && slot <= 0x2f))
+ machine().debug_break();
+
+ logerror("snd_w [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, data);
}
+
+
+// Synthesis
+
void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
// Loop first on the samples and not on the channels otherwise
- // effects will be impossible to implement.
+ // effects will be annoying to implement.
for(int sample = 0; sample < samples; sample++) {
// Accumulate on 64 bits, shift/clamp at the end
@@ -661,7 +813,7 @@ void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **i
// Second, step the sample pos, loop/deactivate as needed
m_sample_pos[channel] += m_sample_increment[m_freq[channel] & 0x3fff];
- s32 loop_size = m_post_size[channel] << 8;
+ s32 loop_size = (m_post_size[channel] << 8) | ((m_address[channel] >> 22) & 0xf8);
if(m_sample_pos[channel] >= loop_size) {
// We reached the loop point, stop if loop size is zero,
// otherwise loop
@@ -677,12 +829,12 @@ void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **i
// - missing lpf_cutoff, lpf_reso, hpf_cutoff
// - eq lowpass
- s32 samp1 = (samp * m_eq_filter[channel][0][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][0][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0][0]) >> 13;
+ s32 samp1 = (samp * m_eq_filter[channel][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0]) >> 13;
m_sample_history[channel][0][1] = m_sample_history[channel][0][0];
m_sample_history[channel][0][0] = samp;
// - eq highpass
- s32 samp2 = (samp1 * m_eq_filter[channel][1][2] + m_sample_history[channel][1][0] * m_eq_filter[channel][1][1] + m_sample_history[channel][1][1] * m_eq_filter[channel][1][0]) >> 13;
+ s32 samp2 = (samp1 * m_eq_filter[channel][5] + m_sample_history[channel][1][0] * m_eq_filter[channel][4] + m_sample_history[channel][1][1] * m_eq_filter[channel][3]) >> 13;
m_sample_history[channel][1][1] = m_sample_history[channel][1][0];
m_sample_history[channel][1][0] = samp1;
@@ -700,7 +852,7 @@ void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **i
}
// Samples are 16 bits, there are up to 64 of them, and the accumulators are fixed-point signed 48.16
- // Global EQ is missing
+ // Global EQ is missing (it's done in the MEG)
acc_left >>= (16+6);
if(acc_left < -0x8000)