summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2018-11-14 14:15:16 +0100
committer Olivier Galibert <galibert@pobox.com>2018-12-06 15:02:17 +0100
commit8f534764f968b989488dec085dd607c51f125e6d (patch)
tree77c98c52e6e7750c02c0b94f33ebf133470e3035
parent76575e6cdd8c08fb3fa6c02080c31d875545f0a8 (diff)
mu100: Add missing roms, also some wip [Guru, O. Galibert]
-rw-r--r--src/devices/sound/swp30.cpp756
-rw-r--r--src/devices/sound/swp30.h106
-rw-r--r--src/emu/debug/debugcpu.cpp6
-rw-r--r--src/mame/drivers/ymmu100.cpp492
4 files changed, 760 insertions, 600 deletions
diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp
index ebae8eb73e8..6f02329993f 100644
--- a/src/devices/sound/swp30.cpp
+++ b/src/devices/sound/swp30.cpp
@@ -7,91 +7,121 @@
#include "swp30.h"
/*
- mode hall 1 reverb 0.3/2.0/2.1/30.0
- 5ab/b56: 1ff2
- 5e1/bc2: 1ff1
- 5e3/bc6, 521/a42, 527/a4e, 563/ac6, 56b/ad6, 5a7/b4e, 5e3/bc6: 1d
- 529/a52: 1a21/13d2/134f/ 70c
- 4e9/9d2, 4eb/9d6: 5/14d0/1585/1ec3
- 565/aca: 1a21/12cb/1247/ 684
- 523/a46, 525/a4a: 10/1631/16d4/1eef
- 5a1/b42: 1a21/11c2/113d/ 606
- 561/ac2, 52b/ac2: a2/176f/1801/1f15
- 5a9/b5a: 1a20/10fb/1077/ 5ae
- 567/ace, 569/ad2: 4d/1847/18cd/1f2e
- 5e5/bca: 1a1f/ f74/ ef5/ 50b
- 5a3/b46, 5a5/b46: d2/19c2/1a31/1f58
- 623/c46: 1a21/130d/1289/ 6a5
- 5ab/b56, 5e1/bc2: c/15dc/1683/1ee4
-
-
-72814: move e0 to reg r0
-
-62fcc: compute stuff
+ The SWP30 is the combination of a rompler called AWM2 (Advanced Wave
+ Memory 2) and an effects DSP called MEG (Multiple Effects
+ Generator). It also includes some routing/mixing capabilities,
+ moving data between AWM2, MEG and serial inputs and outputs with
+ volume management capabilities everywhere. Its clock is 33.9MHz and
+ the output is at 44100Hz stereo (768 cycles per sample pair) per dac
+ output.
+
+ I/O wise, the chip has 8 generic audio serial inputs and 8 outputs
+ for external plugins, and two dac outputs. The DAC output is
+ stereo, and so is the first generic input. It's unclear whether the
+ outputs and the other inputs are stereo. The MU100 connects a stereo ADC to the fist input, and routes
+
+ In practice the chip has the pin for a second DAC, so is
+ probably 4-channel capable on output.
+
+ The
+
+
+ The AWM2 manages 64 channels internally, and has inputs for 8
+ external sources, one at least being stereo (they probably all are).
+ The sound data can be four formats (8 bits, 12 bits, 16 bits, and a
+ 8-bits log format with roughly 10 bits of dynamic). The rom bus is
+ 25 bits address and 32 bits data wide. It applies four filters to
+ the sample data, two of fixed type (low pass then highpass) and two
+ free 3-point FIR filters (used for yet another lowpass and
+ highpass). Envelopes are handled automatically, and the final
+ panned result is accumulated on four stereo accumulators which will
+ be passed to the MEG. Two of the channels (that includes the
+ external ones) can also have their value sent to the MEG.
+
+
+
+
+ The MEG is a DSP with 384 program steps. Instructions are 64 bits
+ wide, and to each instruction is associated a 2.14 fixed point value
+ and, for every third instruction a 16-bit integer memory offset
+ value. In addition 24 LFOs are available, and possibly more. It is
+ connected to a dram of 262144 samples, theorically 18 bits but in
+ practice only the top 16 bits are connected.
+
+ The chip interface presents 4096 16-bits registers in a 64x64 grid.
+ Some of this grid is for per-channel values for AWM2, but parts are
+ isolated and renumbered for MEG regisrers or for general control
+ functions.
+
+ General register address: (64 * channel + slot) * 2 (16-bits values)
+ MEG fixed point constants (n=0-383) : channel = n/6, slot = 0x21 + 2*(n%6)
+ MEG integer constants (n=0-127): channel = n/2, slot = 0x30 + (n%2)
+ MEG LFOs (n=0..23): channel = n/2, slot = 0x3e + (n%2)
+
+ Control registers (n=0..127): channel = n/2, slot = 0x
+
+ Note that the LFOs may be 128 instead of 24, but the mu100 code only
+ reserves 24 values in its structures. OTOH, the mu100 code never
+ uses channel >= 12 slot 3e-3f either.
+
+
+ AWM2 (per-channel) registers:
+ slot(s) function
+ 00 fixed LPF frequency cutoff index
+ 01 fixed LPF frequency cutoff index increment?
+ 02 fixed HPF frequency cutoff
+ 03 40ff at startup, 5010 always afterwards?
+ 04 fixed LPF resonane level
+ 05 unknown
+ 06-09 envelope information, not understood yet
+ 0a-0d unknown, probably something to do with vibrato
+ 10 unknown
+ 11 channel replay frequency, signed 4.10 fixed point, log2 scale, positive is higher resulting frequency
+ 12-13 number of samples before the loop point
+ 14-15 number of samples in the loop
+ 16-17 bit 31-30 = sample format, bits 29-25 = loop samples decimal part, 24-0 = loop start address in rom
+ 20,22,24 first FIR coefficients
+ 26,28,2a second FIR coefficients
+ 2c-2f unknown
+ 32 pan left/right, 2x8 bits of attenuation
+ 33-34 attenuation levels to add to the four accumulators (dry, reverb, chorus, variation for the mu100)
+ 35-37 routing, in particular for the taps. Rather unclear
+
+ Slots e-f are system control, 21,23,25,27,29,2b MEG fixed point
+ registers, 30,31 MEG offset registers, 3e,3f MEG LFO registers.
+ 38-3d are special, not per-channel.
+
+ Known system registers:
+ number function
+ 02 internal register selector, msb = 0 or 6, lsb = channel
+ 03 internal register read port, used for envelope/keyoff management
+ 0c-0f keyon mask
+ 10 write something to trigger a keyon
+ 21 MEG program write address
+ 22-25 MEG program opcode, writing to 25 triggers an auto-increment
+ 30-3e even slots only, MEG buffer mappings
+
+
+ The LFO registers internal counters are 22 bits wide. The LSB of
+ the register gives the increment per sample, encoded in a special
+ 3.5 format.
+ With scale = 3bits and v = 5bits, step = base[scale] + (v << shift[scale])
+ base = { 0, 32, 64, 128, 256, 512, 1024, 2048 }
+ shift = { 0, 0, 1, 2, 3, 4, 5, 6 }
+
+ The 21th bit of the counter inverts bits 20-0 on read, those are
+ interpreted as a 0-1 value, giving a sawtooth wave.
+
+
+ 8 mappings can be setup, which allow to manage rotating buffers in
+ the MEG-attached ram easily by automating masking and offset adding.
+ The register format is: tttttsss oooooooo. 't' is not understood
+ yet. 's' is the sub-buffer size, defined as 1 << (10+s). The base
+ offset is o << 10. There are no alignment issues, e.g. you can have
+ a buffer at 0x28000 which is 0x10000 samples long.
-*/
-#if 0
-// 929a6: 00 2e 30 32 33 34 35 36 37 3a 3c
-
-// 3fb80: list of reverb times as text, 4chars/entry
-
-// 84268: 7c 76 77 78
-// 80 79 7a 7b
-// 84 7e 7d 7f
-// 88 81 82 83
-// 8c 85 86 87
-// 91 89 8a 8b
-
-
-void f_62fcc(e0, r0, e1, r1l, er5) // 8 (11/12) 1 6 84268
-{
- // 929b1
- // 00 00 00 00 00 00
- // 38 3c 40 43 49 3b
- // 46 4a 4e 51 55 43
- // 3e 3f 41 43 48 3c
- // 4b 51 59 5c 00 4e
-
- // 92560
- // 00 01 12 1c 23 28 2d 30 34 37 39 3c 3e 40 42 43
- // 45 46 48 49 4a 4c 4d 4e 4f 50 51 52 53 54 54 55
- // 56 57 58 58 59 5a 5a 5b 5c 5c 5d 5d 5e 5e 5f 60
- // 60 61 61 63 66 68 69 6b 6d 6e 70 71 72 75 77 79
- // 7b 7c 7e 7f 81 82
-
- l8 = 929b1 + 6*e1;
- r6 = r1l == 6 ? 0x15 : r1l == 4 ? 0xd : r1l == 3 ? 0x9 : e1;
- f_64a34(0x01, 01, t929a6[e0].b << 8, 0x40, 0, er5 + 2*r6); // 0101 3700 40 0 84292 - writes 1ff2/1ff1
- pr0 = r0;
- for(r3h=0; r3h<6; r3h++) {
- r0l = *l8++;
- if(r0l) {
- r6 = clamp(r0l + l92560[pr0] - 0x38, 0, 0xff);
- r6, e6 = l925a6[r6].l; // 13d2 5368
- } else
- r6, e6 = 0, 0;
-
- }
- er0 = *er5.w++;
- g214ca4[0x10 + er0].w = e6;
- lc = &g214ca4[0x20 + er0].w
- er0 = float(r6);
-}
-// 80914 = convert unsigned short to float
-
-void write_regs(r0b, er1) // 728c0, r0=3, er1=84292
-{
- if(!g214c98)
- return;
- for(r5=0; r5<r0b; r5++) {
- r6 = er1[r5].w; // 89 8a 8b
- e5 = (r6 % 6) + 0x21;
- r0 = r6 / 6;
- write_reg(r6/6, (r6 % 6)*2 + 0x21, g214cc2[r6].w);
- }
-}
-#endif
+*/
DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip")
@@ -107,23 +137,26 @@ void swp30_device::device_start()
{
m_stream = stream_alloc(0, 2, 44100);
- // Attenuantion for panning is logarithmic on one byte where 0x10
- // means divide by two. It means 0 to -96dB with steps of
- // 0.375dB. Since it's a nice scale, we assume it's the same for
- // other attenuation values.
+ // Attenuantion for panning is 4.4 floating point. That means 0
+ // to -96.3dB. Since it's a nice range, we assume it's the same
+ // for other attenuation values. Computed value is is 1.16
+ // format, to avoid overflow
+
for(int i=0; i<256; i++)
- m_linear_attenuation[i] = 65536.0/pow(2, i/16.0);
+ m_linear_attenuation[i] = ((32 - (i & 15)) << (0xf ^ (i >> 4))) >> 4;
// Relative playback frequency of a sample is encoded on signed 14
// bits. The scale is logarithmic, with 0x400 = 1 octave (e.g. *2
// or /2).
+
for(int i=-0x20000; i<0x2000; i++)
m_sample_increment[i & 0x3fff] = 256 * pow(2, i/1024.0);
// Log to linear 8-bits sample decompression. Statistics say
// that's what it should look like. Note that 0 can be encoded
// both as 0x00 and 0x80, and as it happens 0x80 is never used in
- // these samples.
+ // these samples. Ends up with a 55dB dynamic range, to compare
+ // with 8bits 48dB, 12bits 72dB and 16bits 96dB.
// Rescale so that it's roughly 16 bits. Range ends up being +/- 78c0.
@@ -138,10 +171,40 @@ void swp30_device::device_start()
m_sample_log8[i | 0x80] = -m_sample_log8[i];
}
+ save_item(NAME(m_program));
+
+ save_item(NAME(m_keyon_mask));
+ save_item(NAME(m_active_mask));
+
+ save_item(NAME(m_pre_size));
+ save_item(NAME(m_post_size));
+ save_item(NAME(m_address));
+
+ save_item(NAME(m_sample_pos));
+ save_item(NAME(m_sample_history));
+
+ save_item(NAME(m_program_pfp));
+ save_item(NAME(m_program_pint));
+ save_item(NAME(m_program_plfo));
+
+ save_item(NAME(m_volume));
+ save_item(NAME(m_freq));
+ save_item(NAME(m_pan));
+ save_item(NAME(m_envelope));
+ save_item(NAME(m_lpf_cutoff));
+ save_item(NAME(m_lpf_cutoff_inc));
+ save_item(NAME(m_lpf_reso));
+ save_item(NAME(m_hpf_cutoff));
+ save_item(NAME(m_eq_filter));
+ save_item(NAME(m_routing));
+
+ save_item(NAME(m_program_address));
}
void swp30_device::device_reset()
{
+ memset(m_program, 0, sizeof(m_program));
+
m_keyon_mask = 0;
m_active_mask = 0;
@@ -152,25 +215,22 @@ void swp30_device::device_reset()
memset(m_sample_pos, 0, sizeof(m_sample_pos));
memset(m_sample_history, 0, sizeof(m_sample_history));
+ memset(m_program_pfp, 0, sizeof(m_program_pfp));
+ memset(m_program_pint, 0, sizeof(m_program_pint));
+ memset(m_program_plfo, 0, sizeof(m_program_plfo));
+
memset(m_volume, 0, sizeof(m_volume));
memset(m_freq, 0, sizeof(m_freq));
memset(m_pan, 0, sizeof(m_pan));
+ memset(m_envelope, 0, sizeof(m_envelope));
memset(m_lpf_cutoff, 0, sizeof(m_lpf_cutoff));
+ memset(m_lpf_cutoff_inc, 0, sizeof(m_lpf_cutoff_inc));
memset(m_lpf_reso, 0, sizeof(m_lpf_reso));
memset(m_hpf_cutoff, 0, sizeof(m_hpf_cutoff));
memset(m_eq_filter, 0, sizeof(m_eq_filter));
+ memset(m_routing, 0, sizeof(m_routing));
- memset(m_variation_delay, 0, sizeof(m_variation_delay));
- memset(m_variation_fb_level, 0, sizeof(m_variation_fb_level));
- memset(m_variation_final_level, 0, sizeof(m_variation_final_level));
- memset(m_variation_high_damp, 0, sizeof(m_variation_high_damp));
- memset(m_variation_inv_high_damp, 0, sizeof(m_variation_inv_high_damp));
-
- memset(m_insertion_delay, 0, sizeof(m_insertion_delay));
- memset(m_insertion_fb_level, 0, sizeof(m_insertion_fb_level));
- memset(m_insertion_final_level, 0, sizeof(m_insertion_final_level));
- memset(m_insertion_high_damp, 0, sizeof(m_insertion_high_damp));
- memset(m_insertion_inv_high_damp, 0, sizeof(m_insertion_inv_high_damp));
+ m_program_address = 0;
}
void swp30_device::rom_bank_updated()
@@ -181,78 +241,79 @@ void swp30_device::rom_bank_updated()
void swp30_device::map(address_map &map)
{
map(0x0000, 0x1fff).rw(FUNC(swp30_device::snd_r), FUNC(swp30_device::snd_w));
- map(0x0000, 0x0001).select(0x1f80).rw(FUNC(swp30_device::lpf_cutoff_r), FUNC(swp30_device::lpf_cutoff_w));
- map(0x0004, 0x0005).select(0x1f80).rw(FUNC(swp30_device::hpf_cutoff_r), FUNC(swp30_device::hpf_cutoff_w));
- map(0x0008, 0x0009).select(0x1f80).rw(FUNC(swp30_device::lpf_reso_r), FUNC(swp30_device::lpf_reso_w));
- map(0x0012, 0x0013).select(0x1f80).rw(FUNC(swp30_device::volume_r), FUNC(swp30_device::volume_w));
- map(0x0022, 0x0023).select(0x1f80).rw(FUNC(swp30_device::freq_r), FUNC(swp30_device::freq_w));
- map(0x0024, 0x0027).select(0x1f80).rw(FUNC(swp30_device::pre_size_r), FUNC(swp30_device::pre_size_w));
- map(0x0028, 0x002b).select(0x1f80).rw(FUNC(swp30_device::post_size_r), FUNC(swp30_device::post_size_w));
- map(0x002c, 0x002f).select(0x1f80).rw(FUNC(swp30_device::address_r), FUNC(swp30_device::address_w));
- map(0x0040, 0x0041).select(0x1f80).rw(&swp30_device::eq_filter_r<0, 0>, "swp30_device::eq_filter_r<0, 0>", &swp30_device::eq_filter_w<0, 0>, "swp30_device::eq_filter_w<0, 0>");
- map(0x0044, 0x0045).select(0x1f80).rw(&swp30_device::eq_filter_r<0, 1>, "swp30_device::eq_filter_r<0, 1>", &swp30_device::eq_filter_w<0, 1>, "swp30_device::eq_filter_w<0, 1>");
- map(0x0048, 0x0049).select(0x1f80).rw(&swp30_device::eq_filter_r<0, 2>, "swp30_device::eq_filter_r<0, 2>", &swp30_device::eq_filter_w<0, 2>, "swp30_device::eq_filter_w<0, 2>");
- map(0x004c, 0x004d).select(0x1f80).rw(&swp30_device::eq_filter_r<1, 0>, "swp30_device::eq_filter_r<1, 0>", &swp30_device::eq_filter_w<1, 0>, "swp30_device::eq_filter_w<1, 0>");
- map(0x0050, 0x0051).select(0x1f80).rw(&swp30_device::eq_filter_r<1, 1>, "swp30_device::eq_filter_r<1, 1>", &swp30_device::eq_filter_w<1, 1>, "swp30_device::eq_filter_w<1, 1>");
- map(0x0054, 0x0055).select(0x1f80).rw(&swp30_device::eq_filter_r<1, 2>, "swp30_device::eq_filter_r<1, 2>", &swp30_device::eq_filter_w<1, 2>, "swp30_device::eq_filter_w<1, 2>");
- map(0x0064, 0x0065).select(0x1f80).rw(FUNC(swp30_device::pan_r), FUNC(swp30_device::pan_w));
- map(0x031c, 0x031d). rw(FUNC(swp30_device::keyon_mask_r<3>), FUNC(swp30_device::keyon_mask_w<3>));
- map(0x031e, 0x031f). rw(FUNC(swp30_device::keyon_mask_r<2>), FUNC(swp30_device::keyon_mask_w<2>));
- map(0x039c, 0x039d). rw(FUNC(swp30_device::keyon_mask_r<1>), FUNC(swp30_device::keyon_mask_w<1>));
- map(0x039e, 0x039f). rw(FUNC(swp30_device::keyon_mask_r<0>), FUNC(swp30_device::keyon_mask_w<0>));
- map(0x041c, 0x041d). rw(FUNC(swp30_device::keyon_r), FUNC(swp30_device::keyon_w));
-
- map(0x1060, 0x1061). rw(&swp30_device::insertion_delay_r<0, 0>, "swp30_device::insertion_delay_r<0, 0>", &swp30_device::insertion_delay_w<0, 0>, "swp30_device::insertion_delay_w<0, 0>");
- map(0x1062, 0x1063). rw(&swp30_device::insertion_delay_r<0, 1>, "swp30_device::insertion_delay_r<0, 1>", &swp30_device::insertion_delay_w<0, 1>, "swp30_device::insertion_delay_w<0, 1>");
- map(0x10e0, 0x10e1). rw(&swp30_device::insertion_delay_r<0, 2>, "swp30_device::insertion_delay_r<0, 2>", &swp30_device::insertion_delay_w<0, 2>, "swp30_device::insertion_delay_w<0, 2>");
- map(0x10e2, 0x10e3). rw(&swp30_device::insertion_delay_r<0, 3>, "swp30_device::insertion_delay_r<0, 3>", &swp30_device::insertion_delay_w<0, 3>, "swp30_device::insertion_delay_w<0, 3>");
- map(0x1152, 0x1153). rw(&swp30_device::insertion_inv_high_damp_r<0, 0>, "swp30_device::insertion_inv_high_damp_r<0, 0>", &swp30_device::insertion_inv_high_damp_w<0, 0>, "swp30_device::insertion_inv_high_damp_w<0, 0>");
- map(0x1156, 0x1157). rw(&swp30_device::insertion_high_damp_r<0, 0>, "swp30_device::insertion_high_damp_r<0, 0>", &swp30_device::insertion_high_damp_w<0, 0>, "swp30_device::insertion_high_damp_w<0, 0>");
- map(0x11ca, 0x11cb). rw(&swp30_device::insertion_inv_high_damp_r<0, 1>, "swp30_device::insertion_inv_high_damp_r<0, 1>", &swp30_device::insertion_inv_high_damp_w<0, 1>, "swp30_device::insertion_inv_high_damp_w<0, 1>");
- map(0x11ce, 0x11cf). rw(&swp30_device::insertion_high_damp_r<0, 1>, "swp30_device::insertion_high_damp_r<0, 1>", &swp30_device::insertion_high_damp_w<0, 1>, "swp30_device::insertion_high_damp_w<0, 1>");
- map(0x1242, 0x1243). rw(&swp30_device::insertion_fb_level_r<0, 0>, "swp30_device::insertion_fb_level_r<0, 0>", &swp30_device::insertion_fb_level_w<0, 0>, "swp30_device::insertion_fb_level_w<0, 0>");
- map(0x1246, 0x1247). rw(&swp30_device::insertion_fb_level_r<0, 1>, "swp30_device::insertion_fb_level_r<0, 1>", &swp30_device::insertion_fb_level_w<0, 1>, "swp30_device::insertion_fb_level_w<0, 1>");
- map(0x124e, 0x124f). rw(&swp30_device::insertion_fb_level_r<0, 2>, "swp30_device::insertion_fb_level_r<0, 2>", &swp30_device::insertion_fb_level_w<0, 2>, "swp30_device::insertion_fb_level_w<0, 2>");
- map(0x12d6, 0x12d7). rw(&swp30_device::insertion_final_level_r<0, 0>, "swp30_device::insertion_final_level_r<0, 0>", &swp30_device::insertion_final_level_w<0, 0>, "swp30_device::insertion_final_level_w<0, 0>");
- map(0x1342, 0x1343). rw(&swp30_device::insertion_final_level_r<0, 1>, "swp30_device::insertion_final_level_r<0, 1>", &swp30_device::insertion_final_level_w<0, 1>, "swp30_device::insertion_final_level_w<0, 1>");
- map(0x1346, 0x1347). rw(&swp30_device::insertion_final_level_r<0, 2>, "swp30_device::insertion_final_level_r<0, 2>", &swp30_device::insertion_final_level_w<0, 2>, "swp30_device::insertion_final_level_w<0, 2>");
- map(0x134a, 0x134b). rw(&swp30_device::insertion_final_level_r<0, 3>, "swp30_device::insertion_final_level_r<0, 3>", &swp30_device::insertion_final_level_w<0, 3>, "swp30_device::insertion_final_level_w<0, 3>");
-
- map(0x1460, 0x1461). rw(&swp30_device::insertion_delay_r<1, 0>, "swp30_device::insertion_delay_r<1, 0>", &swp30_device::insertion_delay_w<1, 0>, "swp30_device::insertion_delay_w<1, 0>");
- map(0x1462, 0x1463). rw(&swp30_device::insertion_delay_r<1, 1>, "swp30_device::insertion_delay_r<1, 1>", &swp30_device::insertion_delay_w<1, 1>, "swp30_device::insertion_delay_w<1, 1>");
- map(0x14e0, 0x14e1). rw(&swp30_device::insertion_delay_r<1, 2>, "swp30_device::insertion_delay_r<1, 2>", &swp30_device::insertion_delay_w<1, 2>, "swp30_device::insertion_delay_w<1, 2>");
- map(0x14e2, 0x14e3). rw(&swp30_device::insertion_delay_r<1, 3>, "swp30_device::insertion_delay_r<1, 3>", &swp30_device::insertion_delay_w<1, 3>, "swp30_device::insertion_delay_w<1, 3>");
- map(0x1552, 0x1553). rw(&swp30_device::insertion_inv_high_damp_r<1, 0>, "swp30_device::insertion_inv_high_damp_r<1, 0>", &swp30_device::insertion_inv_high_damp_w<1, 0>, "swp30_device::insertion_inv_high_damp_w<1, 0>");
- map(0x1556, 0x1557). rw(&swp30_device::insertion_high_damp_r<1, 0>, "swp30_device::insertion_high_damp_r<1, 0>", &swp30_device::insertion_high_damp_w<1, 0>, "swp30_device::insertion_high_damp_w<1, 0>");
- map(0x15ca, 0x15cb). rw(&swp30_device::insertion_inv_high_damp_r<1, 1>, "swp30_device::insertion_inv_high_damp_r<1, 1>", &swp30_device::insertion_inv_high_damp_w<1, 1>, "swp30_device::insertion_inv_high_damp_w<1, 1>");
- map(0x15ce, 0x15cf). rw(&swp30_device::insertion_high_damp_r<1, 1>, "swp30_device::insertion_high_damp_r<1, 1>", &swp30_device::insertion_high_damp_w<1, 1>, "swp30_device::insertion_high_damp_w<1, 1>");
- map(0x1642, 0x1643). rw(&swp30_device::insertion_fb_level_r<1, 0>, "swp30_device::insertion_fb_level_r<1, 0>", &swp30_device::insertion_fb_level_w<1, 0>, "swp30_device::insertion_fb_level_w<1, 0>");
- map(0x1646, 0x1647). rw(&swp30_device::insertion_fb_level_r<1, 1>, "swp30_device::insertion_fb_level_r<1, 1>", &swp30_device::insertion_fb_level_w<1, 1>, "swp30_device::insertion_fb_level_w<1, 1>");
- map(0x164e, 0x164f). rw(&swp30_device::insertion_fb_level_r<1, 2>, "swp30_device::insertion_fb_level_r<1, 2>", &swp30_device::insertion_fb_level_w<1, 2>, "swp30_device::insertion_fb_level_w<1, 2>");
- map(0x16d6, 0x16d7). rw(&swp30_device::insertion_final_level_r<1, 0>, "swp30_device::insertion_final_level_r<1, 0>", &swp30_device::insertion_final_level_w<1, 0>, "swp30_device::insertion_final_level_w<1, 0>");
- map(0x1742, 0x1743). rw(&swp30_device::insertion_final_level_r<1, 1>, "swp30_device::insertion_final_level_r<1, 1>", &swp30_device::insertion_final_level_w<1, 1>, "swp30_device::insertion_final_level_w<1, 1>");
- map(0x1746, 0x1747). rw(&swp30_device::insertion_final_level_r<1, 2>, "swp30_device::insertion_final_level_r<1, 2>", &swp30_device::insertion_final_level_w<1, 2>, "swp30_device::insertion_final_level_w<1, 2>");
- map(0x174a, 0x174b). rw(&swp30_device::insertion_final_level_r<1, 3>, "swp30_device::insertion_final_level_r<1, 3>", &swp30_device::insertion_final_level_w<1, 3>, "swp30_device::insertion_final_level_w<1, 3>");
-
- map(0x18e0, 0x18e1). rw(FUNC(swp30_device::variation_delay_r<0>), FUNC(swp30_device::variation_delay_w<0>));
- map(0x1960, 0x1961). rw(FUNC(swp30_device::variation_delay_r<1>), FUNC(swp30_device::variation_delay_w<1>));
- map(0x19e0, 0x19e1). rw(FUNC(swp30_device::variation_delay_r<2>), FUNC(swp30_device::variation_delay_w<2>));
- map(0x1ae0, 0x1ae1). rw(FUNC(swp30_device::variation_delay_r<3>), FUNC(swp30_device::variation_delay_w<3>));
- map(0x1bd6, 0x1bd7). rw(FUNC(swp30_device::variation_inv_high_damp_r<0>), FUNC(swp30_device::variation_inv_high_damp_w<0>));
- map(0x1c46, 0x1c47). rw(FUNC(swp30_device::variation_high_damp_r<0>), FUNC(swp30_device::variation_high_damp_w<0>));
- map(0x1c52, 0x1c53). rw(FUNC(swp30_device::variation_inv_high_damp_r<1>), FUNC(swp30_device::variation_inv_high_damp_w<1>));
- map(0x1cc2, 0x1cc3). rw(FUNC(swp30_device::variation_high_damp_r<1>), FUNC(swp30_device::variation_high_damp_w<1>));
- map(0x1cce, 0x1ccf). rw(FUNC(swp30_device::variation_fb_level_r<0>), FUNC(swp30_device::variation_fb_level_w<0>));
- map(0x1cd2, 0x1cd3). rw(FUNC(swp30_device::variation_fb_level_r<1>), FUNC(swp30_device::variation_fb_level_w<1>));
- map(0x1d46, 0x1d47). rw(FUNC(swp30_device::variation_fb_level_r<2>), FUNC(swp30_device::variation_fb_level_w<2>));
- map(0x1f4a, 0x1f4b). rw(FUNC(swp30_device::variation_final_level_r<0>), FUNC(swp30_device::variation_final_level_w<0>));
- map(0x1f52, 0x1f53). rw(FUNC(swp30_device::variation_final_level_r<1>), FUNC(swp30_device::variation_final_level_w<1>));
- map(0x1f56, 0x1f57). rw(FUNC(swp30_device::variation_final_level_r<2>), FUNC(swp30_device::variation_final_level_w<2>));
- map(0x1fc6, 0x1fc7). rw(FUNC(swp30_device::variation_final_level_r<3>), FUNC(swp30_device::variation_final_level_w<3>));
-
-}
-
+ rchan(map, 0x00).rw(FUNC(swp30_device::lpf_cutoff_r), FUNC(swp30_device::lpf_cutoff_w));
+ rchan(map, 0x01).rw(FUNC(swp30_device::lpf_cutoff_inc_r), FUNC(swp30_device::lpf_cutoff_inc_w));
+ rchan(map, 0x02).rw(FUNC(swp30_device::hpf_cutoff_r), FUNC(swp30_device::hpf_cutoff_w));
+ // 03 seems to always get 5010 except at startup where it's 40ff
+ rchan(map, 0x04).rw(FUNC(swp30_device::lpf_reso_r), FUNC(swp30_device::lpf_reso_w));
+ // 05 missing
+ rchan(map, 0x06).rw(FUNC(swp30_device::envelope_r<0>), FUNC(swp30_device::envelope_w<0>));
+ rchan(map, 0x07).rw(FUNC(swp30_device::envelope_r<1>), FUNC(swp30_device::envelope_w<1>));
+ rchan(map, 0x08).rw(FUNC(swp30_device::envelope_r<2>), FUNC(swp30_device::envelope_w<2>));
+ rchan(map, 0x09).rw(FUNC(swp30_device::volume_r), FUNC(swp30_device::volume_w));
+ // 0a-0d missing
+ // 10 missing
+ rchan(map, 0x11).rw(FUNC(swp30_device::freq_r), FUNC(swp30_device::freq_w));
+ rchan(map, 0x12).rw(FUNC(swp30_device::pre_size_h_r), FUNC(swp30_device::pre_size_h_w));
+ rchan(map, 0x13).rw(FUNC(swp30_device::pre_size_l_r), FUNC(swp30_device::pre_size_l_w));
+ rchan(map, 0x14).rw(FUNC(swp30_device::post_size_h_r), FUNC(swp30_device::post_size_h_w));
+ rchan(map, 0x15).rw(FUNC(swp30_device::post_size_l_r), FUNC(swp30_device::post_size_l_w));
+ rchan(map, 0x16).rw(FUNC(swp30_device::address_h_r), FUNC(swp30_device::address_h_w));
+ rchan(map, 0x17).rw(FUNC(swp30_device::address_l_r), FUNC(swp30_device::address_l_w));
+ rchan(map, 0x20).rw(FUNC(swp30_device::eq_filter_r<0>), FUNC(swp30_device::eq_filter_w<0>));
+ rchan(map, 0x22).rw(FUNC(swp30_device::eq_filter_r<1>), FUNC(swp30_device::eq_filter_w<1>));
+ rchan(map, 0x24).rw(FUNC(swp30_device::eq_filter_r<2>), FUNC(swp30_device::eq_filter_w<2>));
+ rchan(map, 0x26).rw(FUNC(swp30_device::eq_filter_r<3>), FUNC(swp30_device::eq_filter_w<3>));
+ rchan(map, 0x28).rw(FUNC(swp30_device::eq_filter_r<4>), FUNC(swp30_device::eq_filter_w<4>));
+ rchan(map, 0x2a).rw(FUNC(swp30_device::eq_filter_r<5>), FUNC(swp30_device::eq_filter_w<5>));
+ // 2c-2f missing
+ rchan(map, 0x32).rw(FUNC(swp30_device::pan_r), FUNC(swp30_device::pan_w));
+ rchan(map, 0x33).rw(FUNC(swp30_device::dry_rev_r), FUNC(swp30_device::dry_rev_w));
+ rchan(map, 0x34).rw(FUNC(swp30_device::cho_var_r), FUNC(swp30_device::cho_var_w));
+ rchan(map, 0x35).rw(FUNC(swp30_device::routing_r<0>), FUNC(swp30_device::routing_w<0>));
+ rchan(map, 0x36).rw(FUNC(swp30_device::routing_r<1>), FUNC(swp30_device::routing_w<1>));
+ rchan(map, 0x37).rw(FUNC(swp30_device::routing_r<2>), FUNC(swp30_device::routing_w<2>));
+ // 38-3d missing, are special
+
+ // Control registers
+ // These appear as channel slots 0x0e and 0x0f
+ // 00-0b missing
+ rctrl(map, 0x0c).rw(FUNC(swp30_device::keyon_mask_r<3>), FUNC(swp30_device::keyon_mask_w<3>));
+ rctrl(map, 0x0d).rw(FUNC(swp30_device::keyon_mask_r<2>), FUNC(swp30_device::keyon_mask_w<2>));
+ rctrl(map, 0x0e).rw(FUNC(swp30_device::keyon_mask_r<1>), FUNC(swp30_device::keyon_mask_w<1>));
+ rctrl(map, 0x0f).rw(FUNC(swp30_device::keyon_mask_r<0>), FUNC(swp30_device::keyon_mask_w<0>));
+ rctrl(map, 0x10).rw(FUNC(swp30_device::keyon_r), FUNC(swp30_device::keyon_w));
+ // 11-20 missing
+ rctrl(map, 0x21).rw(FUNC(swp30_device::prg_address_r), FUNC(swp30_device::prg_address_w));
+ rctrl(map, 0x22).rw(FUNC(swp30_device::prg_r<0>), FUNC(swp30_device::prg_w<0>));
+ rctrl(map, 0x23).rw(FUNC(swp30_device::prg_r<1>), FUNC(swp30_device::prg_w<1>));
+ rctrl(map, 0x24).rw(FUNC(swp30_device::prg_r<2>), FUNC(swp30_device::prg_w<2>));
+ rctrl(map, 0x25).rw(FUNC(swp30_device::prg_r<3>), FUNC(swp30_device::prg_w<3>));
+ // 26-7f missing
+ rctrl(map, 0x30).rw(FUNC(swp30_device::map_r<0>), FUNC(swp30_device::map_w<0>));
+ rctrl(map, 0x32).rw(FUNC(swp30_device::map_r<1>), FUNC(swp30_device::map_w<1>));
+ rctrl(map, 0x34).rw(FUNC(swp30_device::map_r<2>), FUNC(swp30_device::map_w<2>));
+ rctrl(map, 0x36).rw(FUNC(swp30_device::map_r<3>), FUNC(swp30_device::map_w<3>));
+ rctrl(map, 0x38).rw(FUNC(swp30_device::map_r<4>), FUNC(swp30_device::map_w<4>));
+ rctrl(map, 0x3a).rw(FUNC(swp30_device::map_r<5>), FUNC(swp30_device::map_w<5>));
+ rctrl(map, 0x3c).rw(FUNC(swp30_device::map_r<6>), FUNC(swp30_device::map_w<6>));
+ rctrl(map, 0x3e).rw(FUNC(swp30_device::map_r<7>), FUNC(swp30_device::map_w<7>));
+
+ // MEG registers
+ rchan(map, 0x21).rw(FUNC(swp30_device::prg_fp_r<0>), FUNC(swp30_device::prg_fp_w<0>));
+ rchan(map, 0x23).rw(FUNC(swp30_device::prg_fp_r<1>), FUNC(swp30_device::prg_fp_w<1>));
+ rchan(map, 0x25).rw(FUNC(swp30_device::prg_fp_r<2>), FUNC(swp30_device::prg_fp_w<2>));
+ rchan(map, 0x27).rw(FUNC(swp30_device::prg_fp_r<3>), FUNC(swp30_device::prg_fp_w<3>));
+ rchan(map, 0x29).rw(FUNC(swp30_device::prg_fp_r<4>), FUNC(swp30_device::prg_fp_w<4>));
+ rchan(map, 0x2b).rw(FUNC(swp30_device::prg_fp_r<5>), FUNC(swp30_device::prg_fp_w<5>));
+ rchan(map, 0x30).rw(FUNC(swp30_device::prg_int_r<0>), FUNC(swp30_device::prg_int_w<0>));
+ rchan(map, 0x31).rw(FUNC(swp30_device::prg_int_r<1>), FUNC(swp30_device::prg_int_w<1>));
+ rchan(map, 0x3e).rw(FUNC(swp30_device::prg_lfo_r<0>), FUNC(swp30_device::prg_lfo_w<0>));
+ rchan(map, 0x3f).rw(FUNC(swp30_device::prg_lfo_r<1>), FUNC(swp30_device::prg_lfo_w<1>));
+}
+
+// Control registers
template<int sel> u16 swp30_device::keyon_mask_r()
{
return m_keyon_mask >> (16*sel);
@@ -268,7 +329,6 @@ u16 swp30_device::keyon_r()
return 0;
}
-static int ncount = 0;
void swp30_device::keyon_w(u16)
{
m_stream->update();
@@ -276,14 +336,60 @@ void swp30_device::keyon_w(u16)
u64 mask = u64(1) << i;
if((m_keyon_mask & mask) && !(m_active_mask & mask) && !(m_volume[i] & 0x8000)) {
m_sample_pos[i] = -s32(m_pre_size[i] << 8);
- logerror("sample count %3d %02x %08x %08x %08x vol %04x pan %04x\n", ncount, i, m_pre_size[i], m_post_size[i], m_address[i], m_volume[i], m_pan[i]);
+ logerror("keyon %02x %08x %08x %08x vol %04x pan %04x\n", i, m_pre_size[i], m_post_size[i], m_address[i], m_volume[i], m_pan[i]);
m_active_mask |= mask;
- ncount++;
}
}
m_keyon_mask = 0;
}
+
+u16 swp30_device::prg_address_r()
+{
+ return m_program_address;
+}
+
+void swp30_device::prg_address_w(u16 data)
+{
+ m_program_address = data;
+ if(m_program_address >= 0x180)
+ m_program_address = 0;
+}
+
+template<int sel> u16 swp30_device::prg_r()
+{
+ constexpr offs_t shift = 48-16*sel;
+ return m_program[m_program_address] >> shift;
+}
+
+template<int sel> void swp30_device::prg_w(u16 data)
+{
+ constexpr offs_t shift = 48-16*sel;
+ constexpr u64 mask = ~(u64(0xffff) << shift);
+ m_program[m_program_address] = (m_program[m_program_address] & mask) | (u64(data) << shift);
+
+ if(sel == 3) {
+ logerror("program %03x %016x\n", m_program_address, m_program[m_program_address]);
+ m_program_address ++;
+ if(m_program_address == 0x180)
+ m_program_address = 0;
+ }
+}
+
+
+template<int sel> u16 swp30_device::map_r()
+{
+ return m_map[sel];
+}
+
+template<int sel> void swp30_device::map_w(u16 data)
+{
+ m_map[sel] = data;
+ logerror("map %d: type=%02x offset=%05x size=%05x\n", sel, data >> 11, (data & 0xff) << 10, 0x400 << ((data >> 8) & 7));
+}
+
+
+// AWM2 per-channel registers
u16 swp30_device::lpf_cutoff_r(offs_t offset)
{
return m_lpf_cutoff[offset >> 6];
@@ -293,11 +399,25 @@ void swp30_device::lpf_cutoff_w(offs_t offset, u16 data)
{
m_stream->update();
u8 chan = offset >> 6;
- if(m_lpf_cutoff[chan] != data)
+ if(m_lpf_cutoff[chan] != data && 0)
logerror("chan %02x lpf cutoff %04x\n", chan, data);
m_lpf_cutoff[chan] = data;
}
+u16 swp30_device::lpf_cutoff_inc_r(offs_t offset)
+{
+ return m_lpf_cutoff_inc[offset >> 6];
+}
+
+void swp30_device::lpf_cutoff_inc_w(offs_t offset, u16 data)
+{
+ m_stream->update();
+ u8 chan = offset >> 6;
+ if(m_lpf_cutoff_inc[chan] != data && 0)
+ logerror("chan %02x lpf cutoff increment %04x\n", chan, data);
+ m_lpf_cutoff_inc[chan] = data;
+}
+
u16 swp30_device::hpf_cutoff_r(offs_t offset)
{
return m_hpf_cutoff[offset >> 6];
@@ -326,17 +446,27 @@ void swp30_device::lpf_reso_w(offs_t offset, u16 data)
m_lpf_reso[chan] = data;
}
-template<int filter, int coef> u16 swp30_device::eq_filter_r(offs_t offset)
+template<int coef> u16 swp30_device::eq_filter_r(offs_t offset)
{
- return m_eq_filter[offset >> 6][filter][coef];
+ return m_eq_filter[offset >> 6][coef];
}
-template<int filter, int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data)
+template<int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data)
{
m_stream->update();
- m_eq_filter[offset >> 6][filter][coef] = data;
+ m_eq_filter[offset >> 6][coef] = data;
}
+template<int sel> u16 swp30_device::routing_r(offs_t offset)
+{
+ return m_routing[offset >> 6][sel];
+}
+
+template<int sel> void swp30_device::routing_w(offs_t offset, u16 data)
+{
+ m_stream->update();
+ m_routing[offset >> 6][sel] = data;
+}
u16 swp30_device::volume_r(offs_t offset)
{
@@ -351,8 +481,12 @@ void swp30_device::volume_w(offs_t offset, u16 data)
if(m_volume[chan] != data)
logerror("snd chan %02x volume %02x %02x\n", chan, data >> 8, data & 0xff);
m_volume[chan] = data;
- if(data & 0x8000)
- m_active_mask &= ~(u64(1) << chan);
+ if(data & 0x8000) {
+ if(m_active_mask & (u64(1) << chan)) {
+ if(m_post_size[chan])
+ m_active_mask &= ~(u64(1) << chan);
+ }
+ }
}
@@ -364,213 +498,211 @@ u16 swp30_device::pan_r(offs_t offset)
void swp30_device::pan_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- double p1 = pow(2, -(data >> 8)/16.0);
- double p2 = pow(2, -(data & 0xff)/16.0);
if(m_pan[chan] != data)
- logerror("snd chan %02x pan l %02x r %02x (%g %g %g)\n", chan, data >> 8, data & 0xff, p1, p2, sqrt(p1*p1+p2*p2));
+ logerror("snd chan %02x pan l %02x r %02x\n", chan, data >> 8, data & 0xff);
m_pan[chan] = data;
}
-u16 swp30_device::freq_r(offs_t offset)
+u16 swp30_device::dry_rev_r(offs_t offset)
{
- return m_freq[offset >> 6];
+ return m_dry_rev[offset >> 6];
}
-void swp30_device::freq_w(offs_t offset, u16 data)
+void swp30_device::dry_rev_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- // delta is 4*256 per octave, positive means higher freq, e.g 4.10 format.
- s16 v = data & 0x2000 ? data | 0xc000 : data;
- if(m_freq[chan] != data)
- logerror("snd chan %02x freq %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
- m_freq[chan] = data;
+ if(m_dry_rev[chan] != data)
+ logerror("snd chan %02x dry %02x rev %02x\n", chan, data >> 8, data & 0xff);
+ m_dry_rev[chan] = data;
}
-u16 swp30_device::pre_size_r(offs_t offset)
+u16 swp30_device::cho_var_r(offs_t offset)
{
- if(offset & 1)
- return m_pre_size[offset >> 6];
- else
- return m_pre_size[offset >> 6] >> 16;
+ return m_dry_rev[offset >> 6];
}
-void swp30_device::pre_size_w(offs_t offset, u16 data)
+void swp30_device::cho_var_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(offset & 1) {
- m_pre_size[chan] = (m_pre_size[chan] & 0xffff0000) | data;
- logerror("snd chan %02x pre-size %02x %06x\n", chan, m_pre_size[chan] >> 24, m_pre_size[chan] & 0xffffff);
- } else
- m_pre_size[chan] = (m_pre_size[chan] & 0x0000ffff) | (data << 16);
+ if(m_dry_rev[chan] != data)
+ logerror("snd chan %02x cho %02x var %02x\n", chan, data >> 8, data & 0xff);
+ m_dry_rev[chan] = data;
}
-u16 swp30_device::post_size_r(offs_t offset)
+u16 swp30_device::freq_r(offs_t offset)
{
- if(offset & 1)
- return m_post_size[offset >> 6];
- else
- return m_post_size[offset >> 6] >> 16;
+ return m_freq[offset >> 6];
}
-void swp30_device::post_size_w(offs_t offset, u16 data)
+void swp30_device::freq_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(offset & 1) {
- m_post_size[chan] = (m_post_size[chan] & 0xffff0000) | data;
- logerror("snd chan %02x post-size %02x %06x\n", chan, m_post_size[chan] >> 24, m_post_size[chan] & 0xffffff);
- } else
- m_post_size[chan] = (m_post_size[chan] & 0x0000ffff) | (data << 16);
+ // delta is 4*256 per octave, positive means higher freq, e.g 4.10 format.
+ s16 v = data & 0x2000 ? data | 0xc000 : data;
+ if(m_freq[chan] != data)
+ logerror("snd chan %02x freq %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
+ m_freq[chan] = data;
}
-u16 swp30_device::address_r(offs_t offset)
+template<int sel> u16 swp30_device::envelope_r(offs_t offset)
{
- if(offset & 1)
- return m_address[offset >> 6];
- else
- return m_address[offset >> 6] >> 16;
+ return m_envelope[offset >> 6][sel];
}
-void swp30_device::address_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::envelope_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(offset & 1) {
- // The address may be 25 bits
- static const char *const formats[4] = { "l16", "l12", "l8", "x8" };
- m_address[chan] = (m_address[chan] & 0xffff0000) | data;
- logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff);
- } else
- m_address[chan] = (m_address[chan] & 0x0000ffff) | (data << 16);
+ bool ch = m_envelope[chan][sel] != data;
+ m_envelope[chan][sel] = data;
+ if(ch)
+ logerror("snd chan %02x envelopes %04x %04x %04x\n", chan, m_envelope[chan][0], m_envelope[chan][1], m_envelope[chan][2]);
}
-template<int id> void swp30_device::variation_delay_w(u16 data)
+u16 swp30_device::pre_size_h_r(offs_t offset)
{
- m_variation_delay[id] = data;
- logerror("variation delay %d: %04x (%fms)\n", id, data, data/44.100);
+ return m_pre_size[offset >> 6] >> 16;
}
-template<int id> u16 swp30_device::variation_delay_r()
+u16 swp30_device::pre_size_l_r(offs_t offset)
{
- return m_variation_delay[id];
+ return m_pre_size[offset >> 6];
}
-template<int id> void swp30_device::variation_fb_level_w(u16 data)
+void swp30_device::pre_size_h_w(offs_t offset, u16 data)
{
- m_variation_fb_level[id] = data;
- logerror("variation feedback level %d: %04x (%+f)\n", id, data, s16(data)/500.0);
+ u8 chan = offset >> 6;
+ m_pre_size[chan] = (m_pre_size[chan] & 0x0000ffff) | (data << 16);
}
-template<int id> u16 swp30_device::variation_fb_level_r()
+void swp30_device::pre_size_l_w(offs_t offset, u16 data)
{
- return m_variation_fb_level[id];
+ u8 chan = offset >> 6;
+ m_pre_size[chan] = (m_pre_size[chan] & 0xffff0000) | data;
+ logerror("snd chan %02x pre-size %02x %06x\n", chan, m_pre_size[chan] >> 24, m_pre_size[chan] & 0xffffff);
}
-template<int id> void swp30_device::variation_final_level_w(u16 data)
+u16 swp30_device::post_size_h_r(offs_t offset)
{
- m_variation_final_level[id] = data;
- logerror("variation final level %s %s: %04x (%f)\n", id & 1 ? "wet" : "dry", id & 2 ? "left" : "right", data, data/16383.0);
+ return m_post_size[offset >> 6] >> 16;
}
-template<int id> u16 swp30_device::variation_final_level_r()
+u16 swp30_device::post_size_l_r(offs_t offset)
{
- return m_variation_final_level[id];
+ return m_post_size[offset >> 6];
}
-template<int id> void swp30_device::variation_high_damp_w(u16 data)
+void swp30_device::post_size_h_w(offs_t offset, u16 data)
{
- m_variation_high_damp[id] = data;
- logerror("variation high damp %d: %04x (%f)\n", id, data, 0.1+double(data-0x2000)/0x2000*0.9);
+ u8 chan = offset >> 6;
+ m_post_size[chan] = (m_post_size[chan] & 0x0000ffff) | (data << 16);
}
-template<int id> u16 swp30_device::variation_high_damp_r()
+void swp30_device::post_size_l_w(offs_t offset, u16 data)
{
- return m_variation_high_damp[id];
+ u8 chan = offset >> 6;
+ m_post_size[chan] = (m_post_size[chan] & 0xffff0000) | data;
+ logerror("snd chan %02x post-size %02x %06x\n", chan, m_post_size[chan] >> 24, m_post_size[chan] & 0xffffff);
}
-template<int id> void swp30_device::variation_inv_high_damp_w(u16 data)
+u16 swp30_device::address_h_r(offs_t offset)
{
- m_variation_inv_high_damp[id] = data;
- logerror("variation inverse high damp %d: %04x (%f)\n", id, data, 0.1+double(0x2000-data)/0x2000*0.9);
+ return m_address[offset >> 6] >> 16;
}
-template<int id> u16 swp30_device::variation_inv_high_damp_r()
+u16 swp30_device::address_l_r(offs_t offset)
{
- return m_variation_inv_high_damp[id];
+ return m_address[offset >> 6];
}
-
-template<int ins, int id> void swp30_device::insertion_delay_w(u16 data)
+void swp30_device::address_h_w(offs_t offset, u16 data)
{
- m_insertion_delay[ins][id] = data;
- logerror("insertion %d delay %d: %04x (%fms)\n", ins, id, data, data/44.100);
+ u8 chan = offset >> 6;
+ m_address[chan] = (m_address[chan] & 0x0000ffff) | (data << 16);
}
-template<int ins, int id> u16 swp30_device::insertion_delay_r()
+void swp30_device::address_l_w(offs_t offset, u16 data)
{
- return m_insertion_delay[ins][id];
+ u8 chan = offset >> 6;
+ // The address probably is 25 bits
+ static const char *const formats[4] = { "l16", "l12", "l8", "x8" };
+ m_address[chan] = (m_address[chan] & 0xffff0000) | data;
+ logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff);
}
-template<int ins, int id> void swp30_device::insertion_fb_level_w(u16 data)
-{
- m_insertion_fb_level[ins][id] = data;
- logerror("insertion %d feedback level %d: %04x (%+f)\n", ins, id, data, s16(data)/500.0);
-}
-template<int ins, int id> u16 swp30_device::insertion_fb_level_r()
-{
- return m_insertion_fb_level[ins][id];
-}
+// MEG registers (Multiple Effects Generator)
-template<int ins, int id> void swp30_device::insertion_final_level_w(u16 data)
+template<int sel> u16 swp30_device::prg_fp_r(offs_t offset)
{
- m_insertion_final_level[ins][id] = data;
- logerror("insertion %d final level %s %s: %04x (%f)\n", ins, id & 1 ? "wet" : "dry", id & 2 ? "left" : "right", data, data/8191.0);
+ offs_t adr = (offset >> 6)*6 + sel;
+ return m_program_pfp[adr];
}
-template<int ins, int id> u16 swp30_device::insertion_final_level_r()
+template<int sel> void swp30_device::prg_fp_w(offs_t offset, u16 data)
{
- return m_insertion_final_level[ins][id];
+ offs_t adr = (offset >> 6)*6 + sel;
+ m_program_pfp[adr] = data;
+ logerror("prg_fp_w %03x, %04x\n", adr, data);
}
-template<int ins, int id> void swp30_device::insertion_high_damp_w(u16 data)
+template<int sel> u16 swp30_device::prg_int_r(offs_t offset)
{
- m_insertion_high_damp[ins][id] = data;
- logerror("insertion %d high damp %d: %04x (%f)\n", ins, id, data, 0.1+double(data-0x4000)/0x4000*0.9);
+ offs_t adr = (offset >> 6)*2 + sel;
+ return m_program_pint[adr];
}
-template<int ins, int id> u16 swp30_device::insertion_high_damp_r()
+template<int sel> void swp30_device::prg_int_w(offs_t offset, u16 data)
{
- return m_insertion_high_damp[ins][id];
+ offs_t adr = (offset >> 6)*2 + sel;
+ m_program_pint[adr] = data;
+ logerror("prg_int_w %02x, %04x\n", adr, data);
}
-template<int ins, int id> void swp30_device::insertion_inv_high_damp_w(u16 data)
+template<int sel> u16 swp30_device::prg_lfo_r(offs_t offset)
{
- m_insertion_inv_high_damp[ins][id] = data;
- logerror("insertion %s inverse high damp %d: %04x (%f)\n", ins, id, data, 0.1+double(0x4000-data)/0x4000*0.9);
+ offs_t adr = (offset >> 6)*2 + sel;
+ return m_program_plfo[adr];
}
-template<int ins, int id> u16 swp30_device::insertion_inv_high_damp_r()
+template<int sel> void swp30_device::prg_lfo_w(offs_t offset, u16 data)
{
- return m_insertion_inv_high_damp[ins][id];
+ offs_t adr = (offset >> 6)*2 + sel;
+ m_program_plfo[adr] = data;
+
+ static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 };
+ static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 };
+
+ int scale = (data >> 5) & 7;
+ int step = ((data & 31) << sh[scale]) + dt[scale];
+ logerror("prg_lfo_w %02x freq=%5.2f phase=%6.4f\n", adr, step * 44100.0/4194304, (data >> 8)/256.0);
}
+// Catch-all
+
static u16 rr[0x40*0x40];
u16 swp30_device::snd_r(offs_t offset)
{
- if(0) {
+ if(1) {
int chan = (offset >> 6) & 0x3f;
int slot = offset & 0x3f;
std::string preg = "-";
if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("%03x", (slot-0x21)/2 + 6*chan);
- logerror("snd_r [%04x %04x - %-4s] %02x.%02x %04x\n", offset, offset*2, preg, chan, slot, rr[offset]);
- }
- if(offset == 0x080f) {
- return rr[offset] & ~8;
- // return chan == 0x20 && slot == 0xf ? 0 : 0xffff;
+ preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
+ else if(slot == 0x30 || slot == 0x31)
+ preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
+ else if(slot == 0x0e || slot == 0x0f)
+ preg = util::string_format("ct%02x", (slot-0x0e) + 2*chan);
+ else
+ preg = util::string_format("%02x.%02x", chan, slot);
+ logerror("snd_r [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, rr[offset]);
}
+ if(0 && offset == 0x080f)
+ machine().debug_break();
+ if(offset == 0x080f)
+ return 0;
return rr[offset];
}
@@ -578,25 +710,45 @@ void swp30_device::snd_w(offs_t offset, u16 data)
{
if(rr[offset] == data)
return;
- if(offset == 0x4e && (data ^ rr[offset]) == 0x400) {
- rr[offset] = data;
- return;
- }
+
rr[offset] = data;
- if(offset == 0x4e)
- return;
+
int chan = (offset >> 6) & 0x3f;
int slot = offset & 0x3f;
+
+ if(offset == 0x04e)
+ return;
+
+ if(0 && slot == 0x03)
+ machine().debug_break();
+
std::string preg = "-";
if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("%03x", (slot-0x21)/2 + 6*chan);
- logerror("snd_w [%04x %04x - %-4s] %02x.%02x, %04x\n", offset, offset*2, preg, chan, slot, data);
+ preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
+ else if(slot == 0x0e || slot == 0x0f)
+ preg = util::string_format("sy%02x", (slot-0x0e) + 2*chan);
+ else if(slot == 0x30 || slot == 0x31)
+ preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
+ else if(slot == 0x38)
+ preg = util::string_format("vl%02x", chan);
+ else if(slot == 0x3e || slot == 0x3f)
+ preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan);
+ else
+ preg = util::string_format("%02x.%02x", chan, slot);
+ if((slot >= 0xa && slot <= 0xd) || (slot >= 0x2c && slot <= 0x2f))
+ machine().debug_break();
+
+ logerror("snd_w [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, data);
}
+
+
+// Synthesis
+
void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
// Loop first on the samples and not on the channels otherwise
- // effects will be impossible to implement.
+ // effects will be annoying to implement.
for(int sample = 0; sample < samples; sample++) {
// Accumulate on 64 bits, shift/clamp at the end
@@ -661,7 +813,7 @@ void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **i
// Second, step the sample pos, loop/deactivate as needed
m_sample_pos[channel] += m_sample_increment[m_freq[channel] & 0x3fff];
- s32 loop_size = m_post_size[channel] << 8;
+ s32 loop_size = (m_post_size[channel] << 8) | ((m_address[channel] >> 22) & 0xf8);
if(m_sample_pos[channel] >= loop_size) {
// We reached the loop point, stop if loop size is zero,
// otherwise loop
@@ -677,12 +829,12 @@ void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **i
// - missing lpf_cutoff, lpf_reso, hpf_cutoff
// - eq lowpass
- s32 samp1 = (samp * m_eq_filter[channel][0][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][0][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0][0]) >> 13;
+ s32 samp1 = (samp * m_eq_filter[channel][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0]) >> 13;
m_sample_history[channel][0][1] = m_sample_history[channel][0][0];
m_sample_history[channel][0][0] = samp;
// - eq highpass
- s32 samp2 = (samp1 * m_eq_filter[channel][1][2] + m_sample_history[channel][1][0] * m_eq_filter[channel][1][1] + m_sample_history[channel][1][1] * m_eq_filter[channel][1][0]) >> 13;
+ s32 samp2 = (samp1 * m_eq_filter[channel][5] + m_sample_history[channel][1][0] * m_eq_filter[channel][4] + m_sample_history[channel][1][1] * m_eq_filter[channel][3]) >> 13;
m_sample_history[channel][1][1] = m_sample_history[channel][1][0];
m_sample_history[channel][1][0] = samp1;
@@ -700,7 +852,7 @@ void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **i
}
// Samples are 16 bits, there are up to 64 of them, and the accumulators are fixed-point signed 48.16
- // Global EQ is missing
+ // Global EQ is missing (it's done in the MEG)
acc_left >>= (16+6);
if(acc_left < -0x8000)
diff --git a/src/devices/sound/swp30.h b/src/devices/sound/swp30.h
index b33cd99299a..56e24b453f0 100644
--- a/src/devices/sound/swp30.h
+++ b/src/devices/sound/swp30.h
@@ -11,7 +11,7 @@
class swp30_device : public device_t, public device_sound_interface, public device_rom_interface
{
public:
- swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+ swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 33868800);
void map(address_map &map);
@@ -28,75 +28,95 @@ private:
s32 m_linear_attenuation[0x100];
s16 m_sample_log8[0x100];
+ u64 m_program[0x180];
u64 m_keyon_mask, m_active_mask;
u32 m_pre_size[0x40], m_post_size[0x40], m_address[0x40];
s32 m_sample_pos[64];
s32 m_sample_history[0x40][2][2];
- u16 m_volume[0x40], m_freq[0x40], m_pan[0x40];
- u16 m_lpf_cutoff[0x40], m_lpf_reso[0x40], m_hpf_cutoff[0x40];
- s16 m_eq_filter[0x40][2][3];
+ u16 m_program_pfp[0x180], m_program_pint[0x80], m_program_plfo[0x80];
- u16 m_variation_delay[4];
- u16 m_variation_fb_level[3];
- u16 m_variation_final_level[4];
- u16 m_variation_high_damp[2], m_variation_inv_high_damp[2];
+ u16 m_volume[0x40], m_freq[0x40], m_pan[0x40], m_dry_rev[0x40], m_cho_var[0x40];
+ u16 m_envelope[0x40][3];
+ u16 m_lpf_cutoff[0x40], m_lpf_cutoff_inc[0x40], m_lpf_reso[0x40], m_hpf_cutoff[0x40];
+ s16 m_eq_filter[0x40][6];
+ u16 m_routing[0x40][3];
+ u16 m_map[8];
- u16 m_insertion_delay[2][4];
- u16 m_insertion_fb_level[2][3];
- u16 m_insertion_final_level[2][4];
- u16 m_insertion_high_damp[2][2], m_insertion_inv_high_damp[2][2];
+ u16 m_program_address;
+ // 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);
- template<int filter, int coef> u16 eq_filter_r(offs_t offset);
- template<int filter, int coef> void eq_filter_w(offs_t offset, u16 data);
+ template<int sel> u16 envelope_r(offs_t offset);
+ template<int sel> void envelope_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 volume_r(offs_t offset);
void volume_w(offs_t offset, u16 data);
u16 freq_r(offs_t offset);
void freq_w(offs_t offset, u16 data);
- u16 pre_size_r(offs_t offset);
- void pre_size_w(offs_t offset, u16 data);
- u16 post_size_r(offs_t offset);
- void post_size_w(offs_t offset, u16 data);
- u16 address_r(offs_t offset);
- void address_w(offs_t offset, u16 data);
+ u16 pre_size_h_r(offs_t offset);
+ u16 pre_size_l_r(offs_t offset);
+ void pre_size_h_w(offs_t offset, u16 data);
+ void pre_size_l_w(offs_t offset, u16 data);
+ u16 post_size_h_r(offs_t offset);
+ u16 post_size_l_r(offs_t offset);
+ void post_size_h_w(offs_t offset, u16 data);
+ void post_size_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 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);
+ template<int sel> u16 routing_r(offs_t offset);
+ template<int sel> void routing_w(offs_t offset, u16 data);
+
+ // Control registers
template<int sel> u16 keyon_mask_r();
template<int sel> void keyon_mask_w(u16 data);
u16 keyon_r();
void keyon_w(u16);
-
- template<int id> void variation_delay_w(u16 data);
- template<int id> u16 variation_delay_r();
- template<int id> void variation_fb_level_w(u16 data);
- template<int id> u16 variation_fb_level_r();
- template<int id> void variation_final_level_w(u16 data);
- template<int id> u16 variation_final_level_r();
- template<int id> void variation_high_damp_w(u16 data);
- template<int id> u16 variation_high_damp_r();
- template<int id> void variation_inv_high_damp_w(u16 data);
- template<int id> u16 variation_inv_high_damp_r();
-
- template<int ins, int id> void insertion_delay_w(u16 data);
- template<int ins, int id> u16 insertion_delay_r();
- template<int ins, int id> void insertion_fb_level_w(u16 data);
- template<int ins, int id> u16 insertion_fb_level_r();
- template<int ins, int id> void insertion_final_level_w(u16 data);
- template<int ins, int id> u16 insertion_final_level_r();
- template<int ins, int id> void insertion_high_damp_w(u16 data);
- template<int ins, int id> u16 insertion_high_damp_r();
- template<int ins, int id> void insertion_inv_high_damp_w(u16 data);
- template<int ins, int id> u16 insertion_inv_high_damp_r();
-
+ 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);
+
+ // MEG registers
+ template<int sel> u16 prg_fp_r(offs_t offset);
+ template<int sel> void prg_fp_w(offs_t offset, u16 data);
+ template<int sel> u16 prg_int_r(offs_t offset);
+ template<int sel> void prg_int_w(offs_t offset, u16 data);
+ template<int sel> u16 prg_lfo_r(offs_t offset);
+ template<int sel> void prg_lfo_w(offs_t offset, u16 data);
+
+
+ // Generic catch-all
u16 snd_r(offs_t offset);
void snd_w(offs_t offset, u16 data);
+
+ inline auto &rchan(address_map &map, int idx) {
+ return map(idx*2, idx*2+1).select(0x1f80);
+ }
+
+ inline auto &rctrl(address_map &map, int idx) {
+ int slot = 0x40*(idx >> 1) | 0xe | (idx & 1);
+ return map(slot*2, slot*2+1);
+ }
};
DECLARE_DEVICE_TYPE(SWP30, swp30_device)
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp
index c3e4ece9470..73739a44411 100644
--- a/src/emu/debug/debugcpu.cpp
+++ b/src/emu/debug/debugcpu.cpp
@@ -2895,12 +2895,12 @@ void device_debug::watchpoint::setEnabled(bool value)
void device_debug::watchpoint::install(read_or_write mode)
{
- if(m_installing)
+ if (m_installing)
return;
m_installing = true;
- if ((u32(mode) & u32(read_or_write::READ) && m_phr)
+ if ((u32(mode) & u32(read_or_write::READ)) && m_phr)
m_phr->remove();
- if ((u32(mode) & u32(read_or_write::WRITE) && m_phw)
+ if ((u32(mode) & u32(read_or_write::WRITE)) && m_phw)
m_phw->remove();
std::string name = util::string_format("wp@%x", m_address);
switch (m_space.data_width())
diff --git a/src/mame/drivers/ymmu100.cpp b/src/mame/drivers/ymmu100.cpp
index c0e258aa305..9fa97e92680 100644
--- a/src/mame/drivers/ymmu100.cpp
+++ b/src/mame/drivers/ymmu100.cpp
@@ -6,7 +6,7 @@
Preliminary driver by R. Belmont and O. Galibert
CPU: Hitachi H8S/2655 (HD6432655F), strapped for mode 4 (24-bit address, 16-bit data, no internal ROM)
- Sound ASIC: Yamaha XS725A0
+ Sound ASIC: Yamaha XS725A0/SWP30
RAM: 1 MSM51008 (1 meg * 1 bit = 128KBytes)
I/O ports from service manual:
@@ -149,105 +149,6 @@ static INPUT_PORTS_START( mu100 )
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END
-/* 40 41 42 43
-
-reads
- .09 at 3e5a, tests & 0x8000
- .05 at 3e4a: read current to change bits (sets xaxx)
- .0a at 4070: read current to change bits (keeps 14-15)
-
-01.0f at d320: put bits 6-13 in r0l inverted, bits 14-15 in r0h 6-7, does complex calcs from it
-
-
-
-[:] snd_w 00.09, f000 (0020a0)
-[:] snd_w 00.10, 4000 (00d376)
-[:] snd_w 00.00, 169b (00d3be)
-[:] snd_w 00.01, e0ff (00d3d0)
-[:] snd_w 00.02, 8000 (00d3f8)
-[:] snd_w 00.03, 5010 (00d400)
-[:] snd_w 00.04, 0000 (00d412)
-[:] snd_w 00.05, fb00 (00d434)
-[:] snd_w 00.20, e05d (00d43e)
-[:] snd_w 00.22, 1fa3 (00d448)
-[:] snd_w 00.24, 2000 (00d452)
-[:] snd_w 00.26, 0257 (00d45c)
-[:] snd_w 00.28, fda9 (00d466)
-[:] snd_w 00.2a, 2000 (00d470)
-[:] snd_w 00.06, 5d80 (00d48e)
-[:] snd_w 00.07, 1414 (00d4ae) 1614 1614 1614
-[:] snd_w 00.08, 18fe (00d4ce) 1afe 1afe 1afe
-[:] snd_w 00.0a, 5f00 (00d510)
-[:] snd_w 00.0b, 0000 (00d530)
-[:] snd_w 00.0b, 6f00 (00d53a)
-[:] snd_w 00.11, be29 (00d54e) be7e bdd2 be27
-[:] snd_w 00.12, 0000 (00d56c)
-[:] snd_w 00.13, 5c74 (00d570) 6f0b
-[:] snd_w 00.14, 0000 (00d586)
-[:] snd_w 00.15, 749d (00d58a) 749d 5951 5951
-[:] snd_w 00.16, ee42 (00d5d6)
-[:] snd_w 00.17, a89b (00d5da)
-[:] snd_w 00.32, 0808 (00d5ea)
-[:] snd_w 00.33, 182b (00d5fa)
-[:] snd_w 00.34, ff10 (00d60a)
-[:] snd_w 00.35, 0d00 (00d614)
-[:] snd_w 00.36, 0800 (00d61e)
-[:] snd_w 00.37, 0400 (00d628)
-[:] snd_w 00.09, 70e0 (00d634)
-[:] snd_r 00.0b (00d638)
-[:] snd_r 00.0b (00d63c)
-[:] snd_r 00.09 (00d658)
-[:] snd_r 00.09 (00d65c)
-
- e3 = (note - ->m1b) * 100 + s8(->m2b)
-
- compute freq adjustment in 1/100th of semitone (ex. 737)
-clamp to +/- 9599
-(2d74)
-div/rem by 300 -> e6 = 137, r6 = 2
-lookup d14c[rem].b (0-299 -> 00-ff, perfectly linear), tack div on top -> 274
-put sign back in, and with 3fff, -> g58
-
-
-
-
-
-
- 9 = attenuation?
-11 = 52|58 -> 15 flag, 14 zero, 13-0 cents frequency adjustment
-12-13 = pre-size + flags
-14-15 = post-size + flags
-16-17 = address + flags + loop size?
-32 = 6a/6b = pan l/r
-33 = 6c/6d = ? / reverb attenuation
-34 = 6e/6f = chorus attenuation / ?
-35 = 70
-36 = 72
-37 = 74
-3e = ? / chorus lfo frequency
-3f = ? / chorus lfo frequency
-
-09 = 0x7000 + 4c
-read 0b twice, store the second is 2114c7
-
-d510:
- r6 = ffec54 (0)
- if(!0) { ... }
- r5 = r6
- +0b = r5
- r5h = ffec50
- +0b = r5
- +11 = ffec58 | (ffec52 << 8)
- +12, +13 = ffec5c.l | (ffec5a << 24)
- +14, +15 = ffec60.l | (ffec64 << 24)
-
-50: 6f 40 80 00 00 00 00 00 3e 27 00 00 00 00 6f 0b
-60: 00 00 59 51 00 ee 00 42 e1 87 08 08 18 2b ff 10
-
-2e1c computes everything
-
-*/
-
class mu100_state : public driver_device
{
public:
@@ -264,21 +165,101 @@ public:
int seq;
+ void regs_s1_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_s2_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_s3_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_s4a_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_s4b_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_s4c_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_lfo_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_s6_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_fp_read_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_fp_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_int_read_tap(offs_t address, u16 data, u16 mem_mask);
+ void regs_int_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void voice_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void chan_write_tap(offs_t address, u16 data, u16 mem_mask);
+ void prg_write_tap(offs_t address, u16 data, u16 mem_mask);
+
virtual void machine_reset() override {
- timer_alloc()->adjust(attotime::from_double(5));
+ timer_alloc()->adjust(attotime::from_double(5));
seq = 0;
+ act = false;
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214cb8, 0x214cbf, "prg select", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ prg_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x20cb10, 0x20cb10 + 0x122*0x22 - 1, "chan debug", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ chan_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x20f03e, 0x20f03e + 0x92*0x40 - 1, "voice debug", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ voice_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_readwrite_tap(0x214ca2+0x20, 0x214ca2+0x320-1, "regs fp",
+ [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_fp_read_tap(offset, data, mem_mask);
+ },
+ [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_fp_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_readwrite_tap(0x214ca2+0x320, 0x214ca2+0x420-1, "regs int",
+ [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_int_read_tap(offset, data, mem_mask);
+ },
+ [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_int_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x420, 0x214ca2+0x440-1, "regs s1", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s1_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x440, 0x214ca2+0x460-1, "regs s2", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s2_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x460, 0x214ca2+0x480-1, "regs s3", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s3_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x480, 0x214ca2+0x4a0-1, "regs s4a", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s4a_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x4a0, 0x214ca2+0x4c0-1, "regs s4b", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s4b_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x4c0, 0x214ca2+0x4e0-1, "regs s4c", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s4c_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x4e0, 0x214ca2+0x510-1, "regs lfo", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_lfo_write_tap(offset, data, mem_mask);
+ });
+ if(0)
+ m_maincpu->space(0).install_write_tap(0x214ca2+0x510, 0x214ca2+0x520-1, "regs s6", [this](offs_t offset, u16 &data, u16 mem_mask) {
+ regs_s6_write_tap(offset, data, mem_mask);
+ });
}
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override {
static u8 xnote[8] = { 60, 62, 64, 65, 67, 69, 71, 72 };
static int note = 0;
+ act = true;
+ if(1) { seq = 42; return; }
+
switch(seq) {
- case 0: push(0x90); logerror("swp30 midi keyon %d\n", note); timer.adjust(attotime::from_double(0.0005)); if(1) machine().debug_break(); break;
+ case 0: push(0x90); logerror("swp30 midi keyon %d\n", note); timer.adjust(attotime::from_double(0.0005)); if(0) machine().debug_break(); break;
case 1: push(xnote[note]); timer.adjust(attotime::from_double(0.0005)); break;
- case 2: push(0x7f); timer.adjust(attotime::from_double(0.5)); break;
+ case 2: push(0x25); timer.adjust(attotime::from_double(1.0)); break;
case 3: push(0x80); logerror("swp30 midi keyoff\n"); timer.adjust(attotime::from_double(0.0005)); if(0) machine().debug_break(); break;
case 4: push(xnote[note]); timer.adjust(attotime::from_double(0.0005)); ; if(note == 8) note = 0; break;
- case 5: push(0x7f); timer.adjust(attotime::from_double(0.05)); seq=0; return;
+ case 5: push(0x01); timer.adjust(attotime::from_double(0.05)); seq=0; return;
}
seq ++;
}
@@ -343,160 +324,167 @@ private:
virtual u16 adc7_r() override;
};
-#include "../drivers/ymmu100.hxx"
+void mu100_state::prg_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ if(mem_mask == 0x00ff) {
+ static const char *names[4] = { "chorus", "variation", "insertion1", "insertion2" };
+ logerror("prg_select %s %d\n", names[(address - 0x214cb8)/2], data);
+ }
+}
-struct v1 {
- u32 m0;
- u8 m4;
- u8 m5;
- u16 m6;
- u8 m8;
- u8 m9;
- u16 ma;
- u8 mc;
- u8 md;
- u16 me;
-};
+void mu100_state::regs_s1_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x420)/2;
+ if(pc != 0x72912)
+ logerror("regs_s1_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
-// g base = ffec00
-
-// fcf30: 0010 27 1b 00 00b0f5 00 006bc4 ee 402c3e
-// ...
-// fcfd0: 0048 c9 45 00 006f0b 00 005951 ee 42e187
-
-// 4772 616e 6450 2023 GrandP #
-// f5b1e: 0225 007f 017f 58f0 8040 7000 4040 3c77
-// 3c3f 3f3f 3f40 4040 4040 0100 441f 3c54
-// 6c42 4042 45d7 093c 3f01 020d 404e 4640
-// 406c 183c 505c 4045 3e3b 9748 902f 0b0d
-// 2075 0000 0000 0050
-
-//20f03e: 000f5b1e 0020de54 00000000 0000fdd5
-
-#if 0
-// samp2:
-// 000000 - 00 20 27 1b 00 00b0f5 00 006bc4 ee 402c3e
-// 004734 - 00 23 f9 1f 00 00bafd 00 007d3c ee 4075f0
-// 009544 - 00 28 2b 23 00 00cbc6 00 007d50 e4 40c832
-// 00e78b - 00 2b c7 28 00 00c0a6 00 007b70 e4 4117b1
-// 013691 - 00 32 29 2d 00 00bd5e 00 007c62 ee 4165e6
-
-
-// g34 = 0x43 = note
-
-// checksums
-// 2.ef = (0/4000000) | (1 << n)
-// 3.e = 0000
-// 3.f = 0001
-// 4.e = 8000
-// wait until 4.f.b != 0
-// read 5.ef and compare
-// 4.e = 0000
-
-// 0..13 for 32M, 0..12 for 16M
-
-// list of checksums at 58712/58766
-// ff49 ffa0
-// feb3 ff03
-// fd93 fdd7
-// fb76 fbb0
-// f79d f7e7
-
-// f1e40 = select instrument
-void f1e40()
-{
- er3 = g04; // 20de54
- r5 = g29; // 7f - global volume?
- r4 = er3->mcb; // 40 - global volume?
- r5 = r4*r5 >> 6; // -> 7f
- r5 += 2*(er3->md - 0x40);
- r5 = clamp(r5, 1, 0x7f);
- g29 = r5;
- r5 = g20f034; // 01
- if(r5 & 1) {
- r4 = er3->m1b; // 00
- r4 = lcb098[r4]; // byte, 00
- if(!r4) {
- r4 = er3->m2b;
- r6 = g20f031;
- if(!r6)
- r4 = lcb118[r4];
- else {
- r6 = g20f034;
- if(r6 == 3)
- r4 = lcb118[r4];
- else
- r4 = lcb218[r4];
- }
- } else {
- if(r4 != 0x48)
- r4 = lcb198[er3->m2b];
- }
- } else
- r4 = lcb518[er3->m1b];
+void mu100_state::regs_s2_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x440)/2;
+ if(pc != 0x72912)
+ logerror("regs_s2_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
- // r4 -> 55
- r4 = (r4 << 8) | (er3->m3b << 1);
- r4 = cb710 + lb4898[r4 >> 1]; // dword 2a404 -> f5b14 005a GrandP #
- g0c = r4 + 0xe;
+void mu100_state::regs_s3_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x460)/2;
+ if(pc != 0x72912)
+ logerror("regs_s3_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
+void mu100_state::regs_s4a_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x480)/2;
+ if(pc != 0x72912)
+ logerror("regs_s4a_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
+void mu100_state::regs_s4b_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x4a0)/2;
+ if(pc != 0x72912)
+ logerror("regs_s4b_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
}
-void f2ace(er4 *instrument) // f5b1e
+void mu100_state::regs_s4c_write_tap(offs_t address, u16 data, u16 mem_mask)
{
- r6 = (er4->m0 << 7) | er4->m1;
- if(r6 < 0x125)
- r6 = lf692e + tfccde[r6].w;
- else if(r6 < 0x147)
- r6 = lfcf30 + tfdf80[r6 - 0x125].w;
- else
- r6 = lfdfc4 + t100c34[r6 - 0x147].w;
- // r6 = fcf30
- while(r6->m3 & 0x7f <= g34)
- r6 += 0x10;
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x4c0)/2;
+ if(pc != 0x72912)
+ logerror("regs_s4c_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
}
-void f2d0a(...)
+void mu100_state::regs_lfo_write_tap(offs_t address, u16 data, u16 mem_mask)
{
- //
- f2f26(); // calc freq
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x4e0)/2;
+ if(pc != 0x72912)
+ logerror("regs_lfo_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
- // ...
- g3f = f6b14(); // r6h
- f330a(); // sets 6a/6b
- f2e1c(); // sets 5a/64/5c/66/60/65
- f3038(); // sets 38
- f31f8(er4); // sets 46 47 48 4a
- f32a4(); // sets 40
- f3486(); // sets 90/92/94/96/98/9a
- f309c();
+void mu100_state::regs_s6_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x510)/2;
+ if(pc != 0x72912)
+ logerror("regs_s6_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
}
-void f2e1c(er4 **instrument) // 20f03e -> f5b1e
+void mu100_state::regs_fp_read_tap(offs_t address, u16 data, u16 mem_mask)
{
- v1 *xv1 = g18; // fcfd0
- g5a = xv1->m4;
- g64 = xv1->m8;
- er1 = (xv1->m5 << 16) | xv1->m6;
- er2 = (xv1->md << 16) | xv1->me;
- er3 = (xv1->m9 << 16) | xv1->ma;
- er6 = *er4;
- r0 = xv1->mc << 8;
- r5 = (er6->m43 << 7) + er6->m44;
- if(!(g64 & 0x80)) {
- er1 = max(0, er1 - er5);
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x20)/2;
+ if(pc != 0x72912)
+ logerror("regs_fp_r %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
+void mu100_state::regs_fp_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x20)/2;
+ logerror("regs_fp_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
+
+void mu100_state::regs_int_read_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x320)/2;
+ if(pc != 0x729c6)
+ logerror("regs_int_r %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
+
+void mu100_state::regs_int_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ offs_t reg = (address - 0x214ca2-0x320)/2;
+ logerror("regs_int_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
+}
+
+void mu100_state::voice_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ if(!act)
+ return;
+ offs_t off = address - 0x20f03e;
+ int voice = off / 0x92;
+ int slot = off % 0x92;
+ if(mem_mask == 0xffff) {
+ logerror("voice_w %02x:%02x, %04x (%06x)\n", voice, slot, data, pc);
} else {
- // 2ae2
+ if(mem_mask == 0xff00)
+ data >>= 8;
+ else
+ slot++;
+ logerror("voice_w %02x:%02x, %02x (%06x)\n", voice, slot, data, pc);
}
- // 2ef8
- g5c = er1;
- g66 = er2;
- g60 = er3;
- g65 = r0 >> 8;
}
-#endif
+
+void mu100_state::chan_write_tap(offs_t address, u16 data, u16 mem_mask)
+{
+ offs_t pc = m_maincpu->pc();
+ if(!act)
+ return;
+ offs_t off = address - 0x20cb10;
+ int voice = off / 0x112;
+ int slot = off % 0x112;
+ if(mem_mask == 0xffff) {
+ if(slot == 0x102 && data == 0)
+ return;
+ if(slot == 0x100 && data == 0)
+ return;
+ if(slot == 0x0fe && data == 0)
+ return;
+ logerror("chan_w %02x:%03x, %04x (%06x)\n", voice, slot, data, pc);
+ } else {
+ if(mem_mask == 0xff00)
+ data >>= 8;
+ else
+ slot++;
+ if(slot == 0x106 && data == 0)
+ return;
+ if(slot == 0x108 && data == 0)
+ return;
+ if(slot == 0x105) // volume
+ return;
+ if(slot == 0x109 && data == 0)
+ return;
+ if(slot == 0x0e7 && data == 0)
+ return;
+ if(slot == 0x0e5 && data == 0)
+ return;
+ if(slot == 0x111 && data == 0x40)
+ return;
+ logerror("chan_w %02x:%03x, %02x (%06x)\n", voice, slot, data, pc);
+ }
+}
+
+#include "../drivers/ymmu100.hxx"
void mu100_state::machine_start()
{
@@ -560,7 +548,7 @@ void mu100_state::mu100_map(address_map &map)
u16 mu100_state::adc0_r()
{
- logerror("adc0_r\n");
+ // logerror("adc0_r\n");
return 0;
}
@@ -658,7 +646,7 @@ void mu100_state::p6_w(u16 data)
u16 mu100_state::p6_r()
{
- logerror("plug in detect read\n");
+ // logerror("plug in detect read\n");
return 0x00;
}
@@ -706,9 +694,9 @@ void mu100_state::mu100_iomap(address_map &map)
void mu100_state::swp30_map(address_map &map)
{
- map(0x000000*4, 0x200000*4-1).rom().region("swp30", 0).mirror(4*0x200000);
- map(0x400000*4, 0x500000*4-1).rom().region("swp30", 0x800000).mirror(4*0x300000);
- map(0x800000*4, 0xa00000*4-1).rom().region("swp30", 0x1000000).mirror(4*0x200000); // Missing roms...
+ map(0x000000*4, 0x200000*4-1).rom().region("swp30", 0).mirror(4*0x200000);
+ map(0x400000*4, 0x500000*4-1).rom().region("swp30", 0x800000).mirror(4*0x300000);
+ map(0x800000*4, 0xa00000*4-1).rom().region("swp30", 0x1000000).mirror(4*0x200000);
}
void mu100_state::mu100(machine_config &config)
@@ -756,12 +744,12 @@ ROM_START( mu100 )
ROM_LOAD16_WORD_SWAP_BIOS( 1, "xt71420.ic11", 0x000000, 0x200000, CRC(0e5b3bae) SHA1(3148c5bd59a3d00809d3ab1921216215fe2582c5) )
ROM_REGION( 0x1800000, "swp30", ROMREGION_ERASE00 )
- ROM_LOAD32_WORD( "sx518b0.ic34", 0x000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
- ROM_LOAD32_WORD( "sx743b0.ic35", 0x000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
- ROM_LOAD32_WORD( "xt445a0-828.ic36", 0x800000, 0x200000, CRC(225c2280) SHA1(23b5e046fd2e2ac01af3e6dc6357c5c6547b286b) )
- ROM_LOAD32_WORD( "xt461a0-829.ic37", 0x800002, 0x200000, CRC(a1d138a3) SHA1(46a7a7225cd7e1818ba551325d2af5ac1bf5b2bf) )
- ROM_LOAD32_WORD( "xt963a0.ic38", 0x1000000, 0x400000, NO_DUMP )
- ROM_LOAD32_WORD( "xt964a0.ic39", 0x1000002, 0x400000, NO_DUMP )
+ ROM_LOAD32_WORD( "sx518b0.ic34", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
+ ROM_LOAD32_WORD( "sx743b0.ic35", 0x0000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
+ ROM_LOAD32_WORD( "xt445a0-828.ic36", 0x0800000, 0x200000, CRC(225c2280) SHA1(23b5e046fd2e2ac01af3e6dc6357c5c6547b286b) )
+ ROM_LOAD32_WORD( "xt461a0-829.ic37", 0x0800002, 0x200000, CRC(a1d138a3) SHA1(46a7a7225cd7e1818ba551325d2af5ac1bf5b2bf) )
+ ROM_LOAD32_WORD( "xt462a0.ic39", 0x1000000, 0x400000, CRC(cbf037da) SHA1(37449e741243305de38cb913b17041942ad334cd) )
+ ROM_LOAD32_WORD( "xt463a0.ic38", 0x1000002, 0x400000, CRC(cce5f8d3) SHA1(bdca8c5158f452f2b5535c7d658c9b22c6d66048) )
ROM_REGION( 0x1000, "lcd", 0)
// Hand made, 3 characters unused
@@ -781,8 +769,8 @@ ROM_START( mu100r )
ROM_LOAD32_WORD( "sx743b0.ic35", 0x000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
ROM_LOAD32_WORD( "xt445a0-828.ic36", 0x800000, 0x200000, CRC(225c2280) SHA1(23b5e046fd2e2ac01af3e6dc6357c5c6547b286b) )
ROM_LOAD32_WORD( "xt461a0-829.ic37", 0x800002, 0x200000, CRC(a1d138a3) SHA1(46a7a7225cd7e1818ba551325d2af5ac1bf5b2bf) )
- ROM_LOAD32_WORD( "xt963a0.ic38", 0x1000000, 0x400000, NO_DUMP )
- ROM_LOAD32_WORD( "xt964a0.ic39", 0x1000002, 0x400000, NO_DUMP )
+ ROM_LOAD32_WORD( "xt462a0.ic39", 0x1000000, 0x400000, CRC(cbf037da) SHA1(37449e741243305de38cb913b17041942ad334cd) )
+ ROM_LOAD32_WORD( "xt463a0.ic38", 0x1000002, 0x400000, CRC(cce5f8d3) SHA1(bdca8c5158f452f2b5535c7d658c9b22c6d66048) )
ROM_REGION( 0x1000, "lcd", 0)
// Hand made, 3 characters unused