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

 ; Entstanden am 20. Juni 2009 bei Sonnenschein und Regenschauern
 ; Fertig am 5. August 2011 bei genauso abwechslungsreichem Wetter :-)
 ; Danach noch viele, viele Erweiterungen und Verbesserungen !

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

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

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

  ; 0E000h:  8   kb
  ; 0DE00h:  8.5 kb
  ; 0DC00h:  9   kb
  ; 0DA00h:  9.5 kb
  ; 0D800h: 10   kb
  ; 0D600h: 10,5 kb
  ; 0D400h: 11   kb
  ; 0D200h: 11.5 kb
  ; 0D000h: 12   kb

  org 0DC00h ;  9 kb Flash für Kernel

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 ?
;------------------------------------------------------------------------------

msp430g2553 equ 1

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

doppeltzahleneinbinden equ 1         ; Bindet Doppelt-Zahlen ein.

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

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

spezialstrings          equ 1        ; s" für ANS - brauche ich das überhaupt je einmal ?
                                     ; Inklusive move und Rausführung von compare und skipstring.

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

caseeinbinden equ 1                  ; Soll die CASE-Struktur eingebunden werden ?

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

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

;------------------------------------------------------------------------------
; 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 ?

initialisiereflashvariablen equ 1    ; Sollen Flashvariablen initialisiert werden ?

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

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

;taktlauschereinbinden equ 1         ; Kommunikation über eine Leuchtdiode.
                                     ; Belegt zusätzlich Ram !

;terminalhooks equ 1                 ; Soll es möglich sein, key ?key und emit umzuleiten ?

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

wakeupeinbinden equ 1                ; Zum Einschlafen und Wecken

;------------------------------------------------------------------------------
; 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 leavepointer, 2
  endif

  ifdef flashdictionaryeinbinden
    ramallot ZweitDictionaryPointer, 2
    ramallot ZweitFadenEnde, 2
    ramallot FlashFlags, 2
    ifdef ramverwaltungeinbinden
      ramallot VariablenPointer, 2
    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 0400h

  ifdef flashdictionaryeinbinden
nFlashDictionaryAnfang equ 0C000h  ; 16k Flash im Chip.
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-lowpower.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 flashdictionaryeinbinden     ; Muss die Pointer fürs Dictionary anfangs belegen !
    include "catchflashpointers.asm" ; Aktualisiert Fadenende, DictionaryPointer, VariablenPointer:
  else                               ; Im Ram allein ist das einfacher:
    mov #nRamDictionaryAnfang, &Dictionarypointer
    mov #CoreFadenende, &Fadenende
  endif

  ; Erstmal alles andere initialisieren, dann geht es los.

  ; 8 MHz ! Achtung: Flash-Timing muss bei Änderungen auch angepasst werden !
  ; Momentaner Teiler 19+1 --> 400kHz. Muss im Bereich 257 kHz bis 476 kHz liegen.

  mov.b &CALBC1_8MHZ, &BCSCTL1   ; Set DCO
  mov.b &CALDCO_8MHZ, &DCOCTL    ;   to 8 MHz.

  mov.b #006h, &P1SEL            ; Use P1.1/P1.2 for USCI_A0
  mov.b #006h, &P1SEL2           ; Use P1.1/P1.2 for USCI_A0

  ;------------------------------------------------------------------------------
  ; Initialisierung für normale serielle Schnittstelle
  bis.b   #UCSSEL_1,&UCA0CTL1     ; ACLK
  mov.b   #3,&UCA0BR0             ; 32768 Hz 9600 Insgesamt 3
  mov.b   #0,&UCA0BR1             ; 
  mov.b   #UCBRS_3,&UCA0MCTL      ; Modulation UCBRSx = 3
  bic.b   #UCSWRST,&UCA0CTL1      ; **Initialize USCI state machine**
  ;------------------------------------------------------------------------------

  ifdef flashdictionaryeinbinden
    clr.b &IE1 ; Lösche die Interrupt-Flags von Oscillator Fault, NMI, Flash-Violation.
    mov.w #FWKEY, &FCTL1            ; Schreib- & Löschzugriffe ausgeschaltet.
    mov.w #FWKEY|FSSEL_1|19, &FCTL2 ; MCLK/20 for Flash Timing Generator
  endif

  eint ; Bevor ich das allererste Losschicke schalte ich die Interrupts ein,
       ; damit die Terminalroutinen weniger Strom verbrauchen.

  writeln "Mecrisp 1.1 for G2553 Lowpower by Matthias Koch"

  ifdef flashdictionaryeinbinden
  ifdef Anfangswortname

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

    pushda #Anfangswortnamensstring
    call #wortsuche
    tst 2(r4) ; Wurde das Wort gefunden ?
    jz +
    ; 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

+  ; Nicht gefunden: Brauche Stack nicht aufräumen, das tut Quit für mich.
  endif ; Anfangswortname
  endif

  br #quit

;------------------------------------------------------------------------------
  include "forth-core.asm"  ; Einbinden von allem, was Forth ausmacht.
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
null_handler: ; Fängt unbenutzte Interrupts auf
;------------------------------------------------------------------------------
  reti

;------------------------------------------------------------------------------
;           Interruptvektoren des F2553
;------------------------------------------------------------------------------


  ifdef interrupthookseinbinden

 org 0FFE0h ; Interrupttabelle mit eigenen Routinen

  .word   null_handler         ; 01: 0FFE0  Unbelegt
  .word   null_handler         ; 02: 0FFE2  Unbelegt
  .word   irq_vektor_port1     ; 03: 0FFE4  Port 1
  .word   irq_vektor_port2     ; 04: 0FFE6  Port 2
  .word   null_handler         ; 05: 0FFE8  Unbelegt
  .word   irq_vektor_adc       ; 06: 0FFEA  Für das Sammeln von Werten aus dem AD-Wandler: Temperatur und Vcc !
  .word   irq_wecken_tx        ; 07: 0FFEC  USCI Transmit  - Hierrüber läuft das Terminal.
  .word   irq_wecken_rx        ; 08: 0FFEE  USCI Receive   - Werde sie also nicht benutzen.
  .word   irq_vektor_timera1   ; 09: 0FFF0  Für den Überlauf-Interrupt in der Zeitnehmer-Routine.
  .word   irq_vektor_timera0   ; 10: 0FFF2  Für das Wecken nach einer Pause und den Taktlauscher
  .word   irq_vektor_watchdog  ; 11: 0FFF4  Für die Zeitzählung
  .word   irq_vektor_comp      ; 12: 0FFF6  Komparantor
  .word   irq_vektor_timerb1   ; 13: 0FFF8  Überlauf-Interrupt
  .word   irq_vektor_timerb0   ; 14: 0FFFA  Normales Wecken
  .word   null_handler         ; 15: 0FFFC  NMI. Vorsichtshalber erstmal nicht rausgeführt.
  .word   Reset                ; 16: 0FFFE  Für den Start

  else

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

  .word   null_handler  ; 0FFE0
  .word   null_handler  ; 0FFE2
  .word   null_handler  ; 0FFE4
  .word   null_handler  ; 0FFE6
  .word   null_handler  ; 0FFE8
  .word   null_handler  ; 0FFEA
  .word   null_handler  ; 0FFEC
  .word   null_handler  ; 0FFEE
  .word   null_handler  ; 0FFF0
  .word   null_handler  ; 0FFF2
  .word   null_handler  ; 0FFF4
  .word   null_handler  ; 0FFF6
  .word   null_handler  ; 0FFF8
  .word   null_handler  ; 0FFFA
  .word   null_handler  ; 0FFFC
  .word   Reset         ; 0FFFE  Für den Start

  endif

end
