mirror of
https://github.com/holub/mame
synced 2025-04-19 07:00:31 +03:00
Major update for HC55516 CVSD audio device and related filtering, as well as hooking it to drivers. (#7290)
* Split hc55516 core into separate cores/subclasses for hc55516 and hc55532 (new 'digital' implementation based on decap/die tracing) as well as mc3417 and mc3418 (old existing 'analog' implementation left alone) and hooked the hc55516 and mc3417 implementations to the appropriate hardware drivers. This should vastly improve CVSD sound quality in drivers that use the hc55516. [Lord Nightmare, Sean Riddle] * Made Exidy's mouse trap use a timer to clock the mc3417 and update the state readable by the z80 rather than relying on the mc3417 to accept a clock parameter and update the z80 state via a callback. The timer implementation ensures proper synchronization and prevents missed clock transitions from the z80 side causing noise in the CVSD audio. [Lord Nightmare] * Added a biquad-based 2nd order filter emulation for an audio stream (to go along with flt_rc 1st order filter emulation) and hooked it to Exidy's mouse trap (mc3417) and williams drivers (hc55516) following the original schematics. For hc55516 this is necessary as the original chip produces a very pronounced audible 16khz 'carrier' in its output audio even on the real chip, and these filters suppress this. This also happens to a lesser extent with the 8khz quieting waveform noise on both mc3417 and hc55516. [Lord Nightmare] * Made Williams System 11 and s11_bg relative sound mixing more accurate to the original audio flow on the PCB itself. [Lord Nightmare] * Switched Williams Joust 2 to use the s11_bg common sound device rather than its own implementation. [Lord Nightmare]
This commit is contained in:
parent
0cb876c375
commit
63dc4b41ed
@ -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",
|
||||
|
@ -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",
|
||||
|
487
src/devices/sound/flt_biquad.cpp
Normal file
487
src/devices/sound/flt_biquad.cpp
Normal file
@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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);
|
||||
}
|
80
src/devices/sound/flt_biquad.h
Normal file
80
src/devices/sound/flt_biquad.h
Normal file
@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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
|
@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
void mc3417_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &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<read_
|
||||
if (is_external_oscillator())
|
||||
{
|
||||
/* external oscillator */
|
||||
for (i = 0; i < buffer.samples(); i++, sample += slope)
|
||||
for (int i = 0; i < buffer.samples(); i++, sample += slope)
|
||||
{
|
||||
uint8_t clock_state;
|
||||
buffer.put(i, sample);
|
||||
|
||||
buffer.put(i, stream_buffer::sample_t(sample));
|
||||
m_samples_generated++;
|
||||
|
||||
m_update_count++;
|
||||
|
||||
clock_state = current_clock_state();
|
||||
uint8_t clock_state = current_clock_state();
|
||||
|
||||
/* pull in next digit on the appropriate edge of the clock */
|
||||
if (is_active_clock_transition(clock_state))
|
||||
if (is_clock_changed(clock_state))
|
||||
{
|
||||
m_digit = m_new_digit;
|
||||
|
||||
process_digit();
|
||||
process_bit(m_buffered_bit, clock_state);
|
||||
}
|
||||
|
||||
m_last_clock_state = clock_state;
|
||||
@ -293,16 +547,18 @@ void hc55516_device::sound_stream_update(sound_stream &stream, std::vector<read_
|
||||
|
||||
/* software driven clock */
|
||||
else
|
||||
for (i = 0; i < buffer.samples(); i++, sample += slope)
|
||||
buffer.put(i, stream_buffer::sample_t(sample));
|
||||
for (int i = 0; i < buffer.samples(); i++, sample += slope)
|
||||
buffer.put(i, sample);
|
||||
}
|
||||
|
||||
void mc3417_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
{
|
||||
hc55516_device::sound_stream_update(stream, inputs, outputs);
|
||||
}
|
||||
|
||||
void mc3418_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &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);
|
||||
}
|
||||
|
@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<read_stream_view> const &inputs, std::vector<write_stream_view> &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
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<riot6532_device> m_riot;
|
||||
|
||||
/* 5220/CVSD variables */
|
||||
optional_device<hc55516_device> m_cvsd;
|
||||
optional_device<mc3417_device> m_cvsd;
|
||||
optional_device<filter_biquad_device> m_cvsd_filter;
|
||||
optional_device<filter_biquad_device> m_cvsd_filter2;
|
||||
optional_device<cpu_device> m_cvsdcpu;
|
||||
optional_device<tms5220_device> m_tms;
|
||||
required_device<pia6821_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<timer_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);
|
||||
|
@ -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()
|
||||
|
@ -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<mc1408_device> m_dac;
|
||||
optional_device<ym2151_device> m_ym2151;
|
||||
optional_device<hc55516_device> m_cvsd;
|
||||
optional_device<filter_biquad_device> m_cvsd_filter;
|
||||
optional_device<filter_biquad_device> m_cvsd_filter2;
|
||||
required_device<pia6821_device> m_pia40;
|
||||
required_memory_bank m_cpubank;
|
||||
|
||||
|
@ -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"));
|
||||
|
@ -63,6 +63,7 @@ private:
|
||||
// devices
|
||||
required_device<mc6809e_device> m_cpu;
|
||||
required_device<pia6821_device> m_pia;
|
||||
required_device<ym2151_device> m_ym2151;
|
||||
required_device<hc55516_device> m_hc55516;
|
||||
|
||||
required_memory_bank m_rombank;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*------------------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) )
|
||||
|
@ -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<m6802_cpu_device> m_audiocpu;
|
||||
optional_device<input_merger_device> m_audioirq;
|
||||
optional_device<hc55516_device> m_hc55516;
|
||||
optional_device<filter_biquad_device> m_cvsd_filter;
|
||||
optional_device<filter_biquad_device> m_cvsd_filter2;
|
||||
optional_device<mc1408_device> m_dac;
|
||||
optional_device<pia6821_device> m_pias;
|
||||
required_device<pia6821_device> m_pia21;
|
||||
|
@ -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<ls157_device> m_mux;
|
||||
required_device<williams_cvsd_sound_device> m_cvsd_sound;
|
||||
required_device<s11_obg_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 -----------*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user