;
;    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 Datei, in der Forh für den echten Microcontroller vorbereitet wird.
; Hier werden die Terminalroutinen eingebunden, die jeweils passend sind,
; hier geschieht die Hardwareinitialisierung und
; hier wird die Speicherkarte aufgeteilt und festgelegt.

 cpu msp430 
;------------------------------------------------------------------------------
; Grundlegende Definitionen
;------------------------------------------------------------------------------ 
 include "mspregister.asm"
 include "datenstack.asm"

 ; Eine Probe für meinen Emulator, ob er auch tut, was er soll !
 ; Entstanden am 20. Juni 2009 bei Sonnenschein und Regenschauern
 ; Fertig am 5. August 2011 bei genauso abwechslungsreichem Wetter :-)

;------------------------------------------------------------------------------
; Stell ein, wieviel Speicher du hast !
;------------------------------------------------------------------------------


  ; Wie viel Platz soll für den Kernel reserviert werden ? Muss an einer 512-Byte-Grenze liegen.

  ;org 0F800h ;  2   kb ; 201x-Chips

  ;org 0E000h ;  8   kb ; Wie beim f1232, 2452...
  ;org 0DE00h ;  8.5 kb
  ;org 0DC00h ;  9   kb Flash für Kernel
  ;org 0DA00h ;  9.5 kb
  org 0D800h ; 10 kb 

  ;org 0D400h ; 11 kb
  ;org 0D000h ; 12 kb  
  ;org 0C000h ; 16 kb Erstmal mit ein bisschen weniger Platz auskommen.

kernelstartadresse equ $
 
 padding on ; Bei Strings muss manchmal noch ein Füllbyte eingesetzt werden, 
            ; damit der nächste Befehl brav bei einer geraden Adresse beginnt ! 

;------------------------------------------------------------------------------
; Für welchen Chip soll assembliert werden ?
;------------------------------------------------------------------------------

;msp430f2274 equ 1
msp430g2553 equ 1

;------------------------------------------------------------------------------
; Triff eine Wahl aus den vorhandenen Möglichkeiten !
;------------------------------------------------------------------------------

doppeltzahleneinbinden equ 1         ; Bindet Doppelt-Zahlen ein. 614 Bytes

fixkommarechnungeneinbinden equ 1    ; Bindet Fixkommazahlen ein. ( Benötigt Doppeltzahlen )  786 Bytes

;kapitelbirneneinbinden equ 1         ; Bindet die Kapitelüberschriften ein. Süße Tunichguts.
                                     ; Sehen hübsch aus, brauchen aber Platz. 582 ... 704 ... 744 Bytes

leaveeinbinden          equ 1        ; Leave ist ein bisschen kompliziert in ANS... 104 Bytes

spezialstrings          equ 1        ; s" für ANS - brauche ich das überhaupt je einmal ? 56 Bytes
                                     ; Inklusive cmove und Rausführung von stringvergleich und stringueberlesen. 108 Bytes

;spruengerausfuehren     equ 1        ; Sollen die internen Sprünge von außen zugänglich sein ? 
                                     ; Praktisch zum Bauen eigener Kontrollstrukturen !
                                     ; Kostet 168 Bytes für Proben und Wortbirnen.

unicodebeachten equ 1                ; Soll Query sich um Unicode kümmern ? 24 Bytes
caseinsensitive equ 1                ; Soll Forth Groß- und Kleinschreibung unterscheiden ?
                                     ; Achtung: Nur a-z wird beachtet, Umlaute nicht. Kostet 32 Bytes.

;kurzezahlen equ 1                    ; Wenn dies aktviert wird, ist eine kompilierte Zahl 4 Bytes groß, aber langsam.
                                     ; Ansonsten werden Zahlen direkt und schnell eingefügt und kosten 8 Bytes. 
                                     ; Kostet nur 2 Bytes extra im Flash.

                                     ; Die Konstantenfaltung ist immer aktiviert, sie ist ein Grundbestandteil geworden.
                                     ; Darauf bauen einige neue Konzepte auf, darf sie nicht mehr rausnehmen !
                                     ; So werden Rechenoperationen auf Konstenten bereits zur Kompilierzeit ausgeführt
                                     ; und wichtige Befehle werden opcodiert. Kostete mal 584 Bytes. 

;------------------------------------------------------------------------------
; Routinen für die Hardware, ohne weitere Abhängigkeiten
;------------------------------------------------------------------------------

analogeinbinden equ 1                ; Eine ganz kleine und einfache Routine, um den ADC10 zu benutzen. 70 Bytes

;------------------------------------------------------------------------------
; Kompilation ins Flash und dessen Feinheiten
;------------------------------------------------------------------------------

flashdictionaryeinbinden equ 1       ; Wollen wir das Flashdictionary benutzen ?
                                     ; Das führt zum Einbinden von einigen Extraroutinen
                                     ; und dem Umkehren der Suchrichtung.

;flashkompilationsstart equ 1         ; Soll zu Beginn in Flash kompiliert werden ?

ramverwaltungeinbinden equ 1         ; Sollen Flashprogramme Ram reservieren können ? Kostet 96 Bytes.

initialisiereflashvariablen equ 1    ; Sollen Flashvariablen initialisiert werden ? Kostet 38 Bytes.

Anfangswortname equ "init"           ; Dies Wort wird automatisch beim Start gesucht und ausgeführt. 42 Bytes.

;------------------------------------------------------------------------------
; Routinen für die Hardware. Sie brauchen initialisierte Variablen.
;------------------------------------------------------------------------------

taktlauschereinbinden equ 1          ; Kommunikation über eine Leuchtdiode. 772, jetzt 624 Bytes extra - Wow !
                                      ; Belegt zusätzlich Ram !

terminalhooks equ 1                  ; Soll es möglich sein, key ?key und emit umzuleiten ? 124 Bytes
                                     ; Die müssen nochmal GRÜNDLICH getestet werden !

;interruptwoertereinbinden equ 1      ; Sollen Interruptwörter definiert werden können ? 110 Bytes.
                                     ; Die Sorte bekommt direkt reti-Rahmen und wird fest in IRQ-Tabelle gebrannt.
                                     ; Problem: Vektoren bleiben beim Löschen des Flash-Dictionarys. Problematisch.

interrupthookseinbinden equ 1        ; Eine ganz neue Variante von IRQ-Hooks, sehr komfortabel ! 298 Bytes ! 
                                     ; Braucht Ramverwaltung und initialisierte Flashvariablen.


;------------------------------------------------------------------------------
; Aufteilung des Speichers - je nach Chip und Funktionen unterschiedlich.
; Hier können Puffergrößen angepasst werden.
;------------------------------------------------------------------------------

rampointer set 0200h ; Ram-Anfang setzen

ramallot macro Name, Menge         ; Für Variablen und Puffer zu Beginn des Rams, die im Kern verwendet werden sollen.
Name equ rampointer                ; Uninitialisiert.
rampointer set rampointer + Menge
         endm

  ramallot Base, 2                 ; Variable mit der Zahlenbasis
  ramallot State, 2                ; Variable mit dem Zustand Kompiliert (192) (true) / Executiert (0) (false)
  ramallot Datenstacksicherung, 2  ; Datenstacksicherung, für die Probe, ob der Stack aufgeräumt worden ist.
  ramallot Dictionarypointer, 2    ; Zeiger für das Dictionary im RAM
  ramallot FadenEnde, 2            ; Einsprung für die Wortsuche
  ramallot konstantenfaltungszeiger, 2 ; Ermöglicht die Bestimmung, wie viele Konstanten gerade auf dem Datenstack liegen.

  ifdef leaveeinbinden
    ramallot leavestackpointer, 2
  endif

  ifdef flashdictionaryeinbinden
    ; Gibt es nicht mehr. Wurde ersetzt durch eine Probe des DictionaryPointers. ramallot WritetoFlash, 2
    ramallot ZweitDictionaryPointer, 2
    ramallot ZweitFadenEnde, 2
    ramallot FlashFlags, 2
    ifdef ramverwaltungeinbinden
      ramallot VariablenPointer, 2 ; Ganz neu !
    endif
  endif

  ifdef taktlauschereinbinden ; Hier die Variablen, die im Kern des Taktlauschers benötigt werden.
    ; Word-Variablen   
    ramallot Lichtmuster_low, 2 ; Der aktuellere Teil der Lichtmustermessungen
    ramallot Datenregister, 2   ; Die hineinkommenden Daten werden hier hineinrotiert
    ramallot Sendedaten, 2      ; Datenbits zum Herausrotieren und Abstrahlen

    ; Byte-Variablen
    ramallot Verbindungsdauer, 1 ; Enthält einen Wert, der angibt, wie lange die Verbindung schon besteht.
                                 ; Erst nach dem in Synchron_Dauer. ungestörtem Durchlauf wird mit dem Senden begonnen,
                                 ; weil auch ein plötzlicher Umgebungslichtwechsel die Probesequenz erfüllen könnte.
    ramallot Strahlzaehler, 1    ; Zähler, der die noch auszugebenden Pulse merkt
    ramallot Lauschzaehler, 1    ; Zähler, der weiß, wie oft noch zu Lauschen ist, bevor es hell werden muss
    ramallot Taktlauscherzustand, 1 ; Ein Bitmuster des Zustandes, Bit 0: 0=Dunkel, 1=Hell
  endif ; Taktlauschereinbinden

Zahlenpufferlaenge equ 35 ; 16 Bits, 1 Vorzeichen, 1 Leerzeichen + 1 Reserve = 19 Bytes + 1 Längenbyte
  ramallot Zahlenpuffer, Zahlenpufferlaenge + 1  ; Mit fixkomma müssen es 35 Bytes sein. 

; Lege den Datenstack so, dass er auf beiden Seiten von "unkritischen" Puffern flankiert wird.
; So erzeugt ein Unter- oder Überlauf nicht gleich einen Absturz.

; Stacks sind von der Sorte: Erst erniedrigen, dann reinschreiben.
datenstacklaenge equ 64 ; 256
  ramallot datenstackende, datenstacklaenge
datenstackanfang equ datenstackende + datenstacklaenge

  ramallot Eingabepuffergesamtlaenge, 1
MaximaleEingabe equ 120 ; Länge des Pufferinhaltes + 1 Längenbyte ! Hier gerade, mit dem Platz davor.
  ramallot Eingabepuffer, MaximaleEingabe + 1

returnstacklaenge equ 64 ; 256
  ramallot returnstackende, returnstacklaenge
returnstackanfang equ returnstackende + returnstacklaenge

nRamDictionaryAnfang equ rampointer

;nRamDictionaryEnde   equ 4000h ; Für den Emulator, viel Ram !
  ifdef msp430f2274
nRamDictionaryEnde   equ 0600h ; 0600h ; Ende ist bei 05FFh + 1. Anfang meist 0200h.
  endif
  ifdef msp430g2553
nRamDictionaryEnde   equ 0400h
  endif



  ifdef flashdictionaryeinbinden

  ifdef msp430f2274
nFlashDictionaryAnfang equ 08000h  ; 32k Flash im Chip.
  endif
  ifdef msp430g2553
nFlashDictionaryAnfang equ 0C000h  ; 16k Flash im Chip.
  endif
  
nFlashDictionaryEnde   equ kernelstartadresse ; Ende + 1. Forth sitzt zwischen A000 und FFFF.

nBacklinkgrenze equ nFlashDictionaryAnfang ; Normalerweise.
  endif  

;------------------------------------------------------------------------------
; Meldungen. Die Makros werden hier definiert, damit sie früh und überall zur Verfügung stehen.

write macro Text           ; Mag keine runden Klammern...
  call #dotgaensefuesschen
  .byte STRLEN(Text), Text
  align 2
  endm
  
writeln macro Text         ; Mag keine runden Klammern...
  call #dotgaensefuesschen
  .byte STRLEN(Text)+1, Text, 10
  align 2
  endm

;------------------------------------------------------------------------------
; Einbinden der hardwareabhängigen Teile des Terminals.

 include "serialterminal.asm"
 ;include "irdaterminal.asm"

;------------------------------------------------------------------------------
Reset:      ; Hauptprogramm
;------------------------------------------------------------------------------

  ; Grundsätzliche Initialisierung
  mov   #WDTPW+WDTHOLD, &WDTCTL   ; Watchdog aus
  mov   #returnstackanfang, SP    ; Stackpointer setzen.
  mov   #datenstackanfang,  r4    ; Pointer für den Datenstack setzen.

  
  ifdef terminal_init ; Das ist wichtig ! Darin passiert auch die Hardwareinitialisierung.
    call #terminal_init ; Danach werden noch einige interne Sachen initialisiert, Zeit, um Takt zu stabilisieren...
  endif

  ;ifdef taktlauschereinbinden ; Das kann ich auch selbst später setzen.
    ; mov.b #1, &Taktlauscherzustand ; Am Anfang hell lauschen
  ;endif
    
  ; Hier sind sooo viele Abhängigkeiten von den Optionen...
  ; Aber all diese Varianten haben sehr verschiedene Initialisierungsbedürfnisse.

  ifdef kurzezahlen
    mov #zahlenleger, r9 ; Dies ist die Einsprungadresse für kompilierte Zahlen ! Register NIE ändern !
  endif

  ifndef flashdictionaryeinbinden
    mov #nRamDictionaryAnfang, &Dictionarypointer         ; Es gibt nur die Rampointer.
    mov #CoreFadenende, &Fadenende
    br #quit

  else

    ; Muss die Pointer immer anfangs suchen !
    call #SucheFlashPointer ; Aktualisiert Fadenende, DictionaryPointer, VariablenPointer
    
    ifdef flashkompilationsstart
      ; mov #-1, &WritetoFlash                              ; Schreibe in Flash
      mov #nRamDictionaryAnfang, &ZweitDictionaryPointer    ; Pointer für Ram vormerken
      mov #CoreDictionaryAnfang, &ZweitFadenEnde            ; für späteres umschalten
    else
      ; mov #0, &WritetoFlash                               ; Schreibe in Ram
      mov &DictionaryPointer, &ZweitDictionaryPointer        ; Die gefundenen Flashzeiger merken !
      mov &FadenEnde, &ZweitFadenende
      mov #nRamDictionaryAnfang, &DictionaryPointer       ; Setze Pointer für Ram direkt.
      mov #CoreDictionaryAnfang, &FadenEnde
    endif

    ; Erstmal alles initialisieren, dann geht es los.
    ; Nein ! eint ; Interrupts noch nicht einschalten

    writeln "Mecrisp - MSP430 Forth by Matthias Koch"

    ifndef Anfangswortname ; Suche nach einen Wort, das zu Beginn ausgeführt wird.
      br #quit
    else
      
      ; Initialisierungen analog zu dem, was normalerweise in quit geschieht.
      mov #10, &Base ; Dezimal.  
      clr &state ; Bin am Ausführen

      clr &konstantenfaltungszeiger ; Im Falle eines Fehlers ist der Datenstack und damit alle Konstanten darin weg.

      ifdef leaveeinbinden                      ; Schleifenstrukturen gehen bei Quit verloren, klar.
        mov #datenstackende, &leavestackpointer ; Lege den Leavestack als von unten nach oben wachsenden Stack
      endif  
        
      pushda #Anfangswortnamensstring     
      call #wortsuche
      tst 2(r4) ; Wurde das Wort gefunden ?
      jnz +    
      br #quit ; Nicht gefunden: Brauche Stack nicht aufräumen, das tut Quit für mich.

      ; Gefunden:
+     drop       ; Flags runterwerfen
      call @r4+  ; Gefundenes Wort ausführen
      br #quit_inneneinsprung ; Denn ich möchte den Zustand nach dem Anfangswort erhalten !
      
Anfangswortnamensstring: .byte STRLEN(Anfangswortname), Anfangswortname
  align 2

    endif ; Anfangswortname

  endif ; flashdictionaryeinbinden

;------------------------------------------------------------------------------
  include "anfang.asm"  ; Einbinden von allem, was Forth ausmacht.
;------------------------------------------------------------------------------
  
; Gutenmorgen:
;  writeln "Schönen guten Morgen, liebe Eskimos, Nachtschulabsolventen"
;  writeln "und zahlreiche andere Daseinsformen !"
;  writeln "Ihr könnt hier viel lernen ! Dies ist eine Probe, ob etwas gesendet werden kann."
;  writeln "Mecrisp - Forth für MSP430 von Matthias Koch"
;  writeln "Mecrisp MSP430 Forth by Matthias Koch"
;  ret
  
;Pointersuchegutgegangen:
;  writeln "Pointer gefunden. Los geht's !"
;  ret  
  
;------------------------------------------------------------------------------
null_handler: ; Fängt unbenutzte Interrupts auf
;------------------------------------------------------------------------------
            ;call #licht ; Zeigt vagabundierende Einsprünge an !
            reti

;------------------------------------------------------------------------------
;           Interruptvektoren des F2013
;------------------------------------------------------------------------------


  ifdef interrupthookseinbinden

 org 0FFE0h ; Interrupttabelle mit eigenen Routinen

vec_01:        .word   null_handler         ; 0FFE0  Im 2274 unbelegt
vec_02:        .word   null_handler         ; 0FFE2  Im 2274 unbelegt
vec_Port1:     .word   irq_vektor_port1     ; 0FFE4  Port 1
vec_Port2:     .word   irq_vektor_port2     ; 0FFE6  Port 2
vec_05:        .word   null_handler         ; 0FFE8  Im 2274 unbelegt.
vec_ADC:       .word   irq_vektor_adc       ; 0FFEA  Für das Sammeln von Werten aus dem AD-Wandler: Temperatur und Vcc !
vec_07:        .word   null_handler         ; 0FFEC  USCI Transmit  - Hierrüber läuft das Terminal.
vec_08:        .word   null_handler         ; 0FFEE  USCI Receive   - Werde sie also nicht benutzen.
vec_TimerA1:   .word   irq_vektor_timera1   ; 0FFF0  Für den Überlauf-Interrupt in der Zeitnehmer-Routine. 
vec_TimerA0:   .word   irq_vektor_timera0   ; 0FFF2  Für das Wecken nach einer Pause und den Taktlauscher
vec_Watchdog:  .word   irq_vektor_watchdog  ; 0FFF4  Für die Zeitzählung

  ifdef msp430g2553
vec_Comp:      .word   irq_vektor_comp      ; 0FFF6  Im 2553 Komparantor
  else
vec_12:        .word   null_handler         ; 0FFF6  Im 2274 unbelegt
  endif

vec_TimerB1:   .word   irq_vektor_timerb1   ; 0FFF8  Überlauf-Interrupt
vec_TimerB0:   .word   irq_vektor_timerb0   ; 0FFFA  Normales Wecken
vec_NMI:       .word   null_handler         ; 0FFFC  Vorsichtshalber erstmal nicht rausgeführt.
vec_RES:       .word   Reset                ; 0FFFE  Für den Start

  else

 org 0FFE0h ; Interrupttabelle, falls nichts beachtet werden soll.

vec_01:        .word   null_handler  ; 0FFE0
vec_02:        .word   null_handler  ; 0FFE2
vec_Port1:     .word   null_handler  ; 0FFE4
vec_Port2:     .word   null_handler  ; 0FFE6
vec_05:        .word   null_handler  ; 0FFE8
vec_ADC:       .word   null_handler  ; 0FFEA
vec_07:        .word   null_handler  ; 0FFEC
vec_08:        .word   null_handler  ; 0FFEE
vec_TimerA0:   .word   null_handler  ; 0FFF0
vec_TimerA1:   .word   null_handler  ; 0FFF2
vec_Watchdog:  .word   null_handler  ; 0FFF4
vec_12:        .word   null_handler  ; 0FFF6
vec_TimerB0:   .word   null_handler  ; 0FFF8
vec_TimerB1:   .word   null_handler  ; 0FFFA
vec_NMI:       .word   null_handler  ; 0FFFC
vec_RES:       .word   Reset         ; 0FFFE  Für den Start

  endif

end


; Wie ist dies in den Chip zu bekommen ?
; asl forth-mecrisp.asm && p2hex forth-mecrisp.p -r 0x0000-0xffff
; mecrisp2 hex forth-1232.hex  
  