2022-01-13 07:52:19 +00:00
// 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.830 k 99.5 k 1.001 M 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
*
* 5 V
* |
* 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 :
*
*
* 5 V
* _ | 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 5 V
*
* RVequiv = RD * ( 5 V / 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 ~ 10 M ) . 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 . . 7ff ff ) .
In the case of channel mixing , output range is 0. . .3 * 7ff f .
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 2 V DC component . This is confirmed by measurements . The approach
taken here is to assume the 2 V 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 0 V . Recordings
I found on the web for gyruss indicate , that the AY - 3 - 8910 offset should
be around 0.2 V . This will also make sound levels more compatible with
user observations for scramble .
The Model :
5 V 5 V
| |
/ |
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 1 K 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.2 V and a VPP of 1.3 V . 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 ( + 5 v )
_ | | _
[ 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 ( + 5 V ) - - | _ | 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 ( + 5 V )
| __________________ |
[ 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 - 8910 A : 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 - 8910 A 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 - 8912 A : Unused bits in registers have unknown behavior .
I / O current source / sink behavior is unknown .
AY - 3 - 8912 die is unknown .
AY - 3 - 8912 A 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 - 8914 A : 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 - 8914 A 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 0xA n or 0xB n 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 4 MHz ( H ) and 6 MHz ( 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 - 8910 A 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 - 8910 A 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 - 8910 A - 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
*/
2022-01-18 23:21:27 +00:00
// additional modifications by tildearrow and Eulous for furnace (particularly AY8930 emulation)
2022-01-13 07:52:19 +00:00
# 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.5 V to 2.8 V
* 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.75 V to 2.05 V
* 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.2 V to 1.5 V
*/
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 8 m Ohm , RU was 0.8 m 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 + + )
2022-01-29 05:20:27 +00:00
tab [ j ] = 8000 * MAX_OUTPUT * ( ( ( temp [ j ] - min ) / ( max - min ) ) ) * factor ;
2022-01-13 07:52:19 +00:00
}
else
{
for ( int j = 0 ; j < 32 * 32 * 32 * 8 ; j + + )
2022-01-29 05:20:27 +00:00
tab [ j ] = 8000 * MAX_OUTPUT * temp [ j ] ;
2022-01-13 07:52:19 +00:00
}
/* 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 ) ;
}
}
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 :
2022-01-18 23:21:27 +00:00
m_noise_and = m_regs [ AY_NOISEAND ] ;
break ;
2022-01-13 07:52:19 +00:00
case AY_NOISEOR :
2022-01-18 23:21:27 +00:00
m_noise_or = m_regs [ AY_NOISEOR ] ;
2022-01-13 07:52:19 +00:00
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 ) ;
tone - > count + = is_expanded_mode ( ) ? 16 : 1 ;
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 ;
}
}
m_count_noise + + ;
if ( m_count_noise > = noise_period ( ) )
{
/* toggle the prescaler output. Noise is no different to
* channels .
*/
m_count_noise = 0 ;
m_prescale_noise ^ = 1 ;
if ( ! m_prescale_noise | | is_expanded_mode ( ) ) // AY8930 noise generator rate is twice compares as compatibility mode
{
2022-01-18 23:21:27 +00:00
if ( is_expanded_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.
unsigned int noiseValuePeriod = ( ( m_rng & 0xFF & m_noise_and ) | m_noise_or ) ;
// Clock the noise value.
if ( m_noise_value > = noiseValuePeriod ) {
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_latch ^ = 1 ;
// The 17-bit LFSR is updated, using an XOR across bits 0 and 2.
unsigned int feedback = ( m_rng & 1 ) ^ ( ( m_rng > > 2 ) & 1 ) ;
m_rng > > = 1 ;
m_rng | = ( feedback < < 16 ) ;
}
m_noise_value + + ;
} else {
/* 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. */
m_rng ^ = ( ( ( m_rng & 1 ) ^ ( ( m_rng > > 3 ) & 1 ) ) < < 17 ) ;
m_rng > > = 1 ;
}
2022-01-13 07:52:19 +00:00
}
}
for ( int chan = 0 ; chan < NUM_CHANNELS ; chan + + )
{
tone = & m_tone [ chan ] ;
2022-03-31 18:46:27 +00:00
m_vol_enabled [ chan ] = ( tone - > output | ( unsigned char ) tone_enable ( chan ) ) & ( noise_output ( ) | ( unsigned char ) noise_enable ( chan ) ) ;
2022-01-13 07:52:19 +00:00
}
/* update envelope */
for ( int chan = 0 ; chan < NUM_CHANNELS ; chan + + )
{
envelope = & m_envelope [ chan ] ;
if ( envelope - > holding = = 0 )
{
const int period = envelope - > period * m_step ;
envelope - > count + + ;
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 ( ) ;
}
2022-01-31 21:40:47 +00:00
void ay8910_device : : ay8910_reset_ym ( bool ay8930 )
2022-01-13 07:52:19 +00:00
{
m_active = false ;
m_register_latch = 0 ;
2022-01-31 21:40:47 +00:00
if ( ay8930 ) {
m_rng = 0x1ffff ;
} else {
m_rng = 1 ;
}
2022-01-13 07:52:19 +00:00
m_mode = 0 ; // ay-3-8910 compatible mode
2022-01-31 21:33:45 +00:00
m_noise_and = 0xff ;
2022-01-18 23:21:27 +00:00
m_noise_or = 0 ;
m_noise_value = 0 ;
m_noise_latch = 0 ;
2022-01-13 07:52:19 +00:00
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 ( )
{
int r = m_register_latch + get_register_bank ( ) ;
if ( ! m_active ) return 0xff ; // high impedance
if ( ( r & 0xf ) = = AY_EASHAPE ) // shared register
r & = 0xf ;
/* There are no state dependent register in the AY8910! */
/* 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 ( )
{
2022-01-31 21:40:47 +00:00
ay8910_reset_ym ( chip_type = = AY8930 ) ;
2022-01-13 07:52:19 +00:00
}
/*************************************
*
* 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 )
: 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_count_noise ( 0 ) ,
m_rng ( 0 ) ,
2022-01-18 23:21:27 +00:00
m_noise_and ( 0 ) ,
m_noise_or ( 0 ) ,
m_noise_value ( 0 ) ,
m_noise_latch ( 0 ) ,
2022-01-13 07:52:19 +00:00
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 )
{
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 ) ;
}
void ay8910_device : : set_type ( psg_type_t psg_type )
{
m_type = psg_type ;
if ( psg_type = = PSG_TYPE_AY )
{
m_env_step_mask = 0x0f ;
m_step = 2 ;
m_zero_is_off = 1 ;
m_par = & ay8910_param ;
m_par_env = & ay8910_param ;
}
else
{
m_env_step_mask = 0x1f ;
m_step = 1 ;
m_zero_is_off = 0 ;
m_par = & ym2149_param ;
m_par_env = & ym2149_param_env ;
}
}
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 )
: ay8910_device ( AY8930 , clock , PSG_TYPE_YM , 3 , 2 , PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE )
{
}
ym2149_device : : ym2149_device ( unsigned int clock )
: ay8910_device ( YM2149 , clock , PSG_TYPE_YM , 3 , 2 , PSG_PIN26_IS_CLKSEL )
{
}
ym3439_device : : ym3439_device ( unsigned int clock )
: ay8910_device ( YM3439 , clock , PSG_TYPE_YM , 3 , 2 , PSG_PIN26_IS_CLKSEL )
{
}
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 )
{
}