From 6127b13ea525eeb5d0b315cbdd02501db525ecfd Mon Sep 17 00:00:00 2001 From: Oliver Stöneberg Date: Wed, 19 Mar 2014 17:40:26 +0000 Subject: renamed disc_*.c to disc_*.inc --- src/emu/sound/disc_wav.inc | 1785 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1785 insertions(+) create mode 100644 src/emu/sound/disc_wav.inc (limited to 'src/emu/sound/disc_wav.inc') diff --git a/src/emu/sound/disc_wav.inc b/src/emu/sound/disc_wav.inc new file mode 100644 index 00000000000..c73e721a754 --- /dev/null +++ b/src/emu/sound/disc_wav.inc @@ -0,0 +1,1785 @@ +/************************************************************************ + * + * MAME - Discrete sound system emulation library + * Written by Keith Wilkins (mame@esplexo.co.uk) + * + * (c) K.Wilkins 2000 + * + ************************************************************************ + * + * DSS_COUNTER - External clock Binary Counter + * DSS_LFSR_NOISE - Linear Feedback Shift Register Noise + * DSS_NOISE - Noise Source - Random source + * DSS_NOTE - Note/tone generator + * DSS_OP_AMP_OSC - Op Amp oscillator circuits + * DSS_SAWTOOTHWAVE - Sawtooth waveform generator + * DSS_SCHMITT_OSC - Schmitt Feedback Oscillator + * DSS_SINEWAVE - Sinewave generator source code + * DSS_SQUAREWAVE - Squarewave generator source code + * DSS_SQUAREWFIX - Squarewave generator - fixed frequency + * DSS_SQUAREWAVE2 - Squarewave generator - by t_on/t_off + * DSS_TRIANGLEWAVE - Triangle waveform generator + * + ************************************************************************/ + + + + + + + +/************************************************************************ + * + * DSS_COUNTER - External clock Binary Counter + * + * input0 - Enable input value + * input1 - Reset input (active high) + * input2 - Clock Input + * input3 - Max count + * input4 - Direction - 0=down, 1=up + * input5 - Reset Value + * input6 - Clock type + * + * Jan 2004, D Renaud. + ************************************************************************/ +#define DSS_COUNTER__ENABLE DISCRETE_INPUT(0) +#define DSS_COUNTER__RESET DISCRETE_INPUT(1) +#define DSS_COUNTER__CLOCK DISCRETE_INPUT(2) +#define DSS_COUNTER__MIN DISCRETE_INPUT(3) +#define DSS_COUNTER__MAX DISCRETE_INPUT(4) +#define DSS_COUNTER__DIR DISCRETE_INPUT(5) +#define DSS_COUNTER__INIT DISCRETE_INPUT(6) +#define DSS_COUNTER__CLOCK_TYPE DISCRETE_INPUT(7) +#define DSS_7492__CLOCK_TYPE DSS_COUNTER__MIN + +static const int disc_7492_count[6] = {0x00, 0x01, 0x02, 0x04, 0x05, 0x06}; + +DISCRETE_STEP(dss_counter) +{ + double cycles; + double ds_clock; + int clock = 0, inc = 0; + UINT32 last_count = m_last_count; /* it is different then output in 7492 */ + double x_time = 0; + UINT32 count = last_count; + + ds_clock = DSS_COUNTER__CLOCK; + if (UNEXPECTED(m_clock_type == DISC_CLK_IS_FREQ)) + { + /* We need to keep clocking the internal clock even if disabled. */ + cycles = (m_t_left + this->sample_time()) * ds_clock; + inc = (int)cycles; + m_t_left = (cycles - inc) / ds_clock; + if (inc) x_time = m_t_left / this->sample_time(); + } + else + { + clock = (int)ds_clock; + /* x_time from input clock */ + x_time = ds_clock - clock; + } + + + /* If reset enabled then set output to the reset value. No x_time in reset. */ + if (UNEXPECTED(DSS_COUNTER__RESET)) + { + m_last_count = (int)DSS_COUNTER__INIT; + set_output(0, (int)DSS_COUNTER__INIT); + return; + } + + /* + * Only count if module is enabled. + * This has the effect of holding the output at it's current value. + */ + if (EXPECTED(DSS_COUNTER__ENABLE)) + { + double v_out; + + switch (m_clock_type) + { + case DISC_CLK_ON_F_EDGE: + case DISC_CLK_ON_R_EDGE: + /* See if the clock has toggled to the proper edge */ + clock = (clock != 0); + if (m_last_clock != clock) + { + m_last_clock = clock; + if (m_clock_type == clock) + { + /* Toggled */ + inc = 1; + } + } + break; + + case DISC_CLK_BY_COUNT: + /* Clock number of times specified. */ + inc = clock; + break; + } + + /* use loops because init is not always min or max */ + if (DSS_COUNTER__DIR) + { + count += inc; + while (count > m_max) + { + count -= m_diff; + } + } + else + { + count -= inc; + while (count < m_min || count > (0xffffffff - inc)) + { + count += m_diff; + } + } + + m_last_count = count; + v_out = m_is_7492 ? disc_7492_count[count] : count; + + if (UNEXPECTED(count != last_count)) + { + /* the x_time is only output if the output changed. */ + switch (m_out_type) + { + case DISC_OUT_HAS_XTIME: + v_out += x_time; + break; + case DISC_OUT_IS_ENERGY: + if (x_time == 0) x_time = 1.0; + v_out = last_count; + if (count > last_count) + v_out += (count - last_count) * x_time; + else + v_out -= (last_count - count) * x_time; + break; + } + } + set_output(0, v_out); + } +} + +DISCRETE_RESET(dss_counter) +{ + if ((int)DSS_COUNTER__CLOCK_TYPE & DISC_COUNTER_IS_7492) + { + m_is_7492 = 1; + m_clock_type = DSS_7492__CLOCK_TYPE; + m_max = 5; + m_min = 0; + m_diff = 6; + } + else + { + m_is_7492 = 0; + m_clock_type = DSS_COUNTER__CLOCK_TYPE; + m_max = DSS_COUNTER__MAX; + m_min = DSS_COUNTER__MIN; + m_diff = m_max - m_min + 1; + } + + + if (!m_is_7492 && (DSS_COUNTER__MAX < DSS_COUNTER__MIN)) + fatalerror("MAX < MIN in NODE_%02d\n", this->index()); + + m_out_type = m_clock_type & DISC_OUT_MASK; + m_clock_type &= DISC_CLK_MASK; + + m_t_left = 0; + m_last_count = 0; + m_last_clock = 0; + set_output(0, DSS_COUNTER__INIT); /* count starts at reset value */ +} + + +/************************************************************************ + * + * DSS_LFSR_NOISE - Usage of node_description values for LFSR noise gen + * + * input0 - Enable input value + * input1 - Register reset + * input2 - Clock Input + * input3 - Amplitude input value + * input4 - Input feed bit + * input5 - Bias + * + * also passed dss_lfsr_context structure + * + ************************************************************************/ +#define DSS_LFSR_NOISE__ENABLE DISCRETE_INPUT(0) +#define DSS_LFSR_NOISE__RESET DISCRETE_INPUT(1) +#define DSS_LFSR_NOISE__CLOCK DISCRETE_INPUT(2) +#define DSS_LFSR_NOISE__AMP DISCRETE_INPUT(3) +#define DSS_LFSR_NOISE__FEED DISCRETE_INPUT(4) +#define DSS_LFSR_NOISE__BIAS DISCRETE_INPUT(5) + +INLINE int dss_lfsr_function(discrete_device *dev, int myfunc, int in0, int in1, int bitmask) +{ + int retval; + + in0 &= bitmask; + in1 &= bitmask; + + switch(myfunc) + { + case DISC_LFSR_XOR: + retval = in0 ^ in1; + break; + case DISC_LFSR_OR: + retval = in0 | in1; + break; + case DISC_LFSR_AND: + retval = in0 & in1; + break; + case DISC_LFSR_XNOR: + retval = in0 ^ in1; + retval = retval ^ bitmask; /* Invert output */ + break; + case DISC_LFSR_NOR: + retval = in0 | in1; + retval = retval ^ bitmask; /* Invert output */ + break; + case DISC_LFSR_NAND: + retval = in0 & in1; + retval = retval ^ bitmask; /* Invert output */ + break; + case DISC_LFSR_IN0: + retval = in0; + break; + case DISC_LFSR_IN1: + retval = in1; + break; + case DISC_LFSR_NOT_IN0: + retval = in0 ^ bitmask; + break; + case DISC_LFSR_NOT_IN1: + retval = in1 ^ bitmask; + break; + case DISC_LFSR_REPLACE: + retval = in0 & ~in1; + retval = retval | in1; + break; + case DISC_LFSR_XOR_INV_IN0: + retval = in0 ^ bitmask; /* invert in0 */ + retval = retval ^ in1; /* xor in1 */ + break; + case DISC_LFSR_XOR_INV_IN1: + retval = in1 ^ bitmask; /* invert in1 */ + retval = retval ^ in0; /* xor in0 */ + break; + default: + dev->discrete_log("dss_lfsr_function - Invalid function type passed"); + retval=0; + break; + } + return retval; +} + + +DISCRETE_STEP(dss_lfsr_noise) +{ + DISCRETE_DECLARE_INFO(discrete_lfsr_desc) + + double cycles; + int clock, inc = 0; + int fb0, fb1, fbresult = 0, noise_feed; + + if (info->clock_type == DISC_CLK_IS_FREQ) + { + /* We need to keep clocking the internal clock even if disabled. */ + cycles = (m_t_left + this->sample_time()) / m_t_clock; + inc = (int)cycles; + m_t_left = (cycles - inc) * m_t_clock; + } + + /* Reset everything if necessary */ + if(((DSS_LFSR_NOISE__RESET == 0) ? 0 : 1) == m_reset_on_high) + { + this->reset(); + return; + } + + switch (info->clock_type) + { + case DISC_CLK_ON_F_EDGE: + case DISC_CLK_ON_R_EDGE: + /* See if the clock has toggled to the proper edge */ + clock = (DSS_LFSR_NOISE__CLOCK != 0); + if (m_last != clock) + { + m_last = clock; + if (info->clock_type == clock) + { + /* Toggled */ + inc = 1; + } + } + break; + + case DISC_CLK_BY_COUNT: + /* Clock number of times specified. */ + inc = (int)DSS_LFSR_NOISE__CLOCK; + break; + } + + if (inc > 0) + { + double v_out; + + noise_feed = (DSS_LFSR_NOISE__FEED ? 0x01 : 0x00); + for (clock = 0; clock < inc; clock++) + { + /* Fetch the last feedback result */ + fbresult = (m_lfsr_reg >> info->bitlength) & 0x01; + + /* Stage 2 feedback combine fbresultNew with infeed bit */ + fbresult = dss_lfsr_function(m_device, info->feedback_function1, fbresult, noise_feed, 0x01); + + /* Stage 3 first we setup where the bit is going to be shifted into */ + fbresult = fbresult * info->feedback_function2_mask; + /* Then we left shift the register, */ + m_lfsr_reg = m_lfsr_reg << 1; + /* Now move the fbresult into the shift register and mask it to the bitlength */ + m_lfsr_reg = dss_lfsr_function(m_device, info->feedback_function2, fbresult, m_lfsr_reg, (1 << info->bitlength) - 1 ); + + /* Now get and store the new feedback result */ + /* Fetch the feedback bits */ + fb0 = (m_lfsr_reg >> info->feedback_bitsel0) & 0x01; + fb1 = (m_lfsr_reg >> info->feedback_bitsel1) & 0x01; + /* Now do the combo on them */ + fbresult = dss_lfsr_function(m_device, info->feedback_function0, fb0, fb1, 0x01); + m_lfsr_reg = dss_lfsr_function(m_device, DISC_LFSR_REPLACE, m_lfsr_reg, fbresult << info->bitlength, (2 << info->bitlength) - 1); + + } + /* Now select the output bit */ + if (m_out_is_f0) + v_out = fbresult & 0x01; + else + v_out = (m_lfsr_reg >> info->output_bit) & 0x01; + + /* Final inversion if required */ + if (m_invert_output) v_out = v_out ? 0 : 1; + + /* Gain stage */ + v_out = v_out ? DSS_LFSR_NOISE__AMP / 2 : -DSS_LFSR_NOISE__AMP / 2; + /* Bias input as required */ + v_out = v_out + DSS_LFSR_NOISE__BIAS; + + set_output(0, v_out); + + /* output the lfsr reg ?*/ + if (m_out_lfsr_reg) + set_output(1, (double) m_lfsr_reg); + + } + if(!DSS_LFSR_NOISE__ENABLE) + { + set_output(0, 0); + } +} + +DISCRETE_RESET(dss_lfsr_noise) +{ + DISCRETE_DECLARE_INFO(discrete_lfsr_desc) + + int fb0 , fb1, fbresult; + double v_out; + + m_reset_on_high = (info->flags & DISC_LFSR_FLAG_RESET_TYPE_H) ? 1 : 0; + m_invert_output = info->flags & DISC_LFSR_FLAG_OUT_INVERT; + m_out_is_f0 = (info->flags & DISC_LFSR_FLAG_OUTPUT_F0) ? 1 : 0; + m_out_lfsr_reg = (info->flags & DISC_LFSR_FLAG_OUTPUT_SR_SN1) ? 1 : 0; + + if ((info->clock_type < DISC_CLK_ON_F_EDGE) || (info->clock_type > DISC_CLK_IS_FREQ)) + m_device->discrete_log("Invalid clock type passed in NODE_%d\n", this->index()); + + m_last = (DSS_COUNTER__CLOCK != 0); + if (info->clock_type == DISC_CLK_IS_FREQ) m_t_clock = 1.0 / DSS_LFSR_NOISE__CLOCK; + m_t_left = 0; + + m_lfsr_reg = info->reset_value; + + /* Now get and store the new feedback result */ + /* Fetch the feedback bits */ + fb0 = (m_lfsr_reg >> info->feedback_bitsel0) & 0x01; + fb1=(m_lfsr_reg >> info->feedback_bitsel1) & 0x01; + /* Now do the combo on them */ + fbresult = dss_lfsr_function(m_device, info->feedback_function0, fb0, fb1, 0x01); + m_lfsr_reg=dss_lfsr_function(m_device, DISC_LFSR_REPLACE, m_lfsr_reg, fbresult << info->bitlength, (2<< info->bitlength ) - 1); + + /* Now select and setup the output bit */ + v_out = (m_lfsr_reg >> info->output_bit) & 0x01; + + /* Final inversion if required */ + if(info->flags & DISC_LFSR_FLAG_OUT_INVERT) v_out = v_out ? 0 : 1; + + /* Gain stage */ + v_out = v_out ? DSS_LFSR_NOISE__AMP / 2 : -DSS_LFSR_NOISE__AMP / 2; + /* Bias input as required */ + v_out += DSS_LFSR_NOISE__BIAS; + + set_output(0, v_out); + set_output(1, 0); +} + + +/************************************************************************ + * + * DSS_NOISE - Usage of node_description values for white nose generator + * + * input0 - Enable input value + * input1 - Noise sample frequency + * input2 - Amplitude input value + * input3 - DC Bias value + * + ************************************************************************/ +#define DSS_NOISE__ENABLE DISCRETE_INPUT(0) +#define DSS_NOISE__FREQ DISCRETE_INPUT(1) +#define DSS_NOISE__AMP DISCRETE_INPUT(2) +#define DSS_NOISE__BIAS DISCRETE_INPUT(3) + +DISCRETE_STEP(dss_noise) +{ + double v_out; + + if(DSS_NOISE__ENABLE) + { + /* Only sample noise on rollover to next cycle */ + if(m_phase > (2.0 * M_PI)) + { + /* GCC's rand returns a RAND_MAX value of 0x7fff */ + int newval = (m_device->machine().rand() & 0x7fff) - 16384; + + /* make sure the peak to peak values are the amplitude */ + v_out = DSS_NOISE__AMP / 2; + if (newval > 0) + v_out *= ((double)newval / 16383); + else + v_out *= ((double)newval / 16384); + + /* Add DC Bias component */ + v_out += DSS_NOISE__BIAS; + set_output(0, v_out); + } + } + else + { + set_output(0, 0); + } + + /* Keep the new phasor in the 2Pi range.*/ + m_phase = fmod(m_phase, 2.0 * M_PI); + + /* The enable input only curtails output, phase rotation still occurs. */ + /* We allow the phase to exceed 2Pi here, so we can tell when to sample the noise. */ + m_phase += ((2.0 * M_PI * DSS_NOISE__FREQ) / this->sample_rate()); +} + + +DISCRETE_RESET(dss_noise) +{ + m_phase=0; + this->step(); +} + + +/************************************************************************ + * + * DSS_NOTE - Note/tone generator + * + * input0 - Enable input value + * input1 - Clock Input + * input2 - data value + * input3 - Max count 1 + * input4 - Max count 2 + * input5 - Clock type + * + * Mar 2004, D Renaud. + ************************************************************************/ + #define DSS_NOTE__ENABLE DISCRETE_INPUT(0) + #define DSS_NOTE__CLOCK DISCRETE_INPUT(1) + #define DSS_NOTE__DATA DISCRETE_INPUT(2) + #define DSS_NOTE__MAX1 DISCRETE_INPUT(3) + #define DSS_NOTE__MAX2 DISCRETE_INPUT(4) + #define DSS_NOTE__CLOCK_TYPE DISCRETE_INPUT(5) + +DISCRETE_STEP(dss_note) +{ + double cycles; + int clock = 0, last_count2, inc = 0; + double x_time = 0; + double v_out; + + if (m_clock_type == DISC_CLK_IS_FREQ) + { + /* We need to keep clocking the internal clock even if disabled. */ + cycles = (m_t_left + this->sample_time()) / m_t_clock; + inc = (int)cycles; + m_t_left = (cycles - inc) * m_t_clock; + if (inc) x_time = m_t_left / this->sample_time(); + } + else + { + /* separate clock info from x_time info. */ + clock = (int)DSS_NOTE__CLOCK; + x_time = DSS_NOTE__CLOCK - clock; + } + + if (DSS_NOTE__ENABLE) + { + last_count2 = m_count2; + + switch (m_clock_type) + { + case DISC_CLK_ON_F_EDGE: + case DISC_CLK_ON_R_EDGE: + /* See if the clock has toggled to the proper edge */ + clock = (clock != 0); + if (m_last != clock) + { + m_last = clock; + if (m_clock_type == clock) + { + /* Toggled */ + inc = 1; + } + } + break; + + case DISC_CLK_BY_COUNT: + /* Clock number of times specified. */ + inc = clock; + break; + } + + /* Count output as long as the data loaded is not already equal to max 1 count. */ + if (DSS_NOTE__DATA != DSS_NOTE__MAX1) + { + for (clock = 0; clock < inc; clock++) + { + m_count1++; + if (m_count1 > m_max1) + { + /* Max 1 count reached. Load Data into counter. */ + m_count1 = (int)DSS_NOTE__DATA; + m_count2 += 1; + if (m_count2 > m_max2) m_count2 = 0; + } + } + } + + v_out = m_count2; + if (m_count2 != last_count2) + { + /* the x_time is only output if the output changed. */ + switch (m_out_type) + { + case DISC_OUT_IS_ENERGY: + if (x_time == 0) x_time = 1.0; + v_out = last_count2; + if (m_count2 > last_count2) + v_out += (m_count2 - last_count2) * x_time; + else + v_out -= (last_count2 - m_count2) * x_time; + break; + case DISC_OUT_HAS_XTIME: + v_out += x_time; + break; + } + } + set_output(0, v_out); + } + else + set_output(0, 0); +} + +DISCRETE_RESET(dss_note) +{ + m_clock_type = (int)DSS_NOTE__CLOCK_TYPE & DISC_CLK_MASK; + m_out_type = (int)DSS_NOTE__CLOCK_TYPE & DISC_OUT_MASK; + + m_last = (DSS_NOTE__CLOCK != 0); + m_t_left = 0; + m_t_clock = 1.0 / DSS_NOTE__CLOCK; + + m_count1 = (int)DSS_NOTE__DATA; + m_count2 = 0; + m_max1 = (int)DSS_NOTE__MAX1; + m_max2 = (int)DSS_NOTE__MAX2; + set_output(0, 0); +} + +/************************************************************************ + * + * DSS_OP_AMP_OSC - Op Amp Oscillators + * + * input0 - Enable input value + * input1 - vMod1 (if needed) + * input2 - vMod2 (if needed) + * + * also passed discrete_op_amp_osc_info structure + * + * Mar 2004, D Renaud. + ************************************************************************/ +#define DSS_OP_AMP_OSC__ENABLE DISCRETE_INPUT(0) +#define DSS_OP_AMP_OSC__VMOD1 DISCRETE_INPUT(1) +#define DSS_OP_AMP_OSC__VMOD2 DISCRETE_INPUT(2) + +/* The inputs on a norton op-amp are (info->vP - OP_AMP_NORTON_VBE) */ +/* which is the same as the output high voltage. We will define them */ +/* the same to save a calculation step */ +#define DSS_OP_AMP_OSC_NORTON_VP_IN m_v_out_high + +DISCRETE_STEP(dss_op_amp_osc) +{ + DISCRETE_DECLARE_INFO(discrete_op_amp_osc_info) + + double i = 0; /* Charging current created by vIn */ + double v = 0; /* all input voltages mixed */ + double dt; /* change in time */ + double v_cap; /* Current voltage on capacitor, before dt */ + double v_cap_next = 0; /* Voltage on capacitor, after dt */ + double charge[2] = {0}; + double x_time = 0; /* time since change happened */ + double exponent; + UINT8 force_charge = 0; + UINT8 enable = DSS_OP_AMP_OSC__ENABLE; + UINT8 update_exponent = 0; + UINT8 flip_flop = m_flip_flop; + int count_f = 0; + int count_r = 0; + + double v_out = 0; + + dt = this->sample_time(); /* Change in time */ + v_cap = m_v_cap; /* Set to voltage before change */ + + /* work out the charge currents/voltages. */ + switch (m_type) + { + case DISC_OP_AMP_OSCILLATOR_VCO_1: + /* Work out the charge rates. */ + /* i is not a current. It is being used as a temp variable. */ + i = DSS_OP_AMP_OSC__VMOD1 * m_temp1; + charge[0] = (DSS_OP_AMP_OSC__VMOD1 - i) / info->r1; + charge[1] = (i - (DSS_OP_AMP_OSC__VMOD1 * m_temp2)) / m_temp3; + break; + + case DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON: + { + /* resistors can be nodes, so everything needs updating */ + double i1, i2; + /* add in enable current if using real enable */ + if (m_has_enable) + { + if (enable) + i = m_i_enable; + enable = 1; + } + /* Work out the charge rates. */ + charge[0] = DSS_OP_AMP_OSC_NORTON_VP_IN / *m_r[1-1] - i; + charge[1] = (m_v_out_high - OP_AMP_NORTON_VBE) / *m_r[2-1] - charge[0]; + /* Work out the Inverting Schmitt thresholds. */ + i1 = DSS_OP_AMP_OSC_NORTON_VP_IN / *m_r[5-1]; + i2 = (0.0 - OP_AMP_NORTON_VBE) / *m_r[4-1]; + m_threshold_low = (i1 + i2) * *m_r[3-1] + OP_AMP_NORTON_VBE; + i2 = (m_v_out_high - OP_AMP_NORTON_VBE) / *m_r[4-1]; + m_threshold_high = (i1 + i2) * *m_r[3-1] + OP_AMP_NORTON_VBE; + break; + } + + case DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON: + /* Millman the input voltages. */ + if (info->r7 == 0) + { + /* No r7 means that the modulation circuit is fed directly into the circuit. */ + v = DSS_OP_AMP_OSC__VMOD1; + } + else + { + /* we need to mix any bias and all modulation voltages together. */ + i = m_i_fixed; + i += DSS_OP_AMP_OSC__VMOD1 / info->r7; + if (info->r8 != 0) + i += DSS_OP_AMP_OSC__VMOD2 / info->r8; + v = i * m_r_total; + } + + /* Work out the charge rates. */ + v -= OP_AMP_NORTON_VBE; + charge[0] = v / info->r1; + charge[1] = v / info->r2 - charge[0]; + + /* use the real enable circuit */ + force_charge = !enable; + enable = 1; + break; + + case DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON: + /* Work out the charge rates. */ + i = DSS_OP_AMP_OSC__VMOD1 / info->r1; + charge[0] = i - m_temp1; + charge[1] = m_temp2 - i; + /* if the negative pin current is less then the positive pin current, */ + /* then the osc is disabled and the cap keeps charging */ + if (charge[0] < 0) + { + force_charge = 1; + charge[0] *= -1; + } + break; + + case DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON: + /* start with fixed bias */ + charge[0] = m_i_fixed; + /* add in enable current if using real enable */ + if (m_has_enable) + { + if (enable) + charge[0] -= m_i_enable; + enable = 1; + } + /* we need to mix any bias and all modulation voltages together. */ + v = DSS_OP_AMP_OSC__VMOD1 - OP_AMP_NORTON_VBE; + if (v < 0) v = 0; + charge[0] += v / info->r1; + if (info->r6 != 0) + { + v = DSS_OP_AMP_OSC__VMOD2 - OP_AMP_NORTON_VBE; + charge[0] += v / info->r6; + } + charge[1] = m_temp1 - charge[0]; + break; + } + + if (!enable) + { + /* we will just output 0 for oscillators that have no real enable. */ + set_output(0, 0); + return; + } + + /* Keep looping until all toggling in time sample is used up. */ + do + { + if (m_is_linear_charge) + { + if ((flip_flop ^ m_flip_flop_xor) || force_charge) + { + /* Charging */ + /* iC=C*dv/dt works out to dv=iC*dt/C */ + v_cap_next = v_cap + (charge[1] * dt / info->c); + dt = 0; + + /* has it charged past upper limit? */ + if (v_cap_next > m_threshold_high) + { + flip_flop = m_flip_flop_xor; + if (flip_flop) + count_r++; + else + count_f++; + if (force_charge) + { + /* we need to keep charging the cap to the max thereby disabling the circuit */ + if (v_cap_next > m_v_out_high) + v_cap_next = m_v_out_high; + } + else + { + /* calculate the overshoot time */ + dt = info->c * (v_cap_next - m_threshold_high) / charge[1]; + x_time = dt; + v_cap_next = m_threshold_high; + } + } + } + else + { + /* Discharging */ + v_cap_next = v_cap - (charge[0] * dt / info->c); + dt = 0; + + /* has it discharged past lower limit? */ + if (v_cap_next < m_threshold_low) + { + flip_flop = !m_flip_flop_xor; + if (flip_flop) + count_r++; + else + count_f++; + /* calculate the overshoot time */ + dt = info->c * (m_threshold_low - v_cap_next) / charge[0]; + x_time = dt; + v_cap_next = m_threshold_low; + } + } + } + else /* non-linear charge */ + { + if (update_exponent) + exponent = RC_CHARGE_EXP_DT(m_charge_rc[flip_flop], dt); + else + exponent = m_charge_exp[flip_flop]; + + v_cap_next = v_cap + ((m_charge_v[flip_flop] - v_cap) * exponent); + dt = 0; + + if (flip_flop) + { + /* Has it charged past upper limit? */ + if (v_cap_next > m_threshold_high) + { + dt = m_charge_rc[1] * log(1.0 / (1.0 - ((v_cap_next - m_threshold_high) / (m_v_out_high - v_cap)))); + x_time = dt; + v_cap_next = m_threshold_high; + flip_flop = 0; + count_f++; + update_exponent = 1; + } + } + else + { + /* has it discharged past lower limit? */ + if (v_cap_next < m_threshold_low) + { + dt = m_charge_rc[0] * log(1.0 / (1.0 - ((m_threshold_low - v_cap_next) / v_cap))); + x_time = dt; + v_cap_next = m_threshold_low; + flip_flop = 1; + count_r++; + update_exponent = 1; + } + } + } + v_cap = v_cap_next; + } while(dt); + if (v_cap > m_v_out_high) + v_cap = m_v_out_high; + if (v_cap < 0) + v_cap = 0; + m_v_cap = v_cap; + + x_time = dt / this->sample_time(); + + switch (m_output_type) + { + case DISC_OP_AMP_OSCILLATOR_OUT_CAP: + v_out = v_cap; + break; + case DISC_OP_AMP_OSCILLATOR_OUT_ENERGY: + if (x_time == 0) x_time = 1.0; + v_out = m_v_out_high * (flip_flop ? x_time : (1.0 - x_time)); + break; + case DISC_OP_AMP_OSCILLATOR_OUT_SQW: + if (count_f + count_r >= 2) + /* force at least 1 toggle */ + v_out = m_flip_flop ? 0 : m_v_out_high; + else + v_out = flip_flop * m_v_out_high; + break; + case DISC_OP_AMP_OSCILLATOR_OUT_COUNT_F_X: + v_out = count_f ? count_f + x_time : count_f; + break; + case DISC_OP_AMP_OSCILLATOR_OUT_COUNT_R_X: + v_out = count_r ? count_r + x_time : count_r; + break; + case DISC_OP_AMP_OSCILLATOR_OUT_LOGIC_X: + v_out = m_flip_flop + x_time; + break; + } + set_output(0, v_out); + m_flip_flop = flip_flop; +} + +#define DIODE_DROP 0.7 + +DISCRETE_RESET(dss_op_amp_osc) +{ + DISCRETE_DECLARE_INFO(discrete_op_amp_osc_info) + + const double *r_info_ptr; + int loop; + + double i1 = 0; /* inverting input current */ + double i2 = 0; /* non-inverting input current */ + + /* link to resistor static or node values */ + r_info_ptr = &info->r1; + for (loop = 0; loop < 8; loop ++) + { + m_r[loop] = m_device->node_output_ptr(*r_info_ptr); + if (m_r[loop] == NULL) + m_r[loop] = r_info_ptr; + r_info_ptr++; + } + + m_is_linear_charge = 1; + m_output_type = info->type & DISC_OP_AMP_OSCILLATOR_OUT_MASK; + m_type = info->type & DISC_OP_AMP_OSCILLATOR_TYPE_MASK; + m_charge_rc[0] = 0; + m_charge_rc[1] = 0; + m_charge_v[0] = 0; + m_charge_v[1] = 0; + m_i_fixed = 0; + m_has_enable = 0; + + switch (m_type) + { + case DISC_OP_AMP_OSCILLATOR_VCO_1: + /* The charge rates vary depending on vMod so they are not precalculated. */ + /* Charges while FlipFlop High */ + m_flip_flop_xor = 0; + /* Work out the Non-inverting Schmitt thresholds. */ + m_temp1 = (info->vP / 2) / info->r4; + m_temp2 = (info->vP - OP_AMP_VP_RAIL_OFFSET) / info->r3; + m_temp3 = 1.0 / (1.0 / info->r3 + 1.0 / info->r4); + m_threshold_low = m_temp1 * m_temp3; + m_threshold_high = (m_temp1 + m_temp2) * m_temp3; + /* There is no charge on the cap so the schmitt goes high at init. */ + m_flip_flop = 1; + /* Setup some commonly used stuff */ + m_temp1 = info->r5 / (info->r2 + info->r5); /* voltage ratio across r5 */ + m_temp2 = info->r6 / (info->r1 + info->r6); /* voltage ratio across r6 */ + m_temp3 = 1.0 / (1.0 / info->r1 + 1.0 / info->r6); /* input resistance when r6 switched in */ + break; + + case DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON: + /* Charges while FlipFlop High */ + m_flip_flop_xor = 0; + /* There is no charge on the cap so the schmitt inverter goes high at init. */ + m_flip_flop = 1; + /* setup current if using real enable */ + if (info->r6 > 0) + { + m_has_enable = 1; + m_i_enable = (info->vP - OP_AMP_NORTON_VBE) / (info->r6 + RES_K(1)); + } + break; + + case DISC_OP_AMP_OSCILLATOR_2 | DISC_OP_AMP_IS_NORTON: + m_is_linear_charge = 0; + /* First calculate the parallel charge resistors and volatges. */ + /* We can cheat and just calcuate the charges in the working area. */ + /* The thresholds are well past the effect of the voltage drop */ + /* and the component tolerances far exceed the .5V charge difference */ + if (info->r1 != 0) + { + m_charge_rc[0] = 1.0 / info->r1; + m_charge_rc[1] = 1.0 / info->r1; + m_charge_v[1] = (info->vP - OP_AMP_NORTON_VBE) / info->r1; + } + if (info->r5 != 0) + { + m_charge_rc[0] += 1.0 / info->r5; + m_charge_v[0] = DIODE_DROP / info->r5; + } + if (info->r6 != 0) + { + m_charge_rc[1] += 1.0 / info->r6; + m_charge_v[1] += (info->vP - OP_AMP_NORTON_VBE - DIODE_DROP) / info->r6; + } + m_charge_rc[0] += 1.0 / info->r2; + m_charge_rc[0] = 1.0 / m_charge_rc[0]; + m_charge_v[0] += OP_AMP_NORTON_VBE / info->r2; + m_charge_v[0] *= m_charge_rc[0]; + m_charge_rc[1] += 1.0 / info->r2; + m_charge_rc[1] = 1.0 / m_charge_rc[1]; + m_charge_v[1] += OP_AMP_NORTON_VBE / info->r2; + m_charge_v[1] *= m_charge_rc[1]; + + m_charge_rc[0] *= info->c; + m_charge_rc[1] *= info->c; + m_charge_exp[0] = RC_CHARGE_EXP(m_charge_rc[0]); + m_charge_exp[1] = RC_CHARGE_EXP(m_charge_rc[1]); + m_threshold_low = (info->vP - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_high = m_threshold_low + (info->vP - 2 * OP_AMP_NORTON_VBE) / info->r3;; + m_threshold_low = m_threshold_low * info->r2 + OP_AMP_NORTON_VBE; + m_threshold_high = m_threshold_high * info->r2 + OP_AMP_NORTON_VBE; + + /* There is no charge on the cap so the schmitt inverter goes high at init. */ + m_flip_flop = 1; + break; + + case DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON: + /* Charges while FlipFlop Low */ + m_flip_flop_xor = 1; + /* There is no charge on the cap so the schmitt goes low at init. */ + m_flip_flop = 0; + /* The charge rates vary depending on vMod so they are not precalculated. */ + /* But we can precalculate the fixed currents. */ + if (info->r6 != 0) m_i_fixed += info->vP / info->r6; + m_i_fixed += OP_AMP_NORTON_VBE / info->r1; + m_i_fixed += OP_AMP_NORTON_VBE / info->r2; + /* Work out the input resistance to be used later to calculate the Millman voltage. */ + m_r_total = 1.0 / info->r1 + 1.0 / info->r2 + 1.0 / info->r7; + if (info->r6) m_r_total += 1.0 / info->r6; + if (info->r8) m_r_total += 1.0 / info->r8; + m_r_total = 1.0 / m_r_total; + /* Work out the Non-inverting Schmitt thresholds. */ + i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5; + i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_low = (i1 - i2) * info->r3 + OP_AMP_NORTON_VBE; + i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_high = (i1 - i2) * info->r3 + OP_AMP_NORTON_VBE; + break; + + case DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON: + /* Charges while FlipFlop High */ + m_flip_flop_xor = 0; + /* There is no charge on the cap so the schmitt inverter goes high at init. */ + m_flip_flop = 1; + /* Work out the charge rates. */ + m_temp1 = (info->vP - OP_AMP_NORTON_VBE) / info->r2; + m_temp2 = (info->vP - OP_AMP_NORTON_VBE) * (1.0 / info->r2 + 1.0 / info->r6); + /* Work out the Inverting Schmitt thresholds. */ + i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5; + i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_low = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE; + i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_high = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE; + break; + + case DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON: + /* Charges while FlipFlop High */ + m_flip_flop_xor = 0; + /* There is no charge on the cap so the schmitt inverter goes high at init. */ + m_flip_flop = 1; + /* setup current if using real enable */ + if (info->r8 > 0) + { + m_has_enable = 1; + m_i_enable = (info->vP - OP_AMP_NORTON_VBE) / (info->r8 + RES_K(1)); + } + /* Work out the charge rates. */ + /* The charge rates vary depending on vMod so they are not precalculated. */ + /* But we can precalculate the fixed currents. */ + if (info->r7 != 0) m_i_fixed = (info->vP - OP_AMP_NORTON_VBE) / info->r7; + m_temp1 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r2; + /* Work out the Inverting Schmitt thresholds. */ + i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5; + i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_low = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE; + i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4; + m_threshold_high = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE; + break; + } + + m_v_out_high = info->vP - ((m_type & DISC_OP_AMP_IS_NORTON) ? OP_AMP_NORTON_VBE : OP_AMP_VP_RAIL_OFFSET); + m_v_cap = 0; + + this->step(); +} + + +/************************************************************************ + * + * DSS_SAWTOOTHWAVE - Usage of node_description values for step function + * + * input0 - Enable input value + * input1 - Frequency input value + * input2 - Amplitde input value + * input3 - DC Bias Value + * input4 - Gradient + * input5 - Initial Phase + * + ************************************************************************/ +#define DSS_SAWTOOTHWAVE__ENABLE DISCRETE_INPUT(0) +#define DSS_SAWTOOTHWAVE__FREQ DISCRETE_INPUT(1) +#define DSS_SAWTOOTHWAVE__AMP DISCRETE_INPUT(2) +#define DSS_SAWTOOTHWAVE__BIAS DISCRETE_INPUT(3) +#define DSS_SAWTOOTHWAVE__GRAD DISCRETE_INPUT(4) +#define DSS_SAWTOOTHWAVE__PHASE DISCRETE_INPUT(5) + +DISCRETE_STEP(dss_sawtoothwave) +{ + double v_out; + + if(DSS_SAWTOOTHWAVE__ENABLE) + { + v_out = (m_type == 0) ? m_phase * (DSS_SAWTOOTHWAVE__AMP / (2.0 * M_PI)) : DSS_SAWTOOTHWAVE__AMP - (m_phase * (DSS_SAWTOOTHWAVE__AMP / (2.0 * M_PI))); + v_out -= DSS_SAWTOOTHWAVE__AMP / 2.0; + /* Add DC Bias component */ + v_out = v_out + DSS_SAWTOOTHWAVE__BIAS; + } + else + { + v_out = 0; + } + set_output(0, v_out); + + /* Work out the phase step based on phase/freq & sample rate */ + /* The enable input only curtails output, phase rotation */ + /* still occurs */ + /* phase step = 2Pi/(output period/sample period) */ + /* boils out to */ + /* phase step = (2Pi*output freq)/sample freq) */ + /* Also keep the new phasor in the 2Pi range. */ + m_phase = fmod((m_phase + ((2.0 * M_PI * DSS_SAWTOOTHWAVE__FREQ) / this->sample_rate())), 2.0 * M_PI); +} + +DISCRETE_RESET(dss_sawtoothwave) +{ + double start; + + /* Establish starting phase, convert from degrees to radians */ + start = (DSS_SAWTOOTHWAVE__PHASE / 360.0) * (2.0 * M_PI); + /* Make sure its always mod 2Pi */ + m_phase = fmod(start, 2.0 * M_PI); + + /* Invert gradient depending on sawtooth type /|/|/|/|/| or |\|\|\|\|\ */ + m_type = (DSS_SAWTOOTHWAVE__GRAD) ? 1 : 0; + + /* Step the node to set the output */ + this->step(); +} + + +/************************************************************************ + * + * DSS_SCHMITT_OSC - Schmitt feedback oscillator + * + * input0 - Enable input value + * input1 - Vin + * input2 - Amplitude + * + * also passed discrete_schmitt_osc_disc structure + * + * Mar 2004, D Renaud. + ************************************************************************/ +#define DSS_SCHMITT_OSC__ENABLE (int)DISCRETE_INPUT(0) +#define DSS_SCHMITT_OSC__VIN DISCRETE_INPUT(1) +#define DSS_SCHMITT_OSC__AMP DISCRETE_INPUT(2) + +DISCRETE_STEP(dss_schmitt_osc) +{ + DISCRETE_DECLARE_INFO(discrete_schmitt_osc_desc) + + double supply, v_cap, new_vCap, t, exponent; + double v_out = 0; + + /* We will always oscillate. The enable just affects the output. */ + v_cap = m_v_cap; + exponent = m_exponent; + + /* Keep looping until all toggling in time sample is used up. */ + do + { + t = 0; + /* The charging voltage to the cap is the sum of the input voltage and the gate + * output voltage in the ratios determined by their resistors in a divider network. + * The input voltage is selectable as straight voltage in or logic level that will + * use vGate as its voltage. Note that ration_in is just the ratio of the total + * voltage and needs to be multipled by the input voltage. ratio_feedback has + * already been multiplied by vGate to save time because that voltage never changes. */ + supply = m_input_is_voltage ? m_ration_in * DSS_SCHMITT_OSC__VIN : (DSS_SCHMITT_OSC__VIN ? m_ration_in * info->vGate : 0); + supply += (m_state ? m_ratio_feedback : 0); + new_vCap = v_cap + ((supply - v_cap) * exponent); + if (m_state) + { + /* Charging */ + /* has it charged past upper limit? */ + if (new_vCap > info->trshRise) + { + /* calculate the overshoot time */ + t = m_rc * log(1.0 / (1.0 - ((new_vCap - info->trshRise) / (info->vGate - v_cap)))); + /* calculate new exponent because of reduced time */ + exponent = RC_CHARGE_EXP_DT(m_rc, t); + v_cap = new_vCap = info->trshRise; + m_state = 0; + } + } + else + { + /* Discharging */ + /* has it discharged past lower limit? */ + if (new_vCap < info->trshFall) + { + /* calculate the overshoot time */ + t = m_rc * log(1.0 / (1.0 - ((info->trshFall - new_vCap) / v_cap))); + /* calculate new exponent because of reduced time */ + exponent = RC_CHARGE_EXP_DT(m_rc, t); + v_cap = new_vCap = info->trshFall; + m_state = 1; + } + } + } while(t); + + m_v_cap = new_vCap; + + switch (m_enable_type) + { + case DISC_SCHMITT_OSC_ENAB_IS_AND: + v_out = DSS_SCHMITT_OSC__ENABLE && m_state; + break; + case DISC_SCHMITT_OSC_ENAB_IS_NAND: + v_out = !(DSS_SCHMITT_OSC__ENABLE && m_state); + break; + case DISC_SCHMITT_OSC_ENAB_IS_OR: + v_out = DSS_SCHMITT_OSC__ENABLE || m_state; + break; + case DISC_SCHMITT_OSC_ENAB_IS_NOR: + v_out = !(DSS_SCHMITT_OSC__ENABLE || m_state); + break; + } + v_out *= DSS_SCHMITT_OSC__AMP; + set_output(0, v_out); +} + +DISCRETE_RESET(dss_schmitt_osc) +{ + DISCRETE_DECLARE_INFO(discrete_schmitt_osc_desc) + + double rSource; + + m_enable_type = info->options & DISC_SCHMITT_OSC_ENAB_MASK; + m_input_is_voltage = (info->options & DISC_SCHMITT_OSC_IN_IS_VOLTAGE) ? 1 : 0; + + /* The 2 resistors make a voltage divider, so their ratios add together + * to make the charging voltage. */ + m_ration_in = info->rFeedback / (info->rIn + info->rFeedback); + m_ratio_feedback = info->rIn / (info->rIn + info->rFeedback) * info->vGate; + + /* The voltage source resistance works out to the 2 resistors in parallel. + * So use this for the RC charge constant. */ + rSource = 1.0 / ((1.0 / info->rIn) + (1.0 / info->rFeedback)); + m_rc = rSource * info->c; + m_exponent = RC_CHARGE_EXP(m_rc); + + /* Cap is at 0V on power up. Causing output to be high. */ + m_v_cap = 0; + m_state = 1; + + set_output(0, info->options ? 0 : DSS_SCHMITT_OSC__AMP); +} + + +/************************************************************************ + * + * DSS_SINEWAVE - Usage of node_description values for step function + * + * input0 - Enable input value + * input1 - Frequency input value + * input2 - Amplitude input value + * input3 - DC Bias + * input4 - Starting phase + * + ************************************************************************/ +#define DSS_SINEWAVE__ENABLE DISCRETE_INPUT(0) +#define DSS_SINEWAVE__FREQ DISCRETE_INPUT(1) +#define DSS_SINEWAVE__AMPL DISCRETE_INPUT(2) +#define DSS_SINEWAVE__BIAS DISCRETE_INPUT(3) +#define DSS_SINEWAVE__PHASE DISCRETE_INPUT(4) + +DISCRETE_STEP(dss_sinewave) +{ + /* Set the output */ + if(DSS_SINEWAVE__ENABLE) + { + set_output(0, (DSS_SINEWAVE__AMPL / 2.0) * sin(m_phase) + DSS_SINEWAVE__BIAS); + /* Add DC Bias component */ + } + else + { + set_output(0, 0); + } + + /* Work out the phase step based on phase/freq & sample rate */ + /* The enable input only curtails output, phase rotation */ + /* still occurs */ + /* phase step = 2Pi/(output period/sample period) */ + /* boils out to */ + /* phase step = (2Pi*output freq)/sample freq) */ + /* Also keep the new phasor in the 2Pi range. */ + m_phase=fmod((m_phase + ((2.0 * M_PI * DSS_SINEWAVE__FREQ) / this->sample_rate())), 2.0 * M_PI); +} + +DISCRETE_RESET(dss_sinewave) +{ + double start; + + /* Establish starting phase, convert from degrees to radians */ + start = (DSS_SINEWAVE__PHASE / 360.0) * (2.0 * M_PI); + /* Make sure its always mod 2Pi */ + m_phase = fmod(start, 2.0 * M_PI); + /* Step the output to make it correct */ + this->step(); +} + + +/************************************************************************ + * + * DSS_SQUAREWAVE - Usage of node_description values for step function + * + * input0 - Enable input value + * input1 - Frequency input value + * input2 - Amplitude input value + * input3 - Duty Cycle + * input4 - DC Bias level + * input5 - Start Phase + * + ************************************************************************/ +#define DSS_SQUAREWAVE__ENABLE DISCRETE_INPUT(0) +#define DSS_SQUAREWAVE__FREQ DISCRETE_INPUT(1) +#define DSS_SQUAREWAVE__AMP DISCRETE_INPUT(2) +#define DSS_SQUAREWAVE__DUTY DISCRETE_INPUT(3) +#define DSS_SQUAREWAVE__BIAS DISCRETE_INPUT(4) +#define DSS_SQUAREWAVE__PHASE DISCRETE_INPUT(5) + +DISCRETE_STEP(dss_squarewave) +{ + /* Establish trigger phase from duty */ + m_trigger=((100-DSS_SQUAREWAVE__DUTY)/100)*(2.0*M_PI); + + /* Set the output */ + if(DSS_SQUAREWAVE__ENABLE) + { + if(m_phase>m_trigger) + set_output(0, DSS_SQUAREWAVE__AMP / 2.0 + DSS_SQUAREWAVE__BIAS); + else + set_output(0, - DSS_SQUAREWAVE__AMP / 2.0 + DSS_SQUAREWAVE__BIAS); + /* Add DC Bias component */ + } + else + { + set_output(0, 0); + } + + /* Work out the phase step based on phase/freq & sample rate */ + /* The enable input only curtails output, phase rotation */ + /* still occurs */ + /* phase step = 2Pi/(output period/sample period) */ + /* boils out to */ + /* phase step = (2Pi*output freq)/sample freq) */ + /* Also keep the new phasor in the 2Pi range. */ + m_phase=fmod(m_phase + ((2.0 * M_PI * DSS_SQUAREWAVE__FREQ) / this->sample_rate()), 2.0 * M_PI); +} + +DISCRETE_RESET(dss_squarewave) +{ + double start; + + /* Establish starting phase, convert from degrees to radians */ + start = (DSS_SQUAREWAVE__PHASE / 360.0) * (2.0 * M_PI); + /* Make sure its always mod 2Pi */ + m_phase = fmod(start, 2.0 * M_PI); + + /* Step the output */ + this->step(); +} + +/************************************************************************ + * + * DSS_SQUAREWFIX - Usage of node_description values for step function + * + * input0 - Enable input value + * input1 - Frequency input value + * input2 - Amplitude input value + * input3 - Duty Cycle + * input4 - DC Bias level + * input5 - Start Phase + * + ************************************************************************/ +#define DSS_SQUAREWFIX__ENABLE DISCRETE_INPUT(0) +#define DSS_SQUAREWFIX__FREQ DISCRETE_INPUT(1) +#define DSS_SQUAREWFIX__AMP DISCRETE_INPUT(2) +#define DSS_SQUAREWFIX__DUTY DISCRETE_INPUT(3) +#define DSS_SQUAREWFIX__BIAS DISCRETE_INPUT(4) +#define DSS_SQUAREWFIX__PHASE DISCRETE_INPUT(5) + +DISCRETE_STEP(dss_squarewfix) +{ + m_t_left -= m_sample_step; + + /* The enable input only curtails output, phase rotation still occurs */ + while (m_t_left <= 0) + { + m_flip_flop = m_flip_flop ? 0 : 1; + m_t_left += m_flip_flop ? m_t_on : m_t_off; + } + + if(DSS_SQUAREWFIX__ENABLE) + { + /* Add gain and DC Bias component */ + + m_t_off = 1.0 / DSS_SQUAREWFIX__FREQ; /* cycle time */ + m_t_on = m_t_off * (DSS_SQUAREWFIX__DUTY / 100.0); + m_t_off -= m_t_on; + + set_output(0, (m_flip_flop ? DSS_SQUAREWFIX__AMP / 2.0 : -(DSS_SQUAREWFIX__AMP / 2.0)) + DSS_SQUAREWFIX__BIAS); + } + else + { + set_output(0, 0); + } +} + +DISCRETE_RESET(dss_squarewfix) +{ + m_sample_step = 1.0 / this->sample_rate(); + m_flip_flop = 1; + + /* Do the intial time shift and convert freq to off/on times */ + m_t_off = 1.0 / DSS_SQUAREWFIX__FREQ; /* cycle time */ + m_t_left = DSS_SQUAREWFIX__PHASE / 360.0; /* convert start phase to % */ + m_t_left = m_t_left - (int)m_t_left; /* keep % between 0 & 1 */ + m_t_left = (m_t_left < 0) ? 1.0 + m_t_left : m_t_left; /* if - then flip to + phase */ + m_t_left *= m_t_off; + m_t_on = m_t_off * (DSS_SQUAREWFIX__DUTY / 100.0); + m_t_off -= m_t_on; + + m_t_left = -m_t_left; + + /* toggle output and work out intial time shift */ + while (m_t_left <= 0) + { + m_flip_flop = m_flip_flop ? 0 : 1; + m_t_left += m_flip_flop ? m_t_on : m_t_off; + } + + /* Step the output */ + this->step(); +} + + +/************************************************************************ + * + * DSS_SQUAREWAVE2 - Usage of node_description values + * + * input0 - Enable input value + * input1 - Amplitude input value + * input2 - OFF Time + * input3 - ON Time + * input4 - DC Bias level + * input5 - Initial Time Shift + * + ************************************************************************/ +#define DSS_SQUAREWAVE2__ENABLE DISCRETE_INPUT(0) +#define DSS_SQUAREWAVE2__AMP DISCRETE_INPUT(1) +#define DSS_SQUAREWAVE2__T_OFF DISCRETE_INPUT(2) +#define DSS_SQUAREWAVE2__T_ON DISCRETE_INPUT(3) +#define DSS_SQUAREWAVE2__BIAS DISCRETE_INPUT(4) +#define DSS_SQUAREWAVE2__SHIFT DISCRETE_INPUT(5) + +DISCRETE_STEP(dss_squarewave2) +{ + double newphase; + + if(DSS_SQUAREWAVE2__ENABLE) + { + /* Establish trigger phase from time periods */ + m_trigger = (DSS_SQUAREWAVE2__T_OFF / (DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON)) * (2.0 * M_PI); + + /* Work out the phase step based on phase/freq & sample rate */ + /* The enable input only curtails output, phase rotation */ + /* still occurs */ + + /* phase step = 2Pi/(output period/sample period) */ + /* boils out to */ + /* phase step = 2Pi/(output period*sample freq) */ + newphase = m_phase + ((2.0 * M_PI) / ((DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON) * this->sample_rate())); + /* Keep the new phasor in the 2Pi range.*/ + m_phase = fmod(newphase, 2.0 * M_PI); + + /* Add DC Bias component */ + if(m_phase>m_trigger) + set_output(0, DSS_SQUAREWAVE2__AMP / 2.0 + DSS_SQUAREWAVE2__BIAS); + else + set_output(0, -DSS_SQUAREWAVE2__AMP / 2.0 + DSS_SQUAREWAVE2__BIAS); + } + else + { + set_output(0, 0); + } +} + +DISCRETE_RESET(dss_squarewave2) +{ + double start; + + /* Establish starting phase, convert from degrees to radians */ + /* Only valid if we have set the on/off time */ + if((DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON) != 0.0) + start = (DSS_SQUAREWAVE2__SHIFT / (DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON)) * (2.0 * M_PI); + else + start = 0.0; + /* Make sure its always mod 2Pi */ + m_phase = fmod(start, 2.0 * M_PI); + + /* Step the output */ + this->step(); +} + +/************************************************************************ + * + * DSS_INVERTER_OSC - Usage of node_description values + * + * input0 - Enable input value + * input1 - RC Resistor + * input2 - RP Resistor + * input3 - C Capacitor + * input4 - Desc + * + ************************************************************************/ + +/* + * Taken from the transfer characteristerics diagram in CD4049UB datasheet (TI) + * There is no default trigger point and vI-vO is a continuous function + */ + +inline double DISCRETE_CLASS_FUNC(dss_inverter_osc, tftab)(double x) +{ + DISCRETE_DECLARE_INFO(description) + + x = x / info->vB; + if (x > 0) + return info->vB * exp(-mc_tf_a * pow(x, mc_tf_b)); + else + return info->vB; +} + +inline double DISCRETE_CLASS_FUNC(dss_inverter_osc, tf)(double x) +{ + DISCRETE_DECLARE_INFO(description) + + if (x < 0.0) + return info->vB; + else if (x <= info->vB) + return mc_tf_tab[(int)((double)(DSS_INV_TAB_SIZE - 1) * x / info->vB)]; + else + return mc_tf_tab[DSS_INV_TAB_SIZE - 1]; +} + +DISCRETE_STEP(dss_inverter_osc) +{ + DISCRETE_DECLARE_INFO(description) + double diff, vG1, vG2, vG3, vI; + double vMix, rMix; + int clamped; + double v_out; + + /* Get new state */ + vI = mc_v_cap + mc_v_g2_old; + switch (info->options & TYPE_MASK) + { + case IS_TYPE1: + case IS_TYPE3: + vG1 = this->tf(vI); + vG2 = this->tf(vG1); + vG3 = this->tf(vG2); + break; + case IS_TYPE2: + vG1 = 0; + vG3 = this->tf(vI); + vG2 = this->tf(vG3); + break; + case IS_TYPE4: + vI = MIN(I_ENABLE(), vI + 0.7); + vG1 = 0; + vG3 = this->tf(vI); + vG2 = this->tf(vG3); + break; + case IS_TYPE5: + vI = MAX(I_ENABLE(), vI - 0.7); + vG1 = 0; + vG3 = this->tf(vI); + vG2 = this->tf(vG3); + break; + default: + fatalerror("DISCRETE_INVERTER_OSC - Wrong type on NODE_%02d\n", this->index()); + } + + clamped = 0; + if (info->clamp >= 0.0) + { + if (vI < -info->clamp) + { + vI = -info->clamp; + clamped = 1; + } + else if (vI > info->vB+info->clamp) + { + vI = info->vB + info->clamp; + clamped = 1; + } + } + + switch (info->options & TYPE_MASK) + { + case IS_TYPE1: + case IS_TYPE2: + case IS_TYPE3: + if (clamped) + { + double ratio = mc_rp / (mc_rp + mc_r1); + diff = vG3 * (ratio) + - (mc_v_cap + vG2) + + vI * (1.0 - ratio); + diff = diff - diff * mc_wc; + } + else + { + diff = vG3 - (mc_v_cap + vG2); + diff = diff - diff * mc_w; + } + break; + case IS_TYPE4: + /* FIXME handle r2 = 0 */ + rMix = (mc_r1 * mc_r2) / (mc_r1 + mc_r2); + vMix = rMix* ((vG3 - vG2) / mc_r1 + (I_MOD() -vG2) / mc_r2); + if (vMix < (vI-vG2-0.7)) + { + rMix = 1.0 / rMix + 1.0 / mc_rp; + rMix = 1.0 / rMix; + vMix = rMix* ( (vG3-vG2) / mc_r1 + (I_MOD() - vG2) / mc_r2 + + (vI - 0.7 - vG2) / mc_rp); + } + diff = vMix - mc_v_cap; + diff = diff - diff * exp(-this->sample_time() / (mc_c * rMix)); + break; + case IS_TYPE5: + /* FIXME handle r2 = 0 */ + rMix = (mc_r1 * mc_r2) / (mc_r1 + mc_r2); + vMix = rMix* ((vG3 - vG2) / mc_r1 + (I_MOD() - vG2) / mc_r2); + if (vMix > (vI -vG2 + 0.7)) + { + rMix = 1.0 / rMix + 1.0 / mc_rp; + rMix = 1.0 / rMix; + vMix = rMix * ( (vG3 - vG2) / mc_r1 + (I_MOD() - vG2) / mc_r2 + + (vI + 0.7 - vG2) / mc_rp); + } + diff = vMix - mc_v_cap; + diff = diff - diff * exp(-this->sample_time()/(mc_c * rMix)); + break; + default: + fatalerror("DISCRETE_INVERTER_OSC - Wrong type on NODE_%02d\n", this->index()); + } + + mc_v_cap += diff; + mc_v_g2_old = vG2; + + if ((info->options & TYPE_MASK) == IS_TYPE3) + v_out = vG1; + else + v_out = vG3; + + if (info->options & OUT_IS_LOGIC) + v_out = (v_out > info->vInFall); + + set_output(0, v_out); +} + +DISCRETE_RESET(dss_inverter_osc) +{ + DISCRETE_DECLARE_INFO(description) + + int i; + + /* exponent */ + mc_w = exp(-this->sample_time() / (I_RC() * I_C())); + mc_wc = exp(-this->sample_time() / ((I_RC() * I_RP()) / (I_RP() + I_RC()) * I_C())); + set_output(0, 0); + mc_v_cap = 0; + mc_v_g2_old = 0; + mc_rp = I_RP(); + mc_r1 = I_RC(); + mc_r2 = I_R2(); + mc_c = I_C(); + mc_tf_b = (log(0.0 - log(info->vOutLow/info->vB)) - log(0.0 - log((info->vOutHigh/info->vB))) ) / log(info->vInRise / info->vInFall); + mc_tf_a = log(0.0 - log(info->vOutLow/info->vB)) - mc_tf_b * log(info->vInRise/info->vB); + mc_tf_a = exp(mc_tf_a); + + for (i = 0; i < DSS_INV_TAB_SIZE; i++) + { + mc_tf_tab[i] = this->tftab((double)i / (double)(DSS_INV_TAB_SIZE - 1) * info->vB); + } +} + +/************************************************************************ + * + * DSS_TRIANGLEWAVE - Usage of node_description values for step function + * + * input0 - Enable input value + * input1 - Frequency input value + * input2 - Amplitde input value + * input3 - DC Bias value + * input4 - Initial Phase + * + ************************************************************************/ +#define DSS_TRIANGLEWAVE__ENABLE DISCRETE_INPUT(0) +#define DSS_TRIANGLEWAVE__FREQ DISCRETE_INPUT(1) +#define DSS_TRIANGLEWAVE__AMP DISCRETE_INPUT(2) +#define DSS_TRIANGLEWAVE__BIAS DISCRETE_INPUT(3) +#define DSS_TRIANGLEWAVE__PHASE DISCRETE_INPUT(4) + +DISCRETE_STEP(dss_trianglewave) +{ + if(DSS_TRIANGLEWAVE__ENABLE) + { + double v_out = m_phase < M_PI ? (DSS_TRIANGLEWAVE__AMP * (m_phase / (M_PI / 2.0) - 1.0)) / 2.0 : + (DSS_TRIANGLEWAVE__AMP * (3.0 - m_phase / (M_PI / 2.0))) / 2.0 ; + + /* Add DC Bias component */ + v_out += DSS_TRIANGLEWAVE__BIAS; + set_output(0, v_out); + } + else + { + set_output(0, 0); + } + + /* Work out the phase step based on phase/freq & sample rate */ + /* The enable input only curtails output, phase rotation */ + /* still occurs */ + /* phase step = 2Pi/(output period/sample period) */ + /* boils out to */ + /* phase step = (2Pi*output freq)/sample freq) */ + /* Also keep the new phasor in the 2Pi range. */ + m_phase=fmod((m_phase + ((2.0 * M_PI * DSS_TRIANGLEWAVE__FREQ) / this->sample_rate())), 2.0 * M_PI); +} + +DISCRETE_RESET(dss_trianglewave) +{ + double start; + + /* Establish starting phase, convert from degrees to radians */ + start = (DSS_TRIANGLEWAVE__PHASE / 360.0) * (2.0 * M_PI); + /* Make sure its always mod 2Pi */ + m_phase=fmod(start, 2.0 * M_PI); + + /* Step to set the output */ + this->step(); +} + + +/************************************************************************ + * + * DSS_ADSR - Attack Decay Sustain Release + * + * input0 - Enable input value + * input1 - Trigger value + * input2 - gain scaling factor + * + ************************************************************************/ +#define DSS_ADSR__ENABLE DISCRETE_INPUT(0) + +DISCRETE_STEP(dss_adsrenv) +{ + if(DSS_ADSR__ENABLE) + { + set_output(0, 0); + } + else + { + set_output(0, 0); + } +} + + +DISCRETE_RESET(dss_adsrenv) +{ + this->step(); +} -- cgit v1.2.3-70-g09d2