summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/audio/dsbz80.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/audio/dsbz80.cpp')
-rw-r--r--src/mame/audio/dsbz80.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/mame/audio/dsbz80.cpp b/src/mame/audio/dsbz80.cpp
new file mode 100644
index 00000000000..dfc97fd6028
--- /dev/null
+++ b/src/mame/audio/dsbz80.cpp
@@ -0,0 +1,317 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont, Olivier Galibert
+/***************************************************************************
+
+ Sega Z80 Digital Sound Board
+
+ used for Model 1/2/3
+
+***************************************************************************/
+
+
+#include "emu.h"
+#include "audio/dsbz80.h"
+
+#define Z80_TAG "mpegcpu"
+
+static ADDRESS_MAP_START( dsbz80_map, AS_PROGRAM, 8, dsbz80_device )
+ AM_RANGE(0x0000, 0x7fff) AM_ROM AM_REGION(":mpegcpu", 0)
+ AM_RANGE(0x8000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( dsbz80io_map, AS_IO, 8, dsbz80_device )
+ AM_RANGE(0xe0, 0xe0) AM_MIRROR(0xff00) AM_WRITE(mpeg_trigger_w)
+ AM_RANGE(0xe2, 0xe4) AM_MIRROR(0xff00) AM_READWRITE(mpeg_pos_r, mpeg_start_w)
+ AM_RANGE(0xe5, 0xe7) AM_MIRROR(0xff00) AM_WRITE(mpeg_end_w)
+ AM_RANGE(0xe8, 0xe8) AM_MIRROR(0xff00) AM_WRITE(mpeg_volume_w)
+ AM_RANGE(0xe9, 0xe9) AM_MIRROR(0xff00) AM_WRITE(mpeg_stereo_w)
+ AM_RANGE(0xf0, 0xf0) AM_MIRROR(0xff00) AM_READ(latch_r)
+ AM_RANGE(0xf1, 0xf1) AM_MIRROR(0xff00) AM_READ(status_r)
+ADDRESS_MAP_END
+
+
+MACHINE_CONFIG_FRAGMENT( dsbz80 )
+ MCFG_CPU_ADD(Z80_TAG, Z80, 4000000) /* unknown clock, but probably pretty slow considering the z80 does like nothing */
+ MCFG_CPU_PROGRAM_MAP(dsbz80_map)
+ MCFG_CPU_IO_MAP(dsbz80io_map)
+MACHINE_CONFIG_END
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+const device_type DSBZ80 = &device_creator<dsbz80_device>;
+
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor dsbz80_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( dsbz80 );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// dsbz80_device - constructor
+//-------------------------------------------------
+
+dsbz80_device::dsbz80_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, DSBZ80, "Sega Z80-based Digital Sound Board", tag, owner, clock, "dsbz80", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_ourcpu(*this, Z80_TAG)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dsbz80_device::device_start()
+{
+ UINT8 *rom_base = machine().root_device().memregion("mpeg")->base();
+ decoder = new mpeg_audio(rom_base, mpeg_audio::L2, false, 0);
+ machine().sound().stream_alloc(*this, 0, 2, 32000);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void dsbz80_device::device_reset()
+{
+ m_dsb_latch = 0;
+ status = 1;
+ start = end = 0;
+ audio_pos = audio_avail = 0;
+ memset(audio_buf, 0, sizeof(audio_buf));
+ mp_state = 0;
+}
+
+WRITE8_MEMBER(dsbz80_device::latch_w)
+{
+ m_ourcpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+ m_dsb_latch = data;
+ status |= 2;
+// printf("%02x to DSB latch\n", data);
+}
+
+READ8_MEMBER(dsbz80_device::latch_r)
+{
+ m_ourcpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+// printf("DSB Z80 read %02x\n", m_dsb_latch);
+ status &= ~2;
+ return m_dsb_latch;
+}
+
+WRITE8_MEMBER(dsbz80_device::mpeg_trigger_w)
+{
+ mp_state = data;
+
+ if (data == 0) // stop
+ {
+ mp_state = 0;
+ audio_pos = audio_avail = 0;
+ }
+ else if (data == 1) // play without loop
+ {
+ mp_pos = mp_start*8;
+ }
+ else if (data == 2) // play with loop
+ {
+ mp_pos = mp_start*8;
+ }
+}
+
+READ8_MEMBER(dsbz80_device::mpeg_pos_r)
+{
+ int mp_prg = mp_pos >> 3;
+
+ switch (offset)
+ {
+ case 0:
+ return (mp_prg>>16)&0xff;
+ case 1:
+ return (mp_prg>>8)&0xff;
+ case 2:
+ return mp_prg&0xff;
+ }
+
+ return 0;
+}
+
+/* NOTE: writes to the start and end while playback is already in progress
+ get latched. When the current stream ends, the MPEG hardware starts playing
+ immediately from the latched start and end position. In this way, the Z80
+ enforces looping where appropriate and multi-part songs in other cases
+ (song #16 is a good example)
+*/
+
+WRITE8_MEMBER(dsbz80_device::mpeg_start_w)
+{
+ switch (offset)
+ {
+ case 0:
+ start &= 0x00ffff;
+ start |= (int)data<<16;
+ break;
+ case 1:
+ start &= 0xff00ff;
+ start |= (int)data<<8;
+ break;
+ case 2:
+ start &= 0xffff00;
+ start |= data;
+
+ if (mp_state == 0)
+ {
+ mp_start = start;
+ }
+ else
+ {
+ lp_start = start;
+ // SWA: if loop end is zero, it means "keep previous end marker"
+ if (lp_end == 0)
+ {
+// MPEG_Set_Loop(ROM + lp_start, mp_end-lp_start);
+ }
+ else
+ {
+// MPEG_Set_Loop(ROM + lp_start, lp_end-lp_start);
+ }
+ }
+ break;
+ }
+}
+
+WRITE8_MEMBER(dsbz80_device::mpeg_end_w)
+{
+ switch (offset)
+ {
+ case 0:
+ end &= 0x00ffff;
+ end |= (int)data<<16;
+ break;
+ case 1:
+ end &= 0xff00ff;
+ end |= (int)data<<8;
+ break;
+ case 2:
+ end &= 0xffff00;
+ end |= data;
+
+ if (mp_state == 0)
+ {
+ mp_end = end;
+ }
+ else
+ {
+ lp_end = end;
+// MPEG_Set_Loop(ROM + lp_start, lp_end-lp_start);
+ }
+ break;
+ }
+}
+
+WRITE8_MEMBER(dsbz80_device::mpeg_volume_w)
+{
+ mp_vol = 0x7f - data;
+}
+
+WRITE8_MEMBER(dsbz80_device::mpeg_stereo_w)
+{
+ mp_pan = data & 3; // 0 = stereo, 1 = left on both channels, 2 = right on both channels
+}
+
+READ8_MEMBER(dsbz80_device::status_r)
+{
+ // bit 0 = ??? (must be 1 for most games)
+ // bit 1 = command is pending (used by SWA instead of IRQ)
+ // other bits = ???
+ // SWA requires that status & 0x38 = 0 or else it loops endlessly...
+ return status;
+}
+
+void dsbz80_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *out_l = outputs[0];
+ stream_sample_t *out_r = outputs[1];
+
+ for(;;)
+ {
+ while(samples && audio_pos < audio_avail)
+ {
+ switch (mp_pan)
+ {
+ case 0: // stereo
+ *out_l++ = audio_buf[audio_pos*2];
+ *out_r++ = audio_buf[audio_pos*2+1];
+ break;
+
+ case 1: // left only
+ *out_l++ = audio_buf[audio_pos*2];
+ *out_r++ = audio_buf[audio_pos*2];
+ break;
+
+ case 2: // right only
+ *out_l++ = audio_buf[audio_pos*2+1];
+ *out_r++ = audio_buf[audio_pos*2+1];
+ break;
+ }
+ audio_pos++;
+ samples--;
+ }
+
+ if(!samples)
+ {
+ break;
+ }
+
+ if(mp_state == 0)
+ {
+ for(int i=0; i != samples; i++)
+ {
+ *out_l++ = 0;
+ *out_r++ = 0;
+ }
+ break;
+
+ }
+ else
+ {
+ int sample_rate, channel_count;
+ bool ok = decoder->decode_buffer(mp_pos, mp_end*8, audio_buf, audio_avail, sample_rate, channel_count);
+
+ if (ok)
+ {
+ audio_pos = 0;
+ }
+ else
+ {
+ if(mp_state == 2)
+ {
+ if (mp_pos == lp_start*8)
+ {
+ // We're looping on un-decodable crap, abort abort abort
+ mp_state = 0;
+ }
+ mp_pos = lp_start*8;
+
+ if (lp_end)
+ {
+ mp_end = lp_end;
+ }
+ }
+ else
+ {
+ mp_state = 0;
+ }
+ }
+ }
+ }
+}