;
;    Mecrisp - A native code Forth implementation for MSP430 microcontrollers
;    Copyright (C) 2011  Matthias Koch
;
;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;    (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
;

      
; Die Routinen, die nötig sind, um neue Definitionen zu kompilieren.      

;------------------------------------------------------------------------------
  Kapitelbirne_flash "--- Dictionary erweitern ---"  
;------------------------------------------------------------------------------ 
      
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, ","
komma: ; ( n -- ) Fügt ein Word an das Dictionary an.
;------------------------------------------------------------------------------

  ifdef flashdictionaryeinbinden
    cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
    jhs +                                   ; jnz +
    ; Für Ram
      push r10  
      mov &DictionaryPointer, r10
      mov @r4+, @r10
      pop r10
      jmp ++
+   ; Für Flash
      pushda &DictionaryPointer
      call #flashstore
+
  else
    push r10  
    mov &DictionaryPointer, r10
    mov @r4+, @r10
    pop r10
  endif
  
  pushda #2
  br #allot

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "c,"
ckomma: ; ( c -- ) Fügt ein Byte an das Dictionary an.
;------------------------------------------------------------------------------
  ifdef flashdictionaryeinbinden
    cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
    jhs +                                   ; jnz +
    ; Für Ram
      push r10  
      mov &DictionaryPointer, r10  
      mov.b @r4, @r10
      incd r4    
      pop r10
      jmp ++
+   ; Für Flash
      pushda &DictionaryPointer
      call #flashCstore
+
  else
    push r10  
    mov &DictionaryPointer, r10  
    mov.b @r4, @r10
    incd r4    
    pop r10
  endif

  pushda #1
  br #allot  
  
;------------------------------------------------------------------------------
;  Wortbirne Flag_sichtbar, ",grade"
  Wortbirne Flag_sichtbar, "align,"
kommagrade: ; Macht den Dictionarypointer mit den Hinzufügen 
            ; einer Null wieder gerade.
;------------------------------------------------------------------------------
  bit #1, &DictionaryPointer
  jnc +
  pushda #0
  call #ckomma         
+ ret
    
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "string,"
kommastring: ; ( Stringadresse -- )
             ; Fügt einen String an das Dictionary an und macht den Zeiger gerade.
;------------------------------------------------------------------------------
  push r10
  push r11
  mov   @r4+, r10  ; Stringadresse holen
  mov.b @r10, r11  ; Längenbyte des Strings
  
  inc r11          ; Einen mehr, weil das Längenbyte auch ein Byte lang ist
  
  ; Schreibe nun r11 Bytes in das Dictionary.
- decd r4
  mov.b @r10+, @r4 ; Byte holen
  call #ckomma
  dec r11
  jnz -

  pop r11
  pop r10

  br #kommagrade

  ifdef kurzezahlen

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "literal,"
literalkomma: ; ( n -- ) Kompiliert eine Zahl ins Dictionary
              ;          mit einem mysteriösen call-ins-Register.
              ;          Sehr langsam, aber nur 4 Bytes pro Zahl.
;------------------------------------------------------------------------------ 
    pushda #1289h ; Opcode für call r8 (1288h) ( call r9 wäre 1289h )
    call #komma

    br #komma ; Zahl einkompilieren

zahlenleger:  ; Springe hier rein, wenn eine Zahl inline folgt.
              ; Auf dem Returnstack liegt die Rücksprungadresse. 
              ; --> An der Stelle liegt die Zahl, die ich einfügen möchte.
              ; Lege sie auf den Datenstack und erhöhe den Rücksprungzeiger !
  
  decd r4  ; Platz schaffen auf dem Datenstack
  push r10 ; Register sichern und Platz für die Adresse schaffen

  mov 2(sp), r10 ; Adresse des Rücksprunges = der Zahl holen
  mov @r10, @r4  ; Zahl auf Datenstack legen
  incd 2(sp)     ; Rücksprung korrigieren

  pop r10 ; Register zurückholen
  ret

  else ; kurzezahlen
                    
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "literal,"
literalkomma: ; ( n -- ) Kompiliert eine Zahl ins Dictionary
              ;          mit dem pushda-Code
              ;          Benötige so 8 Bytes pro Zahl.
;------------------------------------------------------------------------------  
  pushdadouble #40B4h, #8324h
  ;pushda #8324h ; Code für decd r4 = sub #2, r4
  call #komma
  
  ;pushda #40B4h ; Code für mov #Zahl, 0(r4)
  call #komma
  
  call #komma   ; Zahl einkompilieren
  
  pushda #0000h ; Code für die 0 in 0(r4)
  br #komma  

  endif

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "inline,"
inlinekomma: ; ( Einsprungadresse -- ) Bruder von "call,"
             ; Hängt die Routine, die an der Einsprungadresse steht,
             ; bis zum ersten Auftreten des ret-Opcodes an das Dictionary an.
             ; inlinekomma sucht bis zum retopcode und schreibt die Routine einfach binär um.
             ; Da es sich um position independent code handelt, ist das möglich. Alle Sprünge sind relativ;
             ; call-Einsprünge bleiben im Dictionary an ihrer alten Position. 
             ; Einzige Einschränkungen: Innere Aussprünge (mehrere ret), oder retopcodes als Strings oder Literals.
             ; Da inline aber nur für die allerkürzesten Routinen verwendet wird, sollte das kein Problem darstellen.
;------------------------------------------------------------------------------
  ; Auf dem Stack ist die Adresse des Codes, der bis zum ersten ret übernommen werden soll.
  push r10
  push r11

  popda r10 ; Codeadresse

- mov @r10+, r11 ; Inhalt in r11 holen
  cmp #4130h, r11 ; Ret-Opcode ?
  je +            ; Wenn ja, fertig.

  pushda r11
  call #komma
  jmp -
  
+ ; Ret wurde gefunden. Fertig !
  pop r11
  pop r10
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "call,"
callkomma:    ; ( n -- ) Kompiliert einen Subroutinenaufruf ins Dictionary
              ;          mit dem call-Code
;------------------------------------------------------------------------------ 
  pushda #12B0h ; Code für call #Adresse
  call #komma
  
  br #komma   ; Adresse einkompilieren
  
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "ret,"
retkomma:    ; Kompiliert einen Rücksprungbefehl ins Dictionary
;------------------------------------------------------------------------------     
  pushda #4130h ; Code für ret = mov @r1+, r0
  br #komma  

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "allot" ; Erhöht den Dictionaryzeiger, schafft Platz !
allot:  ; Überprüft auch gleich, ob ich mich noch im Ram befinde. 
        ; Ansonsten verweigtert Allot seinen Dienst.
;------------------------------------------------------------------------------  
  push r10
  mov &DictionaryPointer, r10
  add @r4, r10
  
  ifdef flashdictionaryeinbinden
  
  cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
  jhs +                                   ; jnz +

  ifdef ramverwaltungeinbinden
    cmp &VariablenPointer, r10
  else
    cmp #nRamDictionaryEnde, r10
  endif
  jlo ++  ; Solange der DictionaryPointer innerhalb des RAMs ist, ist alles okay.
          ; Oder außerhalb des Variablenbereichs

  ;writeln " Die Grenze des Ram-Dictionarys wurde überschritten !"
  writeln " Ram full"
  pop r10
  br #quit    

+ cmp #nFlashDictionaryEnde, r10
  jlo +  ; Solange der DictionaryPointer innerhalb des RAMs ist, ist alles okay.
  ;writeln " Die Grenze des Flash-Dictionarys wurde überschritten !"
  writeln " Flash full"
  pop r10
  br #quit    

  else

  cmp #nRamDictionaryEnde, r10
  jlo +  ; Solange der DictionaryPointer innerhalb des RAMs ist, ist alles okay.
  writeln " Ram full"
  pop r10
  br #quit      

  endif
    
+ pop r10
  add @r4+, &DictionaryPointer
  ret

;------------------------------------------------------------------------------  
;  Wortbirne Flag_sichtbar, "fadenende" ; ( -- Fadenende  ) liefert den Anfang des zuletzt definierten Wortes zurück
;------------------------------------------------------------------------------    
;  pushda &Fadenende ; Nicht mehr herausgeführt, wird eigentlich nur intern benutzt.
;  ret  
               
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "here" ; Liefert den aktuellen Zeiger ins Dictionary
;------------------------------------------------------------------------------     
  pushda &DictionaryPointer
  ret                

  ifdef 0 ; Das braucht Platz, und ein Druck auf die Reset-Taste erledigt das auch.
  ifdef flashdictionaryeinbinden

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "forget" ; Vergisst den Inhalt des Ram-Dictionarys. 68 Bytes
;------------------------------------------------------------------------------           
  cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
  jlo +                                   ; jz +

  mov #nRamDictionaryAnfang, &ZweitDictionaryPointer       ; Setze Pointer für Ram direkt.
  mov #CoreDictionaryAnfang, &ZweitFadenEnde
  
  jmp ++

+ mov #nRamDictionaryAnfang, &DictionaryPointer       ; Setze Pointer für Ram direkt.
  mov #CoreDictionaryAnfang, &FadenEnde

+ writeln "Ram gelöscht."
  br #quit    

  else

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "forget" ; Vergisst den Inhalt des Ram-Dictionarys. 46 Bytes
;------------------------------------------------------------------------------           
  mov #nRamDictionaryAnfang, &Dictionarypointer
  mov #CoreFadenende, &Fadenende
  writeln "Ram gelöscht."
  br #quit

  endif
  endif

  ifdef 0 ; Drück den Knopf doch selbst !
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "reset" ; Startet den Chip ganz neu. Vergisst Ram. 14 Bytes.
;------------------------------------------------------------------------------           
  clr &WDTCTL
  endif

  ifdef flashdictionaryeinbinden

  ifdef 0
;------------------------------------------------------------------------------
zweitpointertausch: ; Kleines Helferlein, das Pointerpaare vertauscht.
  ; Tausche die Pointer aus

  push r10
    mov &DictionaryPointer, r10
    mov &ZweitDictionaryPointer, &DictionaryPointer
    mov r10, &ZweitDictionaryPointer

    mov &Fadenende, r10
    mov &ZweitFadenende, &FadenEnde
    mov r10, &ZweitFadenEnde
  pop r10
  ret
  

  push &DictionaryPointer
  mov &ZweitDictionaryPointer, &DictionaryPointer
  pop &ZweitDictionaryPointer
  
  push &FadenEnde
  mov &ZweitFadenEnde, &FadenEnde
  pop &ZweitFadenEnde
  ret

  endif

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "compiletoram" ; Ändert das Schreibziel !
compiletoram:
;------------------------------------------------------------------------------     
  cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
  jlo +                                   ; jz +
 
  ; writeln " Kompiliere fortan ins RAM !"
  call #zweitpointertausch ; Tauscht die Pointer  
  ; mov #0, &WritetoFlash    ; Ändert den Zustand

  ifdef ramverwaltungeinbinden
    cmp &VariablenPointer, &DictionaryPointer
    jlo +
   ;writeln "Variablen kollidieren mit Ram-Dictionary !"
   writeln " Variables collide with dictionary !"
  endif

+ ret   
  
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "compiletoflash" ; Ändert das Schreibziel !
compiletoflash:
;------------------------------------------------------------------------------     
  cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
  jhs +                                   ; jnz +
 
  ; writeln " Kompiliere fortan ins Flash !"
  ;call #zweitpointertausch ; Tauscht die Pointer  
  ; mov #-1, &WritetoFlash   ; Ändert den Zustand  

zweitpointertausch: ; Kleines Helferlein, das Pointerpaare vertauscht.
  ; Tausche die Pointer aus
  push &DictionaryPointer
  mov &ZweitDictionaryPointer, &DictionaryPointer
  pop &ZweitDictionaryPointer
  
  push &FadenEnde
  mov &ZweitFadenEnde, &FadenEnde
  pop &ZweitFadenEnde

+ ret   

  endif ; flashdictionaryeinbinden

;------------------------------------------------------------------------------
  Kapitelbirne "--- Dictionary erweitern ---"  
;------------------------------------------------------------------------------    

;------------------------------------------------------------------------------
  Kapitelbirne_flash "--- Links & Flags ---"  
;------------------------------------------------------------------------------    


;------------------------------------------------------------------------------  
;  Wortbirne Flag_sichtbar, "einsprungadresse" Nicht mehr rausgeführt. Warum auch ?
einsprungadresse: ; ( Fadenende -- Einsprungadresse )
                ; Ermittelt aus einem Fadenende eine Einsprungadresse
;------------------------------------------------------------------------------    

  inc @r4                  ; Flags
  call #stringueberlesen   ; Name
  incd @r4                 ; Link  überlesen
  ret

; Vorher 8238 Bytes, nachher: 8220 Bytes :-)

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "smudge"
smudge: ; Macht das zuletzt geschaffene Wort sichtbar
; Wird von dodoes, ;, constant und variable benutzt.
; Neuigkeiten:
; Smudge brennt die Flags, die in FlashFlags gesammelt worden sind.
; Auf diese Weise kann ich alles mit nur einem Schreibzugriff erledigen,
; habe als Kehrseite aber nicht mehr die Möglichkeit, immediate NACH dem 
; gerade definierten Wort aufzurufen, sondern muss das innendrin tun.
;------------------------------------------------------------------------------ 
  ; Smudge muss am Ende im Ram einfach nur dafür sorgen, dass das Wort sichtbar wird.
  ; Im Flash muss Smudge das Ende auf $FFFF prüfen, gegebebenfalls eine Null anhängen 
  ; und die gesammelten Flags brennen.

  ifdef flashdictionaryeinbinden

    push r10 ; Wird immer gebraucht !

    cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
    jhs smudge_flash                        ; jnz smudge_flash

    ; Im Ram ist nicht viel zu tun !
    mov #Flag_sichtbar, r10
    jmp flagsetzer

smudge_flash:
    ; Im Flash ist mehr Arbeit vor uns.
    ; Prüfe, ob am Ende des Wortes ein $FFFF steht. Das darf nicht sein ! 
    ; Es würde als freie Stelle erkannt und später überschrieben werden.
    ; Deshalb wird in diesem Fall hier am Ende eine 0 ans Dictionary angehängt.

    mov &DictionaryPointer, r10
    cmp #-1, -2(r10) ; Denn der Dictionarypointer zeigt auf die nächste freie Stelle !
    jne +
    pushda #0
    call #komma
+   ; Habe nun das Ende auf jeden Fall sauber abgeschlossen.

    pop r10 ; Der Register ist nun wieder frei.

    ; Setze die Flags.
    push &DictionaryPointer
    mov &FadenEnde, &DictionaryPointer
    pushda &FlashFlags
    call #ckomma
    pop &DictionaryPointer
    ret

  else ; flashdictionaryeinbinden - Nur die Routine für Ram

    push r10
    mov #Flag_sichtbar, r10
    jmp flagsetzer

  endif

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, "inline"
; inline: ; Macht das zuletzt geschaffene Wort inline
; Wird vom Assembler nie aufgerufen
; Vorsicht, Neuerung: Für das Flash-Dictionary
;  musste ich dies immediate machen, und es muss INNERHALB
;  des zu beeinflussenden Wortes aufgerufen werden !
;------------------------------------------------------------------------------       
  push r10
  mov #Flag_inline, r10
  jmp flagsetzer

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, "immediate"
; immediate: ; Macht das zuletzt geschaffene Wort immediate
; Wird vom Assembler nie aufgerufen
; Vorsicht, Neuerung: Für das Flash-Dictionary
;  musste ich dies immediate machen, und es muss INNERHALB
;  des zu beeinflussenden Wortes aufgerufen werden !
;------------------------------------------------------------------------------      
  push r10
  mov #Flag_immediate, r10
  jmp flagsetzer ; Läuft direkt hinein.
  
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "setflags"
  push r10
  popda r10

flagsetzer: ; Hilft bei den Flags und ihrem Setzen.
            ; Ist nicht herausgeführt, spart aber Platz.
            ; Erwartet Einsprung mit jmp/br, Flagmuster in r10, vorher gesichert.
;------------------------------------------------------------------------------
  ifdef flashdictionaryeinbinden

    cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
    jhs flagsetzer_flash                    ; jnz flagsetzer_flash

    push r11
    mov &Fadenende, r11
    cmp.b #-1, @r11
    jne +
    mov.b r10, @r11
    jmp ++
+   bis.b r10, @r11
+   pop r11

    jmp flagsetzer_fertig

flagsetzer_flash:
    bis.b r10, &FlashFlags

flagsetzer_fertig:
    pop r10
    ret

  else ; Nur die Routine für Ram

    push r11
    mov &Fadenende, r11
    cmp.b #-1, @r11
    jne +
    mov.b r10, @r11
    jmp ++
+   bis.b r10, @r11
+   pop r11
    pop r10
    ret  

  endif

  ifdef kapitelbirneneinbinden
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, "Kapitel{"
 ; Fügt dem Dictionary einen Kapitelheader hinzu.
;------------------------------------------------------------------------------  
  pushda #125 ; '}'
  call #parse
  push r10
  jmp +
  endif

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "create"
create: ; Nimmt das nächste Token aus dem Puffer,
        ; erstellt einen neuen Kopf im Dictionary und verlinkt ihn.
;------------------------------------------------------------------------------  
  push r10
  
  call #token  ; Hole den Namen der neuen Definition. 
  ; ( Tokenadresse )  
  
  ; Überprüfe, ob der Token leer ist. 
  ; Das passiert, wenn der Eingabepuffer nach create leer ist.
  
+ mov   @r4, r10
  mov.b @r10, r10
  tst r10
  jne +
  
  ; Token ist leer.  
  drop ; Tokenadresse vergessen, Stack wieder leer.
  ;writeln "Create benötigt den Namen des neuen Wortes !"
  ;writeln "Create braucht Namen !"
  writeln " Create needs name !"
  
  pop r10
  ret
  
+ ; Tokenname ist okay.
  ; Prüfe, ob er schon existiert.
  
  ; ( Tokenadresse )
  dup
  ; ( Tokenadresse Tokenadresse )
  call #wortsuche
  ; ( Tokenadresse Einsprungadresse Flags )
  drop ; Benötige die Flags hier nicht. Möchte doch nur schauen, ob es das Wort schon gibt.
  ; ( Tokenadresse Einsprungadresse )
  popda r10
  ; ( Tokenadresse )
  tst r10 ; Prüfe, ob die Suche erfolgreich gewesen ist.
  je +
  
  ;write "Definiere "
  write "Redefine "
  dup          ; ( Tokenadresse Tokenadresse )
  call #schreibestringmitlaenge   ; Den neuen Tokennamen nochmal ausgeben  
  ;writeln " erneut."
  writeln "."
  
+ ; ( Tokenadresse )
  
  ;write "Definiere *"
  ;dup          ; ( Tokenadresse Tokenadresse )
  ;call #schreibestringmitlaenge   ; Den neuen Tokennamen nochmal ausgeben
  ;writeln "* zum ersten Mal."  
  

  push &DictionaryPointer

  pushda #Flag_unsichtbar ; $FF 
  call #ckomma            ; Im Flash wird NIE $FF geschrieben - und im RAM ist das kein Problem.
  
  ifdef flashdictionaryeinbinden
    mov #Flag_sichtbar, &FlashFlags   ; Für Flash
  endif
      
  ; Stattdessen ist der String schon im Stack. ;  pushda #Createname
  call #kommastring

  ifdef flashdictionaryeinbinden ; Hier wird entschieden, ob Links oder Backlinks geschrieben werden.

  cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
  jhs +                                   ; jnz +
    ; Für RAM - Links
    pushda &FadenEnde
    call #komma
    jmp ++

+   ; Für Flash - Backlinks

  ; Hier wird nun der Backlink gesetzt/vorbereitet.
  pushda #2
  call #allot ; 2 Bytes Platz für den Backlink einplanen.
  
  ; Backlink im letzten Wort in die Lücke setzen:
  ; Dies ist eine Berechnung der Adresse des Backlinkfeldes des letzten Wortes.
    pushda &Fadenende        ; Aus dem letzten Fadenende
    call #einsprungadresse   ; Bestimme ich durch Überlesen des Namens und der Links die Einsprungadresse
    popda r10                ; die hole ich mir wieder
    decd r10                 ; und weiß, dass 2 Bytes davor der Backlink liegt.
    cmp #0FFFFh, @r10        ; Ist der Backlink ungesetzt ?
    jne +                    ; Wenn ja,

    ;pushda @sp               ; so wird das aktuelle Fadenende, das eben erschaffen wurde dort eingesetzt.
    ;pushda r10
    pushdadouble @sp, r10
    call #flashstore         ; ehemals mov @sp, @r10
+    
  
  else ; Falls nur ins Ram kompiliert wird
    pushda &FadenEnde
    call #komma
  endif

  pop &FadenEnde ; Der vorhin gesicherte Dictionarypointer ist das neue Fadenende.
    
  pop r10
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "find"
Wortsuche: ; ( Addr -- Einsprungadresse Flags ) 
           ; Nimmt die Adresse von einem String entgegen
           ; und prüft, ob der String im Wörterbuch gefunden wird.
           ; Die Adresse kann direkt angesprungen werden, falls gefunden wurde,
           ; ansonsten enthält sie eine Null, wenn das Wörterbuch
           ; erfolglos durchsucht wurde.
           
           ; Liefert aus Gründen der Nützlichkeit auch gleich
           ; die Flags des gefundenen Wortes mit.
;------------------------------------------------------------------------------ 
  ; ( String -- )
  push r10  ; Hangelpointer
  push r11  ; Flags
  push r12  ; Zieladresse
  push r13  ; Zielflags
  push r14  ; Zwischenspeicher für die Namensadresse

  ifdef flashdictionaryeinbinden
  cmp #nBacklinkgrenze, &DictionaryPointer ; tst &WritetoFlash
  jhs +                                   ; jnz +
  ; Fürs Ram:
    mov &Fadenende, r10
    jmp ++
+ ; Für den Flash
    mov #CoreDictionaryAnfang, r10
+
  else
    mov &Fadenende, r10
  endif

  clr r12   ; Noch keinen Treffer
  clr r13   ; Und noch keine Trefferflags

  ; Auf dem Datenstack die ganze Zeit ( Stringadresse -- )

  ; Aufbau: Flags     Namenslänge
  ;         Name...   0
  ;         Link (zum Nachbarwort - nach vorn oder nach hinten ist egal)
  ;         Code

-
  ifdef flashdictionaryeinbinden
    ; Ist an der Stelle der Flags und der Namenslänge $FFFF ? Dann ist der Faden abgelaufen.
    cmp #0FFFFh, @r10
    je ++ ; Sprung ans Ende
  endif

  ; Habe einen Dictionaryeintrag gefunden !
  mov.b @r10+, r11 ; Flags merken
  
  mov r10, r14 ; Namensadresse für den eventuellen Vergleich vormerken.
  pushda r10
  call #stringueberlesen
  popda r10    ; Zeiger auf Linkfeld vorrücken.  

  ; ( Stringadresse )

  ; Prüfe Sichtbarkeit. Unsichtbar -->                  Stringüberlesen, Weiterhangeln.     Brauche r11 dann nicht mehr.
  ;                       Sichtbar --> Stringvergleich, Stringüberlesen, Treffer markieren.   

  ; Prüfe Sichtbarkeit:
  cmp.b #-1, r11 ; Im Falle von $FF Unsichtbarkeit vermerken.
  jc + ; Weiterhangeln.
              
  ; Mache den Stringvergleich nur bei sichtbaren Wörtern.   
    dup ; Bereit machen zum Stringvergleich
    ; ( Stringadresse Stringadresse )
    pushda r14
    ; ( Stringadresse Stringadresse AktuellerName )
    call #stringvergleich
    ; ( Stringadresse )
  jnc + ; Weiterhangeln.
  
     
    ; Treffer:
    mov r10, r12 ; Adresse vermerken
    incd r12     ; Und den Linkfeld-Zeiger zum Code inkrementieren.
    mov r11, r13 ; Flags vermerken
    ; Jetzt kommt die Probe: Backlinks oder nicht ? 
    ; Entweder fertig - oder weiterhangeln.

    ifdef flashdictionaryeinbinden
      ;bit #Pruefflag_backlink, r11
      ;jnc ++ ; Wenn keine Backlinks vorliegen, bin ich fertig.
      cmp #nBacklinkgrenze, r12 ; Backlinks kann ich auch anhand der Adresse erkennen.
      jlo ++  ; Wenn (im Ram) keine Backlinks vorliegen, bin ich fertig.
    else
      jmp ++ ; Bei normalen Links immer beim ersten Treffer fertig.
    endif

+ ; Weiterhangeln
    
  ; Prüfe, ob der Link schmackhaft und gut ist.
  cmp #0FFFFh, @r10  ; -1 markiert Ende
  je +

  ; Folge dem Link
  mov @r10, r10
  jmp -

+ ; Ende
  mov r12, @r4 ; Aur dem Datenstack liegt noch die Stringadresse - Kann sie nun ersetzen.
  pushda r13   ; Die Flags des Treffers schreiben

  pop r14
  pop r13
  pop r12
  pop r11
  pop r10
  ret


;------------------------------------------------------------------------------
  Kapitelbirne "--- Links & Flags ---"  
;------------------------------------------------------------------------------    

;------------------------------------------------------------------------------
  Kapitelbirne_flash "--- Compiler ---"  
;------------------------------------------------------------------------------ 

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_inline, "execute" ; Führt aus, worauf der Datenstack zeigt.
;------------------------------------------------------------------------------ 
  call @r4+
  ret            

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, "recurse" ; Für Rekursion.
                                      ; Führt das gerade frische Wort aus.
  ; ACHTUNG: Dies ist NICHT position independent !
;------------------------------------------------------------------------------ 
  call #qcomp
  pushda &Fadenende
  call #einsprungadresse
  br #callkomma
;  ret   

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "'" ; Sucht das nächste Wort im Eingabestrom                               
tick: ; ( -- Adresse )
;------------------------------------------------------------------------------ 
  call #token
  ; ( Pufferadresse )
  call #wortsuche
  ; ( Einsprungadresse Flags )
  drop  
  ; ( Einsprungadresse )
  tst @r4
  jne +

  mov #Eingabepuffer, @r4 ; Ehemals Stringpuffer
  call #schreibestringmitlaenge
  ;writeln " nicht gefunden."
  writeln " not found."
  br #quit  

+ ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, "postpone" ; Sucht das nächste Wort im Eingabestrom
                               ; und compiliert es ins Dictionary.
                               ; Das brauche ich, um immediate-Wörter einzukompilieren.
                               ; postpone stattdessen ?
;------------------------------------------------------------------------------     
  call #tick
  br #callkomma
  ; ret
  
  ; Neue Variante: Inline.
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_inline, "does>"
does: ; Fügt dem gerade erstellten Wort eine besondere Aufgabe hinzu.
;------------------------------------------------------------------------------
    ; An die Stelle, wo does> aufgerufen wird, wird ein Sprung in dodoes eingefügt
    ; und gleich dahinter inline ein R> um die Parameteradresse aus dem Einsprung
    ; auf den Datenstack zu legen. Dieser Teil wird nur ein einziges Mal beim
    ; Eincompilieren eines neuen Defining-Wortes angesprungen.
    call #dodoes
    r_from         ; Dies ergibt das inline >R im Wort.
  ret ; Ganz wichtig, da does> jetzt inline ist !
  

dodoes:

;;------------------------------------------------------------------------------
;  Wortbirne Flag_sichtbar, "does>"
;does: ; Fügt dem gerade erstellten Wort eine besondere Aufgabe hinzu.
;; Diese Variante kam ohne immediate-Flag aus, benutzt dafür ein zusätzliches R>.
;;------------------------------------------------------------------------------
  ; Ich habe nun ein Wort vorbereitet, das alle möglichen Daten enthalten kann.
  ; Die Definition ist fertig und abgeschlossen, nur die Call-Adresse fehlt noch.
  
  ; Doch wohin soll das call springen ? Auf den Teil, der nach does> kommt.
  ; Den kann ich über die Rücksprungadresse finden.
  ; Da does> der Ausführung ein Ende setzt, nehme ich die Rücksprungadresse,
  ; die in das Call eingefügt wird komplett vom Stack. 
  ; Das hat in etwa die Wirkung, als würde das Wort, was does> aufgerufen hat
  ; selbst ein ret ausführen.
  
  ; Gucke mal in den Stack:
  ; ( Rücksprung-des-Wortes-das-does>-aufgerufen-hat  Rücksprungadresse-von-does>-selbst )
    
  push &DictionaryPointer
  ; ( Rücksprung-des-Wortes-das-does>-aufgerufen-hat  Rücksprungadresse-von-does>-selbst AlterDictionaryPointer )
  ; Hole die Adresse, an die die Call-Adresse eingefügt werden soll:
  pushda &Fadenende        ; Das aktuelle Wort soll den does>-Teil-Call bekommen
  call #einsprungadresse   ; Berechne aus dem Fadenende die Einsprungstelle
  mov @r4, &DictionaryPointer ; An dieser Stelle soll der Call-Befehl eingefügt werden !  
  mov 2(sp), @r4 ; Zieladresse für den Call-Befehl auf den Datenstack legen
  call #callkomma ; Call-Befehl mit Zieladresse einschreiben
  pop &DictionaryPointer
  ; ( Rücksprung-des-Wortes-das-does>-aufgerufen-hat  Rücksprungadresse-von-does>-selbst)   
  incd sp ; Rücksprungadresse-von-does>-selbst vom Returnstack schmeißen
  ; ( Rücksprung-des-Wortes-das-does>-aufgerufen-hat ) 
  br #smudge  ; Markiere das neue Wort als Ausführbereit  
;  ret

  ; Das Call, dessen Adresse nachträglich eingefügt wurde, kehrt übringens nie zurück.
  ; Die Rücksprungadresse wird als Zeiger auf das Parameterfeld eines tatsächlich definierten Wortes
  ; verwendet und dort inline vom Returnstack auf den Datenstack geschoben. 
  ; Das ret am Ende der Routine nach does> durchspringt ebenfalls eine Ebene.

  
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "<builds"
builds: ; Beginnt ein Defining-Wort.
        ; Dazu lege ich ein neues Wort an, lasse eine Lücke für den Call-Befehl
        ; Keine Strukturkennung
;------------------------------------------------------------------------------              
  call #create  ; Neues Wort wird erzeugt
  ; Hier kommt ein Call-Befehl hinein, aber ich weiß die Adresse noch nicht.
  ; Lasse also eine passende Lücke frei !
  pushda #4
  br #allot

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "state"
;------------------------------------------------------------------------------              
  pushda #state
  ret
            
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, "]" ; In den Compile-Modus übergehen
  mov #-1, &state ; true-Flag in State legen
  ret
    
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, "[" ; In den Execute-Modus übergehen
  clr &state ; false-Flag in State legen.
  ret

; Denke immer an das erste Beispiel:
; Pufferanfang: string "create sqr ] dup u* drop 1 + [ ret, smudge 1 2 + sqr . wortliste"      
; Damit fing am 24.Februar 2010 alles an.

; Vereinfacht sich hiermit zu:
; Pufferanfang: string ": sqr dup u* drop 1 + ; 1 2 + sqr . wortliste"
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar_immediate, ";"
definitionsende: ; Schließt eine Definition ab und macht sie sichtbar.
;------------------------------------------------------------------------------  
  call #qcomp
  call #pruefestackvergleich
  ;writeln "Definitionsende"
  clr &state ; false-Flag in State legen --> Execute-Modus
  call #retkomma
  br #smudge
  
;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar, ":"
definitionsanfang: ; Holt vom Eingabepuffer einen neuen Namen und
                   ; eröffnet eine Colon-Definition
;------------------------------------------------------------------------------  
  call #setzestackvergleich 
  ;writeln "Definitionsanfang"   
  call #create    ; Wortnamen einkompilieren
  mov #-1, &state ; true-Flag in State legen --> Compile-Modus  
  ret  

;------------------------------------------------------------------------------
  Kapitelbirne "--- Compiler ---"  
;------------------------------------------------------------------------------ 