Samstag, 17. Dezember 2011

Z80 in C programmieren

Ich bin wieder fleißig gewesen und habe am Z80-Computer gearbeitet. Ich plane die erste Erweiterungskarte für das System. Darauf wird sich eine IDE-Schnittstelle befinden, die ich ja bereits für die andere Z80-Platine erstellt hatte. Dann ein Controller zum Anschluss von Maus und Tastatur. Ich habe hier ein paar Trockenübungen auf einem Steckbrett gemacht: Tastatur auslesen und Numlock blinken lassen funktioniert, das sollte also kein Problem mehr sein. Eine PS/2-Maus hat das gleiche elektrische Interface, aber die Softwareseite habe ich mir noch nicht näher angeschaut und weiß daher nicht, wie schwierig das zu implementieren ist.
Weiterhin wird sich auf dem Erweiterungsboard der AY-3-8912 befinden. Das ist ein 8-Bit Sound-Chip mit drei "Stimmen". Er kam in einigen 8-Bit Computern und Arcadespielen zum Einsatz. Ich habe noch keinen in der Hand gehabt, aber bereits den Schaltplan für die elektrische Anbindung an den Z80 erstellt. In den nächsten Tagen sollte er eintreffen, er ist bereits bestellt.

Nun zum eigentlichen Thema dieses Posts:
Ich habe mich heute mit dem C-Compiler SDCC beschäftigt, der Code für "Small Devices" erzeugen kann, wie Z80, 8051, 68HC08 und 8-Bit PIC-Controller von Microchip. Eigentlich habe ich mit größeren Schwierigkeiten gerechnet, bis ich etwas Gescheites zum Laufen zu bringen, aber es hat relativ schnell geklappt.
Die erste Frage war, wie man in C auf die IO-Ports des Z80 zugreifen kann. Im Assembler macht man das mit IN/OUT-Befehlen:
IN A, (32)   (Lesen von Port 32)
OUT (32), A  (Schreiben auf Port 32)
Diese Befehle werden Spracherweiterungen mit dem schönen Namen Storage Class Language Extension zugeordnet, wie ich nach einigem Suchen herausfand.
Mit
__sfr __at (32) uart_rxtx;
definiert man eine Variable, die man lesen und schreiben kann. Der Code, der daraus erzeugt wird, ist dann das entsprechende IN/OUT. sfr steht übrigens für Special Function Register.
Ich schreibe mir also folgenden Code in schönstem ANSI-C:

__sfr __at (32) uart_rxtx;                
__sfr __at (33) uart_st;                  
                                          
void sendstr(const char *s) {             
while( *s ) {                            
while( uart_st&2 );                     
uart_rxtx = *s;                         
++s;                                    
}                                        
}                                         
                                          
int main(void) {                          
sendstr("Z80 Computer\r\n");             
sendstr("Programmiert in C!\r\n");       
while(1);                                
}                                         


Kompiliert wird das mit
sdcc -mz80 test.c
Heraus kommt test.ihx im Intel-Hex-Format, mein Programmer schluckt aber nur einfache Binärdaten. Das Hex-File kann man mit
makebin.exe -p test.ihx test.bin
in eine einfache Binärdatei wandeln. Und es funktioniert :-)
So ganz habe ich noch nicht durchdrungen, was da alles vor sich geht. Es wird noch gewisser Startup-Code hinzugefügt, unter Anderem in Form der Datei crt0.s, wo Interruptvektoren und der Stackpointer initialisiert werden. Jetzt als Standalone-Programm läuft das mit dem SDCC-Startup-Code. Wie das eines Tages mal aussieht, wenn ich ein kleines OS geschrieben habe, von dem aus ich solch ein Binary ausführe, kann ich nicht so wirklich einschätzen. Ich werde es sehen :-)