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
This commit is contained in:
cam900 2019-09-10 05:02:17 +09:00 committed by R. Belmont
parent 68a379feab
commit f8df4c6098
2 changed files with 440 additions and 206 deletions

View File

@ -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<double> 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)
{
indx |= (1 << (chan+15)) | ( m_vol_enabled[chan] ? ((m_env_volume >> (3-TONE_ENVELOPE(chan))) << (chan*5)) : 0);
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 |= 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();
m_regs[r] = v;
if ((r & 0xf) == AY_EASHAPE) // shared register
r &= 0xf;
switch( r )
//if (r >= 11 && r <= 13) printf("%d %x %02x\n", PSG->index, r, v);
m_regs[r] = v;
u8 coarse;
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:
case AY_AVOL:
case AY_BVOL:
case AY_CVOL:
case AY_EFINE:
/* No action required */
break;
case AY_ECOARSE:
if ( (v & 0x0f) > 0)
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:
m_tone[2].set_volume(m_regs[AY_CVOL]);
break;
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)
{
if (m_alternate)
m_attack ^= m_env_step_mask;
m_holding = 1;
m_env_step = 0;
}
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;
envelope->count = 0;
envelope->step--;
m_env_step &= m_env_step_mask;
/* check envelope current position */
if (envelope->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;
}
}
}
}
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)
{
}

View File

@ -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<s32[]> 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;