summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2025-09-27 22:45:52 +0200
committer Olivier Galibert <galibert@pobox.com>2025-09-27 22:46:01 +0200
commit996b632bed816427211fa0a7f596fe7c135c2925 (patch)
tree5801c2bc3bf1c003037dfcbf1027c101cb359807
parentd2479ffdcc20903602a07a2aa6f229e243ef6aa0 (diff)
swp30: Overhaul
-rw-r--r--scripts/src/cpu.lua5
-rw-r--r--src/devices/bus/plg1x0/plg100-vl.cpp4
-rw-r--r--src/devices/machine/sci4.cpp2
-rw-r--r--src/devices/sound/swp30.cpp4634
-rw-r--r--src/devices/sound/swp30.h498
-rw-r--r--src/devices/sound/swp30d.cpp172
-rw-r--r--src/devices/sound/swp30d.h39
-rw-r--r--src/emu/dirom.h3
-rw-r--r--src/mame/yamaha/ymmu100.cpp8
-rw-r--r--src/mame/yamaha/ymmu128.cpp2
-rw-r--r--src/mame/yamaha/ymmu2000.cpp72
-rw-r--r--src/mame/yamaha/ymmu90.cpp4
12 files changed, 3907 insertions, 1536 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index 51927330158..b26c4b13b45 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -3591,11 +3591,6 @@ if CPUS["SWP30"] then
}
end
-if opt_tool(CPUS, "SWP30") then
- table.insert(disasm_files , MAME_DIR .. "src/devices/sound/swp30d.cpp")
- table.insert(disasm_files , MAME_DIR .. "src/devices/sound/swp30d.h")
-end
-
--------------------------------------------------
-- Yamaha DSPV
--@src/devices/sound/dspv.h,CPUS["DSPV"] = true
diff --git a/src/devices/bus/plg1x0/plg100-vl.cpp b/src/devices/bus/plg1x0/plg100-vl.cpp
index ee8a5050c14..653e4ddeac3 100644
--- a/src/devices/bus/plg1x0/plg100-vl.cpp
+++ b/src/devices/bus/plg1x0/plg100-vl.cpp
@@ -67,8 +67,8 @@ void plg100_vl_device::device_add_mconfig(machine_config &config)
m_cpu->write_sci_tx<1>().set([this] (int state) { m_connector->do_midi_tx(state); });
DSPV(config, m_dspv, 22.5792_MHz_XTAL);
- m_dspv->add_route(0, DEVICE_SELF_OWNER, 1.0, 0);
- m_dspv->add_route(1, DEVICE_SELF_OWNER, 1.0, 1);
+ // m_dspv->add_route(0, DEVICE_SELF_OWNER, 1.0, 0);
+ // m_dspv->add_route(1, DEVICE_SELF_OWNER, 1.0, 1);
}
ROM_START( plg100_vl )
diff --git a/src/devices/machine/sci4.cpp b/src/devices/machine/sci4.cpp
index b8eb647b4a3..8e494e0383b 100644
--- a/src/devices/machine/sci4.cpp
+++ b/src/devices/machine/sci4.cpp
@@ -182,8 +182,6 @@ u8 sci4_device::reset_r(offs_t slot)
void sci4_device::target_w(u8 data)
{
- if(data == 0x11 && m_targets == 0x07)
- machine().debug_break();
m_targets = data;
u8 rx = ((((m_rx[6] << 3) | (m_rx[5] << 2) | (m_rx[4] << 1) | m_rx[3]) | ~(m_targets >> 4)) & 0xf) == 0xf;
if(rx != m_cur_rx[3]) {
diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp
index d135a77f16c..0ec5cfe5faa 100644
--- a/src/devices/sound/swp30.cpp
+++ b/src/devices/sound/swp30.cpp
@@ -6,626 +6,1961 @@
#include "emu.h"
#include "debugger.h"
#include "swp30.h"
+#include "cpu/drcumlsh.h"
+
+// TODOs:
+// - 7 bits are still not understood in the MEG instructions
+// - in the lfo, the top of slot 9 is not understood
+// - slot b is not understood but used (and even read at times)
+// - some instruments from the demo don't work well (not sure which)
+// - there seems to be some saturation at times in the demo
+// - lots of control registers are not understood, in particular
+// the 5x ones, one of which is used in the talk mod effect
+// - the timing of communication between the meg registers and the
+// the environment is known but not implemented
+
+
+// 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 (MELI) and
+// outputs (MELO) 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, all
+// stereo. The MU100 connects a stereo ADC to the first input, and
+// routes the third input and output to the plugin board.
+
+
+// Registers:
+
+// The chip interface presents 4096 16-bits registers in a 64x64 grid.
+// They are mostly read/write. Some of this grid is for per-channel
+// values for AWM2, but parts are isolated and renumbered for MEG
+// registers or for general control functions.
+
+
+// AWM2:
+
+// The AWM2 is in charge of handling the individual channels. It
+// manages reading the rom, decoding the samples, applying volume and
+// pitch envelopes and lfos and filtering the result. Each channel is
+// then sent as a mono signal to the mixer for further processing.
+
+// It is composed of a number of blocks, please refer to the individual
+// documentations further in the file:
+// - streaming
+// - dual special filters
+// - dual iir1 filters
+// - envelope control
+// - lfo
+
+// The sound data can be four formats (8 bits, 12 bits, 16 bits, and
+// a 8-bits kinda-apdcm format). The rom bus is 25 bits address and
+// 32 bits data wide. It applies four filters to the sample data in
+// two dual filter blocks. The first block has two filters
+// configurable between iir1 and chamberlain, lpf, hpf, band or
+// notch, with our without configurable resonance. The second block
+// is two free iir1 filters. Envelopes are handled automatically,
+// and the final result is sent to the mixer for panning, volume
+// control and routing. In addition lfo acts on the pitch and the
+// volume.
+
+
+// MEG:
+
+// The MEG is a DSP with 384 program steps connected to a reverb
+// samples ram. It computes all the effects and sends to result to
+// the adcs and the serial outputs.
+
+
+// Mixer:
+
+// The mixer gets the outputs of the AWM2, the MEG (for the previous
+// sample) and the external inputs, attenuates and sums them
+// according to its mapping instructions, and pushes the results to
+// the MEG and the external outputs.
+
+
+//--------------------------------------------------------------------------------
+//
+// Memory map in rough numerical order with block indications
+//
+// cccccc 000000 AMW2/IIR mmmm .aaa aaaa aaaa Filter 1 mode and main parameter
+// cccccc 000001 AMW2/IIR .xxx xxxx uuuu uuuu Bypass level
+// cccccc 000010 AMW2/IIR .... .ccc cccc cccc Filter 2 mode and main parameter
+// cccccc 000011 AMW2/IIR .... .... vvvv vvvv Post-filter level
+// cccccc 000100 AMW2/IIR bbbb b... .... .... Filters second parameter
+
+// cccccc 000101 AWM2/LFO .... .... .aaa aaaa LFO amplitude depth
+// cccccc 000110 AWM2/Envelope ssss ssss iiii iiii Attack speed and start volume
+// cccccc 000111 AWM2/Envelope ssss ssss tttt tttt Decay 1 speed and target
+// cccccc 001000 AWM2/Envelope ssss ssss tttt tttt Decay 2 speed and target
+// cccccc 001001 AWM2/Envelope ssss ssss gggg gggg Release speed & global volume
+// cccccc 001010 AWM2/LFO tt.s ssss mppp pppp LFO type, step, pitch mode, pitch depth
+// cccccc 001011 ?
+// cccccc 001100 ?
+// cccccc 001101 ?
+
+// 000000 001110 9100 at startup
+// 000000 001111 c002 at startup then c003
+// 000001 001110 AWM2/Control internal register address
+// 000001 001111 AWM2/Control (read) internal register value
+// 000010 00111* AWM2/Control wave direct access address
+// 000011 00111* AWM2/Control wave direct access size
+// 000100 001110 AWM2/Control wave direct access trigger (8000 = read sample from rom, 9000 = read sample from ram, 5000 = write sample to ram)
+// 000100 001111 AWM2/Control wave direct access status
+// 000101 00111* AWM2/Control wave direct access data
+// 00011* 00111* AWM2/Control keyon mask
+// 001000 001110 AWM2/Control keyon trigger
+// 001101 001110 1100 at startup then 0040
+// 010000 001110 MEG/Control .... .... .... .... commit LFO increments on write
+// 010000 001111 MEG/Control .... ...a aaaa aaaa program address
+// 010001 00111* MEG/Control dddd dddd dddd dddd program data 1/2
+// 010010 00111* MEG/Control dddd dddd dddd dddd program data 2/2
+// 010011 001110 ? sy26
+// 010011 001111 ? sy27
+// 010100 001111 00ff after part 1
+// 010101 001110 00ff after part 1
+// 010101 001111 00ff after part 1
+// 011mmm 001110 MEG/Reverb memory map
+// 100000 001110 MEG/Reverb ram memory map tlb enable (0=on, 1=off, bits 0-7)
+// 100000 001111 MEG/Reverb ram memory bank clear (1=trigger a clear)
+// 100001 001110 MEG/Reverb ram direct access status
+// 100101 00111* MEG/Reverb ram direct access address
+// 100110 00111* MEG/Reverb ram direct access data
+
+// 101*** 00111* ? sy5x
+
+// cccccc 010000 ?
+
+// cccccc 010001 AMW2/Streaming ?-pp pppp pppp pppp Pitch
+// cccccc 01001* AMW2/Streaming ?Lll llll ssss ssss ssss ssss ssss ssss Loop disable. Loop size adjust. Number of samples before the loop point
+// cccccc 01010* AMW2/Streaming bfff ffff ssss ssss ssss ssss ssss ssss Backwards. Finetune. Number of samples in the loop
+// cccccc 01011* AMW2/Streaming ffSS Smma aaaa aaaa aaaa aaaa aaaa aaaa Format, Scaling, Compressor mode, Sample address
+
+// cccccc 100000 AWM2/IIR vvvv vvvv vvvv vvvv IIR1 a1
+// aaaaaa 100001 MEG/Data cccc cccc cccc cccc constant index 6*a + 0
+// cccccc 100010 AWM2/IIR vvvv vvvv vvvv vvvv IIR1 b1
+// aaaaaa 100011 MEG/Data cccc cccc cccc cccc constant index 6*a + 1
+// cccccc 100100 AWM2/IIR vvvv vvvv vvvv vvvv IIR1 a0
+// aaaaaa 100101 MEG/Data cccc cccc cccc cccc constant index 6*a + 2
+// cccccc 100110 AWM2/IIR vvvv vvvv vvvv vvvv IIR1 a1
+// aaaaaa 100111 MEG/Data cccc cccc cccc cccc constant index 6*a + 3
+// cccccc 101000 AWM2/IIR vvvv vvvv vvvv vvvv IIR1 b1
+// aaaaaa 101001 MEG/Data cccc cccc cccc cccc constant index 6*a + 4
+// cccccc 101010 AWM2/IIR vvvv vvvv vvvv vvvv IIR1 a0
+// aaaaaa 101011 MEG/Data cccc cccc cccc cccc constant index 6*a + 5
+
+
+// aaaaaa 11000a MEG/Data oooo oooo oooo oooo offset index a
+// ssssss 110010 Mixer llll llll rrrr rrrr Route attenuation left/right input s
+// ssssss 110011 Mixer 0000 0000 1111 1111 Route attenuation slot 0/1 input s
+// ssssss 110100 Mixer 2222 2222 3333 3333 Route attenuation slot 2/3 input s
+// ssssss 110101 Mixer fedc ba98 7654 3210 Route mode bit 2 input s output 0-f
+// ssssss 110110 Mixer fedc ba98 7654 3210 Route mode bit 1 input s output 0-f
+// ssssss 110111 Mixer fedc ba98 7654 3210 Route mode bit 0 input s output 0-f
+// ssssss 111000 Mixer llll llll rrrr rrrr Route attenuation left/right input s+40
+// ssssss 111001 Mixer 0000 0000 1111 1111 Route attenuation slot 0/1 input s+40
+// ssssss 111010 Mixer 2222 2222 3333 3333 Route attenuation slot 2/3 input s+40
+// ssssss 111011 Mixer fedc ba98 7654 3210 Route mode bit 2 input s+40 output 0-f
+// ssssss 111100 Mixer fedc ba98 7654 3210 Route mode bit 1 input s+40 output 0-f
+// ssssss 111101 Mixer fedc ba98 7654 3210 Route mode bit 0 input s+40 output 0-f
+// aaaaaa 11111a MEG/LFO pppp ttss iiii iiii LFO index a, phase, type, shift, increment
+
+
+//======================= AWM2 blocks ============================================
+
+// Streaming block
+//
+// cccccc 010001 ?-pp pppp pppp pppp Pitch
+// cccccc 01001* ?Lll llll ssss ssss ssss ssss ssss ssss Loop disable. Loop size adjust. Number of samples before the loop point
+// cccccc 01010* bfff ffff ssss ssss ssss ssss ssss ssss Backwards. Finetune. Number of samples in the loop
+// cccccc 01011* ffSS Smma aaaa aaaa aaaa aaaa aaaa aaaa Format, Scaling, Compressor mode, Sample address
+//
+// The streaming block manages reading and decoding samples from rom
+// and/or dram at a given pitch and format. The samples are
+// interpolated for a better quality.
+//
+// Addresses 000000-ffffff are in rom (and maybe sram?),
+// 1000000-1ffffff are in dram.
+//
+// The unknown bit in the 010001 slot tends to be set when reading a
+// compressed sample and unset otherwise. It does not seem to impact
+// the result though. The unknown bit in the 010010 slot doesn't seem
+// to ever been set in the mu100 and does not seem to impact the
+// result.
+//
+// Sample formats
+//
+// Samples can be in one of four formats, 8 bits, 12
+// bits, 16 bits and adaptive-delta-compression with 8 bits per
+// sample. Non-compressed samples are zero-extended on the right to
+// get a almost-full-range 16-bits value.
+//
+// The compressed format uses a running delta and an accumulator. The
+// input byte is expanded into a 10-bit signed value through a fixed
+// table, which is added to the current delta. The delta is then
+// added to the accumulator, which gives the current sample value.
+// Then the current delta is, depending on the mode bits, multiplied
+// by either 0.875 (7/8), 0.75 (3/4), 0.5 (1/2) or 0 (e.g. cleared).
+//
+// The multiplier on the delta is buggy and bias towards negative
+// numbers, but it's not entirely clear how exactly. Even worse, the
+// multiplier results change depending on whether the scaling is zero
+// or non-zero, and also has some kind of context or extra state bits
+// hidden somewhere.
+//
+//
+// Sample scaling
+//
+// Samples just read are then shifted left by Scaling bits (0-7).
+// While the scaling is in practice only used for compressed samples,
+// the hardware applies it to any format. The result is clamped
+// between -0x8000 and a value depending on the amount of scaling
+// (0x7fff for 0, 0x7ffe for 1, ..., 0x7f80 for 7).
+//
+//
+// Sample addressing, pitching and looping
+//
+// The chip has two 25-bits address, 16-bits data buses to the sample
+// roms and drams. The samples are interleaved between the two buses,
+// looking as if the data bus was 32 bits wide. It directly manages
+// dram signals, so there must be a way somewhere for it to tell
+// whether a sample address is in dram or not. When in dual-chip
+// configuration, the address and data lines of the buses are directly
+// connected, so they have a way to arbitrate their accesses. The
+// amount of data needed at a given time varying depending on pitch
+// and sample format, the design of the memory access controller must
+// have been interesting.
+//
+// The current sample position is in signed 25.15 format. The initial
+// value of the sample position is minus the number of samples before
+// the loop point (unsigned 24 bits, slots 010010 and 010011). It is
+// incremented by the unsigned 7.15 step value for each sample, until
+// it reaches a positive value more or equal to the loop size
+// (unsigned 24 bits, slots 010100 and 010101). Then if looping is
+// enabled the position is decreased by the loop size and incremented
+// by the loop size adjust, otherwise the last sample value output is
+// held and a maximum speed envelope release is triggered.
+//
+// The loop size adjust is a 0.6 unsigned value (e.g. between 0 and
+// 0.984375).
+//
+// Looping is enabled when L=0 (slot 010010) and b=0 (slot 010110).
+//
+// The unsigned 7.15 step is computed from the pitch and the finetune
+// values. The base step value is 2**pitch with pitch encoded as a
+// signed 4.10 value, e.g. giving a result between 1/256 and almost
+// but not quite 256. The exponentiation table has 13 bits of
+// precision including the left 1 bit. The finetune is a signed value
+// between -64 and +63 that is added to the pitch once position 0 is
+// reached.
+//
+// Backwards sample reading negates the sample position value before
+// fetching. Note that backwards reading disable looping.
+//
+// Samples are read with sample pos 0 corresponding to the bottom
+// sample at the 25-bits address. It is important to note that four
+// consecutive sample values are required for the interpolation block,
+// and the hardware manages to provide the correct values even for
+// compressed samples with large steps, requiring to compute and
+// accumulate the deltas for all the bytes on the way. The memory
+// controller must be REALLY interesting.
+//
+// A pitch skip bigger than the loop size ends up with results
+// somewhere between weird and utterly insane. Don't do that.
+//
+//
+// Sample interpolation
+//
+// Samples go through an interpolator which uses two past samples and
+// two future samples to compute the final value for a non-integer
+// position, with a weight for each history sample. The weights are
+// computed from two polynoms:
+// f0(t) = (t - t**3)/6
+// f1(t) = t + (t**2 - t**3)/2
+//
+// The polynoms are used with the decimal part 'p' (as in phase) of
+// the sample position. The computation from the four samples s0..s3
+// is:
+// s = - s0 * f0(1-p) + s1 * f1(1-p) + s2 * f1(p) - s3 * f0(p)
+//
+// f0(0) = f0(1) = f1(0) = 0 and f1(1) = 1, so when phase is 0 (sample
+// streaming with no frequency shifting) the sample s1 is output.
+//
+// The implementation of the weights uses two tables with apparently
+// 2048 entries and 10 bits precision (e.g. between 0 and 0.999), but
+// are in reality 1023 entries. Each entry of the 1023-entries tables
+// goes to slots 2n-1 and 2n (n=1..1023), and slots 0 and 2047 are
+// hardcoded to both 0 for f0 and 0/1.0 for f1. That way the tables
+// can be used in both directions and the computation of 1-p consists
+// of inverting all the bits.
+//
+// The two-past sample for the first position, the first pointed at by
+// the streamer, is forced to zero.
+//
+// Post-interpolation, the output is a 16-bits signed value with no
+// decimals.
+
+
+// Dpcm delta expansion table
+const std::array<s16, 256> swp30_device::streaming_block::dpcm_expand = []() {
+ std::array<s16, 256> deltas;
+ static const s16 offset[4] = { 0, 0x20, 0x60, 0xe0 };
+ for(u32 i=0; i != 128; i++) {
+ u32 e = i >> 5;
+ s16 base = ((i & 0x1f) << e) + offset[e];
+ deltas[i] = base;
+ deltas[i+128] = -base;
+ }
+ deltas[0x80] = 0x88; // Not actually used by samples, but tested on hardware
+ return deltas;
+}();
+
+// Pitch conversion table, 2**(i/1024) as 1.12
+const std::array<u16, 0x400> swp30_device::streaming_block::pitch_base = []() {
+ std::array<u16, 0x400> base;
+ for(u32 i=0; i != 0x400; i++)
+ base[i] = pow(2, i/1024.0) * 4096;
+ return base;
+}();
+
+// Sample interpolation functions f0 and f1. The second half of f1 is adjusted so that the combination is 1.0 (e.g. 0x400)
+const std::array<std::array<s16, 0x800>, 2> swp30_device::streaming_block::interpolation_table = []() {
+ std::array<std::array<s16, 0x800>, 2> result;
+
+ // The exact way of doing the computations replicate the values
+ // actually used by the chip (which are very probably a rom, of
+ // course).
+
+ for(u32 i=1; i != 1024; i++) {
+ s16 f0 = (((i << 20) - i*i*i) / 6) >> 20;
+ result[0][2*i-1] = f0;
+ result[0][2*i ] = f0;
+ }
+ for(u32 i=1; i != 513; i++) {
+ s16 f1 = i + ((((i*i) << 10) - i*i*i) >> 21);
+ result[1][2*i-1] = f1;
+ result[1][2*i ] = f1;
+ }
+ for(u32 i=513; i != 1024; i++) {
+ u32 i1 = 2*i;
+ u32 i2 = 2047 ^ i1;
+ // When interpolating, f1 is added and f0 is subtracted, and the total must be 0x400
+ s16 f1 = 0x400 + result[0][i1] + result[0][i2] - result[1][i2];
+ result[1][2*i-1] = f1;
+ result[1][2*i ] = f1;
+ }
+ result[0][ 0] = 0x000;
+ result[0][0x7ff] = 0x000;
+ result[1][ 0] = 0x000;
+ result[1][0x7ff] = 0x400;
+ return result;
+}();
+
+const std::array<s32, 8> swp30_device::streaming_block::max_value = {
+ 0x7fff, 0x7ffe, 0x7ffc, 0x7ff8, 0x7ff0, 0x7fe0, 0x7fc0, 0x7f80
+};
-static int scount = 0;
-
-/*
- 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 outputs are
- 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 first input, and routes the third input and output
- to the plugin boards, but not the left/right input clock, arguing
- for mono.
-
-
- Registers:
-
- The chip interface presents 4096 16-bits registers in a 64x64 grid.
- They all seem to be read/write. 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.
-
- Names we'll use in the rest of the text:
- - reg(y, x) is the register at address 2*(y*0x40 + x)
- - ch<nn> is reg(channel, nn) for a given AWG2 channel
- - sy<nn> is reg(nn/2, 0xe + (nn % 2))
- - fp<nnn> is reg(nn/6, 0x21 + 2*(nn % 6))
- - of<nn> is reg(nn/2, 0x30 + (nn % 2))
- - lfo<nn> is reg(nn/2, 0x3e + (nn % 2)) for nn = 0..17
-
-
- AWM2:
-
- The AWM2 is in charge of handling the individual channels. It
- manages reading the rom, decoding the samples, applying volume and
- pitch envelopes and lfos and filtering the result. Each channel is
- then sent to the mixer for further processing.
-
- 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 semi-automatically, and the final
- panned result is sent to the mixer.
-
-
- ch00 fixed LPF frequency cutoff index
- ch01 fixed LPF frequency cutoff index increment?
- ch02 fixed HPF frequency cutoff
- ch03 40ff at startup, 5010 always afterwards?
- ch04 fixed LPF resonance level
- ch05 unknown
- ch06 attack, bit 14-8 = step, bit 7 = mode
- ch07 decay1, bit 14-8 = step, bit 7-0 = target attenuation (top 8 bits)
- ch08 decay2, bit 14-8 = step, bit 7-0 = target attenuation (top 8 bits)
- ch09 base volume bit 15 = activate decay2, bit 14-8 unknown, bit 7-0 = initial attenuation
+void swp30_device::streaming_block::clear()
+{
+ m_start = 0;
+ m_loop = 0;
+ m_address = 0;
+ m_pitch = 0;
+ m_loop_size = 0x400;
+ m_pos = 0;
+ m_pos_dec = 0;
+ m_dpcm_s0 = m_dpcm_s1 = m_dpcm_s2 = m_dpcm_s3 = 0;
+ m_dpcm_pos = 0;
+ m_dpcm_delta = 0;
+ m_first = false;
+ m_done = false;
+ m_last = 0;
+}
- ch0a-0d unknown, probably something to do with pitch eg
- ch10 unknown
- ch11 bit 15 = compressed 8-bits mode, 13-0 channel replay frequency, signed 3.10 fixed point,
- log2 scale, positive is higher resulting frequency.
+void swp30_device::streaming_block::keyon()
+{
+ m_pos = -(m_start & 0xffffff) - 1;
+ m_pos_dec = 0;
+ m_dpcm_s0 = m_dpcm_s1 = m_dpcm_s2 = m_dpcm_s3 = 0;
+ m_dpcm_pos = m_pos+1;
+ m_dpcm_delta = 0;
+ m_first = true;
+ m_finetune_active = false;
+ m_done = false;
+}
- ch12-13 bit 31 unknown, 30 unknown, 29-0 = number of samples before the loop point
- ch14-15 bit 31 = play sample backwards, 30-0 = number of samples in the loop
- ch16-17 bit 31-30 = sample format, 29-25 = loop samples decimal part, 24-0 = loop start address in rom
- ch20,22,24 first FIR coefficients
- ch26,28,2a second FIR coefficients
- ch2c-2f unknown
- ch32 pan left/right, 2x8 bits of attenuation
+void swp30_device::streaming_block::scale_and_clamp_one(s16 &val, u32 scale, s32 limit)
+{
+ s32 sval = val << scale;
+ if(sval < -0x8000)
+ sval = -0x8000;
+ else if(sval > limit)
+ sval = limit;
+ val = sval;
+}
+
+void swp30_device::streaming_block::scale_and_clamp(s16 &val0, s16 &val1, s16 &val2, s16 &val3)
+{
+ u32 scale = (m_address >> 27) & 7;
+ if(!scale)
+ return;
+ s32 limit = max_value[scale];
+ scale_and_clamp_one(val0, scale, limit);
+ scale_and_clamp_one(val1, scale, limit);
+ scale_and_clamp_one(val2, scale, limit);
+ scale_and_clamp_one(val3, scale, limit);
+}
+
+void swp30_device::streaming_block::read_16(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3)
+{
+ s32 spos = m_loop & 0x80000000 ? -m_pos : m_pos;
+ offs_t base_address = m_address & 0x1ffffff;
+ offs_t adr = base_address + (spos >> 1);
+ switch(spos & 1) {
+ case 0: {
+ // 32103210 32103210 32103210
+ // bbbbaaaa ddddcccc ........
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr);
+ val0 = l0;
+ val1 = l0 >> 16;
+ val2 = l1;
+ val3 = l1 >> 16;
+ break;
+ }
+ case 1: {
+ // 32103210 32103210 32103210
+ // aaaa.... ccccbbbb ....dddd
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ u32 l2 = wave.read_dword(adr+2);
+ val0 = l0 >> 16;
+ val1 = l1;
+ val2 = l1 >> 16;
+ val3 = l2;
+ break;
+ }
+ }
+ scale_and_clamp(val0, val1, val2, val3);
+}
+
+void swp30_device::streaming_block::read_12(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3)
+{
+ s32 spos = m_loop & 0x80000000 ? -m_pos : m_pos;
+ offs_t base_address = m_address & 0x1ffffff;
+ offs_t adr = base_address + (spos >> 3)*3;
+ switch(spos & 7) {
+ case 0: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // ccbbbaaa ....dddc ........ ........ ........
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ val0 = (l0 & 0x00000fff) << 4;
+ val1 = (l0 & 0x00fff000) >> 8;
+ val2 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
+ val3 = l1 & 0x0000fff0;
+ break;
+ }
+ case 1: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // bbaaa... .dddcccb ........ ........ ........
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ val0 = (l0 & 0x00fff000) >> 8;
+ val1 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
+ val2 = l1 & 0x0000fff0;
+ val3 = (l1 & 0x0fff0000) >> 12;
+ break;
+ }
+ case 2: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // aa...... dcccbbba ......dd ........ ........
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ u32 l2 = wave.read_dword(adr+2);
+ val0 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
+ val1 = l1 & 0x0000fff0;
+ val2 = (l1 & 0x0fff0000) >> 12;
+ val3 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
+ break;
+ }
+ case 3: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // ........ cbbbaaa. ...dddcc ........ ........
+ u32 l1 = wave.read_dword(adr+1);
+ u32 l2 = wave.read_dword(adr+2);
+ val0 = l1 & 0x0000fff0;
+ val1 = (l1 & 0x0fff0000) >> 12;
+ val2 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
+ val3 = (l2 & 0x000fff00) >> 4;
+ break;
+ }
+ case 4: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // ........ baaa.... dddcccbb ........ ........
+ u32 l1 = wave.read_dword(adr+1);
+ u32 l2 = wave.read_dword(adr+2);
+ val0 = (l1 & 0x0fff0000) >> 12;
+ val1 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
+ val2 = (l2 & 0x000fff00) >> 4;
+ val3 = (l2 & 0xfff00000) >> 16;
+ break;
+ }
+ case 5: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // ........ a....... cccbbbaa .....ddd ........
+ u32 l1 = wave.read_dword(adr+1);
+ u32 l2 = wave.read_dword(adr+2);
+ u32 l3 = wave.read_dword(adr+3);
+ val0 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
+ val1 = (l2 & 0x000fff00) >> 4;
+ val2 = (l2 & 0xfff00000) >> 16;
+ val3 = (l3 & 0x00000fff) << 4;
+ break;
+ }
+ case 6: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // ........ ........ bbbaaa.. ..dddccc ........
+ u32 l2 = wave.read_dword(adr+2);
+ u32 l3 = wave.read_dword(adr+3);
+ val0 = (l2 & 0x000fff00) >> 4;
+ val1 = (l2 & 0xfff00000) >> 16;
+ val2 = (l3 & 0x00000fff) << 4;
+ val3 = (l3 & 0x00fff000) >> 8;
+ break;
+ }
+ case 7: {
+ // 10210210 02102102 21021021 10210210 10210210
+ // ........ ........ aaa..... ddcccbbb .......d
+ u32 l2 = wave.read_dword(adr+2);
+ u32 l3 = wave.read_dword(adr+3);
+ u32 l4 = wave.read_dword(adr+4);
+ val0 = (l2 & 0xfff00000) >> 16;
+ val1 = (l3 & 0x00000fff) << 4;
+ val2 = (l3 & 0x00fff000) >> 8;
+ val3 = ((l3 & 0xff000000) >> 20) | ((l4 & 0x0000000f) << 12);
+ break;
+ }
+ }
+ scale_and_clamp(val0, val1, val2, val3);
+}
+
+void swp30_device::streaming_block::read_8(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3)
+{
+ s32 spos = m_loop & 0x80000000 ? -m_pos : m_pos;
+ offs_t base_address = m_address & 0x1ffffff;
+ offs_t adr = base_address + (spos >> 2);
+ switch(spos & 3) {
+ case 0: {
+ // 10101010 10101010
+ // ddccbbaa ........
+ u32 l0 = wave.read_dword(adr);
+ val0 = (l0 & 0x000000ff) << 8;
+ val1 = l0 & 0x0000ff00;
+ val2 = (l0 & 0x00ff0000) >> 8;
+ val3 = (l0 & 0xff000000) >> 16;
+ break;
+ }
+ case 1: {
+ // 10101010 10101010
+ // ccbbaa.. ......dd
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ val0 = l0 & 0x0000ff00;
+ val1 = (l0 & 0x00ff0000) >> 8;
+ val2 = (l0 & 0xff000000) >> 16;
+ val3 = (l1 & 0x000000ff) << 8;
+ break;
+ }
+ case 2: {
+ // 10101010 10101010
+ // bbaa.... ....ddcc
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ val0 = (l0 & 0x00ff0000) >> 8;
+ val1 = (l0 & 0xff000000) >> 16;
+ val2 = (l1 & 0x000000ff) << 8;
+ val3 = l1 & 0x0000ff00;
+ break;
+ }
+ case 3: {
+ // 10101010 10101010
+ // aa...... ..ddccbb
+ u32 l0 = wave.read_dword(adr);
+ u32 l1 = wave.read_dword(adr+1);
+ val0 = (l0 & 0xff000000) >> 16;
+ val1 = (l1 & 0x000000ff) << 8;
+ val2 = l1 & 0x0000ff00;
+ val3 = (l1 & 0x00ff0000) >> 8;
+ break;
+ }
+ }
+ scale_and_clamp(val0, val1, val2, val3);
+}
- sy02 internal register selector, msb = 0 or 6, lsb = channel
- sy03 internal register read port, used for envelope/keyoff management, 6 seems to be current volume
- sy0c-0f keyon mask
- sy10 write something to trigger a keyon according to the mask
+void swp30_device::streaming_block::dpcm_step(u8 input)
+{
+ u32 mode = (m_address >> 25) & 3;
+ u32 scale = (m_address >> 27) & 7;
+ s32 limit = max_value[scale];
+ m_dpcm_s0 = m_dpcm_s1;
+ m_dpcm_s1 = m_dpcm_s2;
+ m_dpcm_s2 = m_dpcm_s3;
- The current attenuation (before panning) is on 26 bits, in 4.22
- floating point format, of which only probably the top 8 are used for
- actual volume computations (see the Mixer part). The steps are in
- 4.3 floating-point format, e.g. the value converts to linear as:
+ s32 delta = m_dpcm_delta + dpcm_expand[input];
+ s32 sample = m_dpcm_s3 + (delta << scale);
- step = (8 + bit 2..0) << (bit 7..4)
+ if(sample < -0x8000) {
+ sample = -0x8000;
+ delta = 0;
+ } else if(sample > limit) {
+ sample = limit;
+ delta = 0;
+ }
+ m_dpcm_s3 = sample;
- giving a value between 8 and 0x78000. This value is added or
- substracted after each sample.
+ switch(mode) {
+ case 0: delta = delta * 7 / 8; break;
+ case 1: delta = delta * 3 / 4; break;
+ case 2: delta = delta / 2; break;
+ case 3: delta = 0; break;
+ }
+ m_dpcm_delta = delta;
+}
+
+void swp30_device::streaming_block::read_8c(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3)
+{
+ offs_t base_address = m_address & 0x1ffffff;
+ if(m_loop & 0x80000000) {
+ abort();
+ } else {
+ s32 spos = m_dpcm_pos;
+ base_address += spos >> 2;
+ u32 cv = wave.read_dword(base_address);
+ while(spos != m_pos + 4) {
+ u8 input = cv >> ((spos & 3) << 3);
+ dpcm_step(input);
+ spos++;
+ if((spos & 3) == 0) {
+ base_address ++;
+ cv = wave.read_dword(base_address);
+ }
+ }
+ m_dpcm_pos = spos;
+ }
+
+ val0 = m_dpcm_s0;
+ val1 = m_dpcm_s1;
+ val2 = m_dpcm_s2;
+ val3 = m_dpcm_s3;
+}
+std::pair<s16, bool> swp30_device::streaming_block::step(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s32 pitch_lfo)
+{
+ if(m_done)
+ return std::make_pair(m_last, false);
- MEG:
+ s16 val0, val1, val2, val3;
- The MEG is a DSP with 384 program steps connected to a 0x40000
- samples ram. Instructions are 64 bits wide, and to each instruction
- is associated a 2.14 fixed point value, Every third instruction (pc
- multiple of 3) can initiate a memory access to the reverb buffer
- which will be completed two instructions later. Each of those
- instructions is associated to a 16-bits address offset value.
-
- The DSP also sports 256 rotating registers (e.g. register 1 at run
- <n> becomes register 0 at run <n+1>) and 64 fixed registers. The
- fixed registers are used to store the results of reading the samples
- ram and also communicate with the mixer.
+ switch(m_address >> 30) {
+ case 0: read_16(wave, val0, val1, val2, val3); break;
+ case 1: read_12(wave, val0, val1, val2, val3); break;
+ case 2: read_8 (wave, val0, val1, val2, val3); break;
+ case 3: read_8c(wave, val0, val1, val2, val3); break;
+ }
+ if(m_first)
+ val0 = 0;
+
+ // Not perfectly exact, there are some rounding-like issues from
+ // time to time
+ s32 index = (m_pos_dec >> 4) & 2047;
+ s16 result = (
+ - interpolation_table[0][index ^ 2047] * val0
+ + interpolation_table[1][index ^ 2047] * val1
+ + interpolation_table[1][index ] * val2
+ - interpolation_table[0][index ] * val3
+ ) >> 10;
+
+ u32 pitch = m_pitch + pitch_lfo;
+ if(m_finetune_active) {
+ s32 ft = (m_loop >> 24) & 0x7f;
+ if(ft & 0x40)
+ ft -= 0x80;
+ pitch += ft;
+ if(pitch & 0x80000000)
+ pitch = 0;
+ if(pitch & 0x4000)
+ pitch = 0x3fff;
+ }
+ u32 e = ((pitch >> 10) + 8) & 15;
+ u32 m = pitch & 0x3ff;
+ u32 step = (pitch_base[m] << 10) >> (15-e);
+
+ m_pos_dec += step;
+ if(m_pos_dec >= 0x8000) {
+ m_first = false;
+ m_pos += m_pos_dec >> 15;
+ if(!m_finetune_active && m_pos >= 0)
+ m_finetune_active = true;
+
+ m_pos_dec &= 0x7fff;
+ if(m_pos >= m_loop_size) {
+ if(!((m_loop & 0x80000000) || (m_start & 0x40000000))) {
+ m_pos -= m_loop_size;
+ m_pos_dec += (m_start >> 15) & 0x7e00;
+ if(m_pos_dec >= 0x8000)
+ m_pos ++;
+ m_pos_dec &= 0x7fff;
+ m_dpcm_pos = 3;
+ } else {
+ m_done = true;
+ m_last = result;
+ return std::make_pair(m_last, true);
+ }
+ }
+ }
+ return std::make_pair(result, false);
+}
- Every 44100th of a second the 384 program steps are run once in
- order (no branches) to compute everything.
+void swp30_device::streaming_block::update_loop_size()
+{
+ m_loop_size = m_loop & 0x3ffffff;
+ if(!m_loop_size && !((m_loop & 0x80000000) || (m_start & 0x40000000)))
+ m_loop_size = 0x400;
+}
- 24 LFO registers are available. 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 }
+void swp30_device::streaming_block::start_h_w(u16 data)
+{
+ m_start = (m_start & 0x0000ffff) | (data << 16);
+ update_loop_size();
+}
- The 21th bit of the counter inverts bits 20-0 on read, those are
- interpreted as a 0-1 value, giving a sawtooth wave. When an
- instruction uses the lfo, which one is selected by using pc/16.
+void swp30_device::streaming_block::start_l_w(u16 data)
+{
+ m_start = (m_start & 0xffff0000) | data;
+}
- 8 mappings can be setup, which allow to manage rotating buffers in
- the samples ram easily by automating masking and offset adding. The
- register format is: pppppsss oooooooo. 'p' is the base pc/12 at
- which the map starts to be used. '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.
+void swp30_device::streaming_block::loop_h_w(u16 data)
+{
+ m_loop = (m_loop & 0x0000ffff) | (data << 16);
+ update_loop_size();
+}
+void swp30_device::streaming_block::loop_l_w(u16 data)
+{
+ m_loop = (m_loop & 0xffff0000) | data;
+ update_loop_size();
+}
- fp<nnn> fixed point 2.14 value associated with instruction nnn
- of<nn> 16-bits offset associated with instruction 3*nn
- lfo<nn> LFO registers
+void swp30_device::streaming_block::address_h_w(u16 data)
+{
+ m_address = (m_address & 0x0000ffff) | (data << 16);
+}
- sy21 MEG program write address
- sy22-25 MEG program opcode, msb-first, writing to 25 triggers an auto-increment
- sy30-3e even slots only, MEG buffer mappings
+void swp30_device::streaming_block::address_l_w(u16 data)
+{
+ m_address = (m_address & 0xffff0000) | data;
+}
+void swp30_device::streaming_block::pitch_w(u16 data)
+{
+ m_pitch = data;
+}
- Mixer:
+u16 swp30_device::streaming_block::start_h_r() const
+{
+ return m_start >> 16;
+}
- The mixer gets the outputs of the AWM2, the MEG (for the previous
- sample) and the external inputs, attenuates and sums them according
- to its mapping instructions, and pushes the results to the MEG, the
- DACs and the external outputs. The attenuations are 8-bits values
- in 4.4 floating point format (multiplies by (1-mant/2)*2**(-exp)).
- The routing is indicated through triplets of 16-bits values.
+u16 swp30_device::streaming_block::start_l_r() const
+{
+ return m_start;
+}
- ch33 dry (msb) and reverb (lsb) attenuation for an AWM2 channel
- ch34 chorus (msb) and variation (lsb) atternuation
- ch35-37 routing for an AWM2 channel
+u16 swp30_device::streaming_block::loop_h_r() const
+{
+ return m_loop >> 16;
+}
+u16 swp30_device::streaming_block::loop_l_r() const
+{
+ return m_loop;
+}
-*/
+u16 swp30_device::streaming_block::address_h_r() const
+{
+ return m_address >> 16;
+}
+u16 swp30_device::streaming_block::address_l_r() const
+{
+ return m_address;
+}
-DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip")
+u16 swp30_device::streaming_block::pitch_r() const
+{
+ return m_pitch;
+}
-bool swp30_device::istep(s32 &value, s32 limit, s32 step)
+std::string swp30_device::streaming_block::describe() const
{
- // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step);
- if(value < limit) {
- value += step;
- if(value >= limit) {
- value = limit;
- return true;
- }
- return false;
+ std::string desc;
+ desc = util::string_format("[%04x %08x %08x %08x] ", m_pitch, m_start, m_loop, m_address) + util::string_format("sample %06x-%06x @ %07x ", m_start & 0xffffff, m_loop & 0xffffff, m_address & 0x1ffffff);
+ switch(m_address >> 30) {
+ case 0: desc += "16"; break;
+ case 1: desc += "12"; break;
+ case 2: desc += "8 "; break;
+ case 3: desc += util::string_format("c%x", (m_address >> 25) & 3); break;
}
+ if(m_address & 0x38000000)
+ desc += util::string_format(" scale %x", (m_address >> 27) & 7);
+ if(m_loop & 0x80000000)
+ desc += " back";
+ else if(m_start & 0x40000000)
+ desc += " fwd ";
+ else
+ desc += " loop";
+ if(m_start & 0x3f000000)
+ desc += util::string_format(" loop-adjust %02x", (m_start >> 24) & 0x3f);
+ if(m_loop & 0x7f000000) {
+ if(m_loop & 0x40000000)
+ desc += util::string_format(" loop-tune -%02x", 0x40 - ((m_loop >> 24) & 0x3f));
+ else
+ desc += util::string_format(" loop-tune +%02x", (m_loop >> 24) & 0x3f);
+ }
+ if(m_pitch & 0x2000) {
+ u32 p = 0x4000 - (m_pitch & 0x3fff);
+ desc += util::string_format(" pitch -%x.%03x", p >> 10, p & 0x3ff);
+ } else if(m_pitch & 0x3fff)
+ desc += util::string_format(" pitch +%x.%03x", (m_pitch >> 10) & 7, m_pitch & 0x3ff);
+
+ return desc;
+}
+
+
+//--------------------------------------------------------------------------------
+
+// Special filters block
+
+// cccccc 000000 mmmm .aaa aaaa aaaa Filter 1 mode and main parameter
+// cccccc 000001 .xxx xxxx uuuu uuuu Bypass/dry level
+// cccccc 000010 mmmm .aaa aaaa aaaa Filter 2 mode and main parameter
+// cccccc 000011 .... .... vvvv vvvv Post-filter level
+// cccccc 000100 bbbb b... .... .... Filters second parameter
+//
+// This block takes samples from the streaming block and applies two
+// recursive filters to them. The type of filter and its coefficient
+// encoding depends on the 4-bits mode. Filter 1 has encoded
+// parameters a and b, filter 2 has c and d. First parameter is 11
+// bits and the second 5. A first paramter of 0 disables the
+// associated filter.
+//
+//
+//
+// Volumes
+//
+// Three attenuations are used, in 4.4 format.
+//
+// Attenuation u in slot 000001 is the bypass/dry level, adding an
+// attenuated version of the input directly the output.
+//
+// Attenuation v in slot 000011 is the filter 1 level, attenuating its
+// output before summing to the block output.
+//
+// Filter types
+// 0: Chamberlin configuration low pass filter with fixed q=1
+//
+// a is fp 3.8
+// k = ((0x101 + a.m) << a.e) / 65536
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = L'
+//
+// 1: Chamberlin configuration low pass filter
+//
+// a is fp 3.8, (b+4) is fp 3.3
+// k = ((0x101 + a.m) << a.e) / 65536
+// q = ((0x10 - (b+4).m) << (4 - (b+4).e)) / 128
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - q*B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = L'
+//
+// 2: order-1 lowpass IIR
+//
+// a is fp 3.8, b is unused
+//
+// a0 = ((0x101 + a.m) << a.e) / 65536
+// b1 = 1-a0
+// y0 = x0 * a0 + y1 * b1 = y1 + (x0 - y1) * a0
+//
+// 3: order-2 lowpass IIR
+//
+// a is fp3.8, (b+4) is fp 2.3
+//
+// dt = ((0x10 - (b+4).m) << (4 - (b+4).e)) / 128
+//
+// a0 = ((0x101 + a.m) << a.e) / 65536
+// b1 = (2 - dt - a0)
+// b2 = dt - 1
+//
+// y0 = x0 * a0 + y1 * (2 - dt - a0) + y2 * (dt - 1)
+// = (x0 - y1) * a0 + (y2 - y1) * dt + 2*y1 - y2
+//
+// 4: Chamberlin configuration band pass filter with fixed q=1
+//
+// a is fp 3.8
+// k = ((0x101 + a.m) << a.e) / 65536
+//
+// B(0) = L(0) = 0
+// B' = B + k * (x0 - L - B)
+// L' = L + k * B'
+// y0 = B'
+//
+// 5: Chamberlin configuration band pass filter
+//
+// a is fp 3.8, (b+4) is fp 3.3
+// k = ((0x101 + a.m) << a.e) / 65536
+// q = ((0x10 - (b+4).m) << (4 - (b+4).e)) / 128
+//
+// B(0) = L(0) = 0
+// B' = B + k * (x0 - L - q*B)
+// L' = L + k * B'
+// y0 = B'
+//
+// 6: order-1 ?pass IIR
+//
+// a is fp 3.8, b is unused
+//
+// a0 = ((0x101 + a.m) << a.e) / 65536
+// b1 = 1-a0
+// y0 = x0 - x1 + y1 * b1 = x0 - x1 + y1 - y1 * a0
+//
+// 8: Chamberlin configuration high pass filter with fixed q=1
+//
+// a is fp 3.8
+// k = ((0x101 + a.m) << a.e) / 65536
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = B'
+//
+// 9: Chamberlin configuration high pass filter
+//
+// a is fp 3.8, (b+4) is fp 3.3
+// k = ((0x101 + a.m) << a.e) / 65536
+// q = ((0x10 - (b+4).m) << (4 - (b+4).e)) / 128
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - q*B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = H'
+//
+// c: Chamberlin configuration notch filter with fixed q=1
+//
+// a is fp 3.8
+// k = ((0x101 + a.m) << a.e) / 65536
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = H' + L
+//
+// d: Chamberlin configuration notch filter
+//
+// a is fp 3.8, (b+4) is fp 3.3
+// k = ((0x101 + a.m) << a.e) / 65536
+// q = ((0x10 - (b+4).m) << (4 - (b+4).e)) / 128
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - q*B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = H' + L
+//
+
+
+void swp30_device::filter_block::clear()
+{
+ m_filter_1_a = 0;
+ m_level_1 = 0;
+ m_filter_2_a = 0;
+ m_level_2 = 0;
+ m_filter_b = 0;
+
+ m_filter_1_p1 = 0;
+ m_filter_2_p1 = 0;
+ m_filter_p2 = 0;
+
+ m_filter_1_y0 = 0;
+ m_filter_1_y1 = 0;
+ m_filter_1_x1 = 0;
+ m_filter_1_x2 = 0;
+
+ m_filter_1_h = 0;
+ m_filter_1_b = 0;
+ m_filter_1_l = 0;
+ m_filter_1_n = 0;
+
+ m_filter_2_y0 = 0;
+ m_filter_2_y1 = 0;
+ m_filter_2_x1 = 0;
+ m_filter_2_x2 = 0;
+
+ m_filter_2_h = 0;
+ m_filter_2_b = 0;
+ m_filter_2_l = 0;
+ m_filter_2_n = 0;
+}
+
+void swp30_device::filter_block::keyon()
+{
+ m_filter_1_y0 = 0;
+ m_filter_1_y1 = 0;
+ m_filter_1_x1 = 0;
+ m_filter_1_x2 = 0;
+
+ m_filter_1_h = 0;
+ m_filter_1_b = 0;
+ m_filter_1_l = 0;
+ m_filter_1_n = 0;
+
+ m_filter_2_y0 = 0;
+ m_filter_2_y1 = 0;
+ m_filter_2_x1 = 0;
+ m_filter_2_x2 = 0;
+
+ m_filter_2_h = 0;
+ m_filter_2_b = 0;
+ m_filter_2_l = 0;
+ m_filter_2_n = 0;
+}
+
+s32 swp30_device::filter_block::step(s16 input)
+{
+ s32 y0 = 0;
+ if(m_filter_1_a & 0x7fff) {
+ if(!BIT(m_filter_1_a, 13)) {
+ m_filter_1_h = (input << 6) - m_filter_1_l - ((s64(m_filter_p2) * m_filter_1_b) >> 7);
+ m_filter_1_b = m_filter_1_b + ((s64(m_filter_1_p1) * m_filter_1_h) >> 16);
+ m_filter_1_n = m_filter_1_h + m_filter_1_l;
+ m_filter_1_l = m_filter_1_l + ((s64(m_filter_1_p1) * m_filter_1_b) >> 16);
+
+ switch(m_filter_1_a >> 14) {
+ case 0x0: y0 = m_filter_1_l; break;
+ case 0x1: y0 = m_filter_1_b; break;
+ case 0x2: y0 = m_filter_1_h; break;
+ case 0x3: y0 = m_filter_1_n; break;
+ }
+ } else {
+ switch(m_filter_1_a >> 12) {
+ case 0x2: y0 = m_filter_1_y0 + ((s64(m_filter_1_p1) * ((input << 6) - m_filter_1_y0)) >> 16); break;
+ case 0x3: y0 = 2*m_filter_1_y0 - m_filter_1_y1 + ((s64(m_filter_1_p1) * ((input << 6) - m_filter_1_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_1_y1 - m_filter_1_y0)) >> 7); break;
+ case 0x6: y0 = ((input - m_filter_1_x1) << 6) + m_filter_1_y0 + ((s64(m_filter_1_p1) * (0 - m_filter_1_y0)) >> 16); break;
+ case 0x7: y0 = ((input - m_filter_1_x1) << 6) + 2*m_filter_1_y0 - m_filter_1_y1 + ((s64(m_filter_1_p1) * (0 - m_filter_1_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_1_y1 - m_filter_1_y0)) >> 7); break;
+ case 0xa: y0 = ((input - 2*m_filter_1_x1 + m_filter_1_x2) << 6) + m_filter_1_y0 + ((s64(m_filter_1_p1) * (0 - m_filter_1_y0)) >> 16); break;
+ case 0xb: y0 = ((input - 2*m_filter_1_x1 + m_filter_1_x2) << 6) + 2*m_filter_1_y0 - m_filter_1_y1 + ((s64(m_filter_1_p1) * (0 - m_filter_1_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_1_y1 - m_filter_1_y0)) >> 7); break;
+ case 0xe: y0 = ((input - 2*m_filter_1_x1 + m_filter_1_x2) << 6) + m_filter_1_y0 + ((s64(m_filter_1_p1) * ((m_filter_1_x1 << 6) - m_filter_1_y0)) >> 16); break;
+ case 0xf: y0 = ((input - 2*m_filter_1_x1 + m_filter_1_x2) << 6) + 2*m_filter_1_y0 - m_filter_1_y1 + ((s64(m_filter_1_p1) * ((m_filter_1_x1 << 6) - m_filter_1_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_1_y1 - m_filter_1_y0)) >> 7); break;
+ }
+
+ m_filter_1_x2 = m_filter_1_x1;
+ m_filter_1_x1 = input;
+ m_filter_1_y1 = m_filter_1_y0;
+ m_filter_1_y0 = y0;
+ }
- if(value > limit) {
- value -= step;
- if(value <= limit) {
- value = limit;
- return true;
+ if(m_filter_2_a & 0x7fff) {
+ if(!BIT(m_filter_2_a, 13)) {
+ m_filter_2_h = y0 - m_filter_2_l - ((s64(m_filter_p2) * m_filter_2_b) >> 7);
+ m_filter_2_b = m_filter_2_b + ((s64(m_filter_2_p1) * m_filter_2_h) >> 16);
+ m_filter_2_n = m_filter_2_h + m_filter_2_l;
+ m_filter_2_l = m_filter_2_l + ((s64(m_filter_2_p1) * m_filter_2_b) >> 16);
+
+ switch(m_filter_2_a >> 14) {
+ case 0x0: y0 = m_filter_2_l; break;
+ case 0x1: y0 = m_filter_2_b; break;
+ case 0x2: y0 = m_filter_2_h; break;
+ case 0x3: y0 = m_filter_2_n; break;
+ }
+ } else {
+ s32 y0_1 = y0;
+ switch(m_filter_2_a >> 12) {
+ case 0x2: y0 = m_filter_2_y0 + ((s64(m_filter_2_p1) * (y0 - m_filter_2_y0)) >> 16); break;
+ case 0x3: y0 = 2*m_filter_2_y0 - m_filter_2_y1 + ((s64(m_filter_2_p1) * (y0 - m_filter_2_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_2_y1 - m_filter_2_y0)) >> 7); break;
+ case 0x6: y0 = (y0 - m_filter_2_x1) + m_filter_2_y0 + ((s64(m_filter_2_p1) * (0 - m_filter_2_y0)) >> 16); break;
+ case 0x7: y0 = (y0 - m_filter_2_x1) + 2*m_filter_2_y0 - m_filter_2_y1 + ((s64(m_filter_2_p1) * (0 - m_filter_2_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_2_y1 - m_filter_2_y0)) >> 7); break;
+ case 0xa: y0 = (y0 - 2*m_filter_2_x1 + m_filter_2_x2) + m_filter_2_y0 + ((s64(m_filter_2_p1) * (0 - m_filter_2_y0)) >> 16); break;
+ case 0xb: y0 = (y0 - 2*m_filter_2_x1 + m_filter_2_x2) + 2*m_filter_2_y0 - m_filter_2_y1 + ((s64(m_filter_2_p1) * (0 - m_filter_2_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_2_y1 - m_filter_2_y0)) >> 7); break;
+ case 0xe: y0 = (y0 - 2*m_filter_2_x1 + m_filter_2_x2) + m_filter_2_y0 + ((s64(m_filter_2_p1) * ((m_filter_2_x1 << 6) - m_filter_2_y0)) >> 16); break;
+ case 0xf: y0 = (y0 - 2*m_filter_2_x1 + m_filter_2_x2) + 2*m_filter_2_y0 - m_filter_2_y1 + ((s64(m_filter_2_p1) * ((m_filter_2_x1 << 6) - m_filter_2_y0)) >> 16) + ((s64(m_filter_p2) * (m_filter_2_y1 - m_filter_2_y0)) >> 7); break;
+ }
+
+ m_filter_2_x2 = m_filter_2_x1;
+ m_filter_2_x1 = y0_1;
+ m_filter_2_y1 = m_filter_2_y0;
+ m_filter_2_y0 = y0;
+ }
}
- return false;
}
- return true;
+ s32 result = volume_apply(m_level_1, input << 6) + volume_apply(m_level_2, y0);
+ if(result < -0x400000)
+ result = -0x400000;
+ else if(result > 0x3fffff)
+ result = 0x3fffff;
+ return result;
+}
+
+u16 swp30_device::filter_block::filter_1_a_r() const
+{
+ return m_filter_1_a;
+}
+
+u16 swp30_device::filter_block::level_1_r() const
+{
+ return m_level_1;
+}
+
+u16 swp30_device::filter_block::filter_2_a_r() const
+{
+ return m_filter_2_a;
+}
+
+u16 swp30_device::filter_block::level_2_r() const
+{
+ return m_level_2;
+}
+
+u16 swp30_device::filter_block::filter_b_r() const
+{
+ return m_filter_b;
+}
+
+void swp30_device::filter_block::filter_1_a_w(u16 data)
+{
+ m_filter_1_a = data;
+ m_filter_1_p1 = (0x101 + (m_filter_1_a & 0xff)) << ((m_filter_1_a >> 8) & 7);
}
-s32 swp30_device::fpadd(s32 value, s32 step)
+void swp30_device::filter_block::level_1_w(u16 data)
{
- s32 e = value >> 24;
- s32 m = value & 0xffffff;
+ m_level_1 = data;
+}
- m += step << e;
- if(m & 0xfe000000)
- return 0xfffffff;
+void swp30_device::filter_block::filter_2_a_w(u16 data)
+{
+ m_filter_2_a = data;
+ m_filter_2_p1 = (0x101 + (m_filter_2_a & 0xff)) << ((m_filter_2_a >> 8) & 7);
+}
- while(m & 0x01000000) {
- m <<= 1;
- e ++;
+void swp30_device::filter_block::level_2_w(u16 data)
+{
+ m_level_2 = data;
+}
+
+void swp30_device::filter_block::filter_b_w(u16 data)
+{
+ m_filter_b = data;
+ if(!BIT(m_filter_1_a, 12))
+ m_filter_p2 = 0x80;
+
+ else {
+ u32 p2 = (m_filter_b >> 11) + 4;
+ m_filter_p2 = (0x10 - (p2 & 7)) << (4 - (p2 >> 3));
}
- if(e >= 16)
- return 0xfffffff;
- return (e << 24) | (m & 0xffffff);
}
-s32 swp30_device::fpsub(s32 value, s32 step)
+
+s32 swp30_device::filter_block::volume_apply(u8 level, s32 sample)
{
- s32 e = value >> 24;
- s32 m = (value & 0xffffff) | 0xfe000000;
- m = e < 0xc ? m - (step << e) : (m >> (e - 0xb)) - (step << 0xb);
- if(m >= 0)
+ // Level is 4.4 floating point positive, and represents an attenuation
+ // Sample is 16.6 signed and the result is in the same format
+
+ // ff seems to be hardcoded to 0 output
+ if(level == 0xff)
return 0;
- if(e >= 0xc)
- e = 0xb;
- while(m < 0xfe000000) {
- if(!e)
- return 0;
- e --;
- m >>= 1;
+
+ s32 e = level >> 4;
+ s32 m = level & 0xf;
+ return ((sample << 5) - (sample * m)) >> (e+5);
+}
+
+
+//--------------------------------------------------------------------------------
+
+// IIR1 filters block
+//
+// cccccc 100000 IIR1 a1
+// cccccc 100010 IIR1 b1
+// cccccc 100100 IIR1 a0
+// cccccc 100110 IIR2 b1
+// cccccc 101000 IIR2 a1
+// cccccc 101010 IIR2 a0
+//
+// This block takes samples from the filter block and applies two 3-point
+// FIR filters. The filter constants are encoded in signed 3.13
+// format.
+//
+// Given two consecutive inputs x0, x1 (x1 being the oldest) and the
+// previous output y1 a IIR1 filter computes the output y0 as:
+//
+// y0 = a0 * x0 + a1 * x1 + b1 * y1
+//
+// It gets 16.6 and outputs 17.6 saturated values.
+
+void swp30_device::iir1_block::clear()
+{
+ m_a[0][0] = 0;
+ m_a[0][1] = 0;
+ m_b[0] = 0;
+ m_a[1][0] = 0;
+ m_a[1][1] = 0;
+ m_b[1] = 0;
+
+ m_hx[0] = 0;
+ m_hy[0] = 0;
+ m_hx[1] = 0;
+ m_hy[1] = 0;
+}
+
+void swp30_device::iir1_block::keyon()
+{
+ m_hx[0] = 0;
+ m_hy[0] = 0;
+ m_hx[1] = 0;
+ m_hy[1] = 0;
+}
+
+s32 swp30_device::iir1_block::step(s32 input)
+{
+ s32 ya = std::clamp(s32((s64(m_a[0][0]) * input + s64(m_a[0][1]) * m_hx[0] + s64(m_b[0]) * m_hy[0]) >> 13), -0x800000, 0x7fffff);
+ s32 yb = std::clamp(s32((s64(m_a[1][0]) * ya + s64(m_a[1][1]) * m_hx[1] + s64(m_b[1]) * m_hy[1]) >> 13), -0x800000, 0x7fffff);
+
+ m_hx[0] = input;
+ m_hy[0] = ya;
+
+ m_hx[1] = ya;
+ m_hy[1] = yb;
+
+ return yb;
+}
+
+template<u32 filter> u16 swp30_device::iir1_block::a0_r() const
+{
+ return m_a[filter][0];
+}
+
+template<u32 filter> u16 swp30_device::iir1_block::a1_r() const
+{
+ return m_a[filter][1];
+}
+
+template<u32 filter> u16 swp30_device::iir1_block::b1_r() const
+{
+ return m_b[filter];
+}
+
+template<u32 filter> void swp30_device::iir1_block::a0_w(u16 data)
+{
+ m_a[filter][0] = data;
+}
+
+template<u32 filter> void swp30_device::iir1_block::a1_w(u16 data)
+{
+ m_a[filter][1] = data;
+}
+
+template<u32 filter> void swp30_device::iir1_block::b1_w(u16 data)
+{
+ m_b[filter] = data;
+}
+
+
+//--------------------------------------------------------------------------------
+
+// Envelope block
+//
+//
+// cccccc 000110 ssss ssss iiii iiii Attack speed and start volume
+// cccccc 000111 ssss ssss tttt tttt Decay 1 speed and target
+// cccccc 001000 ssss ssss tttt tttt Decay 2 speed and target
+// cccccc 001001 ssss ssss gggg gggg Release speed & global volume
+//
+// The envelope block manages the final volume of an awm2 voice and
+// allows to automatically run it through four steps:
+// - Attack, climbing up from a programmed value to global volume with a slowing down curve
+// - Decay 1, going down to a target volume in a linear fashion
+// - Decay 2, going up or down to another target in a linear fashion
+// - Release, going down to silence in a linear fashion
+//
+// The global volume, though, is taken into account by adding it to
+// the raw envelope volume. Hence attack actually targets 0, and the
+// levels reached by the decays are the given target plus the global
+// volume.
+//
+// The volume itself is a 14-bit 4.10 attenuation which is manipulated
+// as a single number by this block. An idle voice has stage release
+// and volume 3fff. Start volume, targets and global volume are 4.4,
+// hence just zero-extended on the right.
+//
+// Volume modification uses a concept of speed. At each sample a
+// value is added depending on the speed and a cycle:
+// - Speed 78+: value is always 7f
+//
+// - Speed 70..77: value alternates between 3f and 7f on a 8-samples
+// cycle with seven 7f on speed 77, six on 76, etc up
+// to only 3f on 70.
+//
+// - Speed 48..6f: same cycles with changing pair for every 8 speeds,
+// going 1f/3f then f/1f all the way down to 1/3.
+//
+// - Speed 40..47: 16-samples cycles alternating 0 and 1, where on
+// every two-sample block there is a 1 and then either a
+// 0 or a 1. Speed 47 has one 0, 46 has two, all the way
+// down to 40 which is 50% 0.
+//
+// - Speed 38..3f: cycles of size 2*16, where in each block of 2 cycles there
+// may be one 1 depending on the cycle. Goes from
+// fifteen 1 (out of thirty-two) on speed 3f to eight ones on
+// speed 38.
+//
+// - Speed 00..37: same cycles with bigger blocks, going size 4 for 30..37 up
+// to size 32 for 00..07.
+//
+// Phase on the cycles seems unpredictable.
+//
+// Decay 1, Decay 2 and Release use directly the speed as programmed
+// (with Release inverting bit 7). Speeds 80+ gives someone weird
+// results, with steps still of 7f but sometimes acting on the target
+// level. Attack uses the given speed but adds to it bits 9..13 of
+// the volume multiplied by 4, giving a fast curve at the start which
+// decelerates when approaching maximum volume.
+//
+// Sequencing is automatic. If release speed is non-zero at keyon
+// then the chip will ride the attack from is start value to 0, then
+// go to the two decay values then all the way to 3fff on release. If
+// release speed is zero, it will hold still when reaching the end of
+// decay 2. At any time if a non-zero value is written to release
+// speed and the envelope is not yet in the release stage then the
+// chip switches to release.
+//
+// A 16-bits readonly register gives the main cpu the current stage
+// and raw envelope volume (without the global volume added), with
+// stages numbered 0 to 3 in the two top bits and the volume in the
+// bottom 14.
+
+void swp30_device::envelope_block::clear()
+{
+ m_attack = 0;
+ m_decay1 = 0;
+ m_decay2 = 0;
+ m_release_glo = 0;
+ m_envelope_level = 0x3fff;
+ m_envelope_mode = RELEASE;
+}
+
+void swp30_device::envelope_block::keyon()
+{
+ m_envelope_level = (m_attack & 0xff) << 6;
+ if((m_attack & 0xff) == 0)
+ m_envelope_level = 0x80 << 6;
+ m_envelope_mode = ATTACK;
+}
+
+u16 swp30_device::envelope_block::status() const
+{
+ return (m_envelope_mode << 14) | m_envelope_level;
+}
+
+bool swp30_device::envelope_block::active() const
+{
+ return m_envelope_level != 0x3fff || m_envelope_mode != RELEASE;
+}
+
+u16 swp30_device::envelope_block::level_step(u32 level, u32 sample_counter)
+{
+ // Phase is incorrect, and very weird
+
+ if(level >= 0x78)
+ return 0x7f;
+
+ u32 k0 = level >> 3;
+ u32 k1 = level & 7;
+
+ if(level >= 0x48) {
+ k0 -= 9;
+ u32 a = (4 << k0) - 1;
+ u32 b = (2 << k0) - 1;
+ static const u8 mx[8] = { 0x00, 0x20, 0x44, 0xa2, 0x55, 0x75, 0xee, 0xfe };
+ return ((mx[k1] >> (sample_counter & 7)) & 1) ? a : b;
}
- while(e != 0xf && (m >= 0xff000000)) {
- e ++;
- m <<= 1;
+
+ if(level >= 0x40) {
+ if(sample_counter & 1)
+ return 1;
+ u32 s1 = (sample_counter & 0xe) >> 1;
+ static const u8 mx[8] = { 0x00, 0x01, 0x22, 0xa8, 0x55, 0xab, 0x77, 0xfd };
+ return (mx[k1] >> s1) & 1;
}
- return (e << 24) | (m & 0xffffff);
+ k0 = 8 - k0;
+
+ if(sample_counter & util::make_bitmask<u32>(k0))
+ return 0;
+
+ static const u16 mx[8] = { 0x5555, 0x5557, 0x5757, 0x5777, 0x7777, 0x777f, 0x7f7f, 0x7fff };
+ return (mx[k1] >> ((sample_counter >> k0) & 0xf)) & 1;
}
-bool swp30_device::fpstep(s32 &value, s32 limit, s32 step)
+u16 swp30_device::envelope_block::step(u32 sample_counter)
{
- // value, limit and step are 4.24 but step has its exponent and
- // top four bits zero
+ u16 result = m_envelope_level + ((m_release_glo & 0xff) << 6);
+ switch(m_envelope_mode) {
+ case ATTACK: {
+ s32 level = m_envelope_level - level_step((m_attack >> 8) + ((m_envelope_level >> 9) << 2), sample_counter);
+ if(level <= 0) {
+ level = 0;
+ m_envelope_mode = DECAY1;
+ }
+ m_envelope_level = level;
+ if((m_attack & 0xff) == 0)
+ result = (m_release_glo & 0xff) << 6;
+ break;
+ }
- if(value == limit)
- return true;
- if(value < limit) {
- value = fpadd(value, step);
- if(value >= limit) {
- value = limit;
- return true;
+ case DECAY1: case DECAY2: {
+ u16 reg = m_envelope_mode == DECAY1 ? m_decay1 : m_decay2;
+ s32 limit = (reg & 0xff) << 6;
+ s32 level = m_envelope_level;
+ if(level < limit) {
+ level += level_step(reg >> 8, sample_counter);
+ if(level > limit)
+ level = limit;
+ } else if(level> limit) {
+ level -= level_step(reg >> 8, sample_counter);
+ if(level < limit)
+ level = limit;
+ }
+ m_envelope_level = level;
+ if(level == limit) {
+ if(m_envelope_mode == DECAY1)
+ m_envelope_mode = DECAY2;
+
+ else if(m_release_glo & 0xff00)
+ m_envelope_mode = RELEASE;
}
- return false;
+ break;
}
+
+ case RELEASE: {
+ s32 level = m_envelope_level + level_step((m_release_glo >> 8) ^ 0x80, sample_counter);
+ if(level > 0x3fff)
+ level = 0x3fff;
+ m_envelope_level = level;
+ break;
+ }
+ }
+ static FILE *fd = nullptr;
+ if(!fd)
+ fd = fopen("/people/galibert/mame/mu100/env-mu100.txt", "w");
+ fprintf(fd, "%d\n", result);
+ return result;
+}
+
+u16 swp30_device::envelope_block::attack_r() const
+{
+ return m_attack;
+}
+
+void swp30_device::envelope_block::attack_w(u16 data)
+{
+ m_attack = data;
+}
+
+u16 swp30_device::envelope_block::decay1_r() const
+{
+ return m_decay1;
+}
+
+void swp30_device::envelope_block::decay1_w(u16 data)
+{
+ m_decay1 = data;
+}
+
+u16 swp30_device::envelope_block::decay2_r() const
+{
+ return m_decay2;
+}
+
+void swp30_device::envelope_block::decay2_w(u16 data)
+{
+ m_decay2 = data;
+}
- value = fpsub(value, step);
- if(value <= limit) {
- value = limit;
- return true;
+u16 swp30_device::envelope_block::release_glo_r() const
+{
+ return m_release_glo;
+}
+
+void swp30_device::envelope_block::release_glo_w(u16 data)
+{
+ m_release_glo = data;
+ if(data & 0xff00)
+ m_envelope_mode = RELEASE;
+}
+
+void swp30_device::envelope_block::trigger_release()
+{
+ m_release_glo |= 0xff00;
+ m_envelope_mode = RELEASE;
+}
+
+
+//--------------------------------------------------------------------------------
+
+// LFO block
+
+// cccccc 000101 .... .... .aaa aaaa LFO amplitude depth
+// cccccc 001010 tt.s ssss mppp pppp LFO type, step, pitch mode, pitch depth
+
+// The LFO is a slow oscillator with a period between 0.2 and 6
+// seconds (0.17 to 5.2Hz). It starts with a 18-bits counter which is
+// initialized to a random value on keyon. At each sample the value
+// of step is added to the counter. When sep is zero, the counter is
+// frozen. In addition, somewhere in every 0x4000 block at a somewhat
+// unpredictable time, the counter jumps by an extra 0x40. The final
+// period thus ends up being 0x3fc00/step cycles, or between 8423 and
+// 261120 cycles.
+
+// From the 18-bits counter a 12-bit state value is created. How
+// depends on the type:
+
+// Type 0 (saw); state is bits 6-17 of the counter
+
+// Type 1 (triangle): state is bits 6-16 of the counter followed by a
+// zero if bit 17 = 0, inverted bits 6-16 followed by a zero if
+// bit 17 = 1
+
+// Type 2 (rectangle): state is 0 if bit 17 = 0, fff if bit 17 = 1
+
+// Type 3 (sample&hold): state is random, changes of value when bits
+// 9-17 change
+
+// Amplitude LFO. The current value of the state is multiplied by
+// the amplitude depth (zero hence makes it disabled) and divided by
+// 0x20, giving a 14-bit, 4.10 attenuation (same format as for the
+// envelope).
+
+// Pitch LFO. The value of the state minus 0x400 is multiplied by
+// the pitch depth. It is then shifted by 8 in coarse mode (m=1) and
+// 11 in fine mode (m=0). The resulting signed value is added to the
+// pitch used by the streaming block.
+
+
+void swp30_device::lfo_block::clear()
+{
+ m_counter = 0;
+ m_state = 0;
+ m_type = 0;
+ m_step = 0;
+ m_amplitude = 0;
+ m_pitch_mode = false;
+ m_pitch_depth = 0;
+ m_r_type_step_pitch = 0;
+ m_r_amplitude = 0;
+}
+
+void swp30_device::lfo_block::keyon(running_machine &machine)
+{
+ m_counter = machine.rand() & 0x3ffff;
+ switch(m_type) {
+ case 0: m_state = m_counter >> 6; break;
+ case 1: m_state = m_counter & 0x20000 ? (~m_counter >> 5) & 0xffe : (m_counter >> 5) & 0xffe; break;
+ case 2: m_state = m_counter & 0x20000 ? 0xfff : 0; break;
+ case 3: m_state = machine.rand() & 0xfff; break;
}
- return false;
}
-// sample is signed 24.8
-s32 swp30_device::fpapply(s32 value, s32 sample)
+void swp30_device::lfo_block::step(running_machine &machine)
+{
+ u32 pc = m_counter;
+ m_counter = (m_counter + m_step) & 0x3ffff;
+ if((m_counter & 0x03fc0) == 0x02000)
+ m_counter += 0x40;
+ switch(m_type) {
+ case 0: m_state = m_counter >> 6; break;
+ case 1: m_state = m_counter & 0x20000 ? (~m_counter >> 5) & 0xffe : (m_counter >> 5) & 0xffe; break;
+ case 2: m_state = m_counter & 0x20000 ? 0xfff : 0; break;
+ case 3: if((pc ^ m_counter) & 0x3fe00) m_state = machine.rand() & 0xfff; break;
+ }
+}
+
+u16 swp30_device::lfo_block::get_amplitude() const
+{
+ return (m_state * m_amplitude) >> 5;
+}
+
+s16 swp30_device::lfo_block::get_pitch() const
+{
+ s32 v = (m_state - 0x400) * m_pitch_depth;
+ if(m_pitch_mode)
+ return v >> 8;
+ else
+ return v >> 11;
+
+}
+
+void swp30_device::lfo_block::type_step_pitch_w(u16 data)
+{
+ m_r_type_step_pitch = data;
+ m_type = data >> 14;
+ m_step = (data >> 8) & 0x1f;
+ m_pitch_mode = data & 0x80;
+ m_pitch_depth = data & 0x7f;
+}
+
+void swp30_device::lfo_block::amplitude_w(u16 data)
+{
+ m_r_amplitude = data;
+ m_amplitude = data & 0x7f;
+}
+
+u16 swp30_device::lfo_block::type_step_pitch_r()
{
- if(value >= 0x10000000)
+ return m_r_type_step_pitch;
+}
+
+u16 swp30_device::lfo_block::amplitude_r()
+{
+ return m_r_amplitude;
+}
+
+
+
+
+s32 swp30_device::volume_apply(s32 level, s32 sample)
+{
+ // Level is 4.10 floating point positive, and represents an attenuation
+ // Sample is 16.6 signed and the result is in the same format
+
+ // Passed-in value may have overflowed
+ if(level >= 0x3fff)
return 0;
- return (s64(sample) - ((s64(sample) * ((value >> 9) & 0x7fff)) >> 16)) >> (value >> 24);
-}
-
-// sample is signed 24.8
-s32 swp30_device::lpffpapply(s32 value, s32 sample)
-{
- return ((((value >> 7) & 0x7fff) | 0x8000) * s64(sample)) >> (31 - (value >> 22));
-}
-
-// Some tables we picked up from the swp00. May be different, may not be.
-
-const std::array<s32, 0x80> swp30_device::attack_linear_step = {
- 0x00027, 0x0002b, 0x0002f, 0x00033, 0x00037, 0x0003d, 0x00042, 0x00048,
- 0x0004d, 0x00056, 0x0005e, 0x00066, 0x0006f, 0x0007a, 0x00085, 0x00090,
- 0x0009b, 0x000ac, 0x000bd, 0x000cc, 0x000de, 0x000f4, 0x00109, 0x00120,
- 0x00135, 0x00158, 0x00179, 0x00199, 0x001bc, 0x001e7, 0x00214, 0x00240,
- 0x0026b, 0x002af, 0x002f2, 0x00332, 0x00377, 0x003d0, 0x0042c, 0x00480,
- 0x004dc, 0x0055e, 0x005e9, 0x0066e, 0x006f4, 0x007a4, 0x00857, 0x0090b,
- 0x009c3, 0x00acb, 0x00bd6, 0x00ce6, 0x00e00, 0x00f5e, 0x010d2, 0x01234,
- 0x0139e, 0x015d0, 0x017f3, 0x01a20, 0x01c4a, 0x01f52, 0x02232, 0x0250f,
- 0x027ff, 0x02c72, 0x03109, 0x0338b, 0x039c4, 0x04038, 0x04648, 0x04c84,
- 0x05262, 0x05c1c, 0x065af, 0x06f5c, 0x07895, 0x0866f, 0x09470, 0x0a19e,
- 0x0ae4c, 0x0c566, 0x0db8d, 0x0f00f, 0x10625, 0x12937, 0x14954, 0x16c17,
- 0x1886e, 0x1c71c, 0x20000, 0x239e1, 0x2647c, 0x2aaab, 0x2ecfc, 0x3241f,
- 0x35e51, 0x3a83b, 0x40000, 0x4325c, 0x47dc1, 0x4c8f9, 0x50505, 0x55555,
- 0x58160, 0x5d174, 0x60606, 0x62b2e, 0x67b24, 0x6a63c, 0x6d3a0, 0x6eb3e,
- 0x71c72, 0x73616, 0x75075, 0x76b98, 0x78788, 0x78788, 0x7a44c, 0x7a44c,
- 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c,
-};
-const std::array<s32, 0x20> swp30_device::decay_linear_step = {
- 0x15083, 0x17ad2, 0x1a41a, 0x1cbe7, 0x1f16d, 0x22ef1, 0x26a44, 0x2a1e4,
- 0x2da35, 0x34034, 0x3a197, 0x40000, 0x45b82, 0x4b809, 0x51833, 0x57262,
- 0x5d9f7, 0x6483f, 0x6b15c, 0x71c72, 0x77976, 0x7d119, 0x83127, 0x88889,
- 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769,
-};
+ s32 e = level >> 10;
+ s32 m = level & 0x3ff;
+ s64 mul = (0x4000000 - (m << 15)) >> e;
+ return (sample * mul) >> 26;
+}
-// Pitch conversion table, 2**(31 + i/0x400)
-const std::array<u32, 0x400> swp30_device::pitch_base = {
- 0x80000000, 0x8016302f, 0x802c6436, 0x80429c17, 0x8058d7d2, 0x806f1768, 0x80855ad9, 0x809ba226,
- 0x80b1ed4f, 0x80c83c56, 0x80de8f3b, 0x80f4e5ff, 0x810b40a1, 0x81219f24, 0x81380188, 0x814e67cc,
- 0x8164d1f3, 0x817b3ffd, 0x8191b1ea, 0x81a827ba, 0x81bea170, 0x81d51f0b, 0x81eba08c, 0x820225f4,
- 0x8218af43, 0x822f3c7a, 0x8245cd9a, 0x825c62a4, 0x8272fb97, 0x82899876, 0x82a0393f, 0x82b6ddf5,
- 0x82cd8698, 0x82e43329, 0x82fae3a7, 0x83119814, 0x83285071, 0x833f0cbf, 0x8355ccfd, 0x836c912c,
- 0x8383594e, 0x839a2563, 0x83b0f56c, 0x83c7c969, 0x83dea15b, 0x83f57d43, 0x840c5d21, 0x842340f6,
- 0x843a28c3, 0x84511489, 0x84680447, 0x847ef800, 0x8495efb3, 0x84aceb61, 0x84c3eb0b, 0x84daeeb2,
- 0x84f1f656, 0x850901f8, 0x85201198, 0x85372538, 0x854e3cd8, 0x85655879, 0x857c781c, 0x85939bc0,
- 0x85aac367, 0x85c1ef12, 0x85d91ec1, 0x85f05275, 0x86078a2f, 0x861ec5ef, 0x863605b5, 0x864d4984,
- 0x8664915b, 0x867bdd3b, 0x86932d25, 0x86aa811a, 0x86c1d919, 0x86d93525, 0x86f0953d, 0x8707f963,
- 0x871f6196, 0x8736cdd8, 0x874e3e2a, 0x8765b28c, 0x877d2afe, 0x8794a783, 0x87ac2819, 0x87c3acc2,
- 0x87db357f, 0x87f2c251, 0x880a5337, 0x8821e834, 0x88398146, 0x88511e70, 0x8868bfb2, 0x8880650c,
- 0x88980e80, 0x88afbc0e, 0x88c76db6, 0x88df237a, 0x88f6dd5a, 0x890e9b57, 0x89265d72, 0x893e23ab,
- 0x8955ee03, 0x896dbc7b, 0x89858f13, 0x899d65cc, 0x89b540a7, 0x89cd1fa5, 0x89e502c6, 0x89fcea0b,
- 0x8a14d575, 0x8a2cc504, 0x8a44b8ba, 0x8a5cb096, 0x8a74ac9a, 0x8a8cacc6, 0x8aa4b11c, 0x8abcb99b,
- 0x8ad4c645, 0x8aecd71a, 0x8b04ec1b, 0x8b1d0548, 0x8b3522a3, 0x8b4d442c, 0x8b6569e4, 0x8b7d93cc,
- 0x8b95c1e3, 0x8badf42c, 0x8bc62aa7, 0x8bde6554, 0x8bf6a434, 0x8c0ee748, 0x8c272e91, 0x8c3f7a10,
- 0x8c57c9c4, 0x8c701daf, 0x8c8875d2, 0x8ca0d22d, 0x8cb932c1, 0x8cd1978f, 0x8cea0098, 0x8d026ddb,
- 0x8d1adf5b, 0x8d335517, 0x8d4bcf11, 0x8d644d49, 0x8d7ccfc0, 0x8d955677, 0x8dade16e, 0x8dc670a6,
- 0x8ddf0420, 0x8df79bdc, 0x8e1037dc, 0x8e28d820, 0x8e417ca9, 0x8e5a2577, 0x8e72d28c, 0x8e8b83e7,
- 0x8ea4398b, 0x8ebcf377, 0x8ed5b1ac, 0x8eee742b, 0x8f073af5, 0x8f20060b, 0x8f38d56c, 0x8f51a91b,
- 0x8f6a8117, 0x8f835d62, 0x8f9c3dfc, 0x8fb522e6, 0x8fce0c21, 0x8fe6f9ae, 0x8fffeb8c, 0x9018e1bd,
- 0x9031dc43, 0x904adb1c, 0x9063de4b, 0x907ce5d0, 0x9095f1ab, 0x90af01de, 0x90c81669, 0x90e12f4e,
- 0x90fa4c8b, 0x91136e24, 0x912c9417, 0x9145be67, 0x915eed13, 0x9178201d, 0x91915785, 0x91aa934c,
- 0x91c3d373, 0x91dd17fb, 0x91f660e3, 0x920fae2e, 0x9228ffdc, 0x924255ed, 0x925bb062, 0x92750f3d,
- 0x928e727d, 0x92a7da24, 0x92c14632, 0x92dab6a9, 0x92f42b88, 0x930da4d2, 0x93272285, 0x9340a4a4,
- 0x935a2b2f, 0x9373b626, 0x938d458b, 0x93a6d95e, 0x93c071a0, 0x93da0e52, 0x93f3af75, 0x940d5509,
- 0x9426ff0f, 0x9440ad88, 0x945a6075, 0x947417d6, 0x948dd3ac, 0x94a793f8, 0x94c158bb, 0x94db21f6,
- 0x94f4efa8, 0x950ec1d4, 0x9528987a, 0x9542739a, 0x955c5336, 0x9576374e, 0x95901fe3, 0x95aa0cf5,
- 0x95c3fe86, 0x95ddf497, 0x95f7ef27, 0x9611ee38, 0x962bf1cb, 0x9645f9e1, 0x96600679, 0x967a1795,
- 0x96942d37, 0x96ae475d, 0x96c8660a, 0x96e2893f, 0x96fcb0fb, 0x9716dd3f, 0x97310e0e, 0x974b4366,
- 0x97657d49, 0x977fbbb9, 0x9799feb5, 0x97b4463e, 0x97ce9255, 0x97e8e2fc, 0x98033832, 0x981d91f9,
- 0x9837f051, 0x9852533b, 0x986cbab9, 0x988726c9, 0x98a1976f, 0x98bc0caa, 0x98d6867b, 0x98f104e2,
- 0x990b87e2, 0x99260f7a, 0x99409bab, 0x995b2c77, 0x9975c1dd, 0x99905bdf, 0x99aafa7d, 0x99c59db9,
- 0x99e04593, 0x99faf20b, 0x9a15a324, 0x9a3058dc, 0x9a4b1337, 0x9a65d233, 0x9a8095d2, 0x9a9b5e15,
- 0x9ab62afc, 0x9ad0fc89, 0x9aebd2bb, 0x9b06ad95, 0x9b218d16, 0x9b3c7140, 0x9b575a14, 0x9b724791,
- 0x9b8d39b9, 0x9ba8308d, 0x9bc32c0e, 0x9bde2c3c, 0x9bf93118, 0x9c143aa4, 0x9c2f48df, 0x9c4a5bcb,
- 0x9c657368, 0x9c808fb7, 0x9c9bb0ba, 0x9cb6d670, 0x9cd200db, 0x9ced2ffc, 0x9d0863d3, 0x9d239c61,
- 0x9d3ed9a7, 0x9d5a1ba6, 0x9d75625e, 0x9d90add1, 0x9dabfdff, 0x9dc752e9, 0x9de2ac90, 0x9dfe0af5,
- 0x9e196e18, 0x9e34d5fb, 0x9e50429e, 0x9e6bb401, 0x9e872a27, 0x9ea2a50f, 0x9ebe24bb, 0x9ed9a92b,
- 0x9ef53260, 0x9f10c05b, 0x9f2c531d, 0x9f47eaa6, 0x9f6386f8, 0x9f7f2814, 0x9f9acdf9, 0x9fb678a9,
- 0x9fd22825, 0x9feddc6d, 0xa0099583, 0xa0255367, 0xa041161b, 0xa05cdd9e, 0xa078a9f2, 0xa0947b17,
- 0xa0b0510f, 0xa0cc2bda, 0xa0e80b7a, 0xa103efee, 0xa11fd938, 0xa13bc758, 0xa157ba50, 0xa173b221,
- 0xa18faeca, 0xa1abb04d, 0xa1c7b6ac, 0xa1e3c1e5, 0xa1ffd1fc, 0xa21be6ef, 0xa23800c1, 0xa2541f72,
- 0xa2704303, 0xa28c6b74, 0xa2a898c7, 0xa2c4cafc, 0xa2e10215, 0xa2fd3e11, 0xa3197ef3, 0xa335c4ba,
- 0xa3520f68, 0xa36e5efe, 0xa38ab37c, 0xa3a70ce3, 0xa3c36b34, 0xa3dfce70, 0xa3fc3698, 0xa418a3ac,
- 0xa43515ae, 0xa4518c9e, 0xa46e087d, 0xa48a894c, 0xa4a70f0c, 0xa4c399be, 0xa4e02962, 0xa4fcbdfa,
- 0xa5195786, 0xa535f608, 0xa552997f, 0xa56f41ed, 0xa58bef53, 0xa5a8a1b1, 0xa5c55909, 0xa5e2155c,
- 0xa5fed6a9, 0xa61b9cf3, 0xa6386839, 0xa655387d, 0xa6720dc0, 0xa68ee803, 0xa6abc745, 0xa6c8ab89,
- 0xa6e594cf, 0xa7028319, 0xa71f7665, 0xa73c6eb7, 0xa7596c0e, 0xa7766e6c, 0xa79375d1, 0xa7b0823e,
- 0xa7cd93b4, 0xa7eaaa35, 0xa807c5c0, 0xa824e656, 0xa8420bfa, 0xa85f36aa, 0xa87c6669, 0xa8999b38,
- 0xa8b6d516, 0xa8d41405, 0xa8f15806, 0xa90ea11a, 0xa92bef41, 0xa949427d, 0xa9669ace, 0xa983f836,
- 0xa9a15ab4, 0xa9bec24b, 0xa9dc2efa, 0xa9f9a0c3, 0xaa1717a7, 0xaa3493a7, 0xaa5214c2, 0xaa6f9afb,
- 0xaa8d2652, 0xaaaab6c9, 0xaac84c5f, 0xaae5e716, 0xab0386ef, 0xab212bea, 0xab3ed609, 0xab5c854d,
- 0xab7a39b5, 0xab97f344, 0xabb5b1fa, 0xabd375d8, 0xabf13edf, 0xac0f0d0f, 0xac2ce06a, 0xac4ab8f1,
- 0xac6896a4, 0xac867985, 0xaca46194, 0xacc24ed1, 0xace0413f, 0xacfe38de, 0xad1c35af, 0xad3a37b3,
- 0xad583eea, 0xad764b55, 0xad945cf7, 0xadb273ce, 0xadd08fdd, 0xadeeb124, 0xae0cd7a4, 0xae2b035e,
- 0xae493452, 0xae676a83, 0xae85a5f0, 0xaea3e69b, 0xaec22c84, 0xaee077ad, 0xaefec816, 0xaf1d1dc0,
- 0xaf3b78ad, 0xaf59d8dc, 0xaf783e50, 0xaf96a908, 0xafb51906, 0xafd38e4b, 0xaff208d8, 0xb01088ad,
- 0xb02f0dcb, 0xb04d9834, 0xb06c27e8, 0xb08abce8, 0xb0a95736, 0xb0c7f6d1, 0xb0e69bbc, 0xb10545f6,
- 0xb123f581, 0xb142aa5e, 0xb161648e, 0xb1802411, 0xb19ee8e8, 0xb1bdb315, 0xb1dc8299, 0xb1fb5773,
- 0xb21a31a6, 0xb2391132, 0xb257f618, 0xb276e059, 0xb295cff5, 0xb2b4c4ef, 0xb2d3bf46, 0xb2f2befc,
- 0xb311c412, 0xb330ce88, 0xb34fde60, 0xb36ef39a, 0xb38e0e38, 0xb3ad2e3a, 0xb3cc53a1, 0xb3eb7e6e,
- 0xb40aaea2, 0xb429e43e, 0xb4491f43, 0xb4685fb2, 0xb487a58c, 0xb4a6f0d2, 0xb4c64185, 0xb4e597a5,
- 0xb504f333, 0xb5245432, 0xb543baa0, 0xb5632681, 0xb58297d3, 0xb5a20e99, 0xb5c18ad3, 0xb5e10c82,
- 0xb60093a8, 0xb6202044, 0xb63fb259, 0xb65f49e7, 0xb67ee6ee, 0xb69e8971, 0xb6be316f, 0xb6dddeea,
- 0xb6fd91e3, 0xb71d4a5a, 0xb73d0851, 0xb75ccbc9, 0xb77c94c2, 0xb79c633e, 0xb7bc373d, 0xb7dc10c1,
- 0xb7fbefca, 0xb81bd459, 0xb83bbe70, 0xb85bae0f, 0xb87ba337, 0xb89b9de9, 0xb8bb9e27, 0xb8dba3f0,
- 0xb8fbaf47, 0xb91bc02b, 0xb93bd69f, 0xb95bf2a2, 0xb97c1437, 0xb99c3b5d, 0xb9bc6816, 0xb9dc9a63,
- 0xb9fcd245, 0xba1d0fbc, 0xba3d52ca, 0xba5d9b70, 0xba7de9ae, 0xba9e3d86, 0xbabe96f9, 0xbadef607,
- 0xbaff5ab2, 0xbb1fc4fa, 0xbb4034e0, 0xbb60aa66, 0xbb81258d, 0xbba1a655, 0xbbc22cbf, 0xbbe2b8cd,
- 0xbc034a7e, 0xbc23e1d6, 0xbc447ed3, 0xbc652178, 0xbc85c9c5, 0xbca677bb, 0xbcc72b5b, 0xbce7e4a7,
- 0xbd08a39f, 0xbd296844, 0xbd4a3297, 0xbd6b0299, 0xbd8bd84b, 0xbdacb3af, 0xbdcd94c4, 0xbdee7b8c,
- 0xbe0f6809, 0xbe305a3b, 0xbe515222, 0xbe724fc1, 0xbe935317, 0xbeb45c27, 0xbed56af1, 0xbef67f75,
- 0xbf1799b6, 0xbf38b9b4, 0xbf59df6f, 0xbf7b0aea, 0xbf9c3c24, 0xbfbd731f, 0xbfdeafdd, 0xbffff25d,
- 0xc0213aa1, 0xc04288ab, 0xc063dc7a, 0xc0853610, 0xc0a6956e, 0xc0c7fa95, 0xc0e96586, 0xc10ad642,
- 0xc12c4cca, 0xc14dc91f, 0xc16f4b42, 0xc190d333, 0xc1b260f5, 0xc1d3f488, 0xc1f58ded, 0xc2172d25,
- 0xc238d231, 0xc25a7d12, 0xc27c2dc8, 0xc29de456, 0xc2bfa0bc, 0xc2e162fc, 0xc3032b15, 0xc324f909,
- 0xc346ccda, 0xc368a687, 0xc38a8613, 0xc3ac6b7e, 0xc3ce56c9, 0xc3f047f5, 0xc4123f04, 0xc4343bf6,
- 0xc4563ecc, 0xc4784787, 0xc49a5629, 0xc4bc6ab2, 0xc4de8523, 0xc500a57e, 0xc522cbc3, 0xc544f7f4,
- 0xc5672a11, 0xc589621b, 0xc5aba014, 0xc5cde3fd, 0xc5f02dd6, 0xc6127da1, 0xc634d35e, 0xc6572f0f,
- 0xc67990b5, 0xc69bf851, 0xc6be65e3, 0xc6e0d96d, 0xc70352f0, 0xc725d26c, 0xc74857e4, 0xc76ae358,
- 0xc78d74c8, 0xc7b00c37, 0xc7d2a9a4, 0xc7f54d12, 0xc817f681, 0xc83aa5f2, 0xc85d5b66, 0xc88016de,
- 0xc8a2d85c, 0xc8c59fe0, 0xc8e86d6c, 0xc90b40ff, 0xc92e1a9d, 0xc950fa45, 0xc973dff8, 0xc996cbb8,
- 0xc9b9bd86, 0xc9dcb562, 0xc9ffb34f, 0xca22b74c, 0xca45c15a, 0xca68d17c, 0xca8be7b2, 0xcaaf03fd,
- 0xcad2265e, 0xcaf54ed6, 0xcb187d66, 0xcb3bb20f, 0xcb5eecd3, 0xcb822db2, 0xcba574ae, 0xcbc8c1c7,
- 0xcbec14fe, 0xcc0f6e56, 0xcc32cdcd, 0xcc563367, 0xcc799f23, 0xcc9d1104, 0xccc08909, 0xcce40734,
- 0xcd078b86, 0xcd2b1600, 0xcd4ea6a3, 0xcd723d71, 0xcd95da6a, 0xcdb97d8f, 0xcddd26e2, 0xce00d664,
- 0xce248c15, 0xce4847f6, 0xce6c0a0a, 0xce8fd250, 0xceb3a0ca, 0xced77579, 0xcefb505e, 0xcf1f317a,
- 0xcf4318cf, 0xcf67065c, 0xcf8afa24, 0xcfaef428, 0xcfd2f468, 0xcff6fae5, 0xd01b07a2, 0xd03f1a9e,
- 0xd06333da, 0xd0875359, 0xd0ab791b, 0xd0cfa521, 0xd0f3d76c, 0xd1180ffd, 0xd13c4ed6, 0xd16093f7,
- 0xd184df62, 0xd1a93117, 0xd1cd8918, 0xd1f1e766, 0xd2164c02, 0xd23ab6ec, 0xd25f2827, 0xd2839fb3,
- 0xd2a81d91, 0xd2cca1c3, 0xd2f12c49, 0xd315bd25, 0xd33a5457, 0xd35ef1e1, 0xd38395c4, 0xd3a84001,
- 0xd3ccf099, 0xd3f1a78d, 0xd41664df, 0xd43b288e, 0xd45ff29e, 0xd484c30d, 0xd4a999df, 0xd4ce7713,
- 0xd4f35aab, 0xd51844a8, 0xd53d350c, 0xd5622bd6, 0xd5872909, 0xd5ac2ca5, 0xd5d136ac, 0xd5f6471f,
- 0xd61b5dfe, 0xd6407b4b, 0xd6659f08, 0xd68ac934, 0xd6aff9d1, 0xd6d530e1, 0xd6fa6e65, 0xd71fb25d,
- 0xd744fcca, 0xd76a4daf, 0xd78fa50b, 0xd7b502e1, 0xd7da6731, 0xd7ffd1fc, 0xd8254343, 0xd84abb08,
- 0xd870394c, 0xd895be0f, 0xd8bb4954, 0xd8e0db1b, 0xd9067364, 0xd92c1232, 0xd951b786, 0xd9776360,
- 0xd99d15c2, 0xd9c2cead, 0xd9e88e21, 0xda0e5421, 0xda3420ad, 0xda59f3c7, 0xda7fcd6f, 0xdaa5ada6,
- 0xdacb946f, 0xdaf181c9, 0xdb1775b6, 0xdb3d7038, 0xdb63714f, 0xdb8978fd, 0xdbaf8742, 0xdbd59c20,
- 0xdbfbb797, 0xdc21d9aa, 0xdc480259, 0xdc6e31a6, 0xdc946791, 0xdcbaa41b, 0xdce0e747, 0xdd073114,
- 0xdd2d8185, 0xdd53d899, 0xdd7a3653, 0xdda09ab4, 0xddc705bc, 0xdded776e, 0xde13efc9, 0xde3a6ecf,
- 0xde60f482, 0xde8780e2, 0xdeae13f1, 0xded4adb0, 0xdefb4e1f, 0xdf21f541, 0xdf48a316, 0xdf6f579f,
- 0xdf9612de, 0xdfbcd4d4, 0xdfe39d82, 0xe00a6ce9, 0xe031430a, 0xe0581fe6, 0xe07f037f, 0xe0a5edd6,
- 0xe0ccdeec, 0xe0f3d6c2, 0xe11ad559, 0xe141dab2, 0xe168e6cf, 0xe18ff9b1, 0xe1b71359, 0xe1de33c8,
- 0xe2055aff, 0xe22c8900, 0xe253bdcc, 0xe27af963, 0xe2a23bc7, 0xe2c984fa, 0xe2f0d4fc, 0xe3182bce,
- 0xe33f8972, 0xe366ede9, 0xe38e5934, 0xe3b5cb55, 0xe3dd444c, 0xe404c41a, 0xe42c4ac2, 0xe453d843,
- 0xe47b6ca0, 0xe4a307d9, 0xe4caa9ef, 0xe4f252e5, 0xe51a02ba, 0xe541b971, 0xe5697709, 0xe5913b86,
- 0xe5b906e7, 0xe5e0d92e, 0xe608b25c, 0xe6309273, 0xe6587973, 0xe680675e, 0xe6a85c34, 0xe6d057f8,
- 0xe6f85aaa, 0xe720644c, 0xe74874df, 0xe7708c63, 0xe798aada, 0xe7c0d046, 0xe7e8fca8, 0xe8113000,
- 0xe8396a50, 0xe861ab99, 0xe889f3dd, 0xe8b2431c, 0xe8da9958, 0xe902f692, 0xe92b5acb, 0xe953c605,
- 0xe97c3840, 0xe9a4b17e, 0xe9cd31c0, 0xe9f5b908, 0xea1e4756, 0xea46dcac, 0xea6f790a, 0xea981c73,
- 0xeac0c6e7, 0xeae97868, 0xeb1230f7, 0xeb3af095, 0xeb63b743, 0xeb8c8502, 0xebb559d4, 0xebde35ba,
- 0xec0718b6, 0xec3002c8, 0xec58f3f1, 0xec81ec33, 0xecaaeb8f, 0xecd3f207, 0xecfcff9b, 0xed26144d,
- 0xed4f301e, 0xed785310, 0xeda17d22, 0xedcaae58, 0xedf3e6b1, 0xee1d2630, 0xee466cd5, 0xee6fbaa2,
- 0xee990f98, 0xeec26bb7, 0xeeebcf03, 0xef15397b, 0xef3eab20, 0xef6823f5, 0xef91a3fb, 0xefbb2b32,
- 0xefe4b99b, 0xf00e4f39, 0xf037ec0d, 0xf0619017, 0xf08b3b58, 0xf0b4edd3, 0xf0dea788, 0xf1086879,
- 0xf13230a7, 0xf15c0013, 0xf185d6be, 0xf1afb4aa, 0xf1d999d8, 0xf2038649, 0xf22d79ff, 0xf25774fa,
- 0xf281773c, 0xf2ab80c6, 0xf2d5919a, 0xf2ffa9b8, 0xf329c923, 0xf353efda, 0xf37e1de1, 0xf3a85337,
- 0xf3d28fde, 0xf3fcd3d7, 0xf4271f24, 0xf45171c6, 0xf47bcbbe, 0xf4a62d0d, 0xf4d095b5, 0xf4fb05b7,
- 0xf5257d15, 0xf54ffbce, 0xf57a81e6, 0xf5a50f5c, 0xf5cfa433, 0xf5fa406c, 0xf624e407, 0xf64f8f07,
- 0xf67a416c, 0xf6a4fb38, 0xf6cfbc6c, 0xf6fa8509, 0xf7255510, 0xf7502c84, 0xf77b0b65, 0xf7a5f1b4,
- 0xf7d0df73, 0xf7fbd4a2, 0xf826d145, 0xf851d55a, 0xf87ce0e5, 0xf8a7f3e6, 0xf8d30e5e, 0xf8fe3050,
- 0xf92959bb, 0xf9548aa1, 0xf97fc305, 0xf9ab02e6, 0xf9d64a46, 0xfa019927, 0xfa2cef8a, 0xfa584d70,
- 0xfa83b2db, 0xfaaf1fcb, 0xfada9443, 0xfb061042, 0xfb3193cc, 0xfb5d1ee0, 0xfb88b181, 0xfbb44baf,
- 0xfbdfed6c, 0xfc0b96ba, 0xfc374799, 0xfc63000b, 0xfc8ec011, 0xfcba87ac, 0xfce656de, 0xfd122da9,
- 0xfd3e0c0c, 0xfd69f20b, 0xfd95dfa6, 0xfdc1d4dd, 0xfdedd1b4, 0xfe19d62b, 0xfe45e243, 0xfe71f5fd,
- 0xfe9e115c, 0xfeca3460, 0xfef65f0a, 0xff22915d, 0xff4ecb59, 0xff7b0cff, 0xffa75652, 0xffd3a751,
-};
+void swp30_device::awm2_step(std::array<s32, 0x40> &samples_per_chan)
+{
+ for(int chan = 0; chan != 0x40; chan++) {
+ if(!m_envelope[chan].active()) {
+ samples_per_chan[chan] = 0;
+ continue;
+ }
-// Actual shape of the lfos unknown, since the hardware accepts 4 and
-// 3 are in use (0, 1 and 3) and no recording are currently available
+ auto &lfo = m_lfo[chan];
+
+ auto [sample1, trigger_release] = m_streaming[chan].step(m_wave_cache, lfo.get_pitch());
+ if(trigger_release)
+ m_envelope[chan].trigger_release();
+
+ s32 sample2 = m_filter[chan].step(sample1);
+ s32 sample3 = m_iir1[chan].step(sample2);
+ s32 sample4 = volume_apply(m_envelope[chan].step(m_meg->m_sample_counter) + lfo.get_amplitude(), sample3);
+
+ lfo.step(machine());
+ samples_per_chan[chan] = sample4;
+ }
+}
-const std::array<u32, 4> swp30_device::lfo_shape_centered_saw = { 0x00000000, 0x00000000, 0xfff00000, 0xfff00000 }; // --////--
-const std::array<u32, 4> swp30_device::lfo_shape_centered_tri = { 0x00000000, 0x0007ffff, 0xfff7ffff, 0xfff00000 }; // --/\/\--
-const std::array<u32, 4> swp30_device::lfo_shape_offset_saw = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; // __////__
-const std::array<u32, 4> swp30_device::lfo_shape_offset_tri = { 0x00000000, 0x00000000, 0x000fffff, 0x000fffff }; // __/\/\__
-const std::array<u8, 4> swp30_device::dpcm_offset = { 7, 6, 4, 0 };
swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, SWP30, tag, owner, clock),
device_sound_interface(mconfig, *this),
m_program_config("meg_program", ENDIANNESS_LITTLE, 64, 9, -3, address_map_constructor(FUNC(swp30_device::meg_prg_map), this)),
- m_rom_config("sample_rom", ENDIANNESS_LITTLE, 32, 25, -2),
- m_reverb_config("reverb_ram", ENDIANNESS_LITTLE, 16, 18, -1, address_map_constructor(FUNC(swp30_device::meg_reverb_map), this))
+ m_wave_config("wave", ENDIANNESS_LITTLE, 32, 25, -2),
+ m_reverb_config("reverb_ram", ENDIANNESS_LITTLE, 16, 18, -1, address_map_constructor(FUNC(swp30_device::meg_reverb_map), this)),
+ m_sintab(*this, "sintab"),
+ m_drccache(32*1024*1024 + sizeof(meg_state))
{
}
void swp30_device::device_start()
{
m_program = &space(AS_PROGRAM);
- m_rom = &space(AS_DATA);
+ m_wave = &space(AS_DATA);
m_reverb = &space(AS_REVERB);
- m_rom->cache(m_rom_cache);
+ m_program->cache(m_program_cache);
+ m_wave->cache(m_wave_cache);
m_reverb->cache(m_reverb_cache);
- state_add(STATE_GENPC, "GENPC", m_meg_pc).noshow();
- state_add(STATE_GENPCBASE, "CURPC", m_meg_pc).noshow();
- state_add(0, "PC", m_meg_pc);
-
- set_icountptr(m_icount);
+ m_meg = static_cast<meg_state *>(m_drccache.alloc_near(sizeof(meg_state)));
+ m_meg->m_swp = this;
+ m_meg->reset();
- m_stream = stream_alloc(0, 2, 44100, STREAM_SYNCHRONOUS);
+ state_add(STATE_GENPC, "GENPC", m_meg->m_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_meg->m_pc).noshow();
+ state_add(0, "PC", m_meg->m_pc);
+ state_add(1, "P", m_meg->m_p);
- for(int i=0; i != 128; i++) {
- u32 v = 0;
- switch(i >> 3) {
- default: v = ((i & 7) + 8) << (1 + (i >> 3)); break;
- case 0xb: v = ((i & 7) + 4) << 13; break;
- case 0xc: v = ((i & 6) + 6) << 14; break;
- case 0xd: v = ((i & 4) + 7) << 15; break;
- case 0xe: v = 15 << 15; break;
- case 0xf: v = 31 << 15; break;
- }
- m_global_step[i] = v;
- }
-
- // Delta-packed samples decompression.
+ for(int i=1; i != 0x40; i++)
+ state_add(i+1, util::string_format("m%02x", i).c_str(), m_meg->m_m[i]);
- for(int i=0; i<128; i++) {
- s16 base = ((i & 0x1f) << (3+(i >> 5))) + (((1 << (i >> 5))-1) << 8);
- m_dpcm[i | 0x80] = - base;
- m_dpcm[i] = + base;
- }
-
- save_item(NAME(m_keyon_mask));
+ m_drcuml = std::make_unique<drcuml_state>(*this, m_drccache, 0, 1, 9, 0);
+ m_drcuml->symbol_add(&m_meg->m_pc, sizeof(m_meg->m_pc), "pc");
+ m_drcuml->symbol_add(&m_meg->m_icount, sizeof(m_meg->m_icount), "icount");
+ m_drcuml->symbol_add(&m_meg->m_program, sizeof(m_meg->m_program), "program");
+ m_drcuml->symbol_add(&m_meg->m_const, sizeof(m_meg->m_const), "const");
+ m_drcuml->symbol_add(&m_meg->m_offset, sizeof(m_meg->m_offset), "offset");
+ m_drcuml->symbol_add(&m_meg->m_m, sizeof(m_meg->m_m), "m");
+ m_drcuml->symbol_add(&m_meg->m_r, sizeof(m_meg->m_r), "r");
+ m_drcuml->symbol_add(&m_meg->m_t, sizeof(m_meg->m_t), "t");
+ m_drcuml->symbol_add(&m_meg->m_p, sizeof(m_meg->m_p), "p");
+ m_drcuml->symbol_add(&m_meg->m_mw_value, sizeof(m_meg->m_mw_value), "mw");
+ m_drcuml->symbol_add(&m_meg->m_rw_value, sizeof(m_meg->m_rw_value), "rw");
+ m_drcuml->symbol_add(&m_meg->m_index_value, sizeof(m_meg->m_index_value), "index");
+ m_drcuml->symbol_add(&m_meg->m_memw_value, sizeof(m_meg->m_memw_value), "memw");
- save_item(NAME(m_sample_start));
- save_item(NAME(m_sample_end));
- save_item(NAME(m_sample_address));
- save_item(NAME(m_pitch));
+ m_meg_drc_entry = m_drcuml->handle_alloc("entry");
- save_item(NAME(m_release_glo));
+ m_meg_program_changed = true;
+ m_meg_drc_active = allow_drc();
- save_item(NAME(m_lfo_step_pmod));
- save_item(NAME(m_lfo_amod));
+ set_icountptr(m_meg->m_icount);
- save_item(NAME(m_attack));
- save_item(NAME(m_decay1));
- save_item(NAME(m_decay2));
+ // Separate the streams to avoid loops with plugins and dual-swp30 systems
+ m_input_stream = stream_alloc(16, 0, 44100, STREAM_SYNCHRONOUS);
+ m_output_stream = stream_alloc(0, 20, 44100, STREAM_SYNCHRONOUS);
- save_item(NAME(m_lfo_phase));
- save_item(NAME(m_sample_pos));
- save_item(NAME(m_envelope_level));
- save_item(NAME(m_envelope_on_timer));
- save_item(NAME(m_envelope_timer));
- save_item(NAME(m_decay2_done));
- save_item(NAME(m_envelope_mode));
- save_item(NAME(m_glo_level_cur));
-
- save_item(NAME(m_dpcm_current));
- save_item(NAME(m_dpcm_next));
- save_item(NAME(m_dpcm_address));
- save_item(NAME(m_dpcm_sum));
-
- save_item(NAME(m_sample_history));
+ save_item(NAME(m_keyon_mask));
- 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(STRUCT_MEMBER(m_streaming, m_start));
+ save_item(STRUCT_MEMBER(m_streaming, m_loop));
+ save_item(STRUCT_MEMBER(m_streaming, m_address));
+ save_item(STRUCT_MEMBER(m_streaming, m_pitch));
+ save_item(STRUCT_MEMBER(m_streaming, m_loop_size));
+ save_item(STRUCT_MEMBER(m_streaming, m_pos));
+ save_item(STRUCT_MEMBER(m_streaming, m_pos_dec));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_s0));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_s1));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_s2));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_s3));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_pos));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_delta));
+ save_item(STRUCT_MEMBER(m_streaming, m_first));
+ save_item(STRUCT_MEMBER(m_streaming, m_finetune_active));
+ save_item(STRUCT_MEMBER(m_streaming, m_done));
+ save_item(STRUCT_MEMBER(m_streaming, m_last));
+
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_a));
+ save_item(STRUCT_MEMBER(m_filter, m_level_1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_a));
+ save_item(STRUCT_MEMBER(m_filter, m_level_2));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_b));
+
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_p1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_p1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_p2));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_x1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_x2));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_y0));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_y1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_h));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_b));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_n));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_1_l));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_x1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_x2));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_y0));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_y1));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_h));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_b));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_n));
+ save_item(STRUCT_MEMBER(m_filter, m_filter_2_l));
+
+ save_item(STRUCT_MEMBER(m_iir1, m_a));
+ save_item(STRUCT_MEMBER(m_iir1, m_b));
+ save_item(STRUCT_MEMBER(m_iir1, m_hx));
+ save_item(STRUCT_MEMBER(m_iir1, m_hy));
+
+ save_item(STRUCT_MEMBER(m_envelope, m_attack));
+ save_item(STRUCT_MEMBER(m_envelope, m_decay1));
+ save_item(STRUCT_MEMBER(m_envelope, m_decay2));
+ save_item(STRUCT_MEMBER(m_envelope, m_release_glo));
+ save_item(STRUCT_MEMBER(m_envelope, m_envelope_level));
+ save_item(STRUCT_MEMBER(m_envelope, m_envelope_mode));
+
+ save_item(STRUCT_MEMBER(m_lfo, m_counter));
+ save_item(STRUCT_MEMBER(m_lfo, m_state));
+ save_item(STRUCT_MEMBER(m_lfo, m_type));
+ save_item(STRUCT_MEMBER(m_lfo, m_step));
+ save_item(STRUCT_MEMBER(m_lfo, m_amplitude));
+ save_item(STRUCT_MEMBER(m_lfo, m_pitch_mode));
+ save_item(STRUCT_MEMBER(m_lfo, m_pitch_depth));
+ save_item(STRUCT_MEMBER(m_lfo, m_r_type_step_pitch));
+ save_item(STRUCT_MEMBER(m_lfo, m_r_amplitude));
save_item(NAME(m_internal_adr));
- save_item(NAME(m_meg_program_address));
- save_item(NAME(m_waverom_adr));
- save_item(NAME(m_waverom_mode));
- save_item(NAME(m_waverom_access));
- save_item(NAME(m_waverom_val));
-
- save_item(NAME(m_meg_program));
- save_item(NAME(m_meg_const));
- save_item(NAME(m_meg_offset));
- save_item(NAME(m_meg_lfo));
- save_item(NAME(m_meg_map));
+ save_item(NAME(m_wave_adr));
+ save_item(NAME(m_wave_size));
+ save_item(NAME(m_wave_access));
+ save_item(NAME(m_wave_val));
save_item(STRUCT_MEMBER(m_mixer, vol));
save_item(STRUCT_MEMBER(m_mixer, route));
+ save_item(NAME(m_melo));
+ save_item(NAME(m_meli));
+ save_item(NAME(m_adc));
+
+ save_item(STRUCT_MEMBER(*m_meg, m_program));
+ save_item(STRUCT_MEMBER(*m_meg, m_const));
+ save_item(STRUCT_MEMBER(*m_meg, m_offset));
+ save_item(STRUCT_MEMBER(*m_meg, m_lfo));
+ save_item(STRUCT_MEMBER(*m_meg, m_lfo_counter));
+ save_item(STRUCT_MEMBER(*m_meg, m_lfo_increment));
+ save_item(STRUCT_MEMBER(*m_meg, m_map));
+ save_item(STRUCT_MEMBER(*m_meg, m_ram_read));
+ save_item(STRUCT_MEMBER(*m_meg, m_ram_write));
+ save_item(STRUCT_MEMBER(*m_meg, m_ram_index));
+ save_item(STRUCT_MEMBER(*m_meg, m_program_address));
+ save_item(STRUCT_MEMBER(*m_meg, m_m));
+ save_item(STRUCT_MEMBER(*m_meg, m_r));
+ save_item(STRUCT_MEMBER(*m_meg, m_t));
+ save_item(STRUCT_MEMBER(*m_meg, m_p));
+ save_item(STRUCT_MEMBER(*m_meg, m_mw_value));
+ save_item(STRUCT_MEMBER(*m_meg, m_mw_reg));
+ save_item(STRUCT_MEMBER(*m_meg, m_rw_value));
+ save_item(STRUCT_MEMBER(*m_meg, m_rw_reg));
+ save_item(STRUCT_MEMBER(*m_meg, m_index_value));
+ save_item(STRUCT_MEMBER(*m_meg, m_index_active));
+ save_item(STRUCT_MEMBER(*m_meg, m_memw_value));
+ save_item(STRUCT_MEMBER(*m_meg, m_memw_active));
+ save_item(STRUCT_MEMBER(*m_meg, m_memr_value));
+ save_item(STRUCT_MEMBER(*m_meg, m_memr_active));
+ save_item(STRUCT_MEMBER(*m_meg, m_delay_3));
+ save_item(STRUCT_MEMBER(*m_meg, m_delay_2));
+ save_item(STRUCT_MEMBER(*m_meg, m_sample_counter));
+ save_item(STRUCT_MEMBER(*m_meg, m_retval));
+}
+
+void swp30_device::meg_state::reset()
+{
+ std::fill(m_program.begin(), m_program.end(), 0);
+ std::fill(m_const.begin(), m_const.end(), 0);
+ std::fill(m_offset.begin(), m_offset.end(), 0);
+ std::fill(m_lfo.begin(), m_lfo.end(), 0);
+ std::fill(m_lfo_increment.begin(), m_lfo_increment.end(), 0);
+ std::fill(m_lfo_counter.begin(), m_lfo_counter.end(), 0);
+ std::fill(m_map.begin(), m_map.end(), 0);
+ m_ram_read = 0;
+ m_ram_write = 0;
+ m_ram_index = 0;
+ m_program_address = 0;
+ m_pc = 0;
+ std::fill(m_m.begin(), m_m.end(), 0);
+ std::fill(m_r.begin(), m_r.end(), 0);
+ std::fill(m_t.begin(), m_t.end(), 0);
+ m_p = 0;
+ std::fill(m_mw_value.begin(), m_mw_value.end(), 0);
+ std::fill(m_mw_reg.begin(), m_mw_reg.end(), 0);
+ std::fill(m_rw_value.begin(), m_rw_value.end(), 0);
+ std::fill(m_rw_reg.begin(), m_rw_reg.end(), 0);
+ std::fill(m_index_value.begin(), m_index_value.end(), false);
+ std::fill(m_index_active.begin(), m_index_active.end(), 0);
+ std::fill(m_memw_value.begin(), m_memw_value.end(), false);
+ std::fill(m_memw_active.begin(), m_memw_active.end(), 0);
+ std::fill(m_memr_value.begin(), m_memr_value.end(), false);
+ std::fill(m_memr_active.begin(), m_memr_active.end(), 0);
+ m_delay_3 = 0;
+ m_delay_2 = 0;
+ m_sample_counter = 0;
+ m_retval = 0;
}
void swp30_device::device_reset()
{
m_keyon_mask = 0;
- std::fill(m_sample_start.begin(), m_sample_start.end(), 0);
- std::fill(m_sample_end.begin(), m_sample_end.end(), 0);
- std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
- std::fill(m_pitch.begin(), m_pitch.end(), 0);
-
- std::fill(m_release_glo.begin(), m_release_glo.end(), 0);
-
- std::fill(m_lfo_step_pmod.begin(), m_lfo_step_pmod.end(), 0);
- std::fill(m_lfo_amod.begin(), m_lfo_amod.end(), 0);
-
- std::fill(m_attack.begin(), m_attack.end(), 0);
- std::fill(m_decay1.begin(), m_decay1.end(), 0);
- std::fill(m_decay2.begin(), m_decay2.end(), 0);
-
- std::fill(m_lfo_phase.begin(), m_lfo_phase.end(), 0);
- std::fill(m_sample_pos.begin(), m_sample_pos.end(), 0);
- std::fill(m_envelope_level.begin(), m_envelope_level.end(), 0);
- std::fill(m_envelope_timer.begin(), m_envelope_timer.end(), 0);
- std::fill(m_envelope_on_timer.begin(), m_envelope_on_timer.end(), false);
- std::fill(m_decay2_done.begin(), m_decay2_done.end(), false);
- std::fill(m_envelope_mode.begin(), m_envelope_mode.end(), IDLE);
- std::fill(m_glo_level_cur.begin(), m_glo_level_cur.end(), 0);
-
- std::fill(m_dpcm_current.begin(), m_dpcm_current.end(), false);
- std::fill(m_dpcm_next.begin(), m_dpcm_next.end(), false);
- std::fill(m_dpcm_address.begin(), m_dpcm_address.end(), false);
- std::fill(m_dpcm_sum.begin(), m_dpcm_sum.end(), 0);
-
- std::fill(m_meg_program.begin(), m_meg_program.end(), 0);
- std::fill(m_meg_const.begin(), m_meg_const.end(), 0);
- std::fill(m_meg_offset.begin(), m_meg_offset.end(), 0);
- std::fill(m_meg_lfo.begin(), m_meg_lfo.end(), 0);
- std::fill(m_meg_map.begin(), m_meg_map.end(), 0);
std::fill(m_mixer.begin(), m_mixer.end(), mixer_slot());
- memset(m_sample_history, 0, sizeof(m_sample_history));
+ for(auto &s : m_streaming)
+ s.clear();
+ for(auto &f : m_filter)
+ f.clear();
+ for(auto &i : m_iir1)
+ i.clear();
+ for(auto &e : m_envelope)
+ e.clear();
+ for(auto &l : m_lfo)
+ l.clear();
- 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));
+ m_meg->reset();
- m_meg_program_address = 0;
- m_waverom_adr = 0;
- m_waverom_mode = 0;
- m_waverom_access = 0;
- m_waverom_val = 0;
+ m_wave_adr = 0;
+ m_wave_size = 0;
+ m_wave_access = 0;
+ m_wave_val = 0;
+ m_revram_adr = 0;
+ m_revram_data = 0;
+ m_revram_enable = 0;
+
+ std::fill(m_meli.begin(), m_meli.end(), 0);
+ std::fill(m_melo.begin(), m_melo.end(), 0);
+ std::fill(m_adc.begin(), m_adc.end(), 0);
}
void swp30_device::map(address_map &map)
{
- map(0x0000, 0x1fff).rw(FUNC(swp30_device::snd_r), FUNC(swp30_device::snd_w));
+ map(0x0000, 0x1fff).w(FUNC(swp30_device::snd_w));
- 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));
- rchan(map, 0x05).rw(FUNC(swp30_device::lfo_amod_r), FUNC(swp30_device::lfo_amod_w));
+ rchan(map, 0x00).rw(FUNC(swp30_device::filter_1_a_r), FUNC(swp30_device::filter_1_a_w));
+ rchan(map, 0x01).rw(FUNC(swp30_device::level_1_r), FUNC(swp30_device::level_1_w));
+ rchan(map, 0x02).rw(FUNC(swp30_device::filter_2_a_r), FUNC(swp30_device::filter_2_a_w));
+ rchan(map, 0x03).rw(FUNC(swp30_device::level_2_r), FUNC(swp30_device::level_2_w));
+ rchan(map, 0x04).rw(FUNC(swp30_device::filter_b_r), FUNC(swp30_device::filter_b_w));
+ rchan(map, 0x05).rw(FUNC(swp30_device::lfo_amplitude_r), FUNC(swp30_device::lfo_amplitude_w));
rchan(map, 0x06).rw(FUNC(swp30_device::attack_r), FUNC(swp30_device::attack_w));
rchan(map, 0x07).rw(FUNC(swp30_device::decay1_r), FUNC(swp30_device::decay1_w));
rchan(map, 0x08).rw(FUNC(swp30_device::decay2_r), FUNC(swp30_device::decay2_w));
rchan(map, 0x09).rw(FUNC(swp30_device::release_glo_r), FUNC(swp30_device::release_glo_w));
- rchan(map, 0x0a).rw(FUNC(swp30_device::lfo_step_pmod_r), FUNC(swp30_device::lfo_step_pmod_w));
+ rchan(map, 0x0a).rw(FUNC(swp30_device::lfo_type_step_pitch_r), FUNC(swp30_device::lfo_type_step_pitch_w));
// 0b-0d missing
// 10 missing
rchan(map, 0x11).rw(FUNC(swp30_device::pitch_r), FUNC(swp30_device::pitch_w));
- rchan(map, 0x12).rw(FUNC(swp30_device::sample_start_h_r), FUNC(swp30_device::sample_start_h_w));
- rchan(map, 0x13).rw(FUNC(swp30_device::sample_start_l_r), FUNC(swp30_device::sample_start_l_w));
- rchan(map, 0x14).rw(FUNC(swp30_device::sample_end_h_r), FUNC(swp30_device::sample_end_h_w));
- rchan(map, 0x15).rw(FUNC(swp30_device::sample_end_l_r), FUNC(swp30_device::sample_end_l_w));
- rchan(map, 0x16).rw(FUNC(swp30_device::sample_address_h_r), FUNC(swp30_device::sample_address_h_w));
- rchan(map, 0x17).rw(FUNC(swp30_device::sample_address_l_r), FUNC(swp30_device::sample_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>));
+ rchan(map, 0x12).rw(FUNC(swp30_device::start_h_r), FUNC(swp30_device::start_h_w));
+ rchan(map, 0x13).rw(FUNC(swp30_device::start_l_r), FUNC(swp30_device::start_l_w));
+ rchan(map, 0x14).rw(FUNC(swp30_device::loop_h_r), FUNC(swp30_device::loop_h_w));
+ rchan(map, 0x15).rw(FUNC(swp30_device::loop_l_r), FUNC(swp30_device::loop_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::a1_r<0>), FUNC(swp30_device::a1_w<0>));
+ rchan(map, 0x22).rw(FUNC(swp30_device::b1_r<0>), FUNC(swp30_device::b1_w<0>));
+ rchan(map, 0x24).rw(FUNC(swp30_device::a0_r<0>), FUNC(swp30_device::a0_w<0>));
+ rchan(map, 0x26).rw(FUNC(swp30_device::a1_r<1>), FUNC(swp30_device::a1_w<1>));
+ rchan(map, 0x28).rw(FUNC(swp30_device::b1_r<1>), FUNC(swp30_device::b1_w<1>));
+ rchan(map, 0x2a).rw(FUNC(swp30_device::a0_r<1>), FUNC(swp30_device::a0_w<1>));
// 2c-2f missing
// Control registers
@@ -633,26 +1968,27 @@ void swp30_device::map(address_map &map)
// 00-01 missing
rctrl(map, 0x02).rw(FUNC(swp30_device::internal_adr_r), FUNC(swp30_device::internal_adr_w));
rctrl(map, 0x03).r (FUNC(swp30_device::internal_r));
- rctrl(map, 0x04).rw(FUNC(swp30_device::waverom_adr_r<1>), FUNC(swp30_device::waverom_adr_w<1>));
- rctrl(map, 0x05).rw(FUNC(swp30_device::waverom_adr_r<0>), FUNC(swp30_device::waverom_adr_w<0>));
- rctrl(map, 0x06).rw(FUNC(swp30_device::waverom_mode_r<1>), FUNC(swp30_device::waverom_mode_w<1>));
- rctrl(map, 0x07).rw(FUNC(swp30_device::waverom_mode_r<0>), FUNC(swp30_device::waverom_mode_w<0>));
- rctrl(map, 0x08).rw(FUNC(swp30_device::waverom_access_r), FUNC(swp30_device::waverom_access_w));
- rctrl(map, 0x09).r (FUNC(swp30_device::waverom_busy_r));
- rctrl(map, 0x0a).r (FUNC(swp30_device::waverom_val_r<1>));
- rctrl(map, 0x0b).r (FUNC(swp30_device::waverom_val_r<0>));
+ rctrl(map, 0x04).rw(FUNC(swp30_device::wave_adr_r<1>), FUNC(swp30_device::wave_adr_w<1>));
+ rctrl(map, 0x05).rw(FUNC(swp30_device::wave_adr_r<0>), FUNC(swp30_device::wave_adr_w<0>));
+ rctrl(map, 0x06).rw(FUNC(swp30_device::wave_size_r<1>), FUNC(swp30_device::wave_size_w<1>));
+ rctrl(map, 0x07).rw(FUNC(swp30_device::wave_size_r<0>), FUNC(swp30_device::wave_size_w<0>));
+ rctrl(map, 0x08).rw(FUNC(swp30_device::wave_access_r), FUNC(swp30_device::wave_access_w));
+ rctrl(map, 0x09).r (FUNC(swp30_device::wave_busy_r));
+ rctrl(map, 0x0a).rw(FUNC(swp30_device::wave_val_r<1>), FUNC(swp30_device::wave_val_w<1>));
+ rctrl(map, 0x0b).rw(FUNC(swp30_device::wave_val_r<0>), FUNC(swp30_device::wave_val_w<0>));
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
+ // 11-1f missing
+ rctrl(map, 0x20).w (FUNC(swp30_device::meg_lfo_commit_w));
rctrl(map, 0x21).rw(FUNC(swp30_device::meg_prg_address_r), FUNC(swp30_device::meg_prg_address_w));
rctrl(map, 0x22).rw(FUNC(swp30_device::meg_prg_r<0>), FUNC(swp30_device::meg_prg_w<0>));
rctrl(map, 0x23).rw(FUNC(swp30_device::meg_prg_r<1>), FUNC(swp30_device::meg_prg_w<1>));
rctrl(map, 0x24).rw(FUNC(swp30_device::meg_prg_r<2>), FUNC(swp30_device::meg_prg_w<2>));
rctrl(map, 0x25).rw(FUNC(swp30_device::meg_prg_r<3>), FUNC(swp30_device::meg_prg_w<3>));
- // 26-7f missing
+
rctrl(map, 0x30).rw(FUNC(swp30_device::meg_map_r<0>), FUNC(swp30_device::meg_map_w<0>));
rctrl(map, 0x32).rw(FUNC(swp30_device::meg_map_r<1>), FUNC(swp30_device::meg_map_w<1>));
rctrl(map, 0x34).rw(FUNC(swp30_device::meg_map_r<2>), FUNC(swp30_device::meg_map_w<2>));
@@ -661,6 +1997,13 @@ void swp30_device::map(address_map &map)
rctrl(map, 0x3a).rw(FUNC(swp30_device::meg_map_r<5>), FUNC(swp30_device::meg_map_w<5>));
rctrl(map, 0x3c).rw(FUNC(swp30_device::meg_map_r<6>), FUNC(swp30_device::meg_map_w<6>));
rctrl(map, 0x3e).rw(FUNC(swp30_device::meg_map_r<7>), FUNC(swp30_device::meg_map_w<7>));
+ rctrl(map, 0x40).w (FUNC(swp30_device::revram_enable_w));
+ rctrl(map, 0x41).w (FUNC(swp30_device::revram_clear_w));
+ rctrl(map, 0x42).r (FUNC(swp30_device::revram_status_r));
+ rctrl(map, 0x4a).w (FUNC(swp30_device::revram_adr_w<1>));
+ rctrl(map, 0x4b).w (FUNC(swp30_device::revram_adr_w<0>));
+ rctrl(map, 0x4c).rw(FUNC(swp30_device::revram_data_r<1>), FUNC(swp30_device::revram_data_w<1>));
+ rctrl(map, 0x4d).rw(FUNC(swp30_device::revram_data_r<0>), FUNC(swp30_device::revram_data_w<0>));
// MEG registers
rchan(map, 0x21).rw(FUNC(swp30_device::meg_const_r<0>), FUNC(swp30_device::meg_const_w<0>));
@@ -710,494 +2053,520 @@ void swp30_device::keyon_w(u16)
for(int chan=0; chan<64; chan++) {
u64 mask = u64(1) << chan;
if(m_keyon_mask & mask) {
- m_sample_pos[chan] = -(m_sample_start[chan] & 0xffffff) << 8;
- if(m_release_glo[chan] & 0x8000) {
- m_envelope_level[chan] = 0;
- m_envelope_on_timer[chan] = false;
- m_envelope_mode[chan] = RELEASE;
- } else if(m_attack[chan] & 0x80) {
- m_envelope_level[chan] = 0x8000000;
- m_envelope_on_timer[chan] = false;
- m_envelope_mode[chan] = ATTACK;
- } else {
- m_envelope_level[chan] = 0;
- m_envelope_on_timer[chan] = true;
- m_envelope_timer[chan] = 0x8000000;
- m_envelope_mode[chan] = ATTACK;
- }
-
- m_decay2_done[chan] = false;
- m_glo_level_cur[chan] = (m_release_glo[chan] & 0xff) << 4;
-
- m_dpcm_current[chan] = 0;
- m_dpcm_next[chan] = 0;
- s32 dt = m_sample_start[chan] & 0xffffff;
- if(m_sample_end[chan] & 0x80000000)
- dt = -dt;
- m_dpcm_address[chan] = ((m_sample_address[chan] & 0xffffff) << 2) - dt;
- m_dpcm_sum[chan] = 0;
-
- m_lfo_phase[chan] = 0;
+ m_streaming[chan].keyon();
+ m_filter [chan].keyon();
+ m_iir1 [chan].keyon();
+ m_envelope [chan].keyon();
+ m_lfo [chan].keyon(machine());
if(1)
- logerror("[%08d] keyon %02x %08x %08x %08x vol %04x env %04x %04x %04x pitch %04x pmod %04x\n", scount, chan, m_sample_start[chan], m_sample_end[chan], m_sample_address[chan], m_release_glo[chan], m_attack[chan], m_decay1[chan], m_decay2[chan], m_pitch[chan], m_lfo_step_pmod[chan]);
+ logerror("[%08d] keyon %02x %s\n", m_meg->m_sample_counter, chan, m_streaming[chan].describe());
}
}
m_keyon_mask = 0;
}
-u16 swp30_device::meg_prg_address_r()
+u16 swp30_device::meg_state::prg_address_r()
{
- return m_meg_program_address;
+ return m_program_address;
}
-void swp30_device::meg_prg_address_w(u16 data)
+void swp30_device::meg_state::prg_address_w(u16 data)
{
- m_meg_program_address = data;
- if(m_meg_program_address >= 0x180)
- m_meg_program_address = 0;
+ m_program_address = data;
+ if(m_program_address >= 0x180)
+ m_program_address = 0;
}
-template<int sel> u16 swp30_device::meg_prg_r()
+template<int sel> u16 swp30_device::meg_state::prg_r()
{
constexpr offs_t shift = 48-16*sel;
- return m_meg_program[m_meg_program_address] >> shift;
+ return m_program[m_program_address] >> shift;
}
-template<int sel> void swp30_device::meg_prg_w(u16 data)
+template<int sel> void swp30_device::meg_state::prg_w(u16 data)
{
constexpr offs_t shift = 48-16*sel;
constexpr u64 mask = ~(u64(0xffff) << shift);
- m_meg_program[m_meg_program_address] = (m_meg_program[m_meg_program_address] & mask) | (u64(data) << shift);
+ m_program[m_program_address] = (m_program[m_program_address] & mask) | (u64(data) << shift);
if(sel == 3) {
- if(0)
- logerror("program %03x %016x\n", m_meg_program_address, m_meg_program[m_meg_program_address]);
- m_meg_program_address ++;
- if(m_meg_program_address == 0x180)
- m_meg_program_address = 0;
+ m_program_address ++;
+ if(m_program_address == 0x180)
+ m_program_address = 0;
}
}
+template<int sel> u16 swp30_device::meg_state::map_r()
+{
+ return m_map[sel];
+}
+
+template<int sel> void swp30_device::meg_state::map_w(u16 data)
+{
+ m_map[sel] = data;
+}
+
+
+
+u16 swp30_device::meg_prg_address_r()
+{
+ return m_meg->prg_address_r();
+}
+
+void swp30_device::meg_prg_address_w(u16 data)
+{
+ m_meg->prg_address_w(data);
+}
+
+template<int sel> u16 swp30_device::meg_prg_r()
+{
+ return m_meg->prg_r<sel>();
+}
+
+template<int sel> void swp30_device::meg_prg_w(u16 data)
+{
+ m_meg->prg_w<sel>(data);
+ m_meg_program_changed = true;
+}
+
template<int sel> u16 swp30_device::meg_map_r()
{
- return m_meg_map[sel];
+ return m_meg->map_r<sel>();
}
template<int sel> void swp30_device::meg_map_w(u16 data)
{
- m_meg_map[sel] = data;
- logerror("map %x pc = %03x base = %05x size = %05x\n", sel, 12*(data >> 11), (data & 0xff) << 10, 0x400 << ((data >> 8) & 7));
+ m_meg->map_w<sel>(data);
}
-template<int sel> void swp30_device::waverom_adr_w(u16 data)
+template<int sel> void swp30_device::wave_adr_w(u16 data)
{
if(sel)
- m_waverom_adr = (m_waverom_adr & 0x0000ffff) | (data << 16);
+ m_wave_adr = (m_wave_adr & 0x0000ffff) | (data << 16);
else
- m_waverom_adr = (m_waverom_adr & 0xffff0000) | data;
+ m_wave_adr = (m_wave_adr & 0xffff0000) | data;
+ logerror("wave_adr_w %08x\n", m_wave_adr);
}
-template<int sel> u16 swp30_device::waverom_adr_r()
+template<int sel> u16 swp30_device::wave_adr_r()
{
- return m_waverom_adr >> (16*sel);
+ return m_wave_adr >> (16*sel);
}
-template<int sel> void swp30_device::waverom_mode_w(u16 data)
+template<int sel> void swp30_device::wave_size_w(u16 data)
{
if(sel)
- m_waverom_mode = (m_waverom_mode & 0x0000ffff) | (data << 16);
+ m_wave_size = (m_wave_size & 0x0000ffff) | (data << 16);
else
- m_waverom_mode = (m_waverom_mode & 0xffff0000) | data;
+ m_wave_size = (m_wave_size & 0xffff0000) | data;
+ logerror("wave_size_w %08x\n", m_wave_size);
}
-template<int sel> u16 swp30_device::waverom_mode_r()
+template<int sel> u16 swp30_device::wave_size_r()
{
- return m_waverom_mode >> (16*sel);
+ return m_wave_size >> (16*sel);
}
-void swp30_device::waverom_access_w(u16 data)
+void swp30_device::wave_access_w(u16 data)
{
- m_waverom_access = data;
+ m_wave_access = data;
+ logerror("wave_access_w %04x\n", m_wave_access);
if(data == 0x8000) {
- m_waverom_val = m_rom_cache.read_dword(m_waverom_adr);
- logerror("waverom read adr=%08x mode=%08x -> %08x\n", m_waverom_adr, m_waverom_mode, m_waverom_val);
+ m_wave_val = m_wave_cache.read_dword(m_wave_adr);
+ logerror("wave read adr=%08x size=%08x -> %08x\n", m_wave_adr, m_wave_size, m_wave_val);
}
}
-u16 swp30_device::waverom_access_r()
+u16 swp30_device::wave_access_r()
{
- return m_waverom_access;
+ return m_wave_access;
}
-u16 swp30_device::waverom_busy_r()
+u16 swp30_device::wave_busy_r()
{
- // 0 = busy reading the rom, non-0 = finished
- return 0xffff;
+ return m_wave_size ? 0 : 0xffff;
}
-template<int sel> u16 swp30_device::waverom_val_r()
+template<int sel> u16 swp30_device::wave_val_r()
{
- return m_waverom_val >> (16*sel);
+ return m_wave_val >> (16*sel);
}
-
-// AWM2 per-channel registers
-u16 swp30_device::lpf_cutoff_r(offs_t offset)
+template<int sel> void swp30_device::wave_val_w(u16 data)
{
- return m_lpf_cutoff[offset >> 6];
+ if(sel)
+ m_wave_val = (m_wave_val & 0x0000ffff) | (data << 16);
+ else
+ m_wave_val = (m_wave_val & 0xffff0000) | data;
+ if(!sel) {
+ // logerror("wave_val_w %08x\n", m_wave_val);
+ if(m_wave_access == 0x5000) {
+ m_wave_cache.write_dword(m_wave_adr, m_wave_val);
+ m_wave_adr ++;
+ m_wave_size --;
+ }
+ }
}
-void swp30_device::lpf_cutoff_w(offs_t offset, u16 data)
+// Encoding of the 27-bits sample values into 16-bits values to store
+// and retrieve from the reverb ram. Technically they're supposed to
+// be 18-bits but the two low bits are never connected to anything.
+
+u16 swp30_device::meg_state::revram_encode(u32 v)
{
- u8 chan = offset >> 6;
- if(0 && m_lpf_cutoff[chan] != data)
- logerror("chan %02x lpf cutoff %04x\n", chan, data);
- m_lpf_cutoff[chan] = data;
+ v &= 0x7ffffff;
+ u32 s = 0;
+ if(v & 0x4000000) {
+ v ^= 0x7ffffff;
+ s = 1;
+ }
+ u32 e = 15;
+ while(e && !(v & (0x400 << e)))
+ e --;
+ u32 m = e ? (v >> (e-1)) & 0x7ff : v;
+ return (e << 12) | (s << 11) | m;
}
-u16 swp30_device::lpf_cutoff_inc_r(offs_t offset)
+u32 swp30_device::meg_state::revram_decode(u16 v)
{
- return m_lpf_cutoff_inc[offset >> 6];
+ u32 e = (v >> 12) & 15;
+ u32 s = (v >> 11) & 1;
+ u32 m = v & 0x7ff;
+ u32 vb = e ? (m | 0x800) << (e-1) : m;
+ if(s)
+ vb ^= e ? (0xffffffff << (e-1)) & 0xffffffff : 0xffffffe0;
+ return vb;
}
-void swp30_device::lpf_cutoff_inc_w(offs_t offset, u16 data)
+
+void swp30_device::revram_enable_w(u16 data)
{
- u8 chan = offset >> 6;
- if(0 && m_lpf_cutoff_inc[chan] != data)
- logerror("chan %02x lpf cutoff increment %04x\n", chan, data);
- m_lpf_cutoff_inc[chan] = data;
+ logerror("revram enable = %04x\n", data);
+ m_revram_enable = data;
}
-u16 swp30_device::hpf_cutoff_r(offs_t offset)
+void swp30_device::revram_clear_w(u16 data)
{
- return m_hpf_cutoff[offset >> 6];
+ logerror("revram clear = %04x\n", data);
}
-void swp30_device::hpf_cutoff_w(offs_t offset, u16 data)
+u16 swp30_device::revram_status_r()
{
- u8 chan = offset >> 6;
- if(0 && m_hpf_cutoff[chan] != data)
- logerror("chan %02x hpf cutoff %04x\n", chan, data);
- m_hpf_cutoff[chan] = data;
+ return 0;
}
-u16 swp30_device::lpf_reso_r(offs_t offset)
+template<int sel> void swp30_device::revram_adr_w(u16 data)
{
- return m_lpf_reso[offset >> 6];
+ if(sel)
+ m_revram_adr = (m_revram_adr & 0x0000ffff) | (data << 16);
+ else
+ m_revram_adr = (m_revram_adr & 0xffff0000) | data;
}
-void swp30_device::lpf_reso_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::revram_data_w(u16 data)
{
- u8 chan = offset >> 6;
- if(0 && m_lpf_reso[chan] != data)
- logerror("chan %02x lpf resonance %04x\n", chan, data);
- m_lpf_reso[chan] = data;
+ if(sel)
+ m_revram_data = (m_revram_data & 0x0000ffff) | (data << 16);
+ else
+ m_revram_data = (m_revram_data & 0xffff0000) | data;
+
+ if(!sel)
+ m_reverb->write_word(m_revram_adr, meg_state::revram_encode(m_revram_data >> 5));
}
-template<int coef> u16 swp30_device::eq_filter_r(offs_t offset)
+template<int sel> u16 swp30_device::revram_data_r()
{
- return m_eq_filter[offset >> 6][coef];
+ if(sel)
+ m_revram_data = meg_state::revram_decode(m_reverb->read_word(m_revram_adr)) << 5;
+
+ return sel ? m_revram_data >> 16 : m_revram_data;
}
-template<int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data)
+
+
+// Streaming block trampolines
+u16 swp30_device::pitch_r(offs_t offset)
{
- m_eq_filter[offset >> 6][coef] = data;
+ return m_streaming[offset >> 6].pitch_r();
}
-template<int sel> u16 swp30_device::vol_r(offs_t offset)
+void swp30_device::pitch_w(offs_t offset, u16 data)
{
- return m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3];
+ m_streaming[offset >> 6].pitch_w(data);
}
-template<int sel> void swp30_device::vol_w(offs_t offset, u16 data)
+u16 swp30_device::start_h_r(offs_t offset)
{
- m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3] = data;
+ return m_streaming[offset >> 6].start_h_r();
}
-template<int sel> u16 swp30_device::route_r(offs_t offset)
+u16 swp30_device::start_l_r(offs_t offset)
{
- return m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3];
+ return m_streaming[offset >> 6].start_l_r();
}
-template<int sel> void swp30_device::route_w(offs_t offset, u16 data)
+void swp30_device::start_h_w(offs_t offset, u16 data)
{
- m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3] = data;
+ m_streaming[offset >> 6].start_h_w(data);
}
-u16 swp30_device::release_glo_r(offs_t offset)
+void swp30_device::start_l_w(offs_t offset, u16 data)
{
- return m_release_glo[offset >> 6];
+ m_streaming[offset >> 6].start_l_w(data);
}
-void swp30_device::release_glo_w(offs_t offset, u16 data)
+u16 swp30_device::loop_h_r(offs_t offset)
{
- u8 chan = offset >> 6;
- if(1 && m_release_glo[chan] != data)
- logerror("snd chan %02x rel/glo %02x %02x\n", chan, data >> 8, data & 0xff);
- m_release_glo[chan] = data;
- if((data & 0x8000) && m_envelope_mode[chan] != IDLE && m_envelope_mode[chan] != RELEASE)
- m_envelope_mode[chan] = RELEASE;
+ return m_streaming[offset >> 6].loop_h_r();
}
-u16 swp30_device::pitch_r(offs_t offset)
+u16 swp30_device::loop_l_r(offs_t offset)
{
- return m_pitch[offset >> 6];
+ return m_streaming[offset >> 6].loop_l_r();
}
-void swp30_device::pitch_w(offs_t offset, u16 data)
+void swp30_device::loop_h_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(0 && m_pitch[chan] != data)
- logerror("snd chan %02x pitch %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
- m_pitch[chan] = data;
+ m_streaming[offset >> 6].loop_h_w(data);
}
-u16 swp30_device::attack_r(offs_t offset)
+void swp30_device::loop_l_w(offs_t offset, u16 data)
{
- return m_attack[offset >> 6];
+ m_streaming[offset >> 6].loop_l_w(data);
}
-void swp30_device::attack_w(offs_t offset, u16 data)
+u16 swp30_device::address_h_r(offs_t offset)
{
- if(data != m_attack[offset >> 6])
- logerror("attack[%02x] = %04x\n", offset >> 6, data);
- m_attack[offset >> 6] = data;
+ return m_streaming[offset >> 6].address_h_r();
}
-u16 swp30_device::decay1_r(offs_t offset)
+u16 swp30_device::address_l_r(offs_t offset)
{
- return m_decay1[offset >> 6];
+ return m_streaming[offset >> 6].address_l_r();
}
-void swp30_device::decay1_w(offs_t offset, u16 data)
+void swp30_device::address_h_w(offs_t offset, u16 data)
{
- logerror("decay1[%02x] = %04x\n", offset >> 6, data);
- m_decay1[offset >> 6] = data;
+ m_streaming[offset >> 6].address_h_w(data);
}
-u16 swp30_device::decay2_r(offs_t offset)
+void swp30_device::address_l_w(offs_t offset, u16 data)
{
- return m_decay2[offset >> 6];
+ m_streaming[offset >> 6].address_l_w(data);
}
-void swp30_device::decay2_w(offs_t offset, u16 data)
+
+// IIR block trampolines
+u16 swp30_device::filter_1_a_r(offs_t offset)
{
- logerror("decay2[%02x] = %04x\n", offset >> 6, data);
- m_decay2[offset >> 6] = data;
+ return m_filter[offset >> 6].filter_1_a_r();
}
-u16 swp30_device::lfo_step_pmod_r(offs_t offset)
+void swp30_device::filter_1_a_w(offs_t offset, u16 data)
{
- return m_lfo_step_pmod[offset >> 6];
+ m_filter[offset >> 6].filter_1_a_w(data);
}
-void swp30_device::lfo_step_pmod_w(offs_t offset, u16 data)
+u16 swp30_device::level_1_r(offs_t offset)
{
- // logerror("lfo_step_pmod[%02x] = %04x\n", offset >> 6, data);
- m_lfo_step_pmod[offset >> 6] = data;
+ return m_filter[offset >> 6].level_1_r();
}
-u16 swp30_device::lfo_amod_r(offs_t offset)
+void swp30_device::level_1_w(offs_t offset, u16 data)
{
- return m_lfo_amod[offset >> 6];
+ m_filter[offset >> 6].level_1_w(data);
}
-void swp30_device::lfo_amod_w(offs_t offset, u16 data)
+u16 swp30_device::filter_2_a_r(offs_t offset)
{
- // logerror("lfo_amod[%02x] = %04x\n", offset >> 6, data);
- m_lfo_amod[offset >> 6] = data;
+ return m_filter[offset >> 6].filter_2_a_r();
}
-u16 swp30_device::sample_start_h_r(offs_t offset)
+void swp30_device::filter_2_a_w(offs_t offset, u16 data)
{
- return m_sample_start[offset >> 6] >> 16;
+ m_filter[offset >> 6].filter_2_a_w(data);
}
-u16 swp30_device::sample_start_l_r(offs_t offset)
+u16 swp30_device::level_2_r(offs_t offset)
{
- return m_sample_start[offset >> 6];
+ return m_filter[offset >> 6].level_2_r();
}
-void swp30_device::sample_start_h_w(offs_t offset, u16 data)
+void swp30_device::level_2_w(offs_t offset, u16 data)
{
- u8 chan = offset >> 6;
- m_sample_start[chan] = (m_sample_start[chan] & 0x0000ffff) | (data << 16);
+ m_filter[offset >> 6].level_2_w(data);
}
-void swp30_device::sample_start_l_w(offs_t offset, u16 data)
+u16 swp30_device::filter_b_r(offs_t offset)
{
- u8 chan = offset >> 6;
- m_sample_start[chan] = (m_sample_start[chan] & 0xffff0000) | data;
+ return m_filter[offset >> 6].filter_b_r();
}
-u16 swp30_device::sample_end_h_r(offs_t offset)
+void swp30_device::filter_b_w(offs_t offset, u16 data)
{
- return m_sample_end[offset >> 6] >> 16;
+ m_filter[offset >> 6].filter_b_w(data);
}
-u16 swp30_device::sample_end_l_r(offs_t offset)
+// FIR block trampolines
+template<u32 filter> u16 swp30_device::a0_r(offs_t offset)
{
- return m_sample_end[offset >> 6];
+ return m_iir1[offset >> 6].a0_r<filter>();
}
-void swp30_device::sample_end_h_w(offs_t offset, u16 data)
+template<u32 filter> u16 swp30_device::a1_r(offs_t offset)
{
- u8 chan = offset >> 6;
- m_sample_end[chan] = (m_sample_end[chan] & 0x0000ffff) | (data << 16);
+ return m_iir1[offset >> 6].a1_r<filter>();
}
-void swp30_device::sample_end_l_w(offs_t offset, u16 data)
+template<u32 filter> u16 swp30_device::b1_r(offs_t offset)
{
- u8 chan = offset >> 6;
- m_sample_end[chan] = (m_sample_end[chan] & 0xffff0000) | data;
- if(0)
- logerror("snd chan %02x post-size %02x %06x\n", chan, m_sample_end[chan] >> 24, m_sample_end[chan] & 0xffffff);
+ return m_iir1[offset >> 6].b1_r<filter>();
}
-u16 swp30_device::sample_address_h_r(offs_t offset)
+template<u32 filter> void swp30_device::a0_w(offs_t offset, u16 data)
{
- return m_sample_address[offset >> 6] >> 16;
+ m_iir1[offset >> 6].a0_w<filter>(data);
}
-u16 swp30_device::sample_address_l_r(offs_t offset)
+template<u32 filter> void swp30_device::a1_w(offs_t offset, u16 data)
{
- return m_sample_address[offset >> 6];
+ m_iir1[offset >> 6].a1_w<filter>(data);
}
-void swp30_device::sample_address_h_w(offs_t offset, u16 data)
+template<u32 filter> void swp30_device::b1_w(offs_t offset, u16 data)
{
- u8 chan = offset >> 6;
- m_sample_address[chan] = (m_sample_address[chan] & 0x0000ffff) | (data << 16);
+ m_iir1[offset >> 6].b1_w<filter>(data);
}
-void swp30_device::sample_address_l_w(offs_t offset, u16 data)
+// Envelope block trampolines
+u16 swp30_device::attack_r(offs_t offset)
{
- u8 chan = offset >> 6;
- static const char *const formats[4] = { "l16", "l12", "l8", "x8" };
- m_sample_address[chan] = (m_sample_address[chan] & 0xffff0000) | data;
- if(0)
- logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_sample_address[chan] >> 30], (m_sample_address[chan] >> 24) & 0x3f, m_sample_address[chan] & 0xffffff);
+ return m_envelope[offset >> 6].attack_r();
}
-u16 swp30_device::internal_adr_r()
+void swp30_device::attack_w(offs_t offset, u16 data)
{
- return m_internal_adr;
+ m_envelope[offset >> 6].attack_w(data);
}
-void swp30_device::internal_adr_w(u16 data)
+u16 swp30_device::decay1_r(offs_t offset)
{
- m_internal_adr = data;
+ return m_envelope[offset >> 6].decay1_r();
}
-u16 swp30_device::internal_r()
+void swp30_device::decay1_w(offs_t offset, u16 data)
{
- u8 chan = m_internal_adr & 0x3f;
- switch(m_internal_adr >> 8) {
- case 0:
- // Not certain about the two top bits though, the code seems to only care about 0/non-0
- return m_envelope_mode[chan] == IDLE ? 0xffff : ((m_envelope_mode[chan] - 1) << 14) | (m_envelope_level[chan] >> (28-14));
+ m_envelope[offset >> 6].decay1_w(data);
+}
- case 4:
- // used at 44c4
- // tests & 0x4000 only
- // logerror("read %02x.4\n", chan);
- return 0x0000;
+u16 swp30_device::decay2_r(offs_t offset)
+{
+ return m_envelope[offset >> 6].decay2_r();
+}
- case 6:
- return m_decay2_done[chan] ? 0x0000 : 0x8000;
- }
+void swp30_device::decay2_w(offs_t offset, u16 data)
+{
+ m_envelope[offset >> 6].decay2_w(data);
+}
- logerror("%s internal_r port %x channel %02x sample %d\n", machine().time().to_string(), m_internal_adr >> 8, m_internal_adr & 0x1f, scount);
- machine().debug_break();
+u16 swp30_device::release_glo_r(offs_t offset)
+{
+ return m_envelope[offset >> 6].release_glo_r();
+}
- return 0;
+void swp30_device::release_glo_w(offs_t offset, u16 data)
+{
+ m_envelope[offset >> 6].release_glo_w(data);
}
-// MEG registers
-template<int sel> u16 swp30_device::meg_const_r(offs_t offset)
+
+template<int sel> u16 swp30_device::vol_r(offs_t offset)
{
- return m_meg_const[(offset >> 6)*6 + sel];
+ return m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3];
}
-template<int sel> void swp30_device::meg_const_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::vol_w(offs_t offset, u16 data)
{
- m_meg_const[(offset >> 6)*6 + sel] = data;
+ m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3] = data;
}
-template<int sel> u16 swp30_device::meg_offset_r(offs_t offset)
+template<int sel> u16 swp30_device::route_r(offs_t offset)
{
- return m_meg_offset[(offset >> 6)*2 + sel];
+ return m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3];
}
-template<int sel> void swp30_device::meg_offset_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::route_w(offs_t offset, u16 data)
{
- m_meg_offset[(offset >> 6)*2 + sel] = data;
+ m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3] = data;
}
-template<int sel> u16 swp30_device::meg_lfo_r(offs_t offset)
+u16 swp30_device::lfo_type_step_pitch_r(offs_t offset)
{
- return m_meg_lfo[(offset >> 6)*2 + sel];
+ return m_lfo[offset >> 6].type_step_pitch_r();
}
-template<int sel> void swp30_device::meg_lfo_w(offs_t offset, u16 data)
+void swp30_device::lfo_type_step_pitch_w(offs_t offset, u16 data)
{
- int slot = (offset >> 6)*2 + sel;
- m_meg_lfo[slot] = data;
+ m_lfo[offset >> 6].type_step_pitch_w(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 };
+u16 swp30_device::lfo_amplitude_r(offs_t offset)
+{
+ return m_lfo[offset >> 6].amplitude_r();
+}
- int scale = (data >> 5) & 7;
- int step = ((data & 31) << sh[scale]) + dt[scale];
- logerror("lfo_w %02x %04x freq=%5.2f phase=%6.4f\n", slot, m_meg_lfo[slot], step * 44100.0/4194304, (data >> 8)/256.0);
+void swp30_device::lfo_amplitude_w(offs_t offset, u16 data)
+{
+ m_lfo[offset >> 6].amplitude_w(data);
}
+u16 swp30_device::internal_adr_r()
+{
+ return m_internal_adr;
+}
+void swp30_device::internal_adr_w(u16 data)
+{
+ m_internal_adr = data;
+}
-// Catch-all
+u16 swp30_device::internal_r()
+{
+ u8 chan = m_internal_adr & 0x3f;
+ switch(m_internal_adr >> 8) {
+ case 0:
+ return m_envelope[chan].status();
-static u16 rr[0x40*0x40];
+ case 4:
+ // used at 44c4
+ // tests & 0x4000 only
+ // logerror("read %02x.4\n", chan);
+ return 0x0000;
-u16 swp30_device::snd_r(offs_t offset)
-{
- if(0) {
- int chan = (offset >> 6) & 0x3f;
- int slot = offset & 0x3f;
- std::string preg = "-";
- if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
- else if(slot == 0x30 || slot == 0x31)
- preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
- else if(slot == 0x0e || slot == 0x0f)
- preg = util::string_format("ct%02x", (slot-0x0e) + 2*chan);
- else
- preg = util::string_format("%02x.%02x", chan, slot);
- logerror("snd_r [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, rr[offset]);
+ case 6:
+ return 0x8000;
}
- if(offset == 0x080f)
- return 0;
- return rr[offset];
+
+ logerror("%s internal_r port %x channel %02x sample %d\n", machine().time().to_string(), m_internal_adr >> 8, m_internal_adr & 0x1f, m_meg->m_sample_counter);
+
+ return 0;
}
-void swp30_device::snd_w(offs_t offset, u16 data)
-{
- if(rr[offset] == data)
- return;
- rr[offset] = data;
+// Catch-all
+void swp30_device::snd_w(offs_t offset, u16 data)
+{
int chan = (offset >> 6) & 0x3f;
int slot = offset & 0x3f;
- if(offset == 0x04e)
+ if(slot == 0x0b)
return;
std::string preg = "-";
@@ -1236,12 +2605,12 @@ uint32_t swp30_device::execute_max_cycles() const noexcept
void swp30_device::meg_prg_map(address_map &map)
{
- map(0x000, 0x1bf).r(FUNC(swp30_device::meg_prg_map_r));
+ map(0x000, 0x17f).r(FUNC(swp30_device::meg_prg_map_r));
}
u64 swp30_device::meg_prg_map_r(offs_t address)
{
- return m_meg_program[address];
+ return m_meg->m_program[address];
}
void swp30_device::meg_reverb_map(address_map &map)
@@ -1251,19 +2620,19 @@ void swp30_device::meg_reverb_map(address_map &map)
u16 swp30_device::swp30d_const_r(u16 address) const
{
- return m_meg_const[address];
+ return m_meg->m_const[address];
}
u16 swp30_device::swp30d_offset_r(u16 address) const
{
- return m_meg_offset[address];
+ return m_meg->m_offset[address];
}
device_memory_interface::space_config_vector swp30_device::memory_space_config() const
{
return space_config_vector {
std::make_pair(AS_PROGRAM, &m_program_config),
- std::make_pair(AS_DATA, &m_rom_config),
+ std::make_pair(AS_DATA, &m_wave_config),
std::make_pair(AS_REVERB, &m_reverb_config),
};
}
@@ -1285,426 +2654,1357 @@ void swp30_device::state_string_export(const device_state_entry &entry, std::str
{
}
-void swp30_device::sound_stream_update(sound_stream &stream)
+//======================= Mixer block ============================================
+
+// ssssss 110010 Mixer llll llll rrrr rrrr Route attenuation left/right input s
+// ssssss 110011 Mixer 0000 0000 1111 1111 Route attenuation slot 0/1 input s
+// ssssss 110100 Mixer 2222 2222 3333 3333 Route attenuation slot 2/3 input s
+// ssssss 110101 Mixer fedc ba98 7654 3210 Route mode bit 2 input s output 0-f
+// ssssss 110110 Mixer fedc ba98 7654 3210 Route mode bit 1 input s output 0-f
+// ssssss 110111 Mixer fedc ba98 7654 3210 Route mode bit 0 input s output 0-f
+// ssssss 111000 Mixer llll llll rrrr rrrr Route attenuation left/right input s+40
+// ssssss 111001 Mixer 0000 0000 1111 1111 Route attenuation slot 0/1 input s+40
+// ssssss 111010 Mixer 2222 2222 3333 3333 Route attenuation slot 2/3 input s+40
+// ssssss 111011 Mixer fedc ba98 7654 3210 Route mode bit 2 input s+40 output 0-f
+// ssssss 111100 Mixer fedc ba98 7654 3210 Route mode bit 1 input s+40 output 0-f
+// ssssss 111101 Mixer fedc ba98 7654 3210 Route mode bit 0 input s+40 output 0-f
+
+// The mixer block ensures mixing and routing in the whole system,
+// between the AM2, the MEG, and the MELI/MELO streams. The values
+// passing through are all 27-bits wide.
+
+// It has 96 mono inputs:
+// - 64 outputs of the AWM2 block, numbered 0-63
+// - 16 outputs of the MEG, numbered 64-79, which are read from MEG
+// registers m20-m2f
+// - 16 inputs (8 stereo) on the MELI ports, numbered 80-95
+
+// It has 16 stereo outputs:
+// - 8 outputs on the MELO ports, numbered 0-7
+// - 8 outputs to the MEG as 16 mono streams, numbered 8-15, which
+// are written to MEG registers m20-m2f
+
+// Six 8-bit values provide attenuations, and three 16-bits values
+// provide routing for each of the 96 inputs to each of the 16
+// outputs.
+
+// For a given source, target pair the three bits of routing target
+// are interpreted following in the following way:
+
+// 210
+// 0: 000 - Not routed
+// 1: 001 - No attenuation, add to both channels
+// 2: 010 - No attenuation, add to left channel
+// 3: 011 - No attenuation, add to right channel
+// 4: 100 - Use attenuation slot 0
+// 5: 101 - Use attenuation slot 1
+// 6: 110 - Use attenuation slot 2
+// 7: 111 - Use attenuation slot 3
+
+// The attenuation slots are built from the six attenuation values.
+// Attenuation for a given channel (left/right) and a slot (0-3) is
+// the sum of the left/right attenuation and the slot attenuation.
+// Final value is 4.4 with >= ff hardcoded to mute.
+
+// There is space in the map for channels number 96-127. The MUs
+// never touch that space, it seems that it may have (mostly negative)
+// impacts on the adc outputs (MEG registers m30-m33).
+
+
+s32 swp30_device::mixer_att(s32 sample, s32 att)
+{
+ if(att >= 0xff)
+ return 0;
+ return (sample - ((sample * (att & 0xf)) >> 4)) >> (att >> 4);
+}
+
+void swp30_device::mixer_step(const std::array<s32, 0x40> &samples_per_chan)
{
- stream.put_int_clamp(0, 0, m_meg_output[0], 32768);
- stream.put_int_clamp(1, 0, m_meg_output[1], 32768);
+ std::array<s32, 0x20> mixer_out;
+ std::fill(mixer_out.begin(), mixer_out.end(), 0);
+
+ for(int mix = 0; mix != 0x60; mix++) {
+ u64 route = (u64(m_mixer[mix].route[0]) << 32) | (u64(m_mixer[mix].route[1]) << 16) | m_mixer[mix].route[2];
+ if(route == 0)
+ continue;
+
+ s32 input;
+ if(mix < 0x40)
+ input = samples_per_chan[mix];
+ else if(mix < 0x50)
+ input = m_meg->m_m[0x20 | (mix & 0xf)];
+ else
+ input = m_meli[mix & 0xf];
+
+ if(input == 0)
+ continue;
+
+ const std::array<u16, 3> &vol = m_mixer[mix].vol;
+ for(int out = 0; out != 16; out++) {
+ int mode = ((route >> (out+32-2)) & 4) | ((route >> (out+16-1)) & 2) | ((route >> (out+0-0)) & 1);
+ switch(mode) {
+ case 0: // No routing
+ break;
+
+ case 1: // No attenuation, add to both channels
+ mixer_out[out*2 ] += input;
+ mixer_out[out*2+1] += input;
+ break;
+
+ case 2: // No attenuation, add to left channel
+ mixer_out[out*2 ] += input;
+ break;
+
+ case 3: // No attenuation, add to right channel
+ mixer_out[out*2+1] += input;
+ break;
+
+ case 4: // Use attenuation slot 0
+ mixer_out[out*2 ] += mixer_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ mixer_out[out*2+1] += mixer_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
+
+ case 5: // Use attenuation slot 1
+ mixer_out[out*2 ] += mixer_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
+ mixer_out[out*2+1] += mixer_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
+ break;
+
+ case 6: // Use attenuation slot 2
+ mixer_out[out*2 ] += mixer_att(input, (vol[0] >> 8) + (vol[2] >> 8));
+ mixer_out[out*2+1] += mixer_att(input, (vol[0] & 0xff) + (vol[2] >> 8));
+ break;
+
+ case 7: // Use attenuation slot 3
+ mixer_out[out*2 ] += mixer_att(input, (vol[0] >> 8) + (vol[2] & 0xff));
+ mixer_out[out*2+1] += mixer_att(input, (vol[0] & 0xff) + (vol[2] & 0xff));
+ break;
+ }
+ }
+ }
+ std::copy(mixer_out.begin() + 0x00, mixer_out.begin() + 0x10, m_melo.begin());
+ std::copy(mixer_out.begin() + 0x10, mixer_out.begin() + 0x20, m_meg->m_m.begin() + 0x20);
}
-void swp30_device::change_mode_attack_decay1(int chan)
+
+// MEG:
+
+// 010000 001110 MEG/Control .... .... .... .... commit LFO increments on write
+// 010000 001111 MEG/Control .... ...a aaaa aaaa program address
+// 010001 00111* MEG/Control dddd dddd dddd dddd program data 1/2
+// 010010 00111* MEG/Control dddd dddd dddd dddd program data 2/2
+
+// aaaaaa 100001 MEG/Data cccc cccc cccc cccc constant index 6*a + 0
+// aaaaaa 100011 MEG/Data cccc cccc cccc cccc constant index 6*a + 1
+// aaaaaa 100101 MEG/Data cccc cccc cccc cccc constant index 6*a + 2
+// aaaaaa 100111 MEG/Data cccc cccc cccc cccc constant index 6*a + 3
+// aaaaaa 101001 MEG/Data cccc cccc cccc cccc constant index 6*a + 4
+// aaaaaa 101011 MEG/Data cccc cccc cccc cccc constant index 6*a + 5
+// aaaaaa 11000a MEG/Data oooo oooo oooo oooo offset index a
+// aaaaaa 11111a MEG/LFO pppp ttss iiii iiii LFO index a, phase, type, shift, increment
+
+
+
+// General structure
+
+// The MEG is a DSP with 384 program steps connected to a 0x40000
+// samples ram. Instructions are 64 bits wide, and to each
+// instruction is associated a 1.15 fixed point signed value
+// (between -1 and 1), Every third instruction (pc multiple of 3)
+// can initiate a memory access to the reverb buffer which will be
+// completed two instructions later. Each of those instructions is
+// associated to a 16-bits address offset value.
+
+// The main computation unit is a MAC cell which multiplies two
+// numbers and adds a third.
+
+// Every 44100th of a second the 384 program steps are run once in
+// order (no branches) to compute everything.
+
+
+// Registers
+
+// The DSP has multiple register sets:
+
+// - 127 standard registers (bank 'r') numbered 01-7f, with the extra
+// register number 00 being hardwired to 0 (like in mips)
+
+// - 63 mmio registers (bank 'm') numbered 01-3f with 00 wired to 0,
+// which are usable as normal registers but also are used for
+// communication
+
+// - 8 t(emporary) registers
+
+// - a p register to store the result of the MAC
+
+// - an index register that is optionally added to the memory address
+
+// - two external memory data ports, one holding the value to write,
+// one holding the latest one read
+
+// The registers from r and m are 24 bits each, signed. The p
+// register (and the MAC block itself) is 42 bits, 27.15.
+
+// The m bank is used as the communication interface with the mixer
+// and adcs. Once every sample the registers m20 to m2f are sent as
+// stereo values to the eight MELO ports. Registers m30 to m33 are
+// sent to the two stereo DACs. In addition the mixer outputs to
+// the MEG are loaded in registers m20 to m2f. That bank also
+// communicates with lfos, with the prng and with the external
+// memory data ports.
+
+
+// LFO
+
+// 24 LFO registers are available. 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 top 17 bits of the counter are extracted. They are shifted
+// up by 0-3 bits (depending on s) and truncated at the top, then
+// the phase p selects a value to add to the state. That gives the
+// final 17-bits state which is shaped according to the type:
+
+// 0: sine
+// 1: triangle
+// 2: saw upwards
+// 3: saw downwards
+
+// The final 16 bits value is then shifted by 7 bits to generate the
+// final positive 23-bits value.
+
+// Writes to the MEG/LFO register changes phase, type and shift
+// immediatly but doesn't change the increment yet. Writing then to
+// the commit register sets all the delayed increment changes
+// simultaneously. This allows to keep the counters from
+// independant LFOs in sync.
+
+
+// Reverb ram access
+
+ // 8 mappings can be setup, which allow to manage rotating buffers in
+// the samples ram easily by automating masking and offset adding. The
+// register format is: pppppsss oooooooo. 'p' is the base pc/12 at
+// which the map starts to be used. '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.
+
+
+// Instructions
+
+// 33333333 33333333 22222222 22222222 11111111 11111111 00000000 00000000
+// fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 fedcba98 76543210
+// ABCDEFFF Grrrrrrr HHHmmmmm m-II--J- KKLLMMNN OOPPQRrr rrrrrSmm mmmm----
+// + + + ++++ = bits set at least once in the mu100 programs
+
+// m = low is read port, high is write port, memory register
+// r = low is read port, high is write port, regular register
+
+// A = seems to disable writing to p and nothing else? Used for lo-fi variation only
+// B = set index to p
+// C = set mem write register to p
+// D = temp register write enable
+// E = temp register write source, 0=const, 1=p
+// F = temp register number
+// G = r register write source, 0 = p, 1 = r register
+// H = m register write source (0, 1, 3 unknown, 2 lfo, 4 mem read, 5 rand, 6 p, 7 m register)
+// I = memory mode, none/read/write/read+1
+// J = add index to address on memory access
+// K = saturation mode (0 = none, 1 = 24.15, 2 = 0 to max positive 24.15, 3 = abs then max positive 24.15)
+// L = shift left writing to p
+// M = adder mode (0 = add, 1 = sub, 2 = add abs, 3 = binary and)
+// N = a selector (0=p, 1=r, 2=m, 3=0)
+// O = multiplier mode (0=off, 1=m1, 2=m1*m2, 3=m2)
+// P = mul 1st input = 0,3=constant, 1,2=temp register (note that 2 and 3 seem never used)
+// Q = expand 1st input
+// R = mul 2nd input = 0=r, 1=m
+// S = disable dithering when copying from p
+
+// The instructions are VLIW, 64-bits wide. The VLIW structure
+// means bits of the instruction are directly associated to
+// structures in the chip (muxes, etc) instead of the usual
+// instruction encoding of normal cpus.
+
+
+// Instruction execution
+
+// +--------+ +--------+ +--------+
+// | t read | | r read | | m read |
+// | port | | port | | port |
+// +----+---+ +---+----+ +---+----+
+// | r<-+ +->m
+// +-----+-------+ | +----------------------+---+
+// Constant--+ m1 selector | +-|-------------+ |
+// +-----+-------+ +------+-+----+ +-+--------+-+
+// | | m2 selector | | a selector |
+// | +-+-----------+ +-+--------+-+
+// | | | |
+// +----+---+ +-------+----+ +--------+--+ |
+// | expand +---+ multiplier +------+ adder | |
+// +--------+ +------------+ +-----+-----+ |
+// | |
+// +----+----+ |
+// | shifter | |
+// +----+----+ |
+// | |
+// +-----+------+ |
+// | saturation | |
+// +-----+------+ |
+// | |
+// +-+-+ |
+// | p +---------+
+// +-+-+
+// |
+// +----+---+
+// | dither |
+// +----+---+
+// |
+// +------+-------------+-----------+-----+---+
+// | | | | |
+// r | | Constant | | | m lfo, prng, mem read
+// | | | | | | | | |
+// +---+--+--+ +-+-----+-+ +---+---+ +-----+-----+ +-+-+--+--+
+// | r write | | t write | | index | | mem write | | m write |
+// | port | | port | | | | register | | port |
+// +---------+ +---------+ +-------+ +-----------+ +---------+
+
+
+// Read ports are always active, but for the r and m ports if the
+// register number is 0 then the result is 0.
+
+// Write ports for r and m are disabled when the register number is
+// 0. T, index and mem write have explicit enable bits (D, B and C
+// respectively). P write is disabled though the multiplier mode
+// and the A bit. P is passthrough, e.g. if it's written to and
+// read in the same instruction the read value is the written value.
+
+// The m1 selector (P) chooses between the instruction-associated
+// constant and a T register. Optionally the value can be expanded
+// from floating-point to linear (Q). The m2 selector chooses
+// between the r and m read ports (R). m1 and m2 are combined in
+// the multiplier block, which can multiply them together, or pass
+// m1, or pass m2 (4th case disables the write to p).
+
+// The a selector (N) can choose between outputting 0, m, r, p, or
+// if r or m is selected but the register number is 0, then p >> 15.
+// Then the result of the multiplier and the a selector are combined
+// in the adder, through one of four operations: add a and m, sub a
+// from m, add m to abs(a) and binary and between m and a. Then a
+// shifter left shifts the result by 0, 1, 2 or 4 bits. Finally a
+// saturation method may be applied (K) before writing to p. After
+// p a dither is optionally applied (S) before the value is
+// distributed to the other registers.
+
+
+// MEG quarter-sine "ROM"
+
+// Pretty sure it's actually a computation given how imprecise it
+// actually is (a rom would have no reason not to be perfect). But
+// guessing what calculation gives the correct pattern of
+// imprecision is not trivial.
+
+ROM_START( swp30 )
+ ROM_REGION16_LE( 0x10000, "sintab", 0 )
+ ROM_LOAD( "sin-table.bin", 0, 0x10000, CRC(4305f63c) SHA1(ab3aeacc7a6261cd77019d2f3febd2c21986bf46) )
+ROM_END
+
+const tiny_rom_entry *swp30_device::device_rom_region() const
+{
+ return ROM_NAME( swp30 );
+}
+
+const std::array<u32, 256> swp30_device::meg_state::lfo_increment_table = []() {
+ std::array<u32, 256> increments;
+ 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 };
+
+ for(u32 i=0; i != 256; i++) {
+ int scale = (i >> 5) & 7;
+ increments[i] = ((i & 31) << sh[scale]) + dt[scale];
+ }
+ return increments;
+}();
+
+swp30_disassembler::swp30_disassembler(info *inf) : m_info(inf)
{
- m_envelope_mode[chan] = DECAY1;
- m_envelope_timer[chan] = 0x8000000;
- m_envelope_on_timer[chan] = (m_decay1[chan] & 0xff) == 0;
}
-void swp30_device::change_mode_decay1_decay2(int chan)
+u32 swp30_disassembler::opcode_alignment() const
{
- m_envelope_mode[chan] = DECAY2;
- m_envelope_timer[chan] = 0x8000000;
- m_envelope_on_timer[chan] = (m_decay2[chan] & 0xff) == (m_decay1[chan] & 0xff);
+ return 1;
}
-s32 swp30_device::meg_att(s32 sample, s32 att)
+std::string swp30_disassembler::gconst(offs_t address) const
{
- if(att >= 0xff)
- return 0;
- return (sample - ((sample * (att & 0xf)) >> 4)) >> (att >> 4);
+ if(!m_info)
+ return util::string_format("c%03x", address);
+ s16 value = m_info->swp30d_const_r(address);
+ return util::string_format("%g", value / 32768.0);
+}
+std::string swp30_disassembler::goffset(offs_t address) const
+{
+ return m_info ? util::string_format("%x", m_info->swp30d_offset_r(address)) : util::string_format("of%02x", address);
}
-void swp30_device::execute_run()
+void swp30_disassembler::append(std::string &r, const std::string &e)
{
- while(m_icount >= 0) {
- if(m_meg_pc == 0) {
- scount++;
- if(0) {
- static std::array<mixer_slot, 0x80> mixer;
- if(memcmp(mixer.data(), m_mixer.data(), sizeof(mixer))) {
- mixer = m_mixer;
- for(int i=0; i != 0x20; i++) {
- logerror("mixer %02x %04x.%04x.%04x %04x.%04x.%04x %02x %04x.%04x.%04x %04x.%04x.%04x %02x %04x.%04x.%04x %04x.%04x.%04x %02x %04x.%04x.%04x %04x.%04x.%04x\n",
- 0x00 | i, m_mixer[0x00|i].vol[0], m_mixer[0x00|i].vol[1], m_mixer[0x00|i].vol[2], m_mixer[0x00|i].route[0], m_mixer[0x00|i].route[1], m_mixer[0x00|i].route[2],
- 0x20 | i, m_mixer[0x20|i].vol[0], m_mixer[0x20|i].vol[1], m_mixer[0x20|i].vol[2], m_mixer[0x20|i].route[0], m_mixer[0x20|i].route[1], m_mixer[0x20|i].route[2],
- 0x40 | i, m_mixer[0x40|i].vol[0], m_mixer[0x40|i].vol[1], m_mixer[0x40|i].vol[2], m_mixer[0x40|i].route[0], m_mixer[0x40|i].route[1], m_mixer[0x40|i].route[2],
- 0x60 | i, m_mixer[0x60|i].vol[0], m_mixer[0x60|i].vol[1], m_mixer[0x60|i].vol[2], m_mixer[0x60|i].route[0], m_mixer[0x60|i].route[1], m_mixer[0x60|i].route[2]);
- }
- }
+ if(r != "")
+ r += " ; ";
+ r += e;
+}
+
+
+u16 swp30_device::meg_state::const_r(offs_t offset)
+{
+ return m_const[offset];
+}
+
+void swp30_device::meg_state::const_w(offs_t offset, u16 data)
+{
+ m_const[offset] = data;
+}
+
+u16 swp30_device::meg_state::offset_r(offs_t offset)
+{
+ return m_offset[offset];
+}
+
+void swp30_device::meg_state::offset_w(offs_t offset, u16 data)
+{
+ m_offset[offset] = data;
+}
+
+u16 swp30_device::meg_state::lfo_r(offs_t offset)
+{
+ return m_lfo[offset];
+}
+
+void swp30_device::meg_state::lfo_w(offs_t offset, u16 data)
+{
+ m_lfo[offset] = data;
+}
+
+void swp30_device::meg_state::lfo_commit_w()
+{
+ for(int i=0; i != 24; i++)
+ m_lfo_increment[i] = lfo_increment_table[m_lfo[i] & 0xff];
+}
+
+
+template<int sel> u16 swp30_device::meg_const_r(offs_t offset)
+{
+ return m_meg->const_r((offset >> 6)*6 + sel);
+}
+
+template<int sel> void swp30_device::meg_const_w(offs_t offset, u16 data)
+{
+ // logerror("meg const[%03x] = %04x / %f\n", (offset >> 6)*6 + sel, data, s16(data) / 32768.0);
+ m_meg->const_w((offset >> 6)*6 + sel, data);
+}
+
+template<int sel> u16 swp30_device::meg_offset_r(offs_t offset)
+{
+ return m_meg->offset_r((offset >> 6)*2 + sel);
+}
+
+template<int sel> void swp30_device::meg_offset_w(offs_t offset, u16 data)
+{
+ // logerror("meg offset[%02x/%03x] = %x / %d\n", (offset >> 6)*2 + sel, 3*((offset >> 6)*2 + sel), data, data);
+ m_meg->offset_w((offset >> 6)*2 + sel, data);
+}
+
+template<int sel> u16 swp30_device::meg_lfo_r(offs_t offset)
+{
+ return m_meg->lfo_r((offset >> 6)*2 + sel);
+}
+
+template<int sel> void swp30_device::meg_lfo_w(offs_t offset, u16 data)
+{
+ if((offset >> 6)*2 + sel >= 0x18) {
+ logerror("nolfo[%02x] = %04x\n", (offset >> 6)*2 + sel, data);
+ }
+ m_meg->lfo_w((offset >> 6)*2 + sel, data);
+ m_meg_program_changed = true;
+}
+
+void swp30_device::meg_lfo_commit_w(u16)
+{
+ m_meg->lfo_commit_w();
+ m_meg_program_changed = true;
+}
+
+void swp30_device::meg_state::lfo_step()
+{
+ for(int i = 0; i != 24; i++)
+ m_lfo_counter[i] = (m_lfo_counter[i] + m_lfo_increment[i]) & 0x3fffff;
+}
+
+u32 swp30_device::meg_state::resolve_address(u16 pc, s32 offset)
+{
+ u16 key = (pc / 12) << 11;
+ for(int i=0; i != 8; i++)
+ if(i == 7 || m_map[i+1] <= m_map[i] || ((m_map[i+1] & 0xf800) > key)) {
+ u32 mask = (1 << (10+BIT(m_map[i], 8, 3))) - 1;
+ return (offset & mask) + (BIT(m_map[i], 0, 8) << 10);
+ }
+ return 0xffffffff;
+}
+
+u32 swp30_device::meg_state::get_lfo(int lfo)
+{
+ static const u32 offsets[16] = {
+ 0x00000, 0x02aaa, 0x04000, 0x05555,
+ 0x08000, 0x0aaaa, 0x0c000, 0x0d555,
+ 0x10000, 0x12aaa, 0x14000, 0x15555,
+ 0x18000, 0x1aaaa, 0x1c000, 0x1d555,
+ };
+
+ u32 base = (m_lfo_counter[lfo] >> 5);
+ base = base << ((m_lfo[lfo] >> 8) & 3);
+ base = base + offsets[m_lfo[lfo] >> 12];
+ base = base & 0x1ffff;
+ u32 res;
+ switch((m_lfo[lfo] >> 10) & 3) {
+ case 0: // sine
+ if(base < 0x8000)
+ res = m_swp->m_sintab[base];
+ else if(base < 0x10000)
+ res = m_swp->m_sintab[(base & 0x7fff) ^ 0x7fff];
+ else if(base < 0x18000)
+ res = m_swp->m_sintab[base & 0x7fff] ^ 0xffff;
+ else
+ res = m_swp->m_sintab[(base & 0x7fff) ^ 0x7fff] ^ 0xffff;
+ break;
+
+ case 1: // tri
+ res = (base + 0x8000) & 0x1ffff;
+ if(res & 0x10000)
+ res ^= 0x1ffff;
+ break;
+
+ case 2: // saw up
+ res = base >> 1;
+ break;
+
+ case 3: // saw down
+ res = (base ^ 0x1ffff) >> 1;
+ break;
+ }
+ return res << 7;
+}
+
+// Expand the first multiplier input
+
+s16 swp30_device::meg_state::m1_expand(s16 v)
+{
+ if(v < 0)
+ return 0;
+ u32 s = v >> 12;
+ v = 0x1000 | (v & 0xfff);
+ return s == 5 ? v : s < 5 ? v >> (5-s) : v << (s-5);
+}
+
+offs_t swp30_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+{
+ std::string r;
+ u64 opcode = opcodes.r64(pc);
+
+ int sm = BIT(opcode, 0x04, 6);
+ int sr = BIT(opcode, 0x0b, 7);
+ int dm = BIT(opcode, 0x27, 6);
+ int dr = BIT(opcode, 0x30, 7);
+ int t = BIT(opcode, 0x38, 3);
+
+ u32 mmode = BIT(opcode, 0x16, 2);
+ if(mmode != 0 && !BIT(opcode, 0x3f)) {
+ u32 m1t = BIT(opcode, 0x14, 2);
+ std::string mul1 = m1t == 1 || m1t == 2 ? util::string_format("t%x", BIT(opcode, 0x38, 3)) : gconst(pc);
+ if(BIT(opcode, 0x13))
+ mul1 = util::string_format("exp(%s)", mul1);
+
+ std::string mul2 = BIT(opcode, 0x12) ? sm ? util::string_format("m%02x", sm) : "0" : sr ? util::string_format("r%02x", sr) : "0";
+
+ u32 at = BIT(opcode, 0x18, 2);
+ std::string aop;
+ switch(at) {
+ case 0:
+ aop = "p";
+ break;
+
+ case 1:
+ if(sr)
+ aop = util::string_format("r%02x", sr);
+ else
+ aop = "(p >> 15)";
+ break;
+
+ case 2:
+ if(sm)
+ aop = util::string_format("m%02x", sm);
+ else
+ aop = "(p >> 15)";
+ break;
+ }
+
+ std::string op;
+ switch(mmode) {
+ case 1:
+ op = util::string_format("(%s << 8)", mul1);
+ break;
+ case 2:
+ op = util::string_format("%s * %s", mul1, mul2);
+ break;
+ case 3:
+ op = util::string_format("%s", mul2);
+ break;
+ }
+
+ std::string aopf;
+ if(at != 3)
+ switch(BIT(opcode, 0x1a, 2)) {
+ case 0:
+ aopf = util::string_format(" + %s", aop);
+ break;
+ case 1:
+ aopf = util::string_format(" - %s", aop);
+ break;
+ case 2:
+ aopf = util::string_format(" + abs(%s)", aop);
+ break;
+ case 3:
+ aopf = util::string_format(" & %s", aop);
+ break;
}
+ std::string o = op + aopf;
+ u32 shift = BIT(opcode, 0x1c, 2);
+ if(shift)
+ o = util::string_format("(%s) << %d", o, shift == 3 ? 4 : shift);
- // AWM2 synthesis
- s32 samples_per_chan[0x40];
- for(int chan = 0; chan != 0x40; chan++) {
- if(m_envelope_mode[chan] == IDLE) {
- samples_per_chan[chan] = 0;
- continue;
- }
+ u32 sat = BIT(opcode, 0x1e, 2);
+ static const char *const satmode[4] = { "=", "=s", "=_", "=a" };
- // There actually are three shapes (0000, 4000 and c000) but
- // we're not sure what they are
-
- u32 lfo_phase = m_lfo_phase[chan] >> 7;
- s32 lfo_p_phase = lfo_phase ^ (m_lfo_step_pmod[chan] & 0xc000 ? lfo_shape_centered_tri : lfo_shape_centered_saw)[lfo_phase >> 18];
- s32 lfo_a_phase = lfo_phase ^ (m_lfo_step_pmod[chan] & 0xc000 ? lfo_shape_offset_tri : lfo_shape_offset_saw )[lfo_phase >> 18];
-
- lfo_p_phase = lfo_a_phase = 0;
-
- // First, read the sample
-
- // - Find the base sample index and base address
- s32 sample_pos = m_sample_pos[chan];
- if(m_sample_end[chan] & 0x80000000)
- sample_pos = -sample_pos;
-
- s32 spos = sample_pos >> 8;
- offs_t base_address = m_sample_address[chan] & 0x1ffffff;
-
- // - Read/decompress the sample
- s16 val0, val1;
- switch(m_sample_address[chan] >> 30) {
- case 0: { // 16-bits linear
- offs_t adr = base_address + (spos >> 1);
- switch(spos & 1) {
- case 0: { // ABCDabcd ........
- u32 l0 = m_rom_cache.read_dword(adr);
- val0 = l0;
- val1 = l0 >> 16;
- break;
- }
- case 1: { // abcd.... ....ABCD
- u32 l0 = m_rom_cache.read_dword(adr);
- u32 l1 = m_rom_cache.read_dword(adr+1);
- val0 = l0 >> 16;
- val1 = l1;
- break;
- }
- }
- break;
- }
+ append(r, util::string_format("p %s %s", satmode[sat], o));
+ }
- case 1: { // 12-bits linear
- offs_t adr = base_address + (spos >> 3)*3;
- switch(spos & 7) {
- case 0: { // ..ABCabc ........ ........ ........
- u32 l0 = m_rom_cache.read_dword(adr);
- val0 = (l0 & 0x00000fff) << 4;
- val1 = (l0 & 0x00fff000) >> 8;
- break;
- }
- case 1: { // BCabc... .......A ........ ........
- u32 l0 = m_rom_cache.read_dword(adr);
- u32 l1 = m_rom_cache.read_dword(adr+1);
- val0 = (l0 & 0x00fff000) >> 8;
- val1 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
- break;
- }
- case 2: { // bc...... ....ABCa ........ ........
- u32 l0 = m_rom_cache.read_dword(adr);
- u32 l1 = m_rom_cache.read_dword(adr+1);
- val0 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
- val1 = l1 & 0x0000fff0;
- break;
- }
- case 3: { // ........ .ABCabc. ........ ........
- u32 l1 = m_rom_cache.read_dword(adr+1);
- val0 = l1 & 0x0000fff0;
- val1 = (l1 & 0x0fff0000) >> 12;
- break;
- }
- case 4: { // ........ Cabc.... ......AB ........
- u32 l1 = m_rom_cache.read_dword(adr+1);
- u32 l2 = m_rom_cache.read_dword(adr+2);
- val0 = (l1 & 0x0fff0000) >> 12;
- val1 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
- break;
- }
- case 5: { // ........ c....... ...ABCab ........
- u32 l1 = m_rom_cache.read_dword(adr+1);
- u32 l2 = m_rom_cache.read_dword(adr+2);
- val0 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
- val1 = (l2 & 0x000fff00) >> 4;
- break;
- }
- case 6: { // ........ ........ ABCabc.. ........
- u32 l2 = m_rom_cache.read_dword(adr+2);
- val0 = (l2 & 0x000fff00) >> 4;
- val1 = (l2 & 0xfff00000) >> 16;
- break;
- }
- case 7: { // ........ ........ abc..... .....ABC
- u32 l2 = m_rom_cache.read_dword(adr+2);
- u32 l3 = m_rom_cache.read_dword(adr+3);
- val0 = (l2 & 0xfff00000) >> 16;
- val1 = (l3 & 0x00000fff) << 4;
- break;
- }
- }
- break;
- }
+ if(dm) {
+ std::string dst = util::string_format("m%02x", dm);
+ switch(BIT(opcode, 0x2d, 3)) {
+ case 0: case 1: case 2: case 3:
+ append(r, util::string_format("%s = lfo.%02x", dst, pc >> 4));
+ break;
+ case 4: append(r, util::string_format("%s = mr", dst)); break;
+ case 5: append(r, util::string_format("%s = rand", dst)); break;
+ case 6: append(r, util::string_format("%s = p", dst)); break;
+ case 7: append(r, util::string_format("%s = %s", dst, sm ? util::string_format("m%02x", sm) : "0")); break;
+ }
+ }
- case 2: { // 8-bits linear
- offs_t adr = base_address + (spos >> 2);
- switch(spos & 3) {
- case 0: { // ....ABab ........
- u32 l0 = m_rom_cache.read_dword(adr);
- val0 = (l0 & 0x000000ff) << 8;
- val1 = l0 & 0x0000ff00;
- break;
- }
- case 1: { // ..ABab.. ........
- u32 l0 = m_rom_cache.read_dword(adr);
- val0 = l0 & 0x0000ff00;
- val1 = (l0 & 0x00ff0000) >> 8;
- break;
- }
- case 2: { // ABab.... ........
- u32 l0 = m_rom_cache.read_dword(adr);
- val0 = (l0 & 0x00ff0000) >> 8;
- val1 = (l0 & 0xff000000) >> 16;
- break;
- }
- case 3: { // ab...... ......AB
- u32 l0 = m_rom_cache.read_dword(adr);
- u32 l1 = m_rom_cache.read_dword(adr+1);
- val0 = (l0 & 0xff000000) >> 16;
- val1 = (l1 & 0x000000ff) << 8;
- break;
- }
- }
- break;
- }
+ if(dr) {
+ if(BIT(opcode, 0x37))
+ append(r, util::string_format("r%02x = r%02x", dr, sr));
+ else
+ append(r, util::string_format("r%02x = p", dr));
+ }
- case 3: { // 8-bits delta-pcm
- u8 offset = dpcm_offset[(m_sample_address[chan] >> 25) & 3];
- u8 scale = (m_sample_address[chan] >> 27) & 7;
- offs_t adr = m_dpcm_address[chan];
- if(m_sample_end[chan] & 0x80000000) {
- u32 target_address = (base_address << 2) + spos - 1;
- while(adr >= target_address) {
- m_dpcm_current[chan] = m_dpcm_next[chan];
- m_dpcm_sum[chan] += m_dpcm[(m_rom_cache.read_dword(adr >> 2) >> (8*(adr & 3))) & 0xff] - offset;
- s32 sample = (m_dpcm_sum[chan] << scale) >> 3;
- adr --;
- if(sample < -0x8000)
- sample = -0x8000;
- else if(sample > 0x7fff)
- sample = 0x7fff;
- m_dpcm_next[chan] = sample;
- }
- } else {
- u32 target_address = (base_address << 2) + spos + 1;
- while(adr <= target_address) {
- m_dpcm_current[chan] = m_dpcm_next[chan];
- m_dpcm_sum[chan] += m_dpcm[(m_rom_cache.read_dword(adr >> 2) >> (8*(adr & 3))) & 0xff] - offset;
- s32 sample = (m_dpcm_sum[chan] << scale) >> 3;
- // logerror("## + sample %08x %02x %d\n", adr, (m_rom_cache.read_dword(adr >> 2) >> (8*(adr & 3))) & 0xff, sample);
- adr ++;
- if(sample < -0x8000)
- sample = -0x8000;
- else if(sample > 0x7fff)
- sample = 0x7fff;
- m_dpcm_next[chan] = sample;
- }
- }
- m_dpcm_address[chan] = adr;
- val0 = m_dpcm_current[chan];
- val1 = m_dpcm_next[chan];
- break;
- }
- }
+ if(BIT(opcode, 0x3d))
+ append(r, util::string_format("mw = p"));
- s32 mul = sample_pos & 0xff;
- s32 sample = val1 * mul + val0 * (0x100 - mul);
-
-#if 0
- // Third, filter the sample
- // - missing lpf_cutoff, lpf_reso, hpf_cutoff
-
- // - eq lowpass
- s32 samp1 = (samp * m_eq_filter[chan][2] + m_sample_history[chan][0][0] * m_eq_filter[chan][1] + m_sample_history[chan][0][1] * m_eq_filter[chan][0]) >> 13;
- m_sample_history[chan][0][1] = m_sample_history[chan][0][0];
- m_sample_history[chan][0][0] = samp;
-
- // - eq highpass
- s32 samp2 = (samp1 * m_eq_filter[chan][5] + m_sample_history[chan][1][0] * m_eq_filter[chan][4] + m_sample_history[chan][1][1] * m_eq_filter[chan][3]) >> 13;
- m_sample_history[chan][1][1] = m_sample_history[chan][1][0];
- m_sample_history[chan][1][0] = samp1;
-
-#endif
-
- s32 tremolo_level = (lfo_a_phase * (m_lfo_amod[chan] & 0x1f)) << ((m_lfo_step_pmod[chan] & 0xc000) ? 3 : 2);
-
- samples_per_chan[chan] = fpapply(m_envelope_level[chan] + (m_glo_level_cur[chan] << 16) + tremolo_level, sample) >> 8;
-
- istep(m_glo_level_cur[chan], (m_release_glo[chan] & 0x00ff) << 4, 1);
-
- m_lfo_phase[chan] = (m_lfo_phase[chan] + m_global_step[0x20 + ((m_lfo_step_pmod[chan] >> 8) & 0x3f)]) & 0x7ffffff;
-
- u32 sample_increment = pitch_base[m_pitch[chan] & 0x3ff] >> (23 - ((s16(m_pitch[chan] << 2) >> 12)));
- m_sample_pos[chan] += (sample_increment * (0x800 + ((lfo_p_phase * (m_lfo_step_pmod[chan] & 0xff)) >> (m_lfo_step_pmod[chan] & 0xc000 ? 18 : 19)))) >> 11;
- if((m_sample_pos[chan] >> 8) >= (m_sample_end[chan] & 0xffffff)) {
- if(!(m_sample_end[chan] & 0xffffff))
- m_envelope_mode[chan] = IDLE;
- else {
- s32 prev = m_sample_pos[chan];
- do
- m_sample_pos[chan] -= (m_sample_end[chan] & 0xffffff) << 8;
- while((m_sample_pos[chan] >> 8) >= (m_sample_end[chan] & 0xffffff));
- if(m_sample_end[chan] & 0x80000000)
- m_dpcm_address[chan] -= (m_sample_pos[chan] >> 8) - (prev >> 8);
- else
- m_dpcm_address[chan] += (m_sample_pos[chan] >> 8) - (prev >> 8);
- m_dpcm_sum[chan] = 0;
- }
- }
+ if(BIT(opcode, 0x3e))
+ append(r, util::string_format("idx = p"));
- switch(m_envelope_mode[chan]) {
- case ATTACK:
- if(m_envelope_on_timer[chan]) {
- if(istep(m_envelope_timer[chan], 0, m_global_step[(m_attack[chan] >> 8) & 0x7f] << 1))
- change_mode_attack_decay1(chan);
- } else {
- if(fpstep(m_envelope_level[chan], 0, attack_linear_step[(m_attack[chan] >> 8) & 0x7f]))
- change_mode_attack_decay1(chan);
- }
- break;
-
- case DECAY1:
- if(m_envelope_on_timer[chan]) {
- if(istep(m_envelope_timer[chan], 0, m_global_step[(m_decay1[chan] >> 8) & 0x7f] << 1))
- change_mode_decay1_decay2(chan);
- } else if((m_decay1[chan] & 0x6000) == 0x6000) {
- if(fpstep(m_envelope_level[chan], (m_decay1[chan] & 0xff) << 20, decay_linear_step[(m_decay1[chan] >> 8) & 0x1f]))
- change_mode_decay1_decay2(chan);
- } else {
- if(fpstep(m_envelope_level[chan], (m_decay1[chan] & 0xff) << 20, m_global_step[(m_decay1[chan] >> 8) & 0x7f]))
- change_mode_decay1_decay2(chan);
- }
- break;
-
- case DECAY2:
- if(m_envelope_on_timer[chan])
- m_decay2_done[chan] = istep(m_envelope_timer[chan], 0, m_global_step[(m_decay1[chan] >> 8) & 0x7f] << 1);
- else if((m_decay2[chan] & 0x6000) == 0x6000)
- m_decay2_done[chan] = fpstep(m_envelope_level[chan], (m_decay2[chan] & 0xff) << 20, decay_linear_step[(m_decay2[chan] >> 8) & 0x1f]);
- else
- m_decay2_done[chan] = fpstep(m_envelope_level[chan], (m_decay2[chan] & 0xff) << 20, m_global_step[(m_decay2[chan] >> 8) & 0x7f]);
- break;
-
- case RELEASE:
- if((m_release_glo[chan] & 0x6000) == 0x6000) {
- if(fpstep(m_envelope_level[chan], 0x8000000, decay_linear_step[(m_release_glo[chan] >> 8) & 0x1f]))
- m_envelope_mode[chan] = IDLE;
- } else {
- if(fpstep(m_envelope_level[chan], 0x8000000, m_global_step[(m_release_glo[chan] >> 8) & 0x7f]))
- m_envelope_mode[chan] = IDLE;
- }
- break;
- }
+ if(BIT(opcode, 0x3b)) {
+ if(BIT(opcode, 0x3c))
+ append(r, util::string_format("t%x = p", t));
+ else
+ append(r, util::string_format("t%x = %s", t, gconst(pc)));
+ }
+
+ if(BIT(opcode, 0x0a))
+ append(r, "nodither");
+
+ u32 memmode = BIT(opcode, 0x24, 2);
+ if(memmode) {
+ static const char *modes[4] = { nullptr, "w", "r", "1r" };
+ append(r, util::string_format("mem_%s +%s%s", modes[memmode], goffset(pc/3), BIT(opcode, 0x21) ? "+idx" : ""));
+ }
+
+ if(opcode == 0)
+ append(r, "nop");
+
+ stream << r;
+
+ return 1 | SUPPORTED;
+}
+
+void swp30_device::meg_state::call_rand(void *ms)
+{
+ auto *ms1 = static_cast<meg_state *>(ms);
+ ms1->m_retval = ms1->m_swp->machine().rand();
+}
+
+void swp30_device::meg_state::call_revram_encode(void *ms)
+{
+ auto *ms1 = static_cast<meg_state *>(ms);
+ ms1->m_retval = revram_encode(ms1->m_retval);
+}
+
+void swp30_device::meg_state::call_revram_decode(void *ms)
+{
+ auto *ms1 = static_cast<meg_state *>(ms);
+ ms1->m_retval = revram_decode(ms1->m_retval);
+}
+
+void swp30_device::meg_state::drc(drcuml_block &block, u16 pc)
+{
+ enum {
+ L_ABS, // abs value on p
+ L_ABS2, // abs value on the mac add branch
+ L_M1_0, // m1 expansion, value is < 0
+ L_M1_DONE, // m1 expansion, end
+ L_M1_M5, // m1 expansion, exp < 5
+ L_LFO1, // lfo, first label
+ L_LFO2, // lfo, second label
+ };
+
+ UML_DEBUG(block, pc);
+
+ u64 opcodep3 = m_program[(pc + 384 - 3) % 384];
+ u64 opcodep2 = m_program[(pc + 384 - 2) % 384];
+ u64 opcode = m_program[pc];
+ u64 opcode2 = m_program[(pc + 2) % 384];
+ u32 index3 = pc % 3;
+ u32 index2 = pc % 2;
+
+ // Store the m register at the third instruction
+ int delayed_md = BIT(opcodep3, 0x27, 6);
+ if(delayed_md)
+ UML_MOV(block, mem(&m_m[delayed_md]), mem(&m_mw_value[index3]));
+
+ // Store the r register at the third instruction
+ int delayed_rd = BIT(opcodep3, 0x30, 7);
+ if(delayed_rd)
+ UML_MOV(block, mem(&m_r[delayed_rd]), mem(&m_rw_value[index3]));
+
+ // Store the index register at the third instruction
+ if(BIT(opcodep3, 0x3e))
+ UML_MOV(block, mem(&m_ram_index), mem(&m_index_value[index3]));
+
+ // Store the memw register at the second instruction
+ if(BIT(opcodep2, 0x3d))
+ UML_MOV(block, mem(&m_ram_write), mem(&m_memw_value[index2]));
+
+ // Store the memr register at the second instruction
+ if(BIT(opcodep2, 0x25))
+ UML_MOV(block, mem(&m_ram_read), mem(&m_memr_value[index2]));
+
+ int sm = BIT(opcode, 0x04, 6);
+ int sr = BIT(opcode, 0x0b, 7);
+ int dm = BIT(opcode, 0x27, 6);
+ int dr = BIT(opcode, 0x30, 7);
+ int t = BIT(opcode, 0x38, 3);
+
+ u32 mmode = BIT(opcode, 0x16, 2);
+ if(mmode != 0 && !BIT(opcode, 0x3f)) {
+ u32 m1t = BIT(opcode, 0x14, 2);
+ if(mmode != 3) {
+ // Needs m1
+ if(m1t == 1 || m1t == 2)
+ UML_DLOADS(block, I1, m_t.data(), t, SIZE_WORD, SCALE_x2);
+ else
+ UML_DLOADS(block, I1, m_const.data(), pc, SIZE_WORD, SCALE_x2);
+ if(BIT(opcode, 0x13)) {
+ // m1_expand inline
+ UML_DMOV(block, I0, 0x0000000000);
+ UML_DCMP(block, I1, I0);
+ UML_JMPc(block, COND_L, (pc << 4) | L_M1_0); // If negative, clear
+ UML_DSAR(block, I0, I1, 12); // exponent in I2
+ UML_DAND(block, I1, I1, 0xfff);
+ UML_DOR(block, I1, I1, 0x1000); // mantissa in I1
+ UML_DMOV(block, I2, 5); // compare exponent with 5
+ UML_DCMP(block, I0, I2);
+ UML_JMPc(block, COND_E, (pc << 4) | L_M1_DONE); // no shift if 5
+ UML_JMPc(block, COND_L, (pc << 4) | L_M1_M5);
+
+ UML_DSUB(block, I0, I0, I2); // shift left by exp-5 if >5
+ UML_DSHL(block, I1, I1, I0);
+ UML_JMP(block, (pc << 4) | L_M1_DONE);
+
+ UML_LABEL(block, (pc << 4) | L_M1_M5);
+ UML_DSUB(block, I0, I2, I0); // shift right by 5-exp if <5
+ UML_DSAR(block, I1, I1, I0);
+ UML_JMP(block, (pc << 4) | L_M1_DONE);
+
+ UML_LABEL(block, (pc << 4) | L_M1_0); // Clear (negative case), entered with I0=0
+ UML_DMOV(block, I1, I0);
+ UML_LABEL(block, (pc << 4) | L_M1_DONE); // Exit
}
+ }
- // Mixer
- std::array<s32, 0x10> out_samples;
- std::copy(m_meg_m.begin() + 0x20, m_meg_m.begin() + 0x30, out_samples.begin());
- std::fill(m_meg_m.begin() + 0x20, m_meg_m.begin() + 0x30, 0);
- std::fill(m_melo.begin(), m_melo.end(), 0);
- std::fill(m_meg_output.begin(), m_meg_output.end(), 0);
-
- for(int mix = 0; mix != 0x60; mix++) {
- s32 input;
- if(mix < 0x40)
- input = samples_per_chan[mix];
- else if(mix < 0x50)
- input = out_samples[mix & 0xf];
+ if(mmode != 1) {
+ // Needs m2
+ if(BIT(opcode, 0x12)) {
+ if(sm)
+ UML_DLOADS(block, I2, m_m.data(), sm, SIZE_DWORD, SCALE_x4);
else
- input = 0; // Audio input not yet supported in Mame (meli 0-7)
-
- if(input == 0)
- continue;
-
- u64 route = (u64(m_mixer[mix].route[0]) << 32) | (u64(m_mixer[mix].route[1]) << 16) | m_mixer[mix].route[2];
- const std::array<u16, 3> &vol = m_mixer[mix].vol;
-
- // It looks like this could be turned into something generic, but not 100% clear
- // routes 000100010001, 000200020002 etc seem to target the melo ports
- switch(route) {
- case 0x000000000000:
- // Incorrect, the program writes the outputs to
- // m30/m31, but right now the program doesn't run.
- m_meg_output[0] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_output[1] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x000100000000: // Used by the mu90, which does not write to 30/31
- m_meg_output[0] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_output[1] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x010000000000:
- m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x020000000000:
- m_meg_m[0x22] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x23] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x050000000400:
- m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- m_meg_m[0x24] += meg_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
- m_meg_m[0x25] += meg_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
- break;
-
-
- case 0x0d0008000400:
- m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- m_meg_m[0x24] += meg_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
- m_meg_m[0x25] += meg_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
- m_meg_m[0x26] += meg_att(input, (vol[0] >> 8) + (vol[2] >> 8));
- m_meg_m[0x27] += meg_att(input, (vol[0] & 0xff) + (vol[2] >> 8));
- break;
-
- case 0x100010001000:
- m_meg_m[0x28] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x29] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x200020002000:
- m_meg_m[0x2a] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x2b] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x400040004000:
- m_meg_m[0x2c] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x2d] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- break;
-
- case 0x4d0048004400:
- m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
- m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
- m_meg_m[0x24] += meg_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
- m_meg_m[0x25] += meg_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
- m_meg_m[0x26] += meg_att(input, (vol[0] >> 8) + (vol[2] >> 8));
- m_meg_m[0x27] += meg_att(input, (vol[0] & 0xff) + (vol[2] >> 8));
- m_meg_m[0x2c] += meg_att(input, (vol[0] >> 8) + (vol[2] & 0xff));
- m_meg_m[0x2d] += meg_att(input, (vol[0] & 0xff) + (vol[2] & 0xff));
- break;
-
- default:
- logerror("Unhandled route %012x\n", route);
- break;
- }
+ UML_DMOV(block, I2, 0);
+ } else {
+ if(sr)
+ UML_DLOADS(block, I2, m_r.data(), sr, SIZE_DWORD, SCALE_x4);
+ else
+ UML_DMOV(block, I2, 0);
}
}
+
+ switch(mmode) {
+ case 1:
+ UML_DSHL(block, I0, I1, 8+15);
+ break;
+ case 2:
+ UML_DMULSLW(block, I0, I1, I2);
+ break;
+ case 3:
+ UML_DSHL(block, I0, I2, 15);
+ break;
+ }
- debugger_instruction_hook(m_meg_pc);
- m_icount --;
- m_meg_pc ++;
- if(m_meg_pc == 0x180)
- m_meg_pc = 0;
+ bool a_is_zero = false;
+ switch(BIT(opcode, 0x18, 2)) {
+ case 0:
+ UML_DMOV(block, I1, mem(&m_p));
+ break;
+ case 1:
+ if(sr) {
+ UML_DLOADS(block, I1, m_r.data(), sr, SIZE_DWORD, SCALE_x4);
+ UML_DSHL(block, I1, I1, 15);
+ } else
+ UML_DSAR(block, I1, mem(&m_p), 15);
+ break;
+ break;
+ case 2:
+ if(sm) {
+ UML_DLOADS(block, I1, m_m.data(), sm, SIZE_DWORD, SCALE_x4);
+ UML_DSHL(block, I1, I1, 15);
+ } else
+ UML_DSAR(block, I1, mem(&m_p), 15);
+ break;
+ case 3:
+ a_is_zero = true;
+ break;
+ }
+
+ switch(BIT(opcode, 0x1a, 2)) {
+ case 0:
+ if(!a_is_zero)
+ UML_DADD(block, I0, I0, I1);
+ break;
+ case 1:
+ if(!a_is_zero)
+ UML_DSUB(block, I0, I0, I1);
+ break;
+ case 2:
+ if(!a_is_zero) {
+ UML_DMOV(block, I2, 0x0000000000);
+ UML_DCMP(block, I1, I2);
+ UML_JMPc(block, COND_GE, (pc << 4) | L_ABS2);
+ UML_DSUB(block, I1, I2, I1);
+ UML_LABEL(block, (pc << 4) | L_ABS2);
+ UML_DADD(block, I0, I0, I1);
+ }
+ break;
+ case 3:
+ if(!a_is_zero)
+ UML_DAND(block, I0, I0, I1);
+ else
+ UML_DMOV(block, I0, 0);
+ break;
+ }
+
+ // Shift and wrap to 42 bits
+ switch(BIT(opcode, 0x1c, 2)) {
+ case 0:
+ UML_DSHL(block, I0, I0, 0 + (64-42));
+ break;
+ case 1:
+ UML_DSHL(block, I0, I0, 1 + (64-42));
+ break;
+ case 2:
+ UML_DSHL(block, I0, I0, 2 + (64-42));
+ break;
+ case 3:
+ UML_DSHL(block, I0, I0, 4 + (64-42));
+ break;
+ }
+ UML_DSAR(block, I0, I0, (64-42));
+
+ // Clamp/saturate as requested
+ switch(BIT(opcode, 0x1e, 2)) {
+ case 0:
+ break;
+ case 1:
+ UML_DMOV(block, I1, -0x4000000000);
+ UML_DCMP(block, I0, I1);
+ UML_DMOVc(block, COND_L, I0, I1);
+ UML_DMOV(block, I1, 0x3fffffffff);
+ UML_DCMP(block, I0, I1);
+ UML_DMOVc(block, COND_G, I0, I1);
+ break;
+ case 2:
+ UML_DMOV(block, I1, 0x0000000000);
+ UML_DCMP(block, I0, I1);
+ UML_DMOVc(block, COND_L, I0, I1);
+ UML_DMOV(block, I1, 0x3fffffffff);
+ UML_DCMP(block, I0, I1);
+ UML_DMOVc(block, COND_G, I0, I1);
+ break;
+ case 3:
+ UML_DMOV(block, I1, 0x0000000000);
+ UML_DCMP(block, I0, I1);
+ UML_JMPc(block, COND_GE, (pc << 4) | L_ABS);
+ UML_DSUB(block, I0, I1, I0);
+ UML_LABEL(block, (pc << 4) | L_ABS);
+ UML_DMOV(block, I1, 0x3fffffffff);
+ UML_DCMP(block, I0, I1);
+ UML_DMOVc(block, COND_G, I0, I1);
+ break;
+ }
+
+ UML_DMOV(block, mem(&m_p), I0);
+ }
+
+ if(dm) {
+ switch(BIT(opcode, 0x2d, 3)) {
+ case 0: case 1: case 2: case 3: {
+ int lfo = pc >> 4;
+ u16 info = m_lfo[lfo];
+ UML_MOV(block, I0, mem(&m_lfo_counter[lfo]));
+ UML_SAR(block, I0, I0, 5);
+ if(info & 0xf300) {
+ static const u32 offsets[16] = {
+ 0x00000, 0x02aaa, 0x04000, 0x05555,
+ 0x08000, 0x0aaaa, 0x0c000, 0x0d555,
+ 0x10000, 0x12aaa, 0x14000, 0x15555,
+ 0x18000, 0x1aaaa, 0x1c000, 0x1d555,
+ };
+
+ if(info & 0x0300)
+ UML_SHL(block, I0, I0, BIT(info, 8, 2));
+ if(info & 0xf000)
+ UML_ADD(block, I0, I0, offsets[BIT(info, 12, 4)]);
+ UML_AND(block, I0, I0, 0x1ffff);
+ }
+
+ switch((info >> 10) & 3) {
+ case 0:
+ UML_MOV(block, I1, I0);
+ UML_AND(block, I0, I0, 0x7fff);
+ UML_TEST(block, I1, 0x8000);
+ UML_JMPc(block, COND_Z, (pc << 4) | L_LFO1);
+ UML_XOR(block, I0, I0, 0x7fff);
+ UML_LABEL(block, (pc << 4) | L_LFO1);
+ UML_LOAD(block, I0, m_swp->m_sintab, I0, SIZE_WORD, SCALE_x2);
+ UML_TEST(block, I1, 0x10000);
+ UML_JMPc(block, COND_Z, (pc << 4) | L_LFO2);
+ UML_XOR(block, I0, I0, 0xffff);
+ UML_LABEL(block, (pc << 4) | L_LFO2);
+ break;
+ case 1:
+ UML_ADD(block, I0, I0, 0x8000);
+ UML_TEST(block, I0, 0x10000);
+ UML_JMPc(block, COND_Z, (pc << 4) | L_LFO1);
+ UML_XOR(block, I0, I0, 0xffff);
+ UML_LABEL(block, (pc << 4) | L_LFO1);
+ UML_AND(block, I0, I0, 0xffff);
+ break;
+ case 2:
+ UML_SAR(block, I0, I0, 1);
+ break;
+ case 3:
+ UML_XOR(block, I0, I0, 0x1ffff);
+ UML_SAR(block, I0, I0, 1);
+ break;
+ }
+ UML_SHL(block, mem(&m_mw_value[index3]), I0, 7);
+ break;
+ }
+ case 4:
+ UML_MOV(block, mem(&m_mw_value[index3]), mem(&m_ram_read));
+ break;
+ case 5:
+ UML_CALLC(block, call_rand, this);
+ UML_SHL(block, I0, mem(&m_retval), 8);
+ UML_SAR(block, mem(&m_mw_value[index3]), I0, 8);
+ break;
+ case 6:
+ UML_DMOV(block, I0, mem(&m_p));
+ if(!BIT(opcode, 0x0a)) {
+ UML_CALLC(block, call_rand, this);
+ UML_AND(block, I1, mem(&m_retval), 0x07e0);
+ UML_DADD(block, I0, I0, I1);
+ }
+ UML_DSAR(block, I0, I0, (15-8));
+ UML_SAR(block, mem(&m_mw_value[index3]), I0, 8);
+ break;
+ case 7:
+ UML_MOV(block, mem(&m_mw_value[index3]), mem(&m_m[sm]));
+ break;
+ }
+ }
+
+ if(dr) {
+ if(BIT(opcode, 0x37)) {
+ if(sr)
+ UML_DMOV(block, mem(&m_rw_value[index3]), mem(&m_r[sr]));
+ else
+ UML_DMOV(block, mem(&m_rw_value[index3]), 0);
+ } else {
+ UML_DMOV(block, I0, mem(&m_p));
+ if(!BIT(opcode, 0x0a)) {
+ UML_CALLC(block, call_rand, this);
+ UML_AND(block, I1, mem(&m_retval), 0x07e0);
+ UML_DADD(block, I0, I0, I1);
+ }
+ UML_DSAR(block, I0, I0, (15-8));
+ UML_SAR(block, mem(&m_rw_value[index3]), I0, 8);
+ }
+ }
+
+ // T write lookups the p value from two cycles before
+ if(BIT(opcode, 0x3b, 1)) {
+ if(BIT(opcode, 0x3c))
+ UML_LOADS(block, I0, m_t_value.data(), index2, SIZE_WORD, SCALE_x2);
+ else
+ UML_LOADS(block, I0, m_const.data(), pc, SIZE_WORD, SCALE_x2);
+ UML_STORE(block, m_t.data(), t, I0, SIZE_WORD, SCALE_x2);
+ }
+ if(BIT(opcode2, 0x3b, 2) == 3) {
+ if(BIT(opcode, 0x3e)) {
+ UML_DSAR(block, I0, mem(&m_p), 8);
+ UML_AND(block, I0, I0, 0x7fff);
+ UML_STORE(block, m_t_value.data(), index2, I0, SIZE_WORD, SCALE_x2);
+ } else {
+ UML_DSAR(block, I0, mem(&m_p), 15+8);
+ UML_STORE(block, m_t_value.data(), index2, I0, SIZE_WORD, SCALE_x2);
+ }
+ }
+
+ if(BIT(opcode, 0x3d)) {
+ UML_DSAR(block, I0, mem(&m_p), 15);
+ UML_MOV(block, mem(&m_memw_value[index2]), I0);
+ }
+
+ if(BIT(opcode, 0x3e)) {
+ UML_DSAR(block, I0, mem(&m_p), 15+8);
+ UML_STORE(block, m_index_value.data(), index3, I0, SIZE_WORD, SCALE_x2);
+ }
+
+ // Memory access
+ int amem = BIT(opcode, 0x24, 2);
+ if(amem) {
+ u16 key = (pc / 12) << 11;
+ int bank;
+ for(bank=0; bank != 7; bank++)
+ if(m_map[bank+1] <= m_map[bank] || ((m_map[bank+1] & 0xf800) > key))
+ break;
+ u16 mapr = m_map[bank];
+ u32 mask = (1 << (10+BIT(mapr, 8, 3))) - 1;
+ u32 offset = BIT(mapr, 0, 8) << 10;
+ if(amem == 3)
+ offset ++;
+ UML_LOAD(block, I0, m_offset.data(), pc/3, SIZE_WORD, SCALE_x2);
+ UML_SUB(block, I0, I0, mem(&m_sample_counter));
+ UML_ADD(block, I0, I0, offset);
+ if(BIT(opcode, 0x21))
+ UML_ADD(block, I0, I0, mem(&m_ram_index));
+ UML_AND(block, I0, I0, mask);
+ if(amem == 1) {
+ UML_MOV(block, mem(&m_retval), mem(&m_ram_write));
+ UML_CALLC(block, call_revram_encode, this);
+ UML_MOV(block, I1, mem(&m_retval));
+ UML_WRITE(block, I0, I1, SIZE_WORD, memory_space(swp30_device::AS_REVERB));
+ } else {
+ UML_READ(block, I1, I0, SIZE_WORD, memory_space(swp30_device::AS_REVERB));
+ UML_MOV(block, mem(&m_retval), I1);
+ UML_CALLC(block, call_revram_decode, this);
+ UML_MOV(block, mem(&m_memr_value[index2]), mem(&m_retval));
+ }
}
}
+
+void swp30_device::meg_state::step()
+{
+ m_swp->debugger_instruction_hook(m_pc);
+
+ // All register writes are delayed by 3 cycles, probably a pipeline
+ // Register 0 in both banks are wired to value 0
+ if(m_mw_reg[m_delay_3])
+ m_m[m_mw_reg[m_delay_3]] = m_mw_value[m_delay_3];
+
+ if(m_rw_reg[m_delay_3])
+ m_r[m_rw_reg[m_delay_3]] = m_rw_value[m_delay_3];
+
+ // Index is similarly delayed
+ if(m_index_active[m_delay_3])
+ m_ram_index = m_index_value[m_delay_3];
+
+ // Memory read and write ports are delayed by 2 cycles
+ if(m_memw_active[m_delay_2]) {
+ m_ram_write = m_memw_value[m_delay_2];
+ m_memw_active[m_delay_2] = false;
+ }
+ if(m_memr_active[m_delay_2]) {
+ m_ram_read = m_memr_value[m_delay_2];
+ m_memr_active[m_delay_2] = false;
+ }
+
+ u64 opcode = m_swp->m_program_cache.read_qword(m_pc);
+
+ int sm = BIT(opcode, 0x04, 6);
+ int sr = BIT(opcode, 0x0b, 7);
+ int dm = BIT(opcode, 0x27, 6);
+ int dr = BIT(opcode, 0x30, 7);
+ int t = BIT(opcode, 0x38, 3);
+
+ u32 mmode = BIT(opcode, 0x16, 2);
+ if(mmode != 0) {
+ u32 m1t = BIT(opcode, 0x14, 2);
+ s64 m1 = m1t == 1 || m1t == 2 ? m_t[t] : m_const[m_pc];
+ if(BIT(opcode, 0x13))
+ m1 = m1_expand(m1);
+
+ s64 m2 = BIT(opcode, 0x12) ? m_m[sm] : m_r[sr];
+
+ s64 m;
+ switch(mmode) {
+ case 1:
+ m = m1 << (8+15);
+ break;
+ case 2:
+ m = m1 * m2;
+ break;
+ case 3:
+ m = m2 << 15;
+ break;
+ }
+
+ s64 a;
+ switch(BIT(opcode, 0x18, 2)) {
+ case 0: a = m_p; break;
+ case 1: a = sr ? s64(m_r[sr]) << 15 : m_p >> 15; break;
+ case 2: a = sm ? s64(m_m[sm]) << 15 : m_p >> 15; break;
+ case 3: a = 0; break;
+ }
+
+ s64 r;
+ switch(BIT(opcode, 0x1a, 2)) {
+ case 0:
+ r = m + a;
+ break;
+ case 1:
+ r = m - a;
+ break;
+ case 2:
+ r = m + (a < 0 ? -a : a);
+ break;
+ case 3:
+ r = m & a;
+ break;
+ }
+
+ int shift = BIT(opcode, 0x1c, 2);
+ if(shift)
+ r <<= shift == 3 ? 4 : shift;
+
+ // wrap at 42 bits (27.15)
+ if(r & 0x0000020000000000)
+ r |= 0xfffffe0000000000;
+ else
+ r &= 0x000001ffffffffff;
+
+ switch(BIT(opcode, 0x1e, 2)) {
+ case 0:
+ break;
+ case 1:
+ r = std::clamp(r, -0x4000000000, 0x3fffffffff);
+ break;
+ case 2:
+ r = std::clamp(r, s64(0), 0x3fffffffff);
+ break;
+ case 3:
+ r = std::min(r < 0 ? -r : r, 0x3fffffffff);
+ break;
+ }
+
+ m_p = r;
+ }
+
+ m_mw_reg[m_delay_3] = dm;
+ if(dm) {
+ u32 v;
+ switch(BIT(opcode, 0x2d, 3)) {
+ case 0: case 1: case 2: case 3:
+ v = get_lfo(m_pc >> 4);
+ break;
+ case 4: v = m_ram_read; break;
+ case 5: v = m_swp->machine().rand() & 0xffffff; if(v & 0x00800000) v |= 0xff000000; break;
+ case 6: {
+ s64 p = m_p;
+ if(!BIT(opcode, 0x0a))
+ p += m_swp->machine().rand() & 0x07e0;
+ v = (p >> 15) & 0xffffff;
+ if(v & 0x00800000)
+ v |= 0xff000000;
+ break;
+ }
+ case 7: v = m_m[sm]; break;
+ }
+ m_mw_value[m_delay_3] = v;
+ }
+
+ m_rw_reg[m_delay_3] = dr;
+ if(dr) {
+ u32 v;
+ if(BIT(opcode, 0x37))
+ v = m_r[sr];
+ else {
+ s64 p = m_p;
+ if(!BIT(opcode, 0x0a))
+ p += m_swp->machine().rand() & 0x07e0;
+ v = (p >> 15) & 0xffffff;
+ if(v & 0x00800000)
+ v |= 0xff000000;
+ }
+ m_rw_value[m_delay_3] = v;
+ }
+
+ if(BIT(opcode, 0x3d)) {
+ m_memw_active[m_delay_2] = true;
+ m_memw_value[m_delay_2] = m_p >> 15;
+ } else
+ m_memw_active[m_delay_2] = false;
+
+ if(BIT(opcode, 0x3e)) {
+ m_index_active[m_delay_3] = true;
+ m_index_value[m_delay_3] = m_p >> (15+8);
+ } else
+ m_index_active[m_delay_3] = false;
+
+ // T write lookups the p value from two cycles before, but which
+ // bits depends on the presence of index setting
+ if(BIT(opcode, 0x3b, 1)) {
+ if(BIT(opcode, 0x3c))
+ m_t[t] = m_t_value[m_delay_2];
+ else
+ m_t[t] = m_const[m_pc];
+ }
+ m_t_value[m_delay_2] = BIT(opcode, 0x3e) ? (m_p >> 8) & 0x7fff : m_p >> (15+8);
+
+ // Memory access
+ switch(BIT(opcode, 0x24, 2)) {
+ case 1: {
+ u32 address = resolve_address(m_pc, m_offset[m_pc/3] + (BIT(opcode, 0x21) ? m_ram_index : 0) - m_sample_counter);
+ if(address != 0xffffffff)
+ m_swp->m_reverb_cache.write_word(address, revram_encode(m_ram_write));
+ break;
+ }
+ case 2: {
+ u32 address = resolve_address(m_pc, m_offset[m_pc/3] + (BIT(opcode, 0x21) ? m_ram_index : 0) - m_sample_counter);
+ if(address != 0xffffffff) {
+ u16 val = m_swp->m_reverb_cache.read_word(address);
+ m_memr_value[m_delay_2] = revram_decode(val);
+ m_memr_active[m_delay_2] = true;
+ }
+ break;
+ }
+ case 3: {
+ u32 address = resolve_address(m_pc, m_offset[m_pc/3] + (BIT(opcode, 0x21) ? m_ram_index : 0) - m_sample_counter + 1);
+ if(address != 0xffffffff) {
+ u16 val = m_swp->m_reverb_cache.read_word(address);
+ m_memr_value[m_delay_2] = revram_decode(val);
+ m_memr_active[m_delay_2] = true;
+ }
+ break;
+ }
+ }
+
+ m_delay_3 ++;
+ if(m_delay_3 == 3)
+ m_delay_3 = 0;
+
+ m_delay_2 ++;
+ if(m_delay_2 == 2)
+ m_delay_2 = 0;
+
+ m_pc ++;
+ m_icount --;
+
+ if(m_pc == 0x180)
+ m_pc = 0;
+}
+
+void swp30_device::execute_run()
+{
+ if(m_meg_drc_active) {
+ if(m_meg_program_changed) {
+ m_drcuml->reset();
+ m_meg_program_changed = false;
+ drcuml_block &block(m_drcuml->begin_block(16384));
+ UML_HANDLE(block, *m_meg_drc_entry);
+ for(u16 pc = 0; pc != 384; pc++)
+ m_meg->drc(block, pc);
+ UML_EXIT(block, 0);
+ block.end();
+ }
+
+ while(m_meg->m_icount > 0) {
+ sample_step();
+ m_drcuml->execute(*m_meg_drc_entry);
+ m_meg->m_icount -= 384;
+ }
+
+ } else {
+ m_meg_program_changed = false;
+
+ while(m_meg->m_icount > 0) {
+ if(m_meg->m_pc == 0)
+ sample_step();
+ m_meg->step();
+ }
+ }
+}
+
+void swp30_device::adc_step()
+{
+ for(int i=0; i != 4; i++)
+ m_adc[i] = std::clamp(m_meg->m_m[0x30 + i] >> 4, -0x20000, +0x1ffff);
+}
+
+void swp30_device::sample_step()
+{
+ std::array<s32, 0x40> samples_per_chan;
+ awm2_step(samples_per_chan);
+ adc_step();
+ mixer_step(samples_per_chan);
+ m_meg->lfo_step();
+ m_meg->m_sample_counter ++;
+}
+
+void swp30_device::sound_stream_update(sound_stream &stream)
+{
+ if(&stream == m_output_stream) {
+ for(int i=0; i != 4; i++)
+ stream.put_int_clamp(i, 0, m_adc[i], 1<<17);
+ for(int i=0; i != 16; i++)
+ stream.put_int_clamp(i+4, 0, m_melo[i], 1<<26);
+ } else
+ for(int i=0; i != 16; i++)
+ m_meli[i] = stream.get(i, 0) * (1<<26);
+}
+
+DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip")
+
diff --git a/src/devices/sound/swp30.h b/src/devices/sound/swp30.h
index eb34fb13eec..466a758802a 100644
--- a/src/devices/sound/swp30.h
+++ b/src/devices/sound/swp30.h
@@ -8,7 +8,31 @@
#pragma once
-#include "swp30d.h"
+#include "cpu/drcfe.h"
+#include "cpu/drcuml.h"
+
+class swp30_disassembler : public util::disasm_interface
+{
+public:
+ class info {
+ public:
+ virtual u16 swp30d_const_r(u16 address) const = 0;
+ virtual u16 swp30d_offset_r(u16 address) const = 0;
+ };
+
+ swp30_disassembler(info *inf = nullptr);
+
+ virtual u32 opcode_alignment() const override;
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+
+private:
+ info *m_info;
+
+ std::string gconst(offs_t address) const;
+ std::string goffset(offs_t address) const;
+
+ static inline void append(std::string &r, const std::string &e);
+};
class swp30_device : public cpu_device, public device_sound_interface, public swp30_disassembler::info
{
@@ -32,14 +56,192 @@ protected:
virtual void state_export(const device_state_entry &entry) override;
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
private:
- enum {
- IDLE,
- ATTACK,
- DECAY1,
- DECAY2,
- RELEASE
+ struct streaming_block {
+ static const std::array<u16, 0x400> pitch_base;
+ static const std::array<s16, 256> dpcm_expand;
+ static const std::array<std::array<s16, 0x800>, 2> interpolation_table;
+ static const std::array<s32, 8> max_value;
+
+ s32 m_start;
+ s32 m_loop;
+ u32 m_address;
+ u16 m_pitch;
+
+ s32 m_loop_size;
+ s32 m_pos;
+ s32 m_pos_dec;
+ s16 m_dpcm_s0, m_dpcm_s1, m_dpcm_s2, m_dpcm_s3;
+ u32 m_dpcm_pos;
+ s32 m_dpcm_delta;
+
+ bool m_first, m_finetune_active, m_done;
+ s16 m_last;
+
+ void clear();
+ void keyon();
+ std::pair<s16, bool> step(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s32 pitch_lfo);
+
+ void start_h_w(u16 data);
+ void start_l_w(u16 data);
+ void loop_h_w(u16 data);
+ void loop_l_w(u16 data);
+ void address_h_w(u16 data);
+ void address_l_w(u16 data);
+ void pitch_w(u16 data);
+
+ u16 start_h_r() const;
+ u16 start_l_r() const;
+ u16 loop_h_r() const;
+ u16 loop_l_r() const;
+ u16 address_h_r() const;
+ u16 address_l_r() const;
+ u16 pitch_r() const;
+
+ void read_16(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3);
+ void read_12(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3);
+ void read_8(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3);
+ void read_8c(memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1, s16 &val2, s16 &val3);
+
+ void dpcm_step(u8 input);
+ void update_loop_size();
+ void scale_and_clamp_one(s16 &val, u32 scale, s32 limit);
+ void scale_and_clamp(s16 &val0, s16 &val1, s16 &val2, s16 &val3);
+
+ std::string describe() const;
+ };
+
+ struct filter_block {
+ u16 m_filter_1_a;
+ u16 m_level_1;
+ u16 m_filter_2_a;
+ u16 m_level_2;
+ u16 m_filter_b;
+
+ s32 m_filter_1_p1;
+ s32 m_filter_2_p1;
+ s32 m_filter_p2;
+
+ s32 m_filter_1_x1;
+ s32 m_filter_1_x2;
+ s32 m_filter_1_y0;
+ s32 m_filter_1_y1;
+
+ s32 m_filter_1_h;
+ s32 m_filter_1_b;
+ s32 m_filter_1_l;
+ s32 m_filter_1_n;
+
+ s32 m_filter_2_x1;
+ s32 m_filter_2_x2;
+ s32 m_filter_2_y0;
+ s32 m_filter_2_y1;
+
+ s32 m_filter_2_h;
+ s32 m_filter_2_b;
+ s32 m_filter_2_l;
+ s32 m_filter_2_n;
+
+ void clear();
+ void keyon();
+ s32 step(s16 input);
+
+ void f1_chamberlin_step(s16 input);
+
+ static s32 volume_apply(u8 level, s32 sample);
+
+ u16 filter_1_a_r() const;
+ u16 level_1_r() const;
+ u16 filter_2_a_r() const;
+ u16 level_2_r() const;
+ u16 filter_b_r() const;
+
+ void filter_1_a_w(u16 data);
+ void level_1_w(u16 data);
+ void filter_2_a_w(u16 data);
+ void level_2_w(u16 data);
+ void filter_b_w(u16 data);
+ };
+
+ struct iir1_block {
+ s16 m_a[2][2];
+ s16 m_b[2];
+ s32 m_hx[2], m_hy[2];
+
+ void clear();
+ void keyon();
+ s32 step(s32 input);
+
+ template<u32 filter> u16 a0_r() const;
+ template<u32 filter> u16 a1_r() const;
+ template<u32 filter> u16 b1_r() const;
+ template<u32 filter> void a0_w(u16 data);
+ template<u32 filter> void a1_w(u16 data);
+ template<u32 filter> void b1_w(u16 data);
+ };
+
+ struct envelope_block {
+ // Hardware values readable through internal read on variable 0, do not change
+ enum {
+ ATTACK = 0,
+ DECAY1 = 1,
+ DECAY2 = 2,
+ RELEASE = 3
+ };
+
+ u16 m_attack;
+ u16 m_decay1;
+ u16 m_decay2;
+ u16 m_release_glo;
+ s32 m_envelope_level;
+ u8 m_envelope_mode;
+
+ void clear();
+ void keyon();
+ u16 status() const;
+ bool active() const;
+ u16 step(u32 sample_counter);
+ void trigger_release();
+
+ void attack_w(u16 data);
+ void decay1_w(u16 data);
+ void decay2_w(u16 data);
+ void release_glo_w(u16 data);
+
+ u16 attack_r() const;
+ u16 decay1_r() const;
+ u16 decay2_r() const;
+ u16 release_glo_r() const;
+
+ u16 level_step(u32 speed, u32 sample_counter);
+ };
+
+ struct lfo_block {
+ u32 m_counter;
+ u16 m_state;
+
+ u16 m_r_type_step_pitch;
+ u16 m_r_amplitude;
+
+ u8 m_type;
+ u8 m_step;
+ u8 m_amplitude;
+ bool m_pitch_mode;
+ s8 m_pitch_depth;
+
+ void clear();
+ void keyon(running_machine &machine);
+ u16 get_amplitude() const;
+ s16 get_pitch() const;
+ void step(running_machine &machine);
+
+ void type_step_pitch_w(u16 data);
+ void amplitude_w(u16 data);
+
+ u16 type_step_pitch_r();
+ u16 amplitude_r();
};
struct mixer_slot {
@@ -47,89 +249,156 @@ private:
std::array<u16, 3> route;
};
- address_space_config m_program_config, m_rom_config, m_reverb_config;
- address_space *m_program, *m_rom, *m_reverb;
- memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache m_rom_cache;
+ struct meg_state {
+ public:
+ static const std::array<u32, 256> lfo_increment_table;
+
+ swp30_device *m_swp;
+ std::array<u64, 0x180> m_program;
+ std::array<s16, 0x180> m_const;
+ std::array<u16, 0x80> m_offset;
+ std::array<u16, 0x18> m_lfo;
+ std::array<u32, 0x18> m_lfo_increment;
+ std::array<u32, 0x18> m_lfo_counter;
+ std::array<u16, 8> m_map;
+
+ std::array<s32, 0x40> m_m;
+ std::array<s32, 0x80> m_r;
+ std::array<s16, 8> m_t;
+ s64 m_p;
+
+ std::array<s32, 3> m_mw_value;
+ std::array<u8, 3> m_mw_reg;
+ std::array<s32, 3> m_rw_value;
+ std::array<u8, 3> m_rw_reg;
+ std::array<s32, 3> m_index_value;
+ std::array<bool, 3> m_index_active;
+ std::array<s32, 3> m_memw_value;
+ std::array<s32, 3> m_memr_value;
+ std::array<s16, 2> m_t_value;
+ std::array<bool, 3> m_memw_active;
+ std::array<bool, 3> m_memr_active;
+ u32 m_delay_3;
+ u32 m_delay_2;
+
+ u32 m_ram_read, m_ram_write;
+ s32 m_ram_index;
+ u32 m_sample_counter;
+ u16 m_program_address;
+ u16 m_pc;
+ int m_icount;
+ u32 m_retval;
+
+ u16 prg_address_r();
+ void prg_address_w(u16 data);
+ template<int sel> u16 prg_r();
+ template<int sel> void prg_w(u16 data);
+ template<int sel> u16 map_r();
+ template<int sel> void map_w(u16 data);
+ u16 const_r(offs_t offset);
+ void const_w(offs_t offset, u16 data);
+ u16 offset_r(offs_t offset);
+ void offset_w(offs_t offset, u16 data);
+ u16 lfo_r(offs_t offset);
+ void lfo_w(offs_t offset, u16 data);
+ void lfo_commit_w();
+ void lfo_step();
+ u32 get_lfo(int lfo);
+ u32 resolve_address(u16 pc, s32 offset);
+
+ static u16 revram_encode(u32 v);
+ static u32 revram_decode(u16 v);
+ static s16 m1_expand(s16 v);
+
+ static void call_rand(void *ms);
+ static void call_revram_encode(void *ms);
+ static void call_revram_decode(void *ms);
+
+ void step();
+ void drc(drcuml_block &block, u16 pc);
+ void reset();
+ };
+
+ address_space_config m_program_config, m_wave_config, m_reverb_config;
+ address_space *m_program, *m_wave, *m_reverb;
+
+ required_region_ptr<u16> m_sintab;
+
+ memory_access< 9, 3, -3, ENDIANNESS_LITTLE>::cache m_program_cache;
+ memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache m_wave_cache;
memory_access<18, 1, -1, ENDIANNESS_LITTLE>::cache m_reverb_cache;
- sound_stream *m_stream;
-
- static const std::array<s32, 0x80> attack_linear_step;
- static const std::array<s32, 0x20> decay_linear_step;
- static const std::array<s32, 16> panmap;
- static const std::array<u32, 0x400> pitch_base;
- std::array<s32, 0x80> m_global_step;
- std::array<s16, 0x100> m_dpcm;
-
- static const std::array<u32, 4> lfo_shape_centered_saw;
- static const std::array<u32, 4> lfo_shape_centered_tri;
- static const std::array<u32, 4> lfo_shape_offset_saw;
- static const std::array<u32, 4> lfo_shape_offset_tri;
- static const std::array<u8, 4> dpcm_offset;
-
- std::array<s32, 0x40> m_sample_start;
- std::array<s32, 0x40> m_sample_end;
- std::array<u32, 0x40> m_sample_address;
- std::array<u16, 0x40> m_pitch;
-
- std::array<u16, 0x40> m_attack;
- std::array<u16, 0x40> m_decay1;
- std::array<u16, 0x40> m_decay2;
- std::array<u16, 0x40> m_release_glo;
- std::array<u16, 0x40> m_lfo_step_pmod;
- std::array<u16, 0x40> m_lfo_amod;
-
- std::array<u32, 0x40> m_lfo_phase;
- std::array<s32, 0x40> m_sample_pos;
- std::array<s32, 0x40> m_envelope_level;
- std::array<s32, 0x40> m_envelope_timer;
- std::array<bool, 0x40> m_envelope_on_timer;
- std::array<bool, 0x40> m_decay2_done;
- std::array<u8, 0x40> m_envelope_mode;
- std::array<s32, 0x40> m_glo_level_cur;
- std::array<s32, 0x40> m_pan_l;
- std::array<s32, 0x40> m_pan_r;
- std::array<s16, 0x40> m_dpcm_current;
- std::array<s16, 0x40> m_dpcm_next;
- std::array<u32, 0x40> m_dpcm_address;
- std::array<s32, 0x40> m_dpcm_sum;
-
- std::array<u64, 0x180> m_meg_program;
- std::array<s16, 0x180> m_meg_const;
- std::array<u16, 0x80> m_meg_offset;
- std::array<u16, 0x18> m_meg_lfo;
- std::array<u16, 8> m_meg_map;
+ sound_stream *m_input_stream, *m_output_stream;
+
+ std::array<streaming_block, 0x40> m_streaming;
+ std::array<filter_block, 0x40> m_filter;
+ std::array<iir1_block, 0x40> m_iir1;
+ std::array<envelope_block, 0x40> m_envelope;
+ std::array<lfo_block, 0x40> m_lfo;
std::array<mixer_slot, 0x80> m_mixer;
- std::array<s32, 0x40> m_meg_m;
std::array<s32, 0x10> m_melo;
- std::array<s32, 2> m_meg_output;
+ std::array<s32, 0x10> m_meli;
+ std::array<s32, 4> m_adc;
- s32 m_sample_history[0x40][2][2];
+ meg_state *m_meg;
+ drc_cache m_drccache;
+ std::unique_ptr<drcuml_state> m_drcuml;
+ uml::code_handle *m_meg_drc_entry;
+ uml::code_handle *m_meg_drc_nocode;
+ uml::code_handle *m_meg_drc_out_of_cycles;
- u32 m_waverom_adr, m_waverom_mode, m_waverom_val;
- u16 m_waverom_access;
+ bool m_meg_program_changed;
+ bool m_meg_drc_active;
- u16 m_lpf_cutoff[0x40], m_lpf_cutoff_inc[0x40], m_lpf_reso[0x40], m_hpf_cutoff[0x40];
- s16 m_eq_filter[0x40][6];
+ u32 m_sample_counter;
+ u32 m_wave_adr, m_wave_size, m_wave_val, m_revram_adr, m_revram_data;
+ u16 m_wave_access, m_revram_enable;
u64 m_keyon_mask;
u16 m_internal_adr;
- u16 m_meg_program_address;
- u16 m_meg_pc;
- int m_icount;
-
- // AWM2 per-channel registers
- u16 lpf_cutoff_r(offs_t offset);
- void lpf_cutoff_w(offs_t offset, u16 data);
- u16 lpf_cutoff_inc_r(offs_t offset);
- void lpf_cutoff_inc_w(offs_t offset, u16 data);
- u16 hpf_cutoff_r(offs_t offset);
- void hpf_cutoff_w(offs_t offset, u16 data);
- u16 lpf_reso_r(offs_t offset);
- void lpf_reso_w(offs_t offset, u16 data);
+ // Streaming block trampolines
+ u16 start_h_r(offs_t offset);
+ u16 start_l_r(offs_t offset);
+ void start_h_w(offs_t offset, u16 data);
+ void start_l_w(offs_t offset, u16 data);
+ u16 loop_h_r(offs_t offset);
+ u16 loop_l_r(offs_t offset);
+ void loop_h_w(offs_t offset, u16 data);
+ void loop_l_w(offs_t offset, u16 data);
+ u16 address_h_r(offs_t offset);
+ u16 address_l_r(offs_t offset);
+ void address_h_w(offs_t offset, u16 data);
+ void address_l_w(offs_t offset, u16 data);
+ u16 pitch_r(offs_t offset);
+ void pitch_w(offs_t offset, u16 data);
+
+
+ // Filter block trampolines
+ u16 filter_1_a_r(offs_t offset);
+ u16 level_1_r(offs_t offset);
+ u16 filter_2_a_r(offs_t offset);
+ u16 level_2_r(offs_t offset);
+ u16 filter_b_r(offs_t offset);
+
+ void filter_1_a_w(offs_t offset, u16 data);
+ void level_1_w(offs_t offset, u16 data);
+ void filter_2_a_w(offs_t offset, u16 data);
+ void level_2_w(offs_t offset, u16 data);
+ void filter_b_w(offs_t offset, u16 data);
+
+
+ // IIR1 block trampolines
+ template<u32 filter> u16 a0_r(offs_t offset);
+ template<u32 filter> u16 a1_r(offs_t offset);
+ template<u32 filter> u16 b1_r(offs_t offset);
+ template<u32 filter> void a0_w(offs_t offset, u16 data);
+ template<u32 filter> void a1_w(offs_t offset, u16 data);
+ template<u32 filter> void b1_w(offs_t offset, u16 data);
+
+ // Envelope block trampolines
u16 attack_r(offs_t offset);
void attack_w(offs_t offset, u16 data);
u16 decay1_r(offs_t offset);
@@ -138,35 +407,11 @@ private:
void decay2_w(offs_t offset, u16 data);
u16 release_glo_r(offs_t offset);
void release_glo_w(offs_t offset, u16 data);
- template<int coef> u16 eq_filter_r(offs_t offset);
- template<int coef> void eq_filter_w(offs_t offset, u16 data);
-
- u16 sample_start_h_r(offs_t offset);
- u16 sample_start_l_r(offs_t offset);
- void sample_start_h_w(offs_t offset, u16 data);
- void sample_start_l_w(offs_t offset, u16 data);
- u16 sample_end_h_r(offs_t offset);
- u16 sample_end_l_r(offs_t offset);
- void sample_end_h_w(offs_t offset, u16 data);
- void sample_end_l_w(offs_t offset, u16 data);
- u16 sample_address_h_r(offs_t offset);
- u16 sample_address_l_r(offs_t offset);
- void sample_address_h_w(offs_t offset, u16 data);
- void sample_address_l_w(offs_t offset, u16 data);
- u16 pitch_r(offs_t offset);
- void pitch_w(offs_t offset, u16 data);
- u16 pan_r(offs_t offset);
- void pan_w(offs_t offset, u16 data);
- u16 dry_rev_r(offs_t offset);
- void dry_rev_w(offs_t offset, u16 data);
- u16 cho_var_r(offs_t offset);
- void cho_var_w(offs_t offset, u16 data);
-
- void lfo_step_pmod_w(offs_t offset, u16 data);
- u16 lfo_step_pmod_r(offs_t offset);
- void lfo_amod_w(offs_t offset, u16 data);
- u16 lfo_amod_r(offs_t offset);
+ void lfo_amplitude_w(offs_t offset, u16 data);
+ u16 lfo_amplitude_r(offs_t offset);
+ void lfo_type_step_pitch_w(offs_t offset, u16 data);
+ u16 lfo_type_step_pitch_r(offs_t offset);
u16 internal_adr_r();
void internal_adr_w(u16 data);
@@ -176,16 +421,9 @@ private:
template<int sel> u16 vol_r(offs_t offset);
template<int sel> void vol_w(offs_t offset, u16 data);
- // Envelope control
- void change_mode_attack_decay1(int chan);
- void change_mode_decay1_decay2(int chan);
- static bool istep(s32 &value, s32 limit, s32 step);
- static bool fpstep(s32 &value, s32 limit, s32 step);
- static s32 fpadd(s32 value, s32 step);
- static s32 fpsub(s32 value, s32 step);
- static s32 fpapply(s32 value, s32 sample);
- static s32 lpffpapply(s32 value, s32 sample);
- static s32 meg_att(s32 sample, s32 att);
+ static s32 volume_apply(s32 level, s32 sample);
+
+ static s32 mixer_att(s32 sample, s32 att);
// Control registers
template<int sel> u16 keyon_mask_r();
@@ -194,18 +432,26 @@ private:
void keyon_w(u16);
u16 meg_prg_address_r();
void meg_prg_address_w(u16 data);
+ void meg_lfo_commit_w(u16);
template<int sel> u16 meg_prg_r();
template<int sel> void meg_prg_w(u16 data);
template<int sel> u16 meg_map_r();
template<int sel> void meg_map_w(u16 data);
- template<int sel> void waverom_adr_w(u16 data);
- template<int sel> u16 waverom_adr_r();
- template<int sel> void waverom_mode_w(u16 data);
- template<int sel> u16 waverom_mode_r();
- void waverom_access_w(u16 data);
- u16 waverom_access_r();
- u16 waverom_busy_r();
- template<int sel> u16 waverom_val_r();
+ template<int sel> void wave_adr_w(u16 data);
+ template<int sel> u16 wave_adr_r();
+ template<int sel> void wave_size_w(u16 data);
+ template<int sel> u16 wave_size_r();
+ void wave_access_w(u16 data);
+ u16 wave_access_r();
+ u16 wave_busy_r();
+ template<int sel> u16 wave_val_r();
+ template<int sel> void wave_val_w(u16 data);
+ void revram_enable_w(u16 data);
+ void revram_clear_w(u16 data);
+ u16 revram_status_r();
+ template<int sel> void revram_adr_w(u16 data);
+ template<int sel> void revram_data_w(u16 data);
+ template<int sel> u16 revram_data_r();
// MEG registers
template<int sel> u16 meg_const_r(offs_t offset);
@@ -220,6 +466,7 @@ private:
void meg_reverb_map(address_map &map) ATTR_COLD;
+
virtual u16 swp30d_const_r(u16 address) const override;
virtual u16 swp30d_offset_r(u16 address) const override;
@@ -235,6 +482,11 @@ private:
int slot = 0x40*(idx >> 1) | 0xe | (idx & 1);
return map(slot*2, slot*2+1);
}
+
+ void awm2_step(std::array<s32, 0x40> &samples_per_chan);
+ void mixer_step(const std::array<s32, 0x40> &samples_per_chan);
+ void adc_step();
+ void sample_step();
};
DECLARE_DEVICE_TYPE(SWP30, swp30_device)
diff --git a/src/devices/sound/swp30d.cpp b/src/devices/sound/swp30d.cpp
deleted file mode 100644
index df76426c462..00000000000
--- a/src/devices/sound/swp30d.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
-
-// Yamaha SWP30 - Multiple effects generator subpart
-//
-// Audio dsp dedicated to effects generation
-//
-// Disassembler
-
-// Known problems with the mu100 bios0 uploaded programs:
-//
-// At address 94 & 97, there's a missing r78 = p / r79 = p
-// respectively. Those are places where is both a rnn = p and mnn = p
-//
-// insertion1 rooms, at addresses cd, cf, d0, d2, d4, d5, dc, e3, e7
-// the r slots have data but are not used. No idea how they should be
-// used.
-//
-// Same for insertion2 rooms, addresses fd+
-//
-// Insertion 1/2 aural have a bunch of instructions (c7-ce, f7-fe)
-// using r02-r09 and r28/r3a which are not understood.
-//
-
-// Amplitude lfo is missing something. For instance variation delay
-// m34 is loaded at 120 and used at 13c, but the way it is used is not
-// decoded. In practice, we do not yet have a way to multiply two
-// registers together, only with a constant.
-
-// insertion phaser
-
-// y0 = x1 + (y1 - x0) * lfo
-
-// x = r4a
-// y = r4b
-
-// 14f:
-// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000
-// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210
-
-// XLB--ttt -rrrrrrr -xlmmmmm m-MM---- -P----** *--T-Arr rrrrrsmm mmmm----
-// 0000000003c25000 ........ ........ ........ ........ ......11 11....1. .1.1.... ........ p = 0 * x1
-// 0100000004925800 .......1 ........ ........ ........ .....1.. 1..1..1. .1.11... ........ p += 0 * y1
-// 014b000044925400 .......1 .1..1.11 ........ ........ .1...1.. 1..1..1. .1.1.1.. ........ p += 0 * x0 ; y0 = p
-
-// 0000000000805800 ........ ........ ........ ........ ........ 1....... .1.11... ........ p += fp48a * r0b;
-// 0000000003822800 ........ ........ ........ ........ ......11 1.....1. ..1.1... ........ p = f1_2_c1 * r45;
-
-// Important detail: the writes to register (rnn and mnn) seem to be
-// delayed by 2 cycles. That makes the filter computation work out.
-
-
-#include "emu.h"
-#include "swp30d.h"
-
-swp30_disassembler::swp30_disassembler(info *inf) : m_info(inf)
-{
-}
-
-u32 swp30_disassembler::opcode_alignment() const
-{
- return 1;
-}
-
-std::string swp30_disassembler::gconst(offs_t address) const
-{
- if(!m_info)
- return util::string_format("c%03x", address);
- s16 value = m_info->swp30d_const_r(address);
- return util::string_format("%g", value / 16384.0);
-}
-
-std::string swp30_disassembler::goffset(offs_t address) const
-{
- return m_info ? util::string_format("%x", m_info->swp30d_offset_r(address)) : util::string_format("of%02x", address);
-}
-
-u32 swp30_disassembler::b(u64 opc, u32 start, u32 count)
-{
- return (opc >> start) & ((1 << count) - 1);
-}
-
-void swp30_disassembler::append(std::string &r, const std::string &e)
-{
- if(r != "")
- r += " ; ";
- r += e;
-}
-
-// 33333333 33333333 22222222 22222222 11111111 11111111 00000000 00000000
-// fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 fedcba98 76543210
-
-// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000
-// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210
-// XLB----- -rrrrrrr -xlmmmmm m-MM---- -P----** *c---Arr rrrrrsmm mmmm----
-
-// m = low is read port, high is write port, memory register
-// r = low is read port, high is write port, rotating register
-
-// X = used for lo-fi variation only
-// L = lfo read for memory offset
-// * = compute mul + mode
-// A = mul input = m or r
-// s = substract to p instead of adding
-// P = P sent for register write
-// B = register write to mbuf
-// M = memory mode, none/read/write/read+1
-// x = 0 register write to memory, 1 to rotating
-// l = 0 = lfo sent for register write
-
-offs_t swp30_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
-{
- u64 opc = opcodes.r64(pc);
-
- std::string r;
-
- if(b(opc, 62, 1))
- append(r, util::string_format("m%02x = lfo.%02x", b(opc, 4, 6), pc >> 4));
-
- if(b(opc, 23, 1))
- switch(b(opc, 24, 2)) {
- case 0:
- if(b(opc, 18, 1))
- append(r, util::string_format("p %c= %s*m%02x", b(opc, 10, 1) ? '-' : '+', gconst(pc), b(opc, 4, 6)));
- else
- append(r, util::string_format("p %c= %s*r%02x", b(opc, 10, 1) ? '-' : '+', gconst(pc), b(opc, 11, 7)));
- break;
- case 1:
- append(r, util::string_format("p %c= %s*(r%02x+m%02x)", b(opc, 10, 1) ? '-' : '+', gconst(pc), b(opc, 11, 7), b(opc, 4, 6)));
- break;
- case 2:
- append(r, util::string_format("p = %s*(r%02x+m%02x)", gconst(pc), b(opc, 11, 7), b(opc, 4, 6)));
- break;
- case 3:
- if(b(opc, 18, 1))
- append(r, util::string_format("p = %s*m%02x", gconst(pc), b(opc, 4, 6)));
- else
- append(r, util::string_format("p = %s*r%02x", gconst(pc), b(opc, 11, 7)));
- break;
- }
-
- if(b(opc, 62, 1))
- append(r, util::string_format("idx = p*4000"));
-
- if(b(opc, 30, 1) == 1 && b(opc, 61, 1) == 1)
- append(r, util::string_format("mw = p"));
-
- if(b(opc, 30, 1) == 1 && b(opc, 61, 1) == 0 && b(opc, 46, 1) == 1 && b(opc, 62, 1) == 0)
- append(r, util::string_format("m%02x = p", b(opc, 39, 6)));
-
- if(b(opc, 30, 1) == 1 && b(opc, 61, 1) == 0 && b(opc, 46, 1) == 0)
- append(r, util::string_format("r%02x = p", b(opc, 48, 7)));
-
- if(b(opc, 30, 1) == 0 && b(opc, 45, 2) == 2)
- append(r, util::string_format("m%02x = lfo.%02x", b(opc, 39, 6), pc >> 4));
-
- if(b(opc, 30, 1) == 0 && b(opc, 45, 2) == 3)
- append(r, util::string_format("m%02x = m%02x", b(opc, 39, 6), b(opc, 4, 6)));
-
- if(b(opc, 46, 2) == 2)
- append(r, util::string_format("m%02x = mr", b(opc, 39, 6)));
-
- u32 memmode = b(opc, 36, 2);
- if(memmode) {
- static const char *modes[4] = { nullptr, "w", "r", "1r" };
- append(r, util::string_format("mem_%s %x +%s", modes[memmode], b(opc, 33, 3), goffset(pc/3)));
- }
-
- stream << r;
-
- return 1 | SUPPORTED;
-}
diff --git a/src/devices/sound/swp30d.h b/src/devices/sound/swp30d.h
deleted file mode 100644
index d6e13bb4c8d..00000000000
--- a/src/devices/sound/swp30d.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
-
-// Yamaha SWP30 - Multiple effects generator subpart
-//
-// Audio dsp dedicated to effects generation
-//
-// Disassembler
-
-#ifndef MAME_SOUND_SWP30D_H
-#define MAME_SOUND_SWP30D_H
-
-#pragma once
-
-class swp30_disassembler : public util::disasm_interface
-{
-public:
- class info {
- public:
- virtual u16 swp30d_const_r(u16 address) const = 0;
- virtual u16 swp30d_offset_r(u16 address) const = 0;
- };
-
- swp30_disassembler(info *inf = nullptr);
-
- virtual u32 opcode_alignment() const override;
- virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
-
-private:
- info *m_info;
-
- std::string gconst(offs_t address) const;
- std::string goffset(offs_t address) const;
-
- static inline u32 b(u64 opc, u32 start, u32 count);
- static inline void append(std::string &r, const std::string &e);
-};
-
-#endif // MAME_SOUND_SWP30D_H
diff --git a/src/emu/dirom.h b/src/emu/dirom.h
index 72d844f8b0e..40c31050e1c 100644
--- a/src/emu/dirom.h
+++ b/src/emu/dirom.h
@@ -33,6 +33,8 @@ public:
void set_rom_bank(int bank);
protected:
+ typename memory_access<AddrWidth, DataWidth, AddrShift, Endian>::cache m_rom_cache;
+
virtual void rom_bank_pre_change() { }
virtual void rom_bank_post_change() { }
virtual space_config_vector memory_space_config() const override;
@@ -43,7 +45,6 @@ private:
optional_memory_region m_rom_region;
optional_address_space m_rom_space;
address_space_config m_rom_config;
- typename memory_access<AddrWidth, DataWidth, AddrShift, Endian>::cache m_rom_cache;
memory_bank_creator m_bank;
u32 m_cur_bank, m_bank_count;
diff --git a/src/mame/yamaha/ymmu100.cpp b/src/mame/yamaha/ymmu100.cpp
index 1dc406bc95c..65a1d200b5c 100644
--- a/src/mame/yamaha/ymmu100.cpp
+++ b/src/mame/yamaha/ymmu100.cpp
@@ -544,7 +544,7 @@ ROM_END
ROM_START( mu100b )
ROM_REGION( 0x200000, "maincpu", 0 )
// MU-100B v1.08 (Nov. 28, 1997)
- ROM_LOAD16_WORD_SWAP( "xu50710.bin", 0x000000, 0x200000, CRC(4b10bd27) SHA1(12d7c6e1bce7974b34916e1bfa5057ab55867476) )
+ ROM_LOAD16_WORD_SWAP( "xu50710.ic11", 0x000000, 0x200000, CRC(4b10bd27) SHA1(12d7c6e1bce7974b34916e1bfa5057ab55867476) )
ROM_REGION32_LE( 0x1800000, "swp30", ROMREGION_ERASE00 )
ROM_LOAD32_WORD( "sx518b0.ic34", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
@@ -558,6 +558,6 @@ ROM_END
} // anonymous namespace
-SYST( 1997, mu100, 0, 0, mu100, mu100, mu100_state, empty_init, "Yamaha", "MU100", MACHINE_NOT_WORKING )
-SYST( 1997, mu100r, mu100, 0, mu100r, mu100, mu100r_state, empty_init, "Yamaha", "MU100 Rackable version", MACHINE_NOT_WORKING )
-SYST( 1998, mu100b, mu100, 0, mu100b, mu100, mu100_state, empty_init, "Yamaha", "MU100 Screenless version", MACHINE_NOT_WORKING )
+SYST( 1997, mu100, 0, 0, mu100, mu100, mu100_state, empty_init, "Yamaha", "MU100", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
+SYST( 1997, mu100r, mu100, 0, mu100r, mu100, mu100r_state, empty_init, "Yamaha", "MU100 Rackable version", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
+SYST( 1998, mu100b, mu100, 0, mu100b, mu100, mu100_state, empty_init, "Yamaha", "MU100 Screenless version", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
diff --git a/src/mame/yamaha/ymmu128.cpp b/src/mame/yamaha/ymmu128.cpp
index a8ae8340a0a..96889a0f8c8 100644
--- a/src/mame/yamaha/ymmu128.cpp
+++ b/src/mame/yamaha/ymmu128.cpp
@@ -241,4 +241,4 @@ ROM_END
} // anonymous namespace
-CONS( 1998, mu128, 0, 0, mu128, mu128, mu128_state, empty_init, "Yamaha", "MU128", MACHINE_NOT_WORKING )
+CONS( 1998, mu128, 0, 0, mu128, mu128, mu128_state, empty_init, "Yamaha", "MU128", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
diff --git a/src/mame/yamaha/ymmu2000.cpp b/src/mame/yamaha/ymmu2000.cpp
index 4ef450f7940..6d58de837bf 100644
--- a/src/mame/yamaha/ymmu2000.cpp
+++ b/src/mame/yamaha/ymmu2000.cpp
@@ -113,7 +113,7 @@ protected:
required_device<sci4_device> m_sci;
required_device<mulcd_device> m_lcd;
required_shared_ptr<u32> m_ram;
- required_device_array<plg1x0_connector, 3> m_ext;
+ optional_device_array<plg1x0_connector, 3> m_ext;
required_ioport_array<6> m_ioports;
u16 m_pe;
@@ -318,7 +318,8 @@ void mu2000_state::map_2000(address_map &map)
void mu500_state::swp30_map(address_map &map)
{
- map(0x000000, 0x7fffff).rom().region("swp30", 0);
+ map(0x0000000, 0x07fffff).rom().region("swp30", 0);
+ map(0x1000000, 0x1100000).ram().share("samples");
}
void mu500_state::mu500(machine_config &config)
@@ -367,30 +368,65 @@ void mu500_state::mu500(machine_config &config)
midiout_slot(mdout);
m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
+ config.set_default_layout(layout_mu128);
+}
+
+void mu1000_state::mu1000(machine_config &config)
+{
+ mu500(config);
+ m_maincpu->set_addrmap(AS_PROGRAM, &mu1000_state::map_1000);
+
+ SWP30(config, m_swp30s);
+ m_swp30s->set_addrmap(AS_DATA, &mu1000_state::swp30_map);
+
+ m_swp30s->add_route( 0+4, m_swp30m, 1.0, 0);
+ m_swp30s->add_route( 1+4, m_swp30m, 1.0, 1);
+ m_swp30s->add_route( 2+4, m_swp30m, 1.0, 2);
+ m_swp30s->add_route( 3+4, m_swp30m, 1.0, 3);
+ m_swp30s->add_route( 4+4, m_swp30m, 1.0, 4);
+ m_swp30s->add_route( 5+4, m_swp30m, 1.0, 5);
+ m_swp30s->add_route( 6+4, m_swp30m, 1.0, 6);
+ m_swp30s->add_route( 7+4, m_swp30m, 1.0, 7);
+ m_swp30s->add_route( 8+4, m_swp30m, 1.0, 8);
+ m_swp30s->add_route( 9+4, m_swp30m, 1.0, 9);
+ m_swp30s->add_route(10+4, m_swp30m, 1.0, 10);
+ m_swp30s->add_route(11+4, m_swp30m, 1.0, 11);
+ m_swp30s->add_route(12+4, m_swp30m, 1.0, 12);
+ m_swp30s->add_route(13+4, m_swp30m, 1.0, 13);
+
+ m_swp30m->add_route( 0+4, m_swp30s, 1.0, 0);
+ m_swp30m->add_route( 1+4, m_swp30s, 1.0, 1);
+ m_swp30m->add_route( 2+4, m_swp30s, 1.0, 2);
+ m_swp30m->add_route( 3+4, m_swp30s, 1.0, 3);
+ m_swp30m->add_route( 4+4, m_swp30s, 1.0, 4);
+ m_swp30m->add_route( 5+4, m_swp30s, 1.0, 5);
+ m_swp30m->add_route( 8+4, m_swp30s, 1.0, 8);
+ m_swp30m->add_route( 9+4, m_swp30s, 1.0, 9);
+
+
PLG1X0_CONNECTOR(config, m_ext[0], plg1x0_intf, nullptr);
m_ext[0]->midi_tx().set(m_sci, FUNC(sci4_device::rx_w<30>));
m_sci->write_tx<30>().set(m_ext[0], FUNC(plg1x0_connector::midi_rx));
+ m_swp30s->add_route(14+4, m_ext[0], 1.0, 0);
+ m_swp30s->add_route(15+4, m_ext[0], 1.0, 1);
+ m_ext[0]->add_route(0, m_swp30s, 1.0, 10);
+ m_ext[0]->add_route(0, m_swp30s, 1.0, 11);
PLG1X0_CONNECTOR(config, m_ext[1], plg1x0_intf, nullptr);
m_ext[1]->midi_tx().set(m_sci, FUNC(sci4_device::rx_w<31>));
m_sci->write_tx<31>().set(m_ext[1], FUNC(plg1x0_connector::midi_rx));
+ m_swp30s->add_route(14+4, m_ext[1], 1.0, 0);
+ m_swp30s->add_route(15+4, m_ext[1], 1.0, 1);
+ m_ext[1]->add_route(0, m_swp30s, 1.0, 12);
+ m_ext[1]->add_route(0, m_swp30s, 1.0, 13);
PLG1X0_CONNECTOR(config, m_ext[2], plg1x0_intf, nullptr);
m_ext[2]->midi_tx().set(m_sci, FUNC(sci4_device::rx_w<32>));
m_sci->write_tx<32>().set(m_ext[2], FUNC(plg1x0_connector::midi_rx));
-
- config.set_default_layout(layout_mu128);
-}
-
-void mu1000_state::mu1000(machine_config &config)
-{
- mu500(config);
- m_maincpu->set_addrmap(AS_PROGRAM, &mu1000_state::map_1000);
-
- SWP30(config, m_swp30s);
- m_swp30s->set_addrmap(AS_DATA, &mu1000_state::swp30_map);
- m_swp30s->add_route(0, "speaker", 1.0, 0);
- m_swp30s->add_route(1, "speaker", 1.0, 1);
+ m_swp30s->add_route(14+4, m_ext[2], 1.0, 0);
+ m_swp30s->add_route(15+4, m_ext[2], 1.0, 1);
+ m_ext[2]->add_route(0, m_swp30s, 1.0, 14);
+ m_ext[2]->add_route(0, m_swp30s, 1.0, 15);
}
void mu2000_state::mu2000(machine_config &config)
@@ -451,6 +487,6 @@ ROM_END
} // anonymous namespace
-CONS( 2000, mu500, 0, 0, mu500, mu500, mu500_state, empty_init, "Yamaha", "MU500", MACHINE_NOT_WORKING )
-CONS( 1999, mu1000, mu500, 0, mu1000, mu500, mu1000_state, empty_init, "Yamaha", "MU1000", MACHINE_NOT_WORKING )
-CONS( 1999, mu2000, mu500, 0, mu2000, mu500, mu2000_state, empty_init, "Yamaha", "MU2000", MACHINE_NOT_WORKING )
+CONS( 2000, mu500, 0, 0, mu500, mu500, mu500_state, empty_init, "Yamaha", "MU500", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
+CONS( 1999, mu1000, mu500, 0, mu1000, mu500, mu1000_state, empty_init, "Yamaha", "MU1000", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
+CONS( 1999, mu2000, mu500, 0, mu2000, mu500, mu2000_state, empty_init, "Yamaha", "MU2000", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
diff --git a/src/mame/yamaha/ymmu90.cpp b/src/mame/yamaha/ymmu90.cpp
index 8b125c9cf4c..fef134a5fda 100644
--- a/src/mame/yamaha/ymmu90.cpp
+++ b/src/mame/yamaha/ymmu90.cpp
@@ -268,5 +268,5 @@ ROM_END
} // anonymous namespace
-SYST( 1996, mu90, 0, 0, mu90, mu90, mu90_state, empty_init, "Yamaha", "MU90", MACHINE_NOT_WORKING )
-SYST( 2005, mu90b, mu90, 0, mu90b, mu90, mu90_state, empty_init, "Yamaha", "MU90B", MACHINE_NOT_WORKING )
+SYST( 1996, mu90, 0, 0, mu90, mu90, mu90_state, empty_init, "Yamaha", "MU90", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )
+SYST( 2005, mu90b, mu90, 0, mu90b, mu90, mu90_state, empty_init, "Yamaha", "MU90B", MACHINE_SUPPORTS_SAVE|MACHINE_NOT_WORKING )