diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 27002fb8bd8..bcbc9749c4b 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -10,6 +10,8 @@ ---------------------------------------------------------------------------- files { + 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", diff --git a/scripts/target/mame/ci.lua b/scripts/target/mame/ci.lua index d710f3a4ea9..d4f1014864d 100644 --- a/scripts/target/mame/ci.lua +++ b/scripts/target/mame/ci.lua @@ -120,8 +120,8 @@ function createProjects_mame_ci(_target, _subtarget) } files{ - MAME_DIR .. "src/mame/audio/nl_carpolo.h", MAME_DIR .. "src/mame/audio/nl_carpolo.cpp", + MAME_DIR .. "src/mame/audio/nl_carpolo.h", MAME_DIR .. "src/mame/drivers/carpolo.cpp", MAME_DIR .. "src/mame/includes/carpolo.h", MAME_DIR .. "src/mame/machine/carpolo.cpp", @@ -138,10 +138,10 @@ files{ MAME_DIR .. "src/mame/audio/exidy440.cpp", MAME_DIR .. "src/mame/audio/exidy440.h", MAME_DIR .. "src/mame/drivers/starfire.cpp", - MAME_DIR .. "src/mame/audio/nl_fireone.h", MAME_DIR .. "src/mame/audio/nl_fireone.cpp", - MAME_DIR .. "src/mame/audio/nl_starfire.h", + MAME_DIR .. "src/mame/audio/nl_fireone.h", MAME_DIR .. "src/mame/audio/nl_starfire.cpp", + MAME_DIR .. "src/mame/audio/nl_starfire.h", MAME_DIR .. "src/mame/includes/starfire.h", MAME_DIR .. "src/mame/video/starfire.cpp", MAME_DIR .. "src/mame/drivers/vertigo.cpp", @@ -165,6 +165,8 @@ files{ MAME_DIR .. "src/mame/audio/williams.cpp", MAME_DIR .. "src/mame/audio/williams.h", MAME_DIR .. "src/mame/video/williams.cpp", + MAME_DIR .. "src/mame/audio/s11c_bg.cpp", + MAME_DIR .. "src/mame/audio/s11c_bg.h", MAME_DIR .. "src/mame/drivers/gaelco.cpp", MAME_DIR .. "src/mame/includes/gaelco.h", MAME_DIR .. "src/mame/video/gaelco.cpp", diff --git a/src/devices/sound/flt_biquad.cpp b/src/devices/sound/flt_biquad.cpp new file mode 100644 index 00000000000..a0416bfafb1 --- /dev/null +++ b/src/devices/sound/flt_biquad.cpp @@ -0,0 +1,487 @@ +// license:BSD-3-Clause +// copyright-holders:K.Wilkins,Couriersud,Derrick Renaud,Frank Palazzolo,Jonathan Gevaryahu +/* + This is an implementation of a Direct-form II digital biquad filter, + intended for use in audio paths for filtering audio to or from other + stream devices. + + It has a number of constructor-helpers for automatically generating + a biquad filter equivalent to the filter response of a few standard + analog second order filter topographies. + + This biquad filter implementation is based on one written by Frank + Palazzolo, K. Wilkins, Couriersud, and Derrick Renaud, with some changes: + * It uses the Q factor directly in the filter definitions, rather than the damping factor (1/Q) + * It implements every common type of digital biquad filter which I could find documentation for. + * The filter is Direct-form II instead of Direct-form I, which results in shorter compiled code. + + Possibly useful features which aren't implemented because nothing uses them yet: + * More Sallen-Key filter variations (band-pass, high-pass) + * Direct control of the 5 normalized biquad parameters for a custom/raw parameter filter. +*/ +#include "emu.h" +#include "flt_biquad.h" + +// we need the M_SQRT2 constant +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + +// define this to display debug info about the filters being set up +#undef FLT_BIQUAD_DEBUG_SETUP + +// device type definition +DEFINE_DEVICE_TYPE(FILTER_BIQUAD, filter_biquad_device, "filter_biquad", "Biquad Filter") + +// allow the enum class for the biquad filter type to be saved by the savestate system +ALLOW_SAVE_TYPE(filter_biquad_device::biquad_type); + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// filter_biquad_device - constructor +//------------------------------------------------- + +// initialize with some sane defaults for a highpass filter with a cutoff at 16hz, same as flt_rc's 'ac' mode. +filter_biquad_device::filter_biquad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, FILTER_BIQUAD, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_stream(nullptr), + m_type(biquad_type::HIGHPASS), + m_last_sample_rate(0), + m_fc(16.0), + m_q(M_SQRT2/2.0), + m_gain(1.0), + m_input(0.0), + m_w0(0.0), + m_w1(0.0), + m_w2(0.0), + m_output(0.0), + m_a1(0.0), + m_a2(0.0), + m_b0(1.0), + m_b1(0.0), + m_b2(0.0) +{ +} + +// set up the filter with the specified parameters and return a pointer to the new device +filter_biquad_device& filter_biquad_device::setup(biquad_type type, double fc, double q, double gain) +{ + m_type = type; + m_fc = fc; + m_q = q; + m_gain = gain; + return *this; +} + +// update an existing instance with new filter parameters +void filter_biquad_device::update_params(biquad_type type, double fc, double q, double gain) +{ + m_stream->update(); + m_type = type; + m_fc = fc; + m_q = q; + m_gain = gain; + recalc(); +} + + +//------------------------------------------------- +// Filter setup helpers for various filter models +//------------------------------------------------- + +// Sallen-Key filters + +/* Setup a biquad filter structure based on a single op-amp Sallen-Key low-pass filter circuit. + * + * .----------------------------. + * | | + * --- c2 | + * --- | + * | | + * r1 | r2 |\ | + * In >----ZZZZ----+--ZZZZ---+--------+ | \ | + * | '--|+ \ | + * --- c1 | >--+------> out + * --- .--|- / | + * | | | / | + * gnd | |/ | + * | | + * | r4 | + * +--ZZZZ---' + * | + * Z + * Z r3 + * Z + * | + * gnd + */ +filter_biquad_device& filter_biquad_device::opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2) +{ + if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (r4 == 0) || (c1 == 0) || (c2 == 0)) + { + fatalerror("filter_biquad_device::opamp_sk_lowpass_setup() - no parameters can be 0; parameters were: r1: %f, r2: %f, r3: %f, r4: %f, c1: %f, c2: %f", r1, r2, r3, r4, c1, c2); /* Filter can not be setup. Undefined results. */ + } + + // note: if R3 doesn't exist (no link to ground), pass a value of RES_M(999.99) or the like, i.e. an 'infinite resistor' + double const gain = (r3 + r4) / r3; + double const fc = 1.0 / (2 * M_PI * sqrt(r1 * r2 * c1 * c2)); + double const q = sqrt(r1 * r2 * c1 * c2) / ((r1 * c1) + (r2 * c1) + ((r2 * c2) * (1.0 - gain))); +#ifdef FLT_BIQUAD_DEBUG_SETUP + logerror("filter_biquad_device::opamp_sk_lowpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain); +#endif + return setup(biquad_type::LOWPASS, fc, q, gain); +} + +// Multiple-Feedback filters +// (This is sometimes called a 'Rauch' filter circuit.) + +/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback low-pass filter circuit. + * NOTE: vRef is not definable when setting up the filter. + * If the analog effects caused by vRef are important to the operation of the specific filter + * in question, a netlist implementation may work better under those circumstances. + * + * .--------+---------. + * | | | + * Z --- c2 | + * Z r3 --- | + * Z | | + * r1 | r2 | |\ | + * In >----ZZZZ----+---------+--ZZZZ--+ | \ | + * | '--|- \ | + * --- c1 | >--+------> out + * --- .--|+ / + * | | | / + * gnd vRef >---' |/ + * + */ +filter_biquad_device& filter_biquad_device::opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2) +{ + if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (c2 == 0)) + { + fatalerror("filter_biquad_device::opamp_mfb_lowpass_setup() - only c1 can be 0; parameters were: r1: %f, r2: %f, r3: %f, c1: %f, c2: %f", r1, r2, r3, c1, c2); /* Filter can not be setup. Undefined results. */ + } + + double const gain = -r3 / r1; + double fc, q = (M_SQRT2 / 2.0); + if (c1 == 0) // set C1 to 0 to run this filter in a degraded single pole mode where C1 was left off the filter entirely. Certain Williams boards seem to have omitted C1, presumably by accident. + { + fc = (r1 * r3) / (2 * M_PI * ((r1 * r2) + (r1 * r3) + (r2 * r3)) * r3 * c2); +#ifdef FLT_BIQUAD_DEBUG_SETUP + logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in degraded mode yields: fc = %f, Q = %f(ignored), gain = %f\n", fc, q, gain); +#endif + return setup(biquad_type::LOWPASS1P, fc, q, gain); + } + else + { + fc = 1.0 / (2 * M_PI * sqrt(r2 * r3 * c1 * c2)); + q = sqrt(r2 * r3 * c1 * c2) / ((r3 * c2) + (r2 * c2) + ((r2 * c2) * -gain)); +#ifdef FLT_BIQUAD_DEBUG_SETUP + logerror("filter_biquad_device::opamp_mfb_lowpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain); +#endif + return setup(biquad_type::LOWPASS, fc, q, gain); + } +} + +/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback band-pass filter circuit. + * NOTE: vRef is not definable when setting up the filter. + * If the analog effects caused by vRef are important to the operation of the specific filter + * in question, a netlist implementation may work better under those circumstances. + * NOTE2: If r2 is not used, then set it to 0 ohms, the code will ignore it and assume a fixed gain of 1. + * + * .--------+---------. + * | | | + * --- c1 Z | + * --- Z r3 | + * | Z | + * r1 | c2 | |\ | + * In >----ZZZZ----+---------+--||----+ | \ | + * Z '--|- \ | + * Z r2 | >--+------> out + * Z .--|+ / + * | | | / + * gnd vRef >---' |/ + * + */ +filter_biquad_device& filter_biquad_device::opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2) +{ + if ((r1 == 0) || (r3 == 0) || (c1 == 0) || (c2 == 0)) + { + fatalerror("filter_biquad_device::opamp_mfb_bandpass_setup() - only r2 can be 0; parameters were: r1: %f, r2: %f, r3: %f, c1: %f, c2: %f", r1, r2, r3, c1, c2); /* Filter can not be setup. Undefined results. */ + } + + double r_in, gain; + + if (r2 == 0) + { + gain = 1; + r_in = r1; + } + else + { + gain = r2 / (r1 + r2); + r_in = 1.0 / (1.0/r1 + 1.0/r2); + } + + double const fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2)); + double const q = sqrt(r3 / r_in * c1 * c2) / (c1 + c2); + gain *= -r3 / r_in * c2 / (c1 + c2); +#ifdef FLT_BIQUAD_DEBUG_SETUP + logerror("filter_biquad_device::opamp_mfb_bandpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain); +#endif + return setup(biquad_type::BANDPASS, fc, q, gain); +} + +/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback high-pass filter circuit. + * NOTE: vRef is not definable when setting up the filter. + * If the analog effects caused by vRef are important to the operation of the specific filter + * in question, a netlist implementation may work better under those circumstances. + * + * .--------+---------. + * | | | + * --- c3 Z | + * --- Z r2 | + * | Z | + * c1 | c2 | |\ | + * In >-----||-----+---------+---||---+ | \ | + * Z '--|- \ | + * Z r1 | >--+------> out + * Z .--|+ / + * | | | / + * gnd vRef >---' |/ + * + */ +filter_biquad_device& filter_biquad_device::opamp_mfb_highpass_setup(double r1, double r2, double c1, double c2, double c3) +{ + if ((r1 == 0) || (r2 == 0) || (c1 == 0) || (c2 == 0) || (c3 == 0)) + { + fatalerror("filter_biquad_device::opamp_mfb_highpass_setup() - no parameters can be 0; parameters were: r1: %f, r2: %f, c1: %f, c2: %f, c3: %f", r1, r2, c1, c2, c3); /* Filter can not be setup. Undefined results. */ + } + // TODO: if c1 is 0/shorted, should the circuit should work with a gain of 1 in a first order mode? + + double const gain = -c1 / c3; + double const fc = 1.0 / (2 * M_PI * sqrt(c2 * c3 * r1 * r2)); + double const q = sqrt(c2 * c3 * r1 * r2) / ((c2 * r1) + (c3 * r1) + ((c3 * r1) * -gain)); +#ifdef FLT_BIQUAD_DEBUG_SETUP + logerror("filter_biquad_device::opamp_mfb_highpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain); +#endif + return setup(biquad_type::HIGHPASS, fc, q, gain); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void filter_biquad_device::device_start() +{ + m_stream = stream_alloc(1, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE); + m_last_sample_rate = 0; + recalc(); + + save_item(NAME(m_type)); + save_item(NAME(m_last_sample_rate)); + save_item(NAME(m_fc)); + save_item(NAME(m_q)); + save_item(NAME(m_gain)); + save_item(NAME(m_input)); + save_item(NAME(m_w0)); + save_item(NAME(m_w1)); + save_item(NAME(m_w2)); + save_item(NAME(m_output)); + save_item(NAME(m_a1)); + save_item(NAME(m_a2)); + save_item(NAME(m_b0)); + save_item(NAME(m_b1)); + save_item(NAME(m_b2)); +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void filter_biquad_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + auto &src = inputs[0]; + auto &dst = outputs[0]; + + if (m_last_sample_rate != m_stream->sample_rate()) + { + recalc(); + m_last_sample_rate = m_stream->sample_rate(); + } + + for (int sampindex = 0; sampindex < dst.samples(); sampindex++) + { + m_input = src.get(sampindex); + step(); + dst.put(sampindex, m_output); + } +} + + +/* Calculate the filter context based on the passed filter type info. + * m_type - 1 of the 9 defined filter types + * m_fc - center frequency + * m_q - 'Q' (quality) factor of filter (1/damp) + * m_gain - overall filter gain. Set to 1.0 if not needed. The exact meaning of gain changes depending on the filter type. + */ +void filter_biquad_device::recalc() +{ + double const MGain = fabs(m_gain); // absolute multiplicative gain + double const DBGain = log10(MGain) * 20.0; // gain in dB + double const AMGain = pow(10, fabs(DBGain) / 20.0); // multiplicative gain of absolute DB + double const K = tan(M_PI * m_fc / m_stream->sample_rate()); + double const Ksquared = K * K; + double const KoverQ = K / m_q; + double normal = 1.0 / (1.0 + KoverQ + Ksquared); + + m_a1 = 2.0 * (Ksquared - 1.0) * normal; + m_a2 = (1.0 - KoverQ + Ksquared) * normal; + + switch (m_type) + { + case biquad_type::LOWPASS1P: + m_a1 = exp(-2.0 * M_PI * (m_fc / m_stream->sample_rate())); + m_b0 = 1.0 - m_a1; + m_a1 = -m_a1; + m_b1 = m_b2 = m_a2 = 0.0; + break; + case biquad_type::HIGHPASS1P: + m_a1 = -exp(-2.0 * M_PI * (0.5 - m_fc / m_stream->sample_rate())); + m_b0 = 1.0 + m_a1; + m_a1 = -m_a1; + m_b1 = m_b2 = m_a2 = 0.0; + break; + case biquad_type::LOWPASS: + m_b0 = Ksquared * normal; + m_b1 = 2.0 * m_b0; + m_b2 = 1.0 * m_b0; + m_a1 = 2.0 * (Ksquared - 1.0) * normal; + m_a2 = (1.0 - KoverQ + Ksquared) * normal; + break; + case biquad_type::HIGHPASS: + m_b0 = 1.0 * normal; + m_b1 = -2.0 * m_b0; + m_b2 = 1.0 * m_b0; + m_a1 = 2.0 * (Ksquared - 1.0) * normal; + m_a2 = (1.0 - KoverQ + Ksquared) * normal; + break; + case biquad_type::BANDPASS: + m_b0 = KoverQ * normal; + m_b1 = 0.0; + m_b2 = -1.0 * m_b0; + m_a1 = 2.0 * (Ksquared - 1.0) * normal; + m_a2 = (1.0 - KoverQ + Ksquared) * normal; + break; + case biquad_type::NOTCH: + m_b0 = (1.0 + Ksquared) * normal; + m_b1 = 2.0 * (Ksquared - 1.0) * normal; + m_b2 = 1.0 * m_b0; + m_a1 = 1.0 * m_b1; + m_a2 = (1.0 - KoverQ + Ksquared) * normal; + break; + case biquad_type::PEAK: + if (DBGain >= 0.0) + { + m_b0 = (1.0 + (AMGain * KoverQ) + Ksquared) * normal; + m_b1 = 2.0 * (Ksquared - 1.0) * normal; + m_b2 = (1.0 - (AMGain * KoverQ) + Ksquared) * normal; + m_a1 = 1.0 * m_b1; + m_a2 = (1.0 - KoverQ + Ksquared) * normal; + } + else + { + normal = 1.0 / (1.0 + (AMGain * KoverQ) + Ksquared); + m_b0 = (1.0 + KoverQ + Ksquared) * normal; + m_b1 = 2.0 * (Ksquared - 1.0) * normal; + m_b2 = (1.0 - KoverQ + Ksquared) * normal; + m_a1 = 1.0 * m_b1; + m_a2 = (1.0 - (AMGain * KoverQ) + Ksquared) * normal; + } + break; + case biquad_type::LOWSHELF: + if (DBGain >= 0.0) + { + normal = 1.0 / (1.0 + M_SQRT2 * K + Ksquared); + m_b0 = (1.0 + sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal; + m_b1 = 2.0 * (AMGain * Ksquared - 1.0) * normal; + m_b2 = (1.0 - sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal; + m_a1 = 2.0 * (Ksquared - 1.0) * normal; + m_a2 = (1.0 - M_SQRT2 * K + Ksquared) * normal; + } + else + { + normal = 1.0 / (1.0 + sqrt(2.0 * AMGain) * K + AMGain * Ksquared); + m_b0 = (1.0 + M_SQRT2 * K + Ksquared) * normal; + m_b1 = 2.0 * (Ksquared - 1.0) * normal; + m_b2 = (1.0 - M_SQRT2 * K + Ksquared) * normal; + m_a1 = 2.0 * (AMGain * Ksquared - 1.0) * normal; + m_a2 = (1.0 - sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal; + } + break; + case biquad_type::HIGHSHELF: + if (DBGain >= 0.0) + { + normal = 1.0 / (1.0 + M_SQRT2 * K + Ksquared); + m_b0 = (AMGain + sqrt(2.0 * AMGain) * K + Ksquared) * normal; + m_b1 = 2.0 * (Ksquared - AMGain) * normal; + m_b2 = (AMGain - sqrt(2.0 * AMGain) * K + Ksquared) * normal; + m_a1 = 2.0 * (Ksquared - 1.0) * normal; + m_a2 = (1.0 - M_SQRT2 * K + Ksquared) * normal; + } + else + { + normal = 1.0 / (AMGain + sqrt(2.0 * AMGain) * K + Ksquared); + m_b0 = (1.0 + M_SQRT2 * K + Ksquared) * normal; + m_b1 = 2.0 * (Ksquared - 1.0) * normal; + m_b2 = (1.0 - M_SQRT2 * K + Ksquared) * normal; + m_a1 = 2.0 * (Ksquared - AMGain) * normal; + m_a2 = (AMGain - sqrt(2.0 * AMGain) * K + Ksquared) * normal; + } + break; + default: + fatalerror("filter_biquad_device::recalc() - Invalid filter type!"); + break; + } +#ifdef FLT_BIQUAD_DEBUG + logerror("Calculated Parameters:\n"); + logerror( "Gain (dB): %f, (raw): %f\n", DBGain, MGain); + logerror( "k: %f\n", K); + logerror( "normal: %f\n", normal); + logerror("b0: %f\n", m_b0); + logerror("b1: %f\n", m_b1); + logerror("b2: %f\n", m_b2); + logerror("a1: %f\n", m_a1); + logerror("a2: %f\n", m_a2); +#endif + // peak and shelf filters do not use gain for the entire signal, only for the peak/shelf portions + // side note: the first order lowpass and highpass filter analogues technically don't have gain either, + // but this can be 'faked' by adjusting the bx factors, so we support that anyway, even if it isn't realistic. + if ( (m_type != biquad_type::PEAK) + && (m_type != biquad_type::LOWSHELF) + && (m_type != biquad_type::HIGHSHELF) ) + { + m_b0 *= m_gain; + m_b1 *= m_gain; + m_b2 *= m_gain; +#ifdef FLT_BIQUAD_DEBUG + logerror("b0g: %f\n", m_b0); + logerror("b1g: %f\n", m_b1); + logerror("b2g: %f\n", m_b2); +#endif + } +} + +/* Step the filter */ +void filter_biquad_device::step() +{ + m_w2 = m_w1; + m_w1 = m_w0; + m_w0 = (-m_a1 * m_w1) + (-m_a2 * m_w2) + m_input; + m_output = (m_b0 * m_w0) + (m_b1 * m_w1) + (m_b2 * m_w2); +} diff --git a/src/devices/sound/flt_biquad.h b/src/devices/sound/flt_biquad.h new file mode 100644 index 00000000000..17050c75017 --- /dev/null +++ b/src/devices/sound/flt_biquad.h @@ -0,0 +1,80 @@ +// license:BSD-3-Clause +// copyright-holders:K.Wilkins,Couriersud,Derrick Renaud,Frank Palazzolo,Jonathan Gevaryahu +#ifndef MAME_SOUND_FLT_BIQUAD_H +#define MAME_SOUND_FLT_BIQUAD_H + +#pragma once + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> filter_biquad_device + +class filter_biquad_device : public device_t, public device_sound_interface +{ +public: + enum class biquad_type : int + { + LOWPASS1P = 0, + HIGHPASS1P, + LOWPASS, + HIGHPASS, + BANDPASS, + NOTCH, + PEAK, + LOWSHELF, + HIGHSHELF + }; + + filter_biquad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + // set up the filter with the specified filter parameters and return a pointer to the new device + filter_biquad_device& setup(biquad_type type, double fc, double q, double gain); + // update an existing instance with new filter parameters + void update_params(biquad_type type, double fc, double q, double gain); + + // helper setup functions to create common filters representable by biquad filters + // Sallen-Key low-pass + filter_biquad_device& opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2); + // TODO when needed: Sallen-Key band-pass + // TODO when needed: Sallen-Key high-pass + + // Multiple-Feedback low-pass + filter_biquad_device& opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2); + + // Multiple-Feedback band-pass + filter_biquad_device& opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2); + + // Multiple-Feedback high-pass + filter_biquad_device& opamp_mfb_highpass_setup(double r1, double r2, double c1, double c2, double c3); + + +protected: + // device-level overrides + virtual void device_start() override; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +private: + void recalc(); + void step(); + + sound_stream* m_stream; + biquad_type m_type; + int m_last_sample_rate; + double m_fc; + double m_q; + double m_gain; + + stream_buffer::sample_t m_input; + double m_w0, m_w1, m_w2; /* w[k], w[k-1], w[k-2], current and previous intermediate values */ + stream_buffer::sample_t m_output; + double m_a1, m_a2; /* digital filter coefficients, denominator */ + double m_b0, m_b1, m_b2; /* digital filter coefficients, numerator */ +}; + +DECLARE_DEVICE_TYPE(FILTER_BIQUAD, filter_biquad_device) + +#endif // MAME_SOUND_FLT_BIQUAD_H diff --git a/src/devices/sound/hc55516.cpp b/src/devices/sound/hc55516.cpp index 5414f378802..2d96ad2cb3b 100644 --- a/src/devices/sound/hc55516.cpp +++ b/src/devices/sound/hc55516.cpp @@ -1,8 +1,18 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Jonathan Gevaryahu +// thanks-to:Zonn Moore /***************************************************************************** - Harris HC-55516 (and related) emulator + Continuously Variable Slope Demodulator standalone chip emulator: + Harris HC-55516 (sometimes labeled HCI-55516 or HC1-55516) + Harris HC-55532 (sometimes labeled HCI-55532 or HC1-55532) [preliminary] + Motorola MC-3417/MC-34115 + Motorola MC-3418 + TODO: research HC-55536 and HC-55564 differences vs HC-55516 (better auto-zeroing, and removal of the encoder offset compensation DAC?) + + Driver TODOs: + /src/mame/audio/exidy440.cpp has its own internal implementation of the MC3417 and MC3418, it should be using this file instead + *****************************************************************************/ @@ -10,7 +20,7 @@ #include "hc55516.h" -/* 4x oversampling */ +/* fixed samplerate of 192khz */ #define SAMPLE_RATE (48000 * 4) #define INTEGRATOR_LEAK_TC 0.001 @@ -21,33 +31,23 @@ #define SAMPLE_GAIN (10000.0 / 32768.0) - - -DEFINE_DEVICE_TYPE(HC55516, hc55516_device, "hc55516", "HC-55516") - -hc55516_device::hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : hc55516_device(mconfig, HC55516, tag, owner, clock) -{ -} - -hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock), - device_sound_interface(mconfig, *this), - m_channel(nullptr), - m_active_clock_hi(0), - m_shiftreg_mask(0), - m_last_clock_state(0), - m_digit(0), - m_new_digit(0), - m_shiftreg(0), - m_curr_sample(0), - m_next_sample(0), - m_update_count(0), - m_filter(0), - m_integrator(0), - m_charge(0), - m_decay(0), - m_leak(0) +//##################################### +// COMMON +//##################################### +cvsd_device_base::cvsd_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool active_clock_edge, uint8_t shiftreg_mask) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_clock_state_push_cb(*this) + , m_digin_pull_cb(*this) + , m_digout_push_cb(*this) + , m_active_clock_edge(active_clock_edge) + , m_shiftreg_mask(shiftreg_mask) + , m_last_clock_state(false) + , m_buffered_bit(false) + , m_shiftreg(0) + , m_curr_sample(0) + , m_next_sample(0) + , m_samples_generated(0) { } @@ -55,9 +55,199 @@ hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, // device_start - device-specific startup //------------------------------------------------- +void cvsd_device_base::device_start() +{ + /* create the stream */ + m_stream = stream_alloc(0, 1, SAMPLE_RATE); + + save_item(NAME(m_last_clock_state)); + save_item(NAME(m_buffered_bit)); + save_item(NAME(m_shiftreg)); + save_item(NAME(m_curr_sample)); + save_item(NAME(m_next_sample)); + save_item(NAME(m_samples_generated)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void cvsd_device_base::device_reset() +{ + m_last_clock_state = 0; +} + +//------------------------------------------------- +// device_clock_changed - device-specific samplerate change +//------------------------------------------------- +/*void cvsd_device_base::device_clock_changed() +{ + // do nothing. + //m_stream->set_sample_rate(clock()); +}*/ + +READ_LINE_MEMBER( cvsd_device_base::clock_r ) +{ + // prevent debugger from changing the internal state + if (!machine().side_effects_disabled()) + m_stream->update(); /* bring up to date first */ + return clock_state_r(); +} + +WRITE_LINE_MEMBER( cvsd_device_base::mclock_w ) +{ + clock_w(state); +} + +WRITE_LINE_MEMBER( cvsd_device_base::digin_w ) +{ + digit_w(state); +} + +// the following encode related functions don't do anything yet, don't call them. +/*void cvsd_device_base::audio_in_w(int16_t data) +{ + assert(0); +}*/ + +WRITE_LINE_MEMBER( cvsd_device_base::dec_encq_w ) +{ + assert(0); +} + +READ_LINE_MEMBER( cvsd_device_base::digout_r ) +{ + return 0; +} + +// default and stub implementations + +inline bool cvsd_device_base::is_external_oscillator() +{ + return clock() != 0; +} + +inline bool cvsd_device_base::is_clock_changed(bool clock_state) +{ + return ((!m_last_clock_state && clock_state) || + (m_last_clock_state && !clock_state)); +} + +inline bool cvsd_device_base::is_active_clock_transition(bool clock_state) +{ + return ((clock_state != m_last_clock_state) && + (clock_state == m_active_clock_edge)); +} + +inline bool cvsd_device_base::current_clock_state() +{ + // keep track of the clock state given its previous state and the number of samples produced + // i.e. if we generated m_samples_generated samples, at a sample rate of SAMPLE_RATE, then are we on a positive or negative level of a squarewave at clock() hz? SAMPLE_RATE may not be an integer multiple of clock() + //uint64_t fractions_of_second = (((uint64_t)m_samples_generated)<<32) / SAMPLE_RATE; // 32.32 bits of seconds passed so far + //uint32_t clock_edges_passed = (fractions_of_second * clock() * 2)>>32 + //return (((((uint64_t)m_samples_generated<<32) * clock() * 2 / SAMPLE_RATE)>>32) & 0x1)?true:false; + return (((uint64_t)m_samples_generated * clock() * 2 / SAMPLE_RATE) & 0x01)?true:false; +} + +void cvsd_device_base::digit_w(int digit) +{ + m_stream->update(); + m_buffered_bit = digit ? true : false; +} + +void cvsd_device_base::clock_w(int state) +{ + /* update the output buffer first */ + m_stream->update(); + bool clock_state = state ? true : false; + + /* only makes sense for setups with a software driven clock */ + assert(!is_external_oscillator()); + + /* speech clock changing? */ + if (is_clock_changed(clock_state)) + { + /* clear the update count */ + m_samples_generated = 0; + process_bit(m_buffered_bit, clock_state); + } + + /* update the clock */ + m_last_clock_state = clock_state; +} + +int cvsd_device_base::clock_state_r() +{ + /* only makes sense for setups with an external oscillator */ + assert(is_external_oscillator()); + + m_stream->update(); + + return current_clock_state(); +} + +void cvsd_device_base::process_bit(bool bit, bool clock_state) +{ + // stub +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void cvsd_device_base::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // Stub, just return silence + auto &buffer = outputs[0]; + + m_samples_generated += buffer.samples(); + if (m_samples_generated >= SAMPLE_RATE) + m_samples_generated -= SAMPLE_RATE; + buffer.fill(0); +} + + +//######################################### +// HC55516 +//######################################### +DEFINE_DEVICE_TYPE(HC55516, hc55516_device, "hc55516", "HC-55516") + +hc55516_device::hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : hc55516_device(mconfig, HC55516, tag, owner, clock, 0xfc0, 6, 0xfc1, 4) +{ +} + +// overridable type for hc55532 etc +hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sylmask, int32_t sylshift, int32_t syladd, int32_t intshift) + : cvsd_device_base(mconfig, type, tag, owner, clock, RISING, 0x7) + , m_agc_push_cb(*this) + , m_fzq_pull_cb(*this) + , m_sylmask(sylmask) + , m_sylshift(sylshift) + , m_syladd(syladd) + , m_intshift(intshift) + , m_sylfilter(0) + , m_intfilter(0) + , m_agc(true) + , m_buffered_fzq(true) +{ +} + +//------------------------------------------------- +// device_start - device-specific start +//------------------------------------------------- + void hc55516_device::device_start() { - start_common(0x07, true); + cvsd_device_base::device_start(); + save_item(NAME(m_sylfilter)); + save_item(NAME(m_intfilter)); + save_item(NAME(m_agc)); + save_item(NAME(m_buffered_fzq)); + + /* resolve lines */ + m_agc_push_cb.resolve(); + m_fzq_pull_cb.resolve(); } //------------------------------------------------- @@ -66,13 +256,200 @@ void hc55516_device::device_start() void hc55516_device::device_reset() { - m_last_clock_state = 0; + cvsd_device_base::device_reset(); + // simulate /FZ having been held for a while + m_sylfilter = 0x3f; + m_intfilter = 0; + m_agc = true; + m_buffered_fzq = true; // assuming /FZ was just released and is now high/inactive } +// device specific functions + +WRITE_LINE_MEMBER( hc55516_device::fzq_w ) +{ + m_buffered_fzq = state; +} + +READ_LINE_MEMBER( hc55516_device::agc_r ) +{ + // prevent debugger from changing the internal state + if (!machine().side_effects_disabled()) + m_stream->update(); /* bring up to date first */ + return m_agc; +} + +void hc55516_device::process_bit(bool bit, bool clock_state) +{ + bool frozen = ( ( (m_intfilter >= 0x180) && (!bit) ) || + ( (m_intfilter <= -0x180) && (bit) ) ); + + int32_t sum; + if (is_active_clock_transition(clock_state)) + { + // grab the /FZ state; if the callback is present, use that, otherwise use the buffered state + bool fzq_state = false; + if (!m_fzq_pull_cb.isnull()) + fzq_state = m_fzq_pull_cb(); + else + fzq_state = m_buffered_fzq; + + if (!fzq_state) // /FZ is active low, if it is active, the input bit is ignored and the inverse of the previous bit in the shifter is used instead + bit = !(m_shiftreg&1); + + /* shift the new bit into the shift register */ + m_shiftreg = (m_shiftreg << 1) | (bit?1:0); + + /* if we got all 0's or all 1's in the last n bits... */ + if (((m_shiftreg & m_shiftreg_mask) == 0) || + ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask)) + { + // coincidence is true + if (!frozen) m_sylfilter += (((~m_sylfilter) & m_sylmask) >> m_sylshift); + } + else + { + // coincidence is false + if (!frozen) m_sylfilter += (((~m_sylfilter) & m_sylmask) >> m_sylshift) + m_syladd; + } + m_sylfilter &= 0xfff; + + sum = ( ((~m_intfilter) >> m_intshift) + 1 ) & 0x3ff; + } + else // inactive clock transition + { + if (m_shiftreg&1) + { + sum = ( ( ~std::max(2, m_sylfilter >> 6) ) + 1 ) & 0x3ff; + } + else + { + sum = std::max(2, m_sylfilter >> 6) & 0x3ff; + } + } + + if (sum & 0x200) + sum |= ~0x3ff; // sign extend + + if (!frozen) + { + m_intfilter += sum; + m_intfilter &= 0x3ff; + if (m_intfilter & 0x200) m_intfilter |= ~0x3ff; // sign extend + } + + /* scale the result (-512 to 511) to -32768 thru 32767 */ + /* + F E D C B A 9 8 7 6 5 4 3 2 1 0 + 9 8 7 6 5 4 3 2 1 0/9 8 7 6 5 4 + */ + m_next_sample = ( (m_intfilter << 6) | ( ((m_intfilter & 0x3ff) ^ 0x200 ) >> 4 ) ); + + // update agc state + if ( (m_intfilter >= 0x100) || (m_intfilter <= -0x100) ) + m_agc = false; + else + m_agc = true; + + // push agc state if a callback is present + if (!m_agc_push_cb.isnull()) + m_agc_push_cb(m_agc); +} + +//------------------------------------------------- +// sound_stream_update_legacy - handle a stream update +//------------------------------------------------- + +void hc55516_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + auto &buffer = outputs[0]; + +/* + if (!is_external_oscillator()) + { + // track how many samples we've updated without a clock; if it's been more than 1/32 of a second, output silence + m_samples_generated += buffer.samples(); + if (m_samples_generated > SAMPLE_RATE / 32) + { + m_samples_generated = SAMPLE_RATE; + m_next_sample = 0; + } + } +*/ + + if (is_external_oscillator()) + { + /* external oscillator */ + for (int i = 0; i < buffer.samples(); i++) + { + buffer.put_int(i, m_next_sample, 32768); + + m_samples_generated++; + + uint8_t clock_state = current_clock_state(); + + /* pull in next digit on the appropriate edge of the clock */ + if (is_clock_changed(clock_state)) + { + process_bit(m_buffered_bit, clock_state); + } + + m_last_clock_state = clock_state; + } + } + + /* software driven clock */ + else + for (int i = 0; i < buffer.samples(); i++) + buffer.put_int(i, m_next_sample, 32768); +} + + + +//######################################### +// HC55532 +//######################################### +DEFINE_DEVICE_TYPE(HC55532, hc55532_device, "hc55532", "HC-55532") + +hc55532_device::hc55532_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : hc55516_device(mconfig, HC55532, tag, owner, clock, 0xf80, 7, 0xfe1, 5) +{ +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void hc55532_device::device_reset() +{ + cvsd_device_base::device_reset(); + // simulate /FZ having been held for a while + m_sylfilter = 0x7f; + m_intfilter = 0; + m_agc = true; + m_buffered_fzq = true; // assuming /FZ was just released and is now high/inactive +} + + + +//########################################## +// MC3417 +//########################################## DEFINE_DEVICE_TYPE(MC3417, mc3417_device, "mc3417", "MC3417") mc3417_device::mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : hc55516_device(mconfig, MC3417, tag, owner, clock) + : mc3417_device(mconfig, MC3417, tag, owner, clock, 0x7) +{ +} + +// overridable type for mc3418 etc +mc3417_device::mc3417_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t shiftreg_mask) + : cvsd_device_base(mconfig, type, tag, owner, clock, FALLING, shiftreg_mask) + , m_charge(pow(exp(-1.0), 1.0 / (FILTER_CHARGE_TC * 16000.0))) + , m_decay(pow(exp(-1.0), 1.0 / (FILTER_DECAY_TC * 16000.0))) + , m_leak(pow(exp(-1.0), 1.0 / (INTEGRATOR_LEAK_TC * 16000.0))) + , m_sylfilter_d(0.0) + , m_intfilter_d(0.0) { } @@ -82,181 +459,62 @@ mc3417_device::mc3417_device(const machine_config &mconfig, const char *tag, dev void mc3417_device::device_start() { - start_common(0x07, false); + cvsd_device_base::device_start(); + save_item(NAME(m_sylfilter_d)); + save_item(NAME(m_intfilter_d)); } - -DEFINE_DEVICE_TYPE(MC3418, mc3418_device, "mc3418", "MC3418") - -mc3418_device::mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : hc55516_device(mconfig, MC3418, tag, owner, clock) +void mc3417_device::process_bit(bool bit, bool clock_state) { -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void mc3418_device::device_start() -{ - start_common(0x0f, false); -} - - -void hc55516_device::start_common(uint8_t _shiftreg_mask, int _active_clock_hi) -{ - /* compute the fixed charge, decay, and leak time constants */ - m_charge = pow(exp(-1.0), 1.0 / (FILTER_CHARGE_TC * 16000.0)); - m_decay = pow(exp(-1.0), 1.0 / (FILTER_DECAY_TC * 16000.0)); - m_leak = pow(exp(-1.0), 1.0 / (INTEGRATOR_LEAK_TC * 16000.0)); - - m_shiftreg_mask = _shiftreg_mask; - m_active_clock_hi = _active_clock_hi; - m_last_clock_state = 0; - - /* create the stream */ - m_channel = stream_alloc(0, 1, SAMPLE_RATE); - - save_item(NAME(m_last_clock_state)); - save_item(NAME(m_digit)); - save_item(NAME(m_new_digit)); - save_item(NAME(m_shiftreg)); - save_item(NAME(m_curr_sample)); - save_item(NAME(m_next_sample)); - save_item(NAME(m_update_count)); - save_item(NAME(m_filter)); - save_item(NAME(m_integrator)); -} - -inline int hc55516_device::is_external_oscillator() -{ - return clock() != 0; -} - - -inline int hc55516_device::is_active_clock_transition(int clock_state) -{ - return (( m_active_clock_hi && !m_last_clock_state && clock_state) || - (!m_active_clock_hi && m_last_clock_state && !clock_state)); -} - - -inline int hc55516_device::current_clock_state() -{ - return ((uint64_t)m_update_count * clock() * 2 / SAMPLE_RATE) & 0x01; -} - - -void hc55516_device::process_digit() -{ - double integrator = m_integrator, temp; - - /* shift the bit into the shift register */ - m_shiftreg = (m_shiftreg << 1) | m_digit; - - /* move the estimator up or down a step based on the bit */ - if (m_digit) - integrator += m_filter; - else - integrator -= m_filter; - - /* simulate leakage */ - integrator *= m_leak; - - /* if we got all 0's or all 1's in the last n bits, bump the step up */ - if (((m_shiftreg & m_shiftreg_mask) == 0) || - ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask)) - { - m_filter = FILTER_MAX - ((FILTER_MAX - m_filter) * m_charge); - - if (m_filter > FILTER_MAX) - m_filter = FILTER_MAX; - } - - /* simulate decay */ - else - { - m_filter *= m_decay; - - if (m_filter < FILTER_MIN) - m_filter = FILTER_MIN; - } - - /* compute the sample as a 32-bit word */ - temp = integrator * SAMPLE_GAIN; - m_integrator = integrator; - - m_next_sample = temp; - /* compress the sample range to fit better in a 16-bit word */ -/* if (temp < 0) - m_next_sample = (int)(temp / (-temp * (1.0 / 32768.0) + 1.0)); - else - m_next_sample = (int)(temp / (temp * (1.0 / 32768.0) + 1.0));*/ -} - -void hc55516_device::clock_w(int state) -{ - uint8_t clock_state = state ? true : false; - - /* only makes sense for setups with a software driven clock */ - assert(!is_external_oscillator()); - - /* speech clock changing? */ if (is_active_clock_transition(clock_state)) { - /* update the output buffer before changing the registers */ - m_channel->update(); - /* clear the update count */ - m_update_count = 0; + /* shift the new bit into the shift register */ + m_shiftreg = (m_shiftreg << 1) | (bit?1:0); - process_digit(); + /* move the estimator up or down a step based on the bit */ + if (!bit) + m_intfilter_d += m_sylfilter_d; + else + m_intfilter_d -= m_sylfilter_d; + + /* simulate leakage */ + m_intfilter_d *= m_leak; + + /* if we got all 0's or all 1's in the last n bits, bump the step up */ + if (((m_shiftreg & m_shiftreg_mask) == 0) || + ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask)) + { + // coincidence is true + m_sylfilter_d = FILTER_MAX - ((FILTER_MAX - m_sylfilter_d) * m_charge); + + if (m_sylfilter_d > FILTER_MAX) + m_sylfilter_d = FILTER_MAX; + } + else + { + m_sylfilter_d *= m_decay; + + if (m_sylfilter_d < FILTER_MIN) + m_sylfilter_d = FILTER_MIN; + } + + /* compute the sample as a 32-bit word */ + m_next_sample = m_intfilter_d * SAMPLE_GAIN; } - - /* update the clock */ - m_last_clock_state = clock_state; } - -void hc55516_device::digit_w(int digit) -{ - if (is_external_oscillator()) - { - m_channel->update(); - m_new_digit = digit & 1; - } - else - m_digit = digit & 1; -} - - -int hc55516_device::clock_state_r() -{ - /* only makes sense for setups with an external oscillator */ - assert(is_external_oscillator()); - - m_channel->update(); - - return current_clock_state(); -} - - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void hc55516_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +void mc3417_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) { auto &buffer = outputs[0]; - int i; if (!is_external_oscillator()) { - /* track how many samples we've updated without a clock */ - m_update_count += buffer.samples(); - if (m_update_count > SAMPLE_RATE / 32) + /* track how many samples we've updated without a clock; if it's been more than 1/32 of a second, output silence */ + m_samples_generated += buffer.samples(); + if (m_samples_generated > SAMPLE_RATE / 32) { - m_update_count = SAMPLE_RATE; + m_samples_generated = SAMPLE_RATE; m_next_sample = 0; } } @@ -269,22 +527,18 @@ void hc55516_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - hc55516_device::sound_stream_update(stream, inputs, outputs); -} -void mc3418_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) + +//########################################## +// MC3418 +//########################################## +DEFINE_DEVICE_TYPE(MC3418, mc3418_device, "mc3418", "MC3418") + +mc3418_device::mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : mc3417_device(mconfig, MC3418, tag, owner, clock, 0xf) { - hc55516_device::sound_stream_update(stream, inputs, outputs); } diff --git a/src/devices/sound/hc55516.h b/src/devices/sound/hc55516.h index 0948ce025a4..48c1b768a4f 100644 --- a/src/devices/sound/hc55516.h +++ b/src/devices/sound/hc55516.h @@ -1,26 +1,92 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Jonathan Gevaryahu +// thanks-to:Zonn Moore #ifndef MAME_SOUND_HC55516_H #define MAME_SOUND_HC55516_H #pragma once -class hc55516_device : public device_t, public device_sound_interface +class cvsd_device_base : public device_t, public device_sound_interface +{ +public: + enum + { + RISING=true, + FALLING=false + }; + + auto clock_state_cb() { return m_clock_state_push_cb.bind(); } // A clock state change callback. Using this is a bad idea due to lack of synchronization to other devices. TODO: remove this. + auto digin_cb() { return m_digin_pull_cb.bind(); } // Digital in pull callback function, for use if a clock is specified and we need to pull in the digital in pin state, otherwise unused. TODO: this is not hooked up yet, and should be. + auto digout_cb() { return m_digout_push_cb.bind(); } // Digital out push callback function. TODO: this is not hooked up or implemented yet, although it is only really relevant for devices which use the CVSD chips in encode mode. + + READ_LINE_MEMBER( clock_r ); // Clock pull, really only relevant of something manually polls the clock (and clock is specified), which is a very bad design pattern and will cause synchronization/missed clock transition issues. This function WILL ASSERT if it is called and the clock hz is NOT specified! TODO: remove all use of this, and remove it. + WRITE_LINE_MEMBER( mclock_w ); // Clock push; this function WILL ASSERT if it is called and the clock hz IS specified! + WRITE_LINE_MEMBER( digin_w ); // Digital in push to the pin, as a pseudo 'buffer' implemented within the cvsd device itself. This is not technically accurate to hardware, and in the future should be deprecated in favor of digin_cb once the latter is implemented. + WRITE_LINE_MEMBER( dec_encq_w ); //DEC/ENC decode/encode select push. This is not implemented yet, and relies on an input audio stream. TODO: implement this beyond a do-nothing stub + READ_LINE_MEMBER( digout_r ); // Digital out pull. TODO: this is not hooked up or implemented yet, although it is only really relevant for devices which use the CVSD chips in encode mode. + //void audio_in_w(stream_buffer::sample_t data); // Audio In pin, an analog value of the audio waveform being pushed to the chip. TODO: this is not hooked up or implemented yet, and this should really be handled as an input stream from a separate DAC device, not a value push function at all. + void digit_w(int digit); /* sets the buffered digit (0 or 1), common to all chips. TODO: replace all use of this with digin_cb once implemented */ + void clock_w(int state); /* sets the clock state (0 or 1, clocked on the rising edge), common to all chips */ + virtual int clock_state_r(); /* returns whether the clock is currently LO or HI, common to all chips. TODO: get rid of all use of this, then get rid of it. */ + +protected: + cvsd_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool active_clock_edge, uint8_t shiftreg_mask); + + // device-level overrides + virtual void device_start() override; + virtual void device_reset() 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; + + // callbacks + devcb_write_line m_clock_state_push_cb; ///TODO: get rid of this, if you use it you should feel bad + devcb_read_line m_digin_pull_cb; + devcb_write_line m_digout_push_cb; + + // const state defined by constructor + const bool m_active_clock_edge; + const uint8_t m_shiftreg_mask; // it may be desirable to allow this to be changed by the user under some circumstances + + // internal state + sound_stream *m_stream; + bool m_last_clock_state; + bool m_buffered_bit; + uint8_t m_shiftreg; + stream_buffer::sample_t m_curr_sample; + stream_buffer::sample_t m_next_sample; + uint32_t m_samples_generated; + + // specific internal handler overrides, overridden by each chip + virtual void process_bit(bool bit, bool clock_state); + + ///TODO: get rid of these + inline bool is_external_oscillator(); + inline bool is_clock_changed(bool clock_state); + inline bool is_active_clock_transition(bool clock_state); + inline bool current_clock_state(); + +}; + + +class hc55516_device : public cvsd_device_base { public: hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - /* sets the digit (0 or 1) */ - void digit_w(int digit); + auto fzq_cb() { return m_fzq_pull_cb.bind(); } // /FZ (partial reset) pull callback, ok to leave unconnected (we assume it is pulled high) + auto agc_cb() { return m_agc_push_cb.bind(); } // AGC callback function, called to push the state if the AGC pin changes, ok to leave unconnected - /* sets the clock state (0 or 1, clocked on the rising edge) */ - void clock_w(int state); - - /* returns whether the clock is currently LO or HI */ - int clock_state_r(); + WRITE_LINE_MEMBER( fzq_w ); // /FZ (partial reset) push + READ_LINE_MEMBER( agc_r ); // AGC pull + /* TODO: These are only relevant for encode mode, which isn't done yet! */ + //WRITE_LINE_MEMBER( aptq_w ); // /APT (silence encoder output) push + //WRITE_LINE_MEMBER( dec_encq_w ); // DEC/ENC decode/encode select push protected: - hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + // overridable type for subclass + hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sylmask, int32_t sylshift, int32_t syladd, int32_t intshift); // device-level overrides virtual void device_start() override; @@ -29,67 +95,82 @@ protected: // sound stream update overrides virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - void start_common(uint8_t _shiftreg_mask, int _active_clock_hi); + // callbacks + devcb_write_line m_agc_push_cb; + devcb_read_line m_fzq_pull_cb; + + // const coefficients defined by constructor + const uint32_t m_sylmask; + const int32_t m_sylshift; + const int32_t m_syladd; + const int32_t m_intshift; // internal state - sound_stream *m_channel; - int m_active_clock_hi; - uint8_t m_shiftreg_mask; + int32_t m_sylfilter; + int32_t m_intfilter; + bool m_agc; + bool m_buffered_fzq; - uint8_t m_last_clock_state; - uint8_t m_digit; - uint8_t m_new_digit; - uint8_t m_shiftreg; - - stream_buffer::sample_t m_curr_sample; - stream_buffer::sample_t m_next_sample; - - uint32_t m_update_count; - - double m_filter; - double m_integrator; - - double m_charge; - double m_decay; - double m_leak; - - inline int is_external_oscillator(); - inline int is_active_clock_transition(int clock_state); - inline int current_clock_state(); - void process_digit(); + // internal handlers + virtual void process_bit(bool bit, bool clock_state) override; }; -class mc3417_device : public hc55516_device +class hc55532_device : public hc55516_device +{ +public: + hc55532_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +protected: + // device-level overrides + virtual void device_reset() override; +}; + + +class mc3417_device : public cvsd_device_base { public: mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + // override for clock_w + //virtual void clock_w(int state) override; + protected: + // overridable type for subclass + mc3417_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t shiftreg_mask); + // device-level overrides virtual void device_start() override; // sound stream update overrides virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + + // const coefficients defined by constructor; should these be adjustable by the user or externally defined, as they are implemented using a set of two small lowpass filters outside the chip? + const double m_charge; + const double m_decay; + const double m_leak; + + // internal state + double m_sylfilter_d; + double m_intfilter_d; + + // internal handlers + virtual void process_bit(bool bit, bool clock_state) override; }; -class mc3418_device : public hc55516_device +class mc3418_device : public mc3417_device { public: mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - -protected: - // device-level overrides - virtual void device_start() override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; }; DECLARE_DEVICE_TYPE(HC55516, hc55516_device) +DECLARE_DEVICE_TYPE(HC55532, hc55532_device) +//DECLARE_DEVICE_TYPE(HC55536, hc55536_device) +//DECLARE_DEVICE_TYPE(HC55564, hc55564_device) DECLARE_DEVICE_TYPE(MC3417, mc3417_device) +//DECLARE_DEVICE_TYPE(MC34115, mc34115_device) DECLARE_DEVICE_TYPE(MC3418, mc3418_device) #endif // MAME_SOUND_HC55516_H diff --git a/src/mame/audio/exidy.cpp b/src/mame/audio/exidy.cpp index 3f5b6559f2e..6c107bf15fe 100644 --- a/src/mame/audio/exidy.cpp +++ b/src/mame/audio/exidy.cpp @@ -196,6 +196,8 @@ exidy_sh8253_sound_device::exidy_sh8253_sound_device(const machine_config &mconf : exidy_sound_device(mconfig, type, tag, owner, clock), m_riot(*this, "riot"), m_cvsd(*this, "cvsd"), + m_cvsd_filter(*this, "cvsd_filter"), + m_cvsd_filter2(*this, "cvsd_filter2"), m_cvsdcpu(*this, "cvsdcpu"), m_tms(*this, "tms"), m_pia(*this, "pia") @@ -788,9 +790,33 @@ DEFINE_DEVICE_TYPE(EXIDY_MTRAP, mtrap_sound_device, "mtrap_sound", "Exidy SFX+PS mtrap_sound_device::mtrap_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : venture_sound_device(mconfig, EXIDY_MTRAP, tag, owner, clock) + , m_cvsd_timer(*this,"cvsd_timer") + , m_cvsd_clk(false) { } +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mtrap_sound_device::device_start() +{ + common_sh_start(); + + /* 8253 */ + m_freq_to_step = (1 << 24) / SH8253_CLOCK; + + sh8253_register_state_globals(); + + save_item(NAME(m_cvsd_clk)); +} + +TIMER_DEVICE_CALLBACK_MEMBER(mtrap_sound_device::cvsd_timer) +{ + m_cvsd_clk = !m_cvsd_clk; + m_cvsd->clock_w(m_cvsd_clk); +} + void mtrap_sound_device::voiceio_w(offs_t offset, uint8_t data) { if (!(offset & 0x10)) @@ -803,19 +829,24 @@ void mtrap_sound_device::voiceio_w(offs_t offset, uint8_t data) uint8_t mtrap_sound_device::voiceio_r(offs_t offset) { + uint8_t retval = 0xff; // this should probably be open bus if (!(offset & 0x80)) { + retval &= 0xf0; uint8_t porta = m_riot->porta_out_get(); uint8_t data = (porta & 0x06) >> 1; data |= (porta & 0x01) << 2; data |= (porta & 0x08); - return data; + retval |= data; } if (!(offset & 0x40)) - return m_cvsd->clock_state_r() << 7; + { + retval &= 0x7f; + retval |= (m_cvsd_clk << 7); + } - return 0; + return retval; } @@ -841,8 +872,15 @@ void mtrap_sound_device::device_add_mconfig(machine_config &config) m_cvsdcpu->set_addrmap(AS_PROGRAM, &mtrap_sound_device::cvsd_map); m_cvsdcpu->set_addrmap(AS_IO, &mtrap_sound_device::cvsd_iomap); + TIMER(config, m_cvsd_timer).configure_periodic(FUNC(mtrap_sound_device::cvsd_timer), attotime::from_hz(CVSD_CLOCK*2.0)); // this is a 555 timer with 53% duty cycle, within margin of error of 50% duty cycle; the handler clocks on both clock edges, hence * 2.0 + /* audio hardware */ - MC3417(config, m_cvsd, CVSD_CLOCK).add_route(ALL_OUTPUTS, "mono", 0.80); + FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(10), RES_K(3.9), RES_K(18), CAP_N(20), CAP_N(2.2)); + m_cvsd_filter2->add_route(ALL_OUTPUTS, "mono", 1.0); + FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(10), RES_K(3.9), RES_K(18), CAP_N(20), CAP_N(2.2)); + m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0); + MC3417(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 0.3086); // each filter has gain of 1.8 for total gain of 3.24, 0.3086 cancels this out. was 0.8 + } diff --git a/src/mame/audio/exidy.h b/src/mame/audio/exidy.h index c619cd1975d..8e3ee42916a 100644 --- a/src/mame/audio/exidy.h +++ b/src/mame/audio/exidy.h @@ -7,6 +7,8 @@ #include "machine/6532riot.h" #include "machine/6821pia.h" +#include "machine/timer.h" +#include "sound/flt_biquad.h" #include "sound/hc55516.h" #include "sound/tms5220.h" @@ -120,7 +122,9 @@ protected: required_device m_riot; /* 5220/CVSD variables */ - optional_device m_cvsd; + optional_device m_cvsd; + optional_device m_cvsd_filter; + optional_device m_cvsd_filter2; optional_device m_cvsdcpu; optional_device m_tms; required_device m_pia; @@ -177,11 +181,15 @@ public: protected: // device-level overrides + virtual void device_start() override; virtual void device_add_mconfig(machine_config &config) override; private: + required_device m_cvsd_timer; + TIMER_DEVICE_CALLBACK_MEMBER(cvsd_timer); void voiceio_w(offs_t offset, uint8_t data); uint8_t voiceio_r(offs_t offset); + bool m_cvsd_clk; void cvsd_map(address_map &map); void cvsd_iomap(address_map &map); diff --git a/src/mame/audio/s11c_bg.cpp b/src/mame/audio/s11c_bg.cpp index fef33ee26b9..33fd826e1b8 100644 --- a/src/mame/audio/s11c_bg.cpp +++ b/src/mame/audio/s11c_bg.cpp @@ -232,6 +232,8 @@ s11c_bg_device::s11c_bg_device(const machine_config &mconfig, const char *tag, d , m_dac(*this, "dac") , m_ym2151(*this, "ym2151") , m_cvsd(*this, "hc55516") + , m_cvsd_filter(*this, "cvsd_filter") + , m_cvsd_filter2(*this, "cvsd_filter2") , m_pia40(*this, "pia40") , m_cpubank(*this, "bgbank") , m_cb2_cb(*this) @@ -248,6 +250,8 @@ s11c_bg_device::s11c_bg_device(const machine_config &mconfig, device_type type, , m_dac(*this, "dac") , m_ym2151(*this, "ym2151") , m_cvsd(*this, "hc55516") + , m_cvsd_filter(*this, "cvsd_filter") + , m_cvsd_filter2(*this, "cvsd_filter2") , m_pia40(*this, "pia40") , m_cpubank(*this, "bgbank") , m_cb2_cb(*this) @@ -390,7 +394,18 @@ void s11c_bg_device::s11_bg_ym(machine_config &config) // add a CVSD chip for boards which have it void s11c_bg_device::s11_bg_cvsd(machine_config &config) { - HC55516(config, m_cvsd, 0); + // m_cvsd_filter is the first 'half' of U18(MC1458), with R32, R30, R29, no capacitor to ground(!?!) and C9, output feeding the second half + // m_cvsd_filter2 is the second 'half' of U18(MC1458), with R20, R15, R19, C10 and C7, output feeding the final mixer + // Note that the (intended 1800uf according to Sinistar/System 6) capacitor + // to ground for m_cvsd_filter is COMPLETELY MISSING, and hence this + // filter section behaves very oddly under simulation, retaining a + // gain but having a first-order falloff response! + // This was presumably a design error, and not intended, given the + // strange filter response. We emulate this weird circuit as it existed. + FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200)); + FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(0), CAP_P(180)); // note the first capacitor is 0pf meaning it doesn't exist + m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0); + HC55516(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0/4.0); // to prevent massive clipping issues, we divide the signal by 4 here before going into the filters, then multiply it by 4 after it comes out the other end } @@ -405,11 +420,13 @@ void s11c_bg_device::device_add_mconfig(machine_config &config) // 1/resistance * 57990 is 4.460769, 2.8895, 2.8895, 11.62124 // the sum of the previous 4 values is 21.88101; 100/21.88101 = 4.570173 // the 4 (1/r)*rtotal numbers * 4.570173 are 20.38649, 13.25122, 13.25122 and 53.11108 respectively - // NOTE: audio passthrough from the mainboard is 4.7kohm - m_dac->add_route(ALL_OUTPUTS, *this, 0.2038); // 13Kohm - m_ym2151->add_route(1, *this, 0.1325); // 20kohm - m_ym2151->add_route(0, *this, 0.1325); // 20kohm - m_cvsd->add_route(ALL_OUTPUTS, *this, 0.5311); // 4.99kohm + // NOTE: Multiply all numbers here or the final output by 1/0.6395 = 1.5638 to get the relative + // volume values if there is no audio input used at all + // audio passthrough resistor from the mainboard input is 4.7kohm, 0.3605 in files sending audio into this device + m_dac->add_route(ALL_OUTPUTS, *this, 0.1304); // 13Kohm + m_ym2151->add_route(1, *this, 0.08473); // 20kohm + m_ym2151->add_route(0, *this, 0.08473); // 20kohm + m_cvsd_filter2->add_route(ALL_OUTPUTS, *this, 0.3396*4.0); // 4.99kohm } // D-11581 (without the W10/W11 jumpers) @@ -423,11 +440,13 @@ void s11_bg_device::device_add_mconfig(machine_config &config) // 1/resistance * 57990 is 4.9666, 3.129, 3.129, 6.2705 // the sum of the previous 4 values is 17.49521; 100/17.49521 = 5.715851 // the 4 (1/r)*rtotal numbers * 5.715851 are 28.38873, 17.8849, 17.8849, and 35.84148 respectively - // NOTE: audio passthrough from the mainboard is 2.2kohm - m_dac->add_route(ALL_OUTPUTS, *this, 0.2839); // 6.3Kohm - m_ym2151->add_route(1, *this, 0.1788); // 10kohm - m_ym2151->add_route(0, *this, 0.1788); // 10kohm - m_cvsd->add_route(ALL_OUTPUTS, *this, 0.3584); // 4.99kohm + // NOTE: Multiply all numbers here or the final output by 1/0.5516 = 1.8129 to get the relative + // volume values correct if there is no audio input used at all + // NOTE: audio passthrough from the mainboard is 2.2kohm, 0.4484 in files sending audio into this device + m_dac->add_route(ALL_OUTPUTS, *this, 0.1566); // 6.3Kohm + m_ym2151->add_route(1, *this, 0.0987); // 10kohm + m_ym2151->add_route(0, *this, 0.0987); // 10kohm + m_cvsd_filter2->add_route(ALL_OUTPUTS, *this, 0.1977*4.0); // 4.99kohm } // D-11297 or D-11298 @@ -441,11 +460,13 @@ void s11_obg_device::device_add_mconfig(machine_config &config) // 1/resistance * 40000 is 4.0, 4.0, 4.0, 4.0 // the sum of the previous 4 values is 16.0; 100/16 = 6.25 // the 4 (1/r)*rtotal numbers * 6.25 are 25.0, 25.0, 25.0, 25.0 respectively - // NOTE: audio passthrough from the mainboard is 2.2kohm - m_dac->add_route(ALL_OUTPUTS, *this, 0.25); // 10Kohm - m_ym2151->add_route(1, *this, 0.25); // 10kohm - m_ym2151->add_route(0, *this, 0.25); // 10kohm - m_cvsd->add_route(ALL_OUTPUTS, *this, 0.25); // 10kohm + // NOTE: Multiply all numbers here or the final output by 1/0.468 = 2.1368 to get the relative + // volume values correct if there is no audio input used at all + // NOTE: audio passthrough from the mainboard is 2.2kohm, 0.5319 in files sending audio into this device + m_dac->add_route(ALL_OUTPUTS, *this, 0.1170); // 10Kohm + m_ym2151->add_route(1, *this, 0.1170); // 10kohm + m_ym2151->add_route(0, *this, 0.1170); // 10kohm + m_cvsd_filter2->add_route(ALL_OUTPUTS, *this, 0.1170*4.0); // 10kohm } // D-11197 @@ -459,10 +480,12 @@ void s11_bgm_device::device_add_mconfig(machine_config &config) // 1/resistance * 40000 is 4.0, 4.0, 4.0, 4.0 // the sum of the previous 4 values is 16.0; 100/16 = 6.25 // the 4 (1/r)*rtotal numbers * 6.25 are 25.0, 25.0, 25.0, 25.0 respectively - // NOTE: audio passthrough from the mainboard is 2.2kohm - m_dac->add_route(ALL_OUTPUTS, *this, 0.25); // 10Kohm - m_ym2151->add_route(1, *this, 0.25); // 10kohm - m_ym2151->add_route(0, *this, 0.25); // 10kohm + // NOTE: Multiply all numbers here or the final output by 1/0.468 = 2.1368 to get the relative + // volume values correct if there is no audio input used at all + // NOTE: audio passthrough from the mainboard is 2.2kohm, 0.5319 in files sending audio into this device + m_dac->add_route(ALL_OUTPUTS, *this, 0.1170); // 10Kohm + m_ym2151->add_route(1, *this, 0.1170); // 10kohm + m_ym2151->add_route(0, *this, 0.1170); // 10kohm // interestingly, there is no cvsd, but a fourth 10k resistor here, but it is tied to ground. this makes the board quieter than it would otherwise be, presumably. } @@ -473,8 +496,8 @@ void s11_bgs_device::device_add_mconfig(machine_config &config) m_cpu->set_addrmap(AS_PROGRAM, &s11c_bg_device::s11c_bgs_map); // volume mixer stuff // the sum of all resistances is 10k + 10k = 20k - // NOTE: audio passthrough from the mainboard is 10k - m_dac->add_route(ALL_OUTPUTS, *this, 1.00); // 10Kohm + // NOTE: audio passthrough from the mainboard is 10k, 0.50 in files sending audio to this device + m_dac->add_route(ALL_OUTPUTS, *this, 0.50); // 10Kohm } void s11c_bg_device::device_start() diff --git a/src/mame/audio/s11c_bg.h b/src/mame/audio/s11c_bg.h index cab8d03defd..756b71c89f7 100644 --- a/src/mame/audio/s11c_bg.h +++ b/src/mame/audio/s11c_bg.h @@ -13,7 +13,9 @@ #include "cpu/m6809/m6809.h" #include "machine/6821pia.h" +#include "machine/rescap.h" #include "sound/dac.h" +#include "sound/flt_biquad.h" #include "sound/hc55516.h" #include "sound/ym2151.h" @@ -63,6 +65,8 @@ protected: required_device m_dac; optional_device m_ym2151; optional_device m_cvsd; + optional_device m_cvsd_filter; + optional_device m_cvsd_filter2; required_device m_pia40; required_memory_bank m_cpubank; diff --git a/src/mame/audio/williams.cpp b/src/mame/audio/williams.cpp index b685d102652..e20ef10f8ed 100644 --- a/src/mame/audio/williams.cpp +++ b/src/mame/audio/williams.cpp @@ -76,6 +76,7 @@ williams_cvsd_sound_device::williams_cvsd_sound_device(const machine_config &mco device_mixer_interface(mconfig, *this), m_cpu(*this, "cpu"), m_pia(*this, "pia"), + m_ym2151(*this, "ym2151"), m_hc55516(*this, "cvsd"), m_rombank(*this, "rombank"), m_talkback(0) @@ -185,12 +186,13 @@ void williams_cvsd_sound_device::device_add_mconfig(machine_config &config) PIA6821(config, m_pia, 0); m_pia->writepa_handler().set("dac", FUNC(dac_byte_interface::data_w)); m_pia->writepb_handler().set(FUNC(williams_cvsd_sound_device::talkback_w)); + m_pia->ca2_handler().set(m_ym2151, FUNC(ym2151_device::reset_w)); m_pia->irqa_handler().set_inputline(m_cpu, M6809_FIRQ_LINE); m_pia->irqb_handler().set_inputline(m_cpu, INPUT_LINE_NMI); - ym2151_device &ym(YM2151(config, "ym2151", CVSD_FM_CLOCK)); - ym.irq_handler().set(m_pia, FUNC(pia6821_device::ca1_w)).invert(); // IRQ is not true state - ym.add_route(ALL_OUTPUTS, *this, 0.10); + YM2151(config, m_ym2151, CVSD_FM_CLOCK); + m_ym2151->irq_handler().set(m_pia, FUNC(pia6821_device::ca1_w)).invert(); // IRQ is not true state + m_ym2151->add_route(ALL_OUTPUTS, *this, 0.10); MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.25); voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); diff --git a/src/mame/audio/williams.h b/src/mame/audio/williams.h index a14e1d57d06..78006b1800d 100644 --- a/src/mame/audio/williams.h +++ b/src/mame/audio/williams.h @@ -63,6 +63,7 @@ private: // devices required_device m_cpu; required_device m_pia; + required_device m_ym2151; required_device m_hc55516; required_memory_bank m_rombank; diff --git a/src/mame/drivers/s11.cpp b/src/mame/drivers/s11.cpp index d1c655aa888..e6219180c92 100644 --- a/src/mame/drivers/s11.cpp +++ b/src/mame/drivers/s11.cpp @@ -466,13 +466,23 @@ void s11_state::s11(machine_config &config) m_audiocpu->set_addrmap(AS_PROGRAM, &s11_state::s11_audio_map); INPUT_MERGER_ANY_HIGH(config, m_audioirq).output_handler().set_inputline(m_audiocpu, M6808_IRQ_LINE); - SPEAKER(config, "speaker").front_center(); - MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); + MC1408(config, m_dac, 0); voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT); - SPEAKER(config, "speech").front_center(); - HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, "speech", 0.5); + // common CVSD filter for system 11 and 11a, this is also the same filter circuit as Sinistar/System 6 uses, and is ALMOST the same filter from the s11 bg sound boards, see /mame/audio/s11c_bg.cpp + // The CVSD filter has a large gain, about 4.6x + // The filter is boosting the ~5vpp audio signal from the CVSD chip to a ~23vpp (really ~17vpp) theoretical audio signal that the s11 + // mainboard outputs on its volume control-repurposed-as-audio-out connector. + // In reality, the S11 mainboard outputs audio at a virtual ground level between +5v and -12v (so, 17VPP balanced around -7VDC), but since + // the CVSD chip's internal DAC can only output between a bit over +0x180/-0x180 out of 0x200, the most voltage it can ever output is + // between (assuming 0x1ff is 5VDC and 0x300 is 0VDC) a max of 4.375VDC and a min of 0.625VDC, i.e. 3.75VPP centered on 2.5VDC. + // In reality, the range is likely less than that. + // This means multiplying a 3.75VPP signal by 4.6 is 17.25VPP, which is almost exactly the expected 17V (12v+5v) VPP the output should have. + FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200)); + FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(1800), CAP_P(180)); + m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0); + HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0); PIA6821(config, m_pias, 0); m_pias->readpa_handler().set(FUNC(s11_state::sound_r)); @@ -489,26 +499,37 @@ void s11_state::s11_bgs(machine_config &config) { s11(config); /* Add the background sound card */ - SPEAKER(config, "bgspk").front_center(); S11_BGS(config, m_bg); + m_dac->add_route(ALL_OUTPUTS, m_bg, 0.5/2.0); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.5/2.0); m_pia34->ca2_handler().set(m_bg, FUNC(s11_bgs_device::resetq_w)); m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w)); m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w)); - m_bg->add_route(ALL_OUTPUTS, "bgspk", 0.5); + SPEAKER(config, "speaker").front_center(); + m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0); } void s11_state::s11_bgm(machine_config &config) { s11(config); /* Add the background music card */ - SPEAKER(config, "bgspk").front_center(); S11_BGM(config, m_bg); + m_dac->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0); m_pia34->ca2_handler().set(m_bg, FUNC(s11_bgm_device::resetq_w)); m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w)); m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w)); - m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0); + SPEAKER(config, "speaker").front_center(); + m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0); } +void s11_state::s11_only(machine_config &config) +{ + s11(config); + SPEAKER(config, "speaker").front_center(); + m_dac->add_route(ALL_OUTPUTS, "speaker", 0.25); + m_cvsd_filter2->add_route(ALL_OUTPUTS, "speaker", 0.25); +} /*---------------------------- / Grand Lizard 04/86 (#523) @@ -709,8 +730,8 @@ GAME( 1986, rdkng_l1, rdkng_l4, s11_bgm, s11, s11_state, init_s11, ROT0, "Willia GAME( 1986, rdkng_l2, rdkng_l4, s11_bgm, s11, s11_state, init_s11, ROT0, "Williams", "Road Kings (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) GAME( 1986, rdkng_l3, rdkng_l4, s11_bgm, s11, s11_state, init_s11, ROT0, "Williams", "Road Kings (L-3)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) -GAME( 1986, tts_l2, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND) -GAME( 1986, tts_l1, tts_l2, s11, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND) -GAME( 1987, gmine_l2, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Gold Mine (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) -GAME( 1987, tdawg_l1, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Top Dawg (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) -GAME( 1987, shfin_l1, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Shuffle Inn (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) +GAME( 1986, tts_l2, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND) +GAME( 1986, tts_l1, tts_l2, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND) +GAME( 1987, gmine_l2, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Gold Mine (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) +GAME( 1987, tdawg_l1, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Top Dawg (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) +GAME( 1987, shfin_l1, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Shuffle Inn (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING) diff --git a/src/mame/drivers/s11a.cpp b/src/mame/drivers/s11a.cpp index 8b2aea080b8..9a8b8f6a001 100644 --- a/src/mame/drivers/s11a.cpp +++ b/src/mame/drivers/s11a.cpp @@ -218,13 +218,24 @@ void s11a_state::s11a_base(machine_config &config) m_audiocpu->set_addrmap(AS_PROGRAM, &s11_state::s11_audio_map); INPUT_MERGER_ANY_HIGH(config, m_audioirq).output_handler().set_inputline(m_audiocpu, M6802_IRQ_LINE); - SPEAKER(config, "speaker").front_center(); - MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); + MC1408(config, m_dac, 0); voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT); - SPEAKER(config, "speech").front_center(); - HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, "speech", 0.50); + // common CVSD filter for system 11 and 11a, this is also the same filter circuit as Sinistar/System 6 uses, + // and is ALMOST the same filter from the s11 bg sound boards, see /mame/audio/s11c_bg.cpp + // The CVSD filter has a large gain, about 4.6x + // The filter is boosting the ~5vpp audio signal from the CVSD chip to a ~23vpp (really ~17vpp) theoretical audio signal that the s11 + // mainboard outputs on its volume control-repurposed-as-audio-out connector. + // In reality, the S11 mainboard outputs audio at a virtual ground level between +5v and -12v (so, 17VPP balanced around -7VDC), but since + // the CVSD chip's internal DAC can only output between a bit over +0x180/-0x180 out of 0x200, the most voltage it can ever output is + // between (assuming 0x1ff is 5VDC and 0x300 is 0VDC) a max of 4.375VDC and a min of 0.625VDC, i.e. 3.75VPP centered on 2.5VDC. + // In reality, the range is likely less than that. + // This means multiplying a 3.75VPP signal by 4.6 is 17.25VPP, which is almost exactly the expected 17V (12v+5v) VPP the output should have. + FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200)); + FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(1800), CAP_P(180)); + m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0); + HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0); PIA6821(config, m_pias, 0); m_pias->readpa_handler().set(FUNC(s11_state::sound_r)); @@ -241,24 +252,28 @@ void s11a_state::s11a(machine_config &config) { s11a_base(config); /* Add the background music card */ - SPEAKER(config, "bgspk").front_center(); S11_BG(config, m_bg); + m_dac->add_route(ALL_OUTPUTS, m_bg, 0.4484/2.0); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.4484/2.0); m_pia34->ca2_handler().set(m_bg, FUNC(s11_bg_device::resetq_w)); m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w)); m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w)); - m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0); + SPEAKER(config, "speaker").front_center(); + m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0); } void s11a_state::s11a_obg(machine_config &config) { s11a_base(config); /* Add the older-style background music card */ - SPEAKER(config, "bgspk").front_center(); S11_OBG(config, m_bg); + m_dac->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0); m_pia34->ca2_handler().set(m_bg, FUNC(s11_obg_device::resetq_w)); m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w)); m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w)); - m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0); + SPEAKER(config, "speaker").front_center(); + m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0); } /*------------------------ diff --git a/src/mame/drivers/s11b.cpp b/src/mame/drivers/s11b.cpp index bcc1965354d..a91622b7e22 100644 --- a/src/mame/drivers/s11b.cpp +++ b/src/mame/drivers/s11b.cpp @@ -27,7 +27,7 @@ - Bad Cats: "H" "Enter" - Banzai Run: "S" "D" "F" (won't start due to calibration? needs more investigation, try hitting E and / and lots of keys until calibration finishes); - starts music - Big Guns: "D" "F" "U" - - Black Knight 2000: "D" "F" "Y"; 'x' starts music + - Black Knight 2000: "D" "F" "Y"; 'x' starts music; 'enter' 'left' and 'right' lock the 3 balls in the upper playfield to start the multiball. hold keypad '.' to activate the lightning wheel. '=' is the after-drawbridge target to score jackpots etc - Cyclone: Nothing, game does not have switches to check for balls in the trough. - Earthshaker: "D" "F" "W" - Elvira and the Party Monsters: "D" "F" "U" @@ -300,13 +300,24 @@ void s11b_state::s11b_base(machine_config &config) m_audiocpu->set_addrmap(AS_PROGRAM, &s11_state::s11_audio_map); INPUT_MERGER_ANY_HIGH(config, m_audioirq).output_handler().set_inputline(m_audiocpu, M6802_IRQ_LINE); - SPEAKER(config, "speaker").front_center(); - MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); + MC1408(config, m_dac, 0); voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT); - SPEAKER(config, "speech").front_center(); - HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, "speech", 0.50); + // this CVSD filter differs from the one on system 11 and 11a, possibly simplified so it uses more of the same components, or so it has a different + // shape/cutoff than the filter on the bg music/speech board, on purpose. + // The CVSD filter has a large gain, about 4.6x + // The filter is boosting the ~5vpp audio signal from the CVSD chip to a ~23vpp (really ~17vpp) theoretical audio signal that the s11 + // mainboard outputs on its volume control-repurposed-as-audio-out connector. + // In reality, the S11 mainboard outputs audio at a virtual ground level between +5v and -12v (so, 17VPP balanced around -7VDC), but since + // the CVSD chip's internal DAC can only output between a bit over +0x180/-0x180 out of 0x200, the most voltage it can ever output is + // between (assuming 0x1ff is 5VDC and 0x300 is 0VDC) a max of 4.375VDC and a min of 0.625VDC, i.e. 3.75VPP centered on 2.5VDC. + // In reality, the range is likely less than that. + // This means multiplying a 3.75VPP signal by 4.6 is 17.25VPP, which is almost exactly the expected 17V (12v+5v) VPP the output should have. + FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(12), RES_K(12), RES_K(56), CAP_P(4700), CAP_P(470)); + FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(180), RES_K(180), RES_K(180), CAP_P(470), CAP_P(100)); + m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0); + HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0/4.0); // to prevent massive clipping issues, we divide the signal by 4 here before going into the filters, then multiply it by 4 after it comes out the other end PIA6821(config, m_pias, 0); m_pias->readpa_handler().set(FUNC(s11_state::sound_r)); @@ -324,11 +335,13 @@ void s11b_state::s11b(machine_config &config) s11b_base(config); /* Add the background music card */ S11_BG(config, m_bg); + m_dac->add_route(ALL_OUTPUTS, m_bg, 0.4484/2.0); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, (0.4484*4.0)/2.0); m_pia34->ca2_handler().set(m_bg, FUNC(s11_bg_device::resetq_w)); m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w)); m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w)); - SPEAKER(config, "bgspk").front_center(); - m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0); + SPEAKER(config, "speaker").front_center(); + m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0); } void s11b_state::s11b_jokerz(machine_config &config) @@ -336,6 +349,12 @@ void s11b_state::s11b_jokerz(machine_config &config) s11b_base(config); /* Add the pin sound 88 music card */ PINSND88(config, m_ps88); + // the dac and cvsd volumes should be equally mixed on the s11 board send to the audio board, whatever type it is + // the 4 gain values in the add_route statements are actually irrelevant, the ps88 device will override them + m_dac->add_route(ALL_OUTPUTS, m_ps88, 0.29, AUTO_ALLOC_INPUT, 0); + m_dac->add_route(ALL_OUTPUTS, m_ps88, 0.25, AUTO_ALLOC_INPUT, 1); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_ps88, (0.29*4.0), AUTO_ALLOC_INPUT, 0); + m_cvsd_filter2->add_route(ALL_OUTPUTS, m_ps88, (0.25*4.0), AUTO_ALLOC_INPUT, 1); m_pia34->ca2_handler().set(m_ps88, FUNC(pinsnd88_device::resetq_w)); m_ps88->syncq_cb().set(m_pia34, FUNC(pia6821_device::ca1_w)); // the sync connection comes from sound connector pin 16 to MCA1, not the usual pin 12 to MCB1 SPEAKER(config, "cabinet").front_floor(); // the cabinet speaker is aimed down underneath the pinball table itself diff --git a/src/mame/drivers/s11c.cpp b/src/mame/drivers/s11c.cpp index 860814017c7..bbe95db10c6 100644 --- a/src/mame/drivers/s11c.cpp +++ b/src/mame/drivers/s11c.cpp @@ -190,7 +190,7 @@ void s11c_state::s11c(machine_config &config) m_pia34->ca2_handler().set(m_bg, FUNC(s11c_bg_device::resetq_w)); m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w)); m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w)); - m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0); + m_bg->add_route(ALL_OUTPUTS, "speaker", 1.5638); } // Unless otherwise noted, assume S11 Background Sound Board jumpers W2/W3 are diff --git a/src/mame/drivers/williams.cpp b/src/mame/drivers/williams.cpp index 0362a9880f2..a47f2195678 100644 --- a/src/mame/drivers/williams.cpp +++ b/src/mame/drivers/williams.cpp @@ -1904,7 +1904,8 @@ void joust2_state::joust2(machine_config &config) // basic machine hardware m_maincpu->set_addrmap(AS_PROGRAM, &joust2_state::d000_map); - WILLIAMS_CVSD_SOUND(config, m_cvsd_sound).add_route(ALL_OUTPUTS, "speaker", 1.0); + S11_OBG(config, m_bg).add_route(ALL_OUTPUTS, "speaker", 2.0); // D-11298-3035 'pinbot style' older BG sound board + // Jumpers for the board: W1=? W2=open W3=present W4=open W5=open W6=open W7=present // pia m_pia[0]->readpa_handler().set_ioport("IN0").mask(0xf0); @@ -1912,12 +1913,16 @@ void joust2_state::joust2(machine_config &config) m_pia[0]->ca2_handler().set("mux", FUNC(ls157_device::select_w)); m_pia[1]->readpa_handler().set_ioport("IN2"); - m_pia[1]->writepb_handler().set(FUNC(joust2_state::snd_cmd_w)); - m_pia[1]->ca2_handler().set(FUNC(joust2_state::pia_3_cb1_w)); + m_pia[1]->writepb_handler().set(FUNC(joust2_state::snd_cmd_w)); // this goes both to the sound cpu AND to the s11 bg cpu + m_pia[1]->ca2_handler().set(FUNC(joust2_state::pia_s11_bg_strobe_w)); m_pia[1]->cb2_handler().set(m_pia[2], FUNC(pia6821_device::ca1_w)); m_pia[1]->irqa_handler().set("mainirq", FUNC(input_merger_any_high_device::in_w<0>)); m_pia[1]->irqb_handler().set("mainirq", FUNC(input_merger_any_high_device::in_w<1>)); + // these (and ca2 above) are educated guesses, as we have no schematics for joust 2's pcb which has the 20 pin system 11 bg sound connector on it; inferno, which we have schematics to, lacks this connector. All of pia[1] ca2, pia[2] cb1, and pia[2] cb2 are unconnected/grounded on inferno. + m_bg->cb2_cb().set(m_pia[2], FUNC(pia6821_device::cb1_w)); + m_pia[2]->cb2_handler().set(m_bg, FUNC(s11_obg_device::resetq_w)); // inverted? + LS157(config, m_mux, 0); m_mux->a_in_callback().set_ioport("INP1"); m_mux->b_in_callback().set_ioport("INP2"); @@ -3518,19 +3523,19 @@ ROM_START( joust2 ) ROM_LOAD( "cpu_2764_ic8_rom1_rev1.0f", 0x0E000, 0x2000, CRC(84517c3c) SHA1(de0b6473953783c091ddcc7aaa89fc1ec3b9d378) ) // sound board - ROM_REGION( 0x90000, "cvsd:cpu", 0 ) - ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x10000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) ) + ROM_REGION( 0x80000, "bg:cpu", 0 ) + ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x00000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) ) + ROM_RELOAD( 0x08000, 0x8000 ) + ROM_RELOAD( 0x10000, 0x8000 ) ROM_RELOAD( 0x18000, 0x8000 ) - ROM_RELOAD( 0x20000, 0x8000 ) + ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x20000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) ) ROM_RELOAD( 0x28000, 0x8000 ) - ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x30000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) ) + ROM_RELOAD( 0x30000, 0x8000 ) ROM_RELOAD( 0x38000, 0x8000 ) - ROM_RELOAD( 0x40000, 0x8000 ) + ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x40000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) ) ROM_RELOAD( 0x48000, 0x8000 ) - ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x50000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) ) + ROM_RELOAD( 0x50000, 0x8000 ) ROM_RELOAD( 0x58000, 0x8000 ) - ROM_RELOAD( 0x60000, 0x8000 ) - ROM_RELOAD( 0x68000, 0x8000 ) ROM_REGION( 0xc000, "gfx1", 0 ) ROM_LOAD( "vid_27128_ic57_rom20_rev1.8f", 0x00000, 0x4000, CRC(572c6b01) SHA1(651df3223c1dc42543f57a7204ae492eb15a4999) ) @@ -3574,19 +3579,19 @@ ROM_START( joust2r1 ) ROM_LOAD( "cpu_2764_ic8_rom1_rev1.0f", 0x0E000, 0x2000, CRC(84517c3c) SHA1(de0b6473953783c091ddcc7aaa89fc1ec3b9d378) ) // sound board - ROM_REGION( 0x90000, "cvsd:cpu", 0 ) - ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x10000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) ) + ROM_REGION( 0x80000, "bg:cpu", 0 ) + ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x00000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) ) + ROM_RELOAD( 0x08000, 0x8000 ) + ROM_RELOAD( 0x10000, 0x8000 ) ROM_RELOAD( 0x18000, 0x8000 ) - ROM_RELOAD( 0x20000, 0x8000 ) + ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x20000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) ) ROM_RELOAD( 0x28000, 0x8000 ) - ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x30000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) ) + ROM_RELOAD( 0x30000, 0x8000 ) ROM_RELOAD( 0x38000, 0x8000 ) - ROM_RELOAD( 0x40000, 0x8000 ) + ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x40000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) ) ROM_RELOAD( 0x48000, 0x8000 ) - ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x50000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) ) + ROM_RELOAD( 0x50000, 0x8000 ) ROM_RELOAD( 0x58000, 0x8000 ) - ROM_RELOAD( 0x60000, 0x8000 ) - ROM_RELOAD( 0x68000, 0x8000 ) ROM_REGION( 0xc000, "gfx1", 0 ) ROM_LOAD( "vid_27128_ic57_rom20_rev1.8f", 0x00000, 0x4000, CRC(572c6b01) SHA1(651df3223c1dc42543f57a7204ae492eb15a4999) ) diff --git a/src/mame/includes/s11.h b/src/mame/includes/s11.h index 4c63ff9df64..af42237c704 100644 --- a/src/mame/includes/s11.h +++ b/src/mame/includes/s11.h @@ -10,12 +10,14 @@ #define MAME_INCLUDES_S11_H #include "cpu/m6800/m6800.h" -#include "audio/s11c_bg.h" #include "audio/pinsnd88.h" +#include "audio/s11c_bg.h" #include "machine/6821pia.h" #include "machine/genpin.h" #include "machine/input_merger.h" +#include "machine/rescap.h" #include "sound/dac.h" +#include "sound/flt_biquad.h" #include "sound/hc55516.h" #include "sound/ym2151.h" @@ -45,6 +47,8 @@ public: , m_audiocpu(*this, "audiocpu") , m_audioirq(*this, "audioirq") , m_hc55516(*this, "hc55516") + , m_cvsd_filter(*this, "cvsd_filter") + , m_cvsd_filter2(*this, "cvsd_filter2") , m_dac(*this, "dac") , m_pias(*this, "pias") , m_pia21(*this, "pia21") @@ -60,6 +64,7 @@ public: { } void s11(machine_config &config); + void s11_only(machine_config &config); void s11_bgs(machine_config &config); void s11_bgm(machine_config &config); @@ -113,6 +118,8 @@ protected: optional_device m_audiocpu; optional_device m_audioirq; optional_device m_hc55516; + optional_device m_cvsd_filter; + optional_device m_cvsd_filter2; optional_device m_dac; optional_device m_pias; required_device m_pia21; diff --git a/src/mame/includes/williams.h b/src/mame/includes/williams.h index bc910e8be0e..2054be06828 100644 --- a/src/mame/includes/williams.h +++ b/src/mame/includes/williams.h @@ -11,6 +11,7 @@ #pragma once #include "audio/williams.h" +#include "audio/s11c_bg.h" #include "cpu/m6800/m6800.h" #include "cpu/m6809/m6809.h" #include "machine/6821pia.h" @@ -479,7 +480,7 @@ public: joust2_state(const machine_config &mconfig, device_type type, const char *tag) : williams_d000_rom_state(mconfig, type, tag), m_mux(*this, "mux"), - m_cvsd_sound(*this, "cvsd") + m_bg(*this, "bg") { } void joust2(machine_config &config); @@ -489,7 +490,7 @@ private: virtual void driver_init() override; required_device m_mux; - required_device m_cvsd_sound; + required_device m_bg; uint16_t m_current_sound_data; virtual TILE_GET_INFO_MEMBER(get_tile_info) override; @@ -497,7 +498,7 @@ private: TIMER_CALLBACK_MEMBER(deferred_snd_cmd_w); void snd_cmd_w(u8 data); - DECLARE_WRITE_LINE_MEMBER(pia_3_cb1_w); + DECLARE_WRITE_LINE_MEMBER(pia_s11_bg_strobe_w); }; /*----------- defined in video/williams.cpp -----------*/ diff --git a/src/mame/machine/williams.cpp b/src/mame/machine/williams.cpp index feff3b4b2bc..bd2815f4faa 100644 --- a/src/mame/machine/williams.cpp +++ b/src/mame/machine/williams.cpp @@ -508,16 +508,16 @@ TIMER_CALLBACK_MEMBER(joust2_state::deferred_snd_cmd_w) } -WRITE_LINE_MEMBER(joust2_state::pia_3_cb1_w) +WRITE_LINE_MEMBER(joust2_state::pia_s11_bg_strobe_w) { m_current_sound_data = (m_current_sound_data & ~0x100) | ((state << 8) & 0x100); - m_cvsd_sound->write(m_current_sound_data); + m_bg->ctrl_w(state); } void joust2_state::snd_cmd_w(u8 data) { m_current_sound_data = (m_current_sound_data & ~0xff) | (data & 0xff); - m_cvsd_sound->write(m_current_sound_data); + m_bg->data_w(data); machine().scheduler().synchronize(timer_expired_delegate(FUNC(joust2_state::deferred_snd_cmd_w),this), m_current_sound_data); }