; ------------------------------------------------------------------------- ; X-FORTH 1.1b ; RELEASE 18.10.2003 ; Homepage: http://www.strotmann.de/twiki/bin/view/APG/ProjXForth ; License: GNU Public License (GPL) ; ; based on Sources from fig-forth and Andreas Jung ; ; compiles with ATASM --> http://atasm.sourceforge.net ;-------------------------------------------------------------------------- ; Flags DEBUG = 0 KEYWAIT = 1 FINDDEBUG = 0 FILE = 1 DYNMEMTOP = 0 ; Startadresse im Speicher * = $2000 ; Kernal-Routinen des Atari800 DOSVEC = $000A ; FORTH-Systemkonstanten. Sie muessen gegebenenfalls an die jeweilige ; Hardware angepasst werden. BOS = $8E ; Start des Daten-Stacks in der Zeropage TOS = $C6 ; Zeiger auf den TOS ($C6) N = $F0 ; temporaerer Arbeitsspeicher ($F0) (orig TOS+8) IP = N+8 ; Instruction Pointer IP ($F8) W = IP+3 ; Codefeld-Pointer W ($FB) UP = W+2 ; User-Pointer UP ($FD) XSAVE = UP+2 ; temporaerer Speicher fuer das X-Register ($FF) ; TIBX = $0100 ; Terminal Input Buffer, 84 Bytes MEM = $B800 ; Ende des FORTH-Speichers UAREA = MEM-128 ; User-Area, 128 Bytes ; Der Speicher zwischen dem Ende des Dictionaries und DAREA steht fuer ; Benutzerprogramme zur Verfuegung. ; Es folgen nun die Boot-up-Parameter, d.h. Sprungvektoren und ; Parameter zur Systembeschreibung. ORIG NOP ; 0+ORIGIN: Kaltstart ueber COLD JMP PFA_COLD ; REENTR NOP ; 4+ORIGIN: Warmstart ueber WARM JMP WARM ; .WORD $0004 ; 8+ORIGIN: "6502" zur Basis 36 .WORD $5ED2 ; .WORD NFA_LASTWORD ; 12+ORIGIN: NFA des letzten Wortes .WORD 126 ; 14+ORIGIN: externer Backspace-Charakter .WORD UAREA ; 16+ORIGIN: initialer User-Pointer UP .WORD TOS ; 18+ORIGIN: Startwert fuer S0 .WORD $1FF ; 20+ORIGIN: Startwert fuer R0 .WORD TIBX ; 22+ORIGIN: Startwert fuer TIB .WORD 31 ; 24+ORIGIN: Startwert fuer WIDTH .WORD 0 ; 26+ORIGIN: Startwert fuer WARNING .WORD TOP ; 28+ORIGIN: Startwert fuer FENCE .WORD TOP ; 30+ORIGIN: Startwert fuer DP .WORD PFA_FORTH+6 ; 32+ORIGIN: Startwert fuer VOC-LINK ; Die folgenden NOP's bewirken eine Verschiebung des Dictionaries, so ; dass kein Codefeld auf eine Adresse der Form $XXFF faellt. Aufgrund ; eines Bugs im 6502-Prozessor wuerde dann naemlich der indirekte ; Sprung an der Adresse W-1 nicht richtig funktionieren. Bei jeder ; Aenderung des Dictionaries muessen die folgenden Zeilen gegebenenfalls ; geaendert werden. ; NOP ; NOP NOP ; >>>>>>>>>>>>>>>> LIT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( LIT [ -> n ] legt die inline folgende Zahl auf den Stack. ) ;( ========================================================================= ) NFA_LIT .CBYTE $83, "LIT" LFA_LIT .WORD 0 CFA_LIT .WORD PFA_LIT PFA_LIT LDA (IP),Y ; Lo-Byte der inline folgenden Zahl holen PHA ; und auf den Stack retten INC IP ; Instruction-Pointer inkrementieren BNE L30 INC IP+1 L30 LDA (IP),Y ; Hi-Byte der inline folgenden Zahl holen L31 INC IP ; Instruction-Pointer inkrementieren BNE PUSH INC IP+1 PUSH DEX ; Datenstack-Pointer dekrementieren DEX PUT STA 1,X ; Hi-Byte (ist in A) auf den Datenstack legen PLA ; Lo-Byte vom Stack holen STA 0,X ; und auf den Datenstack legen ; ========================================================================= ; Der Adressinterpreter NEXT holt die Adresse des naechsten Secondaries ; und fuehrt es aus. ; ========================================================================= NEXT LDY #1 LDA (IP),Y ; Hi-Byte der Wortadresse STA W+1 ; im W-Register ablegen DEY LDA (IP),Y ; Lo-Byte der Wortadresse STA W ; im W-Register ablegen .IF debug PHA LDA DEBUGFLG BEQ DL1 PLA JSR TRACE ; DEBUG PHA DL1 PLA .ENDIF CLC LDA IP ; IP um 2 inkrementieren ADC #2 STA IP BCC L54 INC IP+1 L54 JMP W-1 ; indirekten Sprung nach (W) ausfuehren ; >>>>>>>>>>>>>>>> CLIT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CLIT [ -> b ] legt das inline folgende Byte auf den Stack. ) ;( ========================================================================= ) NFA_CLIT .CBYTE $84,"CLIT" LFA_CLIT .WORD NFA_LIT CFA_CLIT .WORD PFA_CLIT PFA_CLIT LDA (IP),Y ; inline folgendes Byte holen PHA ; und auf den Stack legen TYA ; Akkumulator (Hi-Byte) mit Y=0 laden BEQ L31 ; und in die Routine LIT verzweigen ; ========================================================================= ; Die Routine SETUP poppt n Zellen vom Datenstack und schreibt sie in den ; temporaeren Speicher ab Adresse N. Die Zahl n wird im Akkumulator ; uebergeben. ; ========================================================================= SETUP ASL A ; n verdoppeln, ergibt Anzahl zu poppender Bytes STA N-1 ; Anzahl zu poppender Bytes sichern L63 LDA 0,X ; ein Byte vom Datenstack holen (zu Anfang ist Y=0) STA N,Y ; und im Speicherbereich ab Adresse N ablegen INX ; Datenstack-Pointer inkrementieren INY ; Zielindex inkrementieren CPY N-1 ; sind bereits 2*n Bytes gepoppt? BNE L63 ; nein, dann weitermachen LDY #0 ; Y.=0 wiederherstellen RTS ; und fertig ; >>>>>>>>>>>>>>>> EXECUTE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( EXECUTE [ addr -> ] fuehrt das Wort aus, dessen CFA auf dem Stack liegt. ) ;( ========================================================================= ) NFA_EXECUTE .CBYTE $87,"EXECUTE" LFA_EXECUTE .WORD NFA_CLIT CFA_EXECUTE .WORD PFA_EXECUTE PFA_EXECUTE LDA 0,X ; Lo-Byte von addr STA W ; in das W-Register schreiben LDA 1,X ; Hi-Byte von addr STA W+1 ; in das W-Register schreiben INX ; addr vom Datenstack nehmen INX JMP W-1 ; Secondary ab addr ausfuehren ; >>>>>>>>>>>>>>>> BRANCH <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( BRANCH [ ] bewirkt einen relativen Sprung, abhaengig vom inline folgenden ) ;( Offset. ) ;( ========================================================================= ) NFA_BRANCH .CBYTE $86, "BRANCH" LFA_BRANCH .WORD NFA_EXECUTE CFA_BRANCH .WORD PFA_BRANCH PFA_BRANCH CLC LDA (IP),Y ; Lo-Byte des Offsets holen ADC IP ; zu aktuellem IP hinzuaddieren PHA ; und auf den Stack retten INY LDA (IP),Y ; Hi-Byte des Offsets holen ADC IP+1 ; zu aktuellem IP hinzuaddieren STA IP+1 ; und im IP-Register ablegen PLA ; Lo-Byte des neuen IP-Inhaltes vom Stack holen STA IP ; und im IP-Register ablegen JMP NEXT+2 ; dort weitermachen, LDY #1 kann uebersprungen werden ; >>>>>>>>>>>>>>>> 0BRANCH <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( 0BRANCH [ f -> ] bewirkt einen relativen Sprung, abhaengig vom inline ) ;( folgenden Offset, wenn f Null ist. ) ;( ========================================================================= ) NFA_0BRANCH .CBYTE $87, "0BRANCH" LFA_0BRANCH .WORD NFA_BRANCH CFA_0BRANCH .WORD PFA_0BRANCH PFA_0BRANCH INX ; f vom Datenstack nehmen INX LDA $FE,X ; f testen ORA $FF,X BEQ PFA_BRANCH ; verzweigen, falls f=0 BUMP CLC LDA IP ; sonst IP ueber den Offset hinwegsetzen ADC #2 STA IP BCC L122 INC IP+1 L122 JMP NEXT ; und weitermachen ; >>>>>>>>>>>>>>>> (LOOP) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [LOOP] [ ] inkrementiert den Loop-Index um 1 und testet auf Erreichen der ) ;( Abbruchbedingung. Eventuell wird gemaess einem inline folgenden Offset ) ;( verzweigt. ) ;( ========================================================================= ) NFA_BRACKETLOOP .CBYTE $86, "(LOOP)" LFA_BRACKETLOOP .WORD NFA_0BRANCH CFA_BRACKETLOOP .WORD PFA_BRACKETLOOP PFA_BRACKETLOOP L130 STX XSAVE ; X-Register retten TSX ; Stackpointer nach X bringen INC $101,X ; Loop-Index I inkrementieren BNE PL1 INC $102,X PL1 CLC LDA $103,X ; Hi(Schleifenlimit-I-1) berechnen SBC $101,X LDA $104,X SBC $102,X PL2 LDX XSAVE ; X-Register wiederherstellen ASL A ; falls obige Differenz nicht negativ: BCC PFA_BRANCH ; an den Schleifenanfang verzweigen PLA ; sonst Schleifenlimit und I vom Stack nehmen PLA PLA PLA JMP BUMP ; und Offset uebergehen ; >>>>>>>>>>>>>>>> (+LOOP) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [+LOOP] [ n -> ] inkrementiert den Loop-Index um n und testet auf Errei- ) ;( chen der Abbruchbedingung. Eventuell wird gemaess einem inline folgenden ) ;( Offset verzweigt. ) ;( ========================================================================= ) NFA_BRACKETPLUSLOOP .CBYTE $87, "(+LOOP)" LFA_BRACKETPLUSLOOP .WORD NFA_BRACKETLOOP CFA_BRACKETPLUSLOOP .WORD PFA_BRACKETPLUSLOOP PFA_BRACKETPLUSLOOP INX ; Datenstackpointer inkrementieren INX STX XSAVE ; X-Register retten LDA $FF,X ; Hi-Byte von n PHA ; zweimal auf den Stack legen PHA LDA $FE,X ; Lo-Byte von n holen TSX ; Stackpointer nach X holen INX ; Hi-Byte von n interessiert momentan nicht INX CLC ADC $101,X ; Lo-Byte von n auf I aufaddieren STA $101,X PLA ; Hi-Byte von n holen ADC $102,X ; und auf I aufaddieren STA $102,X PLA ; nochmal Hi-Byte von n holen BPL PL1 ; falls n positiv: bei (LOOP) weitermachen CLC LDA $101,X ; sonst Hi(I-Schleifenlimit-1) berechnen SBC $103,X LDA $102,X SBC $104,X JMP PL2 ; und hiermit bei (LOOP) weitermachen ; >>>>>>>>>>>>>>>> (DO) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [DO] [ n1 n2 -> ] bringt die Loop-Parameter [Startindex, Limit] zum ) ;( Return-Stack. ) ;( ========================================================================= ) NFA_BRACKETDO .CBYTE $84, "(DO)" LFA_BRACKETDO .WORD NFA_BRACKETPLUSLOOP CFA_BRACKETDO .WORD PFA_BRACKETDO PFA_BRACKETDO LDA 3,X ; Limit zum Returnstack bringen PHA LDA 2,X PHA LDA 1,X ; Startindex zum Returnstack bringen PHA LDA 0,X PHA POPTWO INX ; n2 von Datenstack poppen INX POP INX ; n1 vom Datenstack poppen INX JMP NEXT ; und weitermachen ; >>>>>>>>>>>>>>>> R <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( R [ -> n ] kopiert das oberste Element des Return-Stacks zum Parameter- ) ;( Stack. ) ;( ========================================================================= ) NFA_R .CBYTE $81, "R" LFA_R .WORD NFA_BRACKETDO CFA_R .WORD PFA_R PFA_R STX XSAVE ; Datenstackpointer retten TSX ; X-Register als Index in den Returnstack benutzen LDA $101,X ; Lo-Byte vom Returnstack holen PHA ; und merken LDA $102,X ; Hi-Byte vom Returnstack holen LDX XSAVE ; Datenstackpointer wiederherstellen JMP PUSH ; n auf den Datenstack pushen und fertig ; >>>>>>>>>>>>>>>> I <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( I [ -> n ] legt den aktuellen Loop-Index auf den Stack. ) ;( ========================================================================= ) NFA_I .CBYTE $81, "I" LFA_I .WORD NFA_R CFA_I .WORD PFA_R ; Link zu "R", Befehl identisch PFA_I ; >>>>>>>>>>>>>>>> J <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( J [ -> n ] kopiert den Zaehlindex der aesseren Scheife auf den Datenstack ) ;( ) ;( ========================================================================= ) NFA_J .CBYTE $81, "J" LFA_J .WORD NFA_I CFA_J .WORD PFA_J PFA_J STX XSAVE ; Datenstackpointer retten TSX ; X-Register als Index in den Returnstack benutzen LDA $105,X ; Lo-Byte vom Returnstack holen PHA ; und merken LDA $106,X ; Hi-Byte vom Returnstack holen LDX XSAVE ; Datenstackpointer wiederherstellen JMP PUSH ; n auf den Datenstack pushen und fertig ; >>>>>>>>>>>>>>>> DIGIT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DIGIT [ c n1 -> n2 tf ] falls ok, [ c n1 -> ff ] falls schlecht. Wandelt ) ;( den ASCII-Charakter c in sein Zahlen-Aequivalent n2 um. Als Basis wird ) ;( n1 benutzt. Ist c ein gueltiges Ziffernsymbol im n1-System, so wird tf=1 ) ;( zurueckgegeben, sonst ff=0. ) ;( ========================================================================= ) NFA_DIGIT .CBYTE $85, "DIGIT" LFA_DIGIT .WORD NFA_J CFA_DIGIT .WORD PFA_DIGIT PFA_DIGIT SEC LDA 2,X ; Zeichen c holen SBC #$30 ; und '0' subtrahieren BMI L234 ; falls negativ: Misserfolg melden CMP #$A ; mit 10 vergleichen BMI L227 ; kleiner, dann weiter SEC SBC #7 ; sonst weitere 7 abziehen CMP #$A ; falls kleiner als 10: BMI L234 ; Misserfolg melden L227 CMP 0,X ; ermittelte Zahl mit Basis n1 vergleichen BPL L234 ; Zahl groesser oder gleich Basis, dann Misserfolg melden STA 2,X ; sonst Zahl in n2 zurueckgeben LDA #1 ; tf zurueckgeben (Y=0) PHA TYA JMP PUT L234 TYA ; Misserfolg: ff zurueckgeben (Y=0) PHA INX INX JMP PUT ; >>>>>>>>>>>>>>>> (FIND) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [FIND] [ addr1 addr2 -> pfa b tf ] falls ok, [ addr1 addr2 -> ff ] sonst. ) ;( Sucht im Dictionary, beginnend bei der NFA addr2, nach dem String addr1. ) ;( Wird das Wort gefunden, so wird seine PFA, das Count-Byte und ein true- ) ;( Flag uebergeben, sonst lediglich ein false-Flag. ) ;( ========================================================================= ) NFA_BRACKETFIND .CBYTE $86, "(FIND)" LFA_BRACKETFIND .WORD NFA_DIGIT CFA_BRACKETFIND .WORD PFA_BRACKETFIND PFA_BRACKETFIND LDA #2 ; addr1 und addr2 JSR SETUP ; in den Speicher ab Adresse N poppen STX XSAVE ; Datenstackpointer retten L249 LDY #0 ; Vergleich beginnt beim Count-Byte LDA (N),Y ; Count-Byte des Woerterbucheintrages holen EOR (N+2),Y ; und mit Count-Byte des Strings vergleichen AND #$3F ; dabei die beiden obersten Bits ignorieren BNE L281 ; bei Ungleichheit verzweigen L254 INY ; naechstes Zeichen anvisieren LDA (N),Y ; naechstes Zeichen des Woerterbucheintrages holen EOR (N+2),Y ; und mit naechstem Zeichen des Strings vergleichen ASL A ; hoechstes Bit in den Uebertrag schieben BNE L280 ; bei Ungleichheit verzweigen BCC L254 ; weiter vergleichen falls String-Ende noch nicht erreicht LDX XSAVE ; bei Gleichheit Datenstackpointer wiederherstellen DEX ; auf dem Datenstack Platz fuer pfa und b schaffen DEX DEX DEX CLC TYA ; Offset des letzten String-Zeichens ADC #5 ; plus 5 ergibt Offset des Parameterfeldes ADC N ; plus NFA ergibt PFA STA 2,X ; Lo-Byte der PFA in den Datenstack schreiben LDY #0 TYA ; eventuell aufgetretenen Uebertrag ADC N+1 ; in Hi-Byte der PFA beruecksichtigen STA 3,X ; Hi-Byte der PFA in den Datenstack schreiben STY 1,X ; Hi-Byte von b auf Null setzen LDA (N),Y ; Count-Byte des gefundenen Wortes holen STA 0,X ; und in den Datenstack schreiben LDA #1 ; True-Flag vorbereiten PHA JMP PUSH ; auf den Datenstack legen und fertig L280 BCS L284 ; Suche ueberspringen falls String-Ende erreicht L281 INY ; naechstes Zeichen anvisieren LDA (N),Y ; naechstes Zeichen des Woerterbucheintrages holen .IF finddebug JSR OUTCH ; debug - Druckt die Suche nach einem Word LDA (N),Y ; debug - wenn Fehler in der Verkettung bestehen, ; debug - Woerter nicht gefunden werden .ENDIF BPL L281 ; hoechstes Bit nicht gesetzt, dann weitersuchen L284 INY ; sonst Byte hinter dem Namenfeld anvisieren LDA (N),Y ; Lo-Byte des Linkfeldes holen TAX ; und in das X-Register laden INY ; Hi-Byte des Linkfeldes anvisieren LDA (N),Y ; und holen STA N+1 ; und anstelle des alten Wertes von addr2 ablegen STX N ; das gleiche mit dem Lo-Byte des Linkfeldes ORA N ; im Linkfeld angegebene Adresse testen BNE L249 ; ungleich Null, dann erneut vergleichen LDX XSAVE ; sonst Datenstackpointer wiederherstellen LDA #0 ; False-Flag vorbereiten PHA JMP PUSH ; auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> ENCLOSE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ENCLOSE [ addr1 c -> addr1 n1 n2 n3 ] separiert einen Text beginnend ab ) ;( der Adresse addr1 mit dem Delimiter c. n1 ist der Byte-Offset zum ersten ) ;( von c verschiedenen ASCII-Charakter, n2 der Byte-Offset zum ersten ) ;( Delimiter-Charakter c hinter dem Text und n3 der Byte-Offset zum ersten ) ;( Charakter, der auf diesen Delimiter folgt. Das Zeichen ASCII-Null dient ) ;( als unbedingter Delimiter, ab dem nicht mehr weitergesucht wird. ) ;( ========================================================================= ) NFA_ENCLOSE .CBYTE $87,"ENCLOSE" LFA_ENCLOSE .WORD NFA_BRACKETFIND CFA_ENCLOSE .WORD PFA_ENCLOSE PFA_ENCLOSE LDA #2 ; Parameter addr1 und c JSR SETUP ; in den Speicher ab N poppen TXA SEC ; Platz im Datenstack fuer addr1, n1, n2 und n3 SBC #8 ; bereitstellen TAX STY 3,X ; Hi-Byte von n2 auf Null setzen (Y=0) STY 1,X ; Hi-Byte von n1 ebenso DEY L313 INY ; naechstes Zeichen im Text ab addr1 anvisieren LDA (N+2),Y ; Zeichen holen CMP N ; mit c vergleichen BEQ L313 ; alle c's am Anfang ueberlesen STY 4,X ; Position des ersten Nicht-c in n1 ablegen L318 LDA (N+2),Y ; naechstes Zeichen im Text ab addr1 holen BNE L327 ; nicht Null, dann verzweigen STY 2,X ; sonst Position der Null in n2 STY 0,X ; und in n3 ablegen TYA CMP 4,X ; Position mit Position des ersten Nicht-c vergleichen BNE L326 ; andere Position, dann verzweigen INC 2,X ; sonst n2 inkrementieren L326 JMP NEXT ; und fertig L327 STY 2,X ; aktuelle Position in n2 ablegen INY ; naechstes Zeichen anvisieren CMP N ; aktuelles Zeichen mit c vergleichen BNE L318 ; bei Ungleichheit weitersuchen STY 0,X ; sonst naechste Position im n3 ablegen JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> EMIT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( EMIT [ c -> ] sendet den ASCII-Charakter c zum Standard-Ausgabegeraet und ) ;( erhoeht die USER-Variable OUT um 1. ) ;( ========================================================================= ) NFA_EMIT .CBYTE $84, "EMIT" LFA_EMIT .WORD NFA_ENCLOSE CFA_EMIT .WORD PFA_EMIT PFA_EMIT XEMIT TYA ; A.=0 (Y=0) SEC LDY #$1A ; Offset der User-Variablen OUT ADC (UP),Y ; OUT inkrementieren STA (UP),Y INY LDA #0 ADC (UP),Y ; auch Hi-Byte von OUT beruecksichtigen STA (UP),Y TYA ; Y Register auf den Stack retten PHA LDA 0,X ; Zeichen c holen STX XSAVE ; Datenstackpointer retten JSR OUTCH ; Zeichen ausgeben LDX XSAVE ; Datenstackpointer wiederherstellen PLA TAY ; Y Register wiederherstellen JMP POP ; c vom Datenstack entfernen und fertig ; >>>>>>>>>>>>>>>> KEY <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( KEY [ -> c ] wartet auf eine Tastaturbetaetigung und uebergibt den ASCII- ) ;( Code der gedrueckten Taste. ) ;( ========================================================================= ) NFA_KEY .CBYTE $83, "KEY" LFA_KEY .WORD NFA_EMIT CFA_KEY .WORD PFA_KEY PFA_KEY XKEY STX XSAVE ; Datenstackpointer retten XKEY1 JSR GETCH ; Zeichen von der Tastatur einlesen (im accu) LDX XSAVE ; Datenstackpointer wiederherstellen JMP PUSHOA ; Akkumulator als c auf den Datenstack und fertig ; >>>>>>>>>>>>>>>> ?TERMINAL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?TERMINAL [ -> f ] liefert f=1 zurueck, wenn eine Taste betaetigt wurde, ) ;( sonst f=0. ) ;( ========================================================================= ) NFA_QUERYTERMINAL .CBYTE $89, "?TERMINAL" LFA_QUERYTERMINAL .WORD NFA_KEY CFA_QUERYTERMINAL .WORD PFA_QUERYTERMINAL PFA_QUERYTERMINAL XQTER CH = $02FC LDA CH ; wenn $FF dann keine Taste gedrückt CLC ADC #1 ; +1 = 0 wenn keine Taste gedrückt BEQ XQTER1 LDA #1 ; ansonsten 1 XQTER1 JMP PUSHOA ; Akkumulator auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> CR <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CR [ ] gibt einen Zeilenvorschub auf den Bildschirm aus. ) ;( ========================================================================= ) NFA_CR .CBYTE $82, "CR" LFA_CR .WORD NFA_QUERYTERMINAL CFA_CR .WORD PFA_CR PFA_CR XCR STX XSAVE ; Datenstackpointer retten TYA PHA ; Y Register retten LDA #155 ; Carriage Return JSR OUTCH ; auf dem Bildschirm ausgeben LDX XSAVE ; Datenstackpointer wiederherstellen PLA ; Y Register wiederherstellen TAY JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> CMOVE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CMOVE [ from to count ] kopiert von Adresse from nach Adresse to genau ) ;( count Bytes in aufsteigender Richtung. ) ;( ========================================================================= ) NFA_CMOVE .CBYTE $85, "CMOVE" LFA_CMOVE .WORD NFA_CR CFA_CMOVE .WORD PFA_CMOVE PFA_CMOVE LDA #3 ; alle drei Parameter JSR SETUP ; in den Speicherbereich ab Adresse N poppen L370 CPY N ; noch nicht count Bytes kopiert? BNE L375 ; dann verzweigen DEC N+1 ; auch Hi-Byte von count beruecksichtigen BPL L375 ; und verzweigen, wenn noch Bytes zu kopieren sind JMP NEXT ; sonst fertig L375 LDA (N+4),Y ; ein Byte holen STA (N+2),Y ; und im Zielbereich ablegen INY ; naechstes Byte anvisieren BNE L370 ; bei Ueberlauf des Y-Registers: INC N+5 ; Hi-Byte von from inkrementieren INC N+3 ; Hi-Byte von to inkrementieren JMP L370 ; und weiterkopieren ; >>>>>>>>>>>>>>>> U* <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( U* [ u1 u2 -> ud ] multipliziert die beiden vorzeichenlosen Zahlen u1 und ) ;( u2 und gibt das Ergebnis als doppeltgenaue, vorzeichenlose Zahl ud ) ;( zurueck. ) ;( ========================================================================= ) NFA_UMULT .CBYTE $82, "U*" LFA_UMULT .WORD NFA_CMOVE CFA_UMULT .WORD PFA_UMULT PFA_UMULT LDA 2,X ; u1 nach N/N+1 retten STA N ; und ud auf Null setzen (Y=0) STY 2,X LDA 3,X STA N+1 STY 3,X LDY #16 ; Y-Register als Zaehler (16 Bit) L396 ASL 2,X ; Zwischenergebnis ud verdoppeln ROL 3,X ROL 0,X ; und rechts in u2 hineinschieben ROL 1,X ; dabei das jetzt hoechste Bit von u2 holen BCC L411 ; Bit=0, dann verzweigen CLC LDA N ; sonst das gerettete u1 auf ud aufaddieren ADC 2,X STA 2,X LDA N+1 ADC 3,X STA 3,X LDA #0 ; auch das Hi-Word von ud beruecksichtigen ADC 0,X STA 0,X L411 DEY ; naechstes Bit anvisieren BNE L396 ; noch nicht alle Bits durch, dann weiterrechnen JMP NEXT ; sonst fertig ; >>>>>>>>>>>>>>>> U/ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( U/ [ ud u1 -> u2 u3 ] dividiert die doppelt genaue, vorzeichenlose Zahl ) ;( ud durch die vorzeichenlose Zahl u1 und uebergibt den Quotienten u3 und ) ;( den Rest u2 als vorzeichenlose Zahlen. ) ;( ========================================================================= ) NFA_UDIV .CBYTE $82, "U/" LFA_UDIV .WORD NFA_UMULT CFA_UDIV .WORD PFA_UDIV PFA_UDIV LDA 4,X ; Hi-Word von ud nach [4/5,X] bringen LDY 2,X ; und Lo-Word von ud nach [2/3,X] STY 4,X ; wobei das Lo-Word bereits einmal verdoppelt wird ASL A STA 2,X LDA 5,X LDY 3,X STY 5,X ROL A STA 3,X LDA #16 ; Schleife wird 16 mal durchlaufen STA N ; N ist Schleifenzaehler L433 ROL 4,X ; Hi-Word von ud verdoppeln ROL 5,X SEC LDA 4,X ; u1 vom Hi-Word von ud abziehen SBC 0,X ; Ergebnis nach Y/A TAY LDA 5,X SBC 1,X BCC L444 ; falls Ergebnis nicht negativ: STY 4,X ; Subtraktion an ud tatsaechlich durchfuehren STA 5,X L444 ROL 2,X ; Lo-Word von ud nach links schieben ROL 3,X ; dabei eventuell eine 1 in den Quotienten hineinschieben DEC N ; naechstes Bit anvisieren BNE L433 ; noch nicht alle Bits durch, dann weitermachen JMP POP ; sonst u1 vom Datenstack entfernen und fertig ; >>>>>>>>>>>>>>>> AND <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( AND [ n1 n2 -> n3 ] ermittelt die bitweise Und-Verknuepfung der Zahlen n1 ) ;( und n2. ) ;( ========================================================================= ) NFA_AND .CBYTE $83, "AND" LFA_AND .WORD NFA_UDIV CFA_AND .WORD PFA_AND PFA_AND LDA 0,X ; Lo-Byte von n1 AND n2 berechnen AND 2,X PHA LDA 1,X ; Hi-Byte von n1 AND n2 berechnen AND 3,X BINARY INX ; n2 vom Datenstack entfernen INX JMP PUT ; Ergebnis auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> OR <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( OR [ n1 n2 -> n3 ] ermittelt die bitweise Oder-Verknuepfung der Zahlen ) ;( n1 und n2. ) ;( ========================================================================= ) NFA_OR .CBYTE $82, "OR" LFA_OR .WORD NFA_AND CFA_OR .WORD PFA_OR PFA_OR LDA 0,X ; Lo-Byte von n1 OR n2 berechnen ORA 2,X PHA LDA 1,X ; Hi-Byte von n1 OR n2 berechnen ORA 3,X INX ; n2 vom Datenstack entfernen INX JMP PUT ; Ergebnis auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> XOR <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( XOR [ n1 n2 -> n3 ] ermittelt die bitweise Exklusiv-Oder-Verknuepfung der ) ;( Zahlen n1 und n2. ) ;( ========================================================================= ) NFA_XOR .CBYTE $83, "XOR" LFA_XOR .WORD NFA_OR CFA_XOR .WORD PFA_XOR PFA_XOR LDA 0,X ; Lo-Byte von n1 XOR n2 berechnen EOR 2,X PHA LDA 1,X ; Hi-Byte von n1 XOR n2 berechnen EOR 3,X INX ; n2 vom Datenstack entfernen INX JMP PUT ; Ergebnis auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> SP@ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SP@ [ -> addr ] ermittelt die Adresse des TOS [vor dem Aufruf]. ) ;( ========================================================================= ) NFA_SPFETCH .CBYTE $83, "SP@" LFA_SPFETCH .WORD NFA_XOR CFA_SPFETCH .WORD PFA_SPFETCH PFA_SPFETCH TXA ; Adresse des TOS wird durch das X-Register angegeben PUSHOA PHA ; Akkumulator als Lo-Byte LDA #0 ; und 0 als Hi-Byte JMP PUSH ; auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> SP! <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SP! [ ] reinitialisiert den Stack-Pointer. ) ;( ========================================================================= ) NFA_SPSTORE .CBYTE $83, "SP!" LFA_SPSTORE .WORD NFA_SPFETCH CFA_SPSTORE .WORD PFA_SPSTORE PFA_SPSTORE LDY #6 ; User-Variable S0 enthaelt Initialwert des Datenstackpointers LDA (UP),Y ; Inhalt der User-Variablen holen TAX ; in den Datenstackpointer X laden JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> RP! <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( RP! [ ] reinitialisiert den Return-Stack-Pointer. ) ;( ========================================================================= ) NFA_RPSTORE .CBYTE $83, "RP!" LFA_RPSTORE .WORD NFA_SPSTORE CFA_RPSTORE .WORD PFA_RPSTORE PFA_RPSTORE STX XSAVE ; Datenstackpointer retten LDY #8 ; User-Variable R0 enthaelt Initialwert des Returnstackpointers LDA (UP),Y ; Inhalt der User-Variablen holen TAX ; und in den Returnstackpointer laden TXS LDX XSAVE ; Datenstackpointer wiederherstellen JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> ;S <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ;S [ ] beendet die Ausfuehrung des aktuellen Wortes und kehrt zum aufru- ) ;( fenden Wort zurueck. ) ;( ========================================================================= ) NFA_EXIT .CBYTE $82, ";S" LFA_EXIT .WORD NFA_RPSTORE CFA_EXIT .WORD PFA_EXIT PFA_EXIT PLA ; Returnadresse vom Returnstack poppen STA IP ; und in den IP laden PLA STA IP+1 JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> LEAVE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( LEAVE [ ] erzwingt das Verlassen der aktuellen DO ... LOOP - oder DO ... ) ;( +LOOP - Schleife, indem der Abbruchwert auf den Stand des aktuellen ) ;( Index-Wertes gesetzt wird. ) ;( ========================================================================= ) NFA_LEAVE .CBYTE $85, "LEAVE" LFA_LEAVE .WORD NFA_EXIT CFA_LEAVE .WORD PFA_LEAVE PFA_LEAVE STX XSAVE ; Datenstackpointer retten TSX ; X-Register als Index in den Returnstack benutzen LDA $101,X ; Limit auf den Wert des Schleifenzaehlers I setzen STA $103,X LDA $102,X STA $104,X LDX XSAVE ; Datenstackpointer wiederherstellen JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> >R <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( >R [ n -> ] legt die Zahl n auf den Return-Stack. ) ;( ========================================================================= ) NFA_RPUSH .CBYTE $82, ">R" LFA_RPUSH .WORD NFA_LEAVE CFA_RPUSH .WORD PFA_RPUSH PFA_RPUSH LDA 1,X ; HI-Byte von n PHA ; auf den Returnstack pushen LDA 0,X ; Lo-Byte von n PHA ; auf den Returnstack pushen INX ; n vom Datenstack entfernen INX JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> R> <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( R> [ -> n ] poppt das oberste Element vom Return-Stack und legt es auf ) ;( den Parameterstack. ) ;( ========================================================================= ) NFA_RPOP .CBYTE $82, "R>" LFA_RPOP .WORD NFA_RPUSH CFA_RPOP .WORD PFA_RPOP PFA_RPOP DEX ; Platz auf dem Datenstack fuer n schaffen DEX PLA ; Lo-Byte vom Returnstack poppen STA 0,X ; und in den Datenstack schreiben PLA ; Hi-Byte vom Returnstack poppen STA 1,X ; und in den Datenstack schreiben JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> 0= <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( 0= [ n -> f ] uebergibt f=true, wenn die Zahl n gleich Null ist, sonst ) ;( false. ) ;( ========================================================================= ) NFA_NULLEQUAL .CBYTE $82, "0=" LFA_NULLEQUAL .WORD NFA_RPOP CFA_NULLEQUAL .WORD PFA_NULLEQUAL PFA_NULLEQUAL LDA 0,X ; n testen ORA 1,X STY 1,X ; Hi-Byte von f auf Null setzen (Y=0) BNE L613 ; Lo-Byte von f auf 1 setzen, falls n=0 INY L613 STY 0,X JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> 0< <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( 0< [ n -> f ] uebergibt f=true, wenn die Zahl n kleiner Null ist, sonst ) ;( false. ) ;( ========================================================================= ) NFA_LTNULL .CBYTE $82, "0<" LFA_LTNULL .WORD NFA_NULLEQUAL CFA_LTNULL .WORD PFA_LTNULL PFA_LTNULL ASL 1,X ; hoechstes Bit von n holen TYA ; und im Akkumulator zu 1 oder 0 werden lassen (Y=0) ROL A STY 1,X ; Hi-Byte von f auf Null setzen STA 0,X ; Lo-Byte von f auf obiges Ergebnis setzen JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> + <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( + [ n1 n2 -> n3 ] berechnet die Summe der Zahlen n1 und n2. ) ;( ========================================================================= ) NFA_PLUS .CBYTE $81, "+" LFA_PLUS .WORD NFA_LTNULL CFA_PLUS .WORD PFA_PLUS PFA_PLUS CLC LDA 0,X ; Summe n3.=n1+n2 berechnen und in den Datenstack schreiben ADC 2,X STA 2,X LDA 1,X ADC 3,X STA 3,X INX ; n3 vom Datenstack entfernen INX JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> D+ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( D+ [ d1 d2 -> Summe ] berechnet die doppeltgenaue Summe der beiden ) ;( doppeltgenauen Zahlen d1 und d2. ) ;( ========================================================================= ) NFA_DPLUS .CBYTE $82, "D+" LFA_DPLUS .WORD NFA_PLUS CFA_DPLUS .WORD PFA_DPLUS PFA_DPLUS CLC LDA 2,X ; Lo-Word der Summe berechnen ADC 6,X STA 6,X LDA 3,X ADC 7,X STA 7,X LDA 0,X ; Hi-Word der Summe berechnen ADC 4,X STA 4,X LDA 1,X ADC 5,X STA 5,X JMP POPTWO ; d2 vom Stack entfernen und fertig ; >>>>>>>>>>>>>>>> MINUS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( MINUS [ n1 -> n2 ] negiert die Zahl n1. ) ;( ========================================================================= ) NFA_NEGATE .CBYTE $85, "MINUS" LFA_NEGATE .WORD NFA_DPLUS CFA_NEGATE .WORD PFA_NEGATE PFA_NEGATE SEC MINUS1 TYA SBC 0,X ; Differenz 0-n1 berechnen (Y=0) STA 0,X TYA SBC 1,X STA 1,X JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> DMINUS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DMINUS [ d1 -> d2 ] negiert die doppeltgenaue Zahl d1. ) ;( ========================================================================= ) NFA_DMINUS .CBYTE $86, "DMINUS" LFA_DMINUS .WORD NFA_NEGATE CFA_DMINUS .WORD PFA_DMINUS PFA_DMINUS SEC TYA SBC 2,X ; Lo-Word der Differenz 0-d1 berechnen (Y=0) STA 2,X TYA SBC 3,X STA 3,X JMP MINUS1 ; Hi-Word der Differenz berechnen und fertig ; >>>>>>>>>>>>>>>> OVER <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( OVER [ n1 n2 -> n1 n2 n1 ] legt den Second auf den Stack. ) ;( ========================================================================= ) NFA_OVER .CBYTE $84, "OVER" LFA_OVER .WORD NFA_DMINUS CFA_OVER .WORD PFA_OVER PFA_OVER LDA 2,X ; n1 holen PHA LDA 3,X JMP PUSH ; auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> DROP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DROP [ n -> ] entfernt das oberste Element vom Stack. ) ;( ========================================================================= ) NFA_DROP .CBYTE $84, "DROP" LFA_DROP .WORD NFA_OVER CFA_DROP .WORD POP PFA_DROP ; >>>>>>>>>>>>>>>> SWAP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SWAP [ n1 n2 -> n2 n1 ] vertauscht die beiden obersten Stack-Elemente. ) ;( ========================================================================= ) NFA_SWAP .CBYTE $84, "SWAP" LFA_SWAP .WORD NFA_DROP CFA_SWAP .WORD PFA_SWAP PFA_SWAP LDA 2,X ; Lo-Byte von n1 holen PHA ; und merken LDA 0,X ; Lo-Byte von n2 holen STA 2,X ; und damit das Lo-Byte von n1 ueberschreiben LDA 3,X ; Hi-Byte von n1 holen und im Akkumulator merken LDY 1,X ; Hi-Byte von n2 holen STY 3,X ; und damit das Hi-Byte von n1 ueberschreiben JMP PUT ; gemerktes n1 in den Datenstack schreiben und fertig ; >>>>>>>>>>>>>>>> PICK <<<<<<<<<<<<<<< ;( ========================================================================= ) ;( PICK [ n1 -> n2 ] holt das n-te Element des Stacks ) ;( ========================================================================= ) NFA_PICK .CBYTE $84, "PICK" LFA_PICK .WORD NFA_SWAP CFA_PICK .WORD PFA_PICK PFA_PICK STX XSAVE ; Stackzeiger sichern TXA ; Stackzeiger in den Accu SEC ADC 0,X ; Lo-Byte von n1 holen, ADC 0,X ; verdoppeln und zum Stackzeiger addieren TAX DEX ; in den Stackzeiger schieben, zeigt nun auf Element n1 LDA 0,X ; Lo-Byte von n2 holen PHA ; und merken LDA 1,X ; Hi-Byte von n2 holen LDX XSAVE JMP PUT ; gemerktes n2 in den Datenstack schreiben und fertig ; >>>>>>>>>>>>>>>> DUP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DUP [ n -> n n ] dupliziert das oberste Stack-Element. ) ;( ========================================================================= ) NFA_DUP .CBYTE $83, "DUP" LFA_DUP .WORD NFA_PICK CFA_DUP .WORD PFA_DUP PFA_DUP LDA 0,X ; n holen PHA LDA 1,X JMP PUSH ; auf den Datenstack pushen und fertig ; >>>>>>>>>>>>>>>> +! <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( +! [ n addr -> ] inkrementiert die Zahl, auf die die Adresse addr zeigt, ) ;( um n. ) ;( ========================================================================= ) NFA_PLUSSTORE .CBYTE $82,"+!" LFA_PLUSSTORE .WORD NFA_DUP CFA_PLUSSTORE .WORD PFA_PLUSSTORE PFA_PLUSSTORE CLC LDA (0,X) ; Lo-Byte der durch addr adressierten Zahl ADC 2,X ; plus Lo-Byte von n STA (0,X) ; an der Adresse addr ablegen INC 0,X ; addr inkrementieren BNE L754 INC 1,X L754 LDA (0,X) ; Hi-Byte der durch addr adressierten Zahl ADC 3,X ; plus Hi-Byte von n STA (0,X) ; an der Adresse addr+1 ablegen JMP POPTWO ; n und addr vom Datenstack entfernen und fertig ; >>>>>>>>>>>>>>>> TOGGLE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( TOGGLE [ addr b -> ] fuehrt eine bitweise Exklusiv-Oder-Verknuepfung des ) ;( durch addr adressierten Bytes mit dem Bitmuster b durch. ) ;( ========================================================================= ) NFA_TOGGLE .CBYTE $86, "TOGGLE" LFA_TOGGLE .WORD NFA_PLUSSTORE CFA_TOGGLE .WORD PFA_TOGGLE PFA_TOGGLE LDA (2,X) ; durch addr adressiertes Byte holen EOR 0,X ; mit b XOR-verknuepfen STA (2,X) ; und wieder im Speicher ablegen JMP POPTWO ; addr und b vom Datenstack entfernen und fertig ; >>>>>>>>>>>>>>>> @ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( @ [ addr -> n ] ermittelt die durch addr adressierte Zahl. ) ;( ========================================================================= ) NFA_FETCH .CBYTE $81, "@" LFA_FETCH .WORD NFA_TOGGLE CFA_FETCH .WORD PFA_FETCH PFA_FETCH LDA (0,X) ; Lo-Byte der durch addr adressierten Zahl holen PHA ; und merken INC 0,X ; addr inkrementieren BNE L781 INC 1,X L781 LDA (0,X) ; Hi-Byte holen und im Akkumulator merken JMP PUT ; gemerkte Zahl in den Datenstack schreiben und fertig ; >>>>>>>>>>>>>>>> C@ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( C@ [ addr -> b ] ermittelt das durch addr adressierte Byte. ) ;( ========================================================================= ) NFA_CFETCH .CBYTE $82, "C@" LFA_CFETCH .WORD NFA_FETCH CFA_CFETCH .WORD PFA_CFETCH PFA_CFETCH LDA (0,X) ; durch addr adressiertes Byte holen STA 0,X ; und in den Datenstack schreiben STY 1,X ; Hi-Byte auf Null setzen (Y=0) JMP NEXT ; und fertig ; >>>>>>>>>>>>>>>> ! <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ! [ n addr -> ] speichert die Zahl n an die Adresse addr. ) ;( ========================================================================= ) NFA_STORE .CBYTE $81, "!" LFA_STORE .WORD NFA_CFETCH CFA_STORE .WORD PFA_STORE PFA_STORE LDA 2,X ; Lo-Byte von n holen STA (0,X) ; und bei addr abspeichen INC 0,X ; addr inkrementieren BNE L806 INC 1,X L806 LDA 3,X ; Hi-Byte von n holen STA (0,X) ; und bei addr+1 abspeichern JMP POPTWO ; n und addr vom Datenstack entfernen ; >>>>>>>>>>>>>>>> C! <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( C! [ b addr -> ] speichert das Byte b an die Adresse addr. ) ;( ========================================================================= ) NFA_CSTORE .CBYTE $82, "C!" LFA_CSTORE .WORD NFA_STORE CFA_CSTORE .WORD PFA_CSTORE PFA_CSTORE LDA 2,X ; Byte b holen STA (0,X) ; und bei addr abspeichern JMP POPTWO ; b und addr vom Datenstack entfernen und fertig ; >>>>>>>>>>>>>>>> : <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( : [ ] leitet eine Colon-Definition ein. ) ;( ========================================================================= ) NFA_COLON .BYTE $81+$40, 186 ; IMMEDIATE LFA_COLON .WORD NFA_CSTORE CFA_COLON .WORD DOCOL PFA_COLON .WORD CFA_QUERYEXEC ; ?EXEC .WORD CFA_STORECSP ; !CSP .WORD CFA_CURRENT ; CURRENT .WORD CFA_FETCH ; @ .WORD CFA_CONTEXT ; CONTEXT .WORD CFA_STORE ; ! .WORD CFA_CREATE ; CREATE .WORD CFA_RIGHTBRACKET ; ] .WORD CFA_BRACKETCODE ; (;CODE) ; das neue Wort fuehrt zunaechst folgenden Code aus: DOCOL LDA IP+1 ; Instruction Pointer IP auf den Returnstack pushen PHA LDA IP PHA .IF debug PHA LDA DEBUGFLG BEQ DL2 JSR TCOLON ; DEBUG DL2 PLA .ENDIF CLC LDA W ; Wortadressregister ADC #2 ; plus zwei ergibt Adresse des ersten Tokens STA IP ; dort mit der Interpretation fortfahren TYA ADC W+1 ; auch Hi-Byte des IP beruecksichtigen (Y=0) STA IP+1 JMP NEXT ; Interpretation an neuer Stelle fortsetzen ; >>>>>>>>>>>>>>>> ; <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ; [ ] schliesst eine Colon-Definition ab. ) ;( ========================================================================= ) ; : ; ; ?CSP ( ueberpruefen, ob die Stackposition noch die alte ist ) ; COMPILE ;S ( Semis an das Ende des Wortes kompilieren ) ; SMUDGE ( Wort gueltig machen ) ; [COMPILE] [ ( in den Execute-Mode uebergehen ) ; ; IMMEDIATE NFA_SEMIS .BYTE $81+$40, 187 ; IMMEDIATE LFA_SEMIS .WORD NFA_COLON CFA_SEMIS .WORD DOCOL PFA_SEMIS .WORD CFA_QUERYCSP ; ?CSP .WORD CFA_COMPILE ; COMPILE .WORD CFA_EXIT ; ;S .WORD CFA_SMUDGE ; SMUDGE .WORD CFA_LEFTBRACKET ; [ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> CONSTANT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CONSTANT [ n -> ] definiert eine Konstante. ) ;( ========================================================================= ) NFA_CONSTANT .CBYTE $88, "CONSTANT" LFA_CONSTANT .WORD NFA_SEMIS CFA_CONSTANT .WORD DOCOL PFA_CONSTANT .WORD CFA_CREATE ; CREATE .WORD CFA_SMUDGE ; SMUDGE .WORD CFA_COMMA ; , .WORD CFA_BRACKETCODE ; (;CODE) ; das neue Wort fuehrt den folgenden Code aus: DOCON LDY #2 LDA (W),Y ; Lo-Byte des Wertes n im Parameterfeld holen PHA ; und merken INY ; Hi-Byte von n anvisieren LDA (W),Y ; und holen JMP PUSH ; n auf den Datenstack pushen und fertig ; >>>>>>>>>>>>>>>> VARIABLE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( VARIABLE [ n -> ] definiert und initialisiert eine Variable. ) ;( ========================================================================= ) NFA_VARIABLE .CBYTE $88,"VARIABLE" LFA_VARIABLE .WORD NFA_CONSTANT CFA_VARIABLE .WORD DOCOL PFA_VARIABLE .WORD CFA_CONSTANT ; CONSTANT .WORD CFA_BRACKETCODE ; (;CODE) ; aber bei Aufruf den folgenden Code ausfuehren: DOVAR CLC LDA W ; Lo-Byte der PFA der Variablen ermitteln ADC #2 PHA ; und merken TYA ADC W+1 ; Hi-Byte der PFA ermitteln und merken JMP PUSH ; PFA auf den Datenstack pushen und fertig ; >>>>>>>>>>>>>>>> USER <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( USER [ n -> ] definiert eine User-Variable mit dem Offset n. ) ;( ========================================================================= ) NFA_USER .CBYTE $84, "USER" LFA_USER .WORD NFA_VARIABLE CFA_USER .WORD DOCOL PFA_USER .WORD CFA_CONSTANT ; CONSTANT .WORD CFA_BRACKETCODE ; (;CODE) ; aber bei Aufruf den folgenden Code ausfuehren: DOUSE LDY #2 CLC LDA (W),Y ; Lo-Byte des Offsets der Uservariablen ADC UP ; plus Userpointer ergibt Adresse der Uservariablen PHA ; merken LDA #0 ADC UP+1 ; Hi-Byte holen und im Akkumulator merken JMP PUSH ; gemerkte Adresse auf den Datenstack legen und fertig NFA_PREV .= NFA_USER ;( ========================================================================= ) ;( Definition diverser Konstanten. ) ;( ========================================================================= ) ; >>>>>>>>>>>>>>>> 0 <<<<<<<<<<<<<<<< ; CONSTANT 0 ( Null ) NFA_0 .CBYTE $81, "0" LFA_0 .WORD NFA_PREV CFA_0 .WORD DOCON PFA_0 .WORD 0 ; >>>>>>>>>>>>>>>> 1 <<<<<<<<<<<<<<<< ; CONSTANT 1 ( Eins ) NFA_1 .CBYTE $81, "1" LFA_1 .WORD NFA_0 CFA_1 .WORD DOCON PFA_1 .WORD 1 ; >>>>>>>>>>>>>>>> 2 <<<<<<<<<<<<<<<< ; CONSTANT 2 ( Zwei ) NFA_2 .CBYTE $81,"2" LFA_2 .WORD NFA_1 CFA_2 .WORD DOCON PFA_2 .WORD 2 ; >>>>>>>>>>>>>>>> 3 <<<<<<<<<<<<<<<< ; CONSTANT 3 ( Drei ) NFA_3 .CBYTE $81, "3" LFA_3 .WORD NFA_2 CFA_3 .WORD DOCON PFA_3 .WORD 3 ; >>>>>>>>>>>>>>>> BL <<<<<<<<<<<<<<<< ; CONSTANT BL ( Blank ) NFA_BL .CBYTE $82, "BL" LFA_BL .WORD NFA_3 CFA_BL .WORD DOCON PFA_BL .WORD 32 ; >>>>>>>>>>>>>>>> ABORTINIT <<<<<<<<<<<<<<<< ; CONSTANT ABORTINIT ( ABORT INIT ADRESSE ) NFA_ABORTINIT .CBYTE $89, "ABORTINIT" LFA_ABORTINIT .WORD NFA_BL CFA_ABORTINIT .WORD DOCON PFA_ABORTINIT .WORD ABORTINIT NFA_PREV .= NFA_ABORTINIT .IF debug ;( ========================================================================= ) ;( Definition Debug Flag. ) ;( ========================================================================= ) ; >>>>>>>>>>>>>>>> DEBUGFLAG <<<<<<<<<<<<<<<< ; CONSTANT 0 ( Null ) NFA_DEBUGFLAG .CBYTE $89, "DEBUGFLAG" LFA_DEBUGFLAG .WORD NFA_PREV CFA_DEBUGFLAG .WORD DOCON PFA_DEBUGFLAG .WORD DEBUGFLG NFA_PREV .= NFA_DEBUGFLAG ; >>>>>>>>>>>>>>>> DEBUGON <<<<<<<<<<<<<<<< NFA_DEBUGON .CBYTE $87, "DEBUGON" LFA_DEBUGON .WORD NFA_PREV CFA_DEBUGON .WORD DOCOL PFA_DEBUGON .WORD CFA_CLIT .BYTE 1 ; 1 .WORD CFA_DEBUGFLAG ; DEBUGFLAG .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S NFA_PREV .= NFA_DEBUGON ; >>>>>>>>>>>>>>>> DEBUGOFF <<<<<<<<<<<<<<<< NFA_DEBUGOFF .CBYTE $88, "DEBUGOFF" LFA_DEBUGOFF .WORD NFA_PREV CFA_DEBUGOFF .WORD DOCOL PFA_DEBUGOFF .WORD CFA_CLIT .BYTE 0 ; 0 .WORD CFA_DEBUGFLAG ; DEBUGFLAG .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S NFA_PREV .= NFA_DEBUGOFF .ENDIF ; DEBUG ; >>>>>>>>>>>>>>>> +ORIGIN <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( +ORIGIN [ n -> addr ] uebergibt die Adresse Origin+n. Origin ist die ) ;( tiefste Adresse im Forth-Kern. ) ;( ========================================================================= ) NFA_PLUSORIGIN .CBYTE $87, "+ORIGIN" LFA_PLUSORIGIN .WORD NFA_PREV CFA_PLUSORIGIN .WORD DOCOL PFA_PLUSORIGIN .WORD CFA_LIT ; LIT .WORD ORIG .WORD CFA_PLUS ; + .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> S0 <<<<<<<<<<<<<<<< ; 6 USER S0 ( Initialisierungswert fuer Daten-Stack-Pointer ) NFA_S0 .CBYTE $82, "S0" LFA_S0 .WORD NFA_PLUSORIGIN CFA_S0 .WORD DOUSE PFA_S0 .WORD 6 ; >>>>>>>>>>>>>>>> R0 <<<<<<<<<<<<<<<< ; 8 USER R0 ( Initialisierungswert fuer Return-Stack-Pointer ) NFA_R0 .CBYTE $82, "R0" LFA_R0 .WORD NFA_S0 CFA_R0 .WORD DOUSE PFA_R0 .WORD 8 ; >>>>>>>>>>>>>>>> TIB <<<<<<<<<<<<<<<< ; 10 USER TIB ( Startadresse des Terminal-Input-Buffers ) NFA_TIB .CBYTE $83, "TIB" LFA_TIB .WORD NFA_R0 CFA_TIB .WORD DOUSE PFA_TIB .WORD 10 ; >>>>>>>>>>>>>>>> WIDTH <<<<<<<<<<<<<<<< ; 12 USER WIDTH ( Maximallaenge von Forth-Namen ) NFA_WIDTH .CBYTE $85, "WIDTH" LFA_WIDTH .WORD NFA_TIB CFA_WIDTH .WORD DOUSE PFA_WIDTH .WORD 12 ; >>>>>>>>>>>>>>>> WARNING <<<<<<<<<<<<<<<< ; 14 USER WARNING ( Kontrollvariable fuer Systembotschaften ) NFA_WARNING .CBYTE $87, "WARNING" LFA_WARNING .WORD NFA_WIDTH CFA_WARNING .WORD DOUSE PFA_WARNING .WORD 14 ; >>>>>>>>>>>>>>>> FENCE <<<<<<<<<<<<<<<< ; 16 USER FENCE ( unterste Adresse des mit FORGET loeschbaren Bereichs ) NFA_FENCE .CBYTE $85, "FENCE" LFA_FENCE .WORD NFA_WARNING CFA_FENCE .WORD DOUSE PFA_FENCE .WORD 16 ; >>>>>>>>>>>>>>>> DP <<<<<<<<<<<<<<<< ; 18 USER DP ( Dictionary Pointer ) NFA_DP .CBYTE $82, "DP" LFA_DP .WORD NFA_FENCE CFA_DP .WORD DOUSE PFA_DP .WORD 18 ; >>>>>>>>>>>>>>>> VOC-LINK <<<<<<<<<<<<<<<< ; 20 USER VOC-LINK ( Adresse einer Zelle in der Definition des letzten Vokab. ) NFA_VOCLINK .CBYTE $88, "VOC-LINK" LFA_VOCLINK .WORD NFA_DP CFA_VOCLINK .WORD DOUSE PFA_VOCLINK .WORD 20 ; >>>>>>>>>>>>>>>> SOURCE-ID <<<<<<<<<<<<<<<< ; 22 USER SOURCE-ID ( Nummer des geoffneten Kanals ) NFA_SOURCEID .CBYTE $89,"SOURCE-ID" LFA_SOURCEID .WORD NFA_VOCLINK CFA_SOURCEID .WORD DOUSE PFA_SOURCEID .WORD 22 ; >>>>>>>>>>>>>>>> IN <<<<<<<<<<<<<<<< ; 24 USER IN ( Byte-Offset im gegenwaertigen Eingabe-Textbuffer ) NFA_IN .CBYTE $82, "IN" LFA_IN .WORD NFA_SOURCEID CFA_IN .WORD DOUSE PFA_IN .WORD 24 ; >>>>>>>>>>>>>>>> OUT <<<<<<<<<<<<<<<< ; 26 USER OUT ( Anzahl mittels EMIT ausgegebener Zeichen ) NFA_OUT .CBYTE $83, "OUT" LFA_OUT .WORD NFA_IN CFA_OUT .WORD DOUSE PFA_OUT .WORD 26 ; >>>>>>>>>>>>>>>> SCR <<<<<<<<<<<<<<<< ; 28 USER SCR ( Nummer des zuletzt aufgerufenen Screens ) NFA_SCR .CBYTE $83, "SCR" LFA_SCR .WORD NFA_OUT CFA_SCR .WORD DOUSE PFA_SCR .WORD 28 ; >>>>>>>>>>>>>>>> OFFSET <<<<<<<<<<<<<<<< ; 30 USER OFFSET ( Block-Offset zum Disk-Drive, auf das zugegriffen wird ) NFA_OFFSET .CBYTE $86, "OFFSET" LFA_OFFSET .WORD NFA_SCR CFA_OFFSET .WORD DOUSE PFA_OFFSET .WORD 30 ; >>>>>>>>>>>>>>>> CONTEXT <<<<<<<<<<<<<<<< ; 32 USER CONTEXT ( Pointer zum Context-Vokabular ) NFA_CONTEXT .CBYTE $87, "CONTEXT" LFA_CONTEXT .WORD NFA_OFFSET CFA_CONTEXT .WORD DOUSE PFA_CONTEXT .WORD 32 ; >>>>>>>>>>>>>>>> CURRENT <<<<<<<<<<<<<<<< ; 34 USER CURRENT ( Pointer zum Current-Vokabular ) NFA_CURRENT .CBYTE $87,"CURRENT" LFA_CURRENT .WORD NFA_CONTEXT CFA_CURRENT .WORD DOUSE PFA_CURRENT .WORD 34 ; >>>>>>>>>>>>>>>> STATE <<<<<<<<<<<<<<<< ; 36 USER STATE ( ungleich Null, wenn System im Compile-Mode ) NFA_STATE .CBYTE $85, "STATE" LFA_STATE .WORD NFA_CURRENT CFA_STATE .WORD DOUSE PFA_STATE .WORD 36 ; >>>>>>>>>>>>>>>> BASE <<<<<<<<<<<<<<<< ; 38 USER BASE ( gegenwaertig vereinbarte Zahlenbasis ) NFA_BASE .CBYTE $84, "BASE" LFA_BASE .WORD NFA_STATE CFA_BASE .WORD DOUSE PFA_BASE .WORD 38 ; >>>>>>>>>>>>>>>> DPL <<<<<<<<<<<<<<<< ; 40 USER DPL ( Anzahl der Digits rechts vom Dezimalpunkt ) NFA_DPL .CBYTE $83, "DPL" LFA_DPL .WORD NFA_BASE CFA_DPL .WORD DOUSE PFA_DPL .WORD 40 ; >>>>>>>>>>>>>>>> FLD <<<<<<<<<<<<<<<< ; 42 USER FLD ( Kontrollvariable fuer formatierte I/O ) NFA_FLD .CBYTE $83, "FLD" LFA_FLD .WORD NFA_DPL CFA_FLD .WORD DOUSE PFA_FLD .WORD 42 ; >>>>>>>>>>>>>>>> CSP <<<<<<<<<<<<<<<< ; 44 USER CSP ( gesicherter Stackpointer-Stand ) NFA_CSP .CBYTE $83, "CSP" LFA_CSP .WORD NFA_FLD CFA_CSP .WORD DOUSE PFA_CSP .WORD 44 ; >>>>>>>>>>>>>>>> R# <<<<<<<<<<<<<<<< ; 46 USER R# ( Position des Editor-Cursors ) NFA_RNUM .CBYTE $82, "R#" LFA_RNUM .WORD NFA_CSP CFA_RNUM .WORD DOUSE PFA_RNUM .WORD 46 ; >>>>>>>>>>>>>>>> HLD <<<<<<<<<<<<<<<< ; 48 USER HLD ( Adresse des letzten Zeichens bei Umwandlung Zahl->String ) NFA_HLD .CBYTE $83, "HLD" LFA_HLD .WORD NFA_RNUM CFA_HLD .WORD DOUSE PFA_HLD .WORD 48 ; >>>>>>>>>>>>>>>> 1+ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( 1+ [ n1 -> n2 ] inkrementiert die oberste Zahl auf dem Stack. ) ;( ========================================================================= ) ; ; : 1+ 1 + ; NFA_1PLUS .CBYTE $82,"1+" LFA_1PLUS .WORD NFA_HLD CFA_1PLUS .WORD DOCOL PFA_1PLUS .WORD CFA_1 ; 1 .WORD CFA_PLUS ; + .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> 2+ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( 2+ [ n1 -> n2 ] inkrementiert die oberste Zahl auf dem Stack um 2. ) ;( ========================================================================= ) ; ; : 2+ 2 + ; NFA_2PLUS .CBYTE $82,"2+" LFA_2PLUS .WORD NFA_1PLUS CFA_2PLUS .WORD DOCOL PFA_2PLUS .WORD CFA_2 ; 2 .WORD CFA_PLUS ; + .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> HERE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( HERE [ -> addr ] uebergibt die Adresse der ersten freien Stelle im ) ;( Dictionary. ) ;( ========================================================================= ) ; ; : HERE DP @ ; ( Adresse, auf die der Dictionary Pointer zeigt, zurueckgeben ) NFA_HERE .CBYTE $84,"HERE" LFA_HERE .WORD NFA_2PLUS CFA_HERE .WORD DOCOL PFA_HERE .WORD CFA_DP ; DP .WORD CFA_FETCH ; @ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ALLOT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ALLOT [ n -> ] vergroessert das Dictionary um n Bytes. Hierbei ist n eine ) ;( vorzeichenbehaftete Zahl, kann also auch negativ sein. ) ;( ========================================================================= ) ; ; : ALLOT DP +! ; ( Dictionary-Pointer um n inkrementieren ) NFA_ALLOT .CBYTE $85,"ALLOT" LFA_ALLOT .WORD NFA_HERE CFA_ALLOT .WORD DOCOL PFA_ALLOT .WORD CFA_DP ; DP .WORD CFA_PLUSSTORE ; +! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> , <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( , [ n -> ] fuegt die Zahl n an das Dictionary an. ) ;( ========================================================================= ) ; ; : , HERE ! 2 ALLOT ; ( n hinter das Dictionary schreiben und DP erhoehen ) NFA_COMMA .CBYTE $81,"," LFA_COMMA .WORD NFA_ALLOT CFA_COMMA .WORD DOCOL PFA_COMMA .WORD CFA_HERE ; HERE .WORD CFA_STORE ; ! .WORD CFA_2 ; 2 .WORD CFA_ALLOT ; ALLOT .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> C, <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( C, [ b -> ] fuegt das Byte b an das Dictionary an. ) ;( ========================================================================= ) ; ; : C, HERE C! 1 ALLOT ; ( b hinter das Dictionary schreiben und DP erhoehen ) NFA_CCOMMA .CBYTE $82,"C," LFA_CCOMMA .WORD NFA_COMMA CFA_CCOMMA .WORD DOCOL PFA_CCOMMA .WORD CFA_HERE ; HERE .WORD CFA_CSTORE ; C! .WORD CFA_1 ; 1 .WORD CFA_ALLOT ; ALLOT .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> - <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( - [ n1 n2 -> n3 ] berechnet die Differenz n1-n2. ) ;( ========================================================================= ) ; ; : - MINUS + ; ( n2 negieren und auf n1 aufaddieren ) NFA_MINUS .CBYTE $81,"-" LFA_MINUS .WORD NFA_CCOMMA CFA_MINUS .WORD DOCOL PFA_MINUS .WORD CFA_NEGATE ; MINUS .WORD CFA_PLUS ; + .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> = <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( = [ n1 n2 -> f ] vergleicht die Zahlen n1 und n2 und gibt true zurueck, ) ;( wenn sie gleich sind, sonst false. ) ;( ========================================================================= ) ; ; : = - 0= ; ( Differenz von n1 und n2 auf Gleichheit mit 0 testen ) NFA_EQUAL .CBYTE $81,"=" LFA_EQUAL .WORD NFA_MINUS CFA_EQUAL .WORD DOCOL PFA_EQUAL .WORD CFA_MINUS ; - .WORD CFA_NULLEQUAL ; 0= .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> U< <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( U< [ u1 u2 -> f ] liefert f=true, wenn u1>>>>>>>>>>>>>>> < <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( < [ n1 n2 -> f ] liefert f=true, wenn n1>>>>>>>>>>>>>>> > <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( > [ n1 n2 -> f ] liefert f=true, wenn n1>n2 ist. ) ;( ========================================================================= ) ; ; : > SWAP < ; ( n1>n2 gdw n2" LFA_GT .WORD NFA_LT CFA_GT .WORD DOCOL PFA_GT .WORD CFA_SWAP ; SWAP .WORD CFA_LT ; < .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ROT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ROT [ n1 n2 n3 -> n2 n3 n1 ] rotiert die drei obersten Stackelemente. ) ;( ========================================================================= ) ; ; : ROT >R SWAP R> SWAP ; ( n1 mit Hilfe des Returnstacks nach vorne holen ) NFA_ROT .CBYTE $83,"ROT" LFA_ROT .WORD NFA_GT CFA_ROT .WORD DOCOL PFA_ROT .WORD CFA_RPUSH ; >R .WORD CFA_SWAP ; SWAP .WORD CFA_RPOP ; R> .WORD CFA_SWAP ; SWAP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> SPACE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SPACE [ ] sendet ein ASCII-Space zum Ausgabegeraet. ) ;( ========================================================================= ) ; ; : SPACE BL EMIT ; NFA_SPACE .CBYTE $85,"SPACE" LFA_SPACE .WORD NFA_ROT CFA_SPACE .WORD DOCOL PFA_SPACE .WORD CFA_BL ; BL .WORD CFA_EMIT ; EMIT .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> -DUP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( -DUP [ n1 -> n1 ] falls n1=0, [ n1 -> n1 n1 ] sonst. Dupliziert den TOS, ) ;( wenn er ungleich Null ist. ) ;( ========================================================================= ) ; ; : -DUP DUP IF DUP ENDIF ; NFA_MINUSDUP .CBYTE $84,"-DUP" LFA_MINUSDUP .WORD NFA_SPACE CFA_MINUSDUP .WORD DOCOL PFA_MINUSDUP .WORD CFA_DUP ; DUP .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL1-* .WORD CFA_DUP ; DUP LBL1 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> TRAVERSE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( TRAVERSE [ addr1 n -> addr2 ] gibt mit addr2 die Adresse des jeweils ) ;( anderen Ende eines Namens an, Laengenbyte inklusive. Ist n=1, so wird ) ;( aufsteigend gesucht, ist n=-1, so wird absteigend gesucht. ) ;( ========================================================================= ) ; ; : TRAVERSE ; SWAP ; BEGIN OVER + 127 OVER C@ < UNTIL ( solange suchen bis Byte>127 ) ; SWAP DROP ; ; NFA_TRAVERSE .CBYTE $88,"TRAVERSE" LFA_TRAVERSE .WORD NFA_MINUSDUP CFA_TRAVERSE .WORD DOCOL PFA_TRAVERSE .WORD CFA_SWAP ; SWAP LBL2 .WORD CFA_OVER ; OVER .WORD CFA_PLUS ; + .WORD CFA_CLIT ; CLIT .BYTE 127 .WORD CFA_OVER ; OVER .WORD CFA_CFETCH ; C@ .WORD CFA_LT ; < .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL2-* .WORD CFA_SWAP ; SWAP .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> LATEST <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( LATEST [ -> addr ] uebergibt die NFA des zuletzt im Current-Vokabular ) ;( definierten Wortes. ) ;( ========================================================================= ) ; ; : LATEST CURRENT @ @ ; NFA_LATEST .CBYTE $86,"LATEST" LFA_LATEST .WORD NFA_TRAVERSE CFA_LATEST .WORD DOCOL PFA_LATEST .WORD CFA_CURRENT ; CURRENT .WORD CFA_FETCH ; @ .WORD CFA_FETCH ; @ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> LFA <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( LFA [ pfa -> lfa ] wandelt die PFA eines Wortes in die entsprechende LFA ) ;( um. ) ;( ========================================================================= ) ; ; : LFA 4 - ; NFA_LFA .CBYTE $83,"LFA" LFA_LFA .WORD NFA_LATEST CFA_LFA .WORD DOCOL PFA_LFA .WORD CFA_CLIT ; CLIT .BYTE 4 .WORD CFA_MINUS ; - .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> CFA <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CFA [ pfa -> cfa ] wandelt die PFA eines Wortes in die entsprechende CFA ) ;( um. ) ;( ========================================================================= ) ; ; : CFA 2 - ; NFA_CFA .CBYTE $83,"CFA" LFA_CFA .WORD NFA_LFA CFA_CFA .WORD DOCOL PFA_CFA .WORD CFA_2 ; 2 .WORD CFA_MINUS ; - .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> NFA <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( NFA [ pfa -> nfa ] wandelt die PFA eines Wortes in die entsprechende NFA ) ;( um. ) ;( ========================================================================= ) ; ; : NFA 5 - -1 TRAVERSE ; NFA_NFA .CBYTE $83,"NFA" LFA_NFA .WORD NFA_CFA CFA_NFA .WORD DOCOL PFA_NFA .WORD CFA_CLIT ; CLIT .BYTE 5 .WORD CFA_MINUS ; - .WORD CFA_LIT ; LIT .WORD -1 .WORD CFA_TRAVERSE ; TRAVERSE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> PFA <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( PFA [ nfa -> pfa ] wandelt die NFA eines Wortes in die entsprechende PFA ) ;( um. ) ;( ========================================================================= ) ; ; : PFA 1 TRAVERSE 5 + ; NFA_PFA .CBYTE $83,"PFA" LFA_PFA .WORD NFA_NFA CFA_PFA .WORD DOCOL PFA_PFA .WORD CFA_1 ; 1 .WORD CFA_TRAVERSE ; TRAVERSE .WORD CFA_CLIT ; CLIT .BYTE 5 .WORD CFA_PLUS ; + .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> !CSP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( !CSP [ ] rettet die aktuelle Stackpointer-Position in die User-Variable ) ;( CSP. ) ;( ========================================================================= ) ; ; : !CSP SP@ CSP ! ; NFA_STORECSP .CBYTE $84,"!CSP" LFA_STORECSP .WORD NFA_PFA CFA_STORECSP .WORD DOCOL PFA_STORECSP .WORD CFA_SPFETCH ; SP@ .WORD CFA_CSP ; CSP .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?ERROR <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?ERROR [ f n -> ] gibt die Fehlermeldung Nummer n aus, wenn f=true ist. ) ;( ========================================================================= ) ; ; : ?ERROR SWAP IF ERROR ELSE DROP ENDIF ; NFA_QUERYERROR .CBYTE $86,"?ERROR" LFA_QUERYERROR .WORD NFA_STORECSP CFA_QUERYERROR .WORD DOCOL PFA_QUERYERROR .WORD CFA_SWAP ; SWAP .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL3-* .WORD CFA_ERROR ; ERROR .WORD CFA_BRANCH ; BRANCH .WORD LBL4-* LBL3 .WORD CFA_DROP ; DROP LBL4 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?COMP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?COMP [ ] gibt eine entsprechende Fehlermeldung aus, wenn sich das System ) ;( nicht im Compile-Mode befindet. ) ;( ========================================================================= ) ; ; : ?COMP STATE @ 0= 17 ?ERROR ; NFA_QUERYCOMP .CBYTE $85,"?COMP" LFA_QUERYCOMP .WORD NFA_QUERYERROR CFA_QUERYCOMP .WORD DOCOL PFA_QUERYCOMP .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_NULLEQUAL ; 0= .WORD CFA_CLIT ; CLIT .BYTE 17 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?EXEC <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?EXEC [ ] gibt eine entsprechende Fehlermeldung aus, wenn sich das System ) ;( nicht im Execute-Mode befindet. ) ;( ========================================================================= ) ; ; : ?EXEC STATE @ 18 ?ERROR ; NFA_QUERYEXEC .CBYTE $85,"?EXEC" LFA_QUERYEXEC .WORD NFA_QUERYCOMP CFA_QUERYEXEC .WORD DOCOL PFA_QUERYEXEC .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_CLIT ; CLIT .BYTE 18 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?PAIRS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?PAIRS [ n1 n2 -> ] gibt eine entsprechende Fehlermeldung aus, wenn n1 ) ;( und n2 ungleich sind. Das Wort wird waehrend der Kompilation benutzt, um ) ;( festzustellen, ob zwei strukturierte Sprachelemente zusammengehoeren. ) ;( ========================================================================= ) ; ; : ?PAIRS - 19 ?ERROR ; NFA_QUERYPAIRS .CBYTE $86,"?PAIRS" LFA_QUERYPAIRS .WORD NFA_QUERYEXEC CFA_QUERYPAIRS .WORD DOCOL PFA_QUERYPAIRS .WORD CFA_MINUS ; - .WORD CFA_CLIT ; CLIT .BYTE 19 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?CSP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?CSP [ ] gibt eine entsprechende Fehlermeldung aus, wenn die aktuelle ) ;( Stack-Position von der in der User-Variablen CSP geretteten abweicht. ) ;( ========================================================================= ) ; ; : ?CSP SP@ CSP @ - 20 ?ERROR ; NFA_QUERYCSP .CBYTE $84,"?CSP" LFA_QUERYCSP .WORD NFA_QUERYPAIRS CFA_QUERYCSP .WORD DOCOL PFA_QUERYCSP .WORD CFA_SPFETCH ; SP@ .WORD CFA_CSP ; CSP .WORD CFA_FETCH ; @ .WORD CFA_MINUS ; - .WORD CFA_CLIT ; CLIT .BYTE 20 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?LOADING <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?LOADING [ ] gibt eine entsprechende Fehlermeldung aus, falls nicht ) ;( geladen wird. ) ;( ========================================================================= ) ; ; : ?LOADING SOURCE-ID @ 0= 22 ?ERROR ; NFA_QUERYLOADING .CBYTE $88,"?LOADING" LFA_QUERYLOADING .WORD NFA_QUERYCSP CFA_QUERYLOADING .WORD DOCOL PFA_QUERYLOADING .WORD CFA_SOURCEID ; SOURCE-ID .WORD CFA_FETCH ; @ .WORD CFA_NULLEQUAL ; 0= .WORD CFA_CLIT ; CLIT .BYTE 22 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> COMPILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( COMPILE [ ] kompiliert das inline folgende Wort in das Dictionary. ) ;( ========================================================================= ) ; ; : COMPILE ; ?COMP ( nur im Compile-Mode erlaubt ) ; R> ( Fortsetzungsadresse holen ) ; DUP 2+ >R ( inline folgendes Wort in der Ausfuehrung ueberspringen ) ; @ , ( inline folgendes Wort an das Dictionary anfuegen ) ; ; NFA_COMPILE .CBYTE $87,"COMPILE" LFA_COMPILE .WORD NFA_QUERYLOADING CFA_COMPILE .WORD DOCOL PFA_COMPILE .WORD CFA_QUERYCOMP ; ?COMP .WORD CFA_RPOP ; R> .WORD CFA_DUP ; DUP .WORD CFA_2PLUS ; 2+ .WORD CFA_RPUSH ; >R .WORD CFA_FETCH ; @ .WORD CFA_COMMA ; , .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> [ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [ [ ] schaltet in den Execute-Mode um. ) ;( ========================================================================= ) ; ; : [ 0 STATE ! ; IMMEDIATE NFA_LEFTBRACKET .CBYTE $81+$40, "[" ; IMMEDIATE LFA_LEFTBRACKET .WORD NFA_COMPILE CFA_LEFTBRACKET .WORD DOCOL PFA_LEFTBRACKET .WORD CFA_0 ; 0 .WORD CFA_STATE ; STATE .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ] <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ] [ ] schaltet in den Compile-Mode um. ) ;( ========================================================================= ) ; ; : ] 192 STATE ! ; NFA_RIGHTBRACKET .CBYTE $81,"]" LFA_RIGHTBRACKET .WORD NFA_LEFTBRACKET CFA_RIGHTBRACKET .WORD DOCOL PFA_RIGHTBRACKET .WORD CFA_CLIT ; CLIT .BYTE 192 .WORD CFA_STATE ; STATE .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> SMUDGE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SMUDGE [ ] dreht das SMUDGE-Bit im Header des zuletzt definierten Wortes ) ;( um, d.h. macht das Wort gueltig, wenn es vorher ungueltig war, und ) ;( umgekehrt. ) ;( ========================================================================= ) ; ; : SMUDGE LATEST 32 TOGGLE ; NFA_SMUDGE .CBYTE $86,"SMUDGE" LFA_SMUDGE .WORD NFA_RIGHTBRACKET CFA_SMUDGE .WORD DOCOL PFA_SMUDGE .WORD CFA_LATEST ; LATEST .WORD CFA_CLIT ; CLIT .BYTE 32 .WORD CFA_TOGGLE ; TOGGLE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> HEX <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( HEX [ ] setzt die I/O-Zahlenbasis auf 16. ) ;( ========================================================================= ) ; ; : HEX 16 BASE ! ; NFA_HEX .CBYTE $83,"HEX" LFA_HEX .WORD NFA_SMUDGE CFA_HEX .WORD DOCOL PFA_HEX .WORD CFA_CLIT ; CLIT .BYTE 16 .WORD CFA_BASE ; BASE .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> DECIMAL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DECIMAL [ ] setzt die I/O-Zahlenbasis auf 10. ) ;( ========================================================================= ) ; ; : DECIMAL 10 BASE ! ; NFA_DECIMAL .CBYTE $87,"DECIMAL" LFA_DECIMAL .WORD NFA_HEX CFA_DECIMAL .WORD DOCOL PFA_DECIMAL .WORD CFA_CLIT ; CLIT .BYTE 10 .WORD CFA_BASE ; BASE .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> (;CODE) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [;CODE] [ ] ueberschreibt das Codefeld des soeben definierten Wortes mit ) ;( der unmittelbar auf [;CODE] folgenden Adresse und kehrt zum aufrufenden ) ;( Wort zurueck. ) ;( ========================================================================= ) ; ; : (;CODE) R> LATEST PFA CFA ! ; NFA_BRACKETCODE .CBYTE $87,"(;CODE)" LFA_BRACKETCODE .WORD NFA_DECIMAL CFA_BRACKETCODE .WORD DOCOL PFA_BRACKETCODE .WORD CFA_RPOP ; R> .WORD CFA_LATEST ; LATEST .WORD CFA_PFA ; PFA .WORD CFA_CFA ; CFA .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ;CODE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ;CODE [ ] beendet die Colon-Definition eines Definitionswortes und ) ;( beginnt die Spezifikationsphase des Assemblerteiles. ) ;( ========================================================================= ) ; ; : ;CODE ; ?CSP ( Stackpointer-Position ueberpruefen ) ; COMPILE (;CODE) ( [;CODE] kompilieren ) ; [COMPILE] [ ( in den Execute-Mode umschalten ) ; SMUDGE ( soeben definiertes Wort gueltig machen ) ; ; IMMEDIATE NFA_CODE .CBYTE $85+$40,";CODE" ; IMMEDIATE LFA_CODE .WORD NFA_BRACKETCODE CFA_CODE .WORD DOCOL PFA_CODE .WORD CFA_QUERYCSP ; ?CSP .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRACKETCODE ; (;CODE) .WORD CFA_LEFTBRACKET ; [ .WORD CFA_SMUDGE ; SMUDGE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>> DOES> <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DOES> [ ] beendet die Definitionsphase eines High-Level-Definitionswortes ) ;( und leitet die Definitionsphase des entsprechenden Ausfuehrungsteils ein. ) ;( ========================================================================= ) ; ; : DOES> ; R> ( Adresse des auf DOES> folgenden High-Level-Codes ) ; LATEST PFA ! ( in das Parameterfeld des soeben def. Wortes schreiben ) ; ;CODE ; das neue Wort fuehrt den folgenden Code aus: ; DODOE LDA IP+1 ; Instruction-Pointer IP auf den Returnstack pushen ; PHA ; LDA IP ; PHA ; LDY #2 ; LDA (W),Y ; IP mit der Adresse laden, die als erstes im ; STA IP ; Parameterfeld des Wortes abgelegt ist. ; INY ; Dies ist die Adresse unmittelbar hinter dem DOES> ; LDA (W),Y ; des definierenden Wortes. ; STA IP+1 ; CLC ; LDA W ; Adresse des zweiten Eintrags im Parameterfeld des ; ADC #4 ; Wortes holen ; PHA ; LDA W+1 ; ADC #0 ; JMP PUSH ; auf den Datenstack legen und fertig ; END-CODE NFA_DOES .CBYTE $85,"DOES>" LFA_DOES .WORD NFA_BUILDS CFA_DOES .WORD DOCOL PFA_DOES .WORD CFA_RPOP ; R> .WORD CFA_LATEST ; LATEST .WORD CFA_PFA ; PFA .WORD CFA_STORE ; ! .WORD CFA_BRACKETCODE ; (;CODE) ; das neue Wort fuehrt den folgenden Code aus: DODOE LDA IP+1 ; Instruction-Pointer IP auf den Returnstack pushen PHA LDA IP PHA LDY #2 LDA (W),Y ; IP mit der Adresse laden, die als erstes im STA IP ; Parameterfeld des Wortes abgelegt ist. INY ; Dies ist die Adresse unmittelbar hinter dem DOES> LDA (W),Y ; des definierenden Wortes. STA IP+1 CLC LDA W ; Adresse des zweiten Eintrags im Parameterfeld des ADC #4 ; Wortes holen PHA LDA W+1 ADC #0 JMP PUSH ; auf den Datenstack legen und fertig ; >>>>>>>>>>>>>>>> COUNT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( COUNT [ addr1 -> addr2 n ] ermittelt zum String, auf dessen Count-Byte ) ;( addr1 zeigt, die eigentliche Startadresse addr2 und die Anzahl n der ) ;( Zeichen. ) ;( ========================================================================= ) ; ; : COUNT DUP 1+ SWAP C@ ; ( addr2=addr1+1 und n=[addr1] ) NFA_COUNT .CBYTE $85,"COUNT" LFA_COUNT .WORD NFA_DOES CFA_COUNT .WORD DOCOL PFA_COUNT .WORD CFA_DUP ; DUP .WORD CFA_1PLUS ; 1+ .WORD CFA_SWAP ; SWAP .WORD CFA_CFETCH ; C@ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> TYPE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( TYPE [ addr count -> ] gibt den aus count Zeichen bestehenden String, ) ;( beginnend ab Adresse addr, auf die Standardausgabe aus. ) ;( ========================================================================= ) ; ; : TYPE ; -DUP IF ( falls count<>0, d.h. falls Zeichen auszugeben sind: ) ; OVER + SWAP DO ( fuer alle I von addr bis addr+count-1: ) ; I C@ EMIT ( Zeichen in Adresse I ausgeben ) ; LOOP ; ELSE ( falls count=0: ) ; DROP ( nichts ausgeben, addr vom Stack nehmen ) ; ENDIF ; ; NFA_TYPE .CBYTE $84,"TYPE" LFA_TYPE .WORD NFA_COUNT CFA_TYPE .WORD DOCOL PFA_TYPE .WORD CFA_MINUSDUP ; -DUP .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL5-* .WORD CFA_OVER ; OVER .WORD CFA_PLUS ; + .WORD CFA_SWAP ; SWAP .WORD CFA_BRACKETDO ; (DO) LBL6 .WORD CFA_I ; I .WORD CFA_CFETCH ; C@ .WORD CFA_EMIT ; EMIT .WORD CFA_BRACKETLOOP ; (LOOP) .WORD LBL6-* .WORD CFA_BRANCH ; BRANCH .WORD LBL7-* LBL5 .WORD CFA_DROP ; DROP LBL7 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> -TRAILING <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( -TRAILING [ addr n1 -> addr n2 ] verkuerzt den durch addr und n1 bezeich- ) ;( neten String um Blanks am Textende, d.h. die Zeichen in addr+n2 bis ) ;( addr+n1-1 sind Blanks. ) ;( ========================================================================= ) ; ; : -TRAILING ; DUP 0 DO ( wiederhole die folgende Schleife maximal n1-mal ) ; OVER OVER + 1 - C@ ( Zeichen an der Adresse addr+n-1 ) ; BL - IF ( falls es kein Blank ist: ) ; LEAVE ( Suche beenden ) ; ELSE ( sonst: ) ; 1 - ( n dekrementieren ) ; ENDIF ; LOOP ; ; NFA_MINUSTRAILING .CBYTE $89,"-TRAILING" LFA_MINUSTRAILING .WORD NFA_TYPE CFA_MINUSTRAILING .WORD DOCOL PFA_MINUSTRAILING .WORD CFA_DUP ; DUP .WORD CFA_0 ; 0 .WORD CFA_BRACKETDO ; (DO) LBL8 .WORD CFA_OVER ; OVER .WORD CFA_OVER ; OVER .WORD CFA_PLUS ; + .WORD CFA_1 ; 1 .WORD CFA_MINUS ; - .WORD CFA_CFETCH ; C@ .WORD CFA_BL ; BL .WORD CFA_MINUS ; - .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL9-* .WORD CFA_LEAVE ; LEAVE .WORD CFA_BRANCH ; BRANCH .WORD LBL10-* LBL9 .WORD CFA_1 ; 1 .WORD CFA_MINUS ; - LBL10 .WORD CFA_BRACKETLOOP ; (LOOP) .WORD LBL8-* .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> (.") <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [."] [ ] gibt den inline folgenden String auf die Standardausgabe aus. ) ;( ========================================================================= ) ; ; : (.") ; R COUNT ( Adresse und Laenge des inline folgenden Strings ) ; DUP 1+ R> + >R ( Returnadresse um die Gesamtlaenge des Strings erhoehen ) ; TYPE ( String auf die Standardausgabe ausgeben ) ; ; NFA_BRAKETDOTQUOTE .CBYTE $84,"(.",34,")" LFA_BRAKETDOTQUOTE .WORD NFA_MINUSTRAILING CFA_BRAKETDOTQUOTE .WORD DOCOL PFA_BRAKETDOTQUOTE .WORD CFA_R ; R .WORD CFA_COUNT ; COUNT .WORD CFA_DUP ; DUP .WORD CFA_1PLUS ; 1+ .WORD CFA_RPOP ; R> .WORD CFA_PLUS ; + .WORD CFA_RPUSH ; >R .WORD CFA_TYPE ; TYPE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ." <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ." [ ] gibt den im Eingabestrom folgenden String auf die Standardausgabe ) ;( aus. Im Compile-Mode werden [."] und der String kompiliert. ) ;( ========================================================================= ) ; ; : ." ; 34 ( Zeichen '"' ) ; STATE @ IF ( falls Compile-Mode: ) ; COMPILE (.") ( [."] kompilieren ) ; WORD ( folgenden String kompilieren ) ; HERE C@ 1+ ALLOT ( Dictionary-Pointer entsprechend erhoehen ) ; ELSE ( falls Execute-Mode: ) ; WORD ( folgenden String holen ) ; HERE COUNT TYPE ( und auf die Standardausgabe ausgeben ) ; ENDIF ; ; IMMEDIATE NFA_DOTQUOTE .BYTE $C2,".",$A2 LFA_DOTQUOTE .WORD NFA_BRAKETDOTQUOTE CFA_DOTQUOTE .WORD DOCOL PFA_DOTQUOTE .WORD CFA_CLIT ; CLIT .BYTE 34 .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL11-* .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRAKETDOTQUOTE ; (.") .WORD CFA_WORD ; WORD .WORD CFA_CFETCH ; C@ .WORD CFA_1PLUS ; 1+ .WORD CFA_ALLOT ; ALLOT .WORD CFA_BRANCH ; BRANCH .WORD LBL12-* LBL11 .WORD CFA_WORD ; WORD .WORD CFA_COUNT ; COUNT .WORD CFA_TYPE ; TYPE LBL12 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> EXPECT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( EXPECT [ addr count -> ] holt bis zu count Zeichen vom Keyboard - wenn ) ;( nicht vorher CR eingegeben wird - und legt sie ab Adresse addr ab, durch ) ;( ein oder mehrere Null-Bytes abgeschlossen. ) ;( ========================================================================= ) ; ; : EXPECT ; OVER + OVER DO ( fuer I von addr bis addr+count-1 tue: ) ; KEY ( Taste holen ) ; DUP 126 +ORIGIN @ = IF ( falls externes Backspace: ) ; DROP 8 ( Taste durch internes Backspace ersetzen ) ; OVER I = ( 1 falls am linken Anschlag, sonst 0 ) ; DUP R> 2 - + >R ( I um 1 [falls Anfang] oder 2 dekrementieren ) ; - ( ASCII 8 [BS] oder ASCII 7 [BEL] zuruecklassen ) ; ELSE ( falls kein externes Backspace: ) ; DUP 155 = IF ( falls CR: ) ; LEAVE DROP BL 0 ( Eingabe beenden, Blank ausgeben, 0 speichern ) ; ELSE ( falls kein CR: ) ; DUP ( Zeichen ausgeben und speichern ) ; ENDIF ; I C! ( eingegebenes Zeichen speichern ) ; 0 I 1+ ! ( 0 in der darauffolgenden Zelle speichern ) ; ENDIF ; EMIT ( eingegebenes Zeichen ausgeben ) ; LOOP ; DROP ( Adresse addr vergessen ) ; ; NFA_EXPECT .CBYTE $86,"EXPECT" LFA_EXPECT .WORD NFA_DOTQUOTE CFA_EXPECT .WORD DOCOL PFA_EXPECT .WORD CFA_OVER ; OVER .WORD CFA_PLUS ; + .WORD CFA_OVER ; OVER .WORD CFA_BRACKETDO ; (DO) LBL13 .WORD CFA_KEY ; KEY .WORD CFA_DUP ; DUP .WORD CFA_CLIT ; CLIT .BYTE $0E .WORD CFA_PLUSORIGIN ; +ORIGIN .WORD CFA_FETCH ; @ .WORD CFA_EQUAL ; = .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL14-* .WORD CFA_DROP ; DROP .WORD CFA_CLIT ; CLIT .BYTE 126 .WORD CFA_OVER ; OVER .WORD CFA_I ; I .WORD CFA_EQUAL ; = .WORD CFA_DUP ; DUP .WORD CFA_RPOP ; R> .WORD CFA_2 ; 2 .WORD CFA_MINUS ; - .WORD CFA_PLUS ; + .WORD CFA_RPUSH ; >R .WORD CFA_MINUS ; - .WORD CFA_BRANCH ; BRANCH .WORD LBL15-* LBL14 .WORD CFA_DUP ; DUP .WORD CFA_CLIT ; CLIT .BYTE 155 .WORD CFA_EQUAL ; = .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL16-* .WORD CFA_LEAVE ; LEAVE .WORD CFA_DROP ; DROP .WORD CFA_BL ; BL .WORD CFA_0 ; 0 .WORD CFA_BRANCH ; BRANCH .WORD LBL17-* LBL16 .WORD CFA_DUP ; DUP LBL17 .WORD CFA_I ; I .WORD CFA_CSTORE ; C! .WORD CFA_0 ; 0 .WORD CFA_I ; I .WORD CFA_1PLUS ; 1+ .WORD CFA_STORE ; ! LBL15 .WORD CFA_EMIT ; EMIT .WORD CFA_BRACKETLOOP ; (LOOP) .WORD LBL13-* .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> QUERY <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( QUERY [ ] holt bis zu 80 Zeichen - wenn nicht vorher CR eingegeben wird - ) ;( vom Terminal und legt sie im Terminal-Input-Buffer TIB ab. Ausserdem wird ) ;( die User-Variable IN auf 0 gesetzt. ) ;( ========================================================================= ) ; ; : QUERY ; TIB @ 80 EXPECT ( bis zu 80 Zeichen in den Terminal-Input-Buffer einlesen ) ; 0 IN ! ( Interpretation beginnt beim Offset 0 ) ; ; NFA_QUERY .CBYTE $85,"QUERY" LFA_QUERY .WORD NFA_EXPECT CFA_QUERY .WORD DOCOL PFA_QUERY .WORD CFA_TIB ; TIB .WORD CFA_FETCH ; @ .WORD CFA_CLIT ; CLIT .BYTE 80 .WORD CFA_EXPECT ; EXPECT .WORD CFA_0 ; 0 .WORD CFA_IN ; IN .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> X <<<<<<<<<<<<<<<< ; ( ========================================================================= ) ; ( X [ ] bricht die Interpretation einer Zeile Text bedingungslos ab. X ist ) ; ( ein Pseudonym fuer den Namen, der nur aus dem ASCII-0-Zeichen besteht. ) ; ( ========================================================================= ) ; ; ( 32897 HERE ) ( hex 8081 und aktuelle Adresse merken ) ; ; : X ; SOURCE-ID @ IF ( falls von Disk geladen wird: ) ; 1 BLK +! ( naechsten Block interpretieren ) ; 0 IN ! ( beginnend beim Offset 0 ) ; BLK @ 0 B/SCR U/ DROP 0= IF ( falls Screen-Ende erreicht wurde: ) ; ?EXEC R> DROP ( Interpretation ganz abbrechen ) ; ENDIF ; ELSE ( falls nicht von Disk geladen wird: ) ; R> DROP ( Interpretation ganz abbrechen ) ; ENDIF ; ; IMMEDIATE NFA_X .BYTE $C1, $80 LFA_X .WORD NFA_QUERY CFA_X .WORD DOCOL PFA_X ;.WORD CFA_BLK ; BLK ;.WORD CFA_FETCH ; @ ;.WORD CFA_0BRANCH ; 0BRANCH ;.WORD LBL18-* ;.WORD CFA_1 ; 1 ;.WORD CFA_BLK ; BLK ;.WORD CFA_PLUSSTORE ; +! ;.WORD CFA_0 ; 0 ;.WORD CFA_IN ; IN ;.WORD CFA_STORE ; ! ;.WORD CFA_BLK ; BLK ;.WORD CFA_FETCH ; @ ;.WORD CFA_0 ; 0 ;.WORD CFA_BSCR ; B/SCR ;.WORD CFA_UDIV ; U/ ;.WORD CFA_DROP ; DROP ;.WORD CFA_NULLEQUAL ; 0= ;.WORD CFA_0BRANCH ; 0BRANCH ;.WORD LBL19-* ;.WORD CFA_QUERYEXEC ; ?EXEC ;.WORD CFA_RPOP ; R> ;.WORD CFA_DROP ; DROP ;LBL19 ;.WORD CFA_BRANCH ; BRANCH ;.WORD LBL20-* LBL18 .WORD CFA_RPOP ; R> .WORD CFA_DROP ; DROP LBL20 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> FILL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( FILL [ addr n b -> ] fuellt n Bytes ab Adresse addr mit dem Wert b. ) ;( ========================================================================= ) ; ; : FILL ; SWAP >R OVER C! ( Byte b in Adresse addr speichern ) ; DUP 1+ R> 1 - CMOVE ( n-1 Bytes aufsteigend von addr nach addr+1 kopieren ) ; ; NFA_FILL .CBYTE $84,"FILL" LFA_FILL .WORD NFA_X CFA_FILL .WORD DOCOL PFA_FILL .WORD CFA_SWAP ; SWAP .WORD CFA_RPUSH ; >R .WORD CFA_OVER ; OVER .WORD CFA_CSTORE ; C! .WORD CFA_DUP ; DUP .WORD CFA_1PLUS ; 1+ .WORD CFA_RPOP ; R> .WORD CFA_1 ; 1 .WORD CFA_MINUS ; - .WORD CFA_CMOVE ; CMOVE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ERASE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ERASE [ addr n -> ] fuellt n Bytes ab Adresse addr mit Nullen. ) ;( ========================================================================= ) ; ;: ERASE 0 FILL ; NFA_ERASE .CBYTE $85,"ERASE" LFA_ERASE .WORD NFA_FILL CFA_ERASE .WORD DOCOL PFA_ERASE .WORD CFA_0 ; 0 .WORD CFA_FILL ; FILL .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> BLANKS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( BLANKS [ addr count -> ] fuellt count Bytes ab Adresse addr mit Blanks. ) ;( ========================================================================= ) ; ; : BLANKS BL FILL ; NFA_BLANKS .CBYTE $86,"BLANKS" LFA_BLANKS .WORD NFA_ERASE CFA_BLANKS .WORD DOCOL PFA_BLANKS .WORD CFA_BL ; BL .WORD CFA_FILL ; FILL .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> HOLD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( HOLD [ c -> ] fuegt das Zeichen c in einen Ziffernstring ein. Das Wort ) ;( wird in Verbindung mit <# und #> gebraucht. ) ;( ========================================================================= ) ; ;: HOLD ; -1 HLD +! ( Zeiger im Ziffernstring weitersetzen ) ; HLD @ C! ( Zeichen c in das Byte, auf das HLD zeigt, speichern ) ; ; NFA_HOLD .CBYTE $84,"HOLD" LFA_HOLD .WORD NFA_BLANKS CFA_HOLD .WORD DOCOL PFA_HOLD .WORD CFA_LIT ; LIT .WORD -1 .WORD CFA_HLD ; HLD .WORD CFA_PLUSSTORE ; +! .WORD CFA_HLD ; HLD .WORD CFA_FETCH ; @ .WORD CFA_CSTORE ; C! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> PAD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( PAD [ -> addr ] uebergibt die Startadresse des Text-Ausgabe-Buffers PAD. ) ;( ========================================================================= ) ; ; : PAD HERE 68 + ; NFA_PAD .CBYTE $83, "PAD" LFA_PAD .WORD NFA_HOLD CFA_PAD .WORD DOCOL PFA_PAD .WORD CFA_HERE ; HERE .WORD CFA_CLIT ; CLIT .BYTE 68 .WORD CFA_PLUS ; + .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> WORD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( WORD [ c -> c-addr ] bringt das im Input-Strom folgende, durch das ) ;( Zeichen c abgeschlossene Wort als String nach HERE. Der String beginnt ) ;( mit einem Count-Byte. ) ;( ========================================================================= ) ; ;: WORD ; TIB @ ( Adresse des Terminal-Input-Buffers ) ; IN @ + ( Adresse, ab der gelesen wird ) ; SWAP ENCLOSE ( Wort separieren ) ; HERE 34 BLANKS ( Bereich HERE ... HERE+33 mit Blanks fuellen ) ; IN +! ( IN zeigt jetzt auf Offset hinter dem Wort ) ; OVER - >R ( Wortlaenge auf den Return-Stack legen ) ; R HERE C! ( Wortlaenge als Count-Byte nach HERE speichern ) ; + ( Startadresse des separierten Wortes ) ; HERE 1+ ( Zieladresse des Wortes ) ; R> ( Anzahl der zu kopierenden Zeichen ) ; CMOVE ( separiertes Wort kopieren ) ; HERE ( HERE auf dem Stack hinterlassen ) ; ; NFA_WORD .CBYTE $84, "WORD" LFA_WORD .WORD NFA_PAD CFA_WORD .WORD DOCOL PFA_WORD .WORD CFA_TIB ; TIB .WORD CFA_FETCH ; @ .WORD CFA_IN ; IN .WORD CFA_FETCH ; @ .WORD CFA_PLUS ; + .WORD CFA_SWAP ; SWAP .WORD CFA_ENCLOSE ; ENCLOSE .WORD CFA_HERE ; HERE .WORD CFA_CLIT ; CLIT .BYTE 34 .WORD CFA_BLANKS ; BLANKS .WORD CFA_IN ; IN .WORD CFA_PLUSSTORE ; +! .WORD CFA_OVER ; OVER .WORD CFA_MINUS ; - .WORD CFA_RPUSH ; >R .WORD CFA_R ; R .WORD CFA_HERE ; HERE .WORD CFA_CSTORE ; C! .WORD CFA_PLUS ; + .WORD CFA_HERE ; HERE .WORD CFA_1PLUS ; 1+ .WORD CFA_RPOP ; R> .WORD CFA_CMOVE ; CMOVE .WORD CFA_HERE ; HERE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> (NUMBER) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [NUMBER] [ d1 addr1 -> d2 addr2 ] akkumuliert den Ziffernstring ab ) ;( Adresse addr1+1 zur doppeltgenauen, vorzeichenlosen Zahl d2 auf. addr2 ) ;( ist die Adresse des ersten gefundenen Zeichens, das keine gueltige Ziffer ) ;( darstellt. ) ;( ========================================================================= ) ; ; : (NUMBER) ; BEGIN ; 1+ ( auf naechstes Zeichen zeigen ) ; DUP >R ( diesen Zeiger auf dem Return-Stack merken ) ; C@ ( Zeichen holen ) ; BASE @ DIGIT ( und in Ziffer umwandeln ) ; WHILE ( solange es sich um eine gueltige Ziffer handelt: ) ; SWAP BASE @ U* ( Hi-Word von d mit der Basis multiplizieren ) ; DROP ( Hi-Word dieses Produktes vergessen ) ; ROT BASE @ U* ( Lo-Word von d mit der Basis multiplizieren ) ; D+ ( Summe Basis*d+n berechnen ) ; DPL @ 1+ IF ( falls DPL nicht den Wert -1 enthaelt: ) ; 1 DPL +! ( DPL inkrementieren ) ; ENDIF ; R> ( neuen Zeiger vom Return-Stack zurueckholen ) ; REPEAT ( wiederholen ) ; R> ( falls fertig: Zeiger vom Returnstack holen ) ; ; NFA_PARENTNUMBER .CBYTE $88,"(NUMBER)" LFA_PARENTNUMBER .WORD NFA_WORD CFA_PARENTNUMBER .WORD DOCOL PFA_PARENTNUMBER LBL23 .WORD CFA_1PLUS ; 1+ .WORD CFA_DUP ; DUP .WORD CFA_RPUSH ; >R .WORD CFA_CFETCH ; C@ .WORD CFA_BASE ; BASE .WORD CFA_FETCH ; @ .WORD CFA_DIGIT; DIGIT .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL24-* .WORD CFA_SWAP ; SWAP .WORD CFA_BASE ; BASE .WORD CFA_FETCH ; @ .WORD CFA_UMULT ; U* .WORD CFA_DROP ; DROP .WORD CFA_ROT ; ROT .WORD CFA_BASE ; BASE .WORD CFA_FETCH ; @ .WORD CFA_UMULT ; U* .WORD CFA_DPLUS ; D+ .WORD CFA_DPL ; DPL .WORD CFA_FETCH ; @ .WORD CFA_1PLUS ; 1+ .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL25-* .WORD CFA_1 ; 1 .WORD CFA_DPL ; DPL .WORD CFA_PLUSSTORE ; +! LBL25 .WORD CFA_RPOP ; R> .WORD CFA_BRANCH ; BRANCH .WORD LBL23-* LBL24 .WORD CFA_RPOP ; R> .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> NUMBER <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( NUMBER [ addr -> d ] wandelt den String, auf dessen Count-Byte addr ) ;( zeigt, in eine doppeltgenaue Zahl d um. Das Count-Byte selbst wird nicht ) ;( beruecksichtigt, der Ziffernstring muss durch ein Blank abgeschlossen ) ;( sein. ) ;( ========================================================================= ) ; ; : NUMBER ; 0 0 ROT ( Anfangsbedingungen fuer [NUMBER] schaffen ) ; DUP 1+ C@ ( erstes Zeichen nach dem Count-Byte holen ) ; 45 = ( Vorzeichenflag: 1 falls Minuszeichen, sonst 0 ) ; DUP >R ( Vorzeichenflag auf den Returnstack legen ) ; + ( falls Vorzeichen: Adresse inkrementieren ) ; -1 ( noch kein Dezimalpunkt gefunden ) ; BEGIN ( Ziffern und Punkte einlesen und umwandeln: ) ; DPL ! ( Dezimalpunkt-Position in DPL speichern ) ; (NUMBER) ( folgende Ziffern in d aufakkumulieren ) ; DUP C@ BL - ( Abbruch der Schleife, falls Blank angetroffen ) ; WHILE ( sonst weitermachen ) ; DUP C@ 46 - 0 ?ERROR ( Fehler falls kein Punkt ) ; 0 ( Punkt gefunden, dann 0 in DPL speichern [s.o.] ) ; REPEAT ; DROP ( Adresse vergessen ) ; R> IF ( falls Minuszeichen am Anfang: ) ; DMINUS ( Zahl negieren ) ; ENDIF ; ; NFA_NUMBER .CBYTE $86,"NUMBER" LFA_NUMBER .WORD NFA_PARENTNUMBER CFA_NUMBER .WORD DOCOL PFA_NUMBER .WORD CFA_0 ; 0 .WORD CFA_0 ; 0 .WORD CFA_ROT ; ROT .WORD CFA_DUP ; DUP .WORD CFA_1PLUS ; 1+ .WORD CFA_CFETCH ; C@ .WORD CFA_CLIT ; CLIT .BYTE 45 .WORD CFA_EQUAL ; = .WORD CFA_DUP ; DUP .WORD CFA_RPUSH ; >R .WORD CFA_PLUS ; + .WORD CFA_LIT ; LIT .WORD -1 LBL26 .WORD CFA_DPL ; DPL .WORD CFA_STORE ; ! .WORD CFA_PARENTNUMBER ; (NUMBER) .WORD CFA_DUP ; DUP .WORD CFA_CFETCH ; C@ .WORD CFA_BL ; BL .WORD CFA_MINUS ; - .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL27-* .WORD CFA_DUP ; DUP .WORD CFA_CFETCH ; C@ .WORD CFA_CLIT ; CLIT .BYTE 46 .WORD CFA_MINUS ; - .WORD CFA_0 ; 0 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_0 ; 0 .WORD CFA_BRANCH ; BRANCH .WORD LBL26-* LBL27 .WORD CFA_DROP ; DROP .WORD CFA_RPOP ; R> .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL28-* .WORD CFA_DMINUS ; DMINUS LBL28 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> -FIND <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( -FIND [ -> pfa b tf ] falls Suche erfolgreich, sonst [ -> ff ]. Liest das ) ;( naechste Wort und bringt es, mit Count-Byte versehen, nach HERE. ) ;( Anschliessend wird das CONTEXT- und das CURRENT-Vokabular nach dem Wort ) ;( abgesucht. Wird es gefunden, so wird seine PFA, das Count-Byte b und ein ) ;( true-Flag uebergeben, sonst lediglich ein false-Flag. ) ;( ========================================================================= ) ; ; : -FIND ; BL WORD ( naechstes Wort lesen und nach HERE bringen ) ; HERE CONTEXT @ @ (FIND) ( CONTEXT-Vokabular nach dem Wort absuchen ) ; DUP 0= IF ( falls nicht gefunden: ) ; DROP ( false-Flag vergessen ) ; HERE LATEST (FIND) ( CURRENT-Vokabular nach dem Wort absuchen ) ; ENDIF ; ; NFA_MINUSFIND .CBYTE $85,"-FIND" LFA_MINUSFIND .WORD NFA_NUMBER CFA_MINUSFIND .WORD DOCOL PFA_MINUSFIND .WORD CFA_BL ; BL .WORD CFA_WORD ; WORD .WORD CFA_CONTEXT ; CONTEXT .WORD CFA_FETCH ; @ .WORD CFA_FETCH ; @ .WORD CFA_BRACKETFIND ; (FIND) .WORD CFA_DUP ; DUP .WORD CFA_NULLEQUAL ; 0= .WORD CFA_0BRANCH ; 0BRACH .WORD LBL29-* .WORD CFA_DROP ; DROP .WORD CFA_HERE ; HERE .WORD CFA_LATEST ; LATEST .WORD CFA_BRACKETFIND ; (FIND) LBL29 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> (ABORT) <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [ABORT] [ ] wird im Anschluss an eine Fehlermeldung ausgefuehrt, wenn die ) ;( User-Variable WARNING den Wert -1 hat. Es wird lediglich das Wort ABORT ) ;( aufgerufen. ) ;( ========================================================================= ) ; ; : (ABORT) ABORT ; NFA_PARENTABORT .CBYTE $87,"(ABORT)" LFA_PARENTABORT .WORD NFA_MINUSFIND CFA_PARENTABORT .WORD DOCOL PFA_PARENTABORT .WORD CFA_ABORT ; ABORT .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ERROR <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ERROR [ n -> in blk ] gibt je nach Inhalt der User-Variablen WARNING den ) ;( Text der Zeile n relativ zu Zeile 0 in Screen 4 aus [WARNING=1] oder ) ;( lediglich eine verkuerzte Fehlermeldung unter Angabe der Fehlernummer n ) ;( [WARNING=0]. Ist WARNING=-1, so wird [ABORT] aufgerufen. Schliesslich ) ;( wird der Inhalt der User-Variablen IN und BLK zurueckgegeben. ) ;( ========================================================================= ) ; : ERROR ; WARNING @ 0< IF (ABORT) THEN ; HERE COUNT TYPE ."?" MESSAGE SP! ; IN @ BLK @ QUIT ; NFA_ERROR .CBYTE $85, "ERROR" LFA_ERROR .WORD NFA_PARENTABORT CFA_ERROR .WORD DOCOL PFA_ERROR .WORD CFA_WARNING ; WARNING .WORD CFA_FETCH ; @ .WORD CFA_LTNULL ; 0< .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL30-* .WORD CFA_PARENTABORT ; (ABORT) LBL30 .WORD CFA_HERE ; HERE .WORD CFA_COUNT ; COUNT .WORD CFA_TYPE ; TYPE .WORD CFA_BRAKETDOTQUOTE ; (.") .BYTE 2 .BYTE 63 .BYTE 32 .WORD CFA_MESSAGE ; MESSAGE .WORD CFA_SPSTORE ; SP! ;.WORD CFA_IN ; IN ; auskommentiert da wir keine blockbefehle haben ;.WORD CFA_FETCH ; @ ; to do --> umsetzen fuer Dateizugriff ;.WORD CFA_BLK ; BLK ;.WORD CFA_FETCH ; @ .WORD CFA_QUIT ; QUIT .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ID. <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ID. [ addr -> ] kopiert das Namenfeld des Wortes, auf dessen NFA addr ) ;( zeigt, nach PAD und gibt den Namen, gefolgt von einem Blank, auf die ) ;( Standardausgabe aus. ) ;( ========================================================================= ) ; ;( Die mit [*] gekennzeichneten Zeilen sind hinzugefuegt worden, damit auch ) ;( auf Rechnern mit einem erweiterten 8-Bit-Zeichensatz der Name korrekt ) ;( ausgegeben wird. ) ; ; : ID. ; PAD 32 95 FILL ( PAD mit 32 Underscores fuellen ) ; DUP PFA LFA ( LFA des Wortes ermitteln ) ; OVER - ( Differenz mit NFA ergibt Laenge des Namenfeldes ) ; SWAP OVER ( Laenge noch mal retten [*] ) ; PAD SWAP CMOVE ( gesamtes Namenfeld nach PAD kopieren ) ; 1 - PAD + 128 TOGGLE ( MS-Bit des letzten Zeichens loeschen [*] ) ; PAD COUNT 31 AND TYPE ( Namen mit korrigiertem Count-Byte ausgeben ) ; SPACE ( Blank ausgeben ) ; ; NFA_IDDOT .CBYTE $83,"ID." LFA_IDDOT .WORD NFA_ERROR CFA_IDDOT .WORD DOCOL PFA_IDDOT .WORD CFA_PAD ; PAD .WORD CFA_CLIT ; CLIT .BYTE 32 .WORD CFA_CLIT ; CLIT .BYTE 95 .WORD CFA_FILL ; FILL .WORD CFA_DUP ; DUP .WORD CFA_PFA ; PFA .WORD CFA_LFA ; LFA .WORD CFA_OVER ; OVER .WORD CFA_MINUS ; - .WORD CFA_SWAP ; SWAP .WORD CFA_OVER ; OVER .WORD CFA_PAD ; PAD .WORD CFA_SWAP ; SWAP .WORD CFA_CMOVE ; CMOVE .WORD CFA_1 ; 1 .WORD CFA_MINUS ; - .WORD CFA_PAD ; PAD .WORD CFA_PLUS ; + .WORD CFA_CLIT ; CLIT .BYTE 128 .WORD CFA_TOGGLE ; TOGGLE .WORD CFA_PAD ; PAD .WORD CFA_COUNT ; COUNT .WORD CFA_CLIT ; CLIT .BYTE 31 .WORD CFA_AND ; AND .WORD CFA_TYPE ; TYPE .WORD CFA_SPACE ; SPACE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> CREATE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CREATE [ ] erzeugt einen Wort-Header im Dictionary. ) ;( ========================================================================= ) ; ; : CREATE ; TIB HERE 160 + U< 2 ?ERROR ( nur 6502: Fehler falls kein Platz mehr ) ; -FIND IF ( Namenstring ablegen; falls schon vorhanden: ) ; DROP NFA ID. ( Namen ausgeben ) ; 4 MESSAGE SPACE ( Fehlermeldung 4 und Blank ausgeben ) ; ENDIF ; HERE DUP C@ WIDTH @ MIN ( Laenge des evtl. gekuerzten Namen ) ; 1+ ALLOT ( plus Count-Byte: ins Dictionary einverleiben ) ; DP C@ 253 = ALLOT ( nur 6502: Codefeld darf nicht auf FF liegen ) ; DUP 160 TOGGLE ( hoechstes und SMUDGE-Bit setzen ) ; HERE 1 - 128 TOGGLE ( hoechstes Bit im letzten Buchstaben setzen ) ; LATEST , ( Link-Feld auf vorheriges Wort zeigen lassen ) ; CURRENT @ ! ( Current-Kette beginnt b. NFA d. neuen Wortes ) ; HERE 2+ , ( naechste Adresse in das Codefeld schreiben ) ; ; NFA_CREATE .CBYTE $86,"CREATE" LFA_CREATE .WORD NFA_IDDOT CFA_CREATE .WORD DOCOL PFA_CREATE .WORD CFA_TIB ; TIB .WORD CFA_HERE ; HERE .WORD CFA_CLIT ; CLIT .BYTE 160 .WORD CFA_PLUS ; + .WORD CFA_ULT ; U< .WORD CFA_2 ; 2 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_MINUSFIND ; -FIND .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL31-* .WORD CFA_DROP ; DROP .WORD CFA_NFA ; NFA .WORD CFA_IDDOT ; ID. .WORD CFA_CLIT ; CLIT .BYTE 4 .WORD CFA_MESSAGE ; MESSAGE .WORD CFA_SPACE ; SPACE LBL31 .WORD CFA_HERE ; HERE .WORD CFA_DUP ; DUP .WORD CFA_CFETCH ; C@ .WORD CFA_WIDTH ; WIDTH .WORD CFA_FETCH ; @ .WORD CFA_MIN ; MIN .WORD CFA_1PLUS ; 1+ .WORD CFA_ALLOT ; ALLOT .WORD CFA_DP ; DP .WORD CFA_CFETCH ; C@ .WORD CFA_CLIT ; CLIT .BYTE 253 .WORD CFA_EQUAL ; = .WORD CFA_ALLOT ; ALLOT .WORD CFA_DUP ; DUP .WORD CFA_CLIT ; CLIT .BYTE 160 .WORD CFA_TOGGLE ; TOGGLE .WORD CFA_HERE ; HERE .WORD CFA_1 ; 1 .WORD CFA_MINUS ; - .WORD CFA_CLIT ; CLIT .BYTE 128 .WORD CFA_TOGGLE ; TOGGLE .WORD CFA_LATEST ; LATEST .WORD CFA_COMMA ; , .WORD CFA_CURRENT ; CURRENT .WORD CFA_FETCH ; @ .WORD CFA_STORE ; ! .WORD CFA_HERE ; HERE .WORD CFA_2PLUS ; 2+ .WORD CFA_COMMA ; , .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> [COMPILE] <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [COMPILE] [ ] kompiliert das im Input-Stream folgende Wort in das ) ;( Dictionary. ) ;( ========================================================================= ) ; ; : [COMPILE] ; -FIND 0= 0 ?ERROR ( Fehlermeldung ausgeben, falls Wort nicht vorhanden ) ; DROP CFA , ( sonst CFA des Wortes in das Dictionary kompilieren ) ; ; IMMEDIATE NFA_BRACKETCOMPILE .CBYTE $89+$40,"[COMPILE]" ; IMMEDIATE LFA_BRACKETCOMPILE .WORD NFA_CREATE CFA_BRACKETCOMPILE .WORD DOCOL PFA_BRACKETCOMPILE .WORD CFA_MINUSFIND ; -FIND .WORD CFA_NULLEQUAL ; 0= .WORD CFA_0 ; 0 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_DROP ; DROP .WORD CFA_CFA ; CFA .WORD CFA_COMMA ; , .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> LITERAL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( LITERAL [ n -> n ] falls Execute-Mode, [ n -> ] falls Compile-Mode. ) ;( Im Compile-Mode wird die Zahl n mitsamt einem LIT in das Dictionary ) ;( kompiliert. Im Execute-Mode hat der Aufruf keinen Effekt. ) ;( ========================================================================= ) ; ; : LITERAL ; STATE @ IF ( falls Compile-Mode: ) ; COMPILE LIT ( Wort LIT in das Dictionary kompilieren ) ; , ( Zahl n in das Dictionary kompilieren ) ; ENDIF ; ; IMMEDIATE NFA_LITERAL .CBYTE $87+$40,"LITERAL" ; IMMEDIATE LFA_LITERAL .WORD NFA_BRACKETCOMPILE CFA_LITERAL .WORD DOCOL PFA_LITERAL .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL32-* .WORD CFA_COMPILE ; COMPILE .WORD CFA_LIT ; LIT .WORD CFA_COMMA ; , LBL32 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> DLITERAL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DLITERAL [ d -> d ] falls Execute-Mode, [ d -> ] falls Compile-Mode. ) ;( Im Compile-Mode wird die doppeltgenaue Zahl d mitsamt entsprechenden ) ;( LITs in das Dictionary kompiliert. Im Execute-Mode hat der Aufruf keinen ) ;( Effekt. ) ;( ========================================================================= ) ; ; : DLITERAL ; STATE @ IF ( falls Compile-Mode: ) ; SWAP ( Hi- und Lo-Word von d vertauschen ) ; [COMPILE] LITERAL ( LIT und Lo-Word von d kompilieren ) ; [COMPILE] LITERAL ( LIT und Hi-Word von d kompilieren ) ; ENDIF ; ; IMMEDIATE NFA_DLITERAL .CBYTE $88+$40,"DLITERAL" ; IMMEDIATE LFA_DLITERAL .WORD NFA_LITERAL CFA_DLITERAL .WORD DOCOL PFA_DLITERAL .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL33-* .WORD CFA_SWAP ; SWAP .WORD CFA_LITERAL ; LITERAL .WORD CFA_LITERAL ; LITERAL LBL33 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ?STACK <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ?STACK [ ] gibt eine Fehlermeldung aus, falls der Stack ueber seine ) ;( Grenzen gewachsen ist. ) ;( ========================================================================= ) ; ; : ?STACK ; SP@ ASM TOS SWAP U< 1 ?ERROR ( Fehlermeldung falls Stack-Unterlauf ) ; SP@ ASM BOS U< 7 ?ERROR ( Fehlermeldung falls Stack-Ueberlauf ) ; ; NFA_QUERYSTACK .CBYTE $86,"?STACK" LFA_QUERYSTACK .WORD NFA_DLITERAL CFA_QUERYSTACK .WORD DOCOL PFA_QUERYSTACK .WORD CFA_SPFETCH ; SP@ .WORD CFA_LIT ; LIT .WORD TOS .WORD CFA_SWAP ; SWAP .WORD CFA_ULT ; U< .WORD CFA_1 ; 1 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_SPFETCH ; SP@ .WORD CFA_LIT ; LIT .WORD BOS .WORD CFA_ULT ; U< .WORD CFA_CLIT ; CLIT .BYTE 7 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> INTERPRET <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( INTERPRET [ ] ist der Kern des aeusseren Interpreters. Text wird geholt ) ;( und, je nach Systemzustand, ausgefuehrt oder kompiliert. ASCII Null been- ) ;( det die Prozedur. ) ;( ========================================================================= ) ; ; : INTERPRET ; BEGIN ( Endlosschleife ) ; -FIND IF ( Wort lesen; falls vorhanden: ) ; STATE @ < IF ( falls Compile-Mode und nicht IMMEDIATE: ) ; CFA , ( CFA des Wortes kompilieren ) ; ELSE ( sonst: ) ; CFA EXECUTE ( Wort ausfuehren ) ; ENDIF ; ?STACK ( Stack ueberpruefen ) ; ELSE ( falls Wort nicht vorhanden: ) ; HERE NUMBER ( Wort in Zahl umwandeln ) ; DPL @ 1+ IF ( falls Dezimalpunkt vorhanden: ) ; [COMPILE] DLITERAL ( doppelt-genaue Zahl kompilieren ) ; ELSE ( falls kein Dezimalpunkt vorhanden: ) ; DROP [COMPILE] LITERAL ( einfach genaue Zahl kompilieren ) ; ENDIF ; ?STACK ( Stack ueberpruefen ) ; ENDIF ; AGAIN ( Ende der Endlosschleife ) ; ; NFA_INTERPRET .CBYTE $89,"INTERPRET" LFA_INTERPRET .WORD NFA_QUERYSTACK CFA_INTERPRET .WORD DOCOL PFA_INTERPRET LBL34 .WORD CFA_MINUSFIND ; -FIND .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL35-* .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_LT ; < .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL36-* .WORD CFA_CFA ; CFA .WORD CFA_COMMA ; , .WORD CFA_BRANCH ; BRANCH .WORD LBL37-* LBL36 .WORD CFA_CFA ; CFA .WORD CFA_EXECUTE ; EXECUTE LBL37 .WORD CFA_QUERYSTACK ; ?STACK .WORD CFA_BRANCH ; BRANCH .WORD LBL38-* LBL35 .WORD CFA_HERE ; HERE .WORD CFA_NUMBER ; NUMBER .WORD CFA_DPL ; DPL .WORD CFA_FETCH ; @ .WORD CFA_1PLUS ; 1+ .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL39-* .WORD CFA_DLITERAL ; DLITERAL .WORD CFA_BRANCH ; BRANCH .WORD LBL40-* LBL39 .WORD CFA_DROP ; DROP .WORD CFA_LITERAL ; LITERAL LBL40 .WORD CFA_QUERYSTACK ; ?STACK LBL38 .WORD CFA_BRANCH ; BRANCH .WORD LBL34-* .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> IMMEDIATE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( IMMEDIATE [ ] dreht das Precedence-Bit des zuletzt definierten Wortes um. ) ;( ========================================================================= ) ; ; : IMMEDIATE LATEST 64 TOGGLE ; NFA_IMMEDIATE .CBYTE $89, "IMMEDIATE" LFA_IMMEDIATE .WORD NFA_INTERPRET CFA_IMMEDIATE .WORD DOCOL PFA_IMMEDIATE .WORD CFA_LATEST ; LATEST .WORD CFA_CLIT ; CLIT .BYTE 64 .WORD CFA_TOGGLE ; TOGGLE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> VOCABULARY <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( VOCABULARY [ ] definiert ein Vokabular. ) ;( ========================================================================= ) ; ; : VOCABULARY ; ( das neue Vokabular-Wort tut bei Aufruf folgendes: ) ; 2+ CONTEXT ! ( LFA von " " in CONTEXT ablegen ) ; ; NFA_VOCABULARY .CBYTE $8A, "VOCABULARY" LFA_VOCABULARY .WORD NFA_IMMEDIATE CFA_VOCABULARY .WORD DOCOL PFA_VOCABULARY .WORD CFA_BUILDS ; DOES156 .WORD CFA_2PLUS ; 2+ .WORD CFA_CONTEXT ; CONTEXT .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> FORTH <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( FORTH [ ] macht das FORTH-Vokabular zum CONTEXT-Vokabular. ) ;( ========================================================================= ) ; ; VOCABULARY FORTH IMMEDIATE ; ; FORTH DEFINITIONS NFA_FORTH .CBYTE $85+$40, "FORTH" LFA_FORTH .WORD NFA_VOCABULARY CFA_FORTH .WORD DODOE PFA_FORTH .WORD DOES156 .BYTE 129 .BYTE 160 .WORD NFA_MON .WORD 0 ; >>>>>>>>>>>>>>>> DEFINITIONS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DEFINITIONS [ ] macht das Context-Vokabular auch current. ) ;( ========================================================================= ) ; ; : DEFINITIONS CONTEXT @ CURRENT ! ; NFA_DEFINITIONS .CBYTE $8B, "DEFINITIONS" LFA_DEFINITIONS .WORD NFA_FORTH CFA_DEFINITIONS .WORD DOCOL PFA_DEFINITIONS .WORD CFA_CONTEXT ; CONTEXT .WORD CFA_FETCH ; @ .WORD CFA_CURRENT ; CURRENT .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ( <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( [ [ ] leitet einen Kommentar ein, der bis zur naechsten schliessenden ) ;( Klammer reicht. ) ;( ========================================================================= ) ; ; : ( 41 WORD DROP ; IMMEDIATE ( bis zur naechsten "Klammer zu" ueberlesen ) NFA_LEFTPAREN .CBYTE $81+$40, "(" ; IMMEDIATE LFA_LEFTPAREN .WORD NFA_DEFINITIONS CFA_LEFTPAREN .WORD DOCOL PFA_LEFTPAREN .WORD CFA_CLIT ; CLIT .BYTE 41 .WORD CFA_WORD ; WORD .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> QUIT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( QUIT [ ] schaltet auf Keyboard-Eingabe um, beendet den Compile-Mode und ) ;( tritt in eine Endlosschleife ein, in der der Benutzer Eingaben macht und ) ;( diese anschliessend interpretiert werden. ) ;( ========================================================================= ) ; : QUIT ; 0 SOURCE-ID ! ( auf Keyboard-Eingabe umschalten ) ; [COMPILE] [ ( auf Execute-Mode umschalten ) ; BEGIN ( Endlosschleife: ) ; RP! ( Returnstack-Pointer reinitialisieren ) ; CR ( Zeilenvorschub ausgeben ) ; QUERY ( eine Zeile Text in den Terminal-Input-Buffer einlesen ) ; INTERPRET ( Zeile interpretieren, ASCII 0 beendet Interpretation ) ; STATE @ 0= IF ( falls Execute-Mode: ) ; ." OK" ( "OK" ausgeben ) ; ENDIF ; AGAIN ( Ende der Endlosschleife ) ; ; NFA_QUIT .CBYTE $84, "QUIT" LFA_QUIT .WORD NFA_LEFTPAREN CFA_QUIT .WORD DOCOL PFA_QUIT .WORD CFA_0 ; 0 .WORD CFA_SOURCEID ; SOURCE-ID .WORD CFA_STORE ; ! .WORD CFA_LEFTBRACKET ; [ LBL41 .WORD CFA_RPSTORE ; RP! .WORD CFA_CR ; CR .WORD CFA_QUERY ; QUERY .WORD CFA_INTERPRET ; INTERPRET .WORD CFA_STATE ; STATE .WORD CFA_FETCH ; @ .WORD CFA_NULLEQUAL ; 0= .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL42-* .WORD CFA_BRAKETDOTQUOTE ; (.") .BYTE 2 .BYTE 79 .BYTE 75 LBL42 .WORD CFA_BRANCH ; BRANCH .WORD LBL41-* .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ABORT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ABORT [ ] reinitialisiert die Stacks, schaltet in den Execute-Mode um und ) ;( geht in den aeusseren Interpreter QUIT ueber. ) ;( ========================================================================= ) ; ; : ABORT ; SP! DECIMAL DR0 ( Initialisierungen vornehmen ) ; CR ." X-FORTH 1.1" ( Systemmeldung ausgeben ) ; [COMPILE] FORTH DEFINITIONS ( FORTH zum aktuellen Vokabular machen ) ; QUIT ( zum aeusseren Interpreter QUIT ) ; ; NFA_ABORT .CBYTE $85,"ABORT" LFA_ABORT .WORD NFA_QUIT CFA_ABORT .WORD DOCOL PFA_ABORT .WORD CFA_SPSTORE ; SP! .WORD CFA_DECIMAL ; DECIMAL .WORD CFA_CR ; CR .WORD CFA_FORTH ; FORTH .WORD CFA_DEFINITIONS ; DEFINITIONS .WORD CFA_BRAKETDOTQUOTE ; (.") .BYTE 22,"X-FORTH 1.1b 031018/cs" ABORTINIT .WORD CFA_QUIT ; QUIT .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> COLD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( COLD [ ] fuehrt einen Kaltstart des Systems durch. ) ;( ========================================================================= ) NFA_COLD .CBYTE $84, "COLD" LFA_COLD .WORD NFA_ABORT CFA_COLD .WORD PFA_COLD PFA_COLD SEI ; maskierbare Interrupts sperren ; USERAREA default siehe ORIGIN+16 .IF DYNMEMTOP ; USERAREA nach MEMTOP ($2E5)-$100 setzen LDA $02E5 STA ORIG+16 LDA $02E6 SEC SBC #2 STA ORIG+17 .ENDIF LDA ORIG+12 ; NFA des letzten Wortes im Dictionary STA PFA_FORTH+4 ; in das Vokabular-Wort FORTH eintragen LDA ORIG+13 STA PFA_FORTH+5 LDY #21 ; Kaltstart: Uservariablen 0 bis VOC-LINK init. BNE L2433 ; d.h. zusaetzlich FENCE, DP und VOC-LINK WARM LDY #15 ; Warmstart: nur Uservariablen 0 bis WARNING init. L2433 LDA ORIG+16 ; User-Pointer UP initialisieren STA UP LDA ORIG+17 STA UP+1 L2437 LDA ORIG+12,Y ; Uservariablen mit Werten ab 12+ORIGIN initialis. STA (UP),Y DEY BPL L2437 LDA #>PFA_ABORT ; Instruction-Pointer IP initialisieren: STA IP+1 ; Interpretation beginnt mit dem Wort ABORT LDA #>>>>>>>>>>>>>>> S>D <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( S>D [ n -> d ] wandelt die Zahl n in eine doppeltgenaue Zahl d um. ) ;( ========================================================================= ) ; ; : S>D DUP 0< MINUS ; ( $0000 oder $FFFF als Hi-Word erzeugen ) NFA_STOD .CBYTE $83, "S>D" LFA_STOD .WORD NFA_COLD CFA_STOD .WORD DOCOL PFA_STOD .WORD CFA_DUP ; DUP .WORD CFA_LTNULL ; 0< .WORD CFA_NEGATE ; MINUS .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> +- <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( +- [ n1 n2 -> n3 ] gibt n3=-n1 zurueck, wenn n2 negativ ist, sonst n3=n1. ) ;( ========================================================================= ) ; ; : +- 0< IF MINUS ENDIF ; NFA_PLUSMINUS .CBYTE $82,"+-" LFA_PLUSMINUS .WORD NFA_STOD CFA_PLUSMINUS .WORD DOCOL PFA_PLUSMINUS .WORD CFA_LTNULL ; 0< .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL43-* .WORD CFA_NEGATE ; MINUS LBL43 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> D+- <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( D+- [ d1 n -> d2 ] gibt d2=-d1 zurueck, wenn n negativ ist, sonst d2=d1. ) ;( Hierbei sind d1 und d2 doppeltgenaue Zahlen. ) ;( ========================================================================= ) ; ; : D+- 0< IF DMINUS ENDIF ; NFA_DPLUSMINUS .CBYTE $83,"D+-" LFA_DPLUSMINUS .WORD NFA_PLUSMINUS CFA_DPLUSMINUS .WORD DOCOL PFA_DPLUSMINUS .WORD CFA_LTNULL ; 0< .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL44-* .WORD CFA_DMINUS ; DMINUS LBL44 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ABS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ABS [ n -> u ] berechnet den absoluten Betrag der Zahl n. ) ;( ========================================================================= ) ; ; : ABS DUP +- ; NFA_ABS .CBYTE $83,"ABS" LFA_ABS .WORD NFA_DPLUSMINUS CFA_ABS .WORD DOCOL PFA_ABS .WORD CFA_DUP ; DUP .WORD CFA_PLUSMINUS ; +- .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> DABS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DABS [ d -> ud ] berechnet den absoluten Betrag der doppeltgenauen ) ;( Zahl d. ) ;( ========================================================================= ) ; ; : DABS DUP D+- ; NFA_DABS .CBYTE $84,"DABS" LFA_DABS .WORD NFA_ABS CFA_DABS .WORD DOCOL PFA_DABS .WORD CFA_DUP ; DUP .WORD CFA_DPLUSMINUS ; D+- .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> MIN <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( MIN [ n1 n2 -> min ] berechnet das Minimum der Zahlen n1 und n2. ) ;( ========================================================================= ) ; ; : MIN ; OVER OVER > IF ( falls n1>n2 ist: ) ; SWAP ( n1 in den TOS tauschen ) ; ENDIF ; DROP ( groessere Zahl im TOS vergessen ) ; ; NFA_MIN .CBYTE $83,"MIN" LFA_MIN .WORD NFA_DABS CFA_MIN .WORD DOCOL PFA_MIN .WORD CFA_OVER ; OVER .WORD CFA_OVER ; OVER .WORD CFA_GT ; > .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL45-* .WORD CFA_SWAP ; SWAP LBL45 .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> MAX <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( MAX [ n1 n2 -> max ] berechnet das Maximum der Zahlen n1 und n2. ) ;( ========================================================================= ) ; ; : MAX ; OVER OVER < IF ( falls n1>>>>>>>>>>>>>>> M* <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( M* [ n1 n2 -> d ] multipliziert die beiden einfachgenauen Zahlen n1 und ) ;( n2 und gibt das Ergebnis als doppeltgenaue Zahl d zurueck. ) ;( ========================================================================= ) ; ; : M* ; OVER OVER XOR >R ( Vorzeichen-Flag auf den Return-Stack bringen ) ; ABS SWAP ABS U* ( absolute Betraege von n1 und n2 multiplizieren ) ; R> D+- ( eventuell Ergebnis negieren ) ; ; NFA_MSTAR .CBYTE $82,"M*" LFA_MSTAR .WORD NFA_MAX CFA_MSTAR .WORD DOCOL PFA_MSTAR .WORD CFA_OVER ; OVER .WORD CFA_OVER ; OVER .WORD CFA_XOR ; XOR .WORD CFA_RPUSH ; >R .WORD CFA_ABS ; ABS .WORD CFA_SWAP ; SWAP .WORD CFA_ABS ; ABS .WORD CFA_UMULT ; U* .WORD CFA_RPOP ; R> .WORD CFA_DPLUSMINUS ; D+- .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> M/ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( M/ [ d n1 -> n2 n3 ] berechnet zur doppeltgenauen Zahl d und zur einfach- ) ;( genauen Zahl n1 den Quotienten n3=d/n1 sowie den Rest n2, der das Vorzei- ) ;( chen des Zaehlers d erhaelt. ) ;( ========================================================================= ) ; ; : M/ ; OVER >R >R ( Hi-Wort von d und n1 auf den Return-Stack bringen ) ; DABS R ABS U/ ( |d| durch |n1| teilen, Quotient liegt auf dem TOS ) ; R> R XOR +- ( Quotient negieren, falls sign[d]<>sign[n1] ) ; SWAP R> +- ( Rest negieren, falls d negativ ist ) ; SWAP ( Quotient wieder in den TOS tauschen ) ; ; NFA_MSLASH .CBYTE $82,"M/" LFA_MSLASH .WORD NFA_MSTAR CFA_MSLASH .WORD DOCOL PFA_MSLASH .WORD CFA_OVER ; OVER .WORD CFA_RPUSH ; >R .WORD CFA_RPUSH ; >R .WORD CFA_DABS ; DABS .WORD CFA_R ; R .WORD CFA_ABS ; ABS .WORD CFA_UDIV ; U/ .WORD CFA_RPOP ; R> .WORD CFA_R ; R .WORD CFA_XOR ; XOR .WORD CFA_PLUSMINUS ; +- .WORD CFA_SWAP ; SWAP .WORD CFA_RPOP ; R> .WORD CFA_PLUSMINUS ; +- .WORD CFA_SWAP ; SWAP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> * <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( * [ n1 n2 -> n3 ] multipliziert die Zahlen n1 und n2. ) ;( ========================================================================= ) ; ; : * U* DROP ; ( multiplizieren und Hi-Word des Produktes vergessen ) NFA_STAR .CBYTE $81,"*" LFA_STAR .WORD NFA_MSLASH CFA_STAR .WORD DOCOL PFA_STAR .WORD CFA_UMULT ; U* .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> /MOD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( /MOD [ n1 n2 -> Rest Quot ] teilt die Zahl n1 durch die Zahl n2 und ) ;( uebergibt den Rest und den Quotienten. ) ;( ========================================================================= ) ; ; : /MOD >R S>D R> M/ ; ( n1 doppeltgenau machen und M/ aufrufen ) NFA_SLASHMOD .CBYTE $84,"/MOD" LFA_SLASHMOD .WORD NFA_STAR CFA_SLASHMOD .WORD DOCOL PFA_SLASHMOD .WORD CFA_RPUSH ; >R .WORD CFA_STOD ; S>D .WORD CFA_RPOP ; R> .WORD CFA_MSLASH ; M/ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> / <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( / [ n1 n2 -> Quotient ] berechnet den Quotienten n1/n2. ) ;( ========================================================================= ) ; ; : / /MOD SWAP DROP ; NFA_SLASH .CBYTE $81,"/" LFA_SLASH .WORD NFA_SLASHMOD CFA_SLASH .WORD DOCOL PFA_SLASH .WORD CFA_SLASHMOD ; /MOD .WORD CFA_SWAP ; SWAP .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> MOD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( MOD [ n1 n2 -> mod ] berechnet den Rest der Division n1/n2. ) ;( ========================================================================= ) ; ; : MOD /MOD DROP ; NFA_MOD .CBYTE $83,"MOD" LFA_MOD .WORD NFA_SLASH CFA_MOD .WORD DOCOL PFA_MOD .WORD CFA_SLASHMOD ; /MOD .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> */MOD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( */MOD [ n1 n2 n3 -> n4 n5 ] berechnet [n1*n2]/n3 und uebergibt in n4 den ) ;( Rest und in n5 den Quotienten. Es wird mit einem doppeltgenauen Zwischen- ) ;( produkt n1*n2 gerechnet. ) ;( ========================================================================= ) ; ; : */MOD >R M* R> M/ ; NFA_STARSLASHMOD .CBYTE $85, "*/MOD" LFA_STARSLASHMOD .WORD NFA_MOD CFA_STARSLASHMOD .WORD DOCOL PFA_STARSLASHMOD .WORD CFA_RPUSH ; >R .WORD CFA_MSTAR ; M* .WORD CFA_RPOP ; R> .WORD CFA_MSLASH ; M/ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> */ <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( */ [ n1 n2 n3 -> n4 ] berechnet den Quotienten [n1*n2]/n3. Es wird mit ) ;( einem doppeltgenauen Zwischenprodukt n1*n2 gerechnet. ) ;( ========================================================================= ) ; ; : */ */MOD SWAP DROP ; NFA_STARSLASH .CBYTE $82,"*/" LFA_STARSLASH .WORD NFA_STARSLASHMOD CFA_STARSLASH .WORD DOCOL PFA_STARSLASH .WORD CFA_STARSLASHMOD ; */MOD .WORD CFA_SWAP ; SWAP .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> M/MOD <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( M/MOD [ ud1 u2 -> u3 ud4 ] dividiert die vorzeichenlose, doppeltgenaue ) ;( Zahl ud1 durch die vorzeichenlose Zahl u2 und uebergibt den Rest u3 sowie ) ;( den doppeltgenauen Quotienten ud4. ) ;( ========================================================================= ) ; ;( Funktionsweise des Algorithmus: Seien B=2^16 und ud1=a*B+b. Mit / sei die ) ;( ganzzahlige Division bezeichnet, mit % die Restbildung. Dann gilt: ) ;( ud4 = ud1/u2 = [a*B+b]/u2 = [a/u2]*B + [[a%u2]*B+b]/u2 und ) ;( u3 = ud1%u2 = [a*B+b]%u2 = [[a%u2]*B+b]%u2. ) ;( Im untenstehenden Kommentar bezeichne q=[[a%u2]*B+b]/u2. ) ; ;: M/MOD ( b a u2 ) ; >R ( b a RS: u2 ) ; 0 R ( b a 0 u2 RS: u2 ) ; U/ ( b a%u2 a/u2 RS: u2 ) ; R> SWAP >R ( b a%u2 u2 RS: a/u2 ) ; U/ ( u3 q RS: a/u2 ) ; R> ( u3 q a/u2 ) ; ; NFA_MMOD .CBYTE $85, "M/MOD" LFA_MMOD .WORD NFA_STARSLASH CFA_MMOD .WORD DOCOL PFA_MMOD .WORD CFA_RPUSH ; >R .WORD CFA_0 ; 0 .WORD CFA_R ; R .WORD CFA_UDIV ; U/ .WORD CFA_RPOP ; R> .WORD CFA_SWAP ; SWAP .WORD CFA_RPUSH ; >R .WORD CFA_UDIV ; U/ .WORD CFA_RPOP ; R> .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> MESSAGE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( MESSAGE [ n -> ] gibt die Meldung Nummer n auf die Standardausgabe aus. ) ;( Hat die User-Variable WARNING einen Wert ungleich Null, so ist dies der ) ;( Inhalt der n-ten Zeile relativ zur Zeile 0 in Screen 4, Laufwerk 0. ) ;( ========================================================================= ) NFA_MESSAGE .CBYTE $87, "MESSAGE" LFA_MESSAGE .WORD NFA_MMOD CFA_MESSAGE .WORD DOCOL PFA_MESSAGE .WORD CFA_BRAKETDOTQUOTE ; (.") .BYTE 6,"MSG # " .WORD CFA_DOT ; . .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ' <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ' [ -> addr ] ermittelt die PFA des folgenden Wortes. Im Compile-Mode ) ;( wird die PFA des folgenden Wortes zusammen mit einem LIT kompiliert. ) ;( ========================================================================= ) ; ; : ' ; -FIND ( Wort im Dictionary suchen ) ; 0= 0 ?ERROR ( Fehlermeldung ausgeben, falls nicht gefunden ) ; DROP ( Count-Byte des Wortes vergessen ) ; [COMPILE] LITERAL ( PFA auf dem Stack lassen oder mit LIT kompilieren ) ; ; IMMEDIATE NFA_TICK .BYTE $81+$40, 167 ; IMMEDIATE LFA_TICK .WORD NFA_MESSAGE CFA_TICK .WORD DOCOL PFA_TICK .WORD CFA_MINUSFIND ; -FIND .WORD CFA_NULLEQUAL ; 0= .WORD CFA_0 ; 0 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_DROP ; DROP .WORD CFA_LITERAL ; LITERAL .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> FORGET <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( FORGET [ ] loescht alle Woerter ab dem im Input-Strom folgenden aus dem ) ;( Dictionary. Sind das Current- und das Context-Vokabular nicht identisch, ) ;( so wird eine Fehlermeldung ausgegeben. ) ;( ========================================================================= ) ; ; : FORGET ; CURRENT @ CONTEXT @ - 24 ?ERROR ( Fehlermeldung falls CURRENT<>CONTEXT ) ; [COMPILE] ' ( PFA des folgenden Wortes ) ; DUP FENCE @ < 21 ?ERROR ( Fehlermeldung falls geschuetzt ) ; DUP NFA DP ! ( Dictionary-Pointer herabsetzen ) ; LFA @ CURRENT @ ! ( jetzt letztes Wort im Curr.-V. merken ) ; ; NFA_FORGET .CBYTE $86,"FORGET" LFA_FORGET .WORD NFA_TICK CFA_FORGET .WORD DOCOL PFA_FORGET .WORD CFA_CURRENT ; CURRENT .WORD CFA_FETCH ; @ .WORD CFA_CONTEXT ; CONTEXT .WORD CFA_FETCH ; @ .WORD CFA_MINUS ; - .WORD CFA_CLIT ; CLIT .BYTE 24 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_TICK ; ' .WORD CFA_DUP ; DUP .WORD CFA_FENCE ; FENCE .WORD CFA_FETCH ; @ .WORD CFA_LT ; < .WORD CFA_CLIT ; CLIT .BYTE 21 .WORD CFA_QUERYERROR ; ?ERROR .WORD CFA_DUP ; DUP .WORD CFA_NFA ; NFA .WORD CFA_DP ; DP .WORD CFA_STORE ; ! .WORD CFA_LFA ; LFA .WORD CFA_FETCH ; @ .WORD CFA_CURRENT ; CURRENT .WORD CFA_FETCH ; @ .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> BACK <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( BACK [ addr -> ] kompiliert die Distanz von HERE nach addr in das ) ;( Dictionary. ) ;( ========================================================================= ) ; ; : BACK HERE - , ; NFA_BACK .CBYTE $84,"BACK" LFA_BACK .WORD NFA_FORGET CFA_BACK .WORD DOCOL PFA_BACK .WORD CFA_HERE ; HERE .WORD CFA_MINUS ; - .WORD CFA_COMMA ; , .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> BEGIN <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( BEGIN [ -> addr n ] legt die aktuelle Adresse HERE und die Strukturken- ) ;( nung n=1 auf den Stack. Das Wort wird innerhalb von Colon-Definitionen ) ;( verwendet und leitet eine der Strukturen BEGIN...UNTIL, BEGIN...AGAIN ) ;( oder BEGIN...WHILE...REPEAT ein. ) ;( ========================================================================= ) ; ; : BEGIN ?COMP HERE 1 ; IMMEDIATE NFA_BEGIN .CBYTE $85+$40,"BEGIN" ; IMMEDIATE LFA_BEGIN .WORD NFA_BACK CFA_BEGIN .WORD DOCOL PFA_BEGIN .WORD CFA_QUERYCOMP ; ?COMP .WORD CFA_HERE ; HERE .WORD CFA_1 ; 1 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ENDIF <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ENDIF [ addr n -> ] korrigiert die in der Adresse addr angelegte proviso- ) ;( rische Sprungdistanz auf ihren korrekten Wert, naemlich nach HERE. Eine ) ;( Fehlermeldung wird ausgegeben, wenn die Strukturkennung n<>2 ist und ) ;( somit kein IF-Konstrukt abgeschlossen wurde. ) ;( ========================================================================= ) ; ; : ENDIF ; ?COMP ( Fehlermeldung falls nicht Compile-Mode ) ; 2 ?PAIRS ( Fehlermeldung falls kein IF-Konstrukt abgeschlossen wurde ) ; HERE OVER - ( Sprungdistanz berechnen ) ; SWAP ! ( und in die durch addr bezeichnete Zelle eintragen ) ; ; IMMEDIATE NFA_ENDIF .CBYTE $85+$40, "ENDIF" ; IMMEDIATE LFA_ENDIF .WORD NFA_BEGIN CFA_ENDIF .WORD DOCOL PFA_ENDIF .WORD CFA_QUERYCOMP ; ?COMP .WORD CFA_2 ; 2 .WORD CFA_QUERYPAIRS ; ?PAIRS .WORD CFA_HERE ; HERE .WORD CFA_OVER ; OVER .WORD CFA_MINUS ; - .WORD CFA_SWAP ; SWAP .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> THEN <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( THEN [ addr n -> ] ist ein Alias fuer ENDIF. ) ;( ========================================================================= ) ; ; : THEN [COMPILE] ENDIF ; IMMEDIATE NFA_THEN .CBYTE $84+$40, "THEN" ; IMMEDIATE LFA_THEN .WORD NFA_ENDIF CFA_THEN .WORD DOCOL PFA_THEN .WORD CFA_ENDIF ; ENDIF .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> DO <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DO [ -> addr n ] legt die aktuelle Adresse HERE und die Strukturkennung ) ;( n=3 auf den Stack und kompiliert ein [DO] in das Dictionary. Das Wort ) ;( wird innerhalb von Colon-Definitionen verwendet und leitet eine der ) ;( beiden Strukturen DO...LOOP oder DO...+LOOP ein. ) ;( ========================================================================= ) ; ; : DO ; COMPILE (DO) ( Runtime-Exekutive [DO] kompilieren ) ; HERE 3 ( Adresse HERE und Strukturkennung n=3 auf den Stack legen ) ; ; IMMEDIATE NFA_DO .CBYTE $82+$40, "DO" ; IMMEDIATE LFA_DO .WORD NFA_THEN CFA_DO .WORD DOCOL PFA_DO .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRACKETDO ; (DO) .WORD CFA_HERE ; HERE .WORD CFA_3 ; 3 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> LOOP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( LOOP [ addr n -> ] kompiliert die Runtime-Exekutive [LOOP] zusammen mit ) ;( einer Ruecksprung-Distanz nach addr in das Dictionary und ueberprueft, ob ) ;( die Strukturkennung n=3 uebergeben und somit eine DO-Struktur korrekt ) ;( abgeschlossen wurde. ) ;( ========================================================================= ) ; ; : LOOP ; 3 ?PAIRS ( Fehler falls kein DO-Konstrukt abgeschlossen wurde ) ; COMPILE (LOOP) ( Runtime-Exekutive [LOOP] kompilieren ) ; BACK ( Ruecksprung-Distanz kompilieren ) ; ; IMMEDIATE NFA_LOOP .CBYTE $84+$40,"LOOP" ; IMMEDIATE LFA_LOOP .WORD NFA_DO CFA_LOOP .WORD DOCOL PFA_LOOP .WORD CFA_3 ; 3 .WORD CFA_QUERYPAIRS ; ?PAIRS .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRACKETLOOP ; (LOOP) .WORD CFA_BACK ; BACK .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> +LOOP <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( +LOOP [ addr n -> ] kompiliert die Runtime-Exekutive [+LOOP] zusammen mit ) ;( einer Ruecksprung-Distanz nach addr in das Dictionary und ueberprueft, ob ) ;( die Strukturkennung n=3 uebergeben und somit eine DO-Struktur korrekt ) ;( abgeschlossen wurde. ) ;( ========================================================================= ) ; ; : +LOOP ; 3 ?PAIRS ( Fehler falls kein DO-Konstrukt abgeschlossen wurde ) ; COMPILE (+LOOP) ( Runtime-Exekutive [+LOOP] kompilieren ) ; BACK ( Ruecksprung-Distanz kompilieren ) ; ; IMMEDIATE NFA_ADDLOOP .CBYTE $85+$40, "+LOOP" ; IMMEDIATE LFA_ADDLOOP .WORD NFA_LOOP CFA_ADDLOOP .WORD DOCOL PFA_ADDLOOP .WORD CFA_3 ; 3 .WORD CFA_QUERYPAIRS ; ?PAIRS .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRACKETPLUSLOOP ; (+LOOP) .WORD CFA_BACK ; BACK .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> UNTIL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( UNTIL [ addr n -> ] kompiliert einen bedingten Ruecksprung nach Adresse ) ;( addr in das Dictionary und ueberprueft, ob die Strukturkennung n=1 ueber- ) ;( geben und somit eine BEGIN-Struktur korrekt abgeschlossen wurde. ) ;( ========================================================================= ) ; ; : UNTIL ; 1 ?PAIRS ( Fehler falls kein BEGIN-Konstrukt abgeschlossen wurde ) ; COMPILE 0BRANCH ( bedingten Sprung kompilieren ) ; BACK ( Ruecksprung-Distanz kompilieren ) ; ; IMMEDIATE NFA_UNTIL .CBYTE $85+$40, "UNTIL" ; IMMEDIATE LFA_UNTIL .WORD NFA_ADDLOOP CFA_UNTIL .WORD DOCOL PFA_UNTIL .WORD CFA_1 ; 1 .WORD CFA_QUERYPAIRS ; ?PAIRS .WORD CFA_COMPILE ; COMPILE .WORD CFA_0BRANCH ; 0BRANCH .WORD CFA_BACK ; BACK .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> END <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( END [ addr n -> ] ist ein Alias fuer UNTIL. ) ;( ========================================================================= ) ; ; : END [COMPILE] UNTIL ; IMMEDIATE NFA_END .CBYTE $83+$40, "END" ; IMMEDIATE LFA_END .WORD NFA_UNTIL CFA_END .WORD DOCOL PFA_END .WORD CFA_UNTIL ; UNTIL .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> AGAIN <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( AGAIN [ addr n -> ] kompiliert einen unbedingten Ruecksprung nach Adresse ) ;( addr in das Dictionary und ueberprueft, ob die Strukturkennung n=1 ueber- ) ;( geben und somit eine BEGIN-Struktur korrekt abgeschlossen wurde. ) ;( ========================================================================= ) ; ; : AGAIN ; 1 ?PAIRS ( Fehler falls kein BEGIN-Konstrukt abgeschlossen wurde ) ; COMPILE BRANCH ( unbedingten Sprung kompilieren ) ; BACK ( Ruecksprung-Distanz kompilieren ) ; ; IMMEDIATE NFA_AGAIN .CBYTE $85+$40,"AGAIN" ; IMMEDIATE LFA_AGAIN .WORD NFA_END CFA_AGAIN .WORD DOCOL PFA_AGAIN .WORD CFA_1 ; 1 .WORD CFA_QUERYPAIRS ; ?PAIRS .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRANCH ; BRANCH .WORD CFA_BACK ; BACK .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> REPEAT <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( REPEAT [ addr1 n1 addr2 n2 -> ] kompiliert einen unbedingten Ruecksprung ) ;( nach Adresse addr1 in das Dictionary und korrigiert die Sprungdistanz bei ) ;( Adresse addr2 derart, dass hinter den soeben kompilierten Sprung gesprun- ) ;( gen wird. Darueberhinaus wird ueberprueft, ob mit n1=1 ein BEGIN- ) ;( Konstrukt abgeschlossen wurde, der wegen n2=4 ein WHILE enthielt. ) ;( ========================================================================= ) ; ; : REPEAT ; >R >R ( addr2 und n2 auf den Return-Stack retten ) ; [COMPILE] AGAIN ( unbedingten Ruecksprung nach addr1 kompilieren ) ; R> R> ( addr2 und n2 zurueckholen ) ; 2 - [COMPILE] ENDIF ( Vorwaertssprung bei Adresse addr2 korrigieren ) ; ; IMMEDIATE NFA_REPEAT .CBYTE $86+$40, "REPEAT" ; IMMEDITE LFA_REPEAT .WORD NFA_AGAIN CFA_REPEAT .WORD DOCOL PFA_REPEAT .WORD CFA_RPUSH ; >R .WORD CFA_RPUSH ; >R .WORD CFA_AGAIN ; AGAIN .WORD CFA_RPOP ; R> .WORD CFA_RPOP ; R> .WORD CFA_2 ; 2 .WORD CFA_MINUS ; - .WORD CFA_ENDIF ; ENDIF .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> IF <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( IF [ -> addr n ] kompiliert einen bedingten Sprung mit noch provisori- ) ;( scher Sprungdistanz und legt die Adresse, an der die Distanz steht, sowie ) ;( die Strukturkennung n=2 auf den Stack. Das Wort wird in Colon-Definitio- ) ;( nen zur Einleitung eines IF...ENDIF- oder IF...ELSE...ENDIF-Konstruktes ) ;( benutzt. ) ;( ========================================================================= ) ; ; : IF ; COMPILE 0BRANCH ( bedingten Sprung kompilieren ) ; HERE ( Adresse addr, an der die Distanz steht ) ; 0 , ( provisorische Sprungdistanz kompilieren ) ; 2 ( Strukturkennung n=2 ) ; ; IMMEDIATE NFA_IF .CBYTE $82+$40,"IF" ; IMMEDIATE LFA_IF .WORD NFA_REPEAT CFA_IF .WORD DOCOL PFA_IF .WORD CFA_COMPILE ; COMPILE .WORD CFA_0BRANCH ; 0BRANCH .WORD CFA_HERE ; HERE .WORD CFA_0 ; 0 .WORD CFA_COMMA ; , .WORD CFA_2 ; 2 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ELSE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ELSE [ addr1 n1 -> addr2 n2 ] kompiliert einen unbedingten Sprung mit ) ;( noch provisorischer Sprungdistanz und korrigiert die Sprungdistanz in ) ;( addr1 derart, dass hinter den soeben kompilierten Sprung gesprungen wird. ) ;( Darueberhinaus wird ueberprueft, ob mit n1=2 ein IF-Konstrukt um einen ) ;( ELSE-Zweig ergaenzt wurde. Auf dem Stack werden die Adresse addr2, an der ) ;( sich die soeben kompilierte provisorische Distanz befindet, und die ) ;( Strukturkennung n2=2 hinterlassen. ) ;( ========================================================================= ) ; ; : ELSE ; 2 ?PAIRS ( Fehler falls kein IF-Konstrukt ) ; COMPILE BRANCH ( unbedingten Sprung kompilieren ) ; HERE ( Adresse addr2, an der die Distanz steht ) ; 0 , ( provisorische Distanz kompilieren ) ; SWAP 2 [COMPILE] ENDIF ( Sprungdistanz bei addr1 korrigieren ) ; 2 ( Strukturkennung n2=2 ) ; ; IMMEDIATE NFA_ELSE .CBYTE $84+$40,"ELSE" ; IMMEDIATE LFA_ELSE .WORD NFA_IF CFA_ELSE .WORD DOCOL PFA_ELSE .WORD CFA_2 ; 2 .WORD CFA_QUERYPAIRS ; ?PAIRS .WORD CFA_COMPILE ; COMPILE .WORD CFA_BRANCH ; BRANCH .WORD CFA_HERE ; HERE .WORD CFA_0 ; 0 .WORD CFA_COMMA ; , .WORD CFA_SWAP ; SWAP .WORD CFA_2 ; 2 .WORD CFA_ENDIF ; ENDIF .WORD CFA_2 ; 2 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> WHILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( WHILE [ -> addr n ] kompiliert einen bedingten Vorwaertssprung mit noch ) ;( provisorischer Sprungdistanz und hinterlaesst auf dem Stack die Adresse ) ;( addr, an der die provisorische Sprungdistanz steht, und die Strukturken- ) ;( nung n=4. ) ;( ========================================================================= ) ; ; : WHILE [COMPILE] IF 2+ ; IMMEDIATE NFA_WHILE .CBYTE $85+$40, "WHILE" ; IMMEDIATE LFA_WHILE .WORD NFA_ELSE CFA_WHILE .WORD DOCOL PFA_WHILE .WORD CFA_IF ; IF .WORD CFA_2PLUS ; 2+ .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> SPACES <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SPACES [ n -> ] gibt n Spaces auf die Standardausgabe aus. ) ;( ========================================================================= ) ; ; : SPACES ; 0 MAX -DUP IF ( falls n>0 ist: ) ; 0 DO ( wiederhole n-mal: ) ; SPACE ( Blank ausgeben ) ; LOOP ; ENDIF ; ; NFA_SPACES .CBYTE $86,"SPACES" LFA_SPACES .WORD NFA_WHILE CFA_SPACES .WORD DOCOL PFA_SPACES .WORD CFA_0 ; 0 .WORD CFA_MAX ; MAX .WORD CFA_MINUSDUP ; -DUP .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL50-* .WORD CFA_0 ; 0 .WORD CFA_BRACKETDO ; (DO) LBL51 .WORD CFA_SPACE ; SPACE .WORD CFA_BRACKETLOOP ; (LOOP) .WORD LBL51-* LBL50 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> <# <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( <# [ ] leitet die Umwandlung einer doppeltgenauen Zahl in einen String, ) ;( der von PAD-1 an abwaerts erzeugt wird, ein. ) ;( ========================================================================= ) ; ; : <# PAD HLD ! ; ( Laufzeiger HLD initialisieren ) NFA_LTSHARP .CBYTE $82,"<#" LFA_LTSHARP .WORD NFA_SPACES CFA_LTSHARP .WORD DOCOL PFA_LTSHARP .WORD CFA_PAD ; PAD .WORD CFA_HLD ; HLD .WORD CFA_STORE ; ! .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> #> <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( #> [ ud -> addr n ] beendet die Umwandlung einer doppeltgenauen Zahl in ) ;( einen String und uebergibt seine Startadresse addr und seine Laenge n. ) ;( ========================================================================= ) ; ; : #> ; DROP DROP ( evtl. noch vorhandenen Rest ud vergessen ) ; HLD @ ( Startadresse addr des Strings ) ; PAD OVER - ( Laenge n des Strings ) ; ; NFA_SHARPGT .CBYTE $82,"#>" LFA_SHARPGT .WORD NFA_LTSHARP CFA_SHARPGT .WORD DOCOL PFA_SHARPGT .WORD CFA_DROP ; DROP .WORD CFA_DROP ; DROP .WORD CFA_HLD ; HLD .WORD CFA_FETCH ; @ .WORD CFA_PAD ; PAD .WORD CFA_OVER ; OVER .WORD CFA_MINUS ; - .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> SIGN <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( SIGN [ n d -> d ] fuegt an den im Aufbau befindlichen Zahlenstring ein ) ;( Minuszeichen an, falls n negativ ist. Die doppeltgenaue Zahl d bleibt ) ;( unveraendert. ) ;( ========================================================================= ) ; ; : SIGN ; ROT 0< IF ( falls n<0 ist: ) ; 45 HOLD ( Zeichen "-" an den String anfuegen ) ; ENDIF ; ; NFA_SIGN .CBYTE $84,"SIGN" LFA_SIGN .WORD NFA_SHARPGT CFA_SIGN .WORD DOCOL PFA_SIGN .WORD CFA_ROT ; ROT .WORD CFA_LTNULL ; 0< .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL52-* .WORD CFA_CLIT ; CLIT .BYTE 45 .WORD CFA_HOLD ; HOLD LBL52 .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> # <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( # [ ud1 -> ud2 ] ermittelt aus der vorzeichenlosen, ganzen Zahl die ) ;( niederwertigste Ziffer und fuegt sie an den im Aufbau befindlichen ) ;( Zahlenstring an. Der verbleibende Rest ud2 wird auf dem Stack uebergeben. ) ;( ========================================================================= ) ; ; : # ; BASE @ M/MOD ( einfachgenaue Ziffer und doppeltgenauen Rest ermitteln ) ; ROT ( Ziffer nach vorne holen ) ; 9 OVER < IF ( falls Ziffer groesser als 9 ist: ) ; 7 + ( 7 addieren, um im Buchstabenbereich zu landen ) ; ENDIF ; 48 + ( "0" addieren; ergibt Ziffer als ASCII-Zeichen ) ; HOLD ( Ziffer an den String anfuegen ) ; ; NFA_SHARP .CBYTE $81,"#" LFA_SHARP .WORD NFA_SIGN CFA_SHARP .WORD DOCOL PFA_SHARP .WORD CFA_BASE ; BASE .WORD CFA_FETCH ; @ .WORD CFA_MMOD ; M/MOD .WORD CFA_ROT ; ROT .WORD CFA_CLIT ; CLIT .BYTE 9 .WORD CFA_OVER ; OVER .WORD CFA_LT ; < .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL53-* .WORD CFA_CLIT ; CLIT .BYTE 7 .WORD CFA_PLUS ; + LBL53 .WORD CFA_CLIT ; CLIT .BYTE 48 .WORD CFA_PLUS ; + .WORD CFA_HOLD ; HOLD .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> #S <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( #S [ ud1 -> ud2 ] fuegt solange Ziffern an den im Aufbau befindlichen ) ;( Zahlenstring an, bis der Rest, der als ud2 uebergeben wird, 0 geworden ) ;( ist. ) ;( ========================================================================= ) ; ; : #S ; BEGIN ; # ( eine Ziffer erzeugen und an den String anfuegen ) ; OVER OVER OR 0= ( solange bis der verbleibende Rest 0 geworden ist ) ; UNTIL ; ; NFA_SHARPS .CBYTE $82,"#S" LFA_SHARPS .WORD NFA_SHARP CFA_SHARPS .WORD DOCOL PFA_SHARPS LBL54 .WORD CFA_SHARP ; # .WORD CFA_OVER ; OVER .WORD CFA_OVER ; OVER .WORD CFA_OR ; OR .WORD CFA_NULLEQUAL ; 0= .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL54-* .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> D.R <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( D.R [ d n -> ] gibt die doppeltgenaue Zahl d rechtsbuendig in einem Feld ) ;( der Breite n auf die Standardausgabe aus. ) ;( ========================================================================= ) ; ; : D.R ; >R ( Feldbreite n auf den Return-Stack retten ) ; SWAP OVER ( [ hi d ] erzeugen; hi enthaelt das Vorzeichen von d ) ; DABS ( absoluten Betrag von d ermitteln ) ; <# #S SIGN #> ( d in einen String umwandeln und Vorzeichen beruecks. ) ; R> OVER - SPACES ( zunaechst entsprechend viele Blanks ausgeben ) ; TYPE ( anschliessend erzeugten String ausgeben ) ; ; NFA_DDOTR .CBYTE $83, "D.R" LFA_DDOTR .WORD NFA_SHARPS CFA_DDOTR .WORD DOCOL PFA_DDOTR .WORD CFA_RPUSH ; >R .WORD CFA_SWAP ; SWAP .WORD CFA_OVER ; OVER .WORD CFA_DABS ; DABS .WORD CFA_LTSHARP ; <# .WORD CFA_SHARPS ; #S .WORD CFA_SIGN ; SIGN .WORD CFA_SHARPGT ; #> .WORD CFA_RPOP ; R> .WORD CFA_OVER ; OVER .WORD CFA_MINUS ; - .WORD CFA_SPACES ; SPACES .WORD CFA_TYPE ; TYPE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> .R <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( .R [ n1 n2 -> ] gibt die Zahl n1 rechtsbuendig in einem Feld der Breite ) ;( n2 auf die Standardausgabe aus. ) ;( ========================================================================= ) ; ; : .R >R S>D R> D.R ; ( Zahl n1 doppeltgenau machen und mit D.R ausgeben ) NFA_DOTR .CBYTE $82,".R" LFA_DOTR .WORD NFA_DDOTR CFA_DOTR .WORD DOCOL PFA_DOTR .WORD CFA_RPUSH ; >R .WORD CFA_STOD ; S>D .WORD CFA_RPOP ; R> .WORD CFA_DDOTR ; D.R .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> D. <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( D. [ d -> ] gibt die doppeltgenaue Zahl d auf die Standardausgabe aus. ) ;( ========================================================================= ) ; : D. 0 D.R SPACE ; ( Zahl in einem Feld der Breite 0 ausgeben ) NFA_DDOT .CBYTE $82,"D." LFA_DDOT .WORD NFA_DOTR CFA_DDOT .WORD DOCOL PFA_DDOT .WORD CFA_0 ; 0 .WORD CFA_DDOTR ; D.R .WORD CFA_SPACE ; SPACE .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> . <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( . [ n -> ] gibt die Zahl n auf die Standardausgabe aus. ) ;( ========================================================================= ) ; ;: . S>D D. ; ( Zahl n doppeltgenau machen und mit D. ausgeben ) NFA_DOT .CBYTE $81,"." LFA_DOT .WORD NFA_DDOT CFA_DOT .WORD DOCOL PFA_DOT .WORD CFA_STOD ; S>D .WORD CFA_DDOT ; D. .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> ? <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( ? [ addr -> ] gibt die Zahl, die an der Adresse addr gespeicher ist, aus. ) ;( ========================================================================= ) ; ;: ? @ . ; NFA_QUEST .CBYTE $81, "?" LFA_QUEST .WORD NFA_DOT CFA_QUEST .WORD DOCOL PFA_QUEST .WORD CFA_FETCH ; @ .WORD CFA_DOT ; . .WORD CFA_EXIT ; ;S ; >>>>>>>>>>>>>>>> U. <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( U. [ u -> ] gibt die vorzeichenlose Zahl u auf die Standardausgabe aus. ) ;( ========================================================================= ) ; : U. 0 D. ; ( Zahl vorzeichenlos doppeltgenau machen und mit D. ausgeben ) NFA_UDOT .CBYTE $82,"U." LFA_UDOT .WORD NFA_QUEST CFA_UDOT .WORD DOCOL PFA_UDOT .WORD CFA_0 ; 0 .WORD CFA_DDOT ; D. .WORD CFA_EXIT ; ;S PREVLINK .= NFA_UDOT ;( ========================================================================= ) ;( Definition diverser Konstanten und Variablen, die zur Verwaltung des ) ;( Disk-Systems benoetigt werden. ) ;( ========================================================================= ) ; >>>>>>>>>>>>>>>> C/L <<<<<<<<<<<<<<<< ; 64 CONSTANT C/L ( Zeichen pro Eingabezeile ) NFA_C_L .CBYTE $83, "C/L" LFA_C_L .WORD PREVLINK CFA_C_L .WORD DOCON PFA_C_L .WORD 32 PREVLINK .= NFA_C_L ; >>>>>>>>>>>>>>>> CALL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CALL Sprung zur ASM Subroutine ) ;( ========================================================================= ) NFA_CALL .CBYTE $84, "CALL" LDA_CALL .WORD PREVLINK CFA_CALL .WORD PFA_CALL PFA_CALL STX XSAVE LDA 0,X STA PFA_CALL1 LDA 1,X STA PFA_CALL1+1 .BYTE $20 ; JSR PFA_CALL1 .WORD 0 LDX XSAVE STA 0,X STY 1,X JMP NEXT PREVLINK .= NFA_CALL ; >>>>>>>>>>>>>>>> WORDS <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( WORDS [ ] gibt die Namen aller Worte, beginnend beim Context-Volabular, ) ;( auf die Standardausgabe aus. Die Ausgabe kann mit einer beliebigen Taste ) ;( abgebrochen werden. ) ;( ========================================================================= ) ;: WORDS ; 128 OUT ! ( Initialisierung, damit zu Anfang ein CR erfolgt ) ; CONTEXT @ @ ( NFA des letzten Wortes im Context-Vokabular ) ; BEGIN ; OUT @ C/L > IF ( falls Zeilenende ueberschritten wurde: ) ; CR ( in neue Zeile gehen ) ; 0 OUT ! ( Ausgabe in Spalte 0 der neuen Zeile beginnen ) ; ENDIF ; DUP ID. SPACE SPACE ( Namen des aktuellen Wortes ausgeben ) ; PFA LFA @ ( NFA des naechsten Wortes ermitteln ) ; DUP 0= ?TERMINAL OR ( Ende der Linkerkette oder Taste gedrueckt? ) ; UNTIL ( ja dann fertig, sonst weiter ausgeben ) ; DROP ( letzte NFA vergessen ) ; NFA_WORDS .CBYTE $85, "WORDS" LFA_WORDS .WORD PREVLINK CFA_WORDS .WORD DOCOL PFA_WORDS .WORD CFA_CLIT ; CLIT .BYTE 128 .WORD CFA_OUT ; OUT .WORD CFA_STORE ; ! .WORD CFA_CONTEXT ; CONTEXT .WORD CFA_FETCH ; @ .WORD CFA_FETCH ; @ LBL74 .WORD CFA_OUT ; OUT .WORD CFA_FETCH ; @ .WORD CFA_C_L ; C/L .WORD CFA_GT ; > .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL75-* .WORD CFA_CR ; CR .WORD CFA_0 ; 0 .WORD CFA_OUT ; OUT .WORD CFA_STORE ; ! LBL75 .WORD CFA_DUP ; DUP .WORD CFA_IDDOT ; ID. ;.WORD CFA_SPACE ; SPACE ;.WORD CFA_SPACE ; SPACE .WORD CFA_PFA ; PFA .WORD CFA_LFA ; LFA .WORD CFA_FETCH ; @ .WORD CFA_DUP ; DUP .WORD CFA_NULLEQUAL ; 0= .WORD CFA_QUERYTERMINAL ; ?TERMINAL .WORD CFA_OR ; OR .WORD CFA_0BRANCH ; 0BRANCH .WORD LBL74-* .WORD CFA_DROP ; DROP .WORD CFA_EXIT ; ;S PREVLINK .= NFA_WORDS .IF FILE ;( ========================================================================= ) ;( Definitionen fuer Dateibefehle ) ;( ========================================================================= ) FAM_R = 4 FAM_W = 8 FAM_RW = FAM_R + FAM_W IO_OPEN = $3 IO_GETREC = $5 IO_GETCHR = $7 IO_PUTREC = $9 IO_PUTCHR = $B IO_CLOSE = $C ICFLG = $340 ICCOM = $342 ICSTA = $343 ICBAL = $344 ICBAH = $345 ICBLL = $348 ICBLH = $349 ICAX1 = $34A ICAX2 = $34B CIOV = $E456 ;( ========================================================================= ) ;( Hilfsroutinen fuer die Dateibefehle ) ;( ========================================================================= ) GETFILEID LDA 0,X ; get fileid GETFILEID0 ASL ASL ASL ASL TAY RTS JCIOV STX XSAVE TYA TAX JSR CIOV TXA TAY LDX XSAVE RTS ;( ========================================================================= ) ;( Hilfswoerter fuer die Dateibefehle ) ;( ========================================================================= ) ; >>>>>>>>>>>>>>>> R/O <<<<<<<<<<<<<<<< ; CONSTANT R/O 4 NFA_RO .CBYTE $83, "R/O" LFA_RO .WORD PREVLINK CFA_RO .WORD DOCON PFA_RO .WORD FAM_R PREVLINK .= NFA_RO ; >>>>>>>>>>>>>>>> R/W <<<<<<<<<<<<<<<< ; CONSTANT R/O 12 NFA_RW .CBYTE $83, "R/W" LFA_RW .WORD PREVLINK CFA_RW .WORD DOCON PFA_RW .WORD FAM_RW PREVLINK .= NFA_RW ; >>>>>>>>>>>>>>>> W/O <<<<<<<<<<<<<<<< ; CONSTANT R/O 8 NFA_WO .CBYTE $83, "W/O" LFA_WO .WORD PREVLINK CFA_WO .WORD DOCON PFA_WO .WORD FAM_W PREVLINK .= NFA_WO ; >>>>>>>>>>>>>>>> CIO <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CIO [ ] Schnittstelle zur ATARI CIO ) ;( ========================================================================= ) NFA_CIO .CBYTE $83, "CIO" LDA_CIO .WORD PREVLINK CFA_CIO .WORD PFA_CIO PFA_CIO JMP NEXT PREVLINK .= NFA_CIO ; >>>>>>>>>>>>>>>> FREEIOCB <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( FreeIOCB [ -- freeiocb ] ermittelt naechsten freien IOCB ) ;( ========================================================================= ) NFA_FREEIOCB .CBYTE $88, "FREEIOCB" LDA_FREEIOCB .WORD PREVLINK CFA_FREEIOCB .WORD PFA_FREEIOCB PFA_FREEIOCB JSR FREEIOCB0 LSR A LSR A LSR A LSR A ; div 16 PHA ; iocb und LDA #0 ; highbyte JMP PUSH ; auf den Datenstack legen FREEIOCB0 LDA #$70 ; bei IOCB 7 anfangen L_FREEIOCB2 TAY LDA ICFLG,Y ; Flag holen und CMP #$FF BEQ L_FREEIOCB1 ; testen ob frei (ICFLG = $FF) TYA ; nicht frei! SEC SBC #$10 BNE L_FREEIOCB2 L_FREEIOCB1 TYA RTS PREVLINK .= NFA_FREEIOCB ; >>>>>>>>>>>>>>>> CLOSE-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CLOSE-FILE [fileid -- ior ] Datei schliessen ) ;( ========================================================================= ) NFA_CLOSEFILE .CBYTE $8A, "CLOSE-FILE" LDA_CLOSEFILE .WORD PREVLINK CFA_CLOSEFILE .WORD PFA_CLOSEFILE PFA_CLOSEFILE JSR GETFILEID LDA #IO_CLOSE STA ICCOM,Y JSR JCIOV LDA ICSTA,Y BMI CLOSEFILE1 LDA #0 CLOSEFILE1 PHA LDA #0 ; Store ior highbyte JMP PUT PREVLINK .= NFA_CLOSEFILE ; >>>>>>>>>>>>>>>> CREATE-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( CREATE-FILE [caddr u fam -- ior ] Datei anlegen ) ;( ========================================================================= ) NFA_CREATEFILE .CBYTE $8B, "CREATE-FILE" LDA_CREATEFILE .WORD PREVLINK CFA_CREATEFILE .WORD PFA_CREATEFILE PFA_CREATEFILE JMP NEXT PREVLINK .= NFA_CREATEFILE ; >>>>>>>>>>>>>>>> DELETE-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( DELETE-FILE [caddr u -- ior ] Datei loeschen ) ;( ========================================================================= ) NFA_DELETEFILE .CBYTE $8B, "DELETE-FILE" LDA_DELETEFILE .WORD PREVLINK CFA_DELETEFILE .WORD PFA_DELETEFILE PFA_DELETEFILE JMP NEXT PREVLINK .= NFA_DELETEFILE ; >>>>>>>>>>>>>>>> FLUSH-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( FLUSH-FILE [fileid -- ior ] Dateibuffer schreiben ) ;( ========================================================================= ) NFA_FLUSHFILE .CBYTE $8A, "FLUSH-FILE" LDA_FLUSHFILE .WORD PREVLINK CFA_FLUSHFILE .WORD PFA_FLUSHFILE PFA_FLUSHFILE JMP NEXT PREVLINK .= NFA_FLUSHFILE ; >>>>>>>>>>>>>>>> OPEN-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( OPEN-FILE [caddr u fam -- fileid ior ] Datei oeffnen ) ;( ========================================================================= ) NFA_OPENFILE .CBYTE $89, "OPEN-FILE" LDA_OPENFILE .WORD PREVLINK CFA_OPENFILE .WORD PFA_OPENFILE PFA_OPENFILE JSR FREEIOCB0 ; get free iocb LDA #IO_OPEN STA ICCOM,Y LDA 4,X ; Lowbyte Filename STA ICBAL,Y LDA 5,X ; HighByte Filename STA ICBAH,Y LDA 0,X ; fam STA ICAX1,Y LDA #0 STA ICAX2,Y JSR JCIOV INX INX LDA ICSTA,Y STA 0,X BMI OPENFILE1 DEC 0,X ; ior = 0 = ok OPENFILE1 LDA #0 ; Store ior STA 1,X STA 3,X ; DIV $10 TYA CLC LSR A LSR A LSR A LSR A STA 2,X ; Store fileid JMP NEXT PREVLINK .= NFA_OPENFILE ; >>>>>>>>>>>>>>>> RENAME-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( RENAME-FILE [caddr1 u1 caddr2 u2 -- ior ] Datei umbenennen ) ;( ========================================================================= ) NFA_RENAMEFILE .CBYTE $8B, "RENAME-FILE" LDA_RENAMEFILE .WORD PREVLINK CFA_RENAMEFILE .WORD PFA_RENAMEFILE PFA_RENAMEFILE JMP NEXT PREVLINK .= NFA_RENAMEFILE ; >>>>>>>>>>>>>>>> READ-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( READ-FILE [caddr u1 fileid -- u2 ior ] Datei an Adresse Lesen ) ;( ========================================================================= ) NFA_READFILE .CBYTE $89, "READ-FILE" LDA_READFILE .WORD PREVLINK CFA_READFILE .WORD PFA_READFILE PFA_READFILE JSR GETFILEID LDA 2,X ; get lowbyte length STA ICBLL,Y LDA 3,X ; get highbyte length STA ICBLH,Y LDA 4,X ; get lowbyte address STA ICBAL,Y LDA 5,X ; get highbyte address STA ICBAH,Y LDA #IO_GETCHR ; Get Characters CIO Command STA ICCOM,Y JSR JCIOV INX INX LDA ICSTA,Y STA 0,X BMI READFILE1 DEC 0,X ; OK, ior = 0 READFILE1 CLC LDA ICBLL,Y ; get real length STA 2,X LDA ICBLH,Y ; get real length STA 3,X LDA #0 STA 1,X JMP NEXT PREVLINK .= NFA_READFILE ; >>>>>>>>>>>>>>>> READ-LINE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( READ-LINE [caddr u1 fileid -- u2 flag ior ] Datei zeilenweise lesen ) ;( ========================================================================= ) NFA_READLINE .CBYTE $89, "READ-LINE" LDA_READLINE .WORD PREVLINK CFA_READLINE .WORD PFA_READLINE PFA_READLINE JSR GETFILEID LDA 2,X ; get lowbyte length STA ICBLL,Y LDA 3,X ; get highbyte length STA ICBLH,Y LDA 4,X ; get lowbyte address STA ICBAL,Y LDA 5,X ; get highbyte address STA ICBAH,Y LDA #IO_GETREC ; Get Record CIO Command STA ICCOM,Y JSR JCIOV LDA ICSTA,Y STA 0,X BMI READLINE1 DEC 0,X ; OK, ior = 0 READLINE1 CLC LDA ICBLL,Y ; get real length STA 4,X ADC ICBAL,Y ; calculate end of string STA 2,X DEC 2,X LDA ICBLH,Y STA 5,X ADC ICBAH,Y STA 3,X LDA #0 STA (2,X) STA 1,X STA 2,X ; store flag = true STA 3,X JMP NEXT PREVLINK .= NFA_READLINE ; >>>>>>>>>>>>>>>> REFILL <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( REFILL [ fileid -- flag ] Eingabebuffer fuellen ) ;( ========================================================================= ) ; : REFILL ; TIB @ 128 SOURCE-ID @ READ-LINE SWAP IN ! SWAP DROP ; ; NFA_REFILL .CBYTE $86, "REFILL" LDA_REFILL .WORD PREVLINK CFA_REFILL .WORD DOCOL PFA_REFILL .WORD CFA_TIB .WORD CFA_FETCH ; get Terminal Input Buffer (TIB) .WORD CFA_LIT .WORD $80 ; 80 Char. max .WORD CFA_SOURCEID .WORD CFA_FETCH ; get Channel .WORD CFA_READLINE .WORD CFA_SWAP .WORD CFA_IN .WORD CFA_STORE .WORD CFA_SWAP .WORD CFA_DROP .WORD CFA_EXIT PREVLINK .= NFA_REFILL ; >>>>>>>>>>>>>>>> WRITE-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( WRITE-FILE [caddr u fileid -- ior ] Datei von Adresse schreiben ) ;( ========================================================================= ) NFA_WRITEFILE .CBYTE $8A, "WRITE-FILE" LDA_WRITEFILE .WORD PREVLINK CFA_WRITEFILE .WORD PFA_WRITEFILE PFA_WRITEFILE JSR GETFILEID LDA #IO_PUTCHR ; Put Characters CIO Command STA ICCOM,Y WRITE LDA 2,X ; get lowbyte length STA ICBLL,Y LDA 3,X ; get highbyte length STA ICBLH,Y LDA 4,X ; get lowbyte address STA ICBAL,Y LDA 5,X ; get highbyte address STA ICBAH,Y JSR JCIOV INX INX INX INX LDA ICSTA,Y STA 0,X BMI WRITEFILE1 DEC 0,X ; OK, ior = 0 WRITEFILE1 LDA #0 STA 1,X JMP NEXT PREVLINK .= NFA_WRITEFILE ; >>>>>>>>>>>>>>>> WRITE-LINE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( WRITE-LINE [caddr u fileid -- ior ] Datei zeilenweise schreiben ) ;( ========================================================================= ) NFA_WRITELINE .CBYTE $8A, "WRITE-LINE" LDA_WRITELINE .WORD PREVLINK CFA_WRITELINE .WORD PFA_WRITELINE PFA_WRITELINE JSR GETFILEID LDA #IO_PUTREC ; Put Record CIO Command STA ICCOM,Y JMP WRITE PREVLINK .= NFA_WRITELINE ; >>>>>>>>>>>>>>>> INCLUDE-FILE <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( INCLUDE-FILE [fileid -- ] Liest und interpretiert Datei aus fileid ) ;( ========================================================================= ) ; : INCLUDE-FILE ; SOURCE-ID ! ; BEGIN ; REFILL ; 128 < ; WHILE ; INTERPRET ; REPEAT ; SOURCE-ID @ CLOSE-FILE ; QUIT ; NFA_INCLUDEFILE .CBYTE $8C, "INCLUDE-FILE" LDA_INCLUDEFILE .WORD PREVLINK CFA_INCLUDEFILE .WORD DOCOL PFA_INCLUDEFILE .WORD CFA_SOURCEID ; SOURCE-ID .WORD CFA_STORE ; ! INCLUDEFILE1 ; BEGIN .WORD CFA_REFILL ; REFILL .WORD CFA_LIT ; LITERAL .WORD $80 ; 128 .WORD CFA_LT ; < .WORD CFA_0BRANCH ; WHILE .WORD INCLUDEFILE2-* .WORD CFA_INTERPRET ; INTERPRET .WORD CFA_BRANCH .WORD INCLUDEFILE1-* ; REPEAT INCLUDEFILE2 ; .WORD CFA_SOURCEID ; SOURCE-ID .WORD CFA_FETCH ; @ .WORD CFA_CLOSEFILE ; CLOSE-FILE .WORD CFA_QUIT ; QUIT .WORD CFA_EXIT ; S; PREVLINK .= NFA_INCLUDEFILE ; >>>>>>>>>>>>>>>> INCLUDED <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( INCLUDED [caddr u -- ] Liest und interpretiert Datei - Name an caddr ) ;( ========================================================================= ) ; : INCLUDED ; R/O ; OPEN-FILE ; 128 < IF ; INCLUDE-FILE ; SOURCE-ID @ CLOSE-FILE ; 0 SOURCE-ID ! ( Input-Buffer SOURCE-ID zuruecksetzen ) ; ENDIF NFA_INCLUDED .CBYTE $88, "INCLUDED" LDA_INCLUDED .WORD PREVLINK CFA_INCLUDED .WORD DOCOL PFA_INCLUDED .WORD CFA_RO .WORD CFA_OPENFILE .WORD CFA_LIT .WORD $80 .WORD CFA_LT .WORD CFA_0BRANCH .WORD INCLUDED1-* .WORD CFA_INCLUDEFILE .WORD CFA_SOURCEID .WORD CFA_FETCH .WORD CFA_CLOSEFILE .WORD CFA_0 .WORD CFA_SOURCEID .WORD CFA_STORE INCLUDED1 .WORD CFA_EXIT ; S; PREVLINK .= NFA_INCLUDED ; >>>>>>>>>>>>>>>> FILE" <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( FILE" [ -- u caddr ] Ermittelt Filename im Input Strom ) ;( ========================================================================= ) ; : FILE" ; 1536 BL BLANKS ; HERE BL BLANKS ; 34 WORD HERE COUNT 1536 SWAP CMOVE ; 1536 0 ; NFA_FILE .BYTE $85,"FILE",$A2 LDA_FILE .WORD PREVLINK CFA_FILE .WORD DOCOL PFA_FILE .WORD CFA_LIT ; .WORD 1536 ; $600 .WORD CFA_BL ; BL (32) .WORD CFA_BLANKS ; BLANKS .WORD CFA_HERE ; HERE .WORD CFA_BL ; BL (32) .WORD CFA_BLANKS ; BLANKS .WORD CFA_CLIT .BYTE 34 ; 34 (") .WORD CFA_WORD ; WORD .WORD CFA_COUNT ; COUNT .WORD CFA_LIT .WORD 1536 ; $600 .WORD CFA_SWAP ; SWAP .WORD CFA_CMOVE ; CMOVE .WORD CFA_LIT .WORD 1536 ; $600 .WORD CFA_0 ; 0 .WORD CFA_EXIT ; S; PREVLINK .= NFA_FILE ; >>>>>>>>>>>>>>>> INCLUDE" <<<<<<<<<<<<<<<< ;( ========================================================================= ) ;( INCLUDE" Liest und interpretiert Datei - Name folgt im Input Strom ) ;( ========================================================================= ) ; : INCLUDE" ; FILE" INCLUDED ; ; IMMEDIATE NFA_INCLUDE .BYTE $C8,"INCLUDE",$A2 LDA_INCLUDE .WORD PREVLINK CFA_INCLUDE .WORD DOCOL PFA_INCLUDE .WORD CFA_FILE ; FILE" .WORD CFA_INCLUDED ; INCLUDED .WORD CFA_EXIT ; ;S PREVLINK .= NFA_INCLUDE .ENDIF ; FILE COMMANDS ; >>>>>>>>>>>>>>>> MON <<<<<<<<<<<<<<<< NFA_MON .CBYTE $83, "MON" LFA_MON .WORD PREVLINK CFA_MON .WORD PFA_MON PFA_MON ; Im originalen FIG-Listing wird davon ausgegangen, dass das aufrufende ; Programm ein Monitor ist. STX XSAVE ; Datenstackpointer retten JMP (DOSVEC) ; zum DOS zurueckkehren NOP ; nach BRK wird ein Byte uebersprungen LDX XSAVE ; Datenstackpointer wiederherstellen JMP NEXT ; und fertig NFA_LASTWORD = NFA_MON ;------------------------------------------------------------------------------------------ ; Hilfsroutinen ;------------------------------------------------------------------------------------------ GETCH TYA ; Y Register retten PHA JSR GETCHK TAX PLA ; Y Register wiederherstellen TAY TXA RTS GETCHK LDA $E425 ; PUSH address of GETKEY OS Routine PHA ; to returnstack LDA $E424 PHA RTS OUTCH STX XSAVE TAX TYA ; Y Register retten PHA JSR OUTCHK PLA ; Y Register wiederherstellen TAY LDX XSAVE RTS OUTCHK ; in das ATARI OS springen (CIO Routine) LDA $E407 PHA LDA $E406 PHA TXA RTS .IF debug ; ; This is a temporary trace routine, to be used until FORTH ; is generally operating. Then NOP the terminal query ; "JSR ONEKEY". This will allow user input to the text ; interpreter. When crashes occur, the display shows IP, W, ; and the word locations of the offending code. When all is ; well, remove : TRACE, TCOLON, PRNAM, DECNP, and the ; following monitor/register equates. ; ; ; ; Monitor routines needed to trace. ; ;XBLANK EQU $D0AF ; print one blank ;CRLF EQU $D0D2 ; print a carriage return and line feed. ;HEX2 EQU $D2CE ; print accum as two hex numbers ;LETTER EQU $D2C1 ; print accum as one ASCII character ;ONEKEY EQU $D1DC ; wait for keystroke XW = $43 ; scratch reg. to next code field add NP = $45 ; scratch reg. pointing to name field ; ; DEBUGFLG .BYTE 0 ; debugflg DXSAVE .BYTE 0 ; X Register Sicherungsplatz TRACE STX DXSAVE ; X Register Retten PHA ; Accu retten TYA PHA ; Y Register retten JSR CRLF LDA IP+1 JSR HEX2 LDA IP JSR HEX2 ; print IP, the interpreter pointer JSR XBLANK ; ; LDY #0 LDA (IP),Y STA XW STA NP ; fetch the next code field pointer INY LDA (IP),Y STA XW+1 STA NP+1 JSR PRNAM ; print dictionary name ; LDA XW+1 JSR HEX2 ; print code field address LDA XW JSR HEX2 JSR XBLANK ; LDX DXSAVE ; print TOS Cell LDA 1,X JSR HEX2 LDX DXSAVE LDA 0,X JSR HEX2 JSR XBLANK ; LDA DXSAVE ; print stack location in zero-page JSR HEX2 JSR XBLANK ; LDA #1 ; print return stack bottom in page 1 JSR HEX2 TSX INX TXA JSR HEX2 JSR XBLANK ; .IF keywait JSR ONEKEY ; wait for operator keystroke .ENDIF LDX DXSAVE ; just to pinpoint early problems PLA ; Y Register wiederherstellen TAY PLA ; Accu wiederherstellen RTS ; ; TCOLON is called from DOCOLON to label each point ; where FORTH 'nests' one level. ; TCOLON STX DXSAVE ; X Register retten PHA ; Accu retten TYA PHA ; Y Register retten LDA W STA NP ; locate the name of the called word LDA W+1 STA NP+1 JSR CRLF LDA #$3A ; ': JSR LETTER JSR XBLANK JSR PRNAM ;@ JSR ONEKEY ; wait for operator keystroke LDX DXSAVE ; X Register weiderherstellen PLA TAY ; Y Register wiederherstellen PLA ; Accu wiederherstellen RTS ; ; Print name by it's code field address in NP ; PRNAM ysave = $3FF JSR DECNP JSR DECNP JSR DECNP LDY #0 PN1 JSR DECNP LDA (NP),Y ; loop till D7 in name set BPL PN1 PN2 INY LDA (NP),Y STY YSAVE JSR LETTER ; print letters of name field LDY YSAVE LDA (NP),Y BPL PN2 JSR XTAB LDY #0 RTS ; ; Decrement name field pointer ; DECNP LDA NP BNE DECNP1 DEC NP+1 DECNP1 DEC NP RTS ; ;********************************************************** ; Monitor routines needed to trace. ; ; print a carriage return and line feed. ; CRLF LDA #155 JMP OUTCH ; print one blank ; XBLANK LDA #$20 JMP OUTCH ; print one TAB ; XTAB LDA #127 JMP OUTCH ; print accum as two hex digits HEX2 PHA LSR A LSR A LSR A LSR A JSR HEX2A PLA HEX2A AND #$0F JSR HXDGT JMP OUTCH ; ;convert hex digit to ASCII ; HXDGT CMP #$0A BCC HXDGT1 CLC ADC #7 HXDGT1 ADC #'0 RTS ; ; print accum as one ASCII character ; LETTER AND #$7F CMP #$20 ;compare ASCII space BCS LETTER1 ;good if >= ' ' LDA #'. LETTER1 JMP OUTCH ; ; wait for keystroke ; ONEKEY JMP GETCH .ENDIF ; DEBUG TOP .END ; Ende des Assemblerlistings * = $2E0 .WORD ORIG