★ CODING ★ Null Problemo mit RSX ★![]() |
| Null Problemo mit RSX (CPC Amstrad International) |
Wie RSX-Programme geschrieben werden “Nobody is perfect“, das weiß jeder. Deshalb haben sich die Entwickler des CPC-BASIC etwas ganz besonderes einfallen lassen: Die Befehlserweiterung mit RSX-Befehlen, mit denen Sie die Befehle Ihres CPC fast beliebig erweitern können. Diese Möglichkeit der Befehlserweiterung ist wirklich einmalig auf der großen Palette der BASIC-Dialekte. Mit diesem Bericht wollen wir Anregungen, Tips und Hintergrundinformationen liefern. Gewisse Kenntnisse in Maschinensprache sollten allerdings schon vorhanden sein. Sie werden sich wahrscheinlich fragen, was “RSX“ eigentlich bedeutet. RSX ist die Abkürzung von 'Resident System Extention' und heißt soviel wie 'feste Systemerweiterung'. Doch was soll das Ganze? Nehmen wir einmal an, Sie hätten ein kleines Assemblerprogramm geschrieben, das den Bildschirm Ihres CPC löscht, das heißt, den Bildschirmspeicher mit Nullbytes füllt (siehe Listing 1). Wenn Sie das Programm mit dem CPC-Assembler aus Heft 6/86 assembliert haben, können Sie es mit CALL &A000 aufrufen. Jedesmal, wenn Sie den Bildschirm löschen wollen, müssen Sie die Routine mit CALL &A000 aufrufen. Das geht ja noch, aber was ist, wenn Sie gleich zehn Assemblerroutinen gleichzeitig im Speicher haben und wegen so vieler Zahlen die Adresse jeder einzelnen Routine gar nicht mehr im Kopf haben? Hier hilft die Befehlserweiterung mit RSX. Damit können Sie jeder einzelnen Routine einen einprägsamen Namen geben. In unserem Beispiel wollen wir der Löschroutine den Namen CLS geben. Sehen Sie sich dazu Listing 2 an; eine Erläuterung wird gleich folgen. Nach der Assemblierung und dem Aufruf der Routine mit CALL &A000 bindet Ihr CPC den Befehl CLS bis zum Ausschalten in das BASIC ein. Wenn Sie alles richtig abgetippt haben, können Sie die Löschroutine mit ICLS aufrufen (das I steht für den sog. RSX-Strich, den Sie erhalten, indem Sie SHIFT und die Taste rechts neben dem 'P' drücken). Der RSX-Strich leitet immer einen RSX-Be-fehl ein. Doch zurück zu Listing 2. In Zeile 30 wird die Routine KL LOG EXT mit 'CALL &BCD1 ' aufgerufen. Diese Betriebssystemroutine binde* einen oder mehrere RSX-Befehle ein. Im HL-Register muß ihr die Adresse von vier Systembytes übergeben werden, die der CPC intern zur Verwaltung der RSX-Befehle benötigt. Im BC-Register muß die Adresse einer Tabelle stehen, deren erste zwei Bytes die Adresse einer zweiten Tabelle angeben müssen. Diese zweite Tabelle muß die Namen Ihrer RSX-Befehle enthalten. Auf die gerade genannten zwei Bytes müssen die Sprungbefehle zu den einzelnen Routinen folgen (Zeile 170). In der Namenstabelle stehen, wie vorher erwähnt, die Namen der RSX-Befehle und zwar in der Reihenfolge, in der die Sprungbefehle stehen. Beim letzten Buchstaben eines Namens muß Bit 7 gesetzt sein (Zeile 190), damit der CPC eventuell mehrere RSX-Befehle unterscheiden kann. Die Namenstabelle wird mit einem Nullbyte abgeschlossen (Zeile 200). Zugegeben, das Ganze ist etwas schwierig, deshalb sollten Sie sich Listing 2 noch einmal ganz genau anschauen. Wenn Sie Listing 2 mit dem Assembler assembliert haben, können Sie das Programm mit 'CALL &A000' starten. Sie werden sich vielleicht wundem, daß sich der CPC mit einem schlichten 'Ready' meldet und nicht den Bildschirm löscht. Das hat einen wichtigen Grund: der CPC hat soeben nur einen Befehl eingebunden. Geben Sie jetzt aber '|CLS' ein, wird der Bildschirm gelöscht. Vorsicht, Absturzgefahr Eines müssen Sie beim Arbeiten mit RSX-Programmen allerdings immer beachten: ein und dieselbe Erweiterung darf nur einmal eingebunden werden. Das leuchtet ein, wenn man weiß, daß die Betriebssystemroutine KL LOG EXT alle RSX-Tabellen intern mit Zeigern verbindet. Findet der CPC einen RSX-Befehl in einer Tabelle nicht, so sucht er in einer anderen Tabelle weiter, deren Adresse durch einen Zeiger angegeben wird. Wird eine Erweiterung mehrmals initialisiert, so wird der Zeiger wieder auf die gleiche Tabelle gerichtet. Findet der CPC nun einen RSX-Befehl innerhalb dieser Tabelle nicht, so kommt er unweigerlich in eine Endlosschleife, was natürlich einen Absturz des Systems zur Folge hat. Also noch einmal: dieselbe Erweiterung kein zweites Mal aufrufen, und schon kann (fast) nichts schiefgehen. Bisher haben wir nur Programme besprochen, in denen nur ein RSX-Befehl eingebunden werden mußte. Sehen Sie sich bitte Listing 3 an. Es soll Ihnen anschaulich machen, wie gleich mehrere RSX-Befehle eingebunden werden können. Wenn Sie das Listing mit dem CPC-Assembler assembliert und mit 'CALL &A000' gestartet haben, stehen Ihnen zwei neue RSX-Befehle zur Verfügung: |BLACK, der den Bildschirm mit Nullbytes und |WHITE, der den Bildschirm mit dem Byte 255 füllt. Vergleichen Sie bitte die Zeilen 250 und 260 mit den Zeilen 270 bis 300. Die Reihenfolge der Sprungbefehle muß nämlich genau mit der Reihenfolge der Namen übereinstimmen. Man darf aber nie vergessen, die Namenstabelle mit einem Nullbyte abzuschließen (Zeile 310). Die Sache mit den Parametern Es wäre naiv anzunehmen, RSX diene nur dazu, Maschinen-sprachprogrammen einen Namen zu geben. Nein, Sie können auch vom BASIC aus Parameter an das Maschinensprachprogramm übergeben. Die Parameterübergabe ist übrigens die gleiche wie bei dem CALL-Befehl und sieht folgendermaßen aus: |NAME , Parameter 1 , Parameter 2 , Parameter 3 , Parameter 4 , ... Sie können bis zu 32 Parameter von BASIC an das Maschinensprachprogramm übergeben. Wie können Sie nun die vom BASIC übergebenen Parameter ins Maschinensprachprogramm übernehmen? Hier helfen Ihnen die Register und Flags. Nach der Eingabe eines RSX-Befehls stehen in den Registern und Flags folgende Werte:
Listing 4 soll Ihnen das soeben Beschriebene anschaulich zeigen. Es installiert nach dem Assemblieren und Starten mit 'CALL &A000' einen neuen RSX-Befehl mit dem Format: |FILLBOX, linke, rechte, obere, untere physikalische Koordinate, Füllbyte Der Begriff 'physikalische Koordinate' wird wohl manchen irritieren, er bedeutet aber nichts anderes als 'Textkoordinate' mit dem Ursprung 0,0 links oben. |FILLBOX, 0,19,0,4, 255 füllt zum Beispiel einen Bereich links oben am Bildschirm mit der Breite von 20 und Länge von 5 Textkoordinaten mit dem Byte 255. Doch zurück zum Listing: die Zeilen 80 bis 110 und 210 bis 260 installieren den RSX-Befehl. Die Zeile 120 prüft, ob auch fünf Parameter übergeben wurden. Wenn das nicht der Fall ist, wird das Programm beendet. Diese Überprüfung sollten Sie übrigens bei jedem Maschinensprachprogramm, das vom BASIC Parameter übernimmt, einbauen. Fehleingaben werden seltener und das Programm wird anwenderfreundlicher. In den Zeilen 140 bis 180 übernimmt das Maschinensprachprogramm die benötigten Werte und transferiert sie in die entsprechenden Register. Die Highbytes werden hierbei nicht berücksichtigt, da ja kein Wert größer als 255 ist. Hier sollten Sie beachten, daß der CPC die mit einem RSX-Befehl übergebenen Parameter in eine Tabelle überträgt. Das IX-Register gibt dann die letzte Adresse an. IX gibt damit das Lowbyte, IX + 1 das Highbyte des letzten Parameters, IX + 2 das Lowbyte, IX + 3 das Highbyte des vorletzten Parameters usw. an. Die Zeile 190 ruft schließlich die Betriebssystemroutine SCR FILL BOX auf. Variablensalat Bei dem vorigen RSX-Befehl können Sie auch Variablen übergeben, z.B.: |FILLBOX,a%,b%,c%,d%,e%Das ' % ' hinter jeder Variable gibt an, daß es sich um Integer-(das heißt Ganzzahl-) Variablen handelt. Das Betriebssystem übergibt der Maschinensprachroutine dann die entsprechenden Werte. Es gibt allerdings noch einen zweiten Fall: Sie können Variablen übergeben, deren Adresse der Maschinensprachroutine übergeben werden. Sie können dann die Variablen vor der Rückkehr zum BASIC manipulieren, so daß Sie Werte vom Maschinensprachprogramm an das BASIC zurückgeben.Solche Variablen müssen vorne mit einem sogenannten 'Klammeraffen' gekennzeichnet werden. Diesen 'Klammeraffen' erreichen Sie durch Druck auf die Taste rechts neben dem 'P'. Dem Maschinenspracheprogramm wird dann nicht wie vorher der Wert, sondern eine sogenannte Deskriptoradresse der Variablen übermittelt. Diese Deskriptoradresse gibt bei einer Integervariablen (wir wollen uns auf eine solche beschränken) die Adresse des Wertes an, den die Variable repräsentiert. Nach dem Motto “ein Beispiel sagt mehr aus als tausend Worte“ wollen wir das soeben Gesagte zunächst einmal im BASIC ausprobieren. Geben Sie doch einmal folgendesein: a%-300 : PRINT @a%Der BASIC-Interpreter wird nun irgendeine Adresse auf dem Bildschirm ausgeben. Jetzt geben Sie bitte folgendes ein:PRINT PEEK (@a%) , PEEK (@a% + 1)Jetzt wird der CPC zuerst eine 44 und dann eine 1 ausgeben. Die 44 gibt das Lowbyte, die 1 das Highbyte des Wertes an. Nach der altbekannten Formel Highbyte mal 256 plus Lowbyte können wir den Variableninhalt berechnen, also: 1 mal 256 plus 44. Das ergibt 300.
Wir können den Variableninhalt aber auch manipulieren. Sie wollen eine 10 in der Variablen “a%“? Nichts einfacher als das: Das Lowbyte von 10 ist 10, das Highbyte 0. Mit POKE @a%, 10:POKE @a% +1,0 schreiben wir in die Variable a% den Wert 10. Mit PRINT a% können Sie das überprüfen. Kommen wir aber wieder zurück zur RSX-Programmierung! Nehmen wir einmal an, Sie wollen einen RSX-Befehl schreiben, der vom Bildschirm das Zeichen einer bestimmten Cursor-Position liest (auf den CPCs 664/6128 gibt es in BASIC mit COPYCHR$ schon einen entsprechenden Befehl). Der RSX-Befehl sollte folgendes Format haben: |READ, x, y, @wert%Die Variablen x und y geben die entsprechenden Textkoordinaten an und die Variable wert% gibt nach der Rückkehr zum BASIC den ASCII-Wert des Zeichens an, das sich an der betreffenden Stelle befindet. Für die Programmierung des gewünschten Befehls benötigen wir zwei Betriebssystemroutinen: TXT SET CURSOR, die den Text-Cursor an eine bestimmte Stelle plaziert (analog zum BASIC-Befehl LOCATE) und TXT RD CHAR, die von der aktuellen Cursor-Position ein Zeichen liest (analog zum BASIC-Befehl COPYCHR$ bei den CPC 664/6128). Listing 5 zeigt die Programmierung des |READ-Befehls. Die Zeilen 50 bis 70 und 230 bis 280 installieren den Befehl. Zeile 90 prüft, ob drei Befehle übergeben wurden. Die Zeilen 110 und 120 übergeben die Deskriptoradresse an das HL-Register. Zeile 130 legt das HL-Register auf dem Stapel ab. Die Zeilen 140 bis 170 holen die Koordinaten, setzen den Text-Cursor und lesen das Zeichen vom Bildschirm. Zeile 180 holt die Deskriptoradresse wieder vom Stapel und speichert darin den ASCII-Wert des Zeichens. In Zeile 210 wird in das Highbyte der Deskriptoradresse eine Null geladen, weil der ASCII-Wert nie über 255 hinausgeht. Zeile 220 veranlaßt den CPC schließlich zur Rückkehr ins BASIC.Nach der Assemblierung und dem Start mit 'CALL &A000' steht Ihnen der |READ-Befehl zur Verfügung. Vor Aufruf des Befehls muß die Variable wert% aber definiert sein, sonst gibt der CPC unweigerlich ein 'lmproper Argument' aus. Beispiel: wert% = 0 : |READ , 1 , 1 , @wert% : PRINT wert%Der CPC gibt nun den ASCII-Wert des Zeichens aus, das sich an der Koordinate 1,1 befindet. Wenn der CPC eine Null ausgibt, konnte er das Zeichen nicht identifizieren.RSX und Strings Manche Leser werden sich nun fragen, wie man Strings (Zeichenketten) als Parameter übergibt. Auch hier benötigen wir wieder unseren 'Klammeraffen' (auf dem CPC 664/6128 gibt es eine weitere Möglichkeit der Stringübergabe, die wir hier aber aus Kompatibilitätsgründen außer acht lassen). Wie bei den Integervariablen wird bei Strings dem Maschinensprachprogramm eine Deskriptoradresse übergeben. Diese unterscheidet sich aber erheblich von der vorigen Integer-Deskriptoradresse. Zuerst wird nämlich die Länge des Strings angegeben. Darauf folgen das Low- und Highbyte der Stringadresse. Hierzu muß gesagt werden, daß sich diese Adresse durch die sogenannte 'Garbage Collection' laufend ändern kann. Auch hierzu sollten wir uns ein Beispielprogramm anschauen, nämlich Listing 6. Es initialisiert einen RSX-Befehl, der folgendes Format besitzt: |PRINT,@a$. Er soll einen String, hier a$, auf dem Bildschirm ausgeben (analog zum BASIC-Befehl PRINT). Nehmen wir das Listing einmal unter die Lupe: Die Zeilen 50 bis 70 und 250 bis 300 initialisieren den |PRINT-Befehl. Zeile 90 prüft, ob ein Parameter übergeben wurde. Ist das der Fall, so wird die Deskriptoradresse ins HL-Register übertragen. Die folgenden Zeilen laden das B-Register mit der Länge und das DE-Register mit der Adresse des Strings. Danach wird innerhalb einer Schleife der String mit Hilfe der Betriebssystemroutine TXT OUTPUT auf dem Bildschirm ausgegeben. Mit 'RET' kommt der CPC dann wieder in den Direktmodus zurück. Auf den Spuren Sherlock Holmes' Bisher haben wir mit der Betriebssystemroutine KL LOG EXT RSX-Befehle nur eingebunden. Mit einer anderen Betriebssystemroutine, nämlich KL FIND COMMAND, können wir bereits vorhandene RSX-Befehle suchen. Listing 7 macht das anhand des RSX-Befehls |DIR anschaulich, der nur bei den CPCs mit Diskettenlaufwerk vorhanden ist. Der Routine KL FIND COMMAND muß im HL-Register die Adresse des Namens übergeben werden (Zeile 40). Bei dem letzten Buchstaben muß aber Bit 7 gesetzt sein (Zeile 160 und 170). Zeile 50 ruft KL FIND COMMAND mit 'CALL &BCD4' auf. Wenn das Carry-Flag gesetzt ist, kann der CPC den RSX-Befehl finden. Im HL-Register befindet sich dann die Anfangsadresse und im C-Register die ROM-Nummer. Auf diese Betriebssystemroutine soll aber nicht weiter eingegangen werden, weil sie sehr selten benutzt wird. Abschließend finden Sie noch eine Liste für weiterführende Literatur, die sich mit der RSX-Programmierung beschäftigt, und eine Zusammenstellung aller Betriebssystemroutinen, die wir in den Beispielprogrammen benutzt haben. Nach dem Motto “Probieren geht über Studieren“ wünschen wir Ihnen noch viel Spaß und wenig Abstürze bei der RSX-Programmierung! Weiterführende Literatur
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||