diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/devices/sound/ymz770.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/devices/sound/ymz770.cpp')
-rw-r--r-- | src/devices/sound/ymz770.cpp | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/src/devices/sound/ymz770.cpp b/src/devices/sound/ymz770.cpp new file mode 100644 index 00000000000..f367f4d10eb --- /dev/null +++ b/src/devices/sound/ymz770.cpp @@ -0,0 +1,345 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert, R. Belmont +/*************************************************************************** + + ymz770.c + + Emulation by R. Belmont + AMM decode by Olivier Galibert + +----- +TODO: +- A lot of unimplemented features, even simple ones like panning, + these should be added once we find out any software that uses it. +- Is channel volume linear(current implementation) or logarithmic? +- Sequencer is very preliminary +- What does channel ATBL mean? +- Is YMZ774(and other variants) the same family as this chip? + What are the differences? + +***************************************************************************/ + +#include "emu.h" +#include "ymz770.h" +#include "mpeg_audio.h" + +// device type definition +const device_type YMZ770 = &device_creator<ymz770_device>; + +//------------------------------------------------- +// ymz770_device - constructor +//------------------------------------------------- + +ymz770_device::ymz770_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, YMZ770, "Yamaha YMZ770", tag, owner, clock, "ymz770", __FILE__), + device_sound_interface(mconfig, *this), + m_cur_reg(0), + m_mute(0), + m_doen(0), + m_vlma(0), + m_bsl(0), + m_cpl(0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ymz770_device::device_start() +{ + // create the stream + m_stream = machine().sound().stream_alloc(*this, 0, 2, 16000); + m_rom_base = region()->base(); + m_rom_limit = region()->bytes() * 8; + + for (int i = 0; i < 8; i++) + { + m_channels[i].is_playing = false; + m_channels[i].is_seq_playing = false; + m_channels[i].decoder = new mpeg_audio(m_rom_base, mpeg_audio::AMM, false, 0); + } + + // register for save states + save_item(NAME(m_cur_reg)); + save_item(NAME(m_mute)); + save_item(NAME(m_doen)); + save_item(NAME(m_vlma)); + save_item(NAME(m_bsl)); + save_item(NAME(m_cpl)); + + for (int ch = 0; ch < 8; ch++) + { + save_item(NAME(m_channels[ch].phrase), ch); + save_item(NAME(m_channels[ch].pan), ch); + save_item(NAME(m_channels[ch].volume), ch); + save_item(NAME(m_channels[ch].control), ch); + save_item(NAME(m_channels[ch].is_playing), ch); + save_item(NAME(m_channels[ch].last_block), ch); + save_item(NAME(m_channels[ch].output_remaining), ch); + save_item(NAME(m_channels[ch].output_ptr), ch); + save_item(NAME(m_channels[ch].atbl), ch); + save_item(NAME(m_channels[ch].pptr), ch); + save_item(NAME(m_channels[ch].sequence), ch); + save_item(NAME(m_channels[ch].seqcontrol), ch); + save_item(NAME(m_channels[ch].seqdelay), ch); + save_item(NAME(m_channels[ch].is_seq_playing), ch); + save_item(NAME(m_channels[ch].output_data), ch); + } +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void ymz770_device::device_reset() +{ + for (int ch = 0; ch < 8; ch++) + { + m_channels[ch].phrase = 0; + m_channels[ch].pan = 8; + m_channels[ch].volume = 0; + m_channels[ch].control = 0; + m_channels[ch].sequence = 0; + m_channels[ch].seqcontrol = 0; + m_channels[ch].seqdelay = 0; + m_channels[ch].is_playing = false; + m_channels[ch].is_seq_playing = false; + m_channels[ch].output_remaining = 0; + m_channels[ch].decoder->clear(); + } +} + + +//------------------------------------------------- +// sound_stream_update - handle update requests for +// our sound stream +//------------------------------------------------- + +void ymz770_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *outL, *outR; + + outL = outputs[0]; + outR = outputs[1]; + + for (int i = 0; i < samples; i++) + { + // run sequencers (should probably be in separate timer callbacks) + for (int ch = 0; ch < 8; ch++) + { + if (m_channels[ch].is_seq_playing) + { + if (m_channels[ch].seqdelay > 0) + { + m_channels[ch].seqdelay--; + } + else + { + int reg = *m_channels[ch].seqdata++; + UINT8 data = *m_channels[ch].seqdata++; + switch (reg) + { + case 0x0f: + if (m_channels[ch].seqcontrol & 1) + { + // loop sequence + UINT8 sqn = m_channels[ch].sequence; + UINT32 pptr = m_rom_base[(4*sqn)+1+0x400]<<16 | m_rom_base[(4*sqn)+2+0x400]<<8 | m_rom_base[(4*sqn)+3+0x400]; + m_channels[ch].seqdata = &m_rom_base[pptr]; + } + else + { + m_channels[ch].is_seq_playing = false; + } + break; + case 0x0e: + m_channels[ch].seqdelay = 32 - 1; + break; + default: + internal_reg_write(reg, data); + break; + } + } + } + } + + // process channels + INT32 mix = 0; + + for (int ch = 0; ch < 8; ch++) + { + if (m_channels[ch].output_remaining > 0) + { + // force finish current block + mix += (m_channels[ch].output_data[m_channels[ch].output_ptr++]*m_channels[ch].volume); + m_channels[ch].output_remaining--; + + if (m_channels[ch].output_remaining == 0 && !m_channels[ch].is_playing) + m_channels[ch].decoder->clear(); + } + + else if (m_channels[ch].is_playing) + { +retry: + if (m_channels[ch].last_block) + { + if (m_channels[ch].control & 1) + { + // loop sample + UINT8 phrase = m_channels[ch].phrase; + m_channels[ch].atbl = m_rom_base[(4*phrase)+0] >> 4 & 7; + m_channels[ch].pptr = 8*(m_rom_base[(4*phrase)+1]<<16 | m_rom_base[(4*phrase)+2]<<8 | m_rom_base[(4*phrase)+3]); + } + else + { + m_channels[ch].is_playing = false; + m_channels[ch].output_remaining = 0; + m_channels[ch].decoder->clear(); + } + } + + if (m_channels[ch].is_playing) + { + // next block + int sample_rate, channel_count; + if (!m_channels[ch].decoder->decode_buffer(m_channels[ch].pptr, m_rom_limit, m_channels[ch].output_data, m_channels[ch].output_remaining, sample_rate, channel_count) || m_channels[ch].output_remaining == 0) + { + m_channels[ch].is_playing = !m_channels[ch].last_block; // detect infinite retry loop + m_channels[ch].last_block = true; + m_channels[ch].output_remaining = 0; + goto retry; + } + + m_channels[ch].last_block = m_channels[ch].output_remaining < 1152; + m_channels[ch].output_remaining--; + m_channels[ch].output_ptr = 1; + + mix += (m_channels[ch].output_data[0]*m_channels[ch].volume); + } + } + } + + outL[i] = outR[i] = mix>>8; + } +} + + +//------------------------------------------------- +// write - write to the chip's registers +//------------------------------------------------- + +WRITE8_MEMBER( ymz770_device::write ) +{ + if (offset & 1) + { + m_stream->update(); + internal_reg_write(m_cur_reg, data); + } + else + { + m_cur_reg = data; + } +} + + +void ymz770_device::internal_reg_write(UINT8 reg, UINT8 data) +{ + // global registers + if (reg < 0x40) + { + switch (reg) + { + case 0x00: + m_mute = data & 1; + m_doen = data >> 1 & 1; + break; + + case 0x01: + m_vlma = data; + break; + + case 0x02: + m_bsl = data & 7; + m_cpl = data >> 4 & 7; + break; + + // unused + default: + break; + } + } + + // playback registers + else if (reg < 0x60) + { + int ch = reg >> 2 & 0x07; + + switch (reg & 0x03) + { + case 0: + m_channels[ch].phrase = data; + break; + + case 1: + m_channels[ch].volume = data; + break; + + case 2: + m_channels[ch].pan = data; + break; + + case 3: + if (data & 6) + { + UINT8 phrase = m_channels[ch].phrase; + m_channels[ch].atbl = m_rom_base[(4*phrase)+0] >> 4 & 7; + m_channels[ch].pptr = 8*(m_rom_base[(4*phrase)+1]<<16 | m_rom_base[(4*phrase)+2]<<8 | m_rom_base[(4*phrase)+3]); + m_channels[ch].last_block = false; + + m_channels[ch].is_playing = true; + } + else + { + m_channels[ch].is_playing = false; + } + + m_channels[ch].control = data; + break; + } + } + + // sequencer registers + else + { + int ch = reg >> 4 & 0x07; + + switch (reg & 0x0f) + { + case 0: + m_channels[ch].sequence = data; + break; + case 1: + if (data & 6) + { + UINT8 sqn = m_channels[ch].sequence; + UINT32 pptr = m_rom_base[(4*sqn)+1+0x400]<<16 | m_rom_base[(4*sqn)+2+0x400]<<8 | m_rom_base[(4*sqn)+3+0x400]; + m_channels[ch].seqdata = &m_rom_base[pptr]; + m_channels[ch].seqdelay = 0; + m_channels[ch].is_seq_playing = true; + } + else + { + m_channels[ch].is_seq_playing = false; + } + + m_channels[ch].seqcontrol = data; + break; + + default: + break; + } + } +} |