furnace/src/engine/platform/sound/ay8910.cpp

1566 lines
52 KiB
C++

// 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
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
a least square approach using a fixed RL of 1K Ohm.
For the AY measurements cited in e.g. openmsx as "Hacker Kay" for a single
channel were taken. These were normalized to 0 ... 65535 and consequently
adapted to an offset of 0.2V and a VPP of 1.3V. These measurements are in
line e.g. with the formula used by pcmenc for the volume: vol(i) = exp(i/2-7.5).
The following is documentation from the code moved here and amended to reflect
the changes done:
Careful studies of the chip output prove that the chip counts up from 0
until the counter becomes greater or equal to the period. This is an
important difference when the program is rapidly changing the period to
modulate the sound. This is worthwhile noting, since the datasheets
say, that the chip counts down.
Also, note that period = 0 is the same as period = 1. This is mentioned
in the YM2203 data sheets. However, this does NOT apply to the Envelope
period. In that case, period = 0 is half as period = 1.
Envelope shapes:
C AtAlH
0 0 x x \___
0 1 x x /___
1 0 0 0 \\\\
1 0 0 1 \___
1 0 1 0 \/\/
1 0 1 1 \```
1 1 0 0 ////
1 1 0 1 /```
1 1 1 0 /\/\
1 1 1 1 /___
The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it
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
***************************************************************************/
/**
AY-3-8910(A)/8914/8916/8917/8930/YM2149 (others?):
_______ _______
_| \__/ |_
[4] VSS (GND) -- |_|1 * 40|_| -- VCC (+5v)
_| |_
[5] NC |_|2 39|_| <- TEST 1 [1]
_| |_
ANALOG CHANNEL B <- |_|3 38|_| -> ANALOG CHANNEL C
_| |_
ANALOG CHANNEL A <- |_|4 37|_| <> DA0
_| |_
[5] NC |_|5 36|_| <> DA1
_| |_
IOB7 <> |_|6 35|_| <> DA2
_| |_
IOB6 <> |_|7 34|_| <> DA3
_| /---\ |_
IOB5 <> |_|8 \-/ | A 33|_| <> DA4
_| . . Y |_
IOB4 <> |_|9 |---| - S 32|_| <> DA5
_| ' ' 3 O |_
IOB3 <> |_|10 8 - U 31|_| <> DA6
_| 3 8 N |_
IOB2 <> |_|11 0 9 D 30|_| <> DA7
_| 8 1 |_
IOB1 <> |_|12 0 29|_| <- BC1
_| P |_
IOB0 <> |_|13 28|_| <- BC2
_| |_
IOA7 <> |_|14 27|_| <- BDIR
_| |_ Prelim. DS: YM2149/8930:
IOA6 <> |_|15 26|_| <- TEST 2 [2,3] CS2 /SEL
_| |_
IOA5 <> |_|16 25|_| <- A8 [3] CS1
_| |_
IOA4 <> |_|17 24|_| <- /A9 [3] /CS0
_| |_
IOA3 <> |_|18 23|_| <- /RESET
_| |_
IOA2 <> |_|19 22|_| == CLOCK
_| |_
IOA1 <> |_|20 21|_| <> IOA0
|__________________|
[1] Based on the decap, TEST 1 connects to the Envelope Generator and/or the
frequency divider somehow. Is this an input or an output?
[2] The TEST 2 input connects to the same selector as A8 and /A9 do on the 8910
and acts as just another active high enable like A8(pin 25).
The preliminary datasheet calls this pin CS2.
On the 8914, it performs the same above function but additionally ?disables?
the DA0-7 bus if pulled low/active. This additional function was removed
on the 8910.
This pin has an internal pullup.
On the AY8930 and YM2149, this pin is /SEL; if low, clock input is halved.
[3] These 3 pins are technically enables, and have pullups/pulldowns such that
if the pins are left floating, the chip remains enabled.
[4] On the AY-3-8910 the bond wire for the VSS pin goes to the substrate frame,
and then a separate bond wire connects it to a pad between pins 21 and 22.
[5] These pins lack internal bond wires entirely.
AY-3-8912(A):
_______ _______
_| \__/ |_
ANALOG CHANNEL C <- |_|1 * 28|_| <> DA0
_| |_
TEST 1 -> |_|2 27|_| <> DA1
_| |_
VCC (+5V) -- |_|3 26|_| <> DA2
_| |_
ANALOG CHANNEL B <- |_|4 25|_| <> DA3
_| /---\ |_
ANALOG CHANNEL A <- |_|5 \-/ | A 24|_| <> DA4
_| . . Y |_
VSS (GND) -- |_|6 |---| - S 23|_| <> DA5
_| ' ' 3 O |_
IOA7 <> |_|7 T 8 - U 22|_| <> DA6
_| A 3 8 N |_
IOA6 <> |_|8 I 1 9 D 21|_| <> DA7
_| W 1 1 |_
IOA5 <> |_|9 A C 2 20|_| <- BC1
_| N D |_
IOA4 <> |_|10 A 19|_| <- BC2
_| |_
IOA3 <> |_|11 18|_| <- BDIR
_| |_
IOA2 <> |_|12 17|_| <- A8
_| |_
IOA1 <> |_|13 16|_| <- /RESET
_| |_
IOA0 <> |_|14 15|_| == CLOCK
|__________________|
AY-3-8913:
_______ _______
_| \__/ |_
[1] VSS (GND) -- |_|1 * 24|_| <- /CHIP SELECT [2]
_| |_
BDIR -> |_|2 23|_| <- A8
_| |_
BC1 -> |_|3 22|_| <- /A9
_| /---\ |_
DA7 <> |_|4 \-/ | A 21|_| <- /RESET
_| . . Y |_
DA6 <> |_|5 |---| - 20|_| == CLOCK
_| ' ' 3 |_
DA5 <> |_|6 T 8 - 19|_| -- VSS (GND) [1]
_| A 3 8 |_
DA4 <> |_|7 I 3 9 18|_| -> ANALOG CHANNEL C
_| W 2 1 |_
DA3 <> |_|8 A 3 17|_| -> ANALOG CHANNEL A
_| N C |_
DA2 <> |_|9 - 16|_| NC(?)
_| A |_
DA1 <> |_|10 15|_| -> ANALOG CHANNEL B
_| |_
DA0 <> |_|11 14|_| ?? TEST IN [3]
_| |_
[4] TEST OUT ?? |_|12 13|_| -- VCC (+5V)
|__________________|
[1] Both of these are ground, they are probably connected together internally. Grounding either one should work.
[2] This is effectively another enable, much like TEST 2 is on the AY-3-8910 and 8914, but active low
[3] This is claimed to be equivalent to TEST 1 on the datasheet
[4] This is claimed to be equivalent to TEST 2 on the datasheet
GI AY-3-8910/A Programmable Sound Generator (PSG): 2 I/O ports
A7 thru A4 enable state for selecting a register can be changed with a
factory mask adjustment but default was 0000 for the "common" part shipped
(probably die "-100").
Pins 24, 25, and 26 are /A9, A8, and TEST2, which are an active low, high
and high chip enable, respectively.
AY-3-8910: Unused bits in registers have unknown behavior.
AY-3-8910A: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
AY-3-8910 die is labeled "90-32033" with a 1979 copyright and a "-100" die
code.
AY-3-8910A die is labeled "90-32128" with a 1983 copyright.
GI AY-3-8912/A: 1 I/O port
/A9 pin doesn't exist and is considered pulled low.
TEST2 pin doesn't exist and is considered pulled high.
IOB pins do not exist and have unknown behavior if driven high/low and read
back.
A7 thru A4 enable state for selecting a register can be changed with a
factory mask adjustment but default was 0000 for the "common" part shipped
AY-3-8912: Unused bits in registers have unknown behavior.
AY-3-8912A: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
AY-3-8912 die is unknown.
AY-3-8912A or A/P die is unknown.
AY-3-8913: 0 I/O ports
BC2 pin doesn't exist and is considered pulled high.
IOA/B pins do not exist and have unknown behavior if driven high/low and read back.
A7 thru A4 enable state for selecting a register can be changed with a
factory mask adjustment but default was 0000 for the "common" part shipped
AY-3-8913: Unused bits in registers have unknown behavior.
AY-3-8913 die is unknown.
GI AY-3-8914/A: 2 I/O ports
A7 thru A4 enable state for selecting a register can be changed with a
factory mask adjustment but was 0000 for the part shipped with the
Intellivision.
Pins 24, 25, and 26 are /A9, A8, and TEST2, which are an active low, high
and high chip enable, respectively.
TEST2 additionally ?disables? the data bus if pulled low.
The register mapping is different from the AY-3-8910, the AY-3-8914 register
mapping matches the "preliminary" 1978 AY-3-8910 datasheet.
The Envelope/Volume control register is 6 bits wide instead of 5 bits, and
the additional bit combines with the M bit to form a bit pair C0 and C1,
which shift the volume output of the Envelope generator right by 0, 1 or 2
bits on a given channel, or allow the low 4 bits to drive the channel
volume.
AY-3-8914: Unused bits in registers have unknown behavior.
AY-3-8914A: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
AY-3-8914 die is labeled "90-32022" with a 1978 copyright.
AY-3-8914A die is unknown.
GI AY-3-8916: 2 I/O ports
A7 thru A4 enable state for selecting a register can be changed with a
factory mask adjustment; its mask is unknown. This chip was shipped
with certain later Intellivision II systems.
Pins 24, 25, and 26 are /A9, /A8(!), and TEST2, which are an active low,
low(!) and high chip enable, respectively.
NOTE: the /A8 enable polarity may be mixed up with AY-3-8917 below.
AY-3-8916: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
AY-3-8916 die is unknown.
GI AY-3-8917: 2 I/O ports
A7 thru A4 enable state for selecting a register can be changed with a
factory mask adjustment but was 1111 for the part shipped with the
Intellivision ECS module.
Pins 24, 25, and 26 are /A9, A8, and TEST2, which are an active low, high
and high chip enable, respectively.
NOTE: the A8 enable polarity may be mixed up with AY-3-8916 above.
AY-3-8917: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
AY-3-8917 die is unknown.
Microchip AY8930 Enhanced Programmable Sound Generator (EPSG): 2 I/O ports
BC2 pin exists but is always considered pulled high. The pin might have no
bond wire at all.
Pins 2 and 5 might be additional test pins rather than being NC.
A7 thru A4 enable state for selecting a register are 0000 for all? parts
shipped.
Pins 24 and 25 are /A9, A8 which are an active low and high chip enable.
Pin 26 is /SELECT which if driven low divides the input clock by 2.
Writing 0xAn or 0xBn to register 0x0D turns on extended mode, which enables
an additional 16 registers (banked using 0x0D bit 0), and clears the
contents of all of the registers except the high 3 bits of register 0x0D
(according to the datasheet).
If the AY8930's extended mode is enabled, it gains higher resolution
frequency and volume control, separate volume per-channel, and the duty
cycle can be adjusted for the 3 channels.
If the mode is not enabled, it behaves almost exactly like an AY-3-8910(A?),
barring the BC2 and /SELECT differences.
AY8930: Unused bits in registers have unknown behavior, but the datasheet
explicitly states that unused bits always read as 0.
I/O current source/sink behavior is unknown.
AY8930 die is unknown.
Yamaha YM2149 Software-Controlled Sound Generator (SSG): 2 I/O ports
A7 thru A4 enable state for selecting a register are 0000 for all? parts
shipped.
Pins 24 and 25 are /A9, A8 which are an active low and high chip enable.
Pin 26 is /SEL which if driven low divides the input clock by 2.
The YM2149 envelope register has 5 bits of resolution internally, allowing
for smoother volume ramping, though the register for setting its direct
value remains 4 bits wide.
YM2149: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
YM2149 die is unknown; only one die revision, 'G', has been observed
from Yamaha chip/datecode silkscreen surface markings.
Yamaha YM2203: 2 I/O ports
The pinout of this chip is completely different from the AY-3-8910.
The entire way this chip is accessed is completely different from the usual
AY-3-8910 selection of chips, so there is a /CS and a /RD and a /WR and
an A0 pin; The chip status can be read back by reading the register
select address.
The chip has a 3-channel, 4-op FM synthesis sound core in it, not discussed
in this source file.
The first 16 registers are the same(?) as the YM2149.
YM2203: Unused bits in registers have unknown behavior.
I/O current source/sink behavior is unknown.
YM2203 die is unknown; three die revisions, 'D', 'F' and 'H', have been
observed from Yamaha chip/datecode silkscreen surface markings. It is
unknown what behavioral differences exist between these revisions.
The 'D' revision only appears during the first year of production, 1984, on chips marked 'YM2203B'
The 'F' revision exists from 1984?-1991, chips are marked 'YM2203C'
The 'H' revision exists from 1991 onward, chips are marked 'YM2203C'
Yamaha YM3439: limited info: CMOS version of YM2149?
Yamaha YMZ284: limited info: 0 I/O port, different clock divider
The chip selection logic is again simplified here: pin 1 is /WR, pin 2 is
/CS and pin 3 is A0.
D0-D7 are conveniently all on one side of the 16-pin package.
Pin 8 is /IC (initial clear), with an internal pullup.
Yamaha YMZ294: limited info: 0 I/O port
Pinout is identical to YMZ284 except for two additions: pin 8 selects
between 4MHz (H) and 6MHz (L), while pin 10 is /TEST.
OKI M5255, Winbond WF19054, JFC 95101, File KC89C72, Toshiba T7766A : differences to be listed
AY8930 Expanded mode registers :
Bank Register Bits
A 0 xxxx xxxx Channel A Tone period fine tune
A 1 xxxx xxxx Channel A Tone period coarse tune
A 2 xxxx xxxx Channel B Tone period fine tune
A 3 xxxx xxxx Channel B Tone period coarse tune
A 4 xxxx xxxx Channel C Tone period fine tune
A 5 xxxx xxxx Channel C Tone period coarse tune
A 6 xxxx xxxx Noise period
A 7 x--- ---- I/O Port B input(0) / output(1)
-x-- ---- I/O Port A input(0) / output(1)
--x- ---- Channel C Noise enable(0) / disable(1)
---x ---- Channel B Noise enable(0) / disable(1)
---- x--- Channel A Noise enable(0) / disable(1)
---- -x-- Channel C Tone enable(0) / disable(1)
---- --x- Channel B Tone enable(0) / disable(1)
---- ---x Channel A Tone enable(0) / disable(1)
A 8 --x- ---- Channel A Envelope mode
---x xxxx Channel A Tone volume
A 9 --x- ---- Channel B Envelope mode
---x xxxx Channel B Tone volume
A A --x- ---- Channel C Envelope mode
---x xxxx Channel C Tone volume
A B xxxx xxxx Channel A Envelope period fine tune
A C xxxx xxxx Channel A Envelope period coarse tune
A D 101- ---- 101 = Expanded mode enable, other AY-3-8910A Compatiblity mode
---0 ---- 0 for Register Bank A
---- xxxx Channel A Envelope Shape/Cycle
A E xxxx xxxx 8 bit Parallel I/O on Port A
A F xxxx xxxx 8 bit Parallel I/O on Port B
B 0 xxxx xxxx Channel B Envelope period fine tune
B 1 xxxx xxxx Channel B Envelope period coarse tune
B 2 xxxx xxxx Channel C Envelope period fine tune
B 3 xxxx xxxx Channel C Envelope period coarse tune
B 4 ---- xxxx Channel B Envelope Shape/Cycle
B 5 ---- xxxx Channel C Envelope Shape/Cycle
B 6 ---- xxxx Channel A Duty Cycle
B 7 ---- xxxx Channel B Duty Cycle
B 8 ---- xxxx Channel C Duty Cycle
B 9 xxxx xxxx Noise "And" Mask
B A xxxx xxxx Noise "Or" Mask
B B Reserved (Read as 0)
B C Reserved (Read as 0)
B D 101- ---- 101 = Expanded mode enable, other AY-3-8910A Compatiblity mode
---1 ---- 1 for Register Bank B
---- xxxx Channel A Envelope Shape
B E Reserved (Read as 0)
B F Test (Function unknown)
Decaps:
AY-3-8914 - http://siliconpr0n.org/map/gi/ay-3-8914/mz_mit20x/
AY-3-8910 - http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/
AY-3-8910A - https://seanriddledecap.blogspot.com/2017/01/gi-ay-3-8910-ay-3-8910a-gi-8705-cba.html (TODO: update this link when it has its own page at seanriddle.com)
Links:
AY-3-8910 'preliminary' datasheet (which actually describes the AY-3-8914) from 1978:
http://spatula-city.org/~im14u2c/intv/gi_micro_programmable_tv_games/page_7_100.png
http://spatula-city.org/~im14u2c/intv/gi_micro_programmable_tv_games/page_7_101.png
http://spatula-city.org/~im14u2c/intv/gi_micro_programmable_tv_games/page_7_102.png
http://spatula-city.org/~im14u2c/intv/gi_micro_programmable_tv_games/page_7_103.png
http://spatula-city.org/~im14u2c/intv/gi_micro_programmable_tv_games/page_7_104.png
http://spatula-city.org/~im14u2c/intv/gi_micro_programmable_tv_games/page_7_105.png
AY-3-8910/8912 Feb 1979 manual: https://web.archive.org/web/20140217224114/http://dev-docs.atariforge.org/files/GI_AY-3-8910_Feb-1979.pdf
AY-3-8910/8912/8913 post-1983 manual: http://map.grauw.nl/resources/sound/generalinstrument_ay-3-8910.pdf or http://www.ym2149.com/ay8910.pdf
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
*/
// additional modifications by tildearrow, Eulous, cam900 and Grauw for furnace (particularly AY8930 emulation)
#include "ay8910.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <vector>
/*************************************
*
* constants
*
*************************************/
#define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */
#define LOG_IGNORED_WRITES (0)
static constexpr float MAX_OUTPUT = 1.0;
/*************************************
*
* Type definitions
*
*************************************/
/*************************************
*
* Static
*
*************************************/
// duty cycle used for AY8930 expanded mode
static const unsigned int 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,
16,
{ 73770, 37586, 27458, 21451, 15864, 12371, 8922, 6796,
4763, 3521, 2403, 1737, 1123, 762, 438, 251 },
};
static const ay8910_device::ay_ym_param ym2149_param_env =
{
630, 801,
32,
{ 103350, 73770, 52657, 37586, 32125, 27458, 24269, 21451,
18447, 15864, 14009, 12371, 10506, 8922, 7787, 6796,
5689, 4763, 4095, 3521, 2909, 2403, 2043, 1737,
1397, 1123, 925, 762, 578, 438, 332, 251 },
};
#if 0
/* RL = 1000, Hacker Kay normalized, 2.1V to 3.2V */
static const ay8910_device::ay_ym_param ay8910_param =
{
664, 913,
16,
{ 85785, 34227, 26986, 20398, 14886, 10588, 7810, 4856,
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.
*/
static const ay8910_device::ay_ym_param ay8910_param =
{
930, 454,
16,
{ 85066, 34179, 27027, 20603, 15046, 10724, 7922, 4935,
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 =
{
1371, 313,
16,
{ 93399, 33289, 25808, 19285, 13940, 9846, 7237, 4493,
3814, 2337, 1629, 1263, 962, 727, 580, 458 },
};
/*
* RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V
*/
static const ay8910_device::ay_ym_param ay8910_param =
{
5806, 300,
16,
{ 118996, 42698, 33105, 24770, 17925, 12678, 9331, 5807,
4936, 3038, 2129, 1658, 1271, 969, 781, 623 }
};
#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.
*/
static const ay8910_device::ay_ym_param ay8910_param =
{
800000, 8000000,
16,
{ 15950, 15350, 15090, 14760, 14275, 13620, 12890, 11370,
10600, 8590, 7190, 5985, 4820, 3945, 3017, 2345 }
};
static const ay8910_device::mosfet_param ay8910_mosfet_param =
{
1.465385778,
4.9,
16,
{
0.00076,
0.80536,
1.13106,
1.65952,
2.42261,
3.60536,
5.34893,
8.96871,
10.97202,
19.32370,
29.01935,
38.82026,
55.50539,
78.44395,
109.49257,
153.72985,
}
};
/*************************************
*
* 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, short *tab)
{
double min = 10.0, max = 0.0;
std::vector<double> temp(8*32*32*32, 0);
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++)
{
double n;
if (zero_is_off)
{
n = (j1 != 0 || (e & 0x01)) ? 1 : 0;
n += (j2 != 0 || (e & 0x02)) ? 1 : 0;
n += (j3 != 0 || (e & 0x04)) ? 1 : 0;
}
else
n = 3.0;
double rt = n / par->r_up + 3.0 / par->r_down + 1.0 / rl;
double rw = n / par->r_up;
rw += 1.0 / par_ch1->res[j1];
rt += 1.0 / par_ch1->res[j1];
rw += 1.0 / par_ch2->res[j2];
rt += 1.0 / par_ch2->res[j2];
rw += 1.0 / par_ch3->res[j3];
rt += 1.0 / par_ch3->res[j3];
int indx = (e << 15) | (j3 << 10) | (j2 << 5) | j1;
temp[indx] = rw / rt;
if (temp[indx] < min)
min = temp[indx];
if (temp[indx] > max)
max = temp[indx];
}
}
if (normalize)
{
for (int j = 0; j < 32*32*32*8; j++)
tab[j] = 8000 * MAX_OUTPUT * (((temp[j] - min)/(max-min))) * factor;
}
else
{
for (int j = 0; j < 32*32*32*8; j++)
tab[j] = 8000 * MAX_OUTPUT * temp[j];
}
/* 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, short *tab, int zero_is_off)
{
double rt;
double rw;
double temp[32], min = 10.0, max = 0.0;
for (int j = 0; j < par->res_count; j++)
{
rt = 1.0 / par->r_down + 1.0 / rl;
rw = 1.0 / par->res[j];
rt += 1.0 / par->res[j];
if (!(zero_is_off && j == 0))
{
rw += 1.0 / par->r_up;
rt += 1.0 / par->r_up;
}
temp[j] = rw / rt;
if (temp[j] < min)
min = temp[j];
if (temp[j] > max)
max = temp[j];
}
if (normalize)
{
for (int j = 0; j < par->res_count; j++)
tab[j] = 16384 * MAX_OUTPUT * (((temp[j] - min)/(max-min)) - 0.25) * 0.5;
}
else
{
for (int j = 0; j < par->res_count; j++)
tab[j] = 16384 * MAX_OUTPUT * temp[j];
}
}
static inline void build_mosfet_resistor_table(const ay8910_device::mosfet_param &par, const double rd, short *tab)
{
for (int j = 0; j < par.m_count; j++)
{
const double Vd = 5.0;
const double Vg = par.m_Vg - par.m_Vth;
const double kn = par.m_Kn[j] / 1.0e6;
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);
tab[j] = res * 16384;
//printf("%d %f %10d\n", j, rd / (res + rd) * 5.0, tab[j]);
}
}
float ay8910_device::mix_3D()
{
int indx = 0;
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone_t *tone = &m_tone[chan];
if (tone_envelope(tone) != 0)
{
envelope_t *envelope = &m_envelope[get_envelope_chan(chan)];
unsigned int env_volume = envelope->volume;
unsigned int env_mask = (1 << (chan + 15));
if (m_feature & PSG_HAS_EXPANDED_MODE)
{
if (!is_expanded_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 |= env_mask | (m_vol_enabled[chan] ? env_volume << (chan*5) : 0);
}
}
else
{
const unsigned int tone_mask = is_expanded_mode() ? (1 << (chan + 15)) : 0;
indx |= tone_mask | (m_vol_enabled[chan] ? tone_volume(tone) << (chan*5) : 0);
}
}
lastIndx=indx;
return m_vol3d_table[indx];
}
/*************************************
*
* Static functions
*
*************************************/
void ay8910_device::ay8910_write_reg(int r, int v)
{
if ((r & 0xf) == AY_EASHAPE) // shared register
r &= 0xf;
//if (r >= 11 && r <= 13) printf("%d %x %02x\n", PSG->index, r, v);
m_regs[r] = v;
unsigned char coarse;
switch(r)
{
case AY_AFINE:
case AY_ACOARSE:
coarse = m_regs[AY_ACOARSE] & (is_expanded_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_expanded_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_expanded_mode() ? 0xff : 0xf);
m_tone[2].set_period(m_regs[AY_CFINE], coarse);
break;
case AY_NOISEPER:
/* No action required */
break;
case AY_AVOL:
m_tone[0].set_volume(m_regs[AY_AVOL]);
break;
case AY_BVOL:
m_tone[1].set_volume(m_regs[AY_BVOL]);
break;
case AY_CVOL:
m_tone[2].set_volume(m_regs[AY_CVOL]);
break;
case AY_EACOARSE:
case AY_EAFINE:
m_envelope[0].set_period(m_regs[AY_EAFINE], m_regs[AY_EACOARSE]);
break;
case AY_ENABLE:
m_last_enable = m_regs[AY_ENABLE];
break;
case AY_EASHAPE:
if (m_feature & PSG_HAS_EXPANDED_MODE)
{
const unsigned char old_mode = m_mode;
m_mode = (v >> 4) & 0xf;
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);
}
}
}
}
m_envelope[0].set_shape(m_regs[AY_EASHAPE], m_env_step_mask);
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:
// No action required
break;
default:
m_regs[r] = 0; // reserved, set as 0
break;
}
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void ay8910_device::sound_stream_update(short** outputs, int outLen)
{
tone_t *tone;
envelope_t *envelope;
int samples = outLen;
/* hack to prevent us from hanging when starting filtered outputs */
if (!m_ready)
{
for (int chan = 0; chan < m_streams; chan++)
memset(outputs[chan],0,outLen*sizeof(short));
}
/* 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 */
for (int sampindex = 0; sampindex < samples; sampindex++)
{
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
const int period = std::max<int>(1, tone->period) * (m_step_mul << 1);
tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2);
while (tone->count >= period)
{
tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f;
tone->output = is_expanded_mode() ? BIT(duty_cycle[tone_duty(tone)], tone->duty_cycle) : BIT(tone->duty_cycle, 0);
tone->count -= period;
}
}
const int period_noise = (int)(noise_period()) * m_step_mul;
if ((++m_count_noise) >= period_noise)
{
/* toggle the prescaler output. Noise is no different to
* channels.
*/
m_count_noise = 0;
m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1);
if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode
{
// This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR.
// Using AND/OR gates, specific periods can be "filtered" out.
// A square wave can be generated through this behavior, which can be used for crude AM pulse width modulation.
// The period of the noise is determined by this value.
// The least significant byte of the LFSR is bitwise ANDed with the AND mask, and then bitwise ORed with the OR mask.
if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value.
{
m_noise_value = 0;
// When everything is finally said and done, a 1bit latch is flipped.
// This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel.
m_noise_out ^= 1;
noise_rng_tick();
}
}
else if (!m_prescale_noise)
noise_rng_tick();
}
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
m_vol_enabled[chan] = (tone->output | (unsigned char)tone_enable(chan)) & (noise_output() | (unsigned char)noise_enable(chan));
}
/* update envelope */
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
envelope = &m_envelope[chan];
if (envelope->holding == 0)
{
const int period = std::max<int>(1, envelope->period) * m_env_step_mul;
if ((++envelope->count) >= period)
{
envelope->count = 0;
envelope->step--;
/* 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);
}
if (m_streams == 3)
{
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
if (tone_envelope(tone) != 0)
{
envelope = &m_envelope[get_envelope_chan(chan)];
unsigned int env_volume = envelope->volume;
if (m_feature & PSG_HAS_EXPANDED_MODE)
{
if (!is_expanded_mode())
{
env_volume >>= 1;
if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field
outputs[chan][sampindex]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0];
else
outputs[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
outputs[chan][sampindex]=m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0];
else
outputs[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
outputs[chan][sampindex]=m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0];
else
outputs[chan][sampindex]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
}
else
{
if (is_expanded_mode())
outputs[chan][sampindex]=m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
else
outputs[chan][sampindex]=m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
}
}
}
else
{
outputs[0][sampindex]=mix_3D();
}
}
}
void ay8910_device::build_mixer_table()
{
int normalize = 0;
if ((m_flags & AY8910_LEGACY_OUTPUT) != 0)
{
normalize = 1;
}
if ((m_flags & AY8910_RESISTOR_OUTPUT) != 0)
{
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]);
}
}
else if (m_streams == NUM_CHANNELS)
{
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);
}
}
/*
* 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);
}
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void ay8910_device::device_start()
{
if ((m_flags & AY8910_SINGLE_OUTPUT) != 0)
{
m_streams = 1;
}
build_mixer_table();
}
void ay8910_device::ay8910_reset_ym()
{
m_active = false;
m_register_latch = 0;
m_rng = (m_feature & PSG_HAS_EXPANDED_MODE) ? 0x1ffff : 1;
m_mode = 0; // ay-3-8910 compatible mode
m_noise_value = 0;
m_noise_out = 0;
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
m_tone[chan].reset();
m_envelope[chan].reset();
}
m_count_noise = 0;
m_prescale_noise = 0;
m_last_enable = -1; /* force a write */
for (int i = 0; i < AY_PORTA; i++)
ay8910_write_reg(i,0);
m_ready = 1;
#if ENABLE_REGISTER_TEST
ay8910_write_reg(AY_AFINE, 0);
ay8910_write_reg(AY_ACOARSE, 1);
ay8910_write_reg(AY_BFINE, 0);
ay8910_write_reg(AY_BCOARSE, 2);
ay8910_write_reg(AY_CFINE, 0);
ay8910_write_reg(AY_CCOARSE, 4);
//#define AY_NOISEPER (6)
ay8910_write_reg(AY_ENABLE, ~7);
ay8910_write_reg(AY_AVOL, 10);
ay8910_write_reg(AY_BVOL, 10);
ay8910_write_reg(AY_CVOL, 10);
//#define AY_EAFINE (11)
//#define AY_EACOARSE (12)
//#define AY_EASHAPE (13)
#endif
}
void ay8910_device::ay8910_write_ym(int addr, unsigned char data)
{
if (addr & 1)
{
if (m_active)
{
const unsigned char register_latch = m_register_latch + get_register_bank();
/* Data port */
if (m_register_latch == AY_EASHAPE || m_regs[register_latch] != data)
{
/* update the output buffer before changing the register */
}
ay8910_write_reg(register_latch, data);
}
}
else
{
m_active = (data >> 4) == 0; // mask programmed 4-bit code
if (m_active)
{
/* Register port */
m_register_latch = data & 0x0f;
}
}
}
unsigned char ay8910_device::ay8910_read_ym()
{
unsigned char 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! */
/* Depending on chip type, unused bits in registers may or may not be accessible.
Untested chips are assumed to regard them as 'ram'
Tested and confirmed on hardware:
- AY-3-8910: inaccessible bits (see masks below) read back as 0
- AY-3-8914: same as 8910 except regs B,C,D (8,9,A below due to 8910->8914 remapping) are 0x3f
- AY-3-8916/8917 (used on ECS INTV expansion): inaccessible bits mirror one of the i/o ports, needs further testing
- YM2149: no anomaly
*/
if (chip_type == AY8910) {
const unsigned char mask[0x10]={
0xff,0x0f,0xff,0x0f,0xff,0x0f,0x1f,0xff,0x1f,0x1f,0x1f,0xff,0xff,0x0f,0xff,0xff
};
return m_regs[r] & mask[r];
}
else if (chip_type == AY8914) {
const unsigned char mask[0x10]={
0xff,0x0f,0xff,0x0f,0xff,0x0f,0x1f,0xff,0x3f,0x3f,0x3f,0xff,0xff,0x0f,0xff,0xff
};
return m_regs[r] & mask[r];
}
else return m_regs[r];
}
/*************************************
*
* Sound Interface
*
*************************************/
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void ay8910_device::device_reset()
{
lastIndx=0;
ay8910_reset_ym();
}
/*************************************
*
* Read/Write Handlers
*
*************************************/
void ay8910_device::address_w(unsigned char data)
{
#if ENABLE_REGISTER_TEST
return;
#else
ay8910_write_ym(0, data);
#endif
}
void ay8910_device::data_w(unsigned char data)
{
#if ENABLE_REGISTER_TEST
return;
#else
ay8910_write_ym(1, data);
#endif
}
// here, BC1 is hooked up to A0 on the host and BC2 is hooked up to A1
void ay8910_device::write_bc1_bc2(int offset, unsigned char data)
{
switch (offset & 3)
{
case 0: // latch address
address_w(data);
break;
case 1: // inactive
break;
case 2: // write to psg
data_w(data);
break;
case 3: // latch address
address_w(data);
break;
}
}
static const unsigned char mapping8914to8910[16] = { 0, 2, 4, 11, 1, 3, 5, 12, 7, 6, 13, 8, 9, 10, 14, 15 };
unsigned char ay8914_device::read(int offset)
{
unsigned char rv;
address_w(mapping8914to8910[offset & 0xf]);
rv = data_r();
return rv;
}
void ay8914_device::write(int offset, unsigned char data)
{
address_w(mapping8914to8910[offset & 0xf]);
data_w(data & 0xff);
}
ay8910_device::ay8910_device(unsigned int clock)
: ay8910_device(AY8910, clock, PSG_TYPE_AY, 3, 2)
{
}
ay8910_device::ay8910_device(device_type type, unsigned int clock,
psg_type_t psg_type, int streams, int ioports, int feature, bool clk_sel)
: chip_type(type),
m_type(psg_type),
m_streams(streams),
m_ready(0),
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_mul( ((feature & PSG_HAS_INTERNAL_DIVIDER) || ((feature & PSG_PIN26_IS_CLKSEL) && clk_sel)) ? 2 : 1),
m_env_step_mul( ((feature & PSG_HAS_EXPANDED_MODE) || (psg_type == PSG_TYPE_AY)) ? (m_step_mul << 1) : m_step_mul),
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 | (((feature & PSG_PIN26_IS_CLKSEL) && clk_sel) ? YM2149_PIN26_LOW : 0)),
m_feature(feature)
{
memset(&m_regs,0,sizeof(m_regs));
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
// 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, clk_sel);
}
void ay8910_device::set_type(psg_type_t psg_type, bool clk_sel)
{
m_type = psg_type;
if (psg_type == PSG_TYPE_AY)
{
m_env_step_mask = 0x0f;
m_env_step_mul = is_clock_divided() ? 4 : 2;
m_zero_is_off = 1;
m_par = &ay8910_param;
m_par_env = &ay8910_param;
}
else
{
m_env_step_mask = 0x1f;
m_env_step_mul = is_clock_divided() ? 2 : 1;
m_zero_is_off = 0;
m_par = &ym2149_param;
m_par_env = &ym2149_param_env;
}
if (m_feature & PSG_HAS_EXPANDED_MODE)
m_env_step_mul <<= 1;
set_clock_sel(clk_sel);
}
ay8912_device::ay8912_device(unsigned int clock)
: ay8910_device(AY8912, clock, PSG_TYPE_AY, 3, 1)
{
}
ay8913_device::ay8913_device(unsigned int clock)
: ay8910_device(AY8913, clock, PSG_TYPE_AY, 3, 0)
{
}
ay8914_device::ay8914_device(unsigned int clock)
: ay8910_device(AY8914, clock, PSG_TYPE_AY, 3, 2, PSG_EXTENDED_ENVELOPE)
{
}
ay8930_device::ay8930_device(unsigned int clock, bool clk_sel)
: ay8910_device(AY8930, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE, clk_sel)
{
}
ym2149_device::ym2149_device(unsigned int clock, bool clk_sel)
: ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel)
{
}
ym3439_device::ym3439_device(unsigned int clock, bool clk_sel)
: ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel)
{
}
ymz284_device::ymz284_device(unsigned int clock)
: ay8910_device(YMZ284, clock, PSG_TYPE_YM, 1, 0)
{
}
ymz294_device::ymz294_device(unsigned int clock)
: ay8910_device(YMZ294, clock, PSG_TYPE_YM, 1, 0)
{
}
sunsoft_5b_sound_device::sunsoft_5b_sound_device(unsigned int clock)
: ay8910_device(SUNSOFT_5B_SOUND, clock, PSG_TYPE_YM, 1, 0, PSG_HAS_INTERNAL_DIVIDER)
{
}