summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/l7a1045_l6028_dsp_a.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/l7a1045_l6028_dsp_a.cpp')
-rw-r--r--src/devices/sound/l7a1045_l6028_dsp_a.cpp348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/devices/sound/l7a1045_l6028_dsp_a.cpp b/src/devices/sound/l7a1045_l6028_dsp_a.cpp
new file mode 100644
index 00000000000..baf277b55e2
--- /dev/null
+++ b/src/devices/sound/l7a1045_l6028_dsp_a.cpp
@@ -0,0 +1,348 @@
+// license:LGPL-2.1+
+// copyright-holders:David Haywood, Angelo Salese, ElSemi
+/***************************************************************************
+
+ L7A1045 L6028 DSP-A
+ (QFP120 package)
+
+ this is the audio chip used on the following
+ SNK Hyper NeoGeo 64 (arcade platform)
+ AKAI MPC3000 (synth)
+
+ both are driven by a V53, the MPC3000 isn't dumped.
+
+ appears to write a register number and channel/voice using
+ l7a1045_sound_select_w (offset 0)
+ format:
+
+ ---- rrrr ---c cccc
+ r = register, c = channel
+
+ the channel select appears to address 32 different voices (5-bits)
+ the register select appears to use 4-bits with 0x0 to 0xa being valid
+
+ the registers data is written / read using offsets 1,2,3 after
+ setting the register + channel, this gives 3 16-bit values for
+ each register.
+
+ register format:
+
+ offset 3 offset 2 offset 1
+ fedcba9876543210 | fedcba9876543210 | fedcba9876543210
+
+ 0 ---------------- ---------------- ----------------
+
+ 1 ---------------- ---------------- ----------------
+
+ 2 ---------------- ---------------- ----------------
+
+ 3 ---------------- ---------------- ----------------
+
+ 4 ---------------- ---------------- ----------------
+
+ 5 ---------------- ---------------- ----------------
+
+ 6 ---------------- ---------------- ----------------
+
+ 7 ---------------- ---------------- llllllllrrrrrrrr left/right volume
+
+ 8 ---------------- ---------------- ---------------- (read only?)
+
+ 9 ---------------- ---------------- ---------------- (read only?)
+
+ a ---------------- ---------------- ----------------
+
+ Registers are not yet understood.
+
+ probably sample start, end, loop positions, panning etc.
+ like CPS3, Qsound etc.
+
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03: // 00003fffffff (startup only?)
+ case 0x04: // doesn't use 6
+ case 0x05: // 00003fffffff (mostly, often)
+ case 0x06: // 00007ff0ffff mostly
+ case 0x07: // 0000000f0708 etc. (low values)
+ case 0x08: // doesn't write to 2/4/6 with this set??
+ case 0x09: // doesn't write to 2/4/6 with this set??
+ case 0x0a: // random looking values
+
+ Some of the other ports on the HNG64 sound CPU may also be tied
+ to this chip, this isn't yet clear.
+ Port $8 bit 8 is keyon, low byte is sound status related (masked with 0x7f)
+
+ Sample data format TBA
+
+ TODO:
+ - Sample format needs to be double checked;
+ - Octave Control/BPM/Pitch, right now XRally Network BGM wants 66150 Hz which is definitely too fast for Terry Bogard speech;
+ - Key Off;
+ - ADSR (registers 2 & 4?);
+
+***************************************************************************/
+
+#include "emu.h"
+#include "l7a1045_l6028_dsp_a.h"
+#include "debugger.h"
+
+
+// device type definition
+const device_type L7A1045 = &device_creator<l7a1045_sound_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// l7a1045_sound_device - constructor
+//-------------------------------------------------
+
+l7a1045_sound_device::l7a1045_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, L7A1045, "L7A1045 L6028 DSP-A", tag, owner, clock, "l7a1045_custom", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_key(0),
+ m_rom(NULL),
+ m_rom_size(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void l7a1045_sound_device::device_start()
+{
+ /* Allocate the stream */
+ m_stream = stream_alloc(0, 2, 66150); //clock() / 384);
+
+ m_rom = m_region->base();
+ m_rom_size = m_region->bytes();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void l7a1045_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ /* Clear the buffers */
+ memset(outputs[0], 0, samples*sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples*sizeof(*outputs[1]));
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (m_key & (1 << i))
+ {
+ l7a1045_voice *vptr = &m_voice[i];
+
+ UINT32 start = vptr->start;
+ UINT32 end = vptr->end;
+ UINT32 step = 0x400;
+
+ UINT32 pos = vptr->pos;
+ UINT32 frac = vptr->frac;
+
+ for (int j = 0; j < samples; j++)
+ {
+ INT32 sample;
+ UINT8 data;
+
+ pos += (frac >> 12);
+ frac &= 0xfff;
+
+ if ((start + pos) >= end)
+ {
+ if(vptr->mode == true) // loop
+ {
+ pos = vptr->pos = 0;
+ frac = vptr->frac = 0;
+ }
+ else // no loop, keyoff
+ {
+ m_key &= ~(1 << i);
+ break;
+ }
+ }
+
+
+ data = m_rom[(start + pos) & (m_rom_size-1)];
+ sample = ((INT8)(data & 0xfc)) << (3 - (data & 3));
+ frac += step;
+
+ outputs[0][j] += ((sample * vptr->l_volume) >> 9);
+ outputs[1][j] += ((sample * vptr->r_volume) >> 9);
+ }
+
+ vptr->pos = pos;
+ vptr->frac = frac;
+ }
+ }
+}
+
+// TODO: needs proper memory map
+WRITE16_MEMBER( l7a1045_sound_device::l7a1045_sound_w )
+{
+ m_stream->update(); // TODO
+
+ if(offset == 0)
+ sound_select_w(space, offset, data, mem_mask);
+ else if(offset == 8/2)
+ sound_status_w(space, offset, data, mem_mask);
+ else
+ sound_data_w(space,offset - 1,data,mem_mask);
+}
+
+
+READ16_MEMBER( l7a1045_sound_device::l7a1045_sound_r )
+{
+ m_stream->update();
+
+ if(offset == 0)
+ printf("sound_select_r?\n");
+ else
+ return sound_data_r(space,offset -1,mem_mask);
+
+ return 0xffff;
+}
+
+
+WRITE16_MEMBER(l7a1045_sound_device::sound_select_w)
+{
+ // I'm guessing these addresses are the sound chip / DSP?
+
+ // ---- ---- 000c cccc
+ // c = channel
+
+ if (ACCESSING_BITS_0_7)
+ {
+ m_audiochannel = data;
+ if (m_audiochannel & 0xe0) printf("%08x: l7a1045_sound_select_w unknown channel %01x\n", space.device().safe_pc(), m_audiochannel & 0xff);
+ m_audiochannel &= 0x1f;
+ }
+
+ if (ACCESSING_BITS_8_15)
+ {
+ m_audioregister = (data >> 8);
+ if (m_audioregister >0x0a) printf("%08x: l7a1045_sound_select_w unknown register %01x\n", space.device().safe_pc(), m_audioregister & 0xff);
+ m_audioregister &= 0x0f;
+ }
+
+}
+
+WRITE16_MEMBER(l7a1045_sound_device::sound_data_w)
+{
+ l7a1045_voice *vptr = &m_voice[m_audiochannel];
+
+ //if(m_audioregister != 0 && m_audioregister != 1 && m_audioregister != 7)
+ // printf("%04x %04x (%04x %04x)\n",offset,data,m_audioregister,m_audiochannel);
+
+ m_audiodat[m_audioregister][m_audiochannel].dat[offset] = data;
+
+ switch (m_audioregister)
+ {
+ case 0x00:
+
+ vptr->start = (m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x000f) << (16 + 4);
+ vptr->start |= (m_audiodat[m_audioregister][m_audiochannel].dat[1] & 0xffff) << (4);
+ vptr->start |= (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xf000) >> (12);
+
+ vptr->start &= m_rom_size - 1;
+
+ break;
+ case 0x01:
+ // relative to start
+ //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0]);
+ //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[1]);
+ //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[2]);
+
+ if(m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x100)
+ {
+ vptr->end = (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xffff) << 2;
+ vptr->end += vptr->start;
+ vptr->mode = false;
+ // hopefully it'll never happen? Maybe assert here?
+ vptr->end &= m_rom_size - 1;
+
+ }
+ else // absolute
+ {
+ vptr->end = (m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x000f) << (16 + 4);
+ vptr->end |= (m_audiodat[m_audioregister][m_audiochannel].dat[1] & 0xffff) << (4);
+ vptr->end |= (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xf000) >> (12);
+ vptr->mode = true;
+
+ vptr->end &= m_rom_size - 1;
+ }
+
+ break;
+
+ case 0x07:
+
+ vptr->r_volume = (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xff);
+ /* TODO: volume tables, linear? */
+ vptr->r_volume = (vptr->r_volume) | (vptr->r_volume << 8);
+ vptr->l_volume = (m_audiodat[m_audioregister][m_audiochannel].dat[0] >> 8) & 0xff;
+ vptr->l_volume = (vptr->l_volume) | (vptr->l_volume << 8);
+ //printf("%04x %02x %02x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0],vptr->l_volume,vptr->r_volume);
+
+ break;
+ }
+}
+
+
+READ16_MEMBER(l7a1045_sound_device::sound_data_r)
+{
+ //printf("%04x (%04x %04x)\n",offset,m_audioregister,m_audiochannel);
+ //debugger_break(machine());
+ l7a1045_voice *vptr = &m_voice[m_audiochannel];
+
+ switch(m_audioregister)
+ {
+ case 0x00:
+ {
+ UINT32 current_addr;
+ UINT16 res;
+
+ current_addr = vptr->start + vptr->pos;
+ if(offset == 0)
+ res = (current_addr & 0xf) << 12; // TODO: frac
+ else if(offset == 1)
+ res = (current_addr & 0xffff0) >> 4;
+ else
+ res = (current_addr & 0xf00000) >> 20;
+
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+WRITE16_MEMBER(l7a1045_sound_device::sound_status_w)
+{
+ if(data & 0x100) // keyin
+ {
+ l7a1045_voice *vptr = &m_voice[m_audiochannel];
+
+ #if 0
+ if(vptr->start != 0)
+ {
+ printf("%08x START\n",vptr->start);
+ printf("%08x END\n",vptr->end);
+
+ for(int i=0;i<0x10;i++)
+ printf("%02x (%02x) = %04x%04x%04x\n",m_audiochannel,i,m_audiodat[i][m_audiochannel].dat[2],m_audiodat[i][m_audiochannel].dat[1],m_audiodat[i][m_audiochannel].dat[0]);
+ }
+ #endif
+
+ vptr->frac = 0;
+ vptr->pos = 0;
+ m_key |= 1 << m_audiochannel;
+ }
+}