AY-3-8910

AY-3-8910-datasheet.pdf

This page will be for information for interfacing the General Instruments Programmable Sound Generator (PSG) Chips to the Z-80 also information for using it in other projects.

Just a couple of things, is the PSG was made for a multiplexed DATA/ADDRESS bus, some extra logic is required to interface it directly to the Z-80 or other then the processor General Instruments designed it for. My first example will be using the Datasheet example of using a PIO to interface with it, in my case the Z-80 PIO.

I have a link to the AY-3-8910 PSG in PDF format.


This is the example I am using from the Manual




Diagram of the internal PSG registers, make note that the registers are in Octal format. I made this mistake took me a while to figure out why the sound example did not work and values in examples are in Octal. R0-R7 translate as 0-7, but R10 = 8 in decimal, R11 = 9, R 12 = 10, and so on



This is the read/write assembly code example translated into C

I am interfacing via the Z80 PIO Port A: Data, Read/Write control port B: Port B bit 0 = BC1, Bit 1 = BC2, Bit 3 = BC3 or BDIR

If accessing the Sound chip via logic decoding, PIO code replaced with direct port number writes.



void PSG_write(unsigned char address, unsigned char data)
{// setup Z80_PIO_A_CLT to outputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M0); // Set A as a outputz80_outp( Z80_PIO_A, address & 0x0f ); // Load address into PIO port A// Latch addressz80_outp( Z80_PIO_B, 0x0f); // Set Port B output to PSG pins to latch Address// Setup to write dataz80_outp( Z80_PIO_B, 0x08); // Take Port B pins back to zeroz80_outp( Z80_PIO_A, data); // Load Register data into PIO port A// Write Dataz80_outp( Z80_PIO_B, 0x0e); // Set Port B to latch Data.z80_outp( Z80_PIO_B, 0x08); // Set Port B back to zero// setup Z80_PIO_A_CLT back to input just to be safez80_outp( Z80_PIO_A_CTL, Z80_PIO_M1); }
unsigned char PSG_read(unsigned char address){unsigned char data;// setup Z80_PIO_A_CLT to outputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M0); // Set A as a outputz80_outp( Z80_PIO_A, address & 0x0f ); // Load address into PIO port A// Latch addressz80_outp( Z80_PIO_B, 0x0f);// Setup to read dataz80_outp( Z80_PIO_B, 0x08);// Z80 PIO as inputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M1);
z80_outp( Z80_PIO_B, 0x0b);data = z80_inp(Z80_PIO_A);
// Setup to read dataz80_outp( Z80_PIO_B, 0x08);// Z80 PIO as inputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M1);
return data; }

This code plays the example sound effect from the manual. Include files are found on PIO page, if you using from a terminal window replace my LCD print with printf.


/* AY-3-8910 Sound Generator *//* 2020 */

#include "Hyundai_256x128.h"#include "Z80_IO.h"#include <z80.h>#include <stdlib.h>
void int2ascii(unsigned int cx, char *ptr );void LCD_load_Char(void);void system_init(void);
void PSG_write(unsigned char address, unsigned char data);unsigned char PSG_read(unsigned char address);

void delay(int j){int i;for(i=0; i<j; i++);}

int main(){char data[20]; int i;system_init();i = 0;LCD_set_cursor(0, 0);LCD_print( "AY-3-8910 \n" );LCD_set_cursor(0, 1);LCD_print( "Example Euro-Two-tone Polic siren \n" );
// Reset PSGz80_outp( Z80_PIO_B, 0x00);delay(100);z80_outp( Z80_PIO_B, 0x08);
PSG_write(0x07, 0xf8);PSG_write(0x0e, 0x00);PSG_write(0x0f, 0x00);
PSG_write(0x08, 0x0f);PSG_write(0x09, 0x0f);PSG_write(0x0a, 0x0f);
while (i != 10){i = z80_inp(Z80_PIO_A);
PSG_write(0x00, 0xfe);PSG_write(0x01, 0x00);PSG_write(0x02, 0xf8);PSG_write(0x03, 0x00);PSG_write(0x04, 0xf0);PSG_write(0x05, 0x00);
delay(15000);
PSG_write(0x00, 0x56);PSG_write(0x01, 0x01);PSG_write(0x02, 0x56);PSG_write(0x03, 0x01);PSG_write(0x04, 0x56);PSG_write(0x05, 0x01);delay(15000);

i = PSG_read( 0 );LCD_set_cursor(0, 3);LCD_print( "Read port \n" );int2ascii( i, data );LCD_print( data );delay(60000);
}

LCD_set_cursor(0, 10);LCD_print( "Exit program \n" );return 0;}

void PSG_write(unsigned char address, unsigned char data)
{// setup Z80_PIO_A_CLT to outputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M0);z80_outp( Z80_PIO_A, address & 0x0f );// Latch addressz80_outp( Z80_PIO_B, 0x0f);// Setup to write dataz80_outp( Z80_PIO_B, 0x08);z80_outp( Z80_PIO_A, data);// Write Dataz80_outp( Z80_PIO_B, 0x0e);z80_outp( Z80_PIO_B, 0x08);// setup Z80_PIO_A_CLT back to input just to be safez80_outp( Z80_PIO_A_CTL, Z80_PIO_M1); }
unsigned char PSG_read(unsigned char address){unsigned char data;// setup Z80_PIO_A_CLT to outputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M0);z80_outp( Z80_PIO_A, address & 0x0f );// Latch addressz80_outp( Z80_PIO_B, 0x0f);// Setup to read dataz80_outp( Z80_PIO_B, 0x08);// Z80 PIO as inputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M1);
z80_outp( Z80_PIO_B, 0x0b);data = z80_inp(Z80_PIO_A);
// Setup to read dataz80_outp( Z80_PIO_B, 0x08);// Z80 PIO as inputz80_outp( Z80_PIO_A_CTL, Z80_PIO_M1);
return data; }



void system_init(void){
// initialize PIO ports
z80_outp( Z80_PIO_A_CTL, Z80_PIO_M1); // set mode 1 byte outputz80_outp( Z80_PIO_B_CTL, Z80_PIO_M0); // set mode 0 output mode z80_outp( Z80_PIO_B, 0x00);
// intialize displayLCD_write_cmd( 0x40 ); // System set commandLCD_write(0x30); // P1 use internal 8px char w/ no top line correctionLCD_write(0x87); // P2 8px width charLCD_write(0x07); // P3 8px height charLCD_write(0x1f); // P4 32 char per lineLCD_write(0x23); // P5 previous value + 4LCD_write(0x7f); // P6 128 pixel screen heightLCD_write(0x20); // P7 32 char per lineLCD_write(0x00); // P8

LCD_write_cmd(0x5d); // Set cursor typeLCD_write(0x04);LCD_write(0x86);LCD_write_cmd(0x4c); // Set direction of cursor movementLCD_write_cmd(0x5B); // Set display overlayLCD_write(0x08); //3


LCD_write_cmd(0x44); //Scroll - Set display start address and display regionsLCD_write(0x00);LCD_write(0x00);LCD_write(0x80);LCD_write(0xb0);LCD_write(0x04);LCD_write(0x80);LCD_write(0x00);LCD_write(0x00);LCD_write(0x80);LCD_write(0xb0);

LCD_write_cmd(0x5A); // Set horizontal scroll LCD_write(0x00);

LCD_write_cmd(0x59); // Display onLCD_write(0x16); // 06
LCD_clear();LCD_graphics_clear();LCD_set_cursor(0, 0);
}



void int2ascii(unsigned int cx, char *ptr ){ int i;ptr = ptr + 5;
*ptr-- = '\0';
for( i = 0; i < 5; i++){*ptr-- = '0' + cx % 10 ;cx = cx / 10;} }