diff --git a/src/emu/sound/disc_dev.c b/src/emu/sound/disc_dev.c index ee3abee869b..d8b632d8ab1 100644 --- a/src/emu/sound/disc_dev.c +++ b/src/emu/sound/disc_dev.c @@ -14,6 +14,8 @@ * DSD_555_CC - NE555 Constant Current VCO * DSD_555_VCO1 - Op-Amp linear ramp based 555 VCO * DSD_566 - NE566 Simulation + * DSD_LS624 + * DSD_LS629 * ************************************************************************ * @@ -135,12 +137,12 @@ struct dsd_ls624_context struct dsd_ls629_context { - double v_bias; - double v_cap; - double v_peak; - double v_threshold; - double k_vmod_to_i; + double exponent; + double t_used; + double vmod_scale; + double v_cap_freq_in; int flip_flop; + int has_freq_in_cap; int out_type; }; @@ -1813,11 +1815,12 @@ static DISCRETE_RESET(dsd_ls624) #define DSD_LS629__VRNG DISCRETE_INPUT(2) #define DSD_LS629__C DISCRETE_INPUT(3) #define DSD_LS629__R_FREQ_IN DISCRETE_INPUT(4) -#define DSD_LS629__OUTTYPE DISCRETE_INPUT(5) +#define DSD_LS629__C_FREQ_IN DISCRETE_INPUT(5) +#define DSD_LS629__OUTTYPE DISCRETE_INPUT(6) #define LS624_R_EXT 600.0 /* as specified in data sheet */ #define LS624_OUT_HIGH 4.5 /* measured */ -#define LS624_FREQ_R_IN RES_K(95) /* measured */ +#define LS624_FREQ_R_IN RES_K(90) /* measured & 70K + 20k per data sheet */ /* * The 74LS624 series are constant current based VCOs. The Freq Control voltage @@ -1835,97 +1838,103 @@ static DISCRETE_RESET(dsd_ls624) * 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 / 5) / Rext + * I = (VfreqControl * 20k/90k) / Rext * where Rext = 600 ohms or external Rext on a 74LS628 - * The Freq Control has an input impedance of approximately 100k, so any input resistance + * 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 / 5 * 100000 / (RfreqControl + 100000)) / Rext + * 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. */ -/* The range to bias and threshold routines were created from testing a real chip. - * This data was entered into the function finder routine at www.zunzun.com - * to create the following 2 routines - */ - -double range_to_bias(double range) -{ - /* Quadratic2D_model */ - double bias; - - // coefficients - const double a = 2.3107142857142846E+00; - const double b = -1.0714285714278806E-03; - const double c = -1.7857142857144002E-03; - - bias = a; - bias += b * range; - bias += c * pow(range, 2.0); - return bias; -} - -double range_to_threshold(double range) -{ - /* Polyfunctional2D_model */ - double threshold; - - // coefficients - const double a = -1.6124587587173614E-01; - const double b = -1.3501987413150401E-01; - const double offset = 2.2550390868489893E+00; - - threshold = a * range; - threshold += b * exp(-1.0 * range); - threshold += offset; - return threshold; -} - static DISCRETE_STEP(dsd_ls629) { struct dsd_ls629_context *context = (struct dsd_ls629_context *)node->context; - double i; /* Charging current created by Freq Control */ - double dt; /* change in time */ double x_time = 0; - double v_cap; /* Current voltage on capacitor, before dt */ + double freq, t1; + double t_used = context->t_used; + double dt = node->info->sample_time;; + double vmod = DSD_LS629__VMOD; + double vmod_2, vmod_3, vmod_4; + double range = DSD_LS629__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_LS629__ENABLE == 0)) return; - dt = node->info->sample_time; /* Change in time */ - v_cap = context->v_cap; /* Set to voltage before change */ + /* scale due to input resistance */ + if ((EXPECTED(DSD_LS629__R_FREQ_IN > 0))) + vmod *= context->vmod_scale; + /* apply cap if needed */ + if (context->has_freq_in_cap) + { + context->v_cap_freq_in += (vmod - context->v_cap_freq_in) * context->exponent; + vmod = context->v_cap_freq_in; + } - /* Calculate charging current */ - i = DSD_LS629__VMOD * context->k_vmod_to_i; + /* Polyfunctional3D_model created by zunzun.com using sum of squared absolute error */ + vmod_2 = vmod * vmod; + vmod_3 = vmod_2 * vmod; + vmod_4 = vmod_3 * vmod; + freq = k1; + freq += k2 * vmod; + freq += k3 * vmod_2; + freq += k4 * vmod_3; + freq += k5 * vmod_4; + freq += k6 * range; + freq += k7 * range * vmod; + freq += k8 * range * vmod_2; + freq += k9 * range * vmod_3; + freq += k10 * range * vmod_4; - /* Keep looping until all toggling in this time sample is used up. */ + freq *= CAP_U(0.1) / DSD_LS629__C; + + t1 = 0.5 / freq ; + t_used += node->info->sample_time; do { - /* Always Discharging */ - v_cap -= i * dt / DSD_LS629__C; - dt = 0; - - /* has it discharged past lower limit? */ - if (UNEXPECTED(v_cap < context->v_threshold)) + dt = 0; + if (t_used > t1) { /* calculate the overshoot time */ - dt = DSD_LS629__C * (context->v_threshold - v_cap) / i; - v_cap = context->v_peak; + t_used -= t1; context->flip_flop ^= 1; if (context->flip_flop) count_r++; else count_f++; - x_time = dt; + /* fix up any frequency increase change errors */ + while(t_used > node->info->sample_time) + t_used -= node->info->sample_time; + x_time = t_used; + dt = t_used; } - } while(dt); + }while(dt); - context->v_cap = v_cap; + context->t_used = t_used; /* Convert last switch time to a ratio */ x_time = x_time / node->info->sample_time; @@ -1937,7 +1946,7 @@ static DISCRETE_STEP(dsd_ls629) break; case DISC_LS624_OUT_ENERGY: if (x_time == 0) x_time = 1.0; - node->output[0] = LS624_OUT_HIGH * (context->flip_flop ? x_time : (1.0 - x_time)); + node->output[0] = LS624_OUT_HIGH * (context->flip_flop ? x_time : (1.0 - x_time)); break; case DISC_LS624_OUT_LOGIC: node->output[0] = context->flip_flop; @@ -1957,23 +1966,24 @@ static DISCRETE_STEP(dsd_ls629) } } -#define LS624_LOSS_FACTOR 0.92 - static DISCRETE_RESET(dsd_ls629) { struct dsd_ls629_context *context = (struct dsd_ls629_context *)node->context; context->out_type = (int)DSD_LS629__OUTTYPE; - context->v_bias = range_to_bias(DSD_LS629__VRNG); - context->v_cap = context->v_bias; - context->v_threshold = range_to_threshold(DSD_LS629__VRNG); - /* The voltage at the current charge pin after a state change */ - context->v_peak = context->v_bias * 2 - context->v_threshold; context->flip_flop = 0; - /* precalulate the voltage to current formula */ - context->k_vmod_to_i = 1.0 / 5 * LS624_FREQ_R_IN / (DSD_LS629__R_FREQ_IN + LS624_FREQ_R_IN) / LS624_R_EXT; - context->k_vmod_to_i *= LS624_LOSS_FACTOR; + context->t_used = 0; + context->vmod_scale = RES_K(90) / (DSD_LS629__R_FREQ_IN + RES_K(90)); + if (DSD_LS629__C_FREQ_IN > 0) + { + context->has_freq_in_cap = 1; + context->exponent = RC_CHARGE_EXP(RES_2_PARALLEL(DSD_LS629__R_FREQ_IN, RES_K(90)) * DSD_LS629__C_FREQ_IN); + context->v_cap_freq_in = 0; + } + else + context->has_freq_in_cap = 0; + node->output[0] = 0; } diff --git a/src/emu/sound/discrete.h b/src/emu/sound/discrete.h index c16ee450785..2a8bc3d8b41 100644 --- a/src/emu/sound/discrete.h +++ b/src/emu/sound/discrete.h @@ -4526,7 +4526,7 @@ enum #define DISCRETE_555_VCO1_CV(NODE,RESET,VIN,CTRLV,OPTIONS) { NODE, DSD_555_VCO1 , 3, { RESET,VIN,CTRLV }, { RESET,VIN,CTRLV }, OPTIONS, "DISCRETE_555_VCO1_CV" }, #define DISCRETE_566(NODE,VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS) { NODE, DSD_566 , 7, { VMOD,R,C,NODE_NC,NODE_NC,VCHARGE,NODE_NC }, { VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS }, NULL, "DISCRETE_566" }, #define DISCRETE_74LS624(NODE,VMOD,VRNG,C,OUTTYPE) { NODE, DSD_LS624 , 4, { VMOD,NODE_NC,NODE_NC,NODE_NC }, { VMOD,VRNG,C,OUTTYPE }, NULL, "DISCRETE_74LS624" }, -#define DISCRETE_74LS629(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,OUTTYPE) { NODE, DSD_LS629 , 6, { ENAB,VMOD,NODE_NC,NODE_NC,NODE_NC,NODE_NC }, { ENAB,VMOD,VRNG,C,R_FREQ_IN,OUTTYPE }, NULL, "DISCRETE_74LS629" }, +#define DISCRETE_74LS629(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,OUTTYPE) { NODE, DSD_LS629 , 7, { ENAB,VMOD,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC }, { ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,OUTTYPE }, NULL, "DISCRETE_74LS629" }, /* NOP */ #define DISCRETE_NOP(NODE) { NODE, DSS_NOP , 0, { 0 }, { 0 }, NULL, "DISCRETE_NOP" }, diff --git a/src/mame/audio/dkong.c b/src/mame/audio/dkong.c index 7f5c799d526..cf85113ebb6 100644 --- a/src/mame/audio/dkong.c +++ b/src/mame/audio/dkong.c @@ -21,7 +21,7 @@ /* Set to 1 to use faster custom mixer */ #define DK_USE_CUSTOM (1) -#define USE_LS629 (0) /* set to use new LS624 code */ +#define USE_LS629 (1) /* set to use new LS624 code */ /* Issue surrounded by this define need to be analyzed and * reviewed at a later time. @@ -893,7 +893,7 @@ static DISCRETE_SOUND_START(dkongjr) DISCRETE_TASK_START(1) DISCRETE_LOGIC_INVERT(DS_SOUND7,DS_SOUND7_INV) - DISCRETE_COUNTER(NODE_100, 1, 0, NODE_118, 0, 0xFFFF, DISC_COUNT_UP, 0, DISC_CLK_BY_COUNT) + DISCRETE_COUNTER(NODE_100, 1, 0, NODE_118, 0, 0x3FFF, DISC_COUNT_UP, 0, DISC_CLK_BY_COUNT) DISCRETE_BIT_DECODE(NODE_101, NODE_100, 6, 1) /*LS157 2A */ DISCRETE_BIT_DECODE(NODE_102, NODE_100, 3, 1) /*LS157 2B */ @@ -901,7 +901,7 @@ DISCRETE_TASK_START(1) DISCRETE_BIT_DECODE(NODE_104, NODE_100, 11, 1) /*LS157 3B */ /* LS157 Switches */ - DISCRETE_SWITCH(NODE_105, 1, DS_SOUND7_INV, GND, NODE_113) /* Switch 1 from LS624 */ +// DISCRETE_SWITCH(NODE_105, 1, DS_SOUND7_INV, GND, NODE_113) /* Switch 1 from LS624 */ DISCRETE_SWITCH(NODE_106, 1, DS_SOUND7_INV, NODE_101, NODE_102) /* Switch 2 */ DISCRETE_SWITCH(NODE_107, 1, DS_SOUND7_INV, NODE_103, NODE_104) /* Switch 3 */ @@ -909,7 +909,6 @@ DISCRETE_TASK_START(1) DISCRETE_SWITCH(NODE_111, /* invert voltage */ 1, NODE_110, /* ENAB, SWITCH */ TTL_HIGH, 0) /* INP0, INP1 */ - DISCRETE_RCFILTER(NODE_112, NODE_111, JR_R10, JR_C17) /* Breadboarded measurements IC 5K, pin 7 D.R. Oct 2010 @@ -928,19 +927,20 @@ DISCRETE_TASK_START(1) #if (USE_LS629) DISCRETE_74LS629(NODE_113, /* IC 5K, pin 7 */ 1, /* ENAB */ - NODE_112, DK_SUP_V, /* VMOD, VRNG */ - JR_C18, JR_R10, /* C, R_FREQ_IN */ + NODE_111, DK_SUP_V, /* VMOD, VRNG */ + JR_C18, JR_R10, JR_C17, /* C, R_FREQ_IN, C_FREQ_IN */ DISC_LS624_OUT_ENERGY) #else + DISCRETE_RCFILTER(NODE_112, NODE_111, JR_R10, JR_C17) DISCRETE_74LS624(NODE_113, NODE_112, DK_SUP_V, JR_C18, DISC_LS624_OUT_ENERGY) #endif + DISCRETE_SWITCH(NODE_105, 1, DS_SOUND7_INV, GND, NODE_113) /* Switch 1 from LS624 */ DISCRETE_LOGIC_XOR(NODE_115, NODE_105, NODE_106) DISCRETE_SWITCH(NODE_116, /* invert with TTL voltage */ 1, NODE_107, /* ENAB, SWITCH */ TTL_HIGH, 0) /* INP0, INP1 */ - DISCRETE_RCFILTER(NODE_117, NODE_116, JR_R11, JR_C16) /* Breadboarded measurements IC 5K, pin 10 D.R. Oct 2010 @@ -956,13 +956,14 @@ DISCRETE_TASK_START(1) 4.15 59400 */ -#if (0 && USE_LS629) +#if (USE_LS629) DISCRETE_74LS629(NODE_118, /* IC 5K, pin 10 */ 1, /* ENAB */ - NODE_117, DK_SUP_V, /* VMOD, VRNG */ - JR_C19, JR_R11, /* C, R_FREQ_IN */ + NODE_116, DK_SUP_V, /* VMOD, VRNG */ + JR_C19, JR_R11, JR_C16, /* C, R_FREQ_IN, C_FREQ_IN */ DISC_LS624_OUT_COUNT_F) #else + DISCRETE_RCFILTER(NODE_117, NODE_116, JR_R11, JR_C16) DISCRETE_74LS624(NODE_118, NODE_117, DK_SUP_V, JR_C19, DISC_LS624_OUT_COUNT_F) #endif @@ -1001,7 +1002,8 @@ DISCRETE_TASK_START(2) DISCRETE_74LS629(NODE_14, /* IC 8L, pin 10 */ 1, /* ENAB */ NODE_13, DK_SUP_V, /* VMOD, VRNG */ - JR_C22, RES_2_PARALLEL(JR_R12, JR_R13), /* C, R_FREQ_IN */ + /* C_FREQ_IN is taken care of by the NODE_13 mixer */ + JR_C22, RES_2_PARALLEL(JR_R13, JR_R12), 0, /* C, R_FREQ_IN, C_FREQ_IN */ DISC_LS624_OUT_ENERGY) #else DISCRETE_74LS624( NODE_14, NODE_13, 0.98*DK_SUP_V, JR_C22, DISC_LS624_OUT_ENERGY) @@ -1055,11 +1057,10 @@ DISCRETE_TASK_START(1) */ #if (USE_LS629) - DISCRETE_RCFILTER(NODE_90, DS_SOUND9_INV, JR_R14, JR_C26) DISCRETE_74LS629(NODE_91, /* IC 7P, pin 7 */ 1, /* ENAB */ - NODE_90, DK_SUP_V, /* VMOD, VRNG */ - JR_C37, JR_R14, /* C, R_FREQ_IN */ + DS_SOUND9_INV, DK_SUP_V, /* VMOD, VRNG */ + JR_C37, JR_R14, JR_C26, /* C, R_FREQ_IN, C_FREQ_IN */ DISC_LS624_OUT_ENERGY) DISCRETE_SWITCH(DS_OUT_SOUND9, 1, DS_SOUND9_INV, NODE_91, 0) #else @@ -1104,7 +1105,11 @@ DISCRETE_TASK_START(3) * Just a 1:n amplifier without filters - just the output filter */ DISCRETE_CRFILTER(NODE_295,NODE_288, 1000, JR_C13) +#if (USE_LS629) + DISCRETE_OUTPUT(NODE_295, 32767.0/5.0 * 5) +#else DISCRETE_OUTPUT(NODE_295, 32767.0/5.0 * 10) +#endif DISCRETE_TASK_END() DISCRETE_SOUND_END