// license:CC0
// copyright-holders:Colin Douglas Howell
#include "netlist/devices/net_lib.h"
// This is a netlist description for the sound circuits of Midway's 280-ZZZAP
// and Laguna Racer, based on Midway's schematics "280 ZZZAP Game Logic P.C.
// 0610-00907A", "Laguna Racer Game Logic A084-90700-A622", and "Laguna Racer
// Game Logic A084-90700-B622". Midway PCB drawings for Laguna Racer and a
// photo by Andrew Wellburn of a 280-ZZZAP game logic board were also used to
// help determine correct values for certain components. The netlist should
// also apply to Taito/Midway's Super Speed Race with only modest changes,
// because that game uses very similar sound circuits to 280-ZZZAP and Laguna
// Racer.
// (Incidentally, the 280-ZZZAP schematic has some notes on changes made to
// the circuitry during production, a few of which were to audio circuits.
// This netlist uses the final state of the schematic, but it could be
// adjusted to the earlier state by modifying a couple of component values.)
// Due to its complexity, this netlist is rather CPU intensive. Various
// speedups have been employed to allow the game to run at full speed on
// modern hardware with some CPU to spare. The main sources of complexity are
// the op-amps (15 of them, all of which are National Semiconductor LM3900
// current-differencing "Norton-type" op-amps) and the Motorola MC3340
// Electronic Attenuator, the latter of which is emulated at its internal
// component level, which includes 10 bipolar junction transistors. Several of
// the circuits are oscillators which run at frequencies up to hundreds of
// hertz and which switch state abruptly, increasing the computing demands of
// the emulation.
// Midway's 280-ZZZAP and Laguna Racer schematics don't label discrete
// components, though they do label ICs with their PCB coordinates, as well as
// IC pins. Midway's Super Speed Race schematic, however, does label discrete
// components, most of which directly correspond to the 280-ZZZAP and Laguna
// Racer ones. I have used the Super Speed Race labels for the 280-ZZZAP
// discrete components. IC components I have labeled with their 280-ZZZAP PCB
// coordinates, consistent with the 280-ZZZAP schematic. Each LM3900 IC
// contains 4 op-amps, so I have labeled these op-amps as _1 to _4, the same
// scheme used in the LM3900 datasheet, using the pin numbers on the schematic
// to identify the specific op-amp. Likewise, each CD4016 IC contains 4
// switches, so I identify a specific switch from its pin numbers on the
// schematic; these are labeled _A through _D in the CD4016 datasheet, so I
// use that scheme.
// The sound circuitry can be divided into three sections: engine sound
// generation and control, generation of noise-based sound effects (skidding
// and crashes), and the final mix and amplification of all sounds.
// When comparing the sound circuitry and components between 280-ZZZAP and
// Laguna Racer, here are some things to note. There are two different
// versions of the Laguna Racer game logic board (where the sound circuits
// reside): A622 and B622. Midway's A622 schematic was clearly copied directly
// from 280-ZZZAP's--even the letter indicators for change notes in
// 280-ZZZAP's circuitry remain, though the actual notes were blanked out.
// B622's schematics show sound circuitry differences from 280-ZZZAP and A622,
// and these differences are consistent with Midway's PCB drawings for *both*
// the A622 and B622 games boards. Some of them are also consistent with
// Wellburn's 280-ZZZAP PCB photo. So it seems that actual 280-ZZZAP boards
// lie somewhere between the 280-ZZZAP schematic and the B622 Laguna Racer
// schematic.
// Include special frontiers within engine sound oscillators whose purpose is
// to improve solution convergence by reducing the oscillators' inherent
// numeric instability. Also adjusts values of resistors associated with these
// oscillators to maintain their frequency and voltage levels.
#define CONVERGENCE_FRONTIERS 1
// Use the fast high-level emulation of the analog noise generator, based on
// passing the noise signal to a simple AFUNC that outputs a high or low level
// according to the sign of the noise. This sounds the same as an accurate run
// of the component-level noise generator, but it is fast enough to run in
// real time.
#define FAST_HLE_NOISE_GEN 1
// Enable a voltage limiter on the output of the op-amp which generates the
// post-crash sound. This removes voltage glitches from the output which would
// otherwise be audible when the netlist is run with a fixed 48 kHz timestep,
// as it is in MAME.
#define REMOVE_POST_CRASH_NOISE_GLITCHES 1
// Netlists for two different games are defined in this file. These netlists
// are mostly alike but differ in a few key components. I use Aaron Giles'
// scheme for combining the two definitions in one file.
//
// Initial compilation includes this section.
//
#ifndef SOUND_VARIANT
//
// Now include ourselves twice, once for 280-ZZZAP and once for Laguna Racer.
//
#define VARIANT_280ZZZAP 0
#define VARIANT_LAGUNAR 1
#define SOUND_VARIANT (VARIANT_280ZZZAP)
#include "nl_280zzzap.cpp"
#undef SOUND_VARIANT
#define SOUND_VARIANT (VARIANT_LAGUNAR)
#include "nl_280zzzap.cpp"
#else
//
// Main netlist
//
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
static NETLIST_START(280zzzap_schematics)
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
static NETLIST_START(lagunar_schematics)
#endif
// **** Conversion of accelerator level to "engine speed" control
// **** voltage for engine sound, with capacitor-based "engine
// **** inertia" and gear shift changes.
// The accelerator pedal input gives a 4-bit digital value which the
// microprocessor stores in 74174 latch F5. The 4-bit value from the
// latch output is converted by a resistor network to an analog
// current of about 5.5 to 43 microamps, depending on the accelerator
// level, with 5.5 µA being "zero" and each additional accelerator
// increment adding another 2.5 µA. This current is then converted to
// a corresponding analog voltage by the first LM3900 op-amp H5_3 at a
// ratio of about 0.25 volts per accelerator increment. This op-amp's
// output charges or discharges capacitor C18, whose voltage provides
// the reference input for the second op-amp H5_4. The charging and
// discharging of C18 is how the engine sound's pitch and volume are
// changed, so more accelerator will charge it faster, and less
// accelerator will charge it slower or make it discharge faster.
// Also, it can discharge faster than it can charge (RC time constants
// of about 0.5 seconds vs. 2.2 seconds), so the engine will lose
// speed faster than it will gain it. Normally ENGINE_SOUND_OFF is
// low, but if it goes high (if you crash, or when the game is in
// attract mode), it closes CD4016 switch G5_A, which almost instantly
// discharges C18 through 270-ohm resistor R56 and cuts off the engine
// sound completely.
//
// Op-amp H5_4's output, which is driven to match the input current
// from capacitor C18, provides the actual speed voltage signal to the
// engine sound oscillators. This voltage is shifted down in level
// from H5_3, ranging from 0.7 to 4.1 V. It also accounts for gear
// shift, via capacitor C19 and the two adjacent CD4016 switches G5_B
// and G5_C. When in low gear, LO_SHIFT is active, switch G5_C is
// closed, and C19 is discharged via 10K resistor R60. HI_SHIFT is
// inactive, switch G5_B is open, and all the feedback current to H5_4
// runs through its 560K feedback resistor R58. However, when in high
// gear, the state of the two switches is reversed, and C19 is free to
// slowly charge through 560K resistor R59 while supplying additional
// feedback current to the op-amp without a resistor. This has the
// effect of temporarily depressing (or slowing the growth of) H5_4's
// output voltage while C19 is being charged until it eventually
// reaches equilibrium, at which point the output voltage will be the
// same as in the low-gear case. The effect is to slow down the engine
// speed, as you would expect in a higher gear. Likewise, if the
// accelerator is reduced in high gear, lowering the op-amp's input
// current, the fall in the op-amp's output voltage is temporarily
// delayed while C19 is charged until it has sufficiently discharged
// through R59. Thus the rate of engine slowing is also lowered.
// Some component values here were changed in the later version of the
// Laguna Racer game logic board or in Super Speed Race.
CD4016_DIP(G5)
LM3900(H5_3)
LM3900(H5_4)
RES(R42, RES_K(1))
RES(R43, RES_K(1))
RES(R44, RES_K(1))
RES(R45, RES_K(1))
RES(R62, RES_K(1))
RES(R61, RES_K(1))
// For the following resistors R51 and R46-R49, the values given are
// those shown in the Laguna Racer PCB drawings, the Laguna Racer B622
// board schematic, the Super Speed Race schematic, and Andrew
// Wellburn's 280-ZZZAP PCB photo. In the 280-ZZZAP schematic (and the
// Laguna Racer A622 board schematic, a near copy), R51 and R48 are
// 480 Kohms and R46 is 2 Mohms, but these seem to be errors in that
// schematic. 480 Kohm is not even a standard resistor value for any
// tolerance band, so it could not have been used on production
// boards.
RES(R51, RES_K(470))
RES(R46, RES_M(2.2))
RES(R47, RES_M(1))
RES(R48, RES_K(470))
RES(R49, RES_K(240))
RES(R50, RES_K(100))
// Super Speed Race also has a 1 uF capacitor in parallel with R50.
RES(R53, RES_K(47))
RES(R52, RES_K(10))
RES(R56, RES_R(270))
RES(R54, RES_K(560))
RES(R55, RES_M(4.7))
RES(R57, RES_M(2.7))
RES(R58, RES_K(560))
RES(R59, RES_K(560))
RES(R60, RES_K(10))
// The size of capacitor C18 varies with the game: 47 uF in 280-ZZZAP,
// 22 uF in Laguna Racer and Super Speed Race:
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
CAP(C18, CAP_U(47))
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
CAP(C18, CAP_U(22))
#endif
NET_C(C18.2, GND)
CAP(C19, CAP_U(2.2))
DIODE(D4, 1N914)
DIODE(D5, 1N914)
NET_C(I_V5.Q, R42.1, R43.1, R44.1, R45.1, R62.1, R61.1, R51.1,
R55.1, R57.1)
NET_C(PEDAL_BIT0, R42.2, R46.1)
NET_C(PEDAL_BIT1, R43.2, R47.1)
NET_C(PEDAL_BIT2, R44.2, R48.1)
NET_C(PEDAL_BIT3, R45.2, R49.1)
NET_C(H5_3.PLUS, R46.2, R47.2, R48.2, R49.2, R51.2)
NET_C(H5_3.MINUS, R50.2)
NET_C(H5_3.OUT, R50.1, R53.1, D4.K)
// Super Speed Race also has a 1 uF capacitor in parallel with R50.
NET_C(R53.2, R52.1, C18.1, R56.2, R54.1)
NET_C(R52.2, D4.A)
NET_C(H5_4.PLUS, R54.2, R55.2)
NET_C(H5_4.MINUS, R58.2, R57.2, G5.4)
// The following are only local connections to H5_4.OUT. It also runs
// to all the oscillators; those connections are listed further down.
NET_C(H5_4.OUT, D5.A, R59.1)
NET_C(D5.K, R58.1)
NET_C(C19.1, R59.2, R60.2)
NET_C(G5.1, GND)
NET_C(G5.2, R56.1)
NET_C(G5.13, ENGINE_SOUND_OFF)
NET_C(G5.5, R62.2, HI_SHIFT)
NET_C(G5.3, G5.8, C19.2)
NET_C(G5.9, R60.1)
NET_C(G5.6, R61.2, LO_SHIFT)
// **** Engine sound generation, using three triangle-wave oscillators
// **** running at frequency and amplitude determined by engine speed
// **** control voltage and modulated by MC3340 amplifier/attenuator.
// Engine sound oscillators. There are three of these, at different
// frequencies; all three are triangle-wave oscillators whose
// frequency and amplitude are controlled by the "engine speed"
// voltage signal output from LM3900 op-amp H5_4. Each oscillator is a
// cascaded pair of LM3900s with a CD4016 switch. The switch feeds
// into the reference input of the first op-amp, whose output is also
// the final oscillator output; the second op-amp acts as a Schmitt
// trigger on the first op-amp's output, generating a square wave that
// controls the switch and determines whether the triangle wave is
// rising or falling.
// The two lower-frequency oscillators are summed together. One has
// four times the frequency of the other, producing a "ragged"
// triangle wave. The combined signal is fed into the signal input of
// the MC3340 voltage-controlled amplifier/attenuator. The third,
// highest-frequency oscillator drives the MC3340's control input,
// amplitude-modulating the signal input from the first two
// oscillators. Greater voltages on this control signal produce
// greater attenuation in a logarithmic fashion. The final output
// signal from the MC3340 thus has a waveform of narrow needle-like
// spikes which grow taller and shorter according to the original
// input waveform.
// The Schmitt triggers in these oscillator circuits are numerically
// unstable at the switching points, which makes numeric convergence
// difficult. The standard way to guarantee convergence with such a
// circuit is to use dynamic timestepping with very short minimum
// timesteps. In this case that solution is unacceptably slow, so an
// alternate solution is used instead. To remove the unstable behavior
// of the Schmitt triggers in the solver, a "frontier" is inserted at
// the output of each Schmitt trigger op-amp. With this, a change in
// the op-amp's output level, caused by a change in its inputs, will
// not affect either its feedback input or the CD4016 switch during
// the same timestep. Instead, it will only take effect when computing
// the following timestep. This greatly improves the stability and
// performance of the solver while only slightly altering the
// frequency and voltage levels of the oscillator's triangle-wave
// output. These waveform changes are compensated for by slightly
// adjusting the value of the resistors which determine the Schmitt
// trigger's switch points, bringing the waveform very close to what
// it would be if the standard approach of dynamic timestepping were
// used instead.
// These are the remaining connections from H5_4.OUT:
NET_C(H5_4.OUT, R36.1, R37.1, R31.1, R32.1, R29.1, R30.1)
MC3340_DIP(MC3340_H2)
CD4016_DIP(J4)
// First oscillator (bottommost in schematics).
// Schematic shows frequency of "76 Hz", but when modeled without a
// frontier, the maximum in low gear is 87 Hz.
// With a frontier, uncompensated, this decreases to 86 Hz.
LM3900(J5_2)
LM3900(J5_1)
RES(R36, RES_K(560))
RES(R37, RES_K(270))
RES(R38, RES_K(100))
RES(R41, RES_K(10))
NET_C(R41.2, GND)
#if CONVERGENCE_FRONTIERS
// Schmitt trigger resistors changed to compensate for waveform changes
// from use of frontier:
RES(R39, RES_K(465.0))
RES(R40, RES_K(275.0))
#else
RES(R39, RES_K(470))
RES(R40, RES_K(270))
#endif
CAP(C17, CAP_U(0.022))
DIODE(D3, 1N914)
// Note the connections to this oscillator's CD4016 switch at J4 have
// mislabeled pin numbers on the schematics for 280-ZZZAP and both
// Laguna Racer versions; they are shown as pins 3, 4, 5, which are
// for switch B, the same switch which is used by the middle
// oscillator. The Super Speed Race schematic correctly shows this
// oscillator using pins 10, 11, 12, which are for switch D of the
// CD4016 (located at B1 in that game). It seems very unlikely that
// the earlier games had two oscillators sharing the same switch; that
// shouldn't work at all. I assume that this was a schematic error
// which was not corrected until much later.
NET_C(R37.2, J4.10)
NET_C(J4.11, J5_2.PLUS)
NET_C(R36.2, J5_2.MINUS, C17.1)
NET_C(J5_2.OUT, C17.2, R38.1, D3.A)
NET_C(R38.2, J5_1.MINUS)
NET_C(J5_1.OUT, R40.1, J4.12)
NET_C(I_V5.Q, R39.1)
NET_C(J5_1.PLUS, R39.2, R40.2)
NET_C(R41.1, D3.K, D2.K, C16.1)
// Second oscillator (middle in schematics).
// Schematic shows frequency of "315 Hz", but when modeled without a
// frontier, the maximum in low gear is 343 Hz.
// With a frontier, uncompensated, this decreases to 329 Hz.
LM3900(J3_3)
LM3900(J3_4)
RES(R31, RES_K(300))
RES(R32, RES_K(150))
RES(R33, RES_K(100))
#if CONVERGENCE_FRONTIERS
// Schmitt trigger resistors changed to compensate for waveform changes
// from use of frontier:
RES(R34, RES_K(460))
RES(R35, RES_K(281))
#else
RES(R34, RES_K(470))
RES(R35, RES_K(270))
#endif
CAP(C15, CAP_U(0.01))
CAP(C16, CAP_U(10))
DIODE(D2, 1N914)
NET_C(R32.2, J4.4)
NET_C(J4.3, J3_3.PLUS)
NET_C(R31.2, J3_3.MINUS, C15.1)
NET_C(J3_3.OUT, C15.2, R33.1, D2.A)
NET_C(R33.2, J3_4.MINUS)
NET_C(J3_4.OUT, R35.1, J4.5)
NET_C(I_V5.Q, R34.1)
NET_C(J3_4.PLUS, R34.2, R35.2)
NET_C(C16.2, MC3340_H2.1) // to MC3340 input pin
// Third oscillator (topmost in schematics).
// This is the amplitude-modulation oscillator, whose frequency
// differs slightly between 280-ZZZAP and the later games, Laguna
// Racer and Super Speed Race. This difference distinctly changes the
// tone of the engine sound.
// The 280-ZZZAP and Laguna Racer schematics all show a frequency of
// "428 Hz", but for 280-ZZZAP, when equipped with a 110 Kohm resistor
// and modeled without a frontier, the maximum in low gear is 465 Hz;
// with a frontier, uncompensated, this decreases to 442 Hz.
// For Laguna Racer, when equipped with a 100 Kohm resistor and
// modeled without a frontier, the maximum low gear frequency is 511
// Hz; with a frontier, uncompensated, it's 482 Hz.
LM3900(J3_2)
LM3900(J3_1)
RES(R29, RES_K(220))
// The value of resistor R30 is what changes the engine sound between
// 280-ZZZAP and the later games. The 280-ZZZAP schematic indicates
// that it is 110 Kohm in that game. This resistor is hidden on Andrew
// Wellburn's 280-ZZZAP PCB photo, but the sound resulting from 110
// Kohm is more consistent with actual 280-ZZZAP machines than that
// from 100 Kohm.
// The resistor is 100 Kohm in the B622 Laguna Racer and Super Speed
// Race schematics and on the PCB drawings for both Laguna Racer board
// versions, so I assume all Laguna Racer versions had this value.
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
RES(R30, RES_K(110))
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
RES(R30, RES_K(100))
#endif
RES(R28, RES_K(100))
#if CONVERGENCE_FRONTIERS
// Schmitt trigger resistors changed to compensate for waveform changes
// from use of frontier.
// Since the different games have different oscillator frequencies,
// the compensated values of these resistors differ slightly as well.
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
RES(R26, RES_K(455))
RES(R27, RES_K(284))
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
RES(R26, RES_K(453))
RES(R27, RES_K(286))
#endif
#else
RES(R26, RES_K(470))
RES(R27, RES_K(270))
#endif
CAP(C14, CAP_U(0.01))
RES(R23, RES_K(10))
NET_C(R23.1, I_V12.Q)
RES(R25, RES_K(3.3))
NET_C(R25.2, GND)
CAP(C13, CAP_U(10))
RES(R22, RES_R(470))
DIODE(D1, 1N914)
NET_C(I_V5.Q, R22.1)
NET_C(ENGINE_SOUND_OFF, R22.2, D1.A)
NET_C(R30.2, J4.1)
NET_C(J4.2, J3_2.PLUS)
NET_C(R29.2, J3_2.MINUS, C14.1)
NET_C(J3_2.OUT, C14.2, R28.1, C13.1)
NET_C(R28.2, J3_1.MINUS)
NET_C(J3_1.OUT, R27.1, J4.13)
NET_C(I_V5.Q, R26.1)
NET_C(J3_1.PLUS, R26.2, R27.2)
NET_C(D1.K, R23.2, C13.2, R25.1, MC3340_H2.2) // to MC3340 ctrl pin
// The MC3340's output is the complete engine sound, which is sent to
// the final mix.
// **** Noise generation and noise-based sound effects: tire skid
// **** (NOISE_CR_1), boom from crash (BOOM), post-crash noise
// **** (NOISE_CR_2).
// The noise generator circuit for 280-ZZZAP and Laguna Racer is based
// on a reverse-biased 9.1-volt 1N5239 zener diode which generates a
// noisy current for an LM3900 Norton op-amp input. This op-amp then
// amplifies the noise so strongly that its output saturates, randomly
// oscillating between the maximum and mininum values, producing a
// quasi-digital random waveform which is then smoothed and filtered
// by another op-amp to form a continuous analog noise signal.
// Following circuits re-filter and reshape this noise for the
// noise-based sound effects.
//
// The noise of zener diodes is not well controlled during their
// manufacture, and it can vary widely in strength from one part to
// another even within a single production lot. However, this
// op-amp-based noise generator takes zener noise of unpredictable
// strength and converts it into an analog noise stream whose strength
// is predictable, determined by the response of the op-amp circuits.
//
// Super Speed Race generates its noise in a different fashion, using
// a linear-feedback shift register on the main motherboard to
// generate a true digital random noise signal. However, once this
// digital noise has been generated, it gets smoothed and filtered
// into continuous analog form in the same way and is further
// processed through similar sound effect circuits.
//
// Since the noise generator discrete components are unlabeled on the
// 280-ZZZAP and Laguna Racer schematics and are not present in Super
// Speed Race, I've chosen my own labels for them.
//
// The netlist library supports zener diodes, but not zener noise. The
// noise is simulated using an additional source of noise voltage.
// This noise is handled in one of two ways, depending on how the
// netlist is configured.
//
// If the netlist is configured for detailed circuit simulation, the
// noise source is inserted in series between the zener diode's output
// and the op-amp's non-inverting input. This netlist configuration
// tries to accurately reproduce the amplified noise signal which the
// op-amp would generate. Because of the rapidly changing noise signal
// and the op-amp's high gain, however, producing an accurate,
// glitch-free signal requires using dynamic time-stepping with a very
// short minimum time step on the order of 10 nanoseconds. This has an
// unacceptable impact on performance and is unsuitable for real-time
// operation, like in a normal MAME session.
//
// For such operation, a simpler high-level emulation configuration is
// used. Because of the very high gain of the op-amp, the real output
// signal will always lie at either the op-amp's maximum or minimum
// output value, almost never being in between. So we take the same
// input noise signal and replace the op-amp with a simple AFUNC()
// module which only checks the input signal's sign and outputs the
// corresponding maximum or minimum value. This version runs much
// faster, not requiring dynamic time-stepping at all for a
// glitch-free result, and its output is very similar to running the
// detailed emulation with a short minimum timestep. Once the output
// has been smoothed and filtered, the two configurations will be
// effectively identical, and there will be no difference in the
// resulting sound effects except for how fast they are computed.
// 24 kHz noise clock for the noise source, chosen to retain noise
// frequencies as high as possible for 48 kHz sample rate.
CLOCK(NCLK, 24000)
NET_C(I_V5.Q, NCLK.VCC)
NET_C(GND, NCLK.GND)
// Normally-distributed noise of 10 millivolts RMS voltage. With the
// zener passing about 25 microamps of current, the real noise may be
// even stronger than this, but this is strong enough to cause the
// op-amp to saturate its output, which this noise generator is
// designed to do.
// (If the simplified noise generator netlist is being used, the noise
// signal is re-centered on zero volts, and its amplitude no longer
// matters, only its sign.)
SYS_NOISE_MT_N(NOISE, 0.01)
NET_C(NCLK.Q, NOISE.I)
// Both the quasi-digital noise signal produced by the 280-ZZZAP and
// Laguna Racer noise generators and the digital noise signal produced
// by the Super Speed Race noise generator enter at the upstream end
// of capacitor C1.
CAP(C1, CAP_U(10))
#if FAST_HLE_NOISE_GEN
// Simplified high-level emulation of the noise generator: oscillate
// between full on and full off according to the sign of the noise
// input.
NET_C(NOISE.1, A_NOISE.A0)
NET_C(NOISE.2, GND)
AFUNC(A_NOISE, 1, "if(A0 > 0, 4.5, 0.03)")
NET_C(A_NOISE.Q, C1.1)
#else
// Simple model of a 1N5239 9.1-volt Zener diode. The 1N5239 is
// specified to conduct 20 mA of current at its nominal breakdown
// voltage of 9.1 V. The model produces an exponential I-V curve,
// passing through this point, which has the same general shape as
// that of a normal forward-biased diode. NBV is an exponent scale
// factor; its value here of 1 gives the curve a steep rise and a
// relatively sharp knee. Actual breakdown I-V curves have an even
// steeper rise and sharper knee, too steep and sharp to be
// represented by an exponential, but this model is good enough for
// this emulation, since the diode operates very close to a single
// point on the curve.
ZDIODE(ZD_1N5239, "D(BV=9.1 IBV=0.020 NBV=1)")
RES(RNOISE0, RES_K(100))
CAP(CNOISE0, CAP_U(10))
NET_C(CNOISE0.2, GND)
NET_C(I_V12.Q, RNOISE0.1)
NET_C(RNOISE0.2, CNOISE0.1, ZD_1N5239.K)
LM3900(H4_2)
NET_C(I_V5.Q, H4_2.VCC)
NET_C(GND, H4_2.GND)
RES(RNOISE1, RES_K(56))
RES(RNOISE2, RES_K(47))
RES(RNOISE3, RES_K(1))
CAP(CNOISE, CAP_U(10))
NET_C(CNOISE.1, RNOISE1.1, RNOISE2.1)
NET_C(CNOISE.2, GND)
NET_C(H4_2.MINUS, RNOISE1.2)
NET_C(ZD_1N5239.A, NOISE.1)
NET_C(H4_2.PLUS, NOISE.2)
NET_C(H4_2.OUT, RNOISE2.2, RNOISE3.1, C1.1)
NET_C(RNOISE3.2, GND)
#endif
NET_C(C1.2, R1.1)
// The noise generator is followed by a single-amplifier active
// low-pass filter with a corner frequency of about 6.3 kHz, a very
// broad Q of 0.014, and a gain of about 0.8. This filter attenuates
// the very highest noise frequencies, converting the initial
// quasi-digital noise waveform into a smoother analog noise waveform
// that is still of pretty high frequency.
RES(R1, RES_K(330)) // 680 Kohm in Super Speed Race
LM3900(H4_1)
CAP(C2, CAP_P(6800))
RES(R2, RES_K(10))
RES(R3, RES_K(820))
RES(R4, RES_K(270))
CAP(C3, CAP_P(220))
NET_C(R1.2, C2.1, R2.1, R4.1)
NET_C(C2.2, GND)
NET_C(H4_1.MINUS, R2.2, C3.1)
NET_C(I_V5.Q, R3.1)
NET_C(H4_1.PLUS, R3.2)
NET_C(H4_1.OUT, C3.2, R4.2, R5.1, R17.1, C5.1, C4.1)
// The smoothed analog noise is passed to three different sound-effect
// circuits, each of which is also an active-filter type.
// First noise circuit: tire skid (NOISE_CR_1)
// This is a two-amplifier active bandpass filter with a center
// frequency of about 1 kHz and a high Q value of 25, giving a
// narrow bandwidth of 40 Hz. The gain is about 15.
// The result is a high-pitched "tire skid" screeching sound.
// The circuit appears to be taken practically verbatim from page 19 of
// National Semiconductor's Application Note 72 about the LM3900.
CD4016_DIP(G4)
LM3900(H4_3)
RES(R5, RES_K(39))
RES(R6, RES_R(62))
RES(R7, RES_K(82))
RES(R8, RES_K(39))
CAP(C7, CAP_U(0.1))
CAP(C8, CAP_U(0.1))
NET_C(R5.2, R6.1, C7.1, C8.1, R12.1)
NET_C(R6.2, GND)
NET_C(H4_3.MINUS, C7.2, R8.1)
NET_C(I_V5.Q, R7.1)
NET_C(H4_3.PLUS, R7.2)
NET_C(H4_3.OUT, R8.2, C8.2, R9.1)
// Super Speed Race has an extra CD4016 switch (controlled by the same
// NOISE_CR_1 control line as the one at the end of this circuit) and
// a 0.1 uF capacitor between H4_3.OUT and R9.1 here.
LM3900(H4_4)
// For resistor R11, the value given is that shown in the Laguna Racer
// PCB drawings, the Laguna Racer B622 board schematic, the Super
// Speed Race schematic, and Andrew Wellburn's 280-ZZZAP PCB photo. In
// the 280-ZZZAP schematic and the near-copy Laguna Racer A622 board
// schematic, R11 is 100 Kohms, but this seems to be a schematic
// error. (The original National Semiconductor Application Note 72
// from which this circuit was apparently taken also uses 120 Kohm for
// R11.)
RES(R9, RES_K(39))
RES(R10, RES_K(62)) // 240 Kohm in Super Speed Race
RES(R11, RES_K(120))
RES(R12, RES_K(62))
NET_C(H4_4.MINUS, R9.2, R11.1)
NET_C(I_V5.Q, R10.1)
NET_C(H4_4.PLUS, R10.2)
NET_C(H4_4.OUT, R11.2, R12.2, G4.4)
NET_C(G4.3, R63.1)
NET_C(G4.5, NOISE_CR_1)
// Second noise circuit: post-crash noise (NOISE_CR_2)
// This circuit is peculiar. It's structured like a single-amplifier
// active low-pass filter, with a corner frequency of about 1 kHz and
// a gain of 100--but its Q factor turns out to be a small *negative*
// number. (I'm not sure what effect this has on a filter, but it
// might indicate instability.)
// The result is saturated, heavily clipped noise with frequencies
// mainly below 1 kHz.
// I don't know why the circuit was designed this way, or whether it
// was deliberate or a design or production error which the makers
// decided they liked or at least could accept. It doesn't look like
// they fixed it later; this same circuit is unchanged in the
// schematics for all three games.
RES(R17, RES_K(10))
CAP(C5, CAP_U(10))
CAP(C4, CAP_U(0.022))
LM3900(H5_2)
RES(R16, RES_K(1.5))
RES(R15, RES_K(15))
RES(R14, RES_M(2.7))
RES(R13, RES_K(150))
CAP(C6, CAP_U(0.01))
CAP(C9, CAP_U(0.001))
NET_C(C4.2, R16.1)
NET_C(R16.2, C6.1, R15.1, R13.1)
NET_C(C6.2, GND)
NET_C(H5_2.MINUS, R15.2, C9.1)
NET_C(I_V5.Q, R14.1)
NET_C(H5_2.PLUS, R14.2)
#if REMOVE_POST_CRASH_NOISE_GLITCHES
// With the static time-stepping used to ensure acceptable performance
// with MAME, this part of the netlist will generate extra spikes on
// the op-amp output with voltages outside of the real op-amp's output
// range. These spikes give the sound an unwanted "grittiness" not in
// the original, so I've added a voltage-limiting AFUNC to the op-amp
// output beyond its feedback connection. This gives a smoother sound,
// closer to the original.
NET_C(H5_2.OUT, C9.2, R13.2, H5_2_LIM.A0)
AFUNC(H5_2_LIM, 1, "max(min(A0, 4.5), 0)")
NET_C(H5_2_LIM.Q, G4.8)
#else
NET_C(H5_2.OUT, C9.2, R13.2, G4.8)
#endif
NET_C(G4.9, R64.1)
NET_C(G4.6, NOISE_CR_2)
// Third noise circuit: boom from crash (BOOM)
// This is a single-amplifier active bandpass filter with a center
// frequency of about 60 Hz and a high Q value of about 19, giving a
// narrow 3 Hz bandwidth. The gain is also very high, a little over
// 200.
// The filter is normally cut off from the noise signal, and thus it
// remains quiet. When the BOOM signal is activated, CD4016 switch
// G4_A opens, letting in the noise to be filtered and amplified until
// the switch is cut off again, generating a loud, fairly deep "boom".
// (The "boom" doesn't have much decay, though; it gets cut abruptly.)
LM3900(H5_1)
// Resistor R18, part of the BOOM filter, is 2.2 Kohms on the
// 280-ZZZAP schematic as well as in Laguna Racer, but the 280-ZZZAP
// schematic has a note "E" saying that the first 325 machines had a
// 6.8 Kohm resistor here instead. Andrew Wellburn's 280-ZZZAP board
// photo may show such a resistor. With this larger resistor, the
// filter's values are: center frequency 54 Hz, Q 16.9, gain 73.5; so
// the filter is slightly broader, pitched a bit lower, and has only a
// third as much gain. Still, the basic effect is the same.
RES(R18, RES_K(2.2)) // 20 Kohm in Super Speed Race
RES(R19, RES_K(1))
RES(R20, RES_M(3.3))
RES(R21, RES_M(1)) // 1.5 Mohm in Super Speed Race
CAP(C10, CAP_U(0.1))
CAP(C11, CAP_U(0.1))
NET_C(R17.2, C5.2, G4.1)
NET_C(G4.2, R18.1)
NET_C(G4.13, BOOM)
NET_C(R18.2, R19.1, C10.1, C11.1)
NET_C(R19.2, GND)
NET_C(H5_1.MINUS, C10.2, R21.1)
NET_C(I_V5.Q, R20.1)
NET_C(H5_1.PLUS, R20.2)
NET_C(H5_1.OUT, R21.2, C11.2, R65.1)
// **** Final mix of sound effects and sound preamplification.
// The preamplification stage after the final mix has two LM3900
// op-amps. The audio power amplifier beyond it is a National
// Semiconductor LM377 integrated dual power amp, with two 2-watt
// power amps on the same chip. Both of these power amps drive the
// single speaker from opposite ends in a push-pull fashion, a
// so-called "bridge configuration" which effectively converts the
// dual 2-watt power amp into a single 4-watt amp.
// The two power amps get their inputs from the outputs of the two
// LM3900 op-amps. The first LM3900, J5_3, pre-amplifies the signal
// from the final mix and has a master volume potentiometer to control
// its gain. Its output gets sent directly to one power amp and is
// also passed to the second LM3900, J5_4, which inverts the signal
// without changing the gain. The inverted signal is sent to the
// second power amp that drives the speaker's other end. The first
// LM3900 is AC-coupled to the second, and both are AC-coupled to the
// power amps. As a result, the output signal of the second LM3900
// more closely resembles the final output in waveform and tone
// quality than the first LM3900's output does, so I have chosen the
// second LM3900 as the final output for the emulation. The power amps
// themselves are not emulated.
// The master volume potentiometer is user-adjustable and is set to
// its midpoint by default. Higher settings may produce clipped,
// distorted output, but since the power amps are configured for a
// voltage gain of 20 and use a 20 V power supply, this is likely true
// for the original hardware as well.
RES(R63, RES_K(12)) // 3 Kohm in Super Speed Race
RES(R64, RES_K(150))
// Resistor R65, the mixing resistor for the BOOM effect, is 12 Kohms
// on the 280-ZZZAP schematic and in Laguna Racer and Super Speed
// Race, but the 280-ZZZAP schematic has a note "F" saying that the
// first 325 machines had a 4.3 Kohm resistor here instead. Andrew
// Wellburn's 280-ZZZAP board photo shows such a resistor. The lower
// resistor makes the BOOM effect much louder, countering the reduced
// gain on its generating filter described above. In fact, it makes it
// loud enough to be clipped by the post-mixer op-amps; turning down
// the volume to prevent this clipping makes all other sounds quieter
// by comparison. This may explain why both the mixing resistor and
// the filter resistor were changed in later machines.
RES(R65, RES_K(12))
RES(R66, RES_K(33))
CAP(C20, CAP_U(10))
CAP(C21, CAP_U(10))
NET_C(R63.2, R64.2, R65.2, C20.1)
NET_C(MC3340_H2.7, R66.1) // MC3340 output pin
NET_C(R66.2, C21.1)
LM3900(J5_3)
LM3900(J5_4)
RES(R67, RES_K(2))
CAP(C22, CAP_U(10))
RES(R68, RES_K(220))
RES(R74, RES_K(220))
NET_C(I_V5.Q, R67.1)
NET_C(R67.2, C22.1, R68.1, R74.1)
NET_C(C22.2, GND)
NET_C(J5_3.PLUS, R68.2)
NET_C(J5_4.PLUS, R74.2)
RES(R69, RES_K(100))
POT2(R70, RES_K(10)) // Master volume potentiometer (2 terminals)
CAP(C23, CAP_U(10))
CAP(C24, CAP_U(0.1))
RES(R73, RES_K(100))
RES(R75, RES_K(100))
NET_C(R70.2, C23.2)
NET_C(J5_3.MINUS, C20.2, C21.2, R69.1, R70.1)
NET_C(J5_3.OUT, C23.1, R69.2, C24.1)
NET_C(C24.2, R73.1)
NET_C(J5_4.MINUS, R73.2, R75.1)
NET_C(J5_4.OUT, R75.2)
ALIAS(OUTPUT, J5_4.OUT)
NETLIST_END()
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
NETLIST_START(280zzzap)
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
NETLIST_START(lagunar)
#endif
SOLVER(Solver, 48000)
#if !(FAST_HLE_NOISE_GEN)
PARAM(Solver.DYNAMIC_TS, 1)
// 10 ns is the minimum timestep to avoid significant spikes outside
// the allowed LM3900 output region of 0-4.5 V; even 20 ns gives
// frequent spikes into the 4.5-5 volt region.
PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 1e-8)
#endif
// All together, loosening both tolerances and reducing accuracy
// increases speed by ~10%, but it also causes audible "crackling".
// PARAM(Solver.RELTOL, 1e-2) // default 1e-3 (several % faster, but < quality)
// PARAM(Solver.VNTOL, 5e-3) // default 1e-7 (several % faster, but < quality)
// PARAM(Solver.ACCURACY, 1e-3) // default 1e-7 (few % faster)
// PARAM(Solver.DYNAMIC_TS, 1)
// PARAM(Solver.DYNAMIC_LTE, 1e-4) // default 1e-5
// PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 1e-8) // default 1e-6
ANALOG_INPUT(I_V12, 12)
ANALOG_INPUT(I_V5, 5)
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
LOCAL_SOURCE(280zzzap_schematics)
INCLUDE(280zzzap_schematics)
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
LOCAL_SOURCE(lagunar_schematics)
INCLUDE(lagunar_schematics)
#endif
// The MC3340 gets 12-volt power in 280-ZZZAP and Laguna Racer.
// In Super Speed Race it gets 5-volt power.
NET_C(I_V12.Q, MC3340_H2.8)
NET_C(GND, MC3340_H2.3)
// Logic inputs which represent output pins from 74174 latches at F4
// and F5
LOGIC_INPUT(I_F4_2, 0, "74XX") // BOOM
LOGIC_INPUT(I_F4_5, 0, "74XX") // labeled "ENGINE SOUND", but really
// an "engine sound off" flag
LOGIC_INPUT(I_F4_7, 0, "74XX") // NOISE CR 1
LOGIC_INPUT(I_F4_10, 0, "74XX") // NOISE CR 2
LOGIC_INPUT(I_F5_2, 0, "74XX") // PEDAL_BIT0
LOGIC_INPUT(I_F5_5, 0, "74XX") // PEDAL_BIT1
LOGIC_INPUT(I_F5_7, 0, "74XX") // PEDAL_BIT2
LOGIC_INPUT(I_F5_10, 0, "74XX") // PEDAL_BIT3
LOGIC_INPUT(I_F5_12, 0, "74XX") // HI SHIFT
LOGIC_INPUT(I_F5_15, 0, "74XX") // LO SHIFT
ALIAS(I_BOOM, I_F4_2.IN)
ALIAS(I_ENGINE_SOUND_OFF, I_F4_5.IN)
ALIAS(I_NOISE_CR_1, I_F4_7.IN)
ALIAS(I_NOISE_CR_2, I_F4_10.IN)
ALIAS(I_PEDAL_BIT0, I_F5_2.IN)
ALIAS(I_PEDAL_BIT1, I_F5_5.IN)
ALIAS(I_PEDAL_BIT2, I_F5_7.IN)
ALIAS(I_PEDAL_BIT3, I_F5_10.IN)
ALIAS(I_HI_SHIFT, I_F5_12.IN)
ALIAS(I_LO_SHIFT, I_F5_15.IN)
ALIAS(BOOM, I_F4_2.Q)
ALIAS(ENGINE_SOUND_OFF, I_F4_5.Q)
ALIAS(NOISE_CR_1, I_F4_7.Q)
ALIAS(NOISE_CR_2, I_F4_10.Q)
ALIAS(PEDAL_BIT0, I_F5_2.Q)
ALIAS(PEDAL_BIT1, I_F5_5.Q)
ALIAS(PEDAL_BIT2, I_F5_7.Q)
ALIAS(PEDAL_BIT3, I_F5_10.Q)
ALIAS(HI_SHIFT, I_F5_12.Q)
ALIAS(LO_SHIFT, I_F5_15.Q)
// Power pins for logic inputs:
NET_C(I_V5.Q,
I_F4_2.VCC, I_F4_5.VCC, I_F4_7.VCC, I_F4_10.VCC,
I_F5_2.VCC, I_F5_5.VCC, I_F5_7.VCC, I_F5_10.VCC,
I_F5_12.VCC, I_F5_15.VCC)
NET_C(GND,
I_F4_2.GND, I_F4_5.GND, I_F4_7.GND, I_F4_10.GND,
I_F5_2.GND, I_F5_5.GND, I_F5_7.GND, I_F5_10.GND,
I_F5_12.GND, I_F5_15.GND)
// Power inputs for the LM3900 op-amps. These aren't shown on the
// schematics, but it looks like 5-volt power is needed to get proper
// results.
// (H4_2, the noise generator op-amp, is not listed here because it's
// inside the #else clause of the #if FAST_HLE_NOISE_GEN conditional.)
NET_C(I_V5.Q,
H4_1.VCC, H4_3.VCC, H4_4.VCC,
H5_1.VCC, H5_2.VCC, H5_3.VCC, H5_4.VCC,
J3_1.VCC, J3_2.VCC, J3_3.VCC, J3_4.VCC,
J5_1.VCC, J5_2.VCC, J5_3.VCC, J5_4.VCC)
NET_C(GND,
H4_1.GND, H4_3.GND, H4_4.GND,
H5_1.GND, H5_2.GND, H5_3.GND, H5_4.GND,
J3_1.GND, J3_2.GND, J3_3.GND, J3_4.GND,
J5_1.GND, J5_2.GND, J5_3.GND, J5_4.GND)
// Power inputs for the CD4016 switches. Again, these aren't shown on
// the schematics, but 5-volt power must be used for the switches to
// handle the voltage levels they are switching.
NET_C(I_V5.Q, G5.14, J4.14, G4.14)
NET_C(GND, G5.7, J4.7, G4.7)
// Switches G5_D, J4_C, and G4_D are unused.
NET_C(GND, G5.10, G5.11, G5.12, J4.6, J4.8, J4.9, G4.10, G4.11, G4.12)
// Frontier after output of noise generator.
// FIXME: Anomaly - with FAST_HLE_NOISE_GEN set, this frontier cannot
// be removed, because doing so kills the noise entirely, even though
// the AFUNC is still generating the same output. Perhaps an AFUNC
// output by itself won't change the capacitor state?
OPTIMIZE_FRONTIER(C1.1, RES_M(1), 50)
// Frontier before skid screech generator.
OPTIMIZE_FRONTIER(R5.1, RES_K(39), 50)
// (Adding a frontier *after* the skid screech generator makes the
// sudden jumps in signal level on skids much larger, so the resulting
// clicks are louder and more objectionable. Also it does little or
// nothing for speed. That's why I don't have such a frontier.)
// Frontiers after NOISE CR 2 and BOOM generators.
OPTIMIZE_FRONTIER(R64.1, RES_K(150), 50)
OPTIMIZE_FRONTIER(R65.1, RES_K(12), 50)
// Frontier after engine sound generation.
OPTIMIZE_FRONTIER(R66.1, RES_K(33), 50)
// Frontiers before MC3340 inputs.
OPTIMIZE_FRONTIER(C16.1, RES_M(1), 50)
OPTIMIZE_FRONTIER(C13.1, RES_M(1), 50)
// Frontiers before engine sound op-amp oscillators.
OPTIMIZE_FRONTIER(R36.1, RES_K(560), 50)
OPTIMIZE_FRONTIER(R37.1, RES_K(270), 50)
OPTIMIZE_FRONTIER(R31.1, RES_K(300), 50)
OPTIMIZE_FRONTIER(R32.1, RES_K(150), 50)
OPTIMIZE_FRONTIER(R29.1, RES_K(220), 50)
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
OPTIMIZE_FRONTIER(R30.1, RES_K(110), 50)
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
OPTIMIZE_FRONTIER(R30.1, RES_K(100), 50)
#endif
#if CONVERGENCE_FRONTIERS
// Frontiers at Schmitt trigger op-amp outputs of engine sound
// oscillators, to eliminate numeric instability and speed convergence
// rather than to partition matrices. The resistor values given are
// the modified ones which compensate for how the frontiers would
// otherwise change the oscillator waveforms.
OPTIMIZE_FRONTIER(R40.1, RES_K(275), 50)
OPTIMIZE_FRONTIER(R35.1, RES_K(281), 50)
#if (SOUND_VARIANT == VARIANT_280ZZZAP)
OPTIMIZE_FRONTIER(R27.1, RES_K(284), 50)
#else // (SOUND_VARIANT == VARIANT_LAGUNAR)
OPTIMIZE_FRONTIER(R27.1, RES_K(286), 50)
#endif
#endif
NETLIST_END()
#endif