diff options
Diffstat (limited to 'src/devices/sound/ay8910.cpp')
-rw-r--r-- | src/devices/sound/ay8910.cpp | 882 |
1 files changed, 417 insertions, 465 deletions
diff --git a/src/devices/sound/ay8910.cpp b/src/devices/sound/ay8910.cpp index 6ae9518c29e..f93d0027a85 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,28 +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 LOG_OUTPUT_CONFIG (1U << 3) +#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 int MAX_OUTPUT = 0x7fff; - -/************************************* - * - * Type definitions - * - *************************************/ +static constexpr sound_stream::sample_t MAX_OUTPUT = 1.0; /************************************* @@ -641,7 +635,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 +644,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 +655,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 +666,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 +677,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,14 +752,13 @@ static const ay8910_device::mosfet_param ay8910_mosfet_param = - /************************************* * * Inline * *************************************/ -static inline void build_3D_table(double rl, const ay8910_device::ay_ym_param *par, const ay8910_device::ay_ym_param *par_env, int normalize, double factor, int zero_is_off, s32 *tab) +static inline void build_3D_table(double rl, const ay8910_device::ay_ym_param *par, const ay8910_device::ay_ym_param *par_env, int normalize, double factor, int zero_is_off, sound_stream::sample_t *tab) { double min = 10.0, max = 0.0; @@ -826,10 +814,10 @@ 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, s32 *tab, int zero_is_off) +static inline void build_single_table(double rl, const ay8910_device::ay_ym_param *par, int normalize, sound_stream::sample_t *tab, int zero_is_off) { double rt; double rw; @@ -867,7 +855,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) +static inline void build_mosfet_resistor_table(const ay8910_device::mosfet_param &par, const double rd, sound_stream::sample_t *tab) { for (int j = 0; j < par.m_count; j++) { @@ -878,18 +866,14 @@ static inline void build_mosfet_resistor_table(const ay8910_device::mosfet_param const double Vs = p2 - sqrt(p2 * p2 - Vg * Vg); const double res = rd * (Vd / Vs - 1.0); - /* That's the biggest value we can stream on to netlist. */ - if (res > (1 << 28)) - tab[j] = (1 << 28); - else - tab[j] = res; + tab[j] = res; //printf("%d %f %10d\n", j, rd / (res + rd) * 5.0, tab[j]); } } -u16 ay8910_device::mix_3D() +sound_stream::sample_t ay8910_device::mix_3D() { int indx = 0; @@ -910,13 +894,9 @@ u16 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 { @@ -960,7 +940,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]); @@ -972,26 +952,22 @@ 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; case AY_ENABLE: - if ((m_last_enable == -1) || - ((m_last_enable & 0x40) != (m_regs[AY_ENABLE] & 0x40))) + if (u8 enable = m_regs[AY_ENABLE] & 0x40; enable != (m_last_enable & 0x40)) { - /* 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); + // output is high-impedance if port is set to input + if (!m_port_a_write_cb.isunset()) + m_port_a_write_cb(0, enable ? m_regs[AY_PORTA] : 0xff, enable ? 0xff : 0); } - if ((m_last_enable == -1) || - ((m_last_enable & 0x80) != (m_regs[AY_ENABLE] & 0x80))) + if (u8 enable = m_regs[AY_ENABLE] & 0x80; enable != (m_last_enable & 0x80)) { - /* 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); + // output is high-impedance if port is set to input + if (!m_port_b_write_cb.isunset()) + m_port_b_write_cb(0, enable ? m_regs[AY_PORTB] : 0xff, enable ? 0xff : 0); } m_last_enable = m_regs[AY_ENABLE]; break; @@ -1011,41 +987,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()) - m_port_a_write_cb((offs_t)0, m_regs[AY_PORTA]); + if (!m_port_a_write_cb.isunset()) + m_port_a_write_cb(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()) - m_port_b_write_cb((offs_t)0, m_regs[AY_PORTB]); + if (!m_port_b_write_cb.isunset()) + m_port_b_write_cb(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: @@ -1073,7 +1043,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 @@ -1085,71 +1055,53 @@ void ay8910_device::ay8910_write_reg(int r, int v) // sound_stream_update - handle a stream update //------------------------------------------------- -void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void ay8910_device::sound_stream_update(sound_stream &stream) { - stream_sample_t *buf[NUM_CHANNELS]; tone_t *tone; envelope_t *envelope; - buf[0] = outputs[0]; - buf[1] = nullptr; - buf[2] = nullptr; - if (m_streams == NUM_CHANNELS) - { - buf[1] = outputs[1]; - buf[2] = outputs[2]; - } - - /* hack to prevent us from hanging when starting filtered outputs */ - if (!m_ready) - { - for (int chan = 0; chan < NUM_CHANNELS; chan++) - if (buf[chan] != nullptr) - memset(buf[chan], 0, samples * sizeof(*buf[chan])); - } - - /* 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 */ - while (samples) + // buffering loop + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) { for (int chan = 0; chan < NUM_CHANNELS; chan++) { tone = &m_tone[chan]; - tone->count++; - if (tone->count >= tone->period) + const int period = std::max<int>(1,tone->period); + tone->count += is_expanded_mode() ? 16 : 1; + while (tone->count >= period) { tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; - tone->output = (m_feature & PSG_HAS_EXPANDED_MODE) ? BIT(duty_cycle[tone_duty(tone)], tone->duty_cycle) : BIT(tone->duty_cycle, 0); - tone->count = 0; + tone->output = is_expanded_mode() ? BIT(duty_cycle[tone_duty(tone)], tone->duty_cycle) : BIT(tone->duty_cycle, 0); + tone->count -= period; } } - 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) + // 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; - m_prescale_noise = (m_feature & PSG_HAS_EXPANDED_MODE) ? 16 : 1; + if ((++m_noise_value) >= ((u8(m_rng) & noise_and()) | noise_or())) + { + m_noise_value = 0; + m_noise_out ^= 1; + noise_rng_tick(); + } } - m_prescale_noise--; + else if (!m_prescale_noise) + noise_rng_tick(); } for (int chan = 0; chan < NUM_CHANNELS; chan++) @@ -1158,20 +1110,19 @@ void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t ** 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) @@ -1183,8 +1134,8 @@ void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t ** } 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; @@ -1212,40 +1163,39 @@ void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t ** { 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]; + stream.put(chan, sampindex, 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]; + stream.put(chan, sampindex, 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]; + stream.put(chan, sampindex, 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]; + stream.put(chan, sampindex, m_env_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]; + stream.put(chan, sampindex, 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]; + stream.put(chan, sampindex, m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0]); } } else { if (is_expanded_mode()) - *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0]; + stream.put(chan, sampindex, 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]; + stream.put(chan, sampindex, m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0]); } } } else { - *(buf[0]++) = mix_3D(); + stream.put(0, sampindex, mix_3D()); } - samples--; } } @@ -1255,7 +1205,7 @@ void ay8910_device::build_mixer_table() if ((m_flags & AY8910_LEGACY_OUTPUT) != 0) { - logerror("%s using legacy output levels!\n", name()); + LOGMASKED(LOG_OUTPUT_CONFIG, "%s using legacy output levels!\n", name()); normalize = 1; } @@ -1278,10 +1228,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()); @@ -1290,34 +1238,33 @@ 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(STRUCT_MEMBER(m_tone, period)); + save_item(STRUCT_MEMBER(m_tone, volume)); + save_item(STRUCT_MEMBER(m_tone, duty)); + save_item(STRUCT_MEMBER(m_tone, count)); + save_item(STRUCT_MEMBER(m_tone, duty_cycle)); + save_item(STRUCT_MEMBER(m_tone, output)); + + save_item(STRUCT_MEMBER(m_envelope, period)); + save_item(STRUCT_MEMBER(m_envelope, count)); + save_item(STRUCT_MEMBER(m_envelope, step)); + save_item(STRUCT_MEMBER(m_envelope, volume)); + save_item(STRUCT_MEMBER(m_envelope, hold)); + save_item(STRUCT_MEMBER(m_envelope, alternate)); + save_item(STRUCT_MEMBER(m_envelope, attack)); + save_item(STRUCT_MEMBER(m_envelope, holding)); 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_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,30 +1277,25 @@ 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_OUTPUT_CONFIG, "%s device using single output!\n", name()); m_streams = 1; } - m_vol3d_table = make_unique_clear<s32[]>(8*32*32*32); + m_vol3d_table = make_unique_clear<sound_stream::sample_t[]>(8*32*32*32); 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. */ - m_channel = machine().sound().stream_alloc(*this, 0, m_streams, (m_feature & PSG_HAS_EXPANDED_MODE) ? master_clock * 2 : master_clock / 8); + // 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); ay8910_statesave(); @@ -1365,15 +1307,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 = 0xc0; // force a write for (int i = 0; i < AY_PORTA; i++) ay8910_write_reg(i,0); m_ready = 1; @@ -1399,16 +1343,16 @@ void ay8910_device::set_volume(int channel,int volume) { 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); + set_output_gain(ch, volume / 100.0); } void ay8910_device::ay_set_clock(int clock) { // FIXME: this doesn't belong here, it should be an input pin exposed via devcb - if ((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW)) - m_channel->set_sample_rate((m_feature & PSG_HAS_EXPANDED_MODE) ? clock : clock / 16); + if (((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW)) || (m_feature & PSG_HAS_INTERNAL_DIVIDER)) + m_channel->set_sample_rate(clock / 16); else - m_channel->set_sample_rate((m_feature & PSG_HAS_EXPANDED_MODE) ? clock * 2 : clock / 8); + m_channel->set_sample_rate(clock / 8); } void ay8910_device::device_clock_changed() @@ -1423,10 +1367,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(); } @@ -1438,12 +1382,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()); } } } @@ -1451,21 +1395,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()); + if (m_regs[AY_ENABLE] & 0x40) + 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. @@ -1475,19 +1419,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()) - m_regs[AY_PORTA] = m_port_a_read_cb(0); + */ + if (!m_port_a_read_cb.isunset()) + m_regs[AY_PORTA] = m_port_a_read_cb(); 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()) - m_regs[AY_PORTB] = m_port_b_read_cb(0); + if (m_regs[AY_ENABLE] & 0x80) + 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(); 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; } @@ -1530,6 +1474,7 @@ void ay8910_device::device_reset() ay8910_reset_ym(); } + /************************************* * * Read/Write Handlers @@ -1617,33 +1562,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)); @@ -1676,8 +1622,6 @@ void ay8910_device::set_type(psg_type_t psg_type) m_par = &ym2149_param; m_par_env = &ym2149_param_env; } - if (m_feature & PSG_HAS_EXPANDED_MODE) - m_step *= 16; } DEFINE_DEVICE_TYPE(AY8912, ay8912_device, "ay8912", "AY-3-8912A PSG") @@ -1742,3 +1686,11 @@ ymz294_device::ymz294_device(const machine_config &mconfig, const char *tag, dev : ay8910_device(mconfig, YMZ294, tag, owner, clock, PSG_TYPE_YM, 1, 0) { } + + +DEFINE_DEVICE_TYPE(SUNSOFT_5B_SOUND, sunsoft_5b_sound_device, "sunsoft_5b_sound", "Sunsoft/Yamaha 5B 6630B (Sound)") + +sunsoft_5b_sound_device::sunsoft_5b_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : ay8910_device(mconfig, SUNSOFT_5B_SOUND, tag, owner, clock, PSG_TYPE_YM, 1, 0, PSG_HAS_INTERNAL_DIVIDER) +{ +} |