From 05cacea947d7db129c25f33c85aca7ec74be6799 Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Mon, 31 May 2021 11:20:28 -0700 Subject: [PATCH] ympsr60: Fleshed out the PSR60/PSR70 drivers * Split YM2154 into a separate device * Created fake YM2154 ROMs as placeholders * Created new BBD sound device in src/devices/sound/bbd.cpp * Created new mixer device in src/devices/sound/mixer.cpp * Connected YM2154 and BBD devices * Approximated BBD driver behavior * Exposed analog sliders as adjusters (accessible via ` menu) * Added sliders to layout and animated them --- scripts/src/sound.lua | 12 ++ scripts/target/mame/mess.lua | 1 + scripts/target/mame/virtual.lua | 1 + src/devices/sound/bbd.cpp | 118 ++++++++++++ src/devices/sound/bbd.h | 81 +++++++++ src/devices/sound/mixer.cpp | 30 +++ src/devices/sound/mixer.h | 27 +++ src/devices/sound/ym2154.cpp | 311 ++++++++++++++++++++++++++++++++ src/devices/sound/ym2154.h | 99 ++++++++++ src/mame/drivers/ympsr60.cpp | 266 ++++++++++++--------------- src/mame/layout/psr60.lay | 91 ++++++++++ src/mame/layout/psr70.lay | 63 ++++++- 12 files changed, 947 insertions(+), 153 deletions(-) create mode 100644 src/devices/sound/bbd.cpp create mode 100644 src/devices/sound/bbd.h create mode 100644 src/devices/sound/mixer.cpp create mode 100644 src/devices/sound/mixer.h create mode 100644 src/devices/sound/ym2154.cpp create mode 100644 src/devices/sound/ym2154.h diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 6de550d5dde..f9710d96547 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -10,12 +10,16 @@ ---------------------------------------------------------------------------- files { + MAME_DIR .. "src/devices/sound/bbd.cpp", + MAME_DIR .. "src/devices/sound/bbd.h", MAME_DIR .. "src/devices/sound/flt_biquad.cpp", MAME_DIR .. "src/devices/sound/flt_biquad.h", MAME_DIR .. "src/devices/sound/flt_vol.cpp", MAME_DIR .. "src/devices/sound/flt_vol.h", MAME_DIR .. "src/devices/sound/flt_rc.cpp", MAME_DIR .. "src/devices/sound/flt_rc.h", + MAME_DIR .. "src/devices/sound/mixer.cpp", + MAME_DIR .. "src/devices/sound/mixer.h", MAME_DIR .. "src/devices/sound/samples.cpp", MAME_DIR .. "src/devices/sound/samples.h", } @@ -1169,6 +1173,7 @@ end --------------------------------------------------- -- Yamaha FM synthesizers +--@src/devices/sound/ym2154.h,SOUNDS["YM2154"] = true --@src/devices/sound/ymopm.h,SOUNDS["YM2151"] = true --@src/devices/sound/ymopz.h,SOUNDS["YM2414"] = true --@src/devices/sound/ymopq.h,SOUNDS["YM3806"] = true @@ -1185,6 +1190,13 @@ end --@src/devices/sound/ymopl.h,SOUNDS["Y8950"] = true --------------------------------------------------- +if (SOUNDS["YM2154"]~=null) then + files { + MAME_DIR .. "src/devices/sound/ym2154.cpp", + MAME_DIR .. "src/devices/sound/ym2154.h", + } +end + if (SOUNDS["YM2151"]~=null or SOUNDS["YM2164"]~=null) then files { MAME_DIR .. "src/devices/sound/ymopm.cpp", diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 52dc912def7..84cd754592c 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -183,6 +183,7 @@ SOUNDS["SPEAKER"] = true SOUNDS["BEEP"] = true SOUNDS["DISCRETE"] = true SOUNDS["AY8910"] = true +SOUNDS["YM2154"] = true SOUNDS["YM2151"] = true SOUNDS["YM2414"] = true SOUNDS["YM3806"] = true diff --git a/scripts/target/mame/virtual.lua b/scripts/target/mame/virtual.lua index e3d4da3a59d..6bd3c77cc80 100644 --- a/scripts/target/mame/virtual.lua +++ b/scripts/target/mame/virtual.lua @@ -56,6 +56,7 @@ SOUNDS["VGMVIZ"] = true SOUNDS["WAVE"] = true SOUNDS["X1_010"] = true SOUNDS["Y8950"] = true +SOUNDS["YM2154"] = true SOUNDS["YM2151"] = true SOUNDS["YM2414"] = true SOUNDS["YM3806"] = true diff --git a/src/devices/sound/bbd.cpp b/src/devices/sound/bbd.cpp new file mode 100644 index 00000000000..d19a56d9084 --- /dev/null +++ b/src/devices/sound/bbd.cpp @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#include "emu.h" +#include "bbd.h" + + +//************************************************************************** +// BBD DEVICE BASE +//************************************************************************** + +//------------------------------------------------- +// bbd_device_base - constructor +//------------------------------------------------- + +template +bbd_device_base::bbd_device_base(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_stream(nullptr), + m_curpos(0) +{ + std::fill_n(&m_buffer[0], Entries, 0); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +template +void bbd_device_base::device_start() +{ + m_stream = stream_alloc(1, Outputs, sample_rate()); + save_item(NAME(m_buffer)); +} + + +//------------------------------------------------- +// device_clock_changed - handle a clock change +//------------------------------------------------- + +template +void bbd_device_base::device_clock_changed() +{ + m_stream->set_sample_rate(sample_rate()); +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +template +void bbd_device_base::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // BBDs that I've seen so far typically have 2 outputs, with the first outputting + // sample n-1 and the second outputting sampe n; if chips with more outputs + // or other taps turn up, this logic will need to be made more flexible + for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) + { + for (int outnum = 0; outnum < Outputs; outnum++) + outputs[outnum].put(sampindex, m_buffer[(m_curpos + (Outputs - 1) - outnum) % Entries]); + m_buffer[m_curpos] = inputs[0].get(sampindex); + m_curpos = (m_curpos + 1) % Entries; + } +} + + +//************************************************************************** +// MN3004 +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(MN3004, mn3004_device, "mn3004", "MN3004 BBD") + +mn3004_device::mn3004_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + bbd_device_base(mconfig, tag, owner, clock, MN3004) +{ +} + + +//************************************************************************** +// MN3005 +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(MN3005, mn3005_device, "mn3005", "MN3005 BBD") + +mn3005_device::mn3005_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + bbd_device_base(mconfig, tag, owner, clock, MN3005) +{ +} + + +//************************************************************************** +// MN3006 +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(MN3006, mn3006_device, "mn3006", "MN3006 BBD") + +mn3006_device::mn3006_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + bbd_device_base(mconfig, tag, owner, clock, MN3006) +{ +} + + +//************************************************************************** +// MN3204P +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(MN3204P, mn3204p_device, "mn3204p", "MN3204P BBD") + +mn3204p_device::mn3204p_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + bbd_device_base(mconfig, tag, owner, clock, MN3204P) +{ +} diff --git a/src/devices/sound/bbd.h b/src/devices/sound/bbd.h new file mode 100644 index 00000000000..799fae790c1 --- /dev/null +++ b/src/devices/sound/bbd.h @@ -0,0 +1,81 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_SOUND_BBD_H +#define MAME_SOUND_BBD_H + +#pragma once + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> bbd_device_base + +template +class bbd_device_base : public device_t, public device_sound_interface +{ +protected: + // internal constructor + bbd_device_base(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type); + + // override to convert clock to sample rate + virtual u32 sample_rate() const { return clock(); } + + // device-level overrides + virtual void device_start() override; + virtual void device_clock_changed() override; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +protected: + sound_stream * m_stream; + u32 m_curpos; + stream_buffer::sample_t m_buffer[Entries]; +}; + + +// ======================> mn3004_device + +class mn3004_device : public bbd_device_base<512, 2> +{ +public: + mn3004_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); +}; + +DECLARE_DEVICE_TYPE(MN3004, mn3004_device) + + +// ======================> mn3005_device + +class mn3005_device : public bbd_device_base<4096, 2> +{ +public: + mn3005_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); +}; + +DECLARE_DEVICE_TYPE(MN3005, mn3005_device) + + +// ======================> mn3006_device + +class mn3006_device : public bbd_device_base<128, 2> +{ +public: + mn3006_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); +}; + +DECLARE_DEVICE_TYPE(MN3006, mn3006_device) + + +// ======================> mn3204p_device + +class mn3204p_device : public bbd_device_base<512, 2> +{ +public: + mn3204p_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); +}; + +DECLARE_DEVICE_TYPE(MN3204P, mn3204p_device) + +#endif // MAME_SOUND_BBD_H diff --git a/src/devices/sound/mixer.cpp b/src/devices/sound/mixer.cpp new file mode 100644 index 00000000000..3b8435182c4 --- /dev/null +++ b/src/devices/sound/mixer.cpp @@ -0,0 +1,30 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#include "emu.h" +#include "mixer.h" + + +//************************************************************************** +// MIXER DEVICE +//************************************************************************** + +DEFINE_DEVICE_TYPE(MIXER, mixer_device, "mixer", "Generic Audio Mixer") + +//------------------------------------------------- +// mixer_device - constructor +//------------------------------------------------- + +mixer_device::mixer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, MIXER, tag, owner, clock), + device_mixer_interface(mconfig, *this) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mixer_device::device_start() +{ +} diff --git a/src/devices/sound/mixer.h b/src/devices/sound/mixer.h new file mode 100644 index 00000000000..e25466ea351 --- /dev/null +++ b/src/devices/sound/mixer.h @@ -0,0 +1,27 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_SOUND_MIXER_H +#define MAME_SOUND_MIXER_H + +#pragma once + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +DECLARE_DEVICE_TYPE(MIXER, mixer_device) + +// ======================> mixer_device + +class mixer_device : public device_t, public device_mixer_interface +{ +public: + // internal constructor + mixer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + +protected: + // device-level overrides + virtual void device_start() override; +}; + +#endif // MAME_SOUND_MIXER_H diff --git a/src/devices/sound/ym2154.cpp b/src/devices/sound/ym2154.cpp new file mode 100644 index 00000000000..2ad1bbac1fa --- /dev/null +++ b/src/devices/sound/ym2154.cpp @@ -0,0 +1,311 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#include "emu.h" +#include "ym2154.h" + + +//************************************************************************** +// YM2154 DEVICE +//************************************************************************** + +DEFINE_DEVICE_TYPE(YM2154, ym2154_device, "ym2154", "YM2154 (RYP4)") + +//------------------------------------------------- +// ym2154_device - constructor +//------------------------------------------------- + +ym2154_device::ym2154_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, YM2154, tag, owner, clock), + device_sound_interface(mconfig, *this), + device_memory_interface(mconfig, *this), + m_stream(nullptr), + m_timer(nullptr), + m_update_irq(*this), + m_io_read(*this), + m_io_write(*this), + m_group0_config("group0", ENDIANNESS_LITTLE, 8, 18, 0), + m_group1_config("group1", ENDIANNESS_LITTLE, 8, 18, 0), + m_group0_region(*this, "group0"), + m_group1_region(*this, "group1") +{ +} + + +//------------------------------------------------- +// read - handle a device read +//------------------------------------------------- + +u8 ym2154_device::read(offs_t offset) +{ + u8 result = 0xff; + switch (offset & 0x7f) + { + case 0x01: // A/D converter registers + case 0x02: // A/D converter registers + case 0x03: // A/D converter registers + case 0x04: // A/D converter registers + case 0x05: // A/D converter registers + case 0x06: // A/D converter registers + case 0x07: // A/D converter registers + case 0x08: // A/D converter registers + case 0x09: // A/D converter registers + case 0x0a: // A/D converter registers + result = m_io_read.isnull() ? 0 : m_io_read(offset - 1); + break; + + case 0x0e: // IRQ ack + update_irq_state(0); + result = m_irq_count; + break; + } + return result; +} + + +//------------------------------------------------- +// write - handle a device write +//------------------------------------------------- + +void ym2154_device::write(offs_t offset, u8 data) +{ + m_stream->update(); + + u8 chan = BIT(offset, 0, 3); + u8 old; + switch (offset & 0x7f) + { + // timer count + case 0x02: + if (BIT(data, 7) != 0) + m_timer_count = (m_timer_count & 0x0f) | (BIT(data, 0, 7) << 4); + else + m_timer_count = (m_timer_count & 0x7f0) | BIT(data, 0, 4); + break; + + // timer enable/output + case 0x03: + old = m_timer_enable; + m_timer_enable = BIT(data, 2); + if (!m_timer_enable) + m_timer->enable(false); + else if (m_timer_enable && !old) + m_timer->adjust((2048 - m_timer_count) * attotime::from_hz(sample_rate())); + if (!m_io_write.isnull()) + m_io_write(0, BIT(data, 4, 4) ^ 0x0f); + break; + + // output level + case 0x04: + m_total_level = BIT(data, 0, 6); + break; + + // group 1 trigger + case 0x05: + for (int chan = 0; chan < 6; chan++) + if (BIT(data, chan)) + m_channel[6 + chan].start(); + break; + + // group 0 trigger + case 0x06: + for (int chan = 0; chan < 6; chan++) + if (BIT(data, chan)) + m_channel[0 + chan].start(); + break; + + // DAC mode (not implemented) + case 0x07: + break; + + // pan + case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: + m_channel[0 + chan].m_panpot = BIT(data, 0, 4); + m_channel[6 + chan].m_panpot = BIT(data, 4, 4); + break; + + // rate/level, group 0 + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: + m_channel[0 + chan].m_output_level = BIT(data, 0, 5); + m_channel[0 + chan].m_rate = BIT(data, 5, 2); + break; + + // rate/level, group 1 + case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: + m_channel[6 + chan].m_output_level = BIT(data, 0, 5); + m_channel[6 + chan].m_rate = BIT(data, 5, 2); + break; + + // sample position A, group 0 + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: + m_channel[0 + chan].m_start = (m_channel[0 + chan].m_start & 0x0f) | (data << 4); + break; + + // sample position B, group 0 + case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: + m_channel[0 + chan].m_start = (m_channel[0 + chan].m_start & 0xff0) | (data >> 4); + m_channel[0 + chan].m_end = (m_channel[0 + chan].m_end & 0xff) | ((data & 0xf) << 8); + break; + + // sample position C, group 0 + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: + m_channel[0 + chan].m_end = (m_channel[0 + chan].m_end & 0xf00) | data; + break; + + // sample position A, group 1 + case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: + m_channel[6 + chan].m_start = (m_channel[6 + chan].m_start & 0x0f) | (data << 4); + break; + + // sample position B, group 1 + case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: + m_channel[6 + chan].m_start = (m_channel[6 + chan].m_start & 0xff0) | (data >> 4); + m_channel[6 + chan].m_end = (m_channel[6 + chan].m_end & 0xff) | ((data & 0xf) << 8); + break; + + // sample position C, group 1 + case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: + m_channel[6 + chan].m_end = (m_channel[6 + chan].m_end & 0xf00) | data; + break; + } +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ym2154_device::device_start() +{ + // allocate our timer + m_timer = timer_alloc(0); + + // resolve the handlers + m_update_irq.resolve(); + m_io_read.resolve(); + m_io_write.resolve(); + + // allocate our stream + m_stream = stream_alloc(0, 2, sample_rate()); + + // now register the blob for save, on the assumption the size won't change + save_item(NAME(m_timer_count)); + save_item(NAME(m_timer_enable)); + save_item(NAME(m_irq_state)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_total_level)); + save_item(STRUCT_MEMBER(m_channel, m_pos)); + save_item(STRUCT_MEMBER(m_channel, m_start)); + save_item(STRUCT_MEMBER(m_channel, m_end)); + save_item(STRUCT_MEMBER(m_channel, m_panpot)); + save_item(STRUCT_MEMBER(m_channel, m_output_level)); + save_item(STRUCT_MEMBER(m_channel, m_rate)); + + // automatically map memory regions if not configured externally + if (!has_configured_map(0) && !has_configured_map(1)) + { + if (m_group0_region) + space(0).install_rom(0, m_group0_region->bytes() - 1, m_group0_region->base()); + + if (m_group1_region) + space(1).install_rom(0, m_group1_region->bytes() - 1, m_group1_region->base()); + else if (m_group0_region) + space(1).install_rom(0, m_group0_region->bytes() - 1, m_group0_region->base()); + } +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void ym2154_device::device_reset() +{ + for (int chan = 0; chan < 12; chan++) + m_channel[chan].m_pos = 0xfffffff; +} + + +//------------------------------------------------- +// device_clock_changed - clock changed signal +//------------------------------------------------- + +void ym2154_device::device_clock_changed() +{ + if (m_stream != nullptr) + m_stream->set_sample_rate(sample_rate()); +} + + +//------------------------------------------------- +// sound_stream_update - generate sound data +//------------------------------------------------- + +void ym2154_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + update_irq_state(1); + m_timer->adjust((2048 - m_timer_count) * attotime::from_hz(sample_rate())); +} + + +//------------------------------------------------- +// sound_stream_update - generate sound data +//------------------------------------------------- + +void ym2154_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + static const uint16_t voltable[8] = { 0x7fa,0x751,0x6b5,0x627,0x5a4,0x52c,0x4be,0x45a }; + + auto &outl = outputs[0]; + auto &outr = outputs[1]; + + outl.fill(0); + outr.fill(0); + + for (int chan = 0; chan < 12; chan++) + { + auto &channel = m_channel[chan]; + + // not sure how the "rate" really works but it's used to stop a sample from + // playing so just treat it as such + if (channel.m_rate == 3) + m_channel[chan].m_pos = 0xfffffff; + + if ((channel.m_pos >> ADDR_SHIFT) <= channel.m_end && channel.m_panpot != 0) + { + uint32_t vol = (channel.m_output_level ^ 0x1f) + (m_total_level ^ 0x3f); + + uint32_t lvol = vol; + if (channel.m_panpot > 8) + lvol += 4 * (channel.m_panpot - 8); + lvol = voltable[lvol & 7] >> (lvol >> 3); + + uint32_t rvol = vol; + if (channel.m_panpot < 7) + rvol += 4 * (7 - channel.m_panpot); + rvol = voltable[rvol & 7] >> (rvol >> 3); + + for (int sampindex = 0; sampindex < outl.samples() && (channel.m_pos >> ADDR_SHIFT) <= channel.m_end; sampindex++) + { + // unsure what the real data is; for now guessing 8-bit PCM but could + // be ADPCM-A or some other format + int8_t data = space(chan / 6).read_byte(channel.m_pos++); + outl.add_int(sampindex, data * lvol, 0x80 * 0x800); + outr.add_int(sampindex, data * rvol, 0x80 * 0x800); + } + } + } +} + + +//------------------------------------------------- +// memory_space_config - return a description of +// any address spaces owned by this device +//------------------------------------------------- + +device_memory_interface::space_config_vector ym2154_device::memory_space_config() const +{ + return space_config_vector{ + std::make_pair(0, &m_group0_config), + std::make_pair(1, &m_group1_config) + }; +} diff --git a/src/devices/sound/ym2154.h b/src/devices/sound/ym2154.h new file mode 100644 index 00000000000..58976f0177b --- /dev/null +++ b/src/devices/sound/ym2154.h @@ -0,0 +1,99 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_SOUND_YM2154_H +#define MAME_SOUND_YM2154_H + +#pragma once + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +DECLARE_DEVICE_TYPE(YM2154, ym2154_device) + +// ======================> ym2154_device + +class ym2154_device : public device_t, public device_sound_interface, public device_memory_interface +{ + static constexpr uint8_t ADDR_SHIFT = 6; + +public: + // internal constructor + ym2154_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // configuration helpers + auto irq_handler() { return m_update_irq.bind(); } + auto io_read_handler() { return m_io_read.bind(); } + auto io_write_handler() { return m_io_write.bind(); } + + // register reads + u8 read(offs_t offset); + + // register writes + void write(offs_t offset, u8 data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + // sound overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + + // memory space configuration + virtual space_config_vector memory_space_config() const override; + +private: + struct channel + { + channel() : + m_pos(0xfffffff), m_start(0), m_end(0), m_panpot(0), m_output_level(0), m_rate(0) { } + + void reset() + { + m_pos = 0; + m_start = m_end = 0; + m_panpot = 0; + m_output_level = 0; + m_rate = 0; + } + + void start() + { + m_pos = m_start << ADDR_SHIFT; + } + + uint32_t m_pos; + uint16_t m_start; + uint16_t m_end; + uint8_t m_panpot; + uint8_t m_output_level; + uint8_t m_rate; + }; + + // internal helpers + u32 sample_rate() const { return device_t::clock() / 18 / 6; } + void update_irq_state(u8 state) { if (m_irq_state != state) { m_irq_state = state; m_update_irq(state); } } + + // internal state + sound_stream *m_stream; // sound stream + emu_timer *m_timer; // two timers + uint16_t m_timer_count; // current timer count + uint8_t m_timer_enable; // timer enable + uint8_t m_irq_state; // current IRQ state + uint8_t m_irq_count; // current IRQ count + uint8_t m_total_level; // master volume + channel m_channel[12]; // output channels + devcb_write_line m_update_irq; // IRQ update callback + devcb_read8 m_io_read; // input port handler + devcb_write8 m_io_write; // output port handler + address_space_config const m_group0_config; // address space 0 config + address_space_config const m_group1_config; // address space 1 config + optional_memory_region m_group0_region; // group 0 memory region + optional_memory_region m_group1_region; // group 1 memory region +}; + + +#endif // MAME_SOUND_YM2154_H diff --git a/src/mame/drivers/ympsr60.cpp b/src/mame/drivers/ympsr60.cpp index 80528354359..546ade171e6 100644 --- a/src/mame/drivers/ympsr60.cpp +++ b/src/mame/drivers/ympsr60.cpp @@ -46,6 +46,9 @@ #include "machine/i8255.h" #include "machine/6850acia.h" #include "machine/clock.h" +#include "sound/bbd.h" +#include "sound/mixer.h" +#include "sound/ym2154.h" #include "sound/ymopq.h" #include "bus/midi/midi.h" @@ -68,12 +71,17 @@ public: m_ym3533(*this, "ym3533"), m_ppi(*this, "ppi"), m_acia(*this, "acia"), + m_lmixer(*this, "lmixer"), + m_rmixer(*this, "rmixer"), + m_bbd(*this, "bbd"), + m_ryp4(*this, "ryp4"), m_rom2bank(*this, "rom2bank"), m_keyboard(*this, "P1_%u", 0), m_drvif(*this, "DRVIF_%u", 0), m_drvif_out(*this, "DRVIF_%u_DP%u", 0U, 1U), m_ryp4_in(*this, "RYP4_%u", 1U), - m_ryp4_out(*this, "RYP4_%u", 1U) + m_ryp4_out(*this, "RYP4_%u", 1U), + m_mastervol(*this, "MASTERVOL") { } void psr_common(machine_config &config); @@ -83,21 +91,24 @@ public: protected: virtual void machine_start() override; virtual void machine_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; private: required_device m_maincpu; required_device m_ym3533; required_device m_ppi; required_device m_acia; + required_device m_lmixer; + required_device m_rmixer; + required_device m_bbd; + required_device m_ryp4; required_memory_bank m_rom2bank; required_ioport_array<10> m_keyboard; required_ioport_array m_drvif; output_finder m_drvif_out; required_ioport_array m_ryp4_in; output_finder m_ryp4_out; - u8 m_ym2154_regs[0x80]; - u16 m_ym2154_tempo; - emu_timer *m_ym2154_timer; + required_ioport m_mastervol; void psr60_map(address_map &map); void psr60_io_map(address_map &map); @@ -109,25 +120,24 @@ private: int m_acia_irq, m_ym_irq, m_drvif_irq, m_ym2154_irq; u16 m_keyboard_select; + u8 m_bbd_config; u8 m_drvif_data[2]; u8 m_drvif_select; WRITE_LINE_MEMBER(write_acia_clock) { m_acia->write_txc(state); m_acia->write_rxc(state); } WRITE_LINE_MEMBER(acia_irq_w) { m_acia_irq = state; recalc_irqs(); } - WRITE_LINE_MEMBER(ym_irq_w) { m_ym_irq = state; recalc_irqs(); } + WRITE_LINE_MEMBER(ryp4_irq_w) { m_ym2154_irq = state; recalc_irqs(); } + + u8 ryp4_an_r(offs_t offset); + void ryp4_out_w(u8 data); u8 drvif_r(offs_t offset); void drvif_w(offs_t offset, u8 data); - u8 ym2154_r(offs_t offset); - void ym2154_w(offs_t offset, u8 data); - attotime ym2154_period(u32 tempo) const { return attotime::from_hz(2250000 / 12 / 8) * (2048 - tempo); } - public: INPUT_CHANGED_MEMBER(drvif_changed); - INPUT_CHANGED_MEMBER(ryp4_changed); - TIMER_CALLBACK_MEMBER(ym2154_timer); + INPUT_CHANGED_MEMBER(mastervol_changed); }; void psr60_state::psr60_map(address_map &map) @@ -138,148 +148,13 @@ void psr60_state::psr60_map(address_map &map) map(0xe000, 0xffff).ram(); // work RAM } -// -// YM2154 - ROMpler with I/O ports -// -// Chip is clocked at 2.25MHz -// -// Some of this looks suspiciously like 2x the ADPCM-A attached to the -// YM2608: 6 channels (2 banks), total level and instrument levels behave -// the same. -// -// Start/end addresses are 9 bits to address 32k ROMs (15 bits) so -// presume a left shift of 6 (actually 7 if 4-bit ADPCM data). -// -// Two serial ROMs are connected, containing 32k of sample data each. -// It appears that the channel bank (0-5 vs 6-11) implies which of the -// two ROMs is read. -// -// System-wide registers: -// 01 -------- Unknown ($00 written at init) -// 02 1xxxxxxx Timer upper bits latch -// 0---xxxx Timer lower bits -// 03 -x------ Unknown (IRQ enable?) -// -----x-- Timer enable -// 04 --xxxxxx Master volume (0=min, 3F=max) -// 05 --xxxxxx Bank 2 key on/off -// 06 --xxxxxx Bank 1 key on/off -// 07 -------x Unknown ($01 written at init) -// -// Per-channel registers (channel in address bits 0-2) -// 08-0D -------- Unknown; written as two nibbles so shared between banks -// 10-15 -xx----- Bank 1 Write 11 here to disable? -// ---xxxxx Bank 1 Instrument volume (0=min, 1F=max) -// 18-1D -xx----- Bank 2 Write 11 here to disable? -// ---xxxxx Bank 2 Instrument volume (0=min, 1F=max) -// 20-25 ---xxxxx Bank 1 Start address upper bits -// 28-2D xxxx---- Bank 1 Start address lower bits -// -------x Bank 1 End address upper bits -// 30-35 xxxxxxxx Bank 1 End address lower bits -// 38-3D ---xxxxx Bank 2 Start address upper bits -// 40-45 xxxx---- Bank 2 Start address lower bits -// -------x Bank 2 End address upper bits -// 48-4D xxxxxxxx Bank 2 End address lower bits -// -// Reads: -// 01-0A xxxxxxxx AN1-10 digital values -// 0E -------- IRQ clear? -// - -u8 psr60_state::ym2154_r(offs_t offset) -{ - u8 result = 0; - - // read analog inputs - if (offset >= 1 && offset <= 10) - { - if (offset - 1 < RYP4_MAX_TARGETS) - result = m_ryp4_in[offset - 1]->read(); - } - else if (offset == 14) - { - m_ym2154_irq = 0; - recalc_irqs(); - } - else - printf("YM2154: Read %02X\n", offset); - - return result; -} - -void psr60_state::ym2154_w(offs_t offset, u8 data) -{ -// printf("YM2154: Write %02X=%02X\n", offset, data); - u8 old = m_ym2154_regs[offset]; - m_ym2154_regs[offset] = data; - - // tempo - if (offset == 2) - { - // two writes in succession - if (BIT(data, 7) == 0 && BIT(old, 7) != 0) - { - m_ym2154_tempo = (BIT(old, 0, 7) << 4) | (data & 0xf); - printf("YM2154: tempo = %03X\n", m_ym2154_tempo); - } - } - - // control - else if (offset == 3) - { - if (BIT(old ^ data, 2) != 0) - { - if (BIT(data, 2) != 0) - m_ym2154_timer->adjust(ym2154_period(m_ym2154_tempo)); - else - m_ym2154_timer->enable(false); - } - } - - // total overall level - else if (offset == 4) - { - } - - // key on - else if (offset == 5) - { - for (int bit = 0; bit < 6; bit++) - if (BIT(data, bit) != 0) - printf("YM2154: Bank 2 Ch %d on: %03X-%03X vol=%02X low=%X\n", bit, (m_ym2154_regs[0x38+bit] << 4) | (m_ym2154_regs[0x40+bit] >> 4), ((m_ym2154_regs[0x40+bit] & 0x0f) << 8) | m_ym2154_regs[0x48+bit], m_ym2154_regs[0x18+bit], m_ym2154_regs[0x08+bit] >> 4); - } - else if (offset == 6) - { - for (int bit = 0; bit < 6; bit++) - if (BIT(data, bit) != 0) - printf("YM2154: Bank 1 Ch %d on: %03X-%03X vol=%02X low=%X\n", bit, (m_ym2154_regs[0x20+bit] << 4) | (m_ym2154_regs[0x28+bit] >> 4), ((m_ym2154_regs[0x28+bit] & 0x0f) << 8) | m_ym2154_regs[0x30+bit], m_ym2154_regs[0x10+bit], m_ym2154_regs[0x08+bit] & 0xf); - } - else if (offset < 8 || offset >= 0x50) - printf("YM2154: Write %02X = %02X\n", offset, data); -} - -TIMER_CALLBACK_MEMBER(psr60_state::ym2154_timer) -{ - m_ym2154_irq = 1; - recalc_irqs(); - if (BIT(m_ym2154_regs[3], 2) != 0) - m_ym2154_timer->adjust(ym2154_period(m_ym2154_tempo)); -} - -INPUT_CHANGED_MEMBER(psr60_state::ryp4_changed) -{ - // why does this crash? need to feed the values to an output to reflect - // the sliders in the layout -// m_ryp4_out[param] = m_ryp4_in[param]->read(); -} - - void psr60_state::psr60_io_map(address_map &map) { map.global_mask(0xff); // top 8 bits of the address are ignored by this hardware for I/O access map(0x10, 0x11).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); map(0x20, 0x23).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x30, 0x3f).rw(FUNC(psr60_state::drvif_r), FUNC(psr60_state::drvif_w)); - map(0x80, 0xff).rw(FUNC(psr60_state::ym2154_r), FUNC(psr60_state::ym2154_w)); + map(0x80, 0xff).rw(m_ryp4, FUNC(ym2154_device::read), FUNC(ym2154_device::write)); } u8 psr60_state::ppi_pa_r() @@ -302,6 +177,49 @@ void psr60_state::ppi_pc_w(u8 data) m_keyboard_select = (m_keyboard_select & ~0x300) | ((data & 3) << 8); } +u8 psr60_state::ryp4_an_r(offs_t offset) +{ + return (offset < RYP4_MAX_TARGETS) ? (m_ryp4_in[offset]->read() * 255 / 100) : 0; +} + +void psr60_state::ryp4_out_w(u8 data) +{ + m_bbd_config = data; + + // bit 0 (CT0) enables/disables the effect + m_bbd->set_output_gain(0, BIT(data, 0) ? 1.0 : 0.0); + + // bits 1 + 2 go to the 'T' and 'C' pins and control the frequency + // modulation, which we simulate in a periodic timer +} + +void psr60_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + // only two states have been observed to be measured: CT1=1/CT2=0 and CT1=0/CT2=1 + attotime curtime = machine().time(); + double bbd_freq; + if (BIT(m_bbd_config, 1) && !BIT(m_bbd_config, 2)) + { + // Stereo symphonic off: min freq 35 kHz, max freq 107 kHz, varies at 0,3 Hz + curtime.m_seconds %= 3; + double pos = curtime.as_double() / 3; + pos = (pos < 0.5) ? (2 * pos) : 2 * (1.0 - pos); + bbd_freq = 35 + (107 - 35) * pos; + } + else + { + // Stereo symphonic on: min freq 48 kHz, max freq 61 kHz, varies at 6 Hz + curtime.m_seconds = 0; + double pos = curtime.as_double() * 6; + pos -= floor(pos); + pos = (pos < 0.5) ? (2 * pos) : 2 * (1.0 - pos); + bbd_freq = 48 + (61 - 48) * pos; + } + + // two out-of-phase clocks run the BBD so it's effectively 2x frequency + m_bbd->set_unscaled_clock(bbd_freq * 2); +} + // // DRVIF: driver interface chip // @@ -345,6 +263,13 @@ INPUT_CHANGED_MEMBER(psr60_state::drvif_changed) recalc_irqs(); } +INPUT_CHANGED_MEMBER(psr60_state::mastervol_changed) +{ + float mastervol = m_mastervol->read() / 75.0; + m_lmixer->set_output_gain(0, mastervol); + m_rmixer->set_output_gain(0, mastervol); +} + void psr60_state::recalc_irqs() { int irq_state = m_acia_irq | m_ym_irq | m_drvif_irq | m_ym2154_irq; @@ -357,8 +282,7 @@ void psr60_state::machine_start() m_rom2bank->configure_entries(0, 2, memregion("rom2")->base(), 0x4000); m_rom2bank->set_entry(0); m_acia_irq = CLEAR_LINE; - - m_ym2154_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(psr60_state::ym2154_timer), this)); + timer_alloc()->adjust(attotime::from_hz(50), 0, attotime::from_hz(50)); } void psr60_state::machine_reset() @@ -374,7 +298,7 @@ void psr60_state::machine_reset() #define RYP4_PORT(num, name) \ PORT_START("RYP4_" #num) \ - PORT_ADJUSTER(0xc0, name) PORT_MINMAX(0x00, 0xff) PORT_CHANGED_MEMBER(DEVICE_SELF, psr60_state, ryp4_changed, num - 1) + PORT_ADJUSTER(75, name) static INPUT_PORTS_START(psr60) PORT_START("P1_9") @@ -497,6 +421,9 @@ static INPUT_PORTS_START(psr60) RYP4_PORT(6, "Bass Volume") RYP4_PORT(7, "Unused7") RYP4_PORT(8, "Unused8") + + PORT_START("MASTERVOL") + PORT_ADJUSTER(75, "Master Volume") PORT_CHANGED_MEMBER(DEVICE_SELF, psr60_state, mastervol_changed, 0) INPUT_PORTS_END static INPUT_PORTS_START(psr70) @@ -632,6 +559,9 @@ static INPUT_PORTS_START(psr70) RYP4_PORT(6, "Bass Volume") RYP4_PORT(7, "Unused7") RYP4_PORT(8, "Unused8") + + PORT_START("MASTERVOL") + PORT_ADJUSTER(75, "Master Volume") PORT_CHANGED_MEMBER(DEVICE_SELF, psr60_state, mastervol_changed, 0) INPUT_PORTS_END void psr60_state::psr_common(machine_config &config) @@ -660,10 +590,30 @@ void psr60_state::psr_common(machine_config &config) SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); + MIXER(config, m_lmixer); + m_lmixer->add_route(0, "lspeaker", 1.0); + + MIXER(config, m_rmixer); + m_rmixer->add_route(0, "rspeaker", 1.0); + + YM2154(config, m_ryp4, 2250000); + m_ryp4->irq_handler().set(FUNC(psr60_state::ryp4_irq_w)); + m_ryp4->io_read_handler().set(FUNC(psr60_state::ryp4_an_r)); + m_ryp4->io_write_handler().set(FUNC(psr60_state::ryp4_out_w)); + m_ryp4->add_route(0, m_lmixer, 0.50); // rhythm channels are mixed in separately + m_ryp4->add_route(1, m_rmixer, 0.50); + + MN3204P(config, m_bbd, 12700); // 6.7kHz feeds the iG10090 driver, range is 10-200kHz + m_bbd->add_route(0, m_lmixer, 0.11); // BBD is mixed in + m_bbd->add_route(0, m_rmixer, 0.11); + YM3533(config, m_ym3533, 3.579545_MHz_XTAL); m_ym3533->irq_handler().set(FUNC(psr60_state::ym_irq_w)); - m_ym3533->add_route(0, "lspeaker", 1.0); - m_ym3533->add_route(1, "rspeaker", 1.0); + m_ym3533->add_route(0, m_lmixer, 0.16); // channel 1 = ORC + m_ym3533->add_route(0, m_rmixer, 0.16); + m_ym3533->add_route(0, m_bbd, 1.0); + m_ym3533->add_route(1, m_lmixer, 0.22); // channel 2 = SABC + m_ym3533->add_route(1, m_rmixer, 0.22); } void psr60_state::psr60(machine_config &config) @@ -684,6 +634,12 @@ ROM_START( psr60 ) ROM_REGION(0x8000, "rom2", 0) ROM_LOAD("yamaha_psr60_pgm_ic110.bin", 0x000000, 0x008000, CRC(39db8c74) SHA1(7750104d1e5df3357aa553ac58768dbc34051cd8)) + + ROM_REGION(0x8000, "ryp4:group0", 0) + ROM_LOAD("ym21908.bin", 0x0000, 0x8000, CRC(4ed0d9dc) SHA1(aed7ab6f1c9e28fdf259cb932136b12845040d79) BAD_DUMP) + + ROM_REGION(0x8000, "ryp4:group1", 0) + ROM_LOAD("ym21909.bin", 0x0000, 0x8000, CRC(bb9bb698) SHA1(76563d1f25152cd54041019ef7bc264ede0d8b2b) BAD_DUMP) ROM_END ROM_START(psr70) @@ -692,6 +648,12 @@ ROM_START(psr70) ROM_REGION(0x8000, "rom2", 0) ROM_LOAD("yamaha_psr60_pgm_ic110.bin", 0x000000, 0x008000, CRC(39db8c74) SHA1(7750104d1e5df3357aa553ac58768dbc34051cd8)) + + ROM_REGION(0x8000, "ryp4:group0", 0) + ROM_LOAD("ym21908.bin", 0x0000, 0x8000, CRC(4ed0d9dc) SHA1(aed7ab6f1c9e28fdf259cb932136b12845040d79) BAD_DUMP) + + ROM_REGION(0x8000, "ryp4:group1", 0) + ROM_LOAD("ym21909.bin", 0x0000, 0x8000, CRC(bb9bb698) SHA1(76563d1f25152cd54041019ef7bc264ede0d8b2b) BAD_DUMP) ROM_END CONS(1985, psr60, 0, 0, psr60, psr60, psr60_state, empty_init, "Yamaha", "PSR-60 PortaSound", MACHINE_NOT_WORKING | MACHINE_CLICKABLE_ARTWORK) diff --git a/src/mame/layout/psr60.lay b/src/mame/layout/psr60.lay index e008753326a..7fd4d694efd 100644 --- a/src/mame/layout/psr60.lay +++ b/src/mame/layout/psr60.lay @@ -279,6 +279,61 @@ license:CC0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -388,11 +443,17 @@ license:CC0 + + + + + + @@ -451,11 +512,21 @@ license:CC0 + + + + + + + + + + @@ -553,11 +624,21 @@ license:CC0 + + + + + + + + + + @@ -667,6 +748,11 @@ license:CC0 + + + + + @@ -750,6 +836,11 @@ license:CC0 + + + + + diff --git a/src/mame/layout/psr70.lay b/src/mame/layout/psr70.lay index 102d7951f09..369f7355b12 100644 --- a/src/mame/layout/psr70.lay +++ b/src/mame/layout/psr70.lay @@ -268,6 +268,31 @@ license:CC0 + + + + + + + + + + + + + + + + + + + + + + + + + @@ -384,11 +409,17 @@ license:CC0 + + + + + + @@ -416,11 +447,21 @@ license:CC0 + + + + + + + + + + @@ -518,11 +559,21 @@ license:CC0 + + + + + + + + + + @@ -617,6 +668,11 @@ license:CC0 + + + + + @@ -709,6 +765,11 @@ license:CC0 + + + + + @@ -773,7 +834,7 @@ license:CC0 - +