// license:BSD-3-Clause
// copyright-holders:Zsolt Vasvari
/*****************************************************************************
Texas Instruments SN76477 emulator
SN76477 pin layout. There is a corresponding interface variable with the
same name. The only exception is noise_clock which must be programmatically
set. The other pins have programmatic equivalents as well.
The name of the function is SN76477_<pinname>_w.
All capacitor functions can also specify a fixed voltage on the cap.
The name of this function is SN76477_<pinname>_voltage_w
+-------------------+
envelope_1 | 1 | | 28| envelope_2
| 2 GND - 27| mixer_c
noise_clock | 3 26| mixer_a
noise_clock_res | 4 25| mixer_b
noise_filter_res | 5 24| one_shot_res
noise_filter_cap | 6 23| one_shot_cap
decay_res | 7 22| vco
attack_decay_cap | 8 21| slf_cap
enable o| 9 20| slf_res
attack_res |10 19| pitch_voltage
amplitude_res |11 18| vco_res
feedback_res |12 17| vco_cap
|13 OUTPUT 16| vco_voltage
|14 Vcc +5V OUT 15|
+-------------------+
All resistor values in Ohms
All capacitor values in Farads
Use RES_K, RES_M and CAP_U, CAP_N, CAP_P macros in rescap.h to convert
magnitudes, eg. 220k = RES_K(220), 47nF = CAP_N(47)
*****************************************************************************/
#ifndef MAME_SOUND_SN76477_H
#define MAME_SOUND_SN76477_H
#pragma once
#include "machine/rescap.h"
/*****************************************************************************
*
* Interface definition
*
*****************************************************************************/
#define MCFG_SN76477_NOISE_PARAMS(_clock_res, _filter_res, _filter_cap) \
downcast<sn76477_device &>(*device).set_noise_params(_clock_res, _filter_res, _filter_cap);
#define MCFG_SN76477_DECAY_RES(_decay_res) \
downcast<sn76477_device &>(*device).set_decay_res(_decay_res);
#define MCFG_SN76477_ATTACK_PARAMS(_decay_cap, _res) \
downcast<sn76477_device &>(*device).set_attack_params(_decay_cap, _res);
#define MCFG_SN76477_AMP_RES(_amp_res) \
downcast<sn76477_device &>(*device).set_amp_res(_amp_res);
#define MCFG_SN76477_FEEDBACK_RES(_feedback_res) \
downcast<sn76477_device &>(*device).set_feedback_res(_feedback_res);
#define MCFG_SN76477_VCO_PARAMS(_volt, _cap, _res) \
downcast<sn76477_device &>(*device).set_vco_params(_volt, _cap, _res);
#define MCFG_SN76477_PITCH_VOLTAGE(_volt) \
downcast<sn76477_device &>(*device).set_pitch_voltage(_volt);
#define MCFG_SN76477_SLF_PARAMS(_cap, _res) \
downcast<sn76477_device &>(*device).set_slf_params(_cap, _res);
#define MCFG_SN76477_ONESHOT_PARAMS(_cap, _res) \
downcast<sn76477_device &>(*device).set_oneshot_params(_cap, _res);
#define MCFG_SN76477_VCO_MODE(_mode) \
downcast<sn76477_device &>(*device).set_vco_mode(_mode);
#define MCFG_SN76477_MIXER_PARAMS(_a, _b, _c) \
downcast<sn76477_device &>(*device).set_mixer_params(_a, _b, _c);
#define MCFG_SN76477_ENVELOPE_PARAMS(_env1, _env2) \
downcast<sn76477_device &>(*device).set_envelope_params(_env1, _env2);
#define MCFG_SN76477_ENABLE(_enable) \
downcast<sn76477_device &>(*device).set_enable(_enable);
class sn76477_device : public device_t,
public device_sound_interface
{
public:
sn76477_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
void set_noise_params(double clock_res, double filter_res, double filter_cap)
{
m_noise_clock_res = clock_res;
m_noise_filter_res = filter_res;
m_noise_filter_cap = filter_cap;
}
void set_decay_res(double decay_res) { m_decay_res = decay_res; }
void set_attack_params(double decay_cap, double res)
{
m_attack_decay_cap = decay_cap;
m_attack_res = res;
}
void set_amp_res(double amp_res) { m_amplitude_res = amp_res; }
void set_feedback_res(double feedback_res) { m_feedback_res = feedback_res; }
void set_vco_params(double volt, double cap, double res)
{
m_vco_voltage = volt;
m_vco_cap = cap;
m_vco_res = res;
}
void set_pitch_voltage(double volt) { m_pitch_voltage = volt; }
void set_slf_params(double cap, double res)
{
m_slf_cap = cap;
m_slf_res = res;
}
void set_oneshot_params(double cap, double res)
{
m_one_shot_cap = cap;
m_one_shot_res = res;
}
void set_vco_mode(uint32_t mode) { m_vco_mode = mode; }
void set_mixer_params(uint32_t a, uint32_t b, uint32_t c)
{
m_mixer_a = a;
m_mixer_b = b;
m_mixer_c = c;
}
void set_envelope_params(uint32_t env1, uint32_t env2)
{
m_envelope_1 = env1;
m_envelope_2 = env2;
}
void set_enable(uint32_t enable) { m_enable = enable; }
/* these functions take 0 or 1 as a logic input */
WRITE_LINE_MEMBER( enable_w ); /* active LO, 0 = enabled, 1 = disabled */
WRITE_LINE_MEMBER( mixer_a_w );
WRITE_LINE_MEMBER( mixer_b_w );
WRITE_LINE_MEMBER( mixer_c_w );
WRITE_LINE_MEMBER( envelope_1_w );
WRITE_LINE_MEMBER( envelope_2_w );
WRITE_LINE_MEMBER( vco_w ); /* 0 = external, 1 = controlled by SLF */
WRITE_LINE_MEMBER( noise_clock_w ); /* noise clock write, if noise_clock_res = 0 */
/* these functions take a resistor value in Ohms */
void one_shot_res_w(double data);
void slf_res_w(double data);
void vco_res_w(double data);
void noise_clock_res_w(double data); /* = 0 if the noise gen is clocked via noise_clock */
void noise_filter_res_w(double data);
void decay_res_w(double data);
void attack_res_w(double data);
void amplitude_res_w(double data);
void feedback_res_w(double data);
/* these functions take a capacitor value in Farads or the voltage on it in Volts */
static constexpr double EXTERNAL_VOLTAGE_DISCONNECT = -1.0; /* indicates that the voltage is internally computed,
can be used in all the functions that take a
voltage on a capacitor */
void one_shot_cap_w(double data);
void one_shot_cap_voltage_w(double data);
void slf_cap_w(double data);
void slf_cap_voltage_w(double data);
void vco_cap_w(double data);
void vco_cap_voltage_w(double data);
void noise_filter_cap_w(double data);
void noise_filter_cap_voltage_w(double data);
void attack_decay_cap_w(double data);
void attack_decay_cap_voltage_w(double data);
/* these functions take a voltage value in Volts */
void vco_voltage_w(double data);
void pitch_voltage_w(double data);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_stop() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private:
/* chip's external interface */
uint32_t m_enable;
uint32_t m_envelope_mode;
uint32_t m_vco_mode;
uint32_t m_mixer_mode;
double m_one_shot_res;
double m_one_shot_cap;
uint32_t m_one_shot_cap_voltage_ext;
double m_slf_res;
double m_slf_cap;
uint32_t m_slf_cap_voltage_ext;
double m_vco_voltage;
double m_vco_res;
double m_vco_cap;
uint32_t m_vco_cap_voltage_ext;
double m_noise_clock_res;
uint32_t m_noise_clock_ext;
uint32_t m_noise_clock;
double m_noise_filter_res;
double m_noise_filter_cap;
uint32_t m_noise_filter_cap_voltage_ext;
double m_attack_res;
double m_decay_res;
double m_attack_decay_cap;
uint32_t m_attack_decay_cap_voltage_ext;
double m_amplitude_res;
double m_feedback_res;
double m_pitch_voltage;
// internal state
double m_one_shot_cap_voltage; /* voltage on the one-shot cap */
uint32_t m_one_shot_running_ff; /* 1 = one-shot running, 0 = stopped */
double m_slf_cap_voltage; /* voltage on the SLF cap */
uint32_t m_slf_out_ff; /* output of the SLF */
double m_vco_cap_voltage; /* voltage on the VCO cap */
uint32_t m_vco_out_ff; /* output of the VCO */
uint32_t m_vco_alt_pos_edge_ff; /* keeps track of the # of positive edges for VCO Alt envelope */
double m_noise_filter_cap_voltage; /* voltage on the noise filter cap */
uint32_t m_real_noise_bit_ff; /* the current noise bit before filtering */
uint32_t m_filtered_noise_bit_ff; /* the noise bit after filtering */
uint32_t m_noise_gen_count; /* noise freq emulation */
double m_attack_decay_cap_voltage; /* voltage on the attack/decay cap */
uint32_t m_rng; /* current value of the random number generator */
// configured by the drivers and used to setup m_mixer_mode & m_envelope_mode at start
uint32_t m_mixer_a;
uint32_t m_mixer_b;
uint32_t m_mixer_c;
uint32_t m_envelope_1;
uint32_t m_envelope_2;
/* others */
sound_stream *m_channel; /* returned by stream_create() */
int m_our_sample_rate; /* from machine.sample_rate() */
wav_file *m_file; /* handle of the wave file to produce */
double compute_one_shot_cap_charging_rate();
double compute_one_shot_cap_discharging_rate();
double compute_slf_cap_charging_rate();
double compute_slf_cap_discharging_rate();
double compute_vco_cap_charging_discharging_rate();
double compute_vco_duty_cycle();
uint32_t compute_noise_gen_freq();
double compute_noise_filter_cap_charging_rate();
double compute_noise_filter_cap_discharging_rate();
double compute_attack_decay_cap_charging_rate();
double compute_attack_decay_cap_discharging_rate();
double compute_center_to_peak_voltage_out();
void log_enable_line();
void log_mixer_mode();
void log_envelope_mode();
void log_vco_mode();
void log_one_shot_time();
void log_slf_freq();
void log_vco_pitch_voltage();
void log_vco_duty_cycle();
void log_vco_freq();
void log_vco_ext_voltage();
void log_noise_gen_freq();
void log_noise_filter_freq();
void log_attack_time();
void log_decay_time();
void log_voltage_out();
void log_complete_state();
void open_wav_file();
void close_wav_file();
void add_wav_data(int16_t data_l, int16_t data_r);
void intialize_noise();
inline uint32_t generate_next_real_noise_bit();
void state_save_register();
};
DECLARE_DEVICE_TYPE(SN76477, sn76477_device)
#endif // MAME_SOUND_SN76477_H