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

; Routinen, um eine LED kreativ anders zu nutzen als sonst ... :o)
; Entstanden im Mai 2007, genauer: am 28.
; unter der Feder von Matthias Koch
; Der Taktlauscher entstand daraus am 19. Juni 2007
; Umbau am 28. Oktober 2007:
;  - Vertauschen von dem 0-Puls und dem 1-Puls, damit im Ruhezustand Nullen übertragen werden
;  - Neue HolePuls-Routine mit variabler Datenlänge
; Viele schöne neue Ideen Anfang Oktober 2008
;   6. Oktober 2008: Neue, kürzere Routinen !
;  19. August  2009 wird aufgeräumt, kürzere Routinen !
;------------------------------------------------------------------------------
; Konstanten
;------------------------------------------------------------------------------  
Synchron_Dauer equ 18 ; Gibt an, wie viele Pulse-Her-und-Hin abgewartet werden soll, bevor
                      ; mit dem Senden der Nutzdaten begonnen wird.
                      ; Zwei Mal für die Synchronisation, 16 Mal zum Aufräumen im Datenregister.

;Taktlauschervariablenanfang equ 0240h ; Muss gerade sein ! Adresse im RAM
;------------------------------------------------------------------------------
; Variablen
;------------------------------------------------------------------------------  
; Word - Variablen
;------------------------------------------------------------------------------
;Lichtmuster_low  equ Taktlauschervariablenanfang + 00h ; Der aktuellere Teil der Lichtmustermessungen
; Lichtmuster_high equ Taktlauschervariablenanfang + 0xh ; Lichtmuster_high (Messung 31-16) : Lichtmuster_low (Messung 15-0)
;Datenregister    equ Taktlauschervariablenanfang + 02h ; Die hineinkommenden Daten werden hier hineinrotiert
;Sendedaten       equ Taktlauschervariablenanfang + 04h ; Datenbits zum Herausrotieren und Abstrahlen
;------------------------------------------------------------------------------
; Byte - Variablen
;------------------------------------------------------------------------------
;Verbindungsdauer equ Taktlauschervariablenanfang + 06h 
                           ; 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.
;Strahlzaehler    equ Taktlauschervariablenanfang + 07h ; Zähler, der die noch auszugebenden Pulse merkt
;Lauschzaehler    equ Taktlauschervariablenanfang + 08h ; Zähler, der weiß, wie oft noch zu Lauschen ist, bevor es hell werden muss
;Taktlauscherzustand equ Taktlauschervariablenanfang + 09h ; Ein Bitmuster des Zustandes, Bit 0: 0=Dunkel, 1=Hell
;------------------------------------------------------------------------------
 include "libtaktlauscher-hw.asm"

; HolePuls bereitet die Sendedaten vor. 
; Das MSB wird zuerst hinausgeschoben, die Übertragung beginne immer mit
; "Ü" - für eine 0 oder mit einer führenden "1" und den restlichen Daten, 
; der Register beim Empfangen wird nach jedem Word mit Null gefüllt, 
; auch im Ruhezustand werden Nullen gesendet.

;------------------------------------------------------------------------------
HolePuls: ; Muss im Strahlzaehler die nächste abzustrahlende Lichtpulslänge zurückliefern.
;------------------------------------------------------------------------------  
  ; Am 28. Oktober 2007 ersetzte Routine...
;------------------------------------------------------------------------------  
; Standartpuls
  mov.b #8, &Strahlzaehler ; Standartpuls, die Ruhezustands-0.
  ; ret ; - Falls nie etwas gesendet werden soll.
;------------------------------------------------------------------------------  
; Verbindungsdauer checken, beginne erst zu Senden, wenn die Verbindung auch sicher besteht.
  cmp.b #Synchron_Dauer, &Verbindungsdauer
  jne ++ ; --> ret  Belasse es im Falle einer gerade laufenden Synchronisation dabei, Ruhezustandspulse abzugeben.
;------------------------------------------------------------------------------  
; Verbindung besteht schon lange genug zum Senden ! Daten bereitstellen.   
  cmp #0, &Sendedaten          ; Gilt Sendedaten = 0, so wird gerade nichts übertragen,
  je HolePuls_Neuesanfordern   ; und es kann Neues angefordert werden.  
;------------------------------------------------------------------------------  
; An bestehender Übertragung weiterarbeiten.  
  rla &Sendedaten            ; Bit aus MSB rausrotieren, Null hineinrotieren
  jnc +                      ; Für eine Datennull ist der Ruhezustandspuls schon bereit. 
  mov.b #4, &Strahlzaehler   ; Für eine Dateneins muss der Puls gesetzt werden.
+ cmp #0, &Sendedaten        ; Prüfen, ob noch etwas zu rotieren ist, oder ob dieses Bit gerade das "Merkbit" war.    
  jne +                      ; Ist der Register jetzt Null, war die Eins das Merkbit, es muss der Übertrag gesendet werden.
- mov.b #12, &Strahlzaehler  ; Der Übertragspuls, 12 Basiszeiten lang
/ ret                        ; Dies ret wird von vielen bedingten Sprüngen angesprungen.
;------------------------------------------------------------------------------
Holepuls_Neuesanfordern:   ; Neue Daten holen und vorbereiten !
  ;call #BytesfuerdieWelt   ; Anfrage: Gibt es etwas Neues ?
  ;jnc - ; --> ret          ; Wenn Carry gesetzt ist, gibt es neue Daten. Ansonsten tue nichts weiter.

  call &hook_senden
  and @r4+, -2(r4) ; Prüfe das zurückgegebene Flag auf dem Datenstack.
  jz - ; Wenn es Null war = Falseflag, sind keine Daten da. --> ret.

  popda r11                ; Neue Sendedaten vom Datenstack holen.  
  cmp #0, r11              ; Soll nur eine Null übertragen werden ?
  je --                    ; --> Dann reicht ein Übertragspuls.
  setc                     ; Neue Daten sind ungleich Null. Rotiere eine Merk-1 hinein.
  rlc r11                  ; Sendedaten waren nicht Null. Jetzt sind mindestens zwei Einsbits darin enthalten.
  jc +                     ; Eins schon vorne angekommen ? Dies ist der Fall, wenn das MSB im Datenword gesetzt war.  
- rla r11                  ; Rotiere jetzt so lange Nullbits hinein, bis eine Eins herausfällt.
  jnc -                    ; Jetzt ist die erste Eins im Carry, und die Sendedaten sind fertig vorbereitet.  
+ mov.b #4, &Strahlzaehler ; Gib hier immer eine Eins aus, den Rest erledigen dann die anderen Durchgänge.  
  mov r11, &Sendedaten     ; "Restdaten" für die anderen Durchgänge ablegen.  
  ret

; Pulsmuster, die gesendet und erkannt werden:
; "Ü" : Gesendet: 12 Pulse, Empfangen: Mindestens 11 Pulse. Definiert eigentlich 11-14, doch Lichtmusterregister kann nicht mehr aufnehmen.
; "0" : Gesendet:  8 Pulse, Empfangen: 7-10 Pulse.
; "1" : Gesendet:  4 Pulse, Empfangen: 3- 6 Pulse.  Wenn die Probe die Bitmustererkennung einleitet, ist es mindestens eine "1".

;------------------------------------------------------------------------------
Bitmustererkennung: ; Schaut in die Lichtmuster hinein, um Pfiffiges zu entnehmen
                    ; Benutzt r11 auf kreative Art und Weise - vorher sichern.
;------------------------------------------------------------------------------
  ; Prüfe die Dauer des Bestehens.  
  cmp.b #Synchron_Dauer, &Verbindungsdauer ; Prüfe, wie lange die Verbindung schon besteht.
  je +                                     ; Wenn die Verbindung noch nicht als stabil angesehen werden kann,
  inc.b &Verbindungsdauer                  ; erhöht sich ihre Stabilität mit jedem korrekt eintreffendem Puls.  
  cmp.b #Synchron_Dauer, &Verbindungsdauer ; Ist nun die Schwelle erreicht, dass die Verbindung stabil besteht ?
  jl +                                     ; Wenn es der Fall ist, ist soeben der Beginn
  ;call #Verbindungsbeginn                  ; einer neuen stabilen Verbindung erkannt worden. Tue dies kund:
  call &hook_verbindungsanfang
  
  ; Die eingehenden Pulse werden auf jedem Fall bearbeitet, falls beide Seiten nicht sofort gleich synchronisieren
  ; und der eine schon etwas früher anfängt, Daten zu senden.
   
+  mov &Lichtmuster_low, r11  ; Das Lichtmuster, welches die Probe bestanden hat.
  ;    10987654321            ; 111 1111 1111 00b - Mindeszahl von 11 Pulsen für "Ü"         
  and #1111111111100b, r11    ; Maske anwenden, um zu sehen, ob mindestens 11 Hellpulse
  cmp #1111111111100b, r11    ; enthalten sind. Mehr ist okay. 
  jne +                       ; Nicht gleich ? --> "0" oder "1"  
  pushda &Datenregister       ; Hineingekommenes in den Datenstack legen
  clr &Datenregister          ; Datenregister für den nächsten Durchgang löschen - Wichtig !
  ;jmp ByteEmpfangen           ; Weiterbearbeiten. Darin ist ein ret. Weiterlaufen würde eine Null reinrotieren.
  br &hook_empfangen
    
+ ;    7654321                ;      111 1111 00b - Mindeszahl von 7 Pulsen für "0"
  and #111111100b, r11        ; Wieder mit Maske prüfen,
  cmp #111111100b, r11        ; ob genügend viele Hellpulse gesehen worden sind.
  clrc                        ; Null vormerken
  je +                        ; Oder war es doch eine Eins ?
  setc                        ; 111 00b Mindeszahl von 3 Pulsen für "1" ist schon durch die "Pulserkennung" gewährleistet.
+ rlc &Datenregister          ; Das neue Bit in den "Datensammelregister" schieben
  ret    

;------------------------------------------------------------------------------  
Taktlauscher: ; Lauscht im Takt.
;------------------------------------------------------------------------------
  ; Schenke Deinen Schein
  cmp.b #0, &Strahlzaehler  ; Im Strahlzähler ist die Anzahl der noch abzugebenden Lichtpulse
  jz ++                     ; Wenn es keine sind, wird gerade gelauscht.
  dec.b &Strahlzaehler      ; Solange noch geleuchtet wird, verringere die ausstehende Anzahl.
  jz +                      ; Ist es eben gerade Null geworden, wird der Lauschzyklus eingeleitet.
  jmp Strahle               ; Ansonsten weitergeleuchtet.
;------------------------------------------------------------------------------
; Vorbereitung des ersten Lauschens und Einleitung des Lauschzyklusses  
+ mov.b #32, &Lauschzaehler ; Das Maximum an Aufmerksamkeit...
  clr &Lichtmuster_low      ; Enthält die Messungen 15 -  0
;  clr &Lichtmuster_high    ;                       31 - 16, wobei die 0. Messung zeitlich zuletzt eingegangen ist.
  jmp Lauschen_Vorbereitung ; Das Strahlen ist zu Ende, sogleich wird die erste Lichtmessung nach dem Puls begonnen.
                            ; Wird im ganzen Zyklus nur ein einziges Mal durchlaufen !
;------------------------------------------------------------------------------
; Lauschzyklus läuft, nimm Ergebnisse auf.  
+ call #Lauschen_Nachbearbeitung ; Holt das nächste Lichtbit
  rlc &Lichtmuster_low           ; und vermerkt es
; rlc &Lichtmuster_high          ;     im langen Register des Lichtes.
    
  mov &Lichtmuster_low, r11      ; Die Probe, ob ein Puls erkannt wurde.
  and #11111b, r11               ; Suche unter den 7 aktuellsten Erspähungen
  cmp #11100b, r11               ; folgendes Muster: 3x Licht, 2x Dunkelheit.  Damit es schneller geht nur noch 2x Dunkelheit.
  jne +                          ; ward es nicht gesehen, wird weitergespäht.
  
  ; Die Probe befand das Muster für gut, 
  call #Bitmustererkennung       ; verwerte also die Nutzdaten,
- call #Holepuls                 ; hole das Nächste zum Übertragen
  jmp Strahle                    ; und beginne sofort, es abzustrahlen. Das erste Mal wird nicht gezählt !
  
  ; Die Probe hat noch kein Muster erkannt, lausche also weiter in die Nacht.
+ dec.b &Lauschzaehler           ; Anzahl der noch abzuwartenden Pulse erniedrigen
  jz Taktlauscher_init           ; Null geworden ? Das ist ein Synchronisationsverlust.
  jmp Lauschen_Vorbereitung      ; Klarmachen zur Aufnahme der nächsten Helligkeit.
;------------------------------------------------------------------------------  
Taktlauscher_init: ; Ein Teil des Taktlauschers, der aber auch der Initialisierung dient
;------------------------------------------------------------------------------
                                 ; Datenregister wird beim Synchronisieren geleert, Löschen nur für Debugzwecke.
  clr &Sendedaten                ; Keine Daten zum Herausrotieren und Abstrahlen ! Wichtig !
  
  cmp.b #Synchron_Dauer, &Verbindungsdauer ; Bestand eigentlich eben noch eine Verbindung ?
  clr.b &Verbindungsdauer        ; Ab jetzt: Keine Verbindung mehr, Synchronisation muss neu beginnen !
  jl +                           ; Wenn aber gerade eben noch eine Verbindung sicher bestand,
  ;call #VerbindungsEnde          ; tue deren Ende kund.
  call &hook_verbindungsende

+ bit.b #1, &Taktlauscherzustand ; Hier wird nun unterschieden: Soll der Taktlauscher hell rufen oder dunkel lauschen ?
  jc -                           ; Hell: Hole Ruhezustandspuls und Strahle !
  
  ; Dunkel !                     ; Strahlzähler ist bereits Null, wenn die Routine in den Lauschteil läuft.
                                 ; Ausnahme: Erster Start, soll Dunkel lauschen. Variablen uninitialisiert. 
  clr.b &Strahlzaehler           ; Gibt nach Einschalten je nach Inhalt ganz langen Puls, der wie "Ü" wirkt.
  mov.b #1, &Lauschzaehler
  jmp Lauschen_Vorbereitung

