---------------------------------------- Floppy-Kurs: "Es rappelt in der Kiste..." (Teil 7) ----------------------------------------
Hallo und herzlich Willkommen zum sieb- ten Teil des Floppy-Kurses. In diesem Monat möchten wir uns an die Floppy- Programmierung in Assembler heranwagen. Hierbei werden wir alle notwendigen Rou- tinen zum I/O-Handling von Maschinenspa- che aus kennenlernen. Allerdings sollten Sie schon einige Assembler-Vorerfahrung mitbringen und zumindest Kenntnis vom Befehlssatz des 6510-Prozessors haben.
DER EINSTIEG IN ASSEMBLER
Wie Sie bemerkt haben, waren alle bishe- rigen Programmbeispiele und Befehlser- läuterungen in BASIC programmiert. Dies diente hauptsächlich der Einfachheit halber. Wenn Sie nun ein angehender Assemblerprogrammierer sind, so werden Sie sich desöfteren schon einmal gefragt haben, wie Sie die Floppy in Maschinen- sprache ansprechen können. Dies unter- scheidet sich von BASIC zwar schon in einigen Punkten, da es etwas aufwendiger ist, jedoch können wir hier komplett auf schon im Betriebssystem des 64ers vor- handene Routinen zurückgreifen, die le- diglich mit den richtigen Parametern gefüttert werden müssen. Diese Routinen sind dieselben, die auch vom BASIC des C64 benutzt werden, weshalb die Parame- terübergabe der von BASIC sehr ähnlich ist. Kommen wir zunächst einmal zu eini- gen grundlegenden Dingen: ÜFFNEN UND SCHLIESSEN VON FILES Jedesmal, wenn wir einen Zugriff auf die Floppy ausführen wollten, mussten wir zuvor immer einen Filekanal öffnen. Hierzu wurden mit Hilfe des OPEN-Befehls einige Parameter an die Floppy überge- ben, die zur eindeutigen Definition des Kanals notwendig waren. Diese Parameter waren die Filenummer, die Devicenummer und die Sekundäradresse. Oft folgte der ganzen Anweisung dann auch noch ein Fi- lename, dessen Appendix ebenso die ver- langte Operation spezifizierte. Man kann also sagen, daß diese Werte alles Grund- werte sind, die zum Datenaustausch mit der Floppy benötigt werden. Aus diesem Grund müssen sie auch bei jeder Fileope- ration angegeben werden. Möchten wir nun eine Operation von Assembler aus durch- führen, so müssen wir diese Parameter zunächst dem Betriebssystem mitteilen. Dies geschieht über die beiden ROM- Routinen "SETPAR" und "SETNAM". SETPAR hat die Einsprungadresse $FFBA. An sie werden die Filenummer, sowie Geräte- und Sekundäradresse übergeben. SETNAM legt den Filenamen (inklusive Appendix) fest. Sie steht bei $FFBD. Beide Routinen tun nichts anderes, als die gegebenen Werte in Betriebssystem-Zwischenspeichern der Zeropage abzulegen. Nachdem die Werte festgelegt sind, kön- nen wir die Betriebssystemroutine "OPEN" aufrufen, die uns den Kanal mit der Floppy öffnet. Ihre Einsprungadresse ist bei $FFC0. Zum Schließen benutzen wir die Routine "CLOSE" bei $FFC3. Wenn wir ein File öffnen, so müssen wir zunächst einmal seinen Namen festlegen. Diesen legen wir irgendwo im Speicher, an einer bekannten Adresse, ab. Die Län- ge des Namens benötigen wir ebenso. Als nächstes können wir SETNAM und SETPAR aufrufen. SETNAM wird die Adresse des Filenamens und seine Länge, SETPAR die logische Filenummer, die Geräte- und die Sekundäradesse übergeben. Hier eine Öbersicht der Registerbelegungen:
SETPAR: Akku = Filenummer X-Reg. = Geräteadresse Y-Reg. = Sekundäradresse SETNAM: Akku = Länge des Filenamens X-Reg. = Low-Byte der Speicher- adresse des Namens Y-Reg. = High-Byte der Speicher- adresse des Namens
Ich möchte Ihnen hierzu ein Beispiel liefern. Angenommen, Sie wollten das sequentielle File "TEST" zum Lesen öff- nen. Als Filenummer wollen wir die 1 wählen, als Sekundäradresse 2. Die Gerä- teadresse ist in Bezug auf die Floppy natürlich 8 (oder 9,10,11 wenn Sie eine zweite, dritte oder vierte Floppy besit- zen). All diese Parameter entsprechen also dem BASIC-Befehl:
OPEN 1,8,2,"TEST,S,R"
Möchten wir diesen Befehl nun in Maschi- nensprache umsetzen, so schreiben wir folgendes Assembler-Programm. Hierbei gehe ich davon aus, daß wir den Filena- men "TEST,S,R" als ASCII-Code bei Adres- se $0334 (dem Kasettenpuffer) abgelegt haben:
LDA #$01 ;Filenummer 1 LDX #$08 ;Geräteadresse 8 LDY #$02 ;Sekundäradresse 2 JSR $FFBA ;SETPAR aufrufen LDA #$08 ;Filename ist 8 Zeichen lang LDX #$34 ; und liegt bei Adresse LDY #$03 ; $0334 JSR $FFBD ;SETNAM aufrufen
Die Parameter für das File "TEST" wären damit festgelegt. Da das ",S,R" im File- namen enthalten ist, weiß die Floppy auch gleich schon, daß wir ein sequen- tielles File lesen möchten. Als Nächstes müssen wir das zuvor spezi- fizierte File öffnen. Dies geschieht über die oben schon erwähnte Betriebssy- stemroutine "OPEN". Sie benötigt keine Parameter und wird direkt aufgerufen: JSR $FFC0 ;File mit zuvor gesetzem Na- men und Parametern öffnen Nun ist das File "TEST" geöffnet. Da aber auch jeder Datenfluß einmal ein Ende hat, muß unser File irgendwann ein- mal wieder geschlossen werden. Dies ge- schieht, wer hätte das gedacht, mit der ROM-Routine "CLOSE", die bei Adresse $FFC3 angesprungen wird. Da auch mehrere Files gleichzeitig offen sein können, muß ihr die Filenummer des zu schließen- den Files im Akku übergeben werden: LDA #$01 ;File mit Filenummer 1 JSR $FFC3 ; schließen DER DATENAUSTAUSCH Wie man ein File öffnet, sollte Ihnen nun klar sein. Wenn Sie jedoch auch noch mit ihm komunizieren wollen (was sie bestimmt tun möchten, da es anders sinn- los wäre ein File zu öffnen), so müssen Sie weiterhin fünf Betriebssystem- Routinen kennen, die Ihnen dies ermögli- chen. Zunächst wären da "CHKIN" und "CHKOUT". Sie dienen der Umleitung der Standard-Ein/Ausgabe auf den entspre- chenden Filekanal. Wenn Sie aus einem File lesen wollen, so sollten Sie nach dem Üffnen desselben die Routine CHKIN aufrufen. Mit ihr teilen Sie dem Be- triebssystem mit, daß Sie, wenn Sie jetzt etwas einlesen, die Daten aus die- sem Filekanal haben möchten. Möchten Sie in ein File schreiben, so müssen Sie CHKOUT aufrufen um in das geöffnete File schreiben zu können. Beide Routinen benötigen die Filenummer des entspre- chenden Files im X-Register. Die Ein- sprungadresse von CHKIN ist $FFC6, die von CHKOUT $FFC9. Wenn Sie die Standard-Ein/Ausgabe mit Hilfe unserer beiden Routinen umgeleitet haben, so können Sie die Ihnen viel- leicht schon bekannten Routinen BASIN ($FFCF) und BASOUT ($FFD2) zum Lesen und Schreiben von Daten vom, bzw. an, das Ein/Ausgabe-Gerät benutzen. Rufen Sie diese Routinen bei unveränderter Stan- dard-Ein-/Ausgabe auf, so erscheint bei BASIN ein Cursor auf dem Bildschirm, der dem Assemblerprogramm eine Eingabe von der Tastatur übermittelt, bei BASOUT wird ein Zeichen an die aktuelle Cursor- position gedruckt und der Cursor um eine Stelle weiterbewegt (wie Sie bemerken ist das Standard-Eingabegerät die Tasta- tur, das Standard-Ausgabegerät der Bild- schirm). Bei umgeleiteter Eingabe erhalten Sie nun beim Aufruf von BASIN das aktuelle Byte des geöffneten Files im Akku zurück. Bei umgeleiteter Ausgabe wird jedes Byte, daß Sie in den Akku laden und anschließend an BASOUT übergeben in das geöffnete File hineingeschrieben. Haben Sie nun Ihre Fileoperationen been- det, so ist es notwendig, die fünfte Routine zu benutzen, von der ich oben sprach. Sie heißt "CLRCH" und wird ver- wendet, um die Standard-Ein/Ausgabe wie- der zurückzusetzen auf 'Tastatur' und 'Bildschirm'. Ihre Einsprungadresse ist $FFCC. Sie wird VOR dem Aufruf von CLOSE benutzt und benötigt keine Parameter. FEHLERERKENNUNG UND BEHANDLUNG Bevor wir uns der Praxis zuwenden, zunächst noch ein Wort zur Fehlererken- nung. Hierüber können wir nämlich beim Lesen eines Files auch erkennen, wann wir sein letztes Byte gelesen haben. Prinzipiell gilt: tritt während der Ar- beit einer der Floppy-Betriebssystem- Routinen ein Fehler auf, so wird das an das aufrufende Programm durch ein ge- setztes Carry-Bit zurückgemeldet. So können Sie also nach jeder der Routinen durch eine einfache "BCS"-Verzweigung ("Branch on Carry Set") auf eine Fehler- behandlungsroutine verzweigen. Hierbei steht dann im Akku ein Fehlercode, mit dessen Hilfe Sie die Art des Fehlers feststellen können. Hier eine Liste mit den möglichen Fehlermeldungen und den Ursachen für einen aufgetretenen Fehler. In eckigen Klammern stehen jeweils die Betriebssystem-Routinen, die den ent- sprechenden Fehler auslösen können. Steht nichts dahinter, so handelt es sich um einen allgemeinen Fehler: 0: "Break Error" - Die RUN/STOP-Taste wurde gedrückt. 1: "too many files" - Der 64er verwaltet intern maximal 10 offene Files. Sie versuchten ein elftes File zu öffnen. Oder aber sie haben schon zu viele Files zur Floppy hin offen (Sie erin- nern sich: man darf maximal 3 sequen- tielle, oder 1 relatives und 1 se- quentielles File gleichzeitig offen halten). {OPEN} 2: "file open" - Sie versuchten, ein schon offenes File nochmal zu öffnen. {OPEN} 3: "file not open" - Sie versuchten, ein ungeöffnetes File anzusprechen. {CH- KIN, CHKOUT, CLOSE} 4: "file not found" - Das File, das Sie zum Lesen öffnen wollten existiert gar nicht. {OPEN} 5: "device not present" - Die Floppy (oder das gewählte Gerät) ist nicht eingeschaltet. {OPEN} 6: "not an input file" - Sie versuchten aus einem zum Schreiben geöffneten File zu lesen. {CHKIN} 7: "not an output file" - Sie versuchten in ein zum Lesen geöffneten File zu schreiben. {CHKOUT} 8: "missing filename" - Sie gaben keinen Filenamen an. {OPEN} 9: "illegal device number" - Sie gaben eine ungültige Devicenummer an. {OPEN} Beachten Sie bitte, daß nicht unbedingt alle Fehler aus obiger Liste auftreten müssen, da man die oben genannten Routi- nen auch zum Anprechen von anderen Gerä- ten verwenden kann (z.B. Drucker, Data- sette, etc.). Üffnen Sie z.B. einen Ka- nal zum Drucker, so brauchen Sie keinen Filenamen - der Fehler 8 wird in dem Fall also nie auftreten. Als weitere Fehlererkennungshilfe stellt uns das Betriebssystem auch eine Status- speicherstelle zur Verfügung. Sie ist absolut identisch mit der Variablen "ST" von BASIC. Fragt ein BASIC-Programm die- se Variable ab, so greift der BASIC- Interpreter auf eben diese Speicherstel- le zurück. Die Assemblerprogrammierer finden den I/O-Status in der Zeropage, nämlich in Speicherstelle $90 (dez. 144). Um feststellen zu können, ob wäh- rend der Arbeit mit der Floppy ein Feh- ler aufgetreten ist, müssen wir sie le- diglich einlesen und analysieren. Ein Fehler wird hierbei durch das gesetzt sein eines oder mehrerer Bits dieser Speicherstelle gemeldet. Hier eine Bele- gung der Bits (nur für die Arbeit mit der Floppy gültig, bei Kasettenbetrieb gilt eine andere Belegung):
Bit Bedeutung ----------------------------------------
0 Fehler beim Schreiben 1 Fehler beim Lesen 6 Datei-Ende wurde erreicht (Lesen) 7 Gerät nicht vorhanden oder abge- schaltet ("device not present")
Sie sehen, daß Sie hier, wie auch in BASIC, das Ende eines Files durch ge- setzt sein des 6. Bits von ST feststel- len können. Dann nämlich hat ST den Wert 64, den wir bei BASIC-Abfragen auch im- mer verwendeten. Sie sehen übrigens auch, warum bei erneutem Lesen trotz beendetem File die Fehlernummer 66 zurückgeliefert wird. Das File ist dann nämlich zu Ende und es trat ein Fehler beim Lesen auf, weil es ja gar nichts mehr zu lesen gibt. Damit sind die Bits 1 und 6 gesetzt (2+64) was dem Wert 66 entspricht. Wenn ST den Wert 0 enthält, so ist alles gut gegangen. Auf diese Weise können wir in Assembler sehr einfach den Fehler abfragen: Wir lesen die Speicherstelle $90 einfach ein und verzweigen mittels BNE auf eine Fehlerbehandlungsroutine. ZUSAMMENFASSUNG Abschließend zu diesen Routinen möchte ich Ihnen nun zwei Universal- Lese/Schreib-Routinen vorstellen, die Sie zum Lesen, bzw. Schreiben eines Fi- les verwenden können. Sie finden diese Routinen auch auf dieser MD unter dem Namen "FK.I/O.S". Sie sind im Hypra- Ass-Format gespeichert. Um sie sich an- zuschauen können Sie sie wie ein BASIC- Programm laden und listen. Zunächst einmal möchte ich Ihnen die "ReadFile"-Routine vorstellen. Sie liest ein File an eine beliebige Adresse ein. Hierbei übergeben Sie Low- und High-Byte in X- und Y-Register, sowie die Länge des Filenamens im Akku. Der Filename selbst soll immer bei $0334 (dez. 820) stehen: ReadFile:
stx $fb ;Startadresse in sty $fc ; Zeiger schreiben ldx #$34 ;Namensadresse=$0334 ldy #$03 ;in X/Y (Len in Ak.) jsr $ffbd ;Und Name setzen lda #01 ;Filenummer=1 ldx #08 ;Geräteadresse=1 ldy #00 ;Sek.=0 (=PRG lesen) jsr $ffba ;Parameter setzen jsr open ;File öffnen ldx #01 ;Ausgabe auf FNr. 1
jsr chkin ; umleiten
ldy #00 ;Index auf 0 setzen loop1: jsr basin ;Byte lesen sta ($fb),y ;und auf Zeiger- adresse speichern inc $fb ;Den Zeiger in bne l1 ; $FB/$FC um 1 inc $fc ; erhöhen
l1: lda $90 ;File-Ende erreicht? beq loop1 ;Nein, dann weiter!
jsr $ffcc ;Ja, also Standard- Ein-/Ausgabe zurücksetzen. lda #01 ;Und File mit File- jmp $ffc3 ; nummer 1 schließen
Als nächstes stelle ich Ihnen die "Wri- teFile"-Routine vor. Sie speichert Daten in einem beliebigen Speicherbereich auf Diskette und wird mit denselben Vorraus- setzungen aufgerufen wie "ReadFile": Name bei $0334, Namenslänge im Akku und Startadresse des zu speichernden Be- reichs in X-/Y-Register. Zusätzlich müs- sen Sie die Endadresse dieses Bereichs (in Lo/Hi-Darstellung) vorher in den Speicherstellen $FD/$FE abgelegt haben: WriteFile:
stx $fb ;Startadresse in sty $fc ; Zeiger schreiben ldx #$34 ;Namensadresse=$0334 ldy #$03 ;in X/Y (Len in Ak.) jsr $ffbd ;Und Name setzen lda #01 ;Filenummer=1 ldx #08 ;Geräteadresse=1 ldy #01 ;Sek.=1 (=PRG schreiben) jsr $ffba ;Parameter setzen
jsr $ffc0 ;File öffnen ldx #01 ;Eingabe auf FNr.1 jsr $ffc9 ; umleiten ldy #00 ;Index auf 0 setzen loop2: lda ($fb),y ;Byte aus Sp. holen jsr $ffd2 ;und an Floppy sen- den
inc $fb ;Den Zeiger in bne l2 ; $FB/$FC um 1 inc $fc ; erhöhen l2: lda $fe ;Quellzeiger $FB/$FC cmp $fc ; mit Zielzeiger bne loop2 ; $FD/$FE lda $fd ; vergleichen. Wenn cmp $fb ; ungleich, dann bne loop2 ; weitermachen. jsr $ffcc ;Wenn gleich, dann Standard-I/O zurücksetzen. lda #01 ;und File mit File- jmp $ffc3 ;nummer 1 schließen
Wie Sie sehen, habe ich hier den Trick mit den Sekundäradressen verwendet. Da bei der Sekundäradresse die Werte 0 und 1 für "Programm lesen" bzw. "Programm schreiben" stehen, erübrigt sich ein umständliches anhängen von ",P,R" bzw. ",P,W" an den Filenamen. Dafür jedoch können mit den Routinen nur PRG-Files gelesen und geschrieben werden. Bitte Teil 2 des Floppy-Kurses laden !