Nascom Journal |
Oktober 1981 · Ausgabe 10 |
Wert auf dem Stack obenauf. Wie man sieht, ist bei der Programmierung mit MCODE kein Rückkehrbefehl erforderlich; der Rücksprung zum Interpreter (NEXT) wird mit Hilfe der Konstante NEXTADR (= 103EH) von MCODE an das Maschinenprogramm angehängt.
Will man von FORTH aus Drucker oder Cassettenrecorder betreiben, so kann man sich der Ein- und Ausgaberoutinen des Betriebssystems bedienen. Es empfiehlt sich jedoch, auch die Routinen CIN und COUT zu verbessern. Dazu definieren wir zunächst einige Hilfsfunktionen:
MCODE NORMIN XXXX CF 6F 26 00 E5. MCODE BLINK XXXX DF 7B 6F 26 00 E5. MCODE NORMOUT XXXX C1 79 F7.
Die Adressen XXXX werden von MCODE angegeben und nicht vom Benutzer. Die folgende Funktion verlangt einen Namen als Argument. Wird dieser Namen im Dictionary gefunden, so wird die Startadresse des zugehörigen Codes auf den Stack gegeben, andernfalls wird der Name ausgedruckt:
: CADR GETWORD NAMES PEEKW LOOKUP EQZ IF PRINTS THEN ;
Wir definieren folgende Variablen, wobei mit CADR der Anfangswert berechnet wird:
CADR NORMOUT VARIABLE OUTVAR CADR NORMIN VARIABLE INVAR
Wir werden nun neue Ein-/Ausgabefunktionen definieren, die an Stelle von CIN und COUT treten sollen. Sie rufen diejenige Ein- oder Ausgaberoutine auf, deren Startadresse in den Variablen INVAR bzw. OUTVAR gespeichert sind.
: VAREX PEEKW EXECUTE ; : COUT2 OUTVAR VAREX ;
Im Interpretermodus geben wir nun folgenden Befehl ein:
CADR COUT2 CADR COUT 8 MOVEBYTES DEL
COUT2 kann nun unter der Adresse von COUT aufgerufen werden und wird deshalb gelöscht. Ebenso verfahren wir mit CIN; wir compilieren wiederum vorläufig CIN2, schieben sie an die Stelle von CIN, und löschen sie dann wieder, da man sie mit CIN aufrufen kann:
:/.IN2 INVAR VAREX ; CADR CIN2 CADR CIN 8 MOVEBYTES DEL
Nachdem wir nun so ausgiebig an dem System herumgebastelt haben, wollen wir nun einige simple FORTH-Funktionen compilieren:
MCODE UNDER XXXX E1 D1 E5 D5 E5.
kopiert den obersten Stackwert an die dritte Stackposition.
: < > EQZ ; : / /MOD POP ; : MOD /MOD SWAP POP ; : MAX OVER OVER > IF POP ELSE SWAP POP THEN ; : MIN OVER OVER < IF POP ELSE SWAP POP THEN ;
Diese trivialen Funktionen erwarten jeweils 2 Argumente auf dem Stack.
Die nächsten Funktionen sind etwas komplexer und greifen direkt auf den Speicher zu:
: PRINTM PEEKW CONBXA PRINTS ; : MEM+ DUP PEEKW ROT + SWAP POKEW ; : FILL SWAP ONE FOR UNDER OVER POKEB INC SWAP LOOP POP POP ; : ERASE 0 FILL ; : BLANKS 32 FILL ;
PRINTM druckt den Wert an der Adresse (T) aus (dezimal), MEM+ addiert den Wert von T-1 an die Adresse (T). FILL schreibt T-1 Bytes vom Wert T an die Adresse (T), ERASE und BLANKS schreiben Nullen bzw. Blanks in den Speicher, Message ist dagegen wieder eine definierende Funktion; sie erlaubt das Einfügen von Strings in das Programm:
: MESSAGE GETWORD VARBL CMPLW REPEAT CIN DUP COUT 34 EQ UNTIL DUP 8 EQ IF POP DEC ELSE OVER POKEB INC THEN LOOP POP ZERO OVER POKEB INC CODEADR POKEW ;
z.B. eine Error-Meldung:
MESSAGE ERROR Error"
Die Eingabe wird mit einem Anführungszeichen beendet; sie kann beliebig lang sein und auch Leerzeichen enthalten. Mit TYPE können derartige Strings ausgegeben werden.
: TYPE REPEAT DUP PEEKB DUP EQZ UNTIL COUT INC LOOP POP POP ;
TYPE erwartet eine Adresse auf dem Stack, der Name des Strings muß ihr vorausgehen:
ERROR TYPE Error
Da der Platz im Nascom-Journal beschränkt ist muß ich hier schließen. Im nächsten Heft folgen dann ein Bildschirm-Editor und die Cassettenroutinen, sowie ein erstes kleines Programm in FORTH, eine neue Version eines „alten“ Computerspiels: LIFE in einem „geschlossenen Universum“.
Seite 4 von 28 |
---|