diff --git a/src/devices/sound/ay8910.cpp b/src/devices/sound/ay8910.cpp index c5f22df7243..1f0acab67d7 100644 --- a/src/devices/sound/ay8910.cpp +++ b/src/devices/sound/ay8910.cpp @@ -590,7 +590,7 @@ YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/c * *************************************/ -#define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */ +#define ENABLE_REGISTER_TEST (0) // Enable preprogrammed registers #define LOG_IGNORED_WRITES (0) static constexpr stream_buffer::sample_t MAX_OUTPUT = 1.0; @@ -641,7 +641,7 @@ static const ay8910_device::ay_ym_param ym2149_param_env = }; #if 0 -/* RL = 1000, Hacker Kay normalized, 2.1V to 3.2V */ +// RL = 1000, Hacker Kay normalized, 2.1V to 3.2V static const ay8910_device::ay_ym_param ay8910_param = { 664, 913, @@ -826,7 +826,7 @@ static inline void build_3D_table(double rl, const ay8910_device::ay_ym_param *p 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, stream_buffer::sample_t *tab, int zero_is_off) @@ -906,13 +906,9 @@ stream_buffer::sample_t ay8910_device::mix_3D() } } 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 |= env_mask | (m_vol_enabled[chan] ? env_volume << (chan*5) : 0); - } } else { @@ -956,7 +952,7 @@ void ay8910_device::ay8910_write_reg(int r, int v) m_tone[2].set_period(m_regs[AY_CFINE], coarse); break; case AY_NOISEPER: - /* No action required */ + // No action required break; case AY_AVOL: m_tone[0].set_volume(m_regs[AY_AVOL]); @@ -975,7 +971,7 @@ void ay8910_device::ay8910_write_reg(int r, int v) if ((m_last_enable == -1) || ((m_last_enable & 0x40) != (m_regs[AY_ENABLE] & 0x40))) { - /* write out 0xff if port set to input */ + // write out 0xff if port set to input if (!m_port_a_write_cb.isnull()) m_port_a_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x40) ? m_regs[AY_PORTA] : 0xff); } @@ -983,7 +979,7 @@ void ay8910_device::ay8910_write_reg(int r, int v) if ((m_last_enable == -1) || ((m_last_enable & 0x80) != (m_regs[AY_ENABLE] & 0x80))) { - /* write out 0xff if port set to input */ + // write out 0xff if port set to input if (!m_port_b_write_cb.isnull()) m_port_b_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x80) ? m_regs[AY_PORTB] : 0xff); } @@ -1065,7 +1061,7 @@ void ay8910_device::ay8910_write_reg(int r, int v) break; case AY_NOISEAND: case AY_NOISEOR: - // not implemented + // No action required break; default: m_regs[r] = 0; // reserved, set as 0 @@ -1084,21 +1080,21 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vector= noise_period()) + if ((++m_count_noise) >= noise_period()) { - /* toggle the prescaler output. Noise is no different to - * channels. - */ + // toggle the prescaler output. Noise is no different to + // channels. m_count_noise = 0; m_prescale_noise ^= 1; - if (!m_prescale_noise || is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode + // TODO : verify algorithm for AY8930 + if (is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode { - /* 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; + if ((++m_noise_value) >= ((u8(m_rng) & noise_and()) | noise_or())) + { + m_noise_value = 0; + m_noise_out ^= 1; + noise_rng_tick(); + } } + else if (!m_prescale_noise) + noise_rng_tick(); } for (int chan = 0; chan < NUM_CHANNELS; chan++) @@ -1141,20 +1137,19 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vectoroutput | tone_enable(chan)) & (noise_output() | noise_enable(chan)); } - /* update envelope */ + // update envelope for (int chan = 0; chan < NUM_CHANNELS; chan++) { envelope = &m_envelope[chan]; if (envelope->holding == 0) { const u32 period = envelope->period * m_step; - envelope->count++; - if (envelope->count >= period) + if ((++envelope->count) >= period) { envelope->count = 0; envelope->step--; - /* check envelope current position */ + // check envelope current position if (envelope->step < 0) { if (envelope->hold) @@ -1166,8 +1161,8 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vectoralternate && (envelope->step & (m_env_step_mask + 1))) envelope->attack ^= m_env_step_mask; @@ -1260,10 +1255,8 @@ void ay8910_device::build_mixer_table() build_single_table(m_res_load[chan], m_par_env, normalize, m_env_table[chan], 0); } } - /* - * The previous implementation added all three channels up instead of averaging them. - * The factor of 3 will force the same levels if normalizing is used. - */ + // The previous implementation added all three channels up instead of averaging them. + // The factor of 3 will force the same levels if normalizing is used. else { build_3D_table(m_res_load[0], m_par, m_par_env, normalize, 3, m_zero_is_off, m_vol3d_table.get()); @@ -1293,10 +1286,12 @@ void ay8910_device::ay8910_statesave() save_item(NAME(m_regs)); save_item(NAME(m_last_enable)); + save_item(NAME(m_noise_value)); save_item(NAME(m_count_noise)); save_item(NAME(m_prescale_noise)); save_item(NAME(m_rng)); + save_item(NAME(m_noise_out)); save_item(NAME(m_mode)); save_item(NAME(m_flags)); } @@ -1330,8 +1325,8 @@ 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 multipler of 2 here. */ + // 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 multipler of 2 here. m_channel = stream_alloc(0, m_streams, master_clock / 8); ay_set_clock(master_clock); @@ -1344,15 +1339,17 @@ void ay8910_device::ay8910_reset_ym() m_active = false; m_register_latch = 0; m_rng = 1; + m_noise_out = 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_noise_value = 0; m_count_noise = 0; m_prescale_noise = 0; - m_last_enable = -1; /* force a write */ + m_last_enable = -1; // force a write for (int i = 0; i < AY_PORTA; i++) ay8910_write_reg(i,0); m_ready = 1; @@ -1402,10 +1399,10 @@ 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 */ + // Data port if (m_register_latch == AY_EASHAPE || m_regs[register_latch] != data) { - /* update the output buffer before changing the register */ + // update the output buffer before changing the register m_channel->update(); } @@ -1417,7 +1414,7 @@ void ay8910_device::ay8910_write_ym(int addr, u8 data) m_active = (data >> 4) == 0; // mask programmed 4-bit code if (m_active) { - /* Register port */ + // Register port m_register_latch = data & 0x0f; } else @@ -1430,15 +1427,15 @@ 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 + get_register_bank(); + u8 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(); */ + // There are no state dependent register in the AY8910! + // m_channel->update(); switch (r) { @@ -1609,8 +1606,10 @@ ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, co m_register_latch(0), m_last_enable(0), m_prescale_noise(0), + m_noise_value(0), m_count_noise(0), m_rng(0), + m_noise_out(0), m_mode(0), m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), m_step( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 2 : 1), diff --git a/src/devices/sound/ay8910.h b/src/devices/sound/ay8910.h index 79cf180ac59..e17c5e1f367 100644 --- a/src/devices/sound/ay8910.h +++ b/src/devices/sound/ay8910.h @@ -76,7 +76,12 @@ public: // configuration helpers void set_flags(int flags) { m_flags = flags; } - void set_psg_type(psg_type_t psg_type) { set_type(psg_type); } + void set_psg_type(psg_type_t psg_type) + { + // Ignore when AY8930 for now + if (!(m_feature & PSG_HAS_EXPANDED_MODE)) + set_type(psg_type); + } void set_resistors_load(int res_load0, int res_load1, int res_load2) { m_res_load[0] = res_load0; m_res_load[1] = res_load1; m_res_load[2] = res_load2; } auto port_a_read_callback() { return m_port_a_read_cb.bind(); } auto port_b_read_callback() { return m_port_b_read_cb.bind(); } @@ -144,7 +149,7 @@ protected: private: static constexpr unsigned NUM_CHANNELS = 3; - /* register id's */ + // register id's enum { AY_AFINE = 0x00, @@ -243,7 +248,7 @@ private: attack = (shape & 0x04) ? mask : 0x00; if ((shape & 0x08) == 0) { - /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ + // if Continue = 0, map the shape to the equivalent one which has Continue = 1 hold = 1; alternate = attack; } @@ -258,6 +263,18 @@ private: } }; + inline void noise_rng_tick() + { + // 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. + + if (m_feature & PSG_HAS_EXPANDED_MODE) // AY8930 LFSR algorithm is slightly different, verified from manual + m_rng = (m_rng >> 1) | ((BIT(m_rng, 0) ^ BIT(m_rng, 2)) << 16); + else + m_rng = (m_rng >> 1) | ((BIT(m_rng, 0) ^ BIT(m_rng, 3)) << 16); + } + // 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_expanded_mode() ? 0x1f : 0x0f); } @@ -267,11 +284,14 @@ private: inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); } inline u8 noise_period() { return is_expanded_mode() ? m_regs[AY_NOISEPER] & 0xff : m_regs[AY_NOISEPER] & 0x1f; } - inline u8 noise_output() { return m_rng & 1; } + inline u8 noise_output() { return is_expanded_mode() ? m_noise_out & 1 : m_rng & 1; } inline bool is_expanded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); } inline u8 get_register_bank() { return is_expanded_mode() ? (m_mode & 0x1) << 4 : 0; } + inline u8 noise_and() { return m_regs[AY_NOISEAND] & 0xff; } + inline u8 noise_or() { return m_regs[AY_NOISEOR] & 0xff; } + // internal helpers void set_type(psg_type_t psg_type); inline stream_buffer::sample_t mix_3D(); @@ -286,17 +306,19 @@ private: int m_ready; sound_stream *m_channel; bool m_active; - s32 m_register_latch; + u8 m_register_latch; u8 m_regs[16 * 2]; - s32 m_last_enable; + s16 m_last_enable; tone_t m_tone[NUM_CHANNELS]; envelope_t m_envelope[NUM_CHANNELS]; u8 m_prescale_noise; - s32 m_count_noise; - s32 m_rng; + s16 m_noise_value; + s16 m_count_noise; + u32 m_rng; + u8 m_noise_out; u8 m_mode; u8 m_env_step_mask; - /* init parameters ... */ + // init parameters ... int m_step; int m_zero_is_off; u8 m_vol_enabled[NUM_CHANNELS]; @@ -305,9 +327,9 @@ private: stream_buffer::sample_t m_vol_table[NUM_CHANNELS][16]; stream_buffer::sample_t 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 */ + 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; devcb_write8 m_port_a_write_cb; @@ -337,7 +359,7 @@ class ay8914_device : public ay8910_device public: ay8914_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - /* AY8914 handlers needed due to different register map */ + // AY8914 handlers needed due to different register map u8 read(offs_t offset); void write(offs_t offset, u8 data); };