summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author MetalliC <0vetal0@gmail.com>2017-11-08 06:43:50 +0200
committer MetalliC <0vetal0@gmail.com>2017-11-08 06:43:50 +0200
commitd724f49d4253f209dfa9cc4bc79b4c00ac3be615 (patch)
tree95f09ffd932e554480c08c5e4267bee1be0a1eb0
parentd49ccf883e86d29fce985dff51d80bfb4d6257fc (diff)
preliminary Yamaha YMZ774 emulation (nw)
-rw-r--r--src/devices/sound/ymz770.cpp236
-rw-r--r--src/devices/sound/ymz770.h46
2 files changed, 203 insertions, 79 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;
+ }
+ }
+}
diff --git a/src/devices/sound/ymz770.h b/src/devices/sound/ymz770.h
index b7f047dc4b4..5f38491c637 100644
--- a/src/devices/sound/ymz770.h
+++ b/src/devices/sound/ymz770.h
@@ -25,6 +25,12 @@
#define MCFG_YMZ770_REPLACE(_tag, _clock) \
MCFG_DEVICE_REPLACE(_tag, YMZ770, _clock)
+#define MCFG_YMZ774_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, YMZ774, _clock)
+
+#define MCFG_YMZ774_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, YMZ774, _clock)
+
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
@@ -49,9 +55,15 @@ protected:
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
- void internal_reg_write(uint8_t reg, uint8_t data);
+ virtual void internal_reg_write(uint8_t reg, uint8_t data);
+ virtual uint32_t get_phrase_offs(int phrase) { return m_rom[(4 * phrase) + 1] << 16 | m_rom[(4 * phrase) + 2] << 8 | m_rom[(4 * phrase) + 3]; };
+ virtual uint32_t get_seq_offs(int sqn) { return m_rom[(4 * sqn) + 1 + 0x400] << 16 | m_rom[(4 * sqn) + 2 + 0x400] << 8 | m_rom[(4 * sqn) + 3 + 0x400]; };
+ virtual void sequencer();
+
+ ymz770_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sclock);
sound_stream *m_stream;
+ uint32_t m_sclock;
// data
uint8_t m_cur_reg;
@@ -62,13 +74,12 @@ protected:
uint8_t m_cpl; // clip limiter
required_region_ptr<uint8_t> m_rom;
-private:
struct ymz_channel
{
- uint8_t phrase;
+ uint16_t phrase;
uint8_t pan;
uint8_t volume;
- uint8_t control;
+ uint8_t loop;
bool is_playing, last_block;
@@ -79,19 +90,40 @@ private:
int output_ptr;
int atbl;
int pptr;
-
- uint8_t sequence;
+ };
+ struct ymz_sequence
+ {
+ uint16_t sequence;
uint8_t seqcontrol;
uint8_t seqdelay;
uint8_t *seqdata;
bool is_seq_playing;
};
- ymz_channel m_channels[8];
+ ymz_channel m_channels[16];
+ ymz_sequence m_sequences[8];
};
+// ======================> ymz774_device
+
+class ymz774_device : public ymz770_device
+{
+public:
+ // construction/destruction
+ ymz774_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ DECLARE_READ8_MEMBER(read);
+protected:
+ virtual void internal_reg_write(uint8_t reg, uint8_t data) override;
+ virtual uint32_t get_phrase_offs(int phrase) override { return (m_rom[(4 * phrase) + 1] << 16 | m_rom[(4 * phrase) + 2] << 8 | m_rom[(4 * phrase) + 3]) * 2; };
+ virtual uint32_t get_seq_offs(int sqn) override { return (m_rom[(4 * sqn) + 1 + 0x2000] << 16 | m_rom[(4 * sqn) + 2 + 0x2000] << 8 | m_rom[(4 * sqn) + 3 + 0x2000]) * 2; };
+ virtual void sequencer() override {};
+private:
+ int m_bank;
+};
// device type definition
DECLARE_DEVICE_TYPE(YMZ770, ymz770_device)
+DECLARE_DEVICE_TYPE(YMZ774, ymz774_device)
#endif // MAME_SOUND_YMZ770_H