ESCAPE-Steuerfolgen im MicroDOS
von Uwe Felgentreu
Der KC bietet unter MicroDOS die Nutzung von Resourcen des Grundgerätes an. Dazu wurde im Code des ROM-FC im D004 eine Reihe von Unterprogrammen eingebunden, welche aus der zentralen Abfrageschleife heraus über spezielle Zeichensequenzen ansprechbar sind.
Das Prinzip ist mit den ESC-Folgen zur Druckersteuerung vergleichbar. Hier werden die Codefolgen für Großschrift, Fettschrift etc. auch nur ausgeführt und nicht gedruckt. Wird also im Zeichenstrom für den Bildschirm eine derartige ESC-Folge entdeckt, so werden die Zeichen nicht auf dem Bildschirm dargestellt, sondern eine der Funktion entsprechende Anzahl Bytes herausgefiltert, ausgewertet und die entsprechende Funktion ausgeführt.
Die Aufgabe für den Programmierer besteht nun darin, derartige Codefolgen zu erzeugen. Wichtig ist dabei die korrekte Erzeugung, da der KC nichts prüft und gegebenenfalls bei einer ungültigen Sprunganweisung ins Grundgerät außer Kontrolle gerät. Damit ist dann auch die Arbeit unter MicroDOS beendet, d. h. der KC ist abgestürzt.
Im Handbuch für den Programmierer auf Seite 74 sind die normalen ESC-Folgen abgedruckt. Ich hätte mich fast dazu hinreißen lassen, diese Seite abzutippen, aber Mario Leubner bastelt permanent an neuen ESC-Folgen, so daß er eigentlich die aktuellste Version veröffentlichen kann...
Die Erzeugung einer Codefolge ist denkbar einfach. Mit dem Befehl zur Ausgabe von Zeichen auf dem Bildschirm wird in der jeweiligen Programmiersprache die Zeichenfolge ausgegeben. Meine Beispiele sind in TurboPascal geschrieben. Folgender Befehl schreibt das Byte 41h ("A") an die Adresse 4000h im KC:
write(#27,#83,#00,#40#$41)
Konkret ist hier folgendes passiert. Das Byte 27 leitet eine ESC-Folge ein. Das Byte 83 legt fest, daß hier die Funktion "Speicherzelle im Grundgerät beschreiben" aufgerufen wird. Diese Funktion erwartet nun noch 3 Bytes als Parameter (Adresse-Low, Adresse-High, Datenbyte). Die Übersicht auf Seite 74 im Handbuch für den Programmierer zeigt alle verfügbaren Standardfunktonen mit der Syntax zum Aufruf.
Ich bevorzuge zur Ausgabe die BIOS-Funktionen, da die BDOS-Ausgaben im MicroDOS intern nochmals gefiltert werden. Dabei kann es unter ungünstigen Bedingungen zum Verschlucken von Bytes kommen. Das Grundgerät nimmt die erwartete Anzahl von Bytes aus der Wartschlange und erhält evtl. falsche Parameter. Damit könnte man den KC abstürzen lassen...
Prinzipiell gibt es 2 verschiedene Typen von ESC-Folgen:
- der Funktionsaufruf mit Parametern ohne Rückgabewert
- der Funktionsaufruf mit Parameter und Rückgabewert
Der einfache Funktionsaufruf ohne Rückgabewert wird in der Procedure WRITE_KC gezeigt.
procedure write_kc (adresse:integer;daten:byte); begin bios(3,27); bios(3,83); bios(3,lo(adresse)); bios(3,hi(adresse)); bios(3,daten); end;
Soll ein Wert zurückgegeben werden, so muß die Zelle MEMANF (=0FFAE Hex) im D004 auf einen Wert ungleich 0 gesetzt werden. Dann wird die betreffende Funktion aufgerufen (wie eine ESC-Folge ohne Rückgabewert) und anschließend gewartet, bis diese Speicherzelle einen Wert ungleich 0 annimmt. Der Wert 255 zeigt einen Fehler an. Erst dann kann ab Adresse 0FE00 Hex das Ergebnis bestaunt werden.
Die Art und Weise, wie man auf das Ergebnis wartet, entscheidet über die Qualität eines Programmes. Macht man es so wie im Beispiel READ_KC, so kann MicroDOS ewig auf das Setzen der Zelle MEMANF warten, wenn ein Parameterbyte verlorengegangen ist. Besser ist die Abfrage mit TimeOut.
Statt
repeat until(mem[$ffae]=0)
sollte folgende Abfrage benutzt werden
TimeOut=1000 ; (beliebiger Integer-Wert) repeat TimeOut:=TimeOut-1 until((mem[$ffae]=0) and TimeOut)
Allerdings blähen derartige Sicherheitsvorkehrungen ein Programm sehr auf. Den Nutzen muß der Programmierer selbst abwägen:
function read_kc (adresse:integer):byte; begin mem[$ffae]:=1; bios(3,$1b); bios(3,integer('Q')); bios(3,lo(adresse)); bios(3,hi(adresse)); repeat until(mem[$ffae]=0); read_kc:=mem[$fe00]; end;
Der Assemblerquelltext für READ_KC könnte folgendermaßen aussehen:
;--------------------------- ; Input DE = Adresse im KC ; Output A = DatenByte READ_KC: LD HL,0FFAEH LD (HL),0 LD A,01BH CALL BIOS_OUT ; irgendeine ominoese ; BiosAusgabeRoutine LD A,'Q' CALL BIOS_OUT LD A,E CALL BIOS_OUT LD A,D CALL BIOS_OUT LD HL,0FFAEH LOOP: LD A,(HL) OR A JR Z,LOOP LD HL,0FE00H LD A,(HL) RET
In BASIC würde ich folgendermaßen programmieren:
1000 POKE 65454,0 1010 PRINT CHR$(27) 1020 PRINT "Q" 1030 PRINT CHR$(LO(ADRESSE)) 1040 PRINT CHR$(HI(ADRESSE)) 1050 IF PEEK(65454)=0 THEN GOTO 1050 1060 ERGEBNIS=PEEK(65024) 1070 RETURN
In C ist der Quelltext noch kompakter:
read_kc(adr) { poke(0xffae,0); printf("%cQ%c%c",27,lo(adr),hi(adr)); while(!peek(0xffae); return(peek(0xfe00); }
Es ist also in jeder erdenklichen Programmiersprache der Zugriff auf die ESC-Folgen möglich. Hat man sich erst einmal durchgearbeitet, so entstehen sehr schnell persönliche Bibliotheken, welche häufig benötigte Unterprogramm- und ESC-Rufe enthalten. Es wäre nicht schlecht, wenn jeder seine besten Bibliotheken gut dokumentiert dem KC-Club zur Verfügung stellt. In Verbindung mit einer Aufstellung von Mario Leubners aktueller ESC-Liste wäre hier eine Grundlage geschaffen, um Einsteigern das Programmiern mit ESC-Folgen schmackhaft zu machen.