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.