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 NEXT­ADR (= 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