diff options
Diffstat (limited to 'src/devices/sound/ay8910.cpp')
-rw-r--r-- | src/devices/sound/ay8910.cpp | 737 |
1 files changed, 355 insertions, 382 deletions
diff --git a/src/devices/sound/ay8910.cpp b/src/devices/sound/ay8910.cpp index 8ba53fb8b3f..92e9fcc7df3 100644 --- a/src/devices/sound/ay8910.cpp +++ b/src/devices/sound/ay8910.cpp @@ -1,193 +1,73 @@ // license:BSD-3-Clause // copyright-holders:Couriersud /* - * Couriersud, July 2014: - * - * This documents recent work on the AY8910. A YM2149 is now on it's way from - * Hong Kong as well. - * - * TODO: - * - * - Create a true sound device nAY8910 driver. - * - implement approach outlined below in this driver. - * - * For years I had a AY8910 in my drawer. Arduinos were around as well. - * Using the approach documented in this blog post - * http://www.986-studio.com/2014/05/18/another-ay-entry/#more-476 - * I measured the output voltages using a Extech 520. - * - * Measurement Setup - * - * Laptop <--> Arduino <---> AY8910 - * - * AY8910 Registers: - * 0x07: 3f - * 0x08: RV - * 0x09: RV - * 0x0A: RV - * - * Output was measured on Analog Output B with a resistor RD to - * ground. - * - * Measurement results: - * - * RD 983 9.830k 99.5k 1.001M open - * - * RV B B B B B - * 0 0.0000 0.0000 0.0001 0.0011 0.0616 - * 1 0.0106 0.0998 0.6680 1.8150 2.7260 - * 2 0.0150 0.1377 0.8320 1.9890 2.8120 - * 3 0.0222 0.1960 1.0260 2.1740 2.9000 - * 4 0.0320 0.2708 1.2320 2.3360 2.9760 - * 5 0.0466 0.3719 1.4530 2.4880 3.0440 - * 6 0.0665 0.4938 1.6680 2.6280 3.1130 - * 7 0.1039 0.6910 1.9500 2.7900 3.1860 - * 8 0.1237 0.7790 2.0500 2.8590 3.2340 - * 9 0.1986 1.0660 2.3320 3.0090 3.3090 - * 10 0.2803 1.3010 2.5050 3.0850 3.3380 - * 11 0.3548 1.4740 2.6170 3.1340 3.3590 - * 12 0.4702 1.6870 2.7340 3.1800 3.3730 - * 13 0.6030 1.8870 2.8410 3.2300 3.4050 - * 14 0.7530 2.0740 2.9280 3.2580 3.4170 - * 15 0.9250 2.2510 3.0040 3.2940 3.4380 - * - * Using an equivalent model approach with two resistors - * - * 5V - * | - * Z - * Z Resistor Value for RV - * Z - * | - * +---> Output signal - * | - * Z - * Z External RD - * Z - * | - * GND - * - * will NOT work out of the box since RV = RV(RD). - * - * The following approach will be used going forward based on die pictures - * of the AY8910 done by Dr. Stack van Hay: - * - * - * 5V - * _| D - * G | NMOS - * Vg ---|| Kn depends on volume selected - * |_ S Vs - * | - * | - * +---> VO Output signal - * | - * Z - * Z External RD - * Z - * | - * GND - * - * Whilst conducting, the FET operates in saturation mode: - * - * Id = Kn * (Vgs - Vth)^2 - * - * Using Id = Vs / RD - * - * Vs = Kn * RD * (Vg - Vs - Vth)^2 - * - * finally using Vg' = Vg - Vth - * - * Vs = Vg' + 1 / (2 * Kn * RD) - sqrt((Vg' + 1 / (2 * Kn * RD))^2 - Vg'^2) - * - * and finally - * - * VO = Vs - * - * and this can be used to re-Thenevin to 5V - * - * RVequiv = RD * ( 5V / VO - 1) - * - * The RV and Kn parameter are derived using least squares to match - * calculation results with measurements. - * - * FIXME: - * There is voltage of 60 mV measured with the EX520 (Ri ~ 10M). This may - * be induced by cutoff currents from the 15 FETs. - * - */ - - -/*************************************************************************** - - ay8910.cpp - - Emulation of the AY-3-8910 / YM2149 sound chip. - - Based on various code snippets by Ville Hallik, Michael Cuddy, - Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria. - - Mostly rewritten by couriersud in 2008 - - Public documentation: - - - http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/ - Die pictures of the AY8910 - - - US Patent 4933980 - - Games using ADSR: gyruss - - A list with more games using ADSR can be found here: - http://mametesters.org/view.php?id=3043 - - TODO: - * 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. - - The rewrite also introduces a generic model for the DAC. This model is - not perfect, but allows channel mixing based on a parametrized approach. - This model also allows to factor in different loads on individual channels. - If a better model is developped in the future or better measurements are - available, the driver should be easy to change. The model is described - later. - - In order to not break hundreds of existing drivers by default the flag - AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the - new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff). - In the case of channel mixing, output range is 0...3 * 7fff. - - The main difference between the AY-3-8910 and the YM2149 is, that the - AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by - registers 8 to 10 is "channel off". The YM2149 mentions, that the generated - signal has a 2V DC component. This is confirmed by measurements. The approach - taken here is to assume the 2V DC offset for all outputs for the YM2149. - For the AY-3-8910, an offset is used if envelope is active for a channel. - This is backed by oscilloscope pictures from the datasheet. If a fixed volume - is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings - I found on the web for gyruss indicate, that the AY-3-8910 offset should - be around 0.2V. This will also make sound levels more compatible with - user observations for scramble. - - The Model: - 5V 5V - | | - / | - Volume Level x >---| Z - > Z Pullup Resistor RU - | Z - Z | - Rx Z | - Z | - | | - '-----+--------> >---+----> Output signal - | | - Z Z - Pulldown RD Z Z Load RL - Z Z - | | - GND GND + +Emulation of the AY-3-8910 / YM2149 sound chip. + +Based on various code snippets by Ville Hallik, Michael Cuddy, +Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria. + +Mostly rewritten by couriersud in 2008 + +Public documentation: + +- http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/ + Die pictures of the AY8910 + +- US Patent 4933980 + +Games using ADSR: gyruss + +A list with more games using ADSR can be found here: + http://mametesters.org/view.php?id=3043 + +TODO: +* 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. + +The rewrite also introduces a generic model for the DAC. This model is +not perfect, but allows channel mixing based on a parametrized approach. +This model also allows to factor in different loads on individual channels. +If a better model is developed in the future or better measurements are +available, the driver should be easy to change. The model is described +later. + +In order to not break hundreds of existing drivers by default the flag +AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the +new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff). +In the case of channel mixing, output range is 0...3 * 7fff. + +The main difference between the AY-3-8910 and the YM2149 is, that the +AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by +registers 8 to 10 is "channel off". The YM2149 mentions, that the generated +signal has a 2V DC component. This is confirmed by measurements. The approach +taken here is to assume the 2V DC offset for all outputs for the YM2149. +For the AY-3-8910, an offset is used if envelope is active for a channel. +This is backed by oscilloscope pictures from the datasheet. If a fixed volume +is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings +I found on the web for gyruss indicate, that the AY-3-8910 offset should +be around 0.2V. This will also make sound levels more compatible with +user observations for scramble. + +The Model: + 5V 5V + | | + / | +Volume Level x >---| Z + > Z Pullup Resistor RU + | Z + Z | + Rx Z | + Z | + | | + '-----+--------> >---+----> Output signal + | | + Z Z + Pulldown RD Z Z Load RL + Z Z + | | + GND GND Each Volume level x will select a different resistor Rx. Measurements from fpgaarcade.com where used to calibrate channel mixing for the YM2149. This was done using @@ -228,29 +108,28 @@ has twice the steps, happening twice as fast. **************************************************************************** - The bus control and chip selection signals of the AY PSGs and their - pin-compatible clones such as YM2149 are somewhat unconventional and - redundant, having been designed for compatibility with GI's CP1610 - series of microprocessors. Much of the redundancy can be finessed by - tying BC2 to Vcc; AY-3-8913 and AY8930 do this internally. - - /A9 A8 /CS BDIR BC2 BC1 - AY-3-8910 24 25 n/a 27 28 29 - AY-3-8912 n/a 17 n/a 18 19 20 - AY-3-8913 22 23 24 2 n/a 3 - ------------------------------------ - Inactive NACT 0 0 0 - Latch address ADAR 0 0 1 - Inactive IAB 0 1 0 - Read from PSG DTB 0 1 1 - Latch address BAR 1 0 0 - Inactive DW 1 0 1 - Write to PSG DWS 1 1 0 - Latch address INTAK 1 1 1 - -***************************************************************************/ - -/** +The bus control and chip selection signals of the AY PSGs and their +pin-compatible clones such as YM2149 are somewhat unconventional and +redundant, having been designed for compatibility with GI's CP1610 +series of microprocessors. Much of the redundancy can be finessed by +tying BC2 to Vcc; AY-3-8913 and AY8930 do this internally. + + /A9 A8 /CS BDIR BC2 BC1 + AY-3-8910 24 25 n/a 27 28 29 + AY-3-8912 n/a 17 n/a 18 19 20 + AY-3-8913 22 23 24 2 n/a 3 + ------------------------------------ + Inactive NACT 0 0 0 + Latch address ADAR 0 0 1 + Inactive IAB 0 1 0 + Read from PSG DTB 0 1 1 + Latch address BAR 1 0 0 + Inactive DW 1 0 1 + Write to PSG DWS 1 1 0 + Latch address INTAK 1 1 1 + +**************************************************************************** + AY-3-8910(A)/8914/8916/8917/8930/YM2149 (others?): _______ _______ _| \__/ |_ @@ -578,29 +457,143 @@ AY-3-8910/8912/8913 post-1983 manual: http://map.grauw.nl/resources/sound/genera AY-8930 datasheet: http://www.ym2149.com/ay8930.pdf YM2149 datasheet: http://www.ym2149.com/ym2149.pdf YM2203 English datasheet: http://www.appleii-box.de/APPLE2/JonasCard/YM2203%20datasheet.pdf -YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/chip_ym2203.html +YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/chip_ymopn.html + +**************************************************************************** + +Couriersud, July 2014: + +This documents recent work on the AY8910. A YM2149 is now on it's way from +Hong Kong as well. + +TODO: +- Create a true sound device nAY8910 driver. +- implement approach outlined below in this driver. + +For years I had a AY8910 in my drawer. Arduinos were around as well. +Using the approach documented in this blog post + http://www.986-studio.com/2014/05/18/another-ay-entry/#more-476 +I measured the output voltages using a Extech 520. + +Measurement Setup + +Laptop <--> Arduino <---> AY8910 + +AY8910 Registers: +0x07: 3f +0x08: RV +0x09: RV +0x0A: RV + +Output was measured on Analog Output B with a resistor RD to +ground. + +Measurement results: + +RD 983 9.830k 99.5k 1.001M open + +RV B B B B B + 0 0.0000 0.0000 0.0001 0.0011 0.0616 + 1 0.0106 0.0998 0.6680 1.8150 2.7260 + 2 0.0150 0.1377 0.8320 1.9890 2.8120 + 3 0.0222 0.1960 1.0260 2.1740 2.9000 + 4 0.0320 0.2708 1.2320 2.3360 2.9760 + 5 0.0466 0.3719 1.4530 2.4880 3.0440 + 6 0.0665 0.4938 1.6680 2.6280 3.1130 + 7 0.1039 0.6910 1.9500 2.7900 3.1860 + 8 0.1237 0.7790 2.0500 2.8590 3.2340 + 9 0.1986 1.0660 2.3320 3.0090 3.3090 +10 0.2803 1.3010 2.5050 3.0850 3.3380 +11 0.3548 1.4740 2.6170 3.1340 3.3590 +12 0.4702 1.6870 2.7340 3.1800 3.3730 +13 0.6030 1.8870 2.8410 3.2300 3.4050 +14 0.7530 2.0740 2.9280 3.2580 3.4170 +15 0.9250 2.2510 3.0040 3.2940 3.4380 + +Using an equivalent model approach with two resistors + + 5V + | + Z + Z Resistor Value for RV + Z + | + +---> Output signal + | + Z + Z External RD + Z + | + GND + +will NOT work out of the box since RV = RV(RD). + +The following approach will be used going forward based on die pictures +of the AY8910 done by Dr. Stack van Hay: + + + 5V + _| D + G | NMOS + Vg ---|| Kn depends on volume selected + |_ S Vs + | + | + +---> VO Output signal + | + Z + Z External RD + Z + | + GND + +Whilst conducting, the FET operates in saturation mode: + +Id = Kn * (Vgs - Vth)^2 + +Using Id = Vs / RD + +Vs = Kn * RD * (Vg - Vs - Vth)^2 + +finally using Vg' = Vg - Vth + +Vs = Vg' + 1 / (2 * Kn * RD) - sqrt((Vg' + 1 / (2 * Kn * RD))^2 - Vg'^2) + +and finally + +VO = Vs + +and this can be used to re-Thenevin to 5V + +RVequiv = RD * ( 5V / VO - 1) + +The RV and Kn parameter are derived using least squares to match +calculation results with measurements. + +FIXME: +There is voltage of 60 mV measured with the EX520 (Ri ~ 10M). This may +be induced by cutoff currents from the 15 FETs. + */ #include "emu.h" #include "ay8910.h" +#define LOG_IGNORED_WRITES (1U << 1) +#define LOG_WARNINGS (1U << 2) +#define VERBOSE (LOG_WARNINGS) +#include "logmacro.h" + /************************************* * * constants * *************************************/ -#define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */ -#define LOG_IGNORED_WRITES (0) +#define ENABLE_REGISTER_TEST (0) // Enable preprogrammed registers static constexpr stream_buffer::sample_t MAX_OUTPUT = 1.0; -/************************************* - * - * Type definitions - * - *************************************/ - /************************************* * @@ -641,7 +634,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, @@ -650,11 +643,9 @@ static const ay8910_device::ay_ym_param ay8910_param = 4120, 2512, 1737, 1335, 1005, 747, 586, 451 }, }; -/* - * RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V - * These values correspond with guesses based on Gyruss schematics - * They work well with scramble as well. - */ +// RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V +// These values correspond with guesses based on Gyruss schematics +// They work well with scramble as well. static const ay8910_device::ay_ym_param ay8910_param = { 930, 454, @@ -663,12 +654,10 @@ static const ay8910_device::ay_ym_param ay8910_param = 4189, 2557, 1772, 1363, 1028, 766, 602, 464 }, }; -/* - * RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V - * These values correspond with guesses based on Gyruss schematics - * They work well with scramble as well. - */ -static const ay8910_device::ay_ym_param ay8910_param = +// RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V +// These values correspond with guesses based on Gyruss schematics +// They work well with scramble as well. + tatic const ay8910_device::ay_ym_param ay8910_param = { 1371, 313, 16, @@ -676,9 +665,7 @@ static const ay8910_device::ay_ym_param ay8910_param = 3814, 2337, 1629, 1263, 962, 727, 580, 458 }, }; -/* - * RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V - */ +// RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V static const ay8910_device::ay_ym_param ay8910_param = { 5806, 300, @@ -689,44 +676,45 @@ static const ay8910_device::ay_ym_param ay8910_param = #endif /* - * RL = 2000, Based on Matthew Westcott's measurements from Dec 2001. - * ------------------------------------------------------------------- - * - * http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e - * After what Russell mentioned a couple of weeks back about the lack of - * publicised measurements of AY chip volumes - I've finally got round to - * making these readings, and I'm placing them in the public domain - so - * anyone's welcome to use them in emulators or anything else. - - * To make the readings, I set up the chip to produce a constant voltage on - * channel C (setting bits 2 and 5 of register 6), and varied the amplitude - * (the low 4 bits of register 10). The voltages were measured between the - * channel C output (pin 1) and ground (pin 6). - * - * Level Voltage - * 0 1.147 - * 1 1.162 - * 2 1.169 - * 3 1.178 - * 4 1.192 - * 5 1.213 - * 6 1.238 - * 7 1.299 - * 8 1.336 - * 9 1.457 - * 10 1.573 - * 11 1.707 - * 12 1.882 - * 13 2.06 - * 14 2.32 - * 15 2.58 - * ------------------------------------------------------------------- - * - * The ZX spectrum output circuit was modelled in SwitcherCAD and - * the resistor values below create the voltage levels above. - * RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm. - */ +RL = 2000, Based on Matthew Westcott's measurements from Dec 2001. +------------------------------------------------------------------- + +http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e +After what Russell mentioned a couple of weeks back about the lack of +publicised measurements of AY chip volumes - I've finally got round to +making these readings, and I'm placing them in the public domain - so +anyone's welcome to use them in emulators or anything else. + +To make the readings, I set up the chip to produce a constant voltage on +channel C (setting bits 2 and 5 of register 6), and varied the amplitude +(the low 4 bits of register 10). The voltages were measured between the +channel C output (pin 1) and ground (pin 6). + +Level Voltage + 0 1.147 + 1 1.162 + 2 1.169 + 3 1.178 + 4 1.192 + 5 1.213 + 6 1.238 + 7 1.299 + 8 1.336 + 9 1.457 +10 1.573 +11 1.707 +12 1.882 +13 2.06 +14 2.32 +15 2.58 +------------------------------------------------------------------- + +The ZX spectrum output circuit was modelled in SwitcherCAD and +the resistor values below create the voltage levels above. +RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm. + +*/ static const ay8910_device::ay_ym_param ay8910_param = { @@ -763,7 +751,6 @@ static const ay8910_device::mosfet_param ay8910_mosfet_param = - /************************************* * * Inline @@ -826,7 +813,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 +893,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 +939,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]); @@ -968,8 +951,6 @@ void ay8910_device::ay8910_write_reg(int r, int v) m_tone[2].set_volume(m_regs[AY_CVOL]); break; case AY_EACOARSE: - if ((v & 0x0f) > 0) - osd_printf_verbose("ECoarse\n"); case AY_EAFINE: m_envelope[0].set_period(m_regs[AY_EAFINE], m_regs[AY_EACOARSE]); break; @@ -977,16 +958,16 @@ 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 */ - if (!m_port_a_write_cb.isnull()) + // write out 0xff if port set to input + if (!m_port_a_write_cb.isunset()) m_port_a_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x40) ? m_regs[AY_PORTA] : 0xff); } if ((m_last_enable == -1) || ((m_last_enable & 0x80) != (m_regs[AY_ENABLE] & 0x80))) { - /* write out 0xff if port set to input */ - if (!m_port_b_write_cb.isnull()) + // write out 0xff if port set to input + if (!m_port_b_write_cb.isunset()) m_port_b_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x80) ? m_regs[AY_PORTB] : 0xff); } m_last_enable = m_regs[AY_ENABLE]; @@ -1007,41 +988,35 @@ void ay8910_device::ay8910_write_reg(int r, int v) } } else if (m_mode & 0xf) - logerror("warning: activated unknown mode %02x at %s\n", m_mode & 0xf, name()); + LOGMASKED(LOG_WARNINGS, "%s: warning: activated unknown mode %02x at %s\n", machine().describe_context(), m_mode & 0xf, name()); } } - if ((v & 0x0f) > 0) - osd_printf_verbose("EShape\n"); m_envelope[0].set_shape(m_regs[AY_EASHAPE], m_env_step_mask); break; case AY_PORTA: if (m_regs[AY_ENABLE] & 0x40) { - if (!m_port_a_write_cb.isnull()) + if (!m_port_a_write_cb.isunset()) m_port_a_write_cb((offs_t)0, m_regs[AY_PORTA]); else - logerror("warning: unmapped write %02x to %s Port A\n", v, name()); + LOGMASKED(LOG_WARNINGS, "%s: warning: unmapped write %02x to %s Port A\n", machine().describe_context(), v, name()); } else { -#if LOG_IGNORED_WRITES - logerror("warning: write %02x to %s Port A set as input - ignored\n", v, name()); -#endif + LOGMASKED(LOG_IGNORED_WRITES, "%s: warning: write %02x to %s Port A set as input - ignored\n", machine().describe_context(), v, name()); } break; case AY_PORTB: if (m_regs[AY_ENABLE] & 0x80) { - if (!m_port_b_write_cb.isnull()) + if (!m_port_b_write_cb.isunset()) m_port_b_write_cb((offs_t)0, m_regs[AY_PORTB]); else - logerror("warning: unmapped write %02x to %s Port B\n", v, name()); + LOGMASKED(LOG_WARNINGS, "%s: warning: unmapped write %02x to %s Port B\n", machine().describe_context(), v, name()); } else { -#if LOG_IGNORED_WRITES - logerror("warning: write %02x to %s Port B set as input - ignored\n", v, name()); -#endif + LOGMASKED(LOG_IGNORED_WRITES, "%s: warning: write %02x to %s Port B set as input - ignored\n", machine().describe_context(), v, name()); } break; case AY_EBFINE: @@ -1069,7 +1044,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 @@ -1088,21 +1063,21 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vector<read_s int samples = outputs[0].samples(); - /* hack to prevent us from hanging when starting filtered outputs */ + // hack to prevent us from hanging when starting filtered outputs if (!m_ready) { for (int chan = 0; chan < m_streams; chan++) outputs[chan].fill(0); } - /* The 8910 has three outputs, each output is the mix of one of the three */ - /* tone generators and of the (single) noise generator. The two are mixed */ - /* BEFORE going into the DAC. The formula to mix each channel is: */ - /* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */ - /* Note that this means that if both tone and noise are disabled, the output */ - /* is 1, not 0, and can be modulated changing the volume. */ + // The 8910 has three outputs, each output is the mix of one of the three + // tone generators and of the (single) noise generator. The two are mixed + // BEFORE going into the DAC. The formula to mix each channel is: + // (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). + // Note that this means that if both tone and noise are disabled, the output + // is 1, not 0, and can be modulated changing the volume. - /* buffering loop */ + // buffering loop for (int sampindex = 0; sampindex < samples; sampindex++) { for (int chan = 0; chan < NUM_CHANNELS; chan++) @@ -1118,25 +1093,25 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vector<read_s } } - 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. - */ + // 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++) @@ -1145,20 +1120,19 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vector<read_s m_vol_enabled[chan] = (tone->output | 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) @@ -1170,8 +1144,8 @@ void ay8910_device::sound_stream_update(sound_stream &stream, std::vector<read_s } else { - /* if CountEnv has looped an odd number of times (usually 1), */ - /* invert the output. */ + // 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; @@ -1241,7 +1215,7 @@ void ay8910_device::build_mixer_table() if ((m_flags & AY8910_LEGACY_OUTPUT) != 0) { - logerror("%s using legacy output levels!\n", name()); + LOGMASKED(LOG_WARNINGS, "%s using legacy output levels!\n", name()); normalize = 1; } @@ -1264,10 +1238,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()); @@ -1297,10 +1269,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)); } @@ -1313,20 +1287,15 @@ void ay8910_device::device_start() { const int master_clock = clock(); - if (m_ioports < 1 && !(m_port_a_read_cb.isnull() && m_port_a_write_cb.isnull())) + if (m_ioports < 1 && !(m_port_a_read_cb.isunset() && m_port_a_write_cb.isunset())) fatalerror("Device '%s' is a %s and has no port A!", tag(), name()); - if (m_ioports < 2 && !(m_port_b_read_cb.isnull() && m_port_b_write_cb.isnull())) + if (m_ioports < 2 && !(m_port_b_read_cb.isunset() && m_port_b_write_cb.isunset())) fatalerror("Device '%s' is a %s and has no port B!", tag(), name()); - m_port_a_read_cb.resolve(); - m_port_b_read_cb.resolve(); - m_port_a_write_cb.resolve(); - m_port_b_write_cb.resolve(); - if ((m_flags & AY8910_SINGLE_OUTPUT) != 0) { - logerror("%s device using single output!\n", name()); + LOGMASKED(LOG_WARNINGS, "%s device using single output!\n", name()); m_streams = 1; } @@ -1334,8 +1303,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); @@ -1348,15 +1317,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; @@ -1406,10 +1377,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(); } @@ -1421,12 +1392,12 @@ 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 { - logerror("%s: warning - %s upper address mismatch\n", machine().describe_context(), name()); + LOGMASKED(LOG_WARNINGS, "%s: warning - %s upper address mismatch\n", machine().describe_context(), name()); } } } @@ -1434,21 +1405,21 @@ 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) { case AY_PORTA: if ((m_regs[AY_ENABLE] & 0x40) != 0) - logerror("%s: warning - read from %s Port A set as output\n", machine().describe_context(), name()); + LOGMASKED(LOG_WARNINGS, "%s: warning - read from %s Port A set as output\n", machine().describe_context(), name()); /* even if the port is set as output, we still need to return the external data. Some games, like kidniki, need this to work. @@ -1458,19 +1429,19 @@ u8 ay8910_device::ay8910_read_ym() specifies those pull up resistors as 60k to 600k (min / max). We do need a callback for those two flags. Kid Niki (Irem m62) is one such case were it makes a difference in comparison to a standard TTL output. - */ - if (!m_port_a_read_cb.isnull()) + */ + if (!m_port_a_read_cb.isunset()) m_regs[AY_PORTA] = m_port_a_read_cb(0); else - logerror("%s: warning - read 8910 Port A\n", machine().describe_context()); + LOGMASKED(LOG_WARNINGS, "%s: warning - read 8910 Port A\n", machine().describe_context()); break; case AY_PORTB: if ((m_regs[AY_ENABLE] & 0x80) != 0) - logerror("%s: warning - read from 8910 Port B set as output\n", machine().describe_context()); - if (!m_port_b_read_cb.isnull()) + LOGMASKED(LOG_WARNINGS, "%s: warning - read from 8910 Port B set as output\n", machine().describe_context()); + if (!m_port_b_read_cb.isunset()) m_regs[AY_PORTB] = m_port_b_read_cb(0); else - logerror("%s: warning - read 8910 Port B\n", machine().describe_context()); + LOGMASKED(LOG_WARNINGS, "%s: warning - read 8910 Port B\n", machine().describe_context()); break; } @@ -1513,6 +1484,7 @@ void ay8910_device::device_reset() ay8910_reset_ym(); } + /************************************* * * Read/Write Handlers @@ -1600,33 +1572,34 @@ 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, int feature) - : device_t(mconfig, type, tag, owner, clock), - device_sound_interface(mconfig, *this), - m_type(psg_type), - m_streams(streams), - m_ioports(ioports), - m_ready(0), - m_channel(nullptr), - m_active(false), - m_register_latch(0), - m_last_enable(0), - m_prescale_noise(0), - m_count_noise(0), - m_rng(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), - m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), - m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), - m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), - m_flags(AY8910_LEGACY_OUTPUT), - m_feature(feature), - m_port_a_read_cb(*this), - m_port_b_read_cb(*this), - m_port_a_write_cb(*this), - m_port_b_write_cb(*this) +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, int feature) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_type(psg_type), + m_streams(streams), + m_ioports(ioports), + m_ready(0), + m_channel(nullptr), + m_active(false), + 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), + m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), + m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), + m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), + m_flags(AY8910_LEGACY_OUTPUT), + m_feature(feature), + m_port_a_read_cb(*this, 0xff), + m_port_b_read_cb(*this, 0xff), + m_port_a_write_cb(*this), + m_port_b_write_cb(*this) { memset(&m_regs,0,sizeof(m_regs)); memset(&m_tone,0,sizeof(m_tone)); |