mame/src/emu/sound/disc_dev.c

1764 lines
55 KiB
C

/************************************************************************
*
* MAME - Discrete sound system emulation library
*
* Written by Keith Wilkins (mame@dysfunction.demon.co.uk)
*
* (c) K.Wilkins 2000
* (c) D.Renaud 2003-2004
*
************************************************************************
*
* DSD_555_ASTBL - NE555 Simulation - Astable mode
* DSD_555_MSTBL - NE555 Simulation - Monostable mode
* DSD_555_CC - NE555 Constant Current VCO
* DSD_555_VCO1 - Op-Amp linear ramp based 555 VCO
* DSD_566 - NE566 Simulation
* DSD_LS624 - 74LS624/629 Simulation
*
************************************************************************
*
* You will notice that the code for a lot of these routines are similar.
* I tried to make a common charging routine, but there are too many
* minor differences that affect each module.
*
************************************************************************/
#define DEFAULT_555_BLEED_R RES_M(10)
/************************************************************************
*
* DSD_555_ASTBL - - 555 Astable simulation
*
* input[0] - Reset value
* input[1] - R1 value
* input[2] - R2 value
* input[3] - C value
* input[4] - Control Voltage value
*
* also passed discrete_555_desc structure
*
* Jan 2004, D Renaud.
************************************************************************/
#define DSD_555_ASTBL__RESET (! DISCRETE_INPUT(0))
#define DSD_555_ASTBL__R1 DISCRETE_INPUT(1)
#define DSD_555_ASTBL__R2 DISCRETE_INPUT(2)
#define DSD_555_ASTBL__C DISCRETE_INPUT(3)
#define DSD_555_ASTBL__CTRLV DISCRETE_INPUT(4)
/* bit mask of the above RC inputs */
#define DSD_555_ASTBL_RC_MASK 0x0e
/* charge/discharge constants */
#define DSD_555_ASTBL_T_RC_BLEED (DEFAULT_555_BLEED_R * DSD_555_ASTBL__C)
/* Use quick charge if specified. */
#define DSD_555_ASTBL_T_RC_CHARGE ((DSD_555_ASTBL__R1 + ((info->options & DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE) ? 0 : DSD_555_ASTBL__R2)) * DSD_555_ASTBL__C)
#define DSD_555_ASTBL_T_RC_DISCHARGE (DSD_555_ASTBL__R2 * DSD_555_ASTBL__C)
DISCRETE_STEP(dsd_555_astbl)
{
DISCRETE_DECLARE_INFO(discrete_555_desc)
int count_f = 0;
int count_r = 0;
double dt; /* change in time */
double x_time = 0; /* time since change happened */
double v_cap = m_cap_voltage; /* Current voltage on capacitor, before dt */
double v_cap_next = 0; /* Voltage on capacitor, after dt */
double v_charge, exponent = 0;
UINT8 flip_flop = m_flip_flop;
UINT8 update_exponent = 0;
/* put commonly used stuff in local variables for speed */
double threshold = m_threshold;
double trigger = m_trigger;
if(DSD_555_ASTBL__RESET)
{
/* We are in RESET */
this->output[0] = 0;
m_flip_flop = 1;
m_cap_voltage = 0;
return;
}
/* Check: if the Control Voltage node is connected. */
if (m_use_ctrlv)
{
/* If CV is less then .25V, the circuit will oscillate way out of range.
* So we will just ignore it when it happens. */
if (DSD_555_ASTBL__CTRLV < .25) return;
/* If it is a node then calculate thresholds based on Control Voltage */
threshold = DSD_555_ASTBL__CTRLV;
trigger = DSD_555_ASTBL__CTRLV / 2.0;
/* Since the thresholds may have changed we need to update the FF */
if (v_cap >= threshold)
{
flip_flop = 0;
count_f++;
}
else
if (v_cap <= trigger)
{
flip_flop = 1;
count_r++;
}
}
/* get the v_charge and update each step if it is a node */
if (m_v_charge_node != NULL)
{
v_charge = *m_v_charge_node;
if (info->options & DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE) v_charge -= 0.5;
}
else
v_charge = m_v_charge;
/* Calculate future capacitor voltage.
* ref@ http://www.physics.rutgers.edu/ugrad/205/capacitance.html
* The formulas from the ref pages have been modified to reflect that we are stepping the change.
* dt = time of sample (1/sample frequency)
* VC = Voltage across capacitor
* VC' = Future voltage across capacitor
* Vc = Voltage change
* Vr = is the voltage across the resistor. For charging it is Vcc - VC. Discharging it is VC - 0.
* R = R1+R2 (for charging) R = R2 for discharging.
* Vc = Vr*(1-exp(-dt/(R*C)))
* VC' = VC + Vc (for charging) VC' = VC - Vc for discharging.
*
* We will also need to calculate the amount of time we overshoot the thresholds
* dt = amount of time we overshot
* Vc = voltage change overshoot
* dt = R*C(log(1/(1-(Vc/Vr))))
*/
dt = this->sample_time();
/* Sometimes a switching network is used to setup the capacitance.
* These may select no capacitor, causing oscillation to stop.
*/
if (DSD_555_ASTBL__C == 0)
{
flip_flop = 1;
/* The voltage goes high because the cap circuit is open. */
v_cap_next = v_charge;
v_cap = v_charge;
m_cap_voltage = 0;
}
else
{
/* Update charge contstants and exponents if nodes changed */
if (m_has_rc_nodes && (DSD_555_ASTBL__R1 != m_last_r1 || DSD_555_ASTBL__C != m_last_c || DSD_555_ASTBL__R2 != m_last_r2))
{
m_t_rc_bleed = DSD_555_ASTBL_T_RC_BLEED;
m_t_rc_charge = DSD_555_ASTBL_T_RC_CHARGE;
m_t_rc_discharge = DSD_555_ASTBL_T_RC_DISCHARGE;
m_exp_bleed = RC_CHARGE_EXP_CLASS(m_t_rc_bleed);
m_exp_charge = RC_CHARGE_EXP_CLASS(m_t_rc_charge);
m_exp_discharge = RC_CHARGE_EXP_CLASS(m_t_rc_discharge);
m_last_r1 = DSD_555_ASTBL__R1;
m_last_r2 = DSD_555_ASTBL__R2;
m_last_c = DSD_555_ASTBL__C;
}
/* Keep looping until all toggling in time sample is used up. */
do
{
if (flip_flop)
{
if (DSD_555_ASTBL__R1 == 0)
{
/* Oscillation disabled because there is no longer any charge resistor. */
/* Bleed the cap due to circuit losses. */
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(m_t_rc_bleed, dt);
else
exponent = m_exp_bleed;
v_cap_next = v_cap - (v_cap * exponent);
dt = 0;
}
else
{
/* Charging */
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(m_t_rc_charge, dt);
else
exponent = m_exp_charge;
v_cap_next = v_cap + ((v_charge - v_cap) * exponent);
dt = 0;
/* has it charged past upper limit? */
if (v_cap_next >= threshold)
{
/* calculate the overshoot time */
dt = m_t_rc_charge * log(1.0 / (1.0 - ((v_cap_next - threshold) / (v_charge - v_cap))));
x_time = dt;
v_cap_next = threshold;
flip_flop = 0;
count_f++;
update_exponent = 1;
}
}
}
else
{
/* Discharging */
if(DSD_555_ASTBL__R2 != 0)
{
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(m_t_rc_discharge, dt);
else
exponent = m_exp_discharge;
v_cap_next = v_cap - (v_cap * exponent);
dt = 0;
}
else
{
/* no discharge resistor so we imediately discharge */
v_cap_next = trigger;
}
/* has it discharged past lower limit? */
if (v_cap_next <= trigger)
{
/* calculate the overshoot time */
if (v_cap_next < trigger)
dt = m_t_rc_discharge * log(1.0 / (1.0 - ((trigger - v_cap_next) / v_cap)));
x_time = dt;
v_cap_next = trigger;
flip_flop = 1;
count_r++;
update_exponent = 1;
}
}
v_cap = v_cap_next;
} while(dt);
m_cap_voltage = v_cap;
}
/* Convert last switch time to a ratio */
x_time = x_time / this->sample_time();
switch (m_output_type)
{
case DISC_555_OUT_SQW:
if (count_f + count_r >= 2)
/* force at least 1 toggle */
this->output[0] = m_flip_flop ? 0 : m_v_out_high;
else
this->output[0] = flip_flop * m_v_out_high;
this->output[0] += m_ac_shift;
break;
case DISC_555_OUT_CAP:
this->output[0] = v_cap;
/* Fake it to AC if needed */
if (m_output_is_ac)
this->output[0] -= threshold * 3.0 /4.0;
break;
case DISC_555_OUT_ENERGY:
if (x_time == 0) x_time = 1.0;
this->output[0] = m_v_out_high * (flip_flop ? x_time : (1.0 - x_time));
this->output[0] += m_ac_shift;
break;
case DISC_555_OUT_LOGIC_X:
this->output[0] = flip_flop + x_time;
break;
case DISC_555_OUT_COUNT_F_X:
this->output[0] = count_f ? count_f + x_time : count_f;
break;
case DISC_555_OUT_COUNT_R_X:
this->output[0] = count_r ? count_r + x_time : count_r;
break;
case DISC_555_OUT_COUNT_F:
this->output[0] = count_f;
break;
case DISC_555_OUT_COUNT_R:
this->output[0] = count_r;
break;
}
m_flip_flop = flip_flop;
}
DISCRETE_RESET(dsd_555_astbl)
{
DISCRETE_DECLARE_INFO(discrete_555_desc)
m_use_ctrlv = (this->input_is_node() >> 4) & 1;
m_output_type = info->options & DISC_555_OUT_MASK;
/* Use the defaults or supplied values. */
m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
/* setup v_charge or node */
m_v_charge_node = m_device->node_output_ptr(info->v_charge);
if (m_v_charge_node == NULL)
{
m_v_charge = (info->v_charge == DEFAULT_555_CHARGE) ? info->v_pos : info->v_charge;
if (info->options & DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE) m_v_charge -= 0.5;
}
if ((DSD_555_ASTBL__CTRLV != -1) && !m_use_ctrlv)
{
/* Setup based on supplied Control Voltage static value */
m_threshold = DSD_555_ASTBL__CTRLV;
m_trigger = DSD_555_ASTBL__CTRLV / 2.0;
}
else
{
/* Setup based on v_pos power source */
m_threshold = info->v_pos * 2.0 / 3.0;
m_trigger = info->v_pos / 3.0;
}
/* optimization if none of the values are nodes */
m_has_rc_nodes = 0;
if (this->input_is_node() & DSD_555_ASTBL_RC_MASK)
m_has_rc_nodes = 1;
else
{
m_t_rc_bleed = DSD_555_ASTBL_T_RC_BLEED;
m_exp_bleed = RC_CHARGE_EXP_CLASS(m_t_rc_bleed);
m_t_rc_charge = DSD_555_ASTBL_T_RC_CHARGE;
m_exp_charge = RC_CHARGE_EXP_CLASS(m_t_rc_charge);
m_t_rc_discharge = DSD_555_ASTBL_T_RC_DISCHARGE;
m_exp_discharge = RC_CHARGE_EXP_CLASS(m_t_rc_discharge);
}
m_output_is_ac = info->options & DISC_555_OUT_AC;
/* Calculate DC shift needed to make squarewave waveform AC */
m_ac_shift = m_output_is_ac ? -m_v_out_high / 2.0 : 0;
m_flip_flop = 1;
m_cap_voltage = 0;
/* Step to set the output */
this->step();
}
/************************************************************************
*
* DSD_555_MSTBL - 555 Monostable simulation
*
* input[0] - Reset value
* input[1] - Trigger input
* input[2] - R2 value
* input[3] - C value
*
* also passed discrete_555_desc structure
*
* Oct 2004, D Renaud.
************************************************************************/
#define DSD_555_MSTBL__RESET (! DISCRETE_INPUT(0))
#define DSD_555_MSTBL__TRIGGER DISCRETE_INPUT(1)
#define DSD_555_MSTBL__R DISCRETE_INPUT(2)
#define DSD_555_MSTBL__C DISCRETE_INPUT(3)
/* bit mask of the above RC inputs */
#define DSD_555_MSTBL_RC_MASK 0x0c
DISCRETE_STEP(dsd_555_mstbl)
{
DISCRETE_DECLARE_INFO(discrete_555_desc)
double v_cap; /* Current voltage on capacitor, before dt */
double x_time = 0; /* time since change happened */
double dt, exponent;
double out = 0;
int trigger = 0;
int trigger_type;
int update_exponent = m_has_rc_nodes;
int flip_flop;
if(UNEXPECTED(DSD_555_MSTBL__RESET))
{
/* We are in RESET */
this->output[0] = 0;
m_flip_flop = 0;
m_cap_voltage = 0;
return;
}
dt = this->sample_time();
flip_flop = m_flip_flop;
trigger_type = info->options;
v_cap = m_cap_voltage;
switch (trigger_type & DSD_555_TRIGGER_TYPE_MASK)
{
case DISC_555_TRIGGER_IS_LOGIC:
trigger = ((int)DSD_555_MSTBL__TRIGGER) ? 0 : 1;
if (UNEXPECTED(trigger))
x_time = 1.0 - DSD_555_MSTBL__TRIGGER;
break;
case DISC_555_TRIGGER_IS_VOLTAGE:
trigger = (int)(DSD_555_MSTBL__TRIGGER < m_trigger);
break;
case DISC_555_TRIGGER_IS_COUNT:
trigger = (int)DSD_555_MSTBL__TRIGGER;
if (UNEXPECTED(trigger))
x_time = DSD_555_MSTBL__TRIGGER - trigger;
break;
}
if (UNEXPECTED(trigger && !flip_flop && x_time != 0))
{
/* adjust sample to after trigger */
update_exponent = 1;
dt *= x_time;
}
x_time = 0;
if ((trigger_type & DISC_555_TRIGGER_DISCHARGES_CAP) && trigger)
m_cap_voltage = 0;
/* Wait for trigger */
if (UNEXPECTED(!flip_flop && trigger))
{
flip_flop = 1;
m_flip_flop = 1;
}
if (flip_flop)
{
/* Sometimes a switching network is used to setup the capacitance.
* These may select 'no' capacitor, causing oscillation to stop.
*/
if (UNEXPECTED(DSD_555_MSTBL__C == 0))
{
/* The trigger voltage goes high because the cap circuit is open.
* and the cap discharges */
v_cap = info->v_pos; /* needed for cap output type */
m_cap_voltage = 0;
if (!trigger)
{
flip_flop = 0;
m_flip_flop = 0;
}
}
else
{
/* Charging */
double v_diff = m_v_charge - v_cap;
if (UNEXPECTED(update_exponent))
exponent = RC_CHARGE_EXP_DT(DSD_555_MSTBL__R * DSD_555_MSTBL__C, dt);
else
exponent = m_exp_charge;
v_cap += v_diff * exponent;
/* Has it charged past upper limit? */
/* If trigger is still enabled, then we keep charging,
* regardless of threshold. */
if (UNEXPECTED((v_cap >= m_threshold) && !trigger))
{
dt = DSD_555_MSTBL__R * DSD_555_MSTBL__C * log(1.0 / (1.0 - ((v_cap - m_threshold) / v_diff)));
x_time = 1.0 - dt / this->sample_time();
v_cap = 0;
flip_flop = 0;
m_flip_flop = 0;
}
m_cap_voltage = v_cap;
}
}
switch (m_output_type)
{
case DISC_555_OUT_SQW:
out = flip_flop * m_v_out_high - m_ac_shift;
break;
case DISC_555_OUT_CAP:
if (x_time > 0)
out = v_cap * x_time;
else
out = v_cap;
out -= m_ac_shift;
break;
case DISC_555_OUT_ENERGY:
if (x_time > 0)
out = m_v_out_high * x_time;
else if (flip_flop)
out = m_v_out_high;
else
out = 0;
out -= m_ac_shift;
break;
}
this->output[0] = out;
}
DISCRETE_RESET(dsd_555_mstbl)
{
DISCRETE_DECLARE_INFO(discrete_555_desc)
m_output_type = info->options & DISC_555_OUT_MASK;
if ((m_output_type == DISC_555_OUT_COUNT_F) || (m_output_type == DISC_555_OUT_COUNT_R))
{
m_device->discrete_log("Invalid Output type in NODE_%d.\n", this->index());
m_output_type = DISC_555_OUT_SQW;
}
/* Use the defaults or supplied values. */
m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
m_v_charge = (info->v_charge == DEFAULT_555_CHARGE) ? info->v_pos : info->v_charge;
/* Setup based on v_pos power source */
m_threshold = info->v_pos * 2.0 / 3.0;
m_trigger = info->v_pos / 3.0;
/* Calculate DC shift needed to make waveform AC */
if (info->options & DISC_555_OUT_AC)
{
if (m_output_type == DISC_555_OUT_CAP)
m_ac_shift = m_threshold * 3.0 /4.0;
else
m_ac_shift = m_v_out_high / 2.0;
}
else
m_ac_shift = 0;
m_trig_is_logic = (info->options & DISC_555_TRIGGER_IS_VOLTAGE) ? 0: 1;
m_trig_discharges_cap = (info->options & DISC_555_TRIGGER_DISCHARGES_CAP) ? 1: 0;
m_flip_flop = 0;
m_cap_voltage = 0;
/* optimization if none of the values are nodes */
m_has_rc_nodes = 0;
if (this->input_is_node() & DSD_555_MSTBL_RC_MASK)
m_has_rc_nodes = 1;
else
m_exp_charge = RC_CHARGE_EXP_CLASS(DSD_555_MSTBL__R * DSD_555_MSTBL__C);
this->output[0] = 0;
}
/************************************************************************
*
* DSD_555_CC - Usage of node_description values
*
* input[0] - Reset input value
* input[1] - Voltage input for Constant current source.
* input[2] - R value to set CC current.
* input[3] - C value
* input[4] - rBias value
* input[5] - rGnd value
* input[6] - rDischarge value
*
* also passed discrete_555_cc_desc structure
*
* Mar 2004, D Renaud.
************************************************************************/
#define DSD_555_CC__RESET (! DISCRETE_INPUT(0))
#define DSD_555_CC__VIN DISCRETE_INPUT(1)
#define DSD_555_CC__R DISCRETE_INPUT(2)
#define DSD_555_CC__C DISCRETE_INPUT(3)
#define DSD_555_CC__RBIAS DISCRETE_INPUT(4)
#define DSD_555_CC__RGND DISCRETE_INPUT(5)
#define DSD_555_CC__RDIS DISCRETE_INPUT(6)
/* bit mask of the above RC inputs not including DSD_555_CC__R */
#define DSD_555_CC_RC_MASK 0x78
/* charge/discharge constants */
#define DSD_555_CC_T_RC_BLEED (DEFAULT_555_BLEED_R * DSD_555_CC__C)
#define DSD_555_CC_T_RC_DISCHARGE_01 (DSD_555_CC__RDIS * DSD_555_CC__C)
#define DSD_555_CC_T_RC_DISCHARGE_NO_I (DSD_555_CC__RGND * DSD_555_CC__C)
#define DSD_555_CC_T_RC_CHARGE (r_charge * DSD_555_CC__C)
#define DSD_555_CC_T_RC_DISCHARGE (r_discharge * DSD_555_CC__C)
DISCRETE_STEP(dsd_555_cc)
{
DISCRETE_DECLARE_INFO(discrete_555_cc_desc)
int count_f = 0;
int count_r = 0;
double i; /* Charging current created by vIn */
double r_charge = 0; /* Equivalent charging resistor */
double r_discharge = 0; /* Equivalent discharging resistor */
double vi = 0; /* Equivalent voltage from current source */
double v_bias = 0; /* Equivalent voltage from bias voltage */
double v = 0; /* Equivalent voltage total from current source and bias circuit if used */
double dt; /* change in time */
double x_time = 0; /* time since change happened */
double t_rc ; /* RC time constant */
double v_cap; /* Current voltage on capacitor, before dt */
double v_cap_next = 0; /* Voltage on capacitor, after dt */
double v_vcharge_limit; /* vIn and the junction voltage limit the max charging voltage from i */
double r_temp; /* play thing */
double exponent;
UINT8 update_exponent, update_t_rc;
UINT8 flip_flop = m_flip_flop;
if (UNEXPECTED(DSD_555_CC__RESET))
{
/* We are in RESET */
this->output[0] = 0;
m_flip_flop = 1;
m_cap_voltage = 0;
return;
}
dt = this->sample_time(); /* Change in time */
v_cap = m_cap_voltage; /* Set to voltage before change */
v_vcharge_limit = DSD_555_CC__VIN + info->v_cc_junction; /* the max v_cap can be and still be charged by i */
/* Calculate charging current */
i = (m_v_cc_source - v_vcharge_limit) / DSD_555_CC__R;
if ( i < 0) i = 0;
if (info->options & DISCRETE_555_CC_TO_CAP)
{
vi = i * DSD_555_CC__RDIS;
}
else
{
switch (m_type) /* see dsd_555_cc_reset for descriptions */
{
case 1:
r_discharge = DSD_555_CC__RDIS;
case 0:
break;
case 3:
r_discharge = RES_2_PARALLEL(DSD_555_CC__RDIS, DSD_555_CC__RGND);
case 2:
r_charge = DSD_555_CC__RGND;
vi = i * r_charge;
break;
case 4:
r_charge = DSD_555_CC__RBIAS;
vi = i * r_charge;
v_bias = info->v_pos;
break;
case 5:
r_charge = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
vi = i * DSD_555_CC__RBIAS;
v_bias = info->v_pos;
r_discharge = DSD_555_CC__RDIS;
break;
case 6:
r_charge = RES_2_PARALLEL(DSD_555_CC__RBIAS, DSD_555_CC__RGND);
vi = i * r_charge;
v_bias = info->v_pos * RES_VOLTAGE_DIVIDER(DSD_555_CC__RGND, DSD_555_CC__RBIAS);
break;
case 7:
r_temp = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
r_charge = RES_2_PARALLEL(r_temp, DSD_555_CC__RGND);
r_temp += DSD_555_CC__RGND;
r_temp = DSD_555_CC__RGND / r_temp; /* now has voltage divider ratio, not resistance */
vi = i * DSD_555_CC__RBIAS * r_temp;
v_bias = info->v_pos * r_temp;
r_discharge = RES_2_PARALLEL(DSD_555_CC__RGND, DSD_555_CC__RDIS);
break;
}
}
/* Keep looping until all toggling in time sample is used up. */
update_t_rc = m_has_rc_nodes;
update_exponent = update_t_rc;
do
{
if (m_type <= 1)
{
/* Standard constant current charge */
if (flip_flop)
{
if (i == 0)
{
/* No charging current, so we have to discharge the cap
* due to cap and circuit losses.
*/
if (update_exponent)
{
t_rc = DSD_555_CC_T_RC_BLEED;
exponent = RC_CHARGE_EXP_DT(t_rc, dt);
}
else
exponent = m_exp_bleed;
v_cap_next = v_cap - (v_cap * exponent);
dt = 0;
}
else
{
/* Charging */
/* iC=C*dv/dt works out to dv=iC*dt/C */
v_cap_next = v_cap + (i * dt / DSD_555_CC__C);
/* Yes, if the cap voltage has reached the max voltage it can,
* and the 555 threshold has not been reached, then oscillation stops.
* This is the way the actual electronics works.
* This is why you never play with the pots after being factory adjusted
* to work in the proper range. */
if (v_cap_next > v_vcharge_limit) v_cap_next = v_vcharge_limit;
dt = 0;
/* has it charged past upper limit? */
if (v_cap_next >= m_threshold)
{
/* calculate the overshoot time */
dt = DSD_555_CC__C * (v_cap_next - m_threshold) / i;
x_time = dt;
v_cap_next = m_threshold;
flip_flop = 0;
count_f++;
update_exponent = 1;
}
}
}
else if (DSD_555_CC__RDIS != 0)
{
/* Discharging */
if (update_t_rc)
t_rc = DSD_555_CC_T_RC_DISCHARGE_01;
else
t_rc = m_t_rc_discharge_01;
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(t_rc, dt);
else
exponent = m_exp_discharge_01;
if (info->options & DISCRETE_555_CC_TO_CAP)
{
/* Asteroids - Special Case */
/* Charging in discharge mode */
/* If the cap voltage is past the current source charging limit
* then only the bias voltage will charge the cap. */
v = (v_cap < v_vcharge_limit) ? vi : v_vcharge_limit;
v_cap_next = v_cap + ((v - v_cap) * exponent);
}
else
{
v_cap_next = v_cap - (v_cap * exponent);
}
dt = 0;
/* has it discharged past lower limit? */
if (v_cap_next <= m_trigger)
{
dt = t_rc * log(1.0 / (1.0 - ((m_trigger - v_cap_next) / v_cap)));
x_time = dt;
v_cap_next = m_trigger;
flip_flop = 1;
count_r++;
update_exponent = 1;
}
}
else /* Immediate discharge. No change in dt. */
{
x_time = dt;
v_cap_next = m_trigger;
flip_flop = 1;
count_r++;
}
}
else
{
/* The constant current gets changed to a voltage due to a load resistor. */
if (flip_flop)
{
if ((i == 0) && (DSD_555_CC__RBIAS == 0))
{
/* No charging current, so we have to discharge the cap
* due to rGnd.
*/
if (update_t_rc)
t_rc = DSD_555_CC_T_RC_DISCHARGE_NO_I;
else
t_rc = m_t_rc_discharge_no_i;
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(t_rc, dt);
else
exponent = m_exp_discharge_no_i;
v_cap_next = v_cap - (v_cap * exponent);
dt = 0;
}
else
{
/* Charging */
/* If the cap voltage is past the current source charging limit
* then only the bias voltage will charge the cap. */
v = v_bias;
if (v_cap < v_vcharge_limit) v += vi;
else if (m_type <= 3) v = v_vcharge_limit;
if (update_t_rc)
t_rc = DSD_555_CC_T_RC_CHARGE;
else
t_rc = m_t_rc_charge;
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(t_rc, dt);
else
exponent = m_exp_charge;
v_cap_next = v_cap + ((v - v_cap) * exponent);
dt = 0;
/* has it charged past upper limit? */
if (v_cap_next >= m_threshold)
{
/* calculate the overshoot time */
dt = t_rc * log(1.0 / (1.0 - ((v_cap_next - m_threshold) / (v - v_cap))));
x_time = dt;
v_cap_next = m_threshold;
flip_flop = 0;
count_f++;
update_exponent = 1;
}
}
}
else /* Discharging */
if (r_discharge)
{
if (update_t_rc)
t_rc = DSD_555_CC_T_RC_DISCHARGE;
else
t_rc = m_t_rc_discharge;
if (update_exponent)
exponent = RC_CHARGE_EXP_DT(t_rc, dt);
else
exponent = m_exp_discharge;
v_cap_next = v_cap - (v_cap * exponent);
dt = 0;
/* has it discharged past lower limit? */
if (v_cap_next <= m_trigger)
{
/* calculate the overshoot time */
dt = t_rc * log(1.0 / (1.0 - ((m_trigger - v_cap_next) / v_cap)));
x_time = dt;
v_cap_next = m_trigger;
flip_flop = 1;
count_r++;
update_exponent = 1;
}
}
else /* Immediate discharge. No change in dt. */
{
x_time = dt;
v_cap_next = m_trigger;
flip_flop = 1;
count_r++;
}
}
v_cap = v_cap_next;
} while(dt);
m_cap_voltage = v_cap;
/* Convert last switch time to a ratio */
x_time = x_time / this->sample_time();
switch (m_output_type)
{
case DISC_555_OUT_SQW:
if (count_f + count_r >= 2)
/* force at least 1 toggle */
this->output[0] = m_flip_flop ? 0 : m_v_out_high;
else
this->output[0] = flip_flop * m_v_out_high;
/* Fake it to AC if needed */
this->output[0] += m_ac_shift;
break;
case DISC_555_OUT_CAP:
this->output[0] = v_cap + m_ac_shift;
break;
case DISC_555_OUT_ENERGY:
if (x_time == 0) x_time = 1.0;
this->output[0] = m_v_out_high * (flip_flop ? x_time : (1.0 - x_time));
this->output[0] += m_ac_shift;
break;
case DISC_555_OUT_LOGIC_X:
this->output[0] = flip_flop + x_time;
break;
case DISC_555_OUT_COUNT_F_X:
this->output[0] = count_f ? count_f + x_time : count_f;
break;
case DISC_555_OUT_COUNT_R_X:
this->output[0] = count_r ? count_r + x_time : count_r;
break;
case DISC_555_OUT_COUNT_F:
this->output[0] = count_f;
break;
case DISC_555_OUT_COUNT_R:
this->output[0] = count_r;
break;
}
m_flip_flop = flip_flop;
}
DISCRETE_RESET(dsd_555_cc)
{
DISCRETE_DECLARE_INFO(discrete_555_cc_desc)
double r_temp, r_discharge = 0, r_charge = 0;
m_flip_flop = 1;
m_cap_voltage = 0;
m_output_type = info->options & DISC_555_OUT_MASK;
/* Use the defaults or supplied values. */
m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
m_v_cc_source = (info->v_cc_source == DEFAULT_555_CC_SOURCE) ? info->v_pos : info->v_cc_source;
/* Setup based on v_pos power source */
m_threshold = info->v_pos * 2.0 / 3.0;
m_trigger = info->v_pos / 3.0;
m_output_is_ac = info->options & DISC_555_OUT_AC;
/* Calculate DC shift needed to make squarewave waveform AC */
m_ac_shift = m_output_is_ac ? -m_v_out_high / 2.0 : 0;
/* There are 8 different types of basic oscillators
* depending on the resistors used. We will determine
* the type of circuit at reset, because the ciruit type
* is constant. See Below.
*/
m_type = (DSD_555_CC__RDIS > 0) | ((DSD_555_CC__RGND > 0) << 1) | ((DSD_555_CC__RBIAS > 0) << 2);
/* optimization if none of the values are nodes */
m_has_rc_nodes = 0;
if (this->input_is_node() & DSD_555_CC_RC_MASK)
m_has_rc_nodes = 1;
else
{
switch (m_type) /* see dsd_555_cc_reset for descriptions */
{
case 1:
r_discharge = DSD_555_CC__RDIS;
case 0:
break;
case 3:
r_discharge = RES_2_PARALLEL(DSD_555_CC__RDIS, DSD_555_CC__RGND);
case 2:
r_charge = DSD_555_CC__RGND;
break;
case 4:
r_charge = DSD_555_CC__RBIAS;
break;
case 5:
r_charge = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
r_discharge = DSD_555_CC__RDIS;
break;
case 6:
r_charge = RES_2_PARALLEL(DSD_555_CC__RBIAS, DSD_555_CC__RGND);
break;
case 7:
r_temp = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
r_charge = RES_2_PARALLEL(r_temp, DSD_555_CC__RGND);
r_discharge = RES_2_PARALLEL(DSD_555_CC__RGND, DSD_555_CC__RDIS);
break;
}
m_exp_bleed = RC_CHARGE_EXP_CLASS(DSD_555_CC_T_RC_BLEED);
m_t_rc_discharge_01 = DSD_555_CC_T_RC_DISCHARGE_01;
m_exp_discharge_01 = RC_CHARGE_EXP_CLASS(m_t_rc_discharge_01);
m_t_rc_discharge_no_i = DSD_555_CC_T_RC_DISCHARGE_NO_I;
m_exp_discharge_no_i = RC_CHARGE_EXP_CLASS(m_t_rc_discharge_no_i);
m_t_rc_charge = DSD_555_CC_T_RC_CHARGE;
m_exp_charge = RC_CHARGE_EXP_CLASS(m_t_rc_charge);
m_t_rc_discharge = DSD_555_CC_T_RC_DISCHARGE;
m_exp_discharge = RC_CHARGE_EXP_CLASS(m_t_rc_discharge);
}
/* Step to set the output */
this->step();
/*
* TYPES:
* Note: These are equivalent circuits shown without the 555 circuitry.
* See the schematic in src\sound\discrete.h for full hookup info.
*
* DISCRETE_555_CC_TO_DISCHARGE_PIN
* When the CC source is connected to the discharge pin, it allows the
* circuit to charge when the 555 is in charge mode. But when in discharge
* mode, the CC source is grounded, disabling it's effect.
*
* [0]
* No resistors. Straight constant current charge of capacitor.
* When there is not any charge current, the cap will bleed off.
* Once the lower threshold(trigger) is reached, the output will
* go high but the cap will continue to discharge due to losses.
* .------+---> cap_voltage CHARGING:
* | | dv (change in voltage) compared to dt (change in time in seconds).
* .---. --- dv = i * dt / C; where i is current in amps and C is capacitance in farads.
* | i | --- C cap_voltage = cap_voltage + dv
* '---' |
* | | DISCHARGING:
* gnd gnd instantaneous
*
* [1]
* Same as type 1 but with rDischarge. rDischarge has no effect on the charge rate because
* of the constant current source i.
* When there is not any charge current, the cap will bleed off.
* Once the lower threshold(trigger) is reached, the output will
* go high but the cap will continue to discharge due to losses.
* .----ZZZ-----+---> cap_voltage CHARGING:
* | rDischarge | dv (change in voltage) compared to dt (change in time in seconds).
* .---. --- dv = i * dt / C; where i is current in amps and C is capacitance in farads.
* | i | --- C cap_voltage = cap_voltage + dv
* '---' |
* | | DISCHARGING:
* gnd gnd thru rDischarge
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !!!!! IMPORTANT NOTE ABOUT TYPES 3 - 7 !!!!!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* From here on in all the circuits have either an rBias or rGnd resistor.
* This converts the constant current into a voltage source.
* So all the remaining circuit types will be converted to this circuit.
* When discharging, rBias is out of the equation because the 555 is grounding the circuit
* after that point.
*
* .------------. Rc Rc is the equivilent circuit resistance.
* | v |----ZZZZ---+---> cap_voltage v is the equivilent circuit voltage.
* | | |
* '------------' --- Then the standard RC charging formula applies.
* | --- C
* | | NOTE: All the following types are converted to Rc and v values.
* gnd gnd
*
* [2]
* When there is not any charge current, the cap will bleed off.
* Once the lower threshold(trigger) is reached, the output will
* go high but the cap will continue to discharge due to rGnd.
* .-------+------+------> cap_voltage CHARGING:
* | | | v = vi = i * rGnd
* .---. --- Z Rc = rGnd
* | i | --- C Z rGnd
* '---' | | DISCHARGING:
* | | | instantaneous
* gnd gnd gnd
*
* [3]
* When there is not any charge current, the cap will bleed off.
* Once the lower threshold(trigger) is reached, the output will
* go high but the cap will continue to discharge due to rGnd.
* .----ZZZ-----+------+------> cap_voltage CHARGING:
* | rDischarge | | v = vi = i * rGnd
* .---. --- Z Rc = rGnd
* | i | --- C Z rGnd
* '---' | | DISCHARGING:
* | | | thru rDischarge || rGnd ( || means in parallel)
* gnd gnd gnd
*
* [4]
* .---ZZZ---+------------+-------------> cap_voltage CHARGING:
* | rBias | | Rc = rBias
* .-------. .---. --- vi = i * rBias
* | vBias | | i | --- C v = vBias + vi
* '-------' '---' |
* | | | DISCHARGING:
* gnd gnd gnd instantaneous
*
* [5]
* .---ZZZ---+----ZZZ-----+-------------> cap_voltage CHARGING:
* | rBias | rDischarge | Rc = rBias + rDischarge
* .-------. .---. --- vi = i * rBias
* | vBias | | i | --- C v = vBias + vi
* '-------' '---' |
* | | | DISCHARGING:
* gnd gnd gnd thru rDischarge
*
* [6]
* .---ZZZ---+------------+------+------> cap_voltage CHARGING:
* | rBias | | | Rc = rBias || rGnd
* .-------. .---. --- Z vi = i * Rc
* | vBias | | i | --- C Z rGnd v = vBias * (rGnd / (rBias + rGnd)) + vi
* '-------' '---' | |
* | | | | DISCHARGING:
* gnd gnd gnd gnd instantaneous
*
* [7]
* .---ZZZ---+----ZZZ-----+------+------> cap_voltage CHARGING:
* | rBias | rDischarge | | Rc = (rBias + rDischarge) || rGnd
* .-------. .---. --- Z vi = i * rBias * (rGnd / (rBias + rDischarge + rGnd))
* | vBias | | i | --- C Z rGnd v = vBias * (rGnd / (rBias + rDischarge + rGnd)) + vi
* '-------' '---' | |
* | | | | DISCHARGING:
* gnd gnd gnd gnd thru rDischarge || rGnd
*/
/*
* DISCRETE_555_CC_TO_CAP
*
* When the CC source is connected to the capacitor, it allows the
* current to charge the cap while it is in discharge mode, slowing the
* discharge. So in charge mode it charges linearly from the constant
* current cource. But when in discharge mode it behaves like circuit
* type 2 above.
* .-------+------+------> cap_voltage CHARGING:
* | | | dv = i * dt / C
* .---. --- Z cap_voltage = cap_voltage + dv
* | i | --- C Z rDischarge
* '---' | | DISCHARGING:
* | | | v = vi = i * rGnd
* gnd gnd discharge Rc = rDischarge
*/
}
/************************************************************************
*
* DSD_555_VCO1 - Usage of node_description values
*
* input[0] - Reset input value
* input[1] - Modulation Voltage (Vin1)
* input[2] - Control Voltage (Vin2)
*
* also passed discrete_5555_vco1_desc structure
*
* Apr 2006, D Renaud.
************************************************************************/
#define DSD_555_VCO1__RESET DISCRETE_INPUT(0) /* reset active low */
#define DSD_555_VCO1__VIN1 DISCRETE_INPUT(1)
#define DSD_555_VCO1__VIN2 DISCRETE_INPUT(2)
DISCRETE_STEP(dsd_555_vco1)
{
DISCRETE_DECLARE_INFO(discrete_555_vco1_desc)
int count_f = 0;
int count_r = 0;
double dt; /* change in time */
double x_time = 0; /* time since change happened */
double v_cap; /* Current voltage on capacitor, before dt */
double v_cap_next = 0; /* Voltage on capacitor, after dt */
dt = this->sample_time(); /* Change in time */
v_cap = m_cap_voltage;
/* Check: if the Control Voltage node is connected. */
if (m_ctrlv_is_node && DSD_555_VCO1__RESET) /* reset active low */
{
/* If CV is less then .25V, the circuit will oscillate way out of range.
* So we will just ignore it when it happens. */
if (DSD_555_VCO1__VIN2 < .25) return;
/* If it is a node then calculate thresholds based on Control Voltage */
m_threshold = DSD_555_VCO1__VIN2;
m_trigger = DSD_555_VCO1__VIN2 / 2.0;
/* Since the thresholds may have changed we need to update the FF */
if (v_cap >= m_threshold)
{
x_time = dt;
m_flip_flop = 0;
count_f++;
}
else
if (v_cap <= m_trigger)
{
x_time = dt;
m_flip_flop = 1;
count_r++;
}
}
/* Keep looping until all toggling in time sample is used up. */
do
{
if (m_flip_flop)
{
/* if we are in reset then toggle f/f and discharge */
if (!DSD_555_VCO1__RESET) /* reset active low */
{
m_flip_flop = 0;
count_f++;
}
else
{
/* Charging */
/* iC=C*dv/dt works out to dv=iC*dt/C */
v_cap_next = v_cap + (m_i_charge * dt / info->c);
dt = 0;
/* has it charged past upper limit? */
if (v_cap_next >= m_threshold)
{
/* calculate the overshoot time */
dt = info->c * (v_cap_next - m_threshold) / m_i_charge;
v_cap = m_threshold;
x_time = dt;
m_flip_flop = 0;
count_f++;
}
}
}
else
{
/* Discharging */
/* iC=C*dv/dt works out to dv=iC*dt/C */
v_cap_next = v_cap - (m_i_discharge * dt / info->c);
/* if we are in reset, then the cap can discharge to 0 */
if (!DSD_555_VCO1__RESET) /* reset active low */
{
if (v_cap_next < 0) v_cap_next = 0;
dt = 0;
}
else
{
/* if we are out of reset and the cap voltage is less then
* the lower threshold, toggle f/f and start charging */
if (v_cap <= m_trigger)
{
if (m_flip_flop == 0)
{
/* don't need to track x_time here */
m_flip_flop = 1;
count_r++;
}
}
else
{
dt = 0;
/* has it discharged past lower limit? */
if (v_cap_next <= m_trigger)
{
/* calculate the overshoot time */
dt = info->c * (v_cap_next - m_trigger) / m_i_discharge;
v_cap = m_trigger;
x_time = dt;
m_flip_flop = 1;
count_r++;
}
}
}
}
} while(dt);
m_cap_voltage = v_cap_next;
/* Convert last switch time to a ratio. No x_time in reset. */
x_time = x_time / this->sample_time();
if (!DSD_555_VCO1__RESET) x_time = 0;
switch (m_output_type)
{
case DISC_555_OUT_SQW:
this->output[0] = m_flip_flop * m_v_out_high + m_ac_shift;
break;
case DISC_555_OUT_CAP:
this->output[0] = v_cap_next;
/* Fake it to AC if needed */
if (m_output_is_ac)
this->output[0] -= m_threshold * 3.0 /4.0;
break;
case DISC_555_OUT_ENERGY:
if (x_time == 0) x_time = 1.0;
this->output[0] = m_v_out_high * (m_flip_flop ? x_time : (1.0 - x_time));
this->output[0] += m_ac_shift;
break;
case DISC_555_OUT_LOGIC_X:
this->output[0] = m_flip_flop + x_time;
break;
case DISC_555_OUT_COUNT_F_X:
this->output[0] = count_f ? count_f + x_time : count_f;
break;
case DISC_555_OUT_COUNT_R_X:
this->output[0] = count_r ? count_r + x_time : count_r;
break;
case DISC_555_OUT_COUNT_F:
this->output[0] = count_f;
break;
case DISC_555_OUT_COUNT_R:
this->output[0] = count_r;
break;
}
}
DISCRETE_RESET(dsd_555_vco1)
{
DISCRETE_DECLARE_INFO(discrete_555_vco1_desc)
double v_ratio_r3, v_ratio_r4_1, r_in_1;
m_output_type = info->options & DISC_555_OUT_MASK;
m_output_is_ac = info->options & DISC_555_OUT_AC;
/* Setup op-amp parameters */
/* The voltage at op-amp +in is always a fixed ratio of the modulation voltage. */
v_ratio_r3 = info->r3 / (info->r2 + info->r3); /* +in voltage */
/* The voltage at op-amp -in is 1 of 2 fixed ratios of the modulation voltage,
* based on the 555 Flip-Flop state. */
/* If the FF is 0, then only R1 is connected allowing the full modulation volatge to pass. */
/* v_ratio_r4_0 = 1 */
/* If the FF is 1, then R1 & R4 make a voltage divider similar to R2 & R3 */
v_ratio_r4_1 = info->r4 / (info->r1 + info->r4); /* -in voltage */
/* the input resistance to the op amp depends on the FF state */
/* r_in_0 = info->r1 when FF = 0 */
r_in_1 = 1.0 / (1.0 / info->r1 + 1.0 / info->r4); /* input resistance when r4 switched in */
/* Now that we know the voltages entering the op amp and the resistance for the
* FF states, we can predetermine the ratios for the charge/discharge currents. */
m_i_discharge = (1 - v_ratio_r3) / info->r1;
m_i_charge = (v_ratio_r3 - v_ratio_r4_1) / r_in_1;
/* the cap starts off discharged */
m_cap_voltage = 0;
/* Setup 555 parameters */
/* There is no charge on the cap so the 555 goes high at init. */
m_flip_flop = 1;
m_ctrlv_is_node = (this->input_is_node() >> 2) & 1;
m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
/* Calculate 555 thresholds.
* If the Control Voltage is a node, then the thresholds will be calculated each step.
* If the Control Voltage is a fixed voltage, then the thresholds will be calculated
* from that. Otherwise we will use thresholds based on v_pos. */
if (!m_ctrlv_is_node && (DSD_555_VCO1__VIN2 != -1))
{
/* Setup based on supplied Control Voltage static value */
m_threshold = DSD_555_VCO1__VIN2;
m_trigger = DSD_555_VCO1__VIN2 / 2.0;
}
else
{
/* Setup based on v_pos power source */
m_threshold = info->v_pos * 2.0 / 3.0;
m_trigger = info->v_pos / 3.0;
}
/* Calculate DC shift needed to make squarewave waveform AC */
m_ac_shift = m_output_is_ac ? -m_v_out_high / 2.0 : 0;
}
/************************************************************************
*
* DSD_566 - Usage of node_description values
*
* Mar 2004, D Renaud. updated Sept 2009
*
* The data sheets for this are no where near correct.
* This simulation is based on the internal schematic and testing of
* a real Signetics IC.
*
* The 566 is a constant current based VCO. If you change R, that affects
* the charge/discharge rate. A constant current source will charge the
* cap linearly. Of course due to the transistors there will be some
* non-linear areas at the ends of the Vmod range. As the Vmod voltage
* drops from Vcharge, the frequency generated increases.
*
* The Triangle (pin 4) output is just a buffered version of the cap
* charge. It is about 1.35 higher then the cap voltage.
* The Square (pin 3) output starts low as the cap voltages rises.
* Once a threshold is reached, the cap starts to discharge, and the
* Square output goes high. The Square high output is about 1V less then
* B+. Unloaded it is .75V less. With a 4.7k pull-down resistor, it
* is 1.06V less. So I will simulate at 1V less. The Square low voltage
* is non-linear so I will use a table. The cap toggle thresholds vary
* depending on B+, so they will be simulated with a table.
*
* The data sheets show Vmod should be no less then 3/4*B+. In reality
* you can go to close to 1/2*B+ before you loose linearity. Below 1/2,
* oscillation stops. When Vmod is 0V to 0.1V less then B+, it also
* looses linearity, and stops oscillating when >= B+. This is because
* there is no voltage difference to create a current source.
*
* The current source is dependant on the voltage difference between B+
* and Vmod. Due to transistor action, it is not 100%, but this formula
* gives a good approximation:
* I = ((B+ - Vmod - 0.1) * 0.95) / R
* You can test the current VS modulation function by using 10k for R
* and replace C with a 10k resistor. Then you can monitor the voltage
* on pin 7 to work out the current. I=V/R. It will start to oscillate
* when in the cap threshold range.
*
* When Vmod drops below the stable range, the current source no longer
* functions properly. Technically this is out of the range specified
* for the IC. Of course old games used this range anyways, so we need
* to know how the real IC behaves. When Vmod drops below the stable range,
* the charge current is stops dropping instead of increasing, while the
* discharge current still functions. This means the frequency generated
* starts to drop as the voltage lowers, instead of the normal increase
* in frequency.
*
************************************************************************/
#define DSD_566__VMOD DISCRETE_INPUT(0)
#define DSD_566__R DISCRETE_INPUT(1)
#define DSD_566__C DISCRETE_INPUT(2)
#define DSD_566__VPOS DISCRETE_INPUT(3)
#define DSD_566__VNEG DISCRETE_INPUT(4)
#define DSD_566__VCHARGE DISCRETE_INPUT(5)
#define DSD_566__OPTIONS DISCRETE_INPUT(6)
static const struct
{
double c_high[6];
double c_low[6];
double sqr_low[6];
double osc_stable[6];
double osc_stop[6];
} ne566 =
{
/* 10 10.5 11 11.5 12 13 14 15 B+ */
{3.364, /*3.784,*/ 4.259, /*4.552,*/ 4.888, 5.384, 5.896, 6.416}, /* c_high */
{1.940, /*2.100,*/ 2.276, /*2.404,*/ 2.580, 2.880, 3.180, 3.488}, /* c_low */
{4.352, /*4.144,*/ 4.080, /*4.260,*/ 4.500, 4.960, 5.456, 5.940}, /* sqr_low */
{4.885, /*5.316,*/ 5.772, /*6.075,*/ 6.335, 6.912, 7.492, 7.945}, /* osc_stable */
{4.495, /*4.895,*/ 5.343, /*5.703,*/ 5.997, 6.507, 7.016, 7.518} /* osc_stop */
};
DISCRETE_STEP(dsd_566)
{
double i = 0; /* Charging current created by vIn */
double i_rise; /* non-linear rise charge current */
double dt; /* change in time */
double x_time = 0;
double v_cap; /* Current voltage on capacitor, before dt */
int count_f = 0, count_r = 0;
dt = this->sample_time(); /* Change in time */
v_cap = m_cap_voltage; /* Set to voltage before change */
/* Calculate charging current if it is in range */
if (EXPECTED(DSD_566__VMOD > m_v_osc_stop))
{
double v_charge = DSD_566__VCHARGE - DSD_566__VMOD - 0.1;
if (v_charge > 0)
{
i = (v_charge * .95) / DSD_566__R;
if (DSD_566__VMOD < m_v_osc_stable)
{
/* no where near correct calculation of non linear range */
i_rise = ((DSD_566__VCHARGE - m_v_osc_stable - 0.1) * .95) / DSD_566__R;
i_rise *= 1.0 - (m_v_osc_stable - DSD_566__VMOD) / (m_v_osc_stable - m_v_osc_stop);
}
else
i_rise = i;
}
else
return;
}
else return;
/* Keep looping until all toggling in this time sample is used up. */
do
{
if (m_flip_flop)
{
/* Discharging */
v_cap -= i * dt / DSD_566__C;
dt = 0;
/* has it discharged past lower limit? */
if (UNEXPECTED(v_cap < m_threshold_low))
{
/* calculate the overshoot time */
dt = DSD_566__C * (m_threshold_low - v_cap) / i;
v_cap = m_threshold_low;
m_flip_flop = 0;
count_f++;
x_time = dt;
}
}
else
{
/* Charging */
/* iC=C*dv/dt works out to dv=iC*dt/C */
v_cap += i_rise * dt / DSD_566__C;
dt = 0;
/* Yes, if the cap voltage has reached the max voltage it can,
* and the 566 threshold has not been reached, then oscillation stops.
* This is the way the actual electronics works.
* This is why you never play with the pots after being factory adjusted
* to work in the proper range. */
if (UNEXPECTED(v_cap > DSD_566__VMOD)) v_cap = DSD_566__VMOD;
/* has it charged past upper limit? */
if (UNEXPECTED(v_cap > m_threshold_high))
{
/* calculate the overshoot time */
dt = DSD_566__C * (v_cap - m_threshold_high) / i;
v_cap = m_threshold_high;
m_flip_flop = 1;
count_r++;
x_time = dt;
}
}
} while(dt);
m_cap_voltage = v_cap;
/* Convert last switch time to a ratio */
x_time /= this->sample_time();
switch (m_out_type)
{
case DISC_566_OUT_SQUARE:
this->output[0] = m_flip_flop ? m_v_sqr_high : m_v_sqr_low;
if (m_fake_ac)
this->output[0] += m_ac_shift;
break;
case DISC_566_OUT_ENERGY:
if (x_time == 0) x_time = 1.0;
this->output[0] = m_v_sqr_low + m_v_sqr_diff * (m_flip_flop ? x_time : (1.0 - x_time));
if (m_fake_ac)
this->output[0] += m_ac_shift;
break;
case DISC_566_OUT_LOGIC:
this->output[0] = m_flip_flop;
break;
case DISC_566_OUT_TRIANGLE:
this->output[0] = v_cap;
if (m_fake_ac)
this->output[0] += m_ac_shift;
break;
case DISC_566_OUT_COUNT_F_X:
this->output[0] = count_f ? count_f + x_time : count_f;
break;
case DISC_566_OUT_COUNT_R_X:
this->output[0] = count_r ? count_r + x_time : count_r;
break;
case DISC_566_OUT_COUNT_F:
this->output[0] = count_f;
break;
case DISC_566_OUT_COUNT_R:
this->output[0] = count_r;
break;
}
}
DISCRETE_RESET(dsd_566)
{
int v_int;
double v_float;
m_out_type = (int)DSD_566__OPTIONS & DISC_566_OUT_MASK;
m_fake_ac = (int)DSD_566__OPTIONS & DISC_566_OUT_AC;
if (DSD_566__VNEG >= DSD_566__VPOS)
fatalerror("[v_neg >= v_pos] in NODE_%d!\n", this->index());
v_float = DSD_566__VPOS - DSD_566__VNEG;
v_int = (int)v_float;
if ( v_float < 10 || v_float > 15 )
fatalerror("v_neg and/or v_pos out of range in NODE_%d\n", this->index());
if ( v_float != v_int )
/* fatal for now. */
fatalerror("Power should be integer in NODE_%d\n", this->index());
m_flip_flop = 0;
m_cap_voltage = 0;
v_int -= 10;
m_threshold_high = ne566.c_high[v_int] + DSD_566__VNEG;
m_threshold_low = ne566.c_low[v_int] + DSD_566__VNEG;
m_v_sqr_high = DSD_566__VPOS - 1;
m_v_sqr_low = ne566.sqr_low[v_int] + DSD_566__VNEG;
m_v_sqr_diff = m_v_sqr_high - m_v_sqr_low;
m_v_osc_stable = ne566.osc_stable[v_int] + DSD_566__VNEG;
m_v_osc_stop = ne566.osc_stop[v_int] + DSD_566__VNEG;
m_ac_shift = 0;
if (m_fake_ac)
{
if (m_out_type == DISC_566_OUT_TRIANGLE)
m_ac_shift = (m_threshold_high - m_threshold_low) / 2 - m_threshold_high;
else
m_ac_shift = m_v_sqr_diff / 2 - m_v_sqr_high;
}
/* Step the output */
this->step();
}
/************************************************************************
*
* DSD_LS624 - Usage of node_description values
*
* Dec 2007, Couriersud based on data sheet
* Oct 2009, complete re-write based on IC testing
************************************************************************/
#define DSD_LS624__ENABLE DISCRETE_INPUT(0)
#define DSD_LS624__VMOD DISCRETE_INPUT(1)
#define DSD_LS624__VRNG DISCRETE_INPUT(2)
#define DSD_LS624__C DISCRETE_INPUT(3)
#define DSD_LS624__R_FREQ_IN DISCRETE_INPUT(4)
#define DSD_LS624__C_FREQ_IN DISCRETE_INPUT(5)
#define DSD_LS624__R_RNG_IN DISCRETE_INPUT(6)
#define DSD_LS624__OUTTYPE DISCRETE_INPUT(7)
#define LS624_R_EXT 600.0 /* as specified in data sheet */
#define LS624_OUT_HIGH 4.5 /* measured */
#define LS624_IN_R RES_K(90) /* measured & 70K + 20k per data sheet */
/*
* The 74LS624 series are constant current based VCOs. The Freq Control voltage
* modulates the current source. The current is created from Rext, which is
* internally fixed at 600 ohms for all devices except the 74LS628 which has
* external connections. The current source linearly discharges the cap voltage.
* The cap starts with 0V charge across it. One side is connected to a fixed voltage
* bias circuit. The other side is charged negatively from the current source until
* a certain low threshold is reached. Once this threshold is reached, the output
* toggles state and the pins on the cap reverse in respect to the charge/bias hookup.
* This starts the one side of the cap to be at bias, and the other side of the cap is
* now at bias + the charge on the cap which is bias - threshold.
* Y = 0; CX1 = bias; CX2 = charge
* Y = 1; CX1 = charge; CX2 = bias
* The Range voltage adjusts the threshold voltage. The higher the Range voltage,
* the lower the threshold voltage, the longer the cap can charge, the lower the frequency.
*
* In a perfect world it would work like this:
* The current is based on the mysterious Rext mentioned in the data sheet.
* I = (VfreqControl * 20k/90k) / Rext
* where Rext = 600 ohms or external Rext on a 74LS628
* The Freq Control has an input impedance of approximately 90k, so any input resistance
* connected to the Freq Control pin works as a voltage divider.
* I = (VfreqControl * 20k/(90k + RfreqControlIn)) / Rext
* That gives us a change in voltage on the cap of
* dV = I / sampleRate / C_inFarads
*
* Unfortunately the chip does not behave linearly do to internal interactions,
* so I have just worked out the formula (using zunzun.com) of FreqControl and
* range to frequency out for a fixed cap value of 0.1uf. Other cap values can just
* scale from that. From the freq, we calculate the time of 1/2 cycle using 1/Freq/2.
* Then just use that to toggle a waveform.
*/
DISCRETE_STEP(dsd_ls624)
{
double x_time = 0;
double freq, t1;
double v_freq_2, v_freq_3, v_freq_4;
double t_used = m_t_used;
double dt = this->sample_time();;
double v_freq = DSD_LS624__VMOD;
double v_rng = DSD_LS624__VRNG;
int count_f = 0, count_r = 0;
/* coefficients */
const double k1 = 1.9904769024796283E+03;
const double k2 = 1.2070059213983407E+03;
const double k3 = 1.3266985579561108E+03;
const double k4 = -1.5500979825922698E+02;
const double k5 = 2.8184536266938172E+00;
const double k6 = -2.3503421582744556E+02;
const double k7 = -3.3836786704527788E+02;
const double k8 = -1.3569136703258670E+02;
const double k9 = 2.9914575453819188E+00;
const double k10 = 1.6855569086173170E+00;
if (UNEXPECTED(DSD_LS624__ENABLE == 0))
return;
/* scale due to input resistance */
v_freq *= m_v_freq_scale;
v_rng *= m_v_rng_scale;
/* apply cap if needed */
if (m_has_freq_in_cap)
{
m_v_cap_freq_in += (v_freq - m_v_cap_freq_in) * m_exponent;
v_freq = m_v_cap_freq_in;
}
/* Polyfunctional3D_model created by zunzun.com using sum of squared absolute error */
v_freq_2 = v_freq * v_freq;
v_freq_3 = v_freq_2 * v_freq;
v_freq_4 = v_freq_3 * v_freq;
freq = k1;
freq += k2 * v_freq;
freq += k3 * v_freq_2;
freq += k4 * v_freq_3;
freq += k5 * v_freq_4;
freq += k6 * v_rng;
freq += k7 * v_rng * v_freq;
freq += k8 * v_rng * v_freq_2;
freq += k9 * v_rng * v_freq_3;
freq += k10 * v_rng * v_freq_4;
freq *= CAP_U(0.1) / DSD_LS624__C;
t1 = 0.5 / freq ;
t_used += this->sample_time();
do
{
dt = 0;
if (t_used > t1)
{
/* calculate the overshoot time */
t_used -= t1;
m_flip_flop ^= 1;
if (m_flip_flop)
count_r++;
else
count_f++;
/* fix up any frequency increase change errors */
while(t_used > this->sample_time())
t_used -= this->sample_time();
x_time = t_used;
dt = t_used;
}
}while(dt);
m_t_used = t_used;
/* Convert last switch time to a ratio */
x_time = x_time / this->sample_time();
switch (m_out_type)
{
case DISC_LS624_OUT_LOGIC_X:
this->output[0] = m_flip_flop + x_time;
break;
case DISC_LS624_OUT_COUNT_F_X:
this->output[0] = count_f ? count_f + x_time : count_f;
break;
case DISC_LS624_OUT_COUNT_R_X:
this->output[0] = count_r ? count_r + x_time : count_r;
break;
case DISC_LS624_OUT_COUNT_F:
this->output[0] = count_f;
break;
case DISC_LS624_OUT_COUNT_R:
this->output[0] = count_r;
break;
case DISC_LS624_OUT_ENERGY:
if (x_time == 0) x_time = 1.0;
this->output[0] = LS624_OUT_HIGH * (m_flip_flop ? x_time : (1.0 - x_time));
break;
case DISC_LS624_OUT_LOGIC:
this->output[0] = m_flip_flop;
break;
case DISC_LS624_OUT_SQUARE:
this->output[0] = m_flip_flop ? LS624_OUT_HIGH : 0;
break;
}
}
DISCRETE_RESET(dsd_ls624)
{
m_out_type = (int)DSD_LS624__OUTTYPE;
m_flip_flop = 0;
m_t_used = 0;
m_v_freq_scale = LS624_IN_R / (DSD_LS624__R_FREQ_IN + LS624_IN_R);
m_v_rng_scale = LS624_IN_R / (DSD_LS624__R_RNG_IN + LS624_IN_R);
if (DSD_LS624__C_FREQ_IN > 0)
{
m_has_freq_in_cap = 1;
m_exponent = RC_CHARGE_EXP_CLASS(RES_2_PARALLEL(DSD_LS624__R_FREQ_IN, LS624_IN_R) * DSD_LS624__C_FREQ_IN);
m_v_cap_freq_in = 0;
}
else
m_has_freq_in_cap = 0;
this->output[0] = 0;
}