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!