Programming the AY-3-8910 sound chip


Programming the AY-3-8910 Programmable Sound Generator (PSG) is a relative simple task. But since the Aquarius BASIC doesn't support any command's for INPUT / OUTPUT the task has to be solved by machinecode. The info on this page is therefor for the advanced programmer with some assembler knowledge.

Before we start programming the PSG, we have to know something about it's internal structure. It has 16 registers, which are used as followed:
Register \ bit B7 B6 B5 B4 B3 B2 B1 B0
R0Channel A Tone Period8-Bit Fine Tune A
R14-Bit Coarse Tune A
R2Channel B Tone Period8-Bit Fine Tune B
R34-Bit Coarse Tune B
R4Channel B Tone Period8-Bit Fine Tune C
R54-Bit Coarse Tune C
R6Noise period5-Bit Period control
R7Enable ( bit 0 = on, 1 = off )IN/OUTNoiseTone
IOBIOAC BAC BA
R8Channel A Envelope on/off, VolumeEnvvolume
R9Channel B Envelope on/off, VolumeEnvvolume
R10Channel C Envelope on/off, VolumeEnvvolume
R11Envelope Period8-Bit Fine Tune Envelope
R124-Bit Coarse Tune Envelope
R13Envelope Shape/CycleCONTATTALTHOLD
R14I/O Port A Data Store8-Bit Parallel I/O on Port A
R15I/O Port B Data Store8-Bit Parallel I/O on Port B

The Aquarius has two I/O ports in order to programm the PSG. Port 0xF7 is used to select the register and port 0xF6 is used to read or write the selected register. In order to generate a tone on Channel A we do the following:
datalabelopcodeoperandcomment
62, 8start: LDA,0x08; Select register #8
211, 247OUT(0xF7),A
62, 15LDA,0x0F; Volume channel A full
211, 246OUT(0xF6),A
62, 0LDA,0x00; Select register #0
211, 247OUT(0xF7),A
62, 93LDA,0x5D; Write #93 into register #0
211, 246OUT(0xF6),A
62, 1LDA,0x01; Select register #1
211, 247OUT(0xF7),A
62, 13LDA,0x0D; Write #13 into register #1
211, 246OUT(0xF6),A
62, 7LDA,0x07; Select register #7
211, 247OUT(0xF7),A
62, 62LDA,0x3E; Enable output Channel A (0011 1110)
211, 246OUT(0xF6),A
201RET; Return to BASIC

Translated to BASIC that would be:

10 DATA 62,8,211,247,62,15,211,246
20 DATA 62,0,211,247,62,93,211,246
30 DATA 62,1,211,247,62,13,211,246
40 DATA 62,7,211,247,62,62,211,246
50 DATA 201
60 FORX=32000TO32032:READA:POKEX,A:NEXT
70 POKE14340,0:POKE1341,125:X=USR(0)
Which results in a nice everlasting tone. The tone you hear is the note C. In order to generate certain tones you can use these periods:

The lower the period, the higher the note. In order to get a higher octave, just divide the period by two.

The registers 0 & 1 are used for the period of Channel A. To calculate the correct values for these registers you can use the following formula:

(Ofcourse this also applies to the registers 2 & 3 for channel B and the registers 4 & 5 for channel C)
To work out the frequency of the note, use this equation:

Now let's take it a little step further and try working with an effect. The following will generate a short decaying burst of noise, just like a gunshot:
datalabelopcode operandcomment
62,11start:LD A,0x0B; Register 11
211,247OUT (0xF7),A
62,160LD A,0xA0
211,246OUT (0xF6),A
62,12LD A,0x0C; Register 12
211,247OUT (0xF7),A
62,15LD A,0x0F
211,246OUT (0xF6),A
62,6LD A,0x06; Register 6
211,247OUT (0xF7),A
62,15LD A,0x0F
211,246OUT (0xF6),A
62,7LD A,0x07; Register 7
211,247OUT (0xF7),A
62,55LD A,0x37; 00110111
211,246OUT (0xF6),A
62,13LD A,0x0D; Register 13
211,247OUT (0xF7),A
62,0LD A,0x00
211,246OUT (0xF6),A
62,8LDA,0x08; Register 8
211,247LD(0xF7),A
62,16LDA,0x10; Enable Enveloppe Channel A
211,246OUT(0x246),A
201RET; Return to BASIC

Again, translated to BASIC:

10 DATA 62,11,211,247,62,160,211,246
20 DATA 62,12,211,247,62,15,211,246
30 DATA 62,6,211,247,62,15,211,246
40 DATA 62,7,211,247,62,55,211,246
50 DATA 62,13,211,247,62,0,211,246
60 DATA 62,8,211,247,62,16,211,246
70 DATA 201
80 FORX=32000TO32048:READA:POKEX,A:NEXT
90 POKE14340,0:POKE1341,125:X=USR(0)

The effect is generated by the Enveloppe register #13. It can make several effects which are graphical representated in the next table:

R13 Bits GRAPHICAL REPRESENTATION OF ENVELOPE GENERATOR OUTPUT (E3 E2 E1 E0)
B3 B2 B1 B0 Decimal
representation
Continue Attack Alternate Hold
00xx 0,1,2,3 [Envelope shape 00xx]
01xx 4,5,6,7 [Envelope shape 01xx]
10008 [Envelope shape 1000]
10019 [Envelope shape 1001]
101010 [Envelope shape 1010]
101111 [Envelope shape 1011]
110012 [Envelope shape 1100]
110113 [Envelope shape 1101]
111014 [Envelope shape 1110]
111115 [Envelope shape 1111]
[Envelope Period Note] EP is the Envelope Period, or the duration of 1 cycle. EP = R12*256 + R11

After running the last BASIC programm one can experiment with the enveloppe effects by poking the wanted effect on address 32037 and calling the USR(0) function. For example:

POKE32037,4:X=USR(0)
POKE32037,8:X=USR(0)