;
;    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/>.
;

; Speicherzugriffe aller Art

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "move"
       ; ( Quelladdr Zieladdr Byteanzahl -- )
       ; Kopiert einen Datensatz an eine neue Stelle.
       ; Kann vorwärts und rückwärts kopieren.
;------------------------------------------------------------------------------
  push r10
  push r11

  popda r7  ; Count
  popda r11 ; Zieladresse
  popda r10 ; Quelladresse

  tst r7    ; Prüfe, ob die Anzahl der zu kopierenden Bytes Null ist.
  je ++     ; Wenn ja, bin ich fertig.

  ; Mindestens ein Byte ist noch zu kopieren.

  cmp r11, r10 ; Quelle und Ziel vergleichen
  jhs +

  add r7, r10
  add r7, r11

- mov.b @r10, @r11 ; Von der Quelladresse an die Zieladresse kopieren
  dec r10  ; Quelladresse erniedrigen
  dec r11  ;  Zieladresse erniedrigen
  dec r7   ; Zahl der noch zu kopierenden Bytes erniedrigen
  jnz -    ; Sind noch welche übrig ?

  jmp ++


/ mov.b @r10+, @r11 ; Von der Quelladresse an die Zieladresse kopieren
           ; Quelladresse erhöhen
  inc r11  ;  Zieladresse erhöhen
  dec r7   ; Zahl der noch zu kopierenden Bytes erniedrigen
  jnz -    ; Sind noch welche übrig ?

+ pop r11
  pop r10
  ret

  ;------------------------------------------------------------------------------
  Wortbirne Flag_opcodierbar_spezialfall, "cbit@" ; Testet Bit(s) ( Maske addr -- Flag )

  popda r7 ; Adresse holen
  bit.b @r4, @r7 ; Bits prüfen

  jmp + ; Der Teil ist genauso wir in bit@.

  ret ; Wird nicht aufgerufen, nur als Ende-Markierung, damit die Spezialfallopcodierung den Anhang findet !
  ;------------------------------------------------------------------------------
      ; Einsprung im Spezialfall: Kompilieren mit mindestens einer Konstanten.
      ; Hier könnte ich einfach einen Opcode bereitlegen, der dann in beiden Zweigen verwendet wird.

      mov #0B0E2h, r12  ; r12 ist standardmäßig beim Opcodiereinsprung gesichert !
      ; bis #00400h für bit.b @r4, &Adresse = 0B4E2h
      ; bis #00010h für bit.b @pc+, &Adresse = 0B0F2h
      jmp bitfetch_opcodierung

;------------------------------------------------------------------------------
  Wortbirne Flag_opcodierbar_spezialfall, "bit@" ; Testet Bit(s) ( Maske addr -- Flag )

  popda r7 ; Adresse holen
  bit @r4, @r7 ; Bits prüfen
+

bitfetch_flagsetzer:
  bit #z, sr
  subc @r4, @r4
  ret
  ;------------------------------------------------------------------------------
      ; Einsprung im Spezialfall: Kompilieren mit mindestens einer Konstanten. Benötigt 88 Bytes :-/

      mov #0B0A2h, r12  ; r12 ist standardmäßig beim Opcodiereinsprung gesichert !
      ; bis #00400h für bit @r4, &Adresse = 0B4A2h
      ; bis #00010h für bit @pc+, &Adresse = 0B0B2h

bitfetch_opcodierung:

      cmp #1, r11 ; Genau eine ?
      jne +
        ; Nur die Adresse ist festgelegt.
        bis #00400h, r12  ; Opcode umschalten
        pushda r12
        call #doppelkomma ; Opcode und Adresse schreiben.
        ; Null Konstanten übrig.
        clr &konstantenfaltungszeiger
        jmp bitfetch_flagsetzerschreiben

+     ; Mindestens zwei Konstanten. Adresse und Bitmaske sind festgelegt.
      bis #00010h, r12

          to_r ; Konstanten auf den Returnstack verschieben
          to_r
          decd r11
          call #interpret_konstantenschreiben
          call #zwei_r_from ; Zurückholen und SWAP durchführen !

      pushda #08324h ; decd r4
      call #komma

      ; Habe nun zwei Konstanten in der richtigen Reihenfolge bereitliegen.

      call #pushda_r12_konstantenopcodierer ; Opcode+Bitmaske schreiben
      call #komma                           ; Adresse schreiben

bitfetch_flagsetzerschreiben:
      pushda #bitfetch_flagsetzer ; Schreibt die Sequenz  bit #z, sr
      br #inlinekomma             ;                       subc @r4, @r4

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "cxor!" ; Wechselt Bits ( n addr -- )

  popda r7 ; Adresse holen
  xor.b @r4, @r7 ; Bits setzen
  drop
  ret
  .word 0e0f2h ; Opcode ! xor.b #Konstante, &Adresse. Für die Wirkung xor.b @r4+, &Adresse muss ich e4f2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "xor!" ; Wechselt Bits ( n addr -- )

  popda r7 ; Adresse holen
  xor @r4+, @r7 ; Bits setzen
  ret
  .word 0e0b2h ; Opcode ! xor #Konstante, &Adresse. Für die Wirkung xor @r4+, &Adresse muss ich e4b2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "cbic!" ; Löscht Bits ( n addr -- )

  popda r7 ; Adresse holen
  bic.b @r4, @r7 ; Bits setzen
  drop
  ret
  .word 0c0f2h ; Opcode ! bic.b #Konstante, &Adresse. Für die Wirkung bic.b @r4+, &Adresse muss ich c4f2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "bic!" ; Löscht Bits ( n addr -- )

  popda r7 ; Adresse holen
  bic @r4+, @r7 ; Bits setzen
  ret
  .word 0c0b2h ; Opcode ! bic #Konstante, &Adresse. Für die Wirkung bic @r4+, &Adresse muss ich c4b2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "cbis!" ; Setzt Bits ( n addr -- )

  popda r7 ; Adresse holen
  bis.b @r4, @r7 ; Bits setzen
  drop
  ret
  .word 0d0f2h ; Opcode ! bis.b #Konstante, &Adresse. Für die Wirkung bis.b @r4+, &Adresse muss ich d4f2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "bis!" ; Setzt Bits ( n addr -- )

  popda r7 ; Adresse holen
  bis @r4+, @r7 ; Bits setzen
  ret
  .word 0d0b2h ; Opcode ! bis #Konstante, &Adresse. Für die Wirkung bis @r4+, &Adresse muss ich d4b2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "2constant" ; ( d -- ) Legt eine Doppelt-Konstante an
;------------------------------------------------------------------------------
  call #create
  call #dliteralkomma
  jmp +

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "constant" ; ( n -- ) Legt eine Konstante an
                                     ; : constant create literal, ret, smudge ;
;------------------------------------------------------------------------------
  call #create
  call #literalkomma

+ call #retkomma

setze_faltbarflag_und_smudge:
  call #setze_faltbarflag
  br #smudge

; Variable im Flash:
; pushda #Adresse ret Initialisierungswert

; Variable im Ram:
; pushda #Adresse ret Wert

; Nur mit dem kleinen Unterschied, dass die Adresse bei der Flash-Variablen irgendwo im Ram liegt
; und der Initialisierungswert dahin kopiert wird.

; Somit ist das Anlegen der Variablen selbst eigentlich in beiden Fällen identisch.

; Um Ram-Speicher zu bekommen, markiere das Wort als Flag_ramallot|Größe in Words.
; und dekrementiere den VariablenPointer entsprechend. Prüfe, ob noch genug Speicher da ist.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "2variable" ; ( ZahlL ZahlH -- )
  ; Legt eine initialisierte Doppelt-Variable an
;------------------------------------------------------------------------------
  pushda #2
  jmp +

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "variable" ; ( Zahl -- )
  ; Legt eine initialisierte Variable an
;------------------------------------------------------------------------------
  pushda #1
  jmp +

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "nvariable" ; ( Init-Werte Länge -- )
; Legt eine initialisierte Variable gewünschter Länge an
;------------------------------------------------------------------------------
+ call #create

  ; Auf dem Stack: ( Init-Werte Länge )
  ; Bereite den Code vor, lege die Init-Werte hintenan.

  ifdef flashdictionaryeinbinden
    cmp #nBacklinkgrenze, &DictionaryPointer
    jlo nvariable_fuer_ram

    ifdef ramverwaltungeinbinden
      ; Eine echte Flash-Variable entsteht so, dass Platz im Ram angefordert wird.
      ; Prüfe hier, ob genug Ram da ist !?

      and #000Fh, @r4 ; Maximal mögliche Länge für Flash-Variablen !

      mov #Flag_ramallot, &FlashFlags ; Mache dies NUR beim Schreiben in Flash.
      bis @r4, &FlashFlags ; Merke vor, wie viele Words initialisiert werden sollen.

      sub @r4, &VariablenPointer
      sub @r4, &VariablenPointer  ; Doppelt so viele Bytes abziehen wie Word-Werte kommen.

      pushda &VariablenPointer ; Ram-Adresse für die kommende Variable.
      jmp nvariable_codeschreiben

    else ; ramverwaltungeinbinden
      writeln "Cannot define variables in Flash"
      br #quit
    endif
  endif ; flashdictionaryeinbinden

nvariable_fuer_ram:
  pushda &DictionaryPointer ; Dies ist die Adresse, die für Ram-Variablen benutzt wird.
  add #10, @r4 ; Zeiger auf die Zahl in der Speicherstelle, die genau 10 Bytes später kommt.

  ; ( Initialisierungswerte Länge Ramadresse )
nvariable_codeschreiben:  ; Hier lassen sich für nicht-Flash-Variablen noch einige Befehle aussortieren.
  push r10
  push r11
  mov @r4, r11 ; Zieladresse holen

  call #literalkomma ; Zieladresse einkompilieren
  call #retkomma

  ; ( Initialisierungswerte... Länge )
  ; Passende Anzahl von Initialisierungswerten schreiben und einpflegen.

  popda r10 ; Hole Länge
  tst r10
  jz +     ; Falls nvariable mit Länge Null aufgerufen wird... Das könnte durchaus sinnvoll sein...
  ; ( Initialisierungswerte )

- mov @r4, @r11  ; Mit dieser Methode werden RAM-Variablen zweimal initialisiert... Aber das macht ja nichts :-)
  incd r11
  call #komma ; Zahl bzw. Initialisierungswert einfügen.
  dec r10
  jnz -

+ pop r11
  pop r10

  jmp setze_faltbarflag_und_smudge

  ; Das führt dazu, dass Variablen Ramallot & Faltbarkeit gesetzt haben.
  ; macht nichts, da das Ramallot-Flag zuerst geprüft wird.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "+!" ; Plus-Store ( n addr -- )

  popda r7 ; Adresse holen
  add @r4+, @r7 ; Hinzuzählen
  ret
  .word 50b2h ; Opcode ! add #Konstante, &Adresse. Für die Wirkung add @r4+, &Adresse muss ich 54b2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "2!" ; Store ( d addr -- )
    popda r7 ; Hole die Adresse
    popda @r7  ; High-Word an Adresse
    incd r7
    popda @r7  ; Low-Word an Adresse+2 gemäß fig/ans
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "2@" ; Fetch ( addr -- d )
    mov @r4, r7  ; Hole die Adresse
    decd r4
    mov @r7+, @r4
    mov @r7, 2(r4)
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "c!" ; c-Store ( c addr -- )
    popda r7  ; Hole die Adresse
    mov.b @r4, @r7 ; Lege den gewünschten Inhalt hinein.
    drop
  ret
  .word 40f2h ; Opcode ! mov.b #Konstante, &Adresse. Für die Wirkung mov.b @r4+, &Adresse muss ich 44f2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherlesen, "c@" ; c-Fetch ( addr -- c )
c_fetch:
    mov @r4, r7  ; Hole die Adresse
    mov.b @r7, r7 ; Hole den Inhalt der Adresse
    mov r7, @r4  ; Lege den addressierten Inhalt auf den Stack
  ret
  .word 42d4h ; Opcode ! mov.b &Adresse, 0(r4). Muss im Opcodierfall vorher noch decd r4 = $8324 einfügen.
              ;          High-Byte im Datenstack bleibt ungesetzt. Lösche ihn mit clr.b 1(r4)

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherschreiben, "!" ; Store ( n addr -- )
    popda r7  ; Hole die Adresse
    popda @r7 ; und lege den gewünschten Inhalt hinein.
  ret
  .word 40b2h ; Opcode ! mov #Konstante, &Adresse. Für die Wirkung mov @r4+, &Adresse muss ich 44b2 benutzen.

;------------------------------------------------------------------------------
  Wortbirne Flag_visible|Flag_opcodierbar_speicherlesen, "@" ; Fetch ( addr -- n )
    mov @r4, r7  ; Hole die Adresse
    mov @r7, @r4 ; Lege den addressierten Inhalt auf den Stack
  ret
  .word 4294h ; Opcode ! mov &Adresse, 0(r4). Muss im Opcodierfall vorher noch decd r4 = $8324 einfügen.
