diff options
Diffstat (limited to 'src/devices/sound/swp30.cpp')
-rw-r--r-- | src/devices/sound/swp30.cpp | 1527 |
1 files changed, 1175 insertions, 352 deletions
diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp index f43df8084f9..d135a77f16c 100644 --- a/src/devices/sound/swp30.cpp +++ b/src/devices/sound/swp30.cpp @@ -4,8 +4,11 @@ // Yamaha SWP30/30B, rompler/dsp combo #include "emu.h" +#include "debugger.h" #include "swp30.h" +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 @@ -31,9 +34,9 @@ per-channel values for AWM2, but parts are isolated and renumbered for MEG regisrers or for general control functions. - Names we'll use in th rest of the text: + 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, xx) for a given AWG2 channel + - 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)) @@ -52,7 +55,7 @@ 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 + highpass). Envelopes are handled semi-automatically, and the final panned result is sent to the mixer. @@ -62,13 +65,19 @@ ch03 40ff at startup, 5010 always afterwards? ch04 fixed LPF resonance level ch05 unknown - ch06-09 envelope information, not understood yet + 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 + ch0a-0d unknown, probably something to do with pitch eg ch10 unknown - ch11 channel replay frequency, signed 4.10 fixed point, log2 scale, positive is higher resulting frequency - ch12-13 number of samples before the loop point - ch14-15 number of samples in the loop - ch16-17 bit 31-30 = sample format, bits 29-25 = loop samples decimal part, 24-0 = loop start address in rom + ch11 bit 15 = compressed 8-bits mode, 13-0 channel replay frequency, signed 3.10 fixed point, + log2 scale, positive is higher resulting frequency. + + 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 @@ -80,6 +89,15 @@ sy10 write something to trigger a keyon according to the mask + 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: + + step = (8 + bit 2..0) << (bit 7..4) + + giving a value between 8 and 0x78000. This value is added or + substracted after each sample. MEG: @@ -99,7 +117,7 @@ Every 44100th of a second the 384 program steps are run once in order (no branches) to compute everything. - 24 LFO registers are available (possibly more). The LFO registers + 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, @@ -108,14 +126,16 @@ 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. + interpreted as a 0-1 value, giving a sawtooth wave. When an + instruction uses the lfo, which one is selected by using pc/16. 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: 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. + 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. fp<nnn> fixed point 2.14 value associated with instruction nnn @@ -133,7 +153,7 @@ 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 - is 4.4 floating point format (multiplies by (1-mant/2)*2**(-exp)). + in 4.4 floating point format (multiplies by (1-mant/2)*2**(-exp)). The routing is indicated through triplets of 16-bits values. ch33 dry (msb) and reverb (lsb) attenuation for an AWM2 channel @@ -141,129 +161,439 @@ ch35-37 routing for an AWM2 channel - */ DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip") -swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, SWP30, tag, owner, clock), - device_sound_interface(mconfig, *this), - device_rom_interface(mconfig, *this, 25+2, ENDIANNESS_LITTLE, 32), - m_meg(*this, "meg") +bool swp30_device::istep(s32 &value, s32 limit, s32 step) { - (void)m_map; + // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step); + if(value < limit) { + value += step; + if(value >= limit) { + value = limit; + return true; + } + return false; + } + + if(value > limit) { + value -= step; + if(value <= limit) { + value = limit; + return true; + } + return false; + } + + return true; } -void swp30_device::device_add_mconfig(machine_config &config) +s32 swp30_device::fpadd(s32 value, s32 step) { - MEGEMB(config, m_meg); + s32 e = value >> 24; + s32 m = value & 0xffffff; + + m += step << e; + if(m & 0xfe000000) + return 0xfffffff; + + while(m & 0x01000000) { + m <<= 1; + e ++; + } + if(e >= 16) + return 0xfffffff; + return (e << 24) | (m & 0xffffff); } -void swp30_device::device_start() +s32 swp30_device::fpsub(s32 value, s32 step) { - m_stream = stream_alloc(0, 2, 44100); + s32 e = value >> 24; + s32 m = (value & 0xffffff) | 0xfe000000; + m = e < 0xc ? m - (step << e) : (m >> (e - 0xb)) - (step << 0xb); + if(m >= 0) + return 0; + if(e >= 0xc) + e = 0xb; + while(m < 0xfe000000) { + if(!e) + return 0; + e --; + m >>= 1; + } + while(e != 0xf && (m >= 0xff000000)) { + e ++; + m <<= 1; + } - // 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 1.16 - // format, to avoid overflow + return (e << 24) | (m & 0xffffff); +} - for(int i=0; i<256; i++) - m_linear_attenuation[i] = ((32 - (i & 15)) << (0xf ^ (i >> 4))) >> 4; +bool swp30_device::fpstep(s32 &value, s32 limit, s32 step) +{ + // value, limit and step are 4.24 but step has its exponent and + // top four bits zero + + if(value == limit) + return true; + if(value < limit) { + value = fpadd(value, step); + if(value >= limit) { + value = limit; + return true; + } + return false; + } - // 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). + value = fpsub(value, step); + if(value <= limit) { + value = limit; + return true; + } + return false; +} + +// sample is signed 24.8 +s32 swp30_device::fpapply(s32 value, s32 sample) +{ + if(value >= 0x10000000) + return 0; + return (s64(sample) - ((s64(sample) * ((value >> 9) & 0x7fff)) >> 16)) >> (value >> 24); +} - for(int i=-0x20000; i<0x2000; i++) - m_sample_increment[i & 0x3fff] = 256 * pow(2, i/1024.0); +// sample is signed 24.8 +s32 swp30_device::lpffpapply(s32 value, s32 sample) +{ + return ((((value >> 7) & 0x7fff) | 0x8000) * s64(sample)) >> (31 - (value >> 22)); +} - // 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. Ends up with a 55dB dynamic range, to compare - // with 8bits 48dB, 12bits 72dB and 16bits 96dB. +// 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, +}; + +// 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, +}; + +// 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 + +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 }; - // Rescale so that it's roughly 16 bits. Range ends up being +/- 78c0. +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)) +{ +} - for(int i=0; i<32; i++) { - m_sample_log8[ i] = i << 0; - m_sample_log8[0x20|i] = (i << 1) + 0x21; - m_sample_log8[0x40|i] = (i << 2) + 0x62; - m_sample_log8[0x60|i] = (i << 3) + 0xe3; +void swp30_device::device_start() +{ + m_program = &space(AS_PROGRAM); + m_rom = &space(AS_DATA); + m_reverb = &space(AS_REVERB); + m_rom->cache(m_rom_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_stream = stream_alloc(0, 2, 44100, STREAM_SYNCHRONOUS); + + for(int i=0; i != 128; i++) { + u32 v = 0; + switch(i >> 3) { + default: v = ((i & 7) + 8) << (1 + (i >> 3)); break; + case 0xb: v = ((i & 7) + 4) << 13; break; + case 0xc: v = ((i & 6) + 6) << 14; break; + case 0xd: v = ((i & 4) + 7) << 15; break; + case 0xe: v = 15 << 15; break; + case 0xf: v = 31 << 15; break; + } + m_global_step[i] = v; } + + // Delta-packed samples decompression. + for(int i=0; i<128; i++) { - m_sample_log8[i] = m_sample_log8[i] << 6; - m_sample_log8[i | 0x80] = -m_sample_log8[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_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_start)); + save_item(NAME(m_sample_end)); + save_item(NAME(m_sample_address)); + save_item(NAME(m_pitch)); + save_item(NAME(m_release_glo)); + + save_item(NAME(m_lfo_step_pmod)); + save_item(NAME(m_lfo_amod)); + + save_item(NAME(m_attack)); + save_item(NAME(m_decay1)); + save_item(NAME(m_decay2)); + + save_item(NAME(m_lfo_phase)); save_item(NAME(m_sample_pos)); - save_item(NAME(m_sample_history)); + 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_program_pfp)); - save_item(NAME(m_program_pint)); - save_item(NAME(m_program_plfo)); + save_item(NAME(m_sample_history)); - 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)); + 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(STRUCT_MEMBER(m_mixer, vol)); + save_item(STRUCT_MEMBER(m_mixer, route)); } void swp30_device::device_reset() { - memset(m_program, 0, sizeof(m_program)); - m_keyon_mask = 0; - m_active_mask = 0; - memset(m_pre_size, 0, sizeof(m_pre_size)); - memset(m_post_size, 0, sizeof(m_post_size)); - memset(m_address, 0, sizeof(m_address)); + 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); - memset(m_sample_pos, 0, sizeof(m_sample_pos)); - memset(m_sample_history, 0, sizeof(m_sample_history)); + 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); - 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)); + 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)); - 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)); - m_program_address = 0; -} - -void swp30_device::rom_bank_updated() -{ - m_stream->update(); + m_meg_program_address = 0; + m_waverom_adr = 0; + m_waverom_mode = 0; + m_waverom_access = 0; + m_waverom_val = 0; } void swp30_device::map(address_map &map) @@ -275,20 +605,21 @@ void swp30_device::map(address_map &map) 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 + rchan(map, 0x05).rw(FUNC(swp30_device::lfo_amod_r), FUNC(swp30_device::lfo_amod_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)); + // 0b-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, 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>)); @@ -296,49 +627,66 @@ void swp30_device::map(address_map &map) 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 + // 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, 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>)); + 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::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>)); + 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>)); + rctrl(map, 0x36).rw(FUNC(swp30_device::meg_map_r<3>), FUNC(swp30_device::meg_map_w<3>)); + rctrl(map, 0x38).rw(FUNC(swp30_device::meg_map_r<4>), FUNC(swp30_device::meg_map_w<4>)); + 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>)); // 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_off_r<0>), FUNC(swp30_device::prg_off_w<0>)); - rchan(map, 0x31).rw(FUNC(swp30_device::prg_off_r<1>), FUNC(swp30_device::prg_off_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>)); + rchan(map, 0x21).rw(FUNC(swp30_device::meg_const_r<0>), FUNC(swp30_device::meg_const_w<0>)); + rchan(map, 0x23).rw(FUNC(swp30_device::meg_const_r<1>), FUNC(swp30_device::meg_const_w<1>)); + rchan(map, 0x25).rw(FUNC(swp30_device::meg_const_r<2>), FUNC(swp30_device::meg_const_w<2>)); + rchan(map, 0x27).rw(FUNC(swp30_device::meg_const_r<3>), FUNC(swp30_device::meg_const_w<3>)); + rchan(map, 0x29).rw(FUNC(swp30_device::meg_const_r<4>), FUNC(swp30_device::meg_const_w<4>)); + rchan(map, 0x2b).rw(FUNC(swp30_device::meg_const_r<5>), FUNC(swp30_device::meg_const_w<5>)); + rchan(map, 0x30).rw(FUNC(swp30_device::meg_offset_r<0>), FUNC(swp30_device::meg_offset_w<0>)); + rchan(map, 0x31).rw(FUNC(swp30_device::meg_offset_r<1>), FUNC(swp30_device::meg_offset_w<1>)); + rchan(map, 0x3e).rw(FUNC(swp30_device::meg_lfo_r<0>), FUNC(swp30_device::meg_lfo_w<0>)); + rchan(map, 0x3f).rw(FUNC(swp30_device::meg_lfo_r<1>), FUNC(swp30_device::meg_lfo_w<1>)); + + // Mixer registers + rchan(map, 0x32).rw(FUNC(swp30_device::vol_r <0x00|0>), FUNC(swp30_device::vol_w <0x00|0>)); + rchan(map, 0x33).rw(FUNC(swp30_device::vol_r <0x00|1>), FUNC(swp30_device::vol_w <0x00|1>)); + rchan(map, 0x34).rw(FUNC(swp30_device::vol_r <0x00|2>), FUNC(swp30_device::vol_w <0x00|2>)); + rchan(map, 0x35).rw(FUNC(swp30_device::route_r<0x00|0>), FUNC(swp30_device::route_w<0x00|0>)); + rchan(map, 0x36).rw(FUNC(swp30_device::route_r<0x00|1>), FUNC(swp30_device::route_w<0x00|1>)); + rchan(map, 0x37).rw(FUNC(swp30_device::route_r<0x00|2>), FUNC(swp30_device::route_w<0x00|2>)); + rchan(map, 0x38).rw(FUNC(swp30_device::vol_r <0x40|0>), FUNC(swp30_device::vol_w <0x40|0>)); + rchan(map, 0x39).rw(FUNC(swp30_device::vol_r <0x40|1>), FUNC(swp30_device::vol_w <0x40|1>)); + rchan(map, 0x3a).rw(FUNC(swp30_device::vol_r <0x40|2>), FUNC(swp30_device::vol_w <0x40|2>)); + rchan(map, 0x3b).rw(FUNC(swp30_device::route_r<0x40|0>), FUNC(swp30_device::route_w<0x40|0>)); + rchan(map, 0x3c).rw(FUNC(swp30_device::route_r<0x40|1>), FUNC(swp30_device::route_w<0x40|1>)); + rchan(map, 0x3d).rw(FUNC(swp30_device::route_r<0x40|2>), FUNC(swp30_device::route_w<0x40|2>)); } // Control registers @@ -359,62 +707,141 @@ u16 swp30_device::keyon_r() void swp30_device::keyon_w(u16) { - m_stream->update(); - for(int i=0; i<64; i++) { - 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); - if(0) - 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; + 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; + + 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]); } } m_keyon_mask = 0; } -u16 swp30_device::prg_address_r() +u16 swp30_device::meg_prg_address_r() { - return m_program_address; + return m_meg_program_address; } -void swp30_device::prg_address_w(u16 data) +void swp30_device::meg_prg_address_w(u16 data) { - m_program_address = data; - if(m_program_address >= 0x180) - m_program_address = 0; + m_meg_program_address = data; + if(m_meg_program_address >= 0x180) + m_meg_program_address = 0; } -template<int sel> u16 swp30_device::prg_r() +template<int sel> u16 swp30_device::meg_prg_r() { constexpr offs_t shift = 48-16*sel; - return m_meg->prg_r(m_program_address) >> shift; + return m_meg_program[m_meg_program_address] >> shift; } -template<int sel> void swp30_device::prg_w(u16 data) +template<int sel> void swp30_device::meg_prg_w(u16 data) { constexpr offs_t shift = 48-16*sel; constexpr u64 mask = ~(u64(0xffff) << shift); - m_meg->prg_w(m_program_address, (m_meg->prg_r(m_program_address) & mask) | (u64(data) << shift)); + m_meg_program[m_meg_program_address] = (m_meg_program[m_meg_program_address] & mask) | (u64(data) << shift); if(sel == 3) { if(0) - 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; + 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; } } -template<int sel> u16 swp30_device::map_r() +template<int sel> u16 swp30_device::meg_map_r() { - return m_meg->map_r(sel); + return m_meg_map[sel]; } -template<int sel> void swp30_device::map_w(u16 data) +template<int sel> void swp30_device::meg_map_w(u16 data) { - m_meg->map_w(sel, 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)); +} + + +template<int sel> void swp30_device::waverom_adr_w(u16 data) +{ + if(sel) + m_waverom_adr = (m_waverom_adr & 0x0000ffff) | (data << 16); + else + m_waverom_adr = (m_waverom_adr & 0xffff0000) | data; +} + +template<int sel> u16 swp30_device::waverom_adr_r() +{ + return m_waverom_adr >> (16*sel); +} + +template<int sel> void swp30_device::waverom_mode_w(u16 data) +{ + if(sel) + m_waverom_mode = (m_waverom_mode & 0x0000ffff) | (data << 16); + else + m_waverom_mode = (m_waverom_mode & 0xffff0000) | data; +} + +template<int sel> u16 swp30_device::waverom_mode_r() +{ + return m_waverom_mode >> (16*sel); +} + +void swp30_device::waverom_access_w(u16 data) +{ + m_waverom_access = data; + 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); + } +} + +u16 swp30_device::waverom_access_r() +{ + return m_waverom_access; +} + +u16 swp30_device::waverom_busy_r() +{ + // 0 = busy reading the rom, non-0 = finished + return 0xffff; +} + +template<int sel> u16 swp30_device::waverom_val_r() +{ + return m_waverom_val >> (16*sel); } @@ -426,7 +853,6 @@ u16 swp30_device::lpf_cutoff_r(offs_t offset) void swp30_device::lpf_cutoff_w(offs_t offset, u16 data) { - m_stream->update(); u8 chan = offset >> 6; if(0 && m_lpf_cutoff[chan] != data) logerror("chan %02x lpf cutoff %04x\n", chan, data); @@ -440,7 +866,6 @@ u16 swp30_device::lpf_cutoff_inc_r(offs_t offset) void swp30_device::lpf_cutoff_inc_w(offs_t offset, u16 data) { - m_stream->update(); u8 chan = offset >> 6; if(0 && m_lpf_cutoff_inc[chan] != data) logerror("chan %02x lpf cutoff increment %04x\n", chan, data); @@ -454,7 +879,6 @@ u16 swp30_device::hpf_cutoff_r(offs_t offset) void swp30_device::hpf_cutoff_w(offs_t offset, u16 data) { - m_stream->update(); u8 chan = offset >> 6; if(0 && m_hpf_cutoff[chan] != data) logerror("chan %02x hpf cutoff %04x\n", chan, data); @@ -468,7 +892,6 @@ u16 swp30_device::lpf_reso_r(offs_t offset) void swp30_device::lpf_reso_w(offs_t offset, u16 data) { - m_stream->update(); u8 chan = offset >> 6; if(0 && m_lpf_reso[chan] != data) logerror("chan %02x lpf resonance %04x\n", chan, data); @@ -482,215 +905,259 @@ template<int coef> u16 swp30_device::eq_filter_r(offs_t offset) template<int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data) { - m_stream->update(); m_eq_filter[offset >> 6][coef] = data; } -template<int sel> u16 swp30_device::routing_r(offs_t offset) +template<int sel> u16 swp30_device::vol_r(offs_t offset) { - return m_routing[offset >> 6][sel]; + return m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3]; } -template<int sel> void swp30_device::routing_w(offs_t offset, u16 data) +template<int sel> void swp30_device::vol_w(offs_t offset, u16 data) { - m_stream->update(); - m_routing[offset >> 6][sel] = data; + m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3] = data; } -u16 swp30_device::volume_r(offs_t offset) +template<int sel> u16 swp30_device::route_r(offs_t offset) { - int chan = offset >> 6; - return m_volume[chan]; + return m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3]; } -void swp30_device::volume_w(offs_t offset, u16 data) +template<int sel> void swp30_device::route_w(offs_t offset, u16 data) { - m_stream->update(); - u8 chan = offset >> 6; - if(0 && m_volume[chan] != data) - logerror("snd chan %02x volume %02x %02x\n", chan, data >> 8, data & 0xff); - m_volume[chan] = data; - if(data & 0x8000) { - if(m_active_mask & (u64(1) << chan)) { - if(m_post_size[chan]) - m_active_mask &= ~(u64(1) << chan); - } - } + m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3] = data; } - -u16 swp30_device::pan_r(offs_t offset) +u16 swp30_device::release_glo_r(offs_t offset) { - return m_pan[offset >> 6]; + return m_release_glo[offset >> 6]; } -void swp30_device::pan_w(offs_t offset, u16 data) +void swp30_device::release_glo_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - if(0 && m_pan[chan] != data) - logerror("snd chan %02x pan l %02x r %02x\n", chan, data >> 8, data & 0xff); - m_pan[chan] = data; + 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; } -u16 swp30_device::dry_rev_r(offs_t offset) +u16 swp30_device::pitch_r(offs_t offset) { - return m_dry_rev[offset >> 6]; + return m_pitch[offset >> 6]; } -void swp30_device::dry_rev_w(offs_t offset, u16 data) +void swp30_device::pitch_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - if(0 && m_dry_rev[chan] != data) - logerror("snd chan %02x dry %02x rev %02x\n", chan, data >> 8, data & 0xff); - m_dry_rev[chan] = data; + // 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; } -u16 swp30_device::cho_var_r(offs_t offset) +u16 swp30_device::attack_r(offs_t offset) { - return m_cho_var[offset >> 6]; + return m_attack[offset >> 6]; } -void swp30_device::cho_var_w(offs_t offset, u16 data) +void swp30_device::attack_w(offs_t offset, u16 data) { - u8 chan = offset >> 6; - if(0 && m_cho_var[chan] != data) - logerror("snd chan %02x cho %02x var %02x\n", chan, data >> 8, data & 0xff); - m_cho_var[chan] = data; + if(data != m_attack[offset >> 6]) + logerror("attack[%02x] = %04x\n", offset >> 6, data); + m_attack[offset >> 6] = data; } -u16 swp30_device::freq_r(offs_t offset) +u16 swp30_device::decay1_r(offs_t offset) { - return m_freq[offset >> 6]; + return m_decay1[offset >> 6]; } -void swp30_device::freq_w(offs_t offset, u16 data) +void swp30_device::decay1_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_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; + logerror("decay1[%02x] = %04x\n", offset >> 6, data); + m_decay1[offset >> 6] = data; } -template<int sel> u16 swp30_device::envelope_r(offs_t offset) +u16 swp30_device::decay2_r(offs_t offset) { - return m_envelope[offset >> 6][sel]; + return m_decay2[offset >> 6]; } -template<int sel> void swp30_device::envelope_w(offs_t offset, u16 data) +void swp30_device::decay2_w(offs_t offset, u16 data) { - u8 chan = offset >> 6; - bool ch = m_envelope[chan][sel] != data; - m_envelope[chan][sel] = data; - if(0 && ch) - logerror("snd chan %02x envelopes %04x %04x %04x\n", chan, m_envelope[chan][0], m_envelope[chan][1], m_envelope[chan][2]); + logerror("decay2[%02x] = %04x\n", offset >> 6, data); + m_decay2[offset >> 6] = data; +} + +u16 swp30_device::lfo_step_pmod_r(offs_t offset) +{ + return m_lfo_step_pmod[offset >> 6]; +} + +void swp30_device::lfo_step_pmod_w(offs_t offset, u16 data) +{ + // logerror("lfo_step_pmod[%02x] = %04x\n", offset >> 6, data); + m_lfo_step_pmod[offset >> 6] = data; +} + +u16 swp30_device::lfo_amod_r(offs_t offset) +{ + return m_lfo_amod[offset >> 6]; +} + +void swp30_device::lfo_amod_w(offs_t offset, u16 data) +{ + // logerror("lfo_amod[%02x] = %04x\n", offset >> 6, data); + m_lfo_amod[offset >> 6] = data; } -u16 swp30_device::pre_size_h_r(offs_t offset) +u16 swp30_device::sample_start_h_r(offs_t offset) { - return m_pre_size[offset >> 6] >> 16; + return m_sample_start[offset >> 6] >> 16; } -u16 swp30_device::pre_size_l_r(offs_t offset) +u16 swp30_device::sample_start_l_r(offs_t offset) { - return m_pre_size[offset >> 6]; + return m_sample_start[offset >> 6]; } -void swp30_device::pre_size_h_w(offs_t offset, u16 data) +void swp30_device::sample_start_h_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - m_pre_size[chan] = (m_pre_size[chan] & 0x0000ffff) | (data << 16); + m_sample_start[chan] = (m_sample_start[chan] & 0x0000ffff) | (data << 16); } -void swp30_device::pre_size_l_w(offs_t offset, u16 data) +void swp30_device::sample_start_l_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - m_pre_size[chan] = (m_pre_size[chan] & 0xffff0000) | data; - if(0) - logerror("snd chan %02x pre-size %02x %06x\n", chan, m_pre_size[chan] >> 24, m_pre_size[chan] & 0xffffff); + m_sample_start[chan] = (m_sample_start[chan] & 0xffff0000) | data; } -u16 swp30_device::post_size_h_r(offs_t offset) +u16 swp30_device::sample_end_h_r(offs_t offset) { - return m_post_size[offset >> 6] >> 16; + return m_sample_end[offset >> 6] >> 16; } -u16 swp30_device::post_size_l_r(offs_t offset) +u16 swp30_device::sample_end_l_r(offs_t offset) { - return m_post_size[offset >> 6]; + return m_sample_end[offset >> 6]; } -void swp30_device::post_size_h_w(offs_t offset, u16 data) +void swp30_device::sample_end_h_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - m_post_size[chan] = (m_post_size[chan] & 0x0000ffff) | (data << 16); + m_sample_end[chan] = (m_sample_end[chan] & 0x0000ffff) | (data << 16); } -void swp30_device::post_size_l_w(offs_t offset, u16 data) +void swp30_device::sample_end_l_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - m_post_size[chan] = (m_post_size[chan] & 0xffff0000) | data; + m_sample_end[chan] = (m_sample_end[chan] & 0xffff0000) | data; if(0) - logerror("snd chan %02x post-size %02x %06x\n", chan, m_post_size[chan] >> 24, m_post_size[chan] & 0xffffff); + logerror("snd chan %02x post-size %02x %06x\n", chan, m_sample_end[chan] >> 24, m_sample_end[chan] & 0xffffff); } -u16 swp30_device::address_h_r(offs_t offset) +u16 swp30_device::sample_address_h_r(offs_t offset) { - return m_address[offset >> 6] >> 16; + return m_sample_address[offset >> 6] >> 16; } -u16 swp30_device::address_l_r(offs_t offset) +u16 swp30_device::sample_address_l_r(offs_t offset) { - return m_address[offset >> 6]; + return m_sample_address[offset >> 6]; } -void swp30_device::address_h_w(offs_t offset, u16 data) +void swp30_device::sample_address_h_w(offs_t offset, u16 data) { u8 chan = offset >> 6; - m_address[chan] = (m_address[chan] & 0x0000ffff) | (data << 16); + m_sample_address[chan] = (m_sample_address[chan] & 0x0000ffff) | (data << 16); } -void swp30_device::address_l_w(offs_t offset, u16 data) +void swp30_device::sample_address_l_w(offs_t offset, u16 data) { u8 chan = offset >> 6; static const char *const formats[4] = { "l16", "l12", "l8", "x8" }; - m_address[chan] = (m_address[chan] & 0xffff0000) | data; + 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_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff); + 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); } +u16 swp30_device::internal_adr_r() +{ + return m_internal_adr; +} -// MEG registers forwarding +void swp30_device::internal_adr_w(u16 data) +{ + m_internal_adr = data; +} -template<int sel> u16 swp30_device::prg_fp_r(offs_t offset) +u16 swp30_device::internal_r() { - return m_meg->fp_r((offset >> 6)*6 + sel); + 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)); + + case 4: + // used at 44c4 + // tests & 0x4000 only + // logerror("read %02x.4\n", chan); + return 0x0000; + + case 6: + return m_decay2_done[chan] ? 0x0000 : 0x8000; + } + + 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(); + + return 0; } -template<int sel> void swp30_device::prg_fp_w(offs_t offset, u16 data) + +// MEG registers + +template<int sel> u16 swp30_device::meg_const_r(offs_t offset) +{ + return m_meg_const[(offset >> 6)*6 + sel]; +} + +template<int sel> void swp30_device::meg_const_w(offs_t offset, u16 data) { - m_meg->fp_w((offset >> 6)*6 + sel, data); + m_meg_const[(offset >> 6)*6 + sel] = data; } -template<int sel> u16 swp30_device::prg_off_r(offs_t offset) +template<int sel> u16 swp30_device::meg_offset_r(offs_t offset) { - return m_meg->offset_r((offset >> 6)*2 + sel); + return m_meg_offset[(offset >> 6)*2 + sel]; } -template<int sel> void swp30_device::prg_off_w(offs_t offset, u16 data) +template<int sel> void swp30_device::meg_offset_w(offs_t offset, u16 data) { - m_meg->offset_w((offset >> 6)*2 + sel, data); + m_meg_offset[(offset >> 6)*2 + sel] = data; } -template<int sel> u16 swp30_device::prg_lfo_r(offs_t offset) +template<int sel> u16 swp30_device::meg_lfo_r(offs_t offset) { - return m_meg->lfo_r((offset >> 6)*2 + sel); + return m_meg_lfo[(offset >> 6)*2 + sel]; } -template<int sel> void swp30_device::prg_lfo_w(offs_t offset, u16 data) +template<int sel> void swp30_device::meg_lfo_w(offs_t offset, u16 data) { - m_meg->lfo_w((offset >> 6)*2 + sel, data); + int slot = (offset >> 6)*2 + sel; + m_meg_lfo[slot] = 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("lfo_w %02x %04x freq=%5.2f phase=%6.4f\n", slot, m_meg_lfo[slot], step * 44100.0/4194304, (data >> 8)/256.0); } @@ -715,8 +1182,6 @@ u16 swp30_device::snd_r(offs_t offset) 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]; @@ -735,9 +1200,6 @@ void swp30_device::snd_w(offs_t offset, u16 data) 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("fp%03x", (slot-0x21)/2 + 6*chan); @@ -745,143 +1207,504 @@ void swp30_device::snd_w(offs_t offset, u16 data) 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 >= 0x38 && slot <= 0x3a) + preg = util::string_format("mix[%x, %02x]", slot - 0x38, chan); + else if(slot >= 0x3b && slot <= 0x3d) + preg = util::string_format("route[%x, %02x]", slot - 0x3b, chan); else if(slot == 0x3e || slot == 0x3f) - preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan); + preg = util::string_format("lfo[%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 +// Synthesis and meg -void swp30_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +uint32_t swp30_device::execute_min_cycles() const noexcept { - // Loop first on the samples and not on the channels otherwise - // effects will be annoying to implement. + return 1; +} + +uint32_t swp30_device::execute_max_cycles() const noexcept +{ + return 1; +} + + +void swp30_device::meg_prg_map(address_map &map) +{ + map(0x000, 0x1bf).r(FUNC(swp30_device::meg_prg_map_r)); +} + +u64 swp30_device::meg_prg_map_r(offs_t address) +{ + return m_meg_program[address]; +} + +void swp30_device::meg_reverb_map(address_map &map) +{ + map(0x00000, 0x3ffff).ram(); +} + +u16 swp30_device::swp30d_const_r(u16 address) const +{ + return m_meg_const[address]; +} + +u16 swp30_device::swp30d_offset_r(u16 address) const +{ + return m_meg_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_REVERB, &m_reverb_config), + }; +} + +std::unique_ptr<util::disasm_interface> swp30_device::create_disassembler() +{ + return std::make_unique<swp30_disassembler>(this); +} + +void swp30_device::state_import(const device_state_entry &entry) +{ +} + +void swp30_device::state_export(const device_state_entry &entry) +{ +} + +void swp30_device::state_string_export(const device_state_entry &entry, std::string &str) const +{ +} + +void swp30_device::sound_stream_update(sound_stream &stream) +{ + stream.put_int_clamp(0, 0, m_meg_output[0], 32768); + stream.put_int_clamp(1, 0, m_meg_output[1], 32768); +} + +void swp30_device::change_mode_attack_decay1(int chan) +{ + 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) +{ + m_envelope_mode[chan] = DECAY2; + m_envelope_timer[chan] = 0x8000000; + m_envelope_on_timer[chan] = (m_decay2[chan] & 0xff) == (m_decay1[chan] & 0xff); +} + +s32 swp30_device::meg_att(s32 sample, s32 att) +{ + if(att >= 0xff) + return 0; + return (sample - ((sample * (att & 0xf)) >> 4)) >> (att >> 4); + +} + +void swp30_device::execute_run() +{ + 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]); + } + } + } - for(int sample = 0; sample < samples; sample++) { - // Accumulate on 64 bits, shift/clamp at the end - s64 acc_left = 0, acc_right = 0; - // Loop on channels - for(int channel = 0; channel < 64; channel++) - if(m_active_mask & (u64(1) << channel)) { + // 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; + } + + // 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 spos = m_sample_pos[channel] >> 8; - offs_t base_address = (m_address[channel] & 0x1ffffff) << 2; + 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 samp = 0; - switch(m_address[channel] >> 30) { + s16 val0, val1; + switch(m_sample_address[chan] >> 30) { case 0: { // 16-bits linear - offs_t adr = base_address + (spos << 1); - samp = read_word(adr); + 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; } case 1: { // 12-bits linear - offs_t adr = base_address + (spos >> 2)*6; - switch(spos & 3) { - case 0: { // .abc .... .... - u16 w0 = read_word(adr); - samp = (w0 & 0x0fff) << 4; + 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 1: { // C... ..AB .... - u16 w0 = read_word(adr); - u16 w1 = read_word(adr+2); - samp = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8); + 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 2: { // .... bc.. ...a - u16 w0 = read_word(adr+2); - u16 w1 = read_word(adr+4); - samp = ((w0 & 0xff00) >> 4) | ((w1 & 0x000f) << 12); + case 3: { // ........ .ABCabc. ........ ........ + u32 l1 = m_rom_cache.read_dword(adr+1); + val0 = l1 & 0x0000fff0; + val1 = (l1 & 0x0fff0000) >> 12; break; } - case 3: { // .... .... ABC. - u16 w1 = read_word(adr+4); - samp = w1 & 0xfff0; + 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; } - case 2: // 8-bits linear - samp = read_byte(base_address + spos) << 8; + 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; + } - case 3: // 8-bits logarithmic - samp = m_sample_log8[read_byte(base_address + spos)]; + 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; } - - //logerror("sample %02x %06x [%d] %+5d %04x %04x %04x\n", channel, base_address >> 2, m_address[channel] >> 30, spos, samp & 0xffff, m_volume[channel], m_pan[channel]); - - // 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) | ((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 - if(!loop_size) - m_active_mask &= ~((u64(1) << channel)); - else - do - m_sample_pos[channel] -= loop_size; - while(m_sample_pos[channel] >= loop_size); } + 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[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; + 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[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; + 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; + } + } + + 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; + } + } + + // 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]; + 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; - // - anything else? + 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; - // Fourth, volume (disabled) and pan, clamp the attenuation at -96dB - s32 sampl = samp2 * m_linear_attenuation[std::min(0xff, (m_volume[channel] & 0x00) + (m_pan[channel] >> 8))]; - s32 sampr = samp2 * m_linear_attenuation[std::min(0xff, (m_volume[channel] & 0x00) + (m_pan[channel] & 0xff))]; + 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; - // Fifth, add to the accumulators - acc_left += sampl; - acc_right += sampr; + 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; - // Missing: reverb, chorus, effects in general + + 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; + } } + } - // Samples are 16 bits, there are up to 64 of them, and the accumulators are fixed-point signed 48.16 - // Global EQ is missing (it's done in the MEG) - - acc_left >>= (16+6); - if(acc_left < -0x8000) - acc_left = -0x8000; - else if(acc_left > 0x7fff) - acc_left = 0x7fff; - outputs[0][sample] = acc_left; - - acc_right >>= (16+6); - if(acc_right < -0x8000) - acc_right = -0x8000; - else if(acc_right > 0x7fff) - acc_right = 0x7fff; - outputs[1][sample] = acc_right; + debugger_instruction_hook(m_meg_pc); + m_icount --; + m_meg_pc ++; + if(m_meg_pc == 0x180) + m_meg_pc = 0; } } |