From f8df4c6098e2101b28baefcbd4bcf0f4f44183bb Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 10 Sep 2019 05:02:17 +0900 Subject: ay8910.cpp : Updates (#5607) * ay8910.cpp : Updates Implement ay8930 expanded mode, Add notes, Add feature value for chip specific features, Add runtime pin26 handler, Use struct for tone, envelope, Reduce defines, Fix spacings, Reduce unnecessary lines * ay8910.cpp : Revert unnecessary part --- src/devices/sound/ay8910.cpp | 478 ++++++++++++++++++++++++++----------------- src/devices/sound/ay8910.h | 160 ++++++++++++++- 2 files changed, 436 insertions(+), 202 deletions(-) (limited to 'src') diff --git a/src/devices/sound/ay8910.cpp b/src/devices/sound/ay8910.cpp index 9ee02834f67..0372541d47d 100644 --- a/src/devices/sound/ay8910.cpp +++ b/src/devices/sound/ay8910.cpp @@ -142,8 +142,7 @@ http://mametesters.org/view.php?id=3043 TODO: - * The AY8930 has an extended mode which is currently - not emulated. + * Measure volume / envelope parameters for AY8930 expanded mode * YM2610 & YM2608 will need a separate flag in their config structures to distinguish between legacy and discrete mode. @@ -587,42 +586,14 @@ YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/c /************************************* * - * Defines + * constants * *************************************/ #define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */ #define LOG_IGNORED_WRITES (0) -#define MAX_OUTPUT 0x7fff - -/* register id's */ -#define AY_AFINE (0) -#define AY_ACOARSE (1) -#define AY_BFINE (2) -#define AY_BCOARSE (3) -#define AY_CFINE (4) -#define AY_CCOARSE (5) -#define AY_NOISEPER (6) -#define AY_ENABLE (7) -#define AY_AVOL (8) -#define AY_BVOL (9) -#define AY_CVOL (10) -#define AY_EFINE (11) -#define AY_ECOARSE (12) -#define AY_ESHAPE (13) - -#define AY_PORTA (14) -#define AY_PORTB (15) - -#define NOISE_ENABLEQ(_chan) ((m_regs[AY_ENABLE] >> (3 + _chan)) & 1) -#define TONE_ENABLEQ(_chan) ((m_regs[AY_ENABLE] >> (_chan)) & 1) -#define TONE_PERIOD(_chan) ( m_regs[(_chan) << 1] | ((m_regs[((_chan) << 1) | 1] & 0x0f) << 8) ) -#define NOISE_PERIOD() ( m_regs[AY_NOISEPER] & 0x1f) -#define TONE_VOLUME(_chan) ( m_regs[AY_AVOL + (_chan)] & 0x0f) -#define TONE_ENVELOPE(_chan) ((m_regs[AY_AVOL + (_chan)] >> 4) & ((type() == AY8914) ? 3 : 1)) -#define ENVELOPE_PERIOD() ((m_regs[AY_EFINE] | (m_regs[AY_ECOARSE]<<8))) -#define NOISE_OUTPUT() (m_rng & 1) +static constexpr int MAX_OUTPUT = 0x7fff; /************************************* * @@ -637,6 +608,20 @@ YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/c * *************************************/ +// duty cycle used for AY8930 expanded mode +static const u32 duty_cycle[9] = +{ + 0x80000000, // 3.125 % + 0xc0000000, // 6.25 % + 0xf0000000, // 12.50 % + 0xff000000, // 25.00 % + 0xffff0000, // 50.00 % + 0xffffff00, // 75.00 % + 0xfffffff0, // 87.50 % + 0xfffffffc, // 93.75 % + 0xfffffffe // 96.875 % +}; + static const ay8910_device::ay_ym_param ym2149_param = { 630, 801, @@ -791,15 +776,15 @@ static inline void build_3D_table(double rl, const ay8910_device::ay_ym_param *p std::vector temp(8*32*32*32, 0); - for (int e=0; e < 8; e++) + for (int e = 0; e < 8; e++) { const ay8910_device::ay_ym_param *par_ch1 = (e & 0x01) ? par_env : par; const ay8910_device::ay_ym_param *par_ch2 = (e & 0x02) ? par_env : par; const ay8910_device::ay_ym_param *par_ch3 = (e & 0x04) ? par_env : par; - for (int j1=0; j1 < par_ch1->res_count; j1++) - for (int j2=0; j2 < par_ch2->res_count; j2++) - for (int j3=0; j3 < par_ch3->res_count; j3++) + for (int j1 = 0; j1 < par_ch1->res_count; j1++) + for (int j2 = 0; j2 < par_ch2->res_count; j2++) + for (int j3 = 0; j3 < par_ch3->res_count; j3++) { double n; if (zero_is_off) @@ -821,7 +806,7 @@ static inline void build_3D_table(double rl, const ay8910_device::ay_ym_param *p rw += 1.0 / par_ch3->res[j3]; rt += 1.0 / par_ch3->res[j3]; - int indx = (e << 15) | (j3<<10) | (j2<<5) | j1; + int indx = (e << 15) | (j3 << 10) | (j2 << 5) | j1; temp[indx] = rw / rt; if (temp[indx] < min) min = temp[indx]; @@ -832,26 +817,25 @@ static inline void build_3D_table(double rl, const ay8910_device::ay_ym_param *p if (normalize) { - for (int j=0; j < 32*32*32*8; j++) + for (int j = 0; j < 32*32*32*8; j++) tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min))) * factor; } else { - for (int j=0; j < 32*32*32*8; j++) + for (int j = 0; j < 32*32*32*8; j++) tab[j] = MAX_OUTPUT * temp[j]; } - /* for (e=0;e<16;e++) printf("%d %d\n",e<<10, tab[e<<10]); */ + /* for (e = 0;e<16;e++) printf("%d %d\n",e << 10, tab[e << 10]); */ } static inline void build_single_table(double rl, const ay8910_device::ay_ym_param *par, int normalize, s32 *tab, int zero_is_off) { - int j; double rt; double rw; - double temp[32], min=10.0, max=0.0; + double temp[32], min = 10.0, max = 0.0; - for (j=0; j < par->res_count; j++) + for (int j = 0; j < par->res_count; j++) { rt = 1.0 / par->r_down + 1.0 / rl; @@ -872,12 +856,12 @@ static inline void build_single_table(double rl, const ay8910_device::ay_ym_para } if (normalize) { - for (j=0; j < par->res_count; j++) + for (int j = 0; j < par->res_count; j++) tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min)) - 0.25) * 0.5; } else { - for (j=0; j < par->res_count; j++) + for (int j = 0; j < par->res_count; j++) tab[j] = MAX_OUTPUT * temp[j]; } @@ -885,9 +869,7 @@ static inline void build_single_table(double rl, const ay8910_device::ay_ym_para static inline void build_mosfet_resistor_table(const ay8910_device::mosfet_param &par, const double rd, s32 *tab) { - int j; - - for (j=0; j < par.m_count; j++) + for (int j = 0; j < par.m_count; j++) { const double Vd = 5.0; const double Vg = par.m_Vg - par.m_Vth; @@ -895,7 +877,7 @@ static inline void build_mosfet_resistor_table(const ay8910_device::mosfet_param const double p2 = 1.0 / (2.0 * kn * rd) + Vg; const double Vs = p2 - sqrt(p2 * p2 - Vg * Vg); - const double res = rd * ( Vd / Vs - 1.0); + const double res = rd * (Vd / Vs - 1.0); /* That's the biggest value we can stream on to netlist. */ if (res > (1 << 28)) @@ -909,24 +891,39 @@ static inline void build_mosfet_resistor_table(const ay8910_device::mosfet_param u16 ay8910_device::mix_3D() { - int indx = 0, chan; + int indx = 0; - for (chan = 0; chan < NUM_CHANNELS; chan++) - if (TONE_ENVELOPE(chan) != 0) + for (int chan = 0; chan < NUM_CHANNELS; chan++) + { + tone_t *tone = &m_tone[chan]; + if (tone_envelope(tone) != 0) { - if (type() == AY8914) // AY8914 Has a two bit tone_envelope field + envelope_t *envelope = &m_envelope[get_envelope_chan(chan)]; + u32 env_volume = envelope->volume; + u32 env_mask = (1 << (chan + 15)); + if (m_feature & PSG_HAS_EXPANDED_MODE) + { + if (!is_expnaded_mode()) + { + env_volume >>= 1; + env_mask = 0; + } + } + if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field { - indx |= (1 << (chan+15)) | ( m_vol_enabled[chan] ? ((m_env_volume >> (3-TONE_ENVELOPE(chan))) << (chan*5)) : 0); + indx |= env_mask | (m_vol_enabled[chan] ? ((env_volume >> (3-tone_envelope(tone))) << (chan*5)) : 0); } else { - indx |= (1 << (chan+15)) | ( m_vol_enabled[chan] ? m_env_volume << (chan*5) : 0); + indx |= env_mask | (m_vol_enabled[chan] ? env_volume << (chan*5) : 0); } } else { - indx |= (m_vol_enabled[chan] ? TONE_VOLUME(chan) << (chan*5) : 0); + const u32 tone_mask = is_expnaded_mode() ? (1 << (chan + 15)) : 0; + indx |= tone_mask | (m_vol_enabled[chan] ? tone_volume(tone) << (chan*5) : 0); } + } return m_vol3d_table[indx]; } @@ -938,29 +935,47 @@ u16 ay8910_device::mix_3D() void ay8910_device::ay8910_write_reg(int r, int v) { - //if (r >= 11 && r <= 13 ) printf("%d %x %02x\n", PSG->index, r, v); - device_type chip_type = type(); + if ((r & 0xf) == AY_EASHAPE) // shared register + r &= 0xf; + + //if (r >= 11 && r <= 13) printf("%d %x %02x\n", PSG->index, r, v); m_regs[r] = v; + u8 coarse; - switch( r ) + switch(r) { case AY_AFINE: case AY_ACOARSE: + coarse = m_regs[AY_ACOARSE] & (is_expnaded_mode() ? 0xff : 0xf); + m_tone[0].set_period(m_regs[AY_AFINE], coarse); + break; case AY_BFINE: case AY_BCOARSE: + coarse = m_regs[AY_BCOARSE] & (is_expnaded_mode() ? 0xff : 0xf); + m_tone[1].set_period(m_regs[AY_BFINE], coarse); + break; case AY_CFINE: case AY_CCOARSE: + coarse = m_regs[AY_CCOARSE] & (is_expnaded_mode() ? 0xff : 0xf); + m_tone[2].set_period(m_regs[AY_CFINE], coarse); + break; case AY_NOISEPER: + /* No action required */ + break; case AY_AVOL: + m_tone[0].set_volume(m_regs[AY_AVOL]); + break; case AY_BVOL: + m_tone[1].set_volume(m_regs[AY_BVOL]); + break; case AY_CVOL: - case AY_EFINE: - /* No action required */ + m_tone[2].set_volume(m_regs[AY_CVOL]); break; - case AY_ECOARSE: - if ( (v & 0x0f) > 0) + case AY_EACOARSE: + if ((v & 0x0f) > 0) osd_printf_verbose("ECoarse\n"); - /* No action required */ + case AY_EAFINE: + m_envelope[0].set_period(m_regs[AY_EAFINE], m_regs[AY_EACOARSE]); break; case AY_ENABLE: if ((m_last_enable == -1) || @@ -980,32 +995,28 @@ void ay8910_device::ay8910_write_reg(int r, int v) } m_last_enable = m_regs[AY_ENABLE]; break; - case AY_ESHAPE: - if (chip_type == AY8930) + case AY_EASHAPE: + if (m_feature & PSG_HAS_EXPANDED_MODE) { + const u8 old_mode = m_mode; m_mode = (v >> 4) & 0xf; - if ((m_mode & 0xe) == 0xa) // AY8930 expanded mode - logerror("warning: activated unemulated extended mode at %s, bank %02x\n", name(), m_mode & 1); - else if (m_mode & 0xf) - logerror("warning: activated unknown mode %02x at %s\n", m_mode & 0xf, name()); + if (old_mode != m_mode) + { + if (((old_mode & 0xe) == 0xa) ^ ((m_mode & 0xe) == 0xa)) // AY8930 expanded mode + { + for (int i = 0; i < AY_EASHAPE; i++) + { + ay8910_write_reg(i, 0); + ay8910_write_reg(i + 0x10, 0); + } + } + else if (m_mode & 0xf) + logerror("warning: activated unknown mode %02x at %s\n", m_mode & 0xf, name()); + } } - if ( (v & 0x0f) > 0) + if ((v & 0x0f) > 0) osd_printf_verbose("EShape\n"); - m_attack = (m_regs[AY_ESHAPE] & 0x04) ? m_env_step_mask : 0x00; - if ((m_regs[AY_ESHAPE] & 0x08) == 0) - { - /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ - m_hold = 1; - m_alternate = m_attack; - } - else - { - m_hold = m_regs[AY_ESHAPE] & 0x01; - m_alternate = m_regs[AY_ESHAPE] & 0x02; - } - m_env_step = m_env_step_mask; - m_holding = 0; - m_env_volume = (m_env_step ^ m_attack); + m_envelope[0].set_shape(m_regs[AY_EASHAPE], m_env_step_mask); break; case AY_PORTA: if (m_regs[AY_ENABLE] & 0x40) @@ -1037,6 +1048,36 @@ void ay8910_device::ay8910_write_reg(int r, int v) #endif } break; + case AY_EBFINE: + case AY_EBCOARSE: + m_envelope[1].set_period(m_regs[AY_EBFINE], m_regs[AY_EBCOARSE]); + break; + case AY_ECFINE: + case AY_ECCOARSE: + m_envelope[2].set_period(m_regs[AY_ECFINE], m_regs[AY_ECCOARSE]); + break; + case AY_EBSHAPE: + m_envelope[1].set_shape(m_regs[AY_EBSHAPE], m_env_step_mask); + break; + case AY_ECSHAPE: + m_envelope[2].set_shape(m_regs[AY_ECSHAPE], m_env_step_mask); + break; + case AY_ADUTY: + m_tone[0].set_duty(m_regs[AY_ADUTY]); + break; + case AY_BDUTY: + m_tone[1].set_duty(m_regs[AY_BDUTY]); + break; + case AY_CDUTY: + m_tone[2].set_duty(m_regs[AY_CDUTY]); + break; + case AY_NOISEAND: + case AY_NOISEOR: + // not implemented + break; + default: + m_regs[r] = 0; // reserved, set as 0 + break; } } @@ -1047,7 +1088,8 @@ void ay8910_device::ay8910_write_reg(int r, int v) void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { stream_sample_t *buf[NUM_CHANNELS]; - int chan; + tone_t *tone; + envelope_t *envelope; buf[0] = outputs[0]; buf[1] = nullptr; @@ -1061,7 +1103,7 @@ void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t ** /* hack to prevent us from hanging when starting filtered outputs */ if (!m_ready) { - for (chan = 0; chan < NUM_CHANNELS; chan++) + for (int chan = 0; chan < NUM_CHANNELS; chan++) if (buf[chan] != nullptr) memset(buf[chan], 0, samples * sizeof(*buf[chan])); } @@ -1076,93 +1118,128 @@ void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t ** /* buffering loop */ while (samples) { - for (chan = 0; chan < NUM_CHANNELS; chan++) + for (int chan = 0; chan < NUM_CHANNELS; chan++) { - m_count[chan]++; - if (m_count[chan] >= TONE_PERIOD(chan)) + tone = &m_tone[chan]; + tone->count++; + if (tone->count >= tone->period) { - m_output[chan] ^= 1; - m_count[chan] = 0; + tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; + tone->output = BIT(duty_cycle[tone_duty(tone)], tone->duty_cycle); + tone->count = 0; } } m_count_noise++; - if (m_count_noise >= NOISE_PERIOD()) + if (m_count_noise >= noise_period()) { /* toggle the prescaler output. Noise is no different to * channels. */ m_count_noise = 0; - m_prescale_noise ^= 1; - if ( m_prescale_noise) + if (!m_prescale_noise) { /* The Random Number Generator of the 8910 is a 17-bit shift */ /* register. The input to the shift register is bit0 XOR bit3 */ /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ + // TODO : get actually algorithm for AY8930 m_rng ^= (((m_rng & 1) ^ ((m_rng >> 3) & 1)) << 17); m_rng >>= 1; + m_prescale_noise = 16; } + m_prescale_noise--; } - for (chan = 0; chan < NUM_CHANNELS; chan++) + for (int chan = 0; chan < NUM_CHANNELS; chan++) { - m_vol_enabled[chan] = (m_output[chan] | TONE_ENABLEQ(chan)) & (NOISE_OUTPUT() | NOISE_ENABLEQ(chan)); + tone = &m_tone[chan]; + m_vol_enabled[chan] = (tone->output | tone_enable(chan)) & (noise_output() | noise_enable(chan)); } /* update envelope */ - if (m_holding == 0) + for (int chan = 0; chan < NUM_CHANNELS; chan++) { - m_count_env++; - if (m_count_env >= ENVELOPE_PERIOD() * m_step ) + envelope = &m_envelope[chan]; + if (envelope->holding == 0) { - m_count_env = 0; - m_env_step--; - - /* check envelope current position */ - if (m_env_step < 0) + const u32 period = envelope->period * m_step; + envelope->count++; + if (envelope->count >= period) { - if (m_hold) + envelope->count = 0; + envelope->step--; + + /* check envelope current position */ + if (envelope->step < 0) { - if (m_alternate) - m_attack ^= m_env_step_mask; - m_holding = 1; - m_env_step = 0; + if (envelope->hold) + { + if (envelope->alternate) + envelope->attack ^= m_env_step_mask; + envelope->holding = 1; + envelope->step = 0; + } + else + { + /* if CountEnv has looped an odd number of times (usually 1), */ + /* invert the output. */ + if (envelope->alternate && (envelope->step & (m_env_step_mask + 1))) + envelope->attack ^= m_env_step_mask; + + envelope->step &= m_env_step_mask; + } } - else - { - /* if CountEnv has looped an odd number of times (usually 1), */ - /* invert the output. */ - if (m_alternate && (m_env_step & (m_env_step_mask + 1))) - m_attack ^= m_env_step_mask; - m_env_step &= m_env_step_mask; - } } - } + envelope->volume = (envelope->step ^ envelope->attack); } - m_env_volume = (m_env_step ^ m_attack); if (m_streams == 3) { - for (chan = 0; chan < NUM_CHANNELS; chan++) - if (TONE_ENVELOPE(chan) != 0) + for (int chan = 0; chan < NUM_CHANNELS; chan++) + { + tone = &m_tone[chan]; + if (tone_envelope(tone) != 0) { - if (type() == AY8914) // AY8914 Has a two bit tone_envelope field + envelope = &m_envelope[get_envelope_chan(chan)]; + u32 env_volume = envelope->volume; + if (m_feature & PSG_HAS_EXPANDED_MODE) { - *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? m_env_volume >> (3-TONE_ENVELOPE(chan)) : 0]; + if (!is_expnaded_mode()) + { + env_volume >>= 1; + if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field + *(buf[chan]++) = m_vol_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0]; + else + *(buf[chan]++) = m_vol_table[chan][m_vol_enabled[chan] ? env_volume : 0]; + } + else + { + if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field + *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0]; + else + *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0]; + } } else { - *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? m_env_volume : 0]; + if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field + *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0]; + else + *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0]; } } else { - *(buf[chan]++) = m_vol_table[chan][m_vol_enabled[chan] ? TONE_VOLUME(chan) : 0]; + if (is_expnaded_mode()) + *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0]; + else + *(buf[chan]++) = m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0]; } + } } else { @@ -1175,7 +1252,6 @@ void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t ** void ay8910_device::build_mixer_table() { int normalize = 0; - int chan; if ((m_flags & AY8910_LEGACY_OUTPUT) != 0) { @@ -1188,7 +1264,7 @@ void ay8910_device::build_mixer_table() if (m_type != PSG_TYPE_AY) fatalerror("AY8910_RESISTOR_OUTPUT currently only supported for AY8910 devices."); - for (chan=0; chan < NUM_CHANNELS; chan++) + for (int chan = 0; chan < NUM_CHANNELS; chan++) { build_mosfet_resistor_table(ay8910_mosfet_param, m_res_load[chan], m_vol_table[chan]); build_mosfet_resistor_table(ay8910_mosfet_param, m_res_load[chan], m_env_table[chan]); @@ -1196,7 +1272,7 @@ void ay8910_device::build_mixer_table() } else if (m_streams == NUM_CHANNELS) { - for (chan=0; chan < NUM_CHANNELS; chan++) + for (int chan = 0; chan < NUM_CHANNELS; chan++) { build_single_table(m_res_load[chan], m_par, normalize, m_vol_table[chan], m_zero_is_off); build_single_table(m_res_load[chan], m_par_env, normalize, m_env_table[chan], 0); @@ -1214,27 +1290,36 @@ void ay8910_device::build_mixer_table() void ay8910_device::ay8910_statesave() { + for (int chan = 0; chan < NUM_CHANNELS; chan++) + { + save_item(NAME(m_tone[chan].period), chan); + save_item(NAME(m_tone[chan].volume), chan); + save_item(NAME(m_tone[chan].duty), chan); + save_item(NAME(m_tone[chan].count), chan); + save_item(NAME(m_tone[chan].duty_cycle), chan); + save_item(NAME(m_tone[chan].output), chan); + + save_item(NAME(m_envelope[chan].period), chan); + save_item(NAME(m_envelope[chan].count), chan); + save_item(NAME(m_envelope[chan].step), chan); + save_item(NAME(m_envelope[chan].volume), chan); + save_item(NAME(m_envelope[chan].hold), chan); + save_item(NAME(m_envelope[chan].alternate), chan); + save_item(NAME(m_envelope[chan].attack), chan); + save_item(NAME(m_envelope[chan].holding), chan); + } + save_item(NAME(m_active)); save_item(NAME(m_register_latch)); save_item(NAME(m_regs)); save_item(NAME(m_last_enable)); - save_item(NAME(m_count)); save_item(NAME(m_count_noise)); - save_item(NAME(m_count_env)); - - save_item(NAME(m_env_volume)); - - save_item(NAME(m_output)); save_item(NAME(m_prescale_noise)); - save_item(NAME(m_env_step)); - save_item(NAME(m_hold)); - save_item(NAME(m_alternate)); - save_item(NAME(m_attack)); - save_item(NAME(m_holding)); save_item(NAME(m_rng)); save_item(NAME(m_mode)); + save_item(NAME(m_flags)); } //------------------------------------------------- @@ -1243,7 +1328,7 @@ void ay8910_device::ay8910_statesave() void ay8910_device::device_start() { - int master_clock = clock(); + const int master_clock = clock(); if (m_ioports < 1 && !(m_port_a_read_cb.isnull() && m_port_a_write_cb.isnull())) fatalerror("Device '%s' is a %s and has no port A!", tag(), name()); @@ -1267,33 +1352,29 @@ void ay8910_device::device_start() build_mixer_table(); /* The envelope is pacing twice as fast for the YM2149 as for the AY-3-8910, */ - /* This handled by the step parameter. Consequently we use a divider of 8 here. */ - m_channel = machine().sound().stream_alloc(*this, 0, m_streams, master_clock / 8); + /* This handled by the step parameter. Consequently we use a multipler of 2 here. */ + m_channel = machine().sound().stream_alloc(*this, 0, m_streams, master_clock * 2); ay_set_clock(master_clock); ay8910_statesave(); } - void ay8910_device::ay8910_reset_ym() { - int i; - m_active = false; m_register_latch = 0; m_rng = 1; - m_output[0] = 0; - m_output[1] = 0; - m_output[2] = 0; - m_count[0] = 0; - m_count[1] = 0; - m_count[2] = 0; + m_mode = 0; // ay-3-8910 compatible mode + for (int chan = 0; chan < NUM_CHANNELS; chan++) + { + m_tone[chan].reset(); + m_envelope[chan].reset(); + } m_count_noise = 0; - m_count_env = 0; m_prescale_noise = 0; m_last_enable = -1; /* force a write */ - for (i = 0; i < AY_PORTA; i++) + for (int i = 0; i < AY_PORTA; i++) ay8910_write_reg(i,0); m_ready = 1; #if ENABLE_REGISTER_TEST @@ -1308,17 +1389,15 @@ void ay8910_device::ay8910_reset_ym() ay8910_write_reg(AY_AVOL, 10); ay8910_write_reg(AY_BVOL, 10); ay8910_write_reg(AY_CVOL, 10); - //#define AY_EFINE (11) - //#define AY_ECOARSE (12) - //#define AY_ESHAPE (13) + //#define AY_EAFINE (11) + //#define AY_EACOARSE (12) + //#define AY_EASHAPE (13) #endif } void ay8910_device::set_volume(int channel,int volume) { - int ch; - - for (ch = 0; ch < m_streams; ch++) + for (int ch = 0; ch < m_streams; ch++) if (channel == ch || m_streams == 1 || channel == ALL_8910_CHANNELS) m_channel->set_output_gain(ch, volume / 100.0); } @@ -1326,11 +1405,10 @@ void ay8910_device::set_volume(int channel,int volume) void ay8910_device::ay_set_clock(int clock) { // FIXME: this doesn't belong here, it should be an input pin exposed via devcb - if (type() == YM2149 && (m_flags & YM2149_PIN26_LOW)) - { - clock /= 2; - } - m_channel->set_sample_rate( clock / 8 ); + if ((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW)) + m_channel->set_sample_rate(clock); + else + m_channel->set_sample_rate(clock * 2); } void ay8910_device::device_clock_changed() @@ -1344,14 +1422,15 @@ void ay8910_device::ay8910_write_ym(int addr, u8 data) { if (m_active) { + const u8 register_latch = m_register_latch + get_register_bank(); /* Data port */ - if (m_register_latch == AY_ESHAPE || m_regs[m_register_latch] != data) + if (m_register_latch == AY_EASHAPE || m_regs[register_latch] != data) { /* update the output buffer before changing the register */ m_channel->update(); } - ay8910_write_reg(m_register_latch, data); + ay8910_write_reg(register_latch, data); } } else @@ -1372,10 +1451,13 @@ void ay8910_device::ay8910_write_ym(int addr, u8 data) u8 ay8910_device::ay8910_read_ym() { device_type chip_type = type(); - int r = m_register_latch; + int r = m_register_latch + get_register_bank(); if (!m_active) return 0xff; // high impedance + if ((r & 0xf) == AY_EASHAPE) // shared register + r &= 0xf; + /* There are no state dependent register in the AY8910! */ /* m_channel->update(); */ @@ -1491,6 +1573,25 @@ void ay8910_device::write_bc1_bc2(offs_t offset, u8 data) } } +// /SEL +void ay8910_device::set_pin26_low_w(u8 data) +{ + if ((m_feature & PSG_PIN26_IS_CLKSEL) && (!(m_flags & YM2149_PIN26_LOW))) + { + m_flags |= YM2149_PIN26_LOW; + ay_set_clock(clock()); + } +} + +void ay8910_device::set_pin26_high_w(u8 data) +{ + if ((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW)) + { + m_flags &= ~YM2149_PIN26_LOW; + ay_set_clock(clock()); + } +} + static const u8 mapping8914to8910[16] = { 0, 2, 4, 11, 1, 3, 5, 12, 7, 6, 13, 8, 9, 10, 14, 15 }; u8 ay8914_device::read(offs_t offset) @@ -1517,7 +1618,7 @@ ay8910_device::ay8910_device(const machine_config &mconfig, const char *tag, dev } ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, - psg_type_t psg_type, int streams, int ioports) + psg_type_t psg_type, int streams, int ioports, int config) : device_t(mconfig, type, tag, owner, clock), device_sound_interface(mconfig, *this), m_type(psg_type), @@ -1530,35 +1631,30 @@ ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, co m_last_enable(0), m_prescale_noise(0), m_count_noise(0), - m_count_env(0), - m_env_step(0), - m_env_volume(0), - m_hold(0), - m_alternate(0), - m_attack(0), - m_holding(0), m_rng(0), m_mode(0), - m_env_step_mask(psg_type == PSG_TYPE_AY ? 0x0f : 0x1f), - m_step( psg_type == PSG_TYPE_AY ? 2 : 1), - m_zero_is_off( psg_type == PSG_TYPE_AY ? 1 : 0), - m_par( psg_type == PSG_TYPE_AY ? &ay8910_param : &ym2149_param), - m_par_env( psg_type == PSG_TYPE_AY ? &ay8910_param : &ym2149_param_env), + m_env_step_mask((!(m_feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), + m_step( (!(m_feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 32 : 16), + m_zero_is_off( (!(m_feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), + m_par( (!(m_feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), + m_par_env( (!(m_feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), m_flags(AY8910_LEGACY_OUTPUT), + m_feature(config), m_port_a_read_cb(*this), m_port_b_read_cb(*this), m_port_a_write_cb(*this), m_port_b_write_cb(*this) { memset(&m_regs,0,sizeof(m_regs)); - memset(&m_count,0,sizeof(m_count)); - memset(&m_output,0,sizeof(m_output)); + memset(&m_tone,0,sizeof(m_tone)); + memset(&m_envelope,0,sizeof(m_envelope)); memset(&m_vol_enabled,0,sizeof(m_vol_enabled)); memset(&m_vol_table,0,sizeof(m_vol_table)); memset(&m_env_table,0,sizeof(m_env_table)); m_res_load[0] = m_res_load[1] = m_res_load[2] = 1000; //Default values for resistor loads - set_type(psg_type); + // TODO : measure ay8930 volume parameters (PSG_TYPE_YM for temporary 5 bit handling) + set_type((m_feature & PSG_HAS_EXPANDED_MODE) ? PSG_TYPE_YM : psg_type); } void ay8910_device::set_type(psg_type_t psg_type) @@ -1567,7 +1663,7 @@ void ay8910_device::set_type(psg_type_t psg_type) if (psg_type == PSG_TYPE_AY) { m_env_step_mask = 0x0f; - m_step = 2; + m_step = 32; m_zero_is_off = 1; m_par = &ay8910_param; m_par_env = &ay8910_param; @@ -1575,7 +1671,7 @@ void ay8910_device::set_type(psg_type_t psg_type) else { m_env_step_mask = 0x1f; - m_step = 1; + m_step = 16; m_zero_is_off = 0; m_par = &ym2149_param; m_par_env = &ym2149_param_env; @@ -1601,7 +1697,7 @@ ay8913_device::ay8913_device(const machine_config &mconfig, const char *tag, dev DEFINE_DEVICE_TYPE(AY8914, ay8914_device, "ay8914", "AY-3-8914A PSG") ay8914_device::ay8914_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : ay8910_device(mconfig, AY8914, tag, owner, clock, PSG_TYPE_AY, 3, 2) + : ay8910_device(mconfig, AY8914, tag, owner, clock, PSG_TYPE_AY, 3, 2, PSG_EXTENDED_ENVELOPE) { } @@ -1609,7 +1705,7 @@ ay8914_device::ay8914_device(const machine_config &mconfig, const char *tag, dev DEFINE_DEVICE_TYPE(AY8930, ay8930_device, "ay8930", "AY8930 EPSG") ay8930_device::ay8930_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : ay8910_device(mconfig, AY8930, tag, owner, clock, PSG_TYPE_AY, 3, 2) + : ay8910_device(mconfig, AY8930, tag, owner, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE) { } @@ -1617,7 +1713,7 @@ ay8930_device::ay8930_device(const machine_config &mconfig, const char *tag, dev DEFINE_DEVICE_TYPE(YM2149, ym2149_device, "ym2149", "YM2149 SSG") ym2149_device::ym2149_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : ay8910_device(mconfig, YM2149, tag, owner, clock, PSG_TYPE_YM, 3, 2) + : ay8910_device(mconfig, YM2149, tag, owner, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) { } @@ -1625,7 +1721,7 @@ ym2149_device::ym2149_device(const machine_config &mconfig, const char *tag, dev DEFINE_DEVICE_TYPE(YM3439, ym3439_device, "ym3439", "YM3439 SSGC") ym3439_device::ym3439_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : ay8910_device(mconfig, YM3439, tag, owner, clock, PSG_TYPE_YM, 3, 2) + : ay8910_device(mconfig, YM3439, tag, owner, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) { } diff --git a/src/devices/sound/ay8910.h b/src/devices/sound/ay8910.h index c1bbf718245..685e8d837e5 100644 --- a/src/devices/sound/ay8910.h +++ b/src/devices/sound/ay8910.h @@ -43,8 +43,9 @@ #define AY8910_RESISTOR_OUTPUT (0x08) /* - * This define specifies the initial state of YM2149 - * pin 26 (SEL pin). By default it is set to high, + * This define specifies the initial state of + * YM2149, YM3439, AY8930 pin 26 (SEL pin). + * By default it is set to high, * compatible with AY8910. */ /* TODO: make it controllable while it's running (used by any hw???) */ @@ -61,6 +62,14 @@ public: PSG_TYPE_YM }; + enum config_t + { + PSG_DEFAULT = 0x0, + PSG_PIN26_IS_CLKSEL = 0x1, + PSG_EXTENDED_ENVELOPE = 0x2, + PSG_HAS_EXPANDED_MODE = 0x4 + }; + // construction/destruction ay8910_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); @@ -77,9 +86,13 @@ public: void address_w(u8 data); void data_w(u8 data); - /* /RES */ + // /RES void reset_w(u8 data = 0) { ay8910_reset_ym(); } + // /SEL + void set_pin26_low_w(u8 data = 0); + void set_pin26_high_w(u8 data = 0); + // use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' void data_address_w(offs_t offset, u8 data) { ay8910_write_ym(~offset & 1, data); } // note that directly connecting BC1 to A0 puts data on 0 and address on 1 @@ -113,7 +126,7 @@ public: protected: ay8910_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, - u32 clock, psg_type_t psg_type, int streams, int ioports); + u32 clock, psg_type_t psg_type, int streams, int ioports, int config = PSG_DEFAULT); // device-level overrides virtual void device_start() override; @@ -132,6 +145,134 @@ protected: private: static constexpr unsigned NUM_CHANNELS = 3; + /* register id's */ + enum + { + AY_AFINE = 0x00, + AY_ACOARSE = 0x01, + AY_BFINE = 0x02, + AY_BCOARSE = 0x03, + AY_CFINE = 0x04, + AY_CCOARSE = 0x05, + AY_NOISEPER = 0x06, + AY_ENABLE = 0x07, + AY_AVOL = 0x08, + AY_BVOL = 0x09, + AY_CVOL = 0x0a, + AY_EAFINE = 0x0b, + AY_EACOARSE = 0x0c, + AY_EASHAPE = 0x0d, + AY_PORTA = 0x0e, + AY_PORTB = 0x0f, + AY_EBFINE = 0x10, + AY_EBCOARSE = 0x11, + AY_ECFINE = 0x12, + AY_ECCOARSE = 0x13, + AY_EBSHAPE = 0x14, + AY_ECSHAPE = 0x15, + AY_ADUTY = 0x16, + AY_BDUTY = 0x17, + AY_CDUTY = 0x18, + AY_NOISEAND = 0x19, + AY_NOISEOR = 0x1a, + AY_TEST = 0x1f + }; + + // structs + struct tone_t + { + u32 period; + u8 volume; + u8 duty; + s32 count; + u8 duty_cycle; + u8 output; + + void reset() + { + period = 0; + volume = 0; + duty = 0; + count = 0; + duty_cycle = 0; + output = 0; + } + + void set_period(u8 fine, u8 coarse) + { + period = fine | (coarse << 8); + } + + void set_volume(u8 val) + { + volume = val; + } + + void set_duty(u8 val) + { + duty = val; + } + }; + + struct envelope_t + { + u32 period; + s32 count; + s8 step; + u32 volume; + u8 hold, alternate, attack, holding; + + void reset() + { + period = 0; + count = 0; + step = 0; + volume = 0; + hold = 0; + alternate = 0; + attack = 0; + holding = 0; + } + + void set_period(u8 fine, u8 coarse) + { + period = fine | (coarse << 8); + } + + void set_shape(u8 shape, u8 mask) + { + attack = (shape & 0x04) ? mask : 0x00; + if ((shape & 0x08) == 0) + { + /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ + hold = 1; + alternate = attack; + } + else + { + hold = shape & 0x01; + alternate = shape & 0x02; + } + step = mask; + holding = 0; + volume = (step ^ attack); + } + }; + + // inlines + inline bool tone_enable(int chan) { return BIT(m_regs[AY_ENABLE], chan); } + inline u8 tone_volume(tone_t *tone) { return tone->volume & (is_expnaded_mode() ? 0x1f : 0x0f); } + inline u8 tone_envelope(tone_t *tone) { return (tone->volume >> (is_expnaded_mode() ? 5 : 4)) & ((m_feature & PSG_EXTENDED_ENVELOPE) ? 3 : 1); } + inline u8 tone_duty(tone_t *tone) { return is_expnaded_mode() ? (tone->duty & 0x8 ? 0x8 : tone->duty) : 0x4; } + inline u8 get_envelope_chan(int chan) { return is_expnaded_mode() ? chan : 0; } + + inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); } + inline u8 noise_period() { return is_expnaded_mode() ? m_regs[AY_NOISEPER] & 0xff : (m_regs[AY_NOISEPER] & 0x1f) << 1; } + inline u8 noise_output() { return m_rng & 1; } + + inline bool is_expnaded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); } + inline u8 get_register_bank() { return is_expnaded_mode() ? (m_mode & 0x1) << 4 : 0; } + // internal helpers void set_type(psg_type_t psg_type); inline u16 mix_3D(); @@ -151,16 +292,12 @@ private: sound_stream *m_channel; bool m_active; s32 m_register_latch; - u8 m_regs[16]; + u8 m_regs[16 * 2]; s32 m_last_enable; - s32 m_count[NUM_CHANNELS]; - u8 m_output[NUM_CHANNELS]; + tone_t m_tone[NUM_CHANNELS]; + envelope_t m_envelope[NUM_CHANNELS]; u8 m_prescale_noise; s32 m_count_noise; - s32 m_count_env; - s8 m_env_step; - u32 m_env_volume; - u8 m_hold,m_alternate,m_attack,m_holding; s32 m_rng; u8 m_mode; u8 m_env_step_mask; @@ -174,6 +311,7 @@ private: s32 m_env_table[NUM_CHANNELS][32]; std::unique_ptr m_vol3d_table; int m_flags; /* Flags */ + int m_feature; /* Chip specific features */ int m_res_load[3]; /* Load on channel in ohms */ devcb_read8 m_port_a_read_cb; devcb_read8 m_port_b_read_cb; -- cgit v1.2.3-70-g09d2