diff options
author | 2017-11-08 06:43:50 +0200 | |
---|---|---|
committer | 2017-11-08 06:43:50 +0200 | |
commit | d724f49d4253f209dfa9cc4bc79b4c00ac3be615 (patch) | |
tree | 95f09ffd932e554480c08c5e4267bee1be0a1eb0 /src/devices/sound/ymz770.cpp | |
parent | d49ccf883e86d29fce985dff51d80bfb4d6257fc (diff) |
preliminary Yamaha YMZ774 emulation (nw)
Diffstat (limited to 'src/devices/sound/ymz770.cpp')
-rw-r--r-- | src/devices/sound/ymz770.cpp | 236 |
1 files changed, 164 insertions, 72 deletions
diff --git a/src/devices/sound/ymz770.cpp b/src/devices/sound/ymz770.cpp index 506de02da15..b836471a324 100644 --- a/src/devices/sound/ymz770.cpp +++ b/src/devices/sound/ymz770.cpp @@ -1,10 +1,10 @@ // license:BSD-3-Clause -// copyright-holders:Olivier Galibert, R. Belmont +// copyright-holders:Olivier Galibert, R. Belmont, MetalliC /*************************************************************************** ymz770.c - Emulation by R. Belmont + Emulation by R. Belmont and MetalliC AMM decode by Olivier Galibert ----- @@ -24,16 +24,23 @@ TODO: #include "mpeg_audio.h" // device type definition -DEFINE_DEVICE_TYPE(YMZ770, ymz770_device, "ymz770", "Yamaha YMZ770 AMMS-A") +DEFINE_DEVICE_TYPE(YMZ770, ymz770_device, "ymz770", "Yamaha YMZ770C-F AMMS-A") +DEFINE_DEVICE_TYPE(YMZ774, ymz774_device, "ymz774", "Yamaha YMZ774-S AMMS2") //------------------------------------------------- // ymz770_device - constructor //------------------------------------------------- ymz770_device::ymz770_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, YMZ770, tag, owner, clock) + : ymz770_device(mconfig, YMZ770, tag, owner, clock, 16000) +{ +} + +ymz770_device::ymz770_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sclock) + : device_t(mconfig, type, tag, owner, clock) , device_sound_interface(mconfig, *this) , m_stream(nullptr) + , m_sclock(sclock) , m_cur_reg(0) , m_mute(0) , m_doen(0) @@ -52,14 +59,15 @@ ymz770_device::ymz770_device(const machine_config &mconfig, const char *tag, dev void ymz770_device::device_start() { // create the stream - m_stream = machine().sound().stream_alloc(*this, 0, 2, 16000); + m_stream = machine().sound().stream_alloc(*this, 0, 2, m_sclock); for (auto & elem : m_channels) { elem.is_playing = false; - elem.is_seq_playing = false; elem.decoder = new mpeg_audio(&m_rom[0], mpeg_audio::AMM, false, 0); } + for (auto & elem : m_sequences) + elem.is_seq_playing = false; // register for save states save_item(NAME(m_cur_reg)); @@ -69,24 +77,27 @@ void ymz770_device::device_start() save_item(NAME(m_bsl)); save_item(NAME(m_cpl)); - for (int ch = 0; ch < 8; ch++) + for (int ch = 0; ch < 16; ch++) // TODO array size { 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].loop), 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); } + for (int ch = 0; ch < 8; ch++) + { + save_item(NAME(m_sequences[ch].sequence), ch); + save_item(NAME(m_sequences[ch].seqcontrol), ch); + save_item(NAME(m_sequences[ch].seqdelay), ch); + save_item(NAME(m_sequences[ch].is_seq_playing), ch); + } } @@ -101,14 +112,17 @@ void ymz770_device::device_reset() elem.phrase = 0; elem.pan = 8; elem.volume = 0; - elem.control = 0; + elem.loop = 0; + elem.is_playing = false; + elem.output_remaining = 0; + elem.decoder->clear(); + } + for (auto & elem : m_sequences) + { elem.sequence = 0; elem.seqcontrol = 0; elem.seqdelay = 0; - elem.is_playing = false; elem.is_seq_playing = false; - elem.output_remaining = 0; - elem.decoder->clear(); } } @@ -127,44 +141,7 @@ void ymz770_device::sound_stream_update(sound_stream &stream, stream_sample_t ** for (int i = 0; i < samples; i++) { - // run sequencers (should probably be in separate timer callbacks) - for (auto & elem : m_channels) - { - if (elem.is_seq_playing) - { - if (elem.seqdelay > 0) - { - elem.seqdelay--; - } - else - { - int reg = *elem.seqdata++; - uint8_t data = *elem.seqdata++; - switch (reg) - { - case 0x0f: - if (elem.seqcontrol & 1) - { - // loop sequence - uint8_t sqn = elem.sequence; - uint32_t pptr = m_rom[(4*sqn)+1+0x400]<<16 | m_rom[(4*sqn)+2+0x400]<<8 | m_rom[(4*sqn)+3+0x400]; - elem.seqdata = &m_rom[pptr]; - } - else - { - elem.is_seq_playing = false; - } - break; - case 0x0e: - elem.seqdelay = 32 - 1; - break; - default: - internal_reg_write(reg, data); - break; - } - } - } - } + sequencer(); // process channels int32_t mixl = 0; @@ -176,8 +153,8 @@ void ymz770_device::sound_stream_update(sound_stream &stream, stream_sample_t ** { // force finish current block int32_t smpl = elem.output_data[elem.output_ptr++] * elem.volume; // volume is linear, 0 - 128 (100%) - mixr += (smpl * elem.pan) >> 11; // pan seems linear, 0 - 16, where 0 = 100% left, 16 = 100% right, 8 = 50% left 50% right - mixl += (smpl * (16 - elem.pan)) >> 11; + mixr += (smpl * elem.pan) >> 14; // pan seems linear, 0 - 128, where 0 = 100% left, 128 = 100% right, 64 = 50% left 50% right + mixl += (smpl * (128 - elem.pan)) >> 14; elem.output_remaining--; if (elem.output_remaining == 0 && !elem.is_playing) @@ -189,12 +166,14 @@ void ymz770_device::sound_stream_update(sound_stream &stream, stream_sample_t ** retry: if (elem.last_block) { - if (elem.control & 1) + if (elem.loop) { + if (elem.loop != 255) + --elem.loop; // loop sample - uint8_t phrase = elem.phrase; + int phrase = elem.phrase; elem.atbl = m_rom[(4*phrase)+0] >> 4 & 7; - elem.pptr = 8*(m_rom[(4*phrase)+1]<<16 | m_rom[(4*phrase)+2]<<8 | m_rom[(4*phrase)+3]); + elem.pptr = 8 * get_phrase_offs(phrase); } else { @@ -221,8 +200,8 @@ retry: elem.output_ptr = 1; int32_t smpl = elem.output_data[0] * elem.volume; - mixr += (smpl * elem.pan) >> 11; - mixl += (smpl * (16 - elem.pan)) >> 11; + mixr += (smpl * elem.pan) >> 14; + mixl += (smpl * (128 - elem.pan)) >> 14; } } } @@ -256,6 +235,46 @@ retry: } } +void ymz770_device::sequencer() +{ + for (auto & elem : m_sequences) + { + if (elem.is_seq_playing) + { + if (elem.seqdelay > 0) + { + elem.seqdelay--; + } + else + { + int reg = *elem.seqdata++; + uint8_t data = *elem.seqdata++; + switch (reg) + { + case 0x0f: + if (elem.seqcontrol & 1) + { + // loop sequence + uint8_t sqn = elem.sequence; + uint32_t pptr = get_seq_offs(sqn); + elem.seqdata = &m_rom[pptr]; + } + else + { + elem.is_seq_playing = false; + } + break; + case 0x0e: + elem.seqdelay = 32 - 1; + break; + default: + internal_reg_write(reg, data); + break; + } + } + } + } +} //------------------------------------------------- // write - write to the chip's registers @@ -318,7 +337,7 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data) break; case 2: - m_channels[ch].pan = data; + m_channels[ch].pan = (data & 0x1f) << 3; break; case 3: @@ -326,7 +345,7 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data) { uint8_t phrase = m_channels[ch].phrase; m_channels[ch].atbl = m_rom[(4*phrase)+0] >> 4 & 7; - m_channels[ch].pptr = 8*(m_rom[(4*phrase)+1]<<16 | m_rom[(4*phrase)+2]<<8 | m_rom[(4*phrase)+3]); + m_channels[ch].pptr = 8 * get_phrase_offs(phrase); m_channels[ch].last_block = false; m_channels[ch].is_playing = true; @@ -336,7 +355,7 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data) m_channels[ch].is_playing = false; } - m_channels[ch].control = data; + m_channels[ch].loop = (data & 1) ? 255 : 0; break; } } @@ -349,23 +368,23 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data) switch (reg & 0x0f) { case 0: - m_channels[ch].sequence = data; + m_sequences[ch].sequence = data; break; case 1: if (data & 6) { - uint8_t sqn = m_channels[ch].sequence; - uint32_t pptr = m_rom[(4*sqn)+1+0x400]<<16 | m_rom[(4*sqn)+2+0x400]<<8 | m_rom[(4*sqn)+3+0x400]; - m_channels[ch].seqdata = &m_rom[pptr]; - m_channels[ch].seqdelay = 0; - m_channels[ch].is_seq_playing = true; + uint8_t sqn = m_sequences[ch].sequence; + uint32_t pptr = get_seq_offs(sqn); + m_sequences[ch].seqdata = &m_rom[pptr]; + m_sequences[ch].seqdelay = 0; + m_sequences[ch].is_seq_playing = true; } else { - m_channels[ch].is_seq_playing = false; + m_sequences[ch].is_seq_playing = false; } - m_channels[ch].seqcontrol = data; + m_sequences[ch].seqcontrol = data; break; default: @@ -373,3 +392,76 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data) } } } + +//------------------------------------------------- +// ymz774_device +//------------------------------------------------- + +ymz774_device::ymz774_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : ymz770_device(mconfig, YMZ774, tag, owner, clock, 44100) +{ +} + +READ8_MEMBER(ymz774_device::read) +{ + // TODO status read ? + return 0; +} + +void ymz774_device::internal_reg_write(uint8_t reg, uint8_t data) +{ + // playback registers + if (reg < 0x60) { + int ch = (reg & 7) + m_bank * 8; + switch (reg & 0xf8) + { + case 0x00: // phrase# H and L + case 0x08: + ch = ((reg >> 1) & 7) + m_bank * 8; + if (reg & 1) + m_channels[ch].phrase = (m_channels[ch].phrase & 0xff00) | data; + else + m_channels[ch].phrase = (m_channels[ch].phrase & 0x00ff) | (data << 8); + break; + case 0x10: // Volume 1 + m_channels[ch].volume = data; + break; + case 0x28: // Pan L/R + m_channels[ch].pan = data; + break; + case 0x48: // Loop + m_channels[ch].loop = data; + break; + case 0x50: // Start / Stop + if (data) + { + int phrase = m_channels[ch].phrase; + m_channels[ch].atbl = m_rom[(4 * phrase) + 0] >> 4 & 7; + m_channels[ch].pptr = 8 * get_phrase_offs(phrase); + m_channels[ch].last_block = false; + + m_channels[ch].is_playing = true; + } + else + { + m_channels[ch].is_playing = false; + } + break; + } + } + // global registers + else if (reg >= 0xd0) + { + switch (reg) { + case 0xd0: + m_vlma = data; + break; + case 0xd2: + m_cpl = data; + break; + case 0xf0: + m_bank = data & 1; + break; + } + } +} |