Hearing the AY-3-8910 chip
by PATRICK BASS, Antic Program Editor
Atari 8-bit computers make music using a chip called POKEY, and control is limited to choosing pitch and volume. Fancy music can be played, but only at great cost in processing time. The Atari 520ST uses a newer sound chip, the AY-3-8910, made by General Instrument Corp. This chip has been used in many Apple II add-on music boards, and it has also been selected for all Japanese computers using the MSX standard.
The AY-3-8910 can play music through as many as three voices at the same time. Each voice has 4,096 different pitches and full control over the ADSR (Attack, Decay, Sustain, Release) envelope.
The chip produces sound by dividing an incoming Master clock frequency by 16, and then by the number stored in the voice's frequency register. Every time the number counts down to zero, that voice's sound output line is toggled, in effect creating an output square wave. A square wave creates a tone similar to a woodwind or reed instrument.
The AY-3-8910 has 16 separate registers, numbered $00-$0F. Each register is eight bits wide, but some registers do not use all their assigned bits. These registers are shown in the diagram in Figure 1.
In the left column you can see the register number, the middle column shows the register function names, and the right-hand column maps the bit-assignment for each register, numbered from 7 to 0. The last two registers, $0E and $0F, are input/output ports and have no bearing on the operation of the AY-3-8910 in making sound.
Registers $00 through $05 control the frequency (pitch) of each voice. The six registers are paired off for each of the three voices. Bits 0-7 of the note are in the first register of each pair, and bits 8-11 can be found in bits 0-3 of the second register. The remaining four bits are not used. Thus, each of the three paired note registers form a 12-bit number whose value ranges from zero to 4095.
Given a master clock frequency of 2 megahertz divided by 16, or 125 Khz, and setting no bits for highest frequency, we get a hightest note of 125Khz divided by zero or 125,000 cycles per second. If we set all the bits in the frequency register, the lowest note is 125Khz divided by 4095, or roughly 30 cycles per second.
We can output either pure tones or "noise." The random noise is also produced through a square wave. The period (width) of this square-shaped pulse of sound affects its tone, and is controlled by register $06. Five bits of resolution offer a range of 0-31. In effect, this control acts just like the treble/bass tone control on your stereo. Low numbers stored in this register will "brighten" the noise and high numbers will "mute" the noise.
Register $07 has multiple functions. Looking at the register from the right, bits 0-2 control if voices A, B and C play pure tones. These are needed for music. The bar above TONE means, "This function is active when the bit is a zero, or LO." No bar shows that a one, or HI, is needed. Bits 3-5 control if noise is played through A, B or C. This would be used for explosions, jet planes, etc.
Bits 6 and 7 have nothing to do with sound. They control if the two I/O ports located in registers $0E and $0F are input or output ports. Since register $07 controls your floppy I/O, be sure and save its state before you alter it, or you will surely lose contact with your disk drives.
Registers $08, $09, and $0A have split functions. First, bits 0-3 control the volume of the voice chosen. Four bits of resolution give us a range of 0-15. Zero is off, and 15 is loudest. However, if bit 4, the "M" bit in the diagram, is set to a 1 then the lower four bits are ignored, and volume information is taken from Registers $0B, $0C and $0D, which enables effects such as wah-wah and vibrate.
Registers $0B and $0C control how long each stage of the ADSR envelope lasts. These two full registers give 16 bits of resolution, or a range of 0-65535. The incoming master clock frequency here is first divided by 256, then this result is divided by the 16-bit number in registers $0B and $0C.
The result is how long each stage of your desired ADSR envelope lasts. "Attack" is how quickly the sound rises from silence to its greatest volume. "Decay" is the time required for the sound level to fall to a constant level, called the "Sustain," where it continues to play until falling off to silence, or "Release."
Register $0D allows the programmer to select which section of the ADSR envelope is operating. The upper four bits are unused, but the lower four bits are: set to select one of 10 available waveforms. (See Figure 2.) For example, at the bottom of the chart, when al! four bits are set, the resulting waveform will start at silence, rise to its greatest volume, then end suddenly and stay silent. The waveform above that (bit pattern 1110) causes the volume of the sound to rise and fall in a repeating pattern.
As previously mentioned, registers $0E and $0F are I/O Ports A and B and have nothing to do with sound output. They connect to RS232, floppy, DMA, and parallel ports.
This month's Sound Demonstration program demonstrates how to read and write values to the AY-3-8910. The program is written in the C language. Last month's issue of Antic introduced the fundamentals of C programming for the Atari ST computers.
The ST Sound program tracks the mouse as it is moved around the dektop, and the mouse's X and Y position values are used as notes for Voices A and B respectively. Current results are then printed to the screen with special GEM VDI graphics text calls. The [LEFT-SHIFT] key raises the volume, and the [ALTERNATE] key lowers it. Pressing the left mouse button will exit the program.
PROGRAM TAKE APART
Since this program is quite similar to the GEM Color Cascade program presented last month, we can skim over the declaration and opening of a workstation. At the top we see a block of #include files, which have pre-written definitions in them. Next, a long line of int's, which here act just like initializing a variable in BASIC. Since int's are 16bit values and the AY-3-8910 registers are eight bits wide we define our notes, note_lo and note_hi, as char's, or 8-bit values.
We have one function defined in this program, main( ). The instructions that follow down through yres = 1_out[l]; say, "Initialize the application (program), get the 'handle,' or ID number, of this window, initialize an input array called l_intin, open a workstation, and get the width and height of this window (xres, yres)." The graf_mouse() (graphics mouse) call changes the mouse form into a pointing finger, which is one of seven pre-defined mouse forms stored inside the computer.
Next come new text functions. The function vst_effects( )(VDI-Set Text Effects) determines how the letters will be displayed. Choices such as normal, thickened, skewed (italics) and outlined are available. Notice we llll have defined four types in our variable declarations. The first vst_effects() call says, "In window 'handle,' use skewed characters."
Right after that call, on the same line, is a vst_color() call (VDI-Set Text Color) which selects the color the text will be printed in. The call vst_color(handle, BLUE) says, "In window 'handle', draw the needed text in color 'BLUE'." BLUE represents a value previously defined in the #included file "obdefs.h".
On the next line is the GEM call to print a text string to the screen: v_gtext() (VDI Graphic Text). The call requires that we tell it which window number to write to, the X,Y co-ordinates where the text will start, and the string itself or its address. So, combining the two lines of instructions above, we wind up printing skewed blue text to cursor position 10, 20.
The next four pairs of lines print the rest of our text in various shapes and colors. We now come to the first call we make to the AY-3-8910 itself.
SOUND CHIP CALL
The GEM call Giaccess() (General Instruments Access) takes the form: result=Giaccess (value, register). To write to register, add 128 to the register number, otherwise the register will be read and a number returned.
When writing, the amount to write is supplied by our program in value and when reading, the registers' value is returned in result. These first two calls access registers $08 and $09, setting voices A and B at an initial volume of eight, or about halfway. Press the [LEFT-SHIFT] key to raise the volume, and press the [ALTERNATE] key to lower the volume. Press the left mouse button to exit.
On the next line we save the number in the port control register in port_state, so later when we exit we don't lose touch with the disks. The next line is a Giaccess( ) call that accesses register $07 and turns on voices A and B so they use pure tones. We use the decimal number 60 here because 60 is the decimal value of the binary bit patterm (00 111 100) used to activate voices A and B.
For bits 0, 1 and 2, bit 2 is voice C. We do not use it, so we set that bit to a one. Bits 1 and 0 are voices B and A, respectively. They are active so we place a zero in their bits. We do not want random noise (bits 3, 4 and 5) in any voice so we set each voice bit to a one. Remember, a bar above means the function is active when LO. The upper two bits, 6 and 7, which control the direction of each I/O port, are not used in our program and are thus relegated to zero.
Next we enter a DO. . WHILE loop. In effect, we DO make noise WHILE not told to stop by pressing the left mouse button. The first GEM call and a block of four IF statements questions the keyboard and determines if the [LEFT-SHIFT] or [ALTERNATE] keys are pressed. If so, it adjusts the volume.
The next two Giaccess() calls update the current volume. Now we call vq_mouse() (VDI Question Mouse) which answers whether the mouse button was pressed and locates where the mouse is.
The next line sets x_note to a value proportional to both the mouse X-coordinate on the screen, and to the range of values the frequency counter can take. We take this value in x_note and bitwise break it into an 8-bit LO byte and a 4-bit HI nibble which are put in note_lo and note_hi. Then we call Giaccess() to turn on voice A with the results.
The next block of code does the same for voice B and the Y-coordinate.
The following three blocks of lines print to the screen the values we are using for the frequencies and volume. The variable number1 is a floating-point variable. It is needed for the line below, which converts a floating-point number to an ASCII string. The call ftoa() (float-to-ASCII) reads: "ftoa( number to print, buffer to build number in, number of digits to right of decimal point)." The GEM calls vst_effects(), vst_color() and v_gtext() then set the text style and color, and print out the number.
The line that ends the loop tests the value we picked up in the vq_mouse() call. If m_state is any value greater than zero, the button has been pressed.
To clean up, we make two Giaccess() calls. One shuts off voices by turning the volume all the way down. The second restores the I/O Ports to the way we found them when we walked in. We then perform a standard GEM exit.