Posts mit dem Label ay-3-8912 werden angezeigt. Alle Posts anzeigen
Posts mit dem Label ay-3-8912 werden angezeigt. Alle Posts anzeigen

Sonntag, 15. April 2012

Z80 Computer spielt Musik

Ich habe wieder etwas Muße gefunden und an meinem Z80-Projekt weitergearbeitet.

Der dsPIC auf dem Ext-Board ist ja unter anderem für die Tastatur zuständig. Er hat nen FIFO, wo Tastendrücke zwischengespeichert werden, bis der Z80 diese Daten abruft. Das PS/2-Protokoll ist ein wenig dämlich (siehe hier). Wird eine Taste gedrückt, so schickt die Tastatur sogenannte MAKE-Codes. Wird diese Taste wieder losgelassen, wird ein BREAK-Code geschickt. Diese Codes sind historisch gewachsen und es gibt deswegen einige Ausnahmen zu programmieren.
Diesen Teil habe ich im dsPIC implementiert: Er liest den MAKE-Code und weiß dadurch, welche Taste gedrückt wurde. Jeder Taste habe ich eine 7-Bit-Zahl zugewiesen. Das 8. Bit (das höchstwertige) zeigt an, ob die Taste gedrückt (1) oder losgelassen wurde (0). Auf diese Weise kann ich Drücken und Loslassen einer Taste der Tastatur in einem Byte darstellen, anstatt dieser Mehr-Byte-Codes, die die Tastatur sendet. Einzelne Bytes kann ich im Z80 auch viel einfacher verarbeiten.

Anderes Thema: Der Soundchip AY-3-8912. Im Netz hatte ich bereits einige Soundfiles gefunden, die auf diesem Chip wiedergegeben werden können. Das sind meistens Songs aus alten Spielen der 8-Bit-Ära. Mir fehlte nur bisher eine Beschreibung über das Format dieser Dateien. Gefunden habe ich die Beschreibung des YM-Formats hier: http://leonard.oxg.free.fr/ymformat.html#ymchip. Die Soundfiles sind i.A. 300 Byte bis 3 kB groß und mit LHA komprimiert, einem alten Packformat, welches ich noch aus den guten alten MS-DOS-Zeiten kenne. Wenn man sie entpackt, vergrößert sich der Bedarf häufig leider auf über 60 kByte, zu viel für meinen Z80 (ok, ich könnte mit Bankswitching arbeiten und hätte so 512 kB RAM, aber das ist mir erstmal zu kompliziert). Einen LHA-Entpacker zu schreiben ist mir auch zu kompliziert :-) Deswegen habe ich mir ein einfaches Komprimierungsverfahren für diese Soundfiles überlegt, welches die Größe (immerhin) mindestens viertelt:
Der Soundchip hat 16 8-Bit-Register, mit denen man den Sound steuern kann. Die Soundfiles wurden erzeugt, in dem mit einer 50Hz-"Frame-Rate" die Werte der 16 Register in die Datei geschrieben wurden. Wenn man sich diese Daten näher anschaut fällt auf, dass sich die Werte eines Registers über mehrere Zeiteinheiten häufig nicht ändert. Deswegen hat man sich das "Interleaved-Format" überlegt, in dem alle Werte des ersten Registers hintereinander geschrieben wurden, danach alle Werte des zweiten Registers usw. Das lässt sich dann sehr gut durch LHA komprimieren.
Mein Format ist leider nicht so gut, aber sehr einfach zu implementieren und macht das Datenvolumen für den Z80 handelbar:
Ein "Frame" besteht aus 2 Byte, in denen jedes Bit anzeigt, ob sich bezogen auf den vorherigen Takt ein Register geändert hat. Dahinter stehen dann nur die Werte der Register, die sich geändert haben.

Der Code, der diese Daten wiedergibt, sieht so aus:


const void* const songdata=0x8002;              
const char *framedata=songdata;                 
volatile uint __at 0x8000 frame_count;          
unsigned int frame=0;                           
char ay_regs[16];                               
                                                
void isr(void) __interrupt {                    
int reg;                                       
unsigned int regchange;                        
DI;                                            
regchange=*((unsigned int*)framedata);         
framedata+=2;                                  
for(reg=0;reg<16;++reg) {                      
if( (regchange&(1<<reg))==0)                  
continue;                                    
ay_regs[reg]=*framedata;                      
++framedata;                                  
AY_ADDR=reg;                                  
AY_REG=ay_regs[reg];                          
}                                              
++frame;                                       
if(frame>=frame_count) {                       
frame=0;                                      
framedata=(char*)songdata;                    
}                                              
EI;                                            
}                                               

Die Interruptroutine wird mit 50 Hz vom dsPIC ausgelöst.

Ich habe dazu ein kleines Video aufgezeichnet:

Der gespielte Song stammt aus dem Spiel Midnight Resistance (das ich selber nicht kenne). Die C64-Version hört sich allerdings deutlich besser an. Der SID-Chip des C64 ist wohl zurecht so beliebt!

Sonntag, 19. Februar 2012

Arbeiten am Ext-Board

Das Platine des Ext-Board ist endlich da und ich habe begonnen, es zu bestücken. Die IDE-Schnittstelle funktioniert bereits und der AY-3-8912 Soundchip auch. Es fehlt noch die Audio-Verstärkerschaltung für den Lautsprecher, da fehlen mir Bauteile.

Ich habe einen 64-Pin dsPIC30F6011 im Schaltplan, jedoch einen dsPIC30F6011A aufgelötet. Ich war so naiv anzunehmen, dass die Pinkompatibel sind. Es unterscheiden sich jedoch zwei Pins: Die Programmierpins PGC und PGD wurden von Pin 15/16 auf Pin 17/18 verschoben. Deswegen hat der PICkit 3 den µC anfangs nicht erkannt. Glücklicherweise habe ich Pin 17/18 auf einem Pinheader herausgeführt, so dass ich keine Änderungen an der Platine vornehmen muss, sondern nur am Programmierkabel.
Der dsPIC ist in einem 64-Pin TQFP-Gehäuse untergebracht. Es war das erste Mal, das ich so kleine Pinabstände gelötet habe. Mit reichlich Flussmittel und Entlötlitze war das Auflöten aber relativ problemlos.
dsPIC6011A im 64-Pin TQFP-Gehäuse
Noch ist nicht alles bestückt: Der Ethernetcontroller fehlt, die Ethernetbuchse und ein 16-Bit Portexpander sind noch nicht aufgelötet.
Das Z80-System

Donnerstag, 5. Januar 2012

AY-3-8912

Ich habe es nun endlich geschafft, dem AY-3-8912 ein paar Töne zu entlocken. Der Baustein ist offensichtlich sehr Timing-empfindlich, z.B. müssen innerhalb von 50ns die Bus-Steuerleitungen ge-setup't sein. Da ich die Busleitungen mit nem Mikrocontroller gesteuert habe, muss man schon überlegt verdrahten und programmieren  :-)

Der Chip wird mit einem externen Takt im Bereich 1-2 MHz getaktet. DA0-DA7 sind Datenbus und Adressbus, die sind gemultiplext. BC1 und BDIR sind die Bussteuerleitungen, die das kritische Timing erfordern. A8 und BC2 sind eine Art Chipselect und können fest auf +5V gelegt werden. CH_A-CH_C sind die drei Audiokanäle, die man einfach stumpf verbinden und in einen Kopfhörer einspeisen kann. IOA0-IOA7 ist ein digitaler "User-Port".

Der AY-3-8912 besitzt 16 interne Register. Um diese Register zu beschreiben/zu lesen, gibt es diesen prinzipiellen Ablauf:
Zum Schreiben versetzt man den Bus in den Inaktiven Zustand, dann wird die Adresse des zu selektierenden Registers auf den Bus DA0-DA7 gelegt und der Bus in den Latch-Zustand und zurück in den Inaktiven Zustand versetzt. Beim Übergang in den Inaktiven Zustand wird die Registeradresse gelatcht. Nun wird der in das Register zu schreibende Wert auf den Datenbus gelegt, in den Write-Zustand und zurück in den Inaktiven Zustand gewechselt. Nun wird der Wert in des Register geschrieben.

Den Buszustand legt man mit BDIR und BC1 fest. Der Inaktive Zustand wird durch BDIR=low und BC1=low gesetzt. Zuerst hatte ich BDIR an Portpin RB5 und BC1 an Portpin RE3 des dsPICs angebunden. Wenn man dann mit

_LATB5=0; 
_LATE3=0; 

den Bus steuern will, geht das schief, da jede Anweisung deutlich länger als 50ns benötigt, zumal der dsPIC auch nur mit 8MHz internen Takt ohne PLL läuft. Der Pegelwechsel selber von 0V auf 5V dauert ca. 7ns, das ist also kein Problem. Es müssen nur beide Pins gleichzeitig wechseln, um die 50ns nicht zu überschreiten. Also müssen beide Pins auf dem selben Port liegen. Um dann beide Pins gleichzeitig zu schalten, kann man dann das gesamte Portregister schreiben:

LATE = LATE & 0xFFFC;

Hier werden beim Port E die ersten beiden Bits gleichzeitig genullt. Möchte man ein Bit nullen, aber das andere "einsen", muss man einen Zwischenschritt einlegen:

LATE = (LATE & 0xFFFC) | 0x01;

So wird nur das zweite Bit genullt, das erste Bit gesetzt.

Den AY-3-8912 kann man auf diese Weise an den Z80 anbinden:
/AY_CS ist das Chipselect des Z80. Mit der untersten Adressleitung A0 des Z80 kann man so bestimmen, ob in das Adresslatch des AY-3-8912 geschrieben werden soll oder in eines der dadurch adressierten Register.

Versuchsaufbau