;
;    Mecrimus-B - A firmware-only USB implementation for MSP430 microcontrollers
;    Copyright (C) 2012  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/>.
;  

; Paket generator to divide data chunks into usb pakets and to add CRC checksum.


; Agenda:
; DatatoTransmit enthält einen String, also eine Länge mit Datenbytes oder Null, wenn gerade keine Übertragung läuft.
; ReadyforTransmit enthält entweder 0 - frei und bereit, oder die Adresse des nächsten zu sendenden Paketes. IRQ liest und löscht, Paketverteiler liest und setzt neu, wenn 0.
; Paketverteiler nimmt also den Pointer auf "Data to Transmit" entgegen und macht daraus einen Strom von Paketen.
; Jeder Aufruf ergibt ein neues Paket. Data0/Data1 müssen sich abwechseln.

; Muss die Daten also in 8-Byte-Blöcke zerlegen, mitzählen, was schon übertragen ist, und am Ende vielleicht noch ein leeres Paket generieren, wenn das vorhergehende voll war.
; Anschließend an jedes eine CRC anhängen und in ReadyforTransmit legen.

 ifdef 0 ; Initialisierungen und benötigte Variablen

ReadyforTransmit     equ 246h
DatatoTransmit       equ 248h
DatatoTransmitOffset equ 24Ah
Data_PID_toggle      equ 29Ch

Sendepaketpuffer equ 2B0h

Init:
  mov #usb_pid_data0, &Data_PID_Toggle
  clr &ReadyforTransmit      ; Kein Sendebereites Paket
  clr &DatatoTransmit        ; Keine Daten in der Warteschlange
  clr &DatatoTransmitOffset  ; Offset Null
  ret

 endif

;------------------------------------------------------------------------------
Paketverteiler:
;------------------------------------------------------------------------------
  ;; writeln ""

  tst &ReadyforTransmit
  jz +
  ;; writeln "Not ready for Transmit"
  ret ; Solange noch Daten zum Senden bereitliegen, nichts tun.

+ ; Als erstes prüfen, ob DatatoTransmit Null ist. Wenn ja --> NAK !
  tst &DatatoTransmit
  jnz +
    ;; mov #usb_paket_nak, &ReadyforTransmit ; Das passiert schon von selbst, wenn nichts da ist.
    ;; writeln "NAK"
    ret

+ ; Ja, es sind noch Daten da, und die müssen übertragen werden.

  ;; writeln "Paket wird generiert"
  ; Verpacke 8 Bytes in ein Paket:
  xor #usb_pid_data0_xor_data1, &Data_PID_toggle ; Implementiere das Data0/Data1-Wechseln. usb_pid_data0_xor_data1
  mov.b &Data_PID_toggle, &Sendepaketpuffer+1 ; PID des Paketes setzen.

  ; Maximal 8 Bytes aus den Sendedaten ins Paket übernehmen.
  push r10
  push r11 ; Datenlesezeiger
  push r12
  push r13

  clr r10  ; Zahl der schon ins Paket gelegten Bytes
  
  mov &DatatoTransmit, r11       ; Adresse der ausgehenden Daten

  mov.b @r11, r12                ; Hole die Länge des Datensatzes
  add r11, r12                   ; Adresse dazu --> Ende des Datensatzes - 1
  inc r12                        ; r12 ist jetzt die Endadresse des Datensatzes.
  

  add &DatatoTransmitOffset, r11 ; Offset dazu, dort ist die Übertragung gerade.
  inc r11                        ; Aktuelle Lesestelle.

;  writeln "Einlauf"
;  write " Current: "
;  writeword r11
;  write " End: "
;  writeword r12
;  write " Anzahl: "
;  writeword r10
;  writeln " Einlauf zu Ende "



  ; Daten sind zu Ende, wenn r11=r12 gilt.
  cmp r11, r12 ; Wenn das schon von Anfang an so ist, war das letzte Paket voll, als die Daten zu Ende waren. Füge nun ein leeres Datenpaket an.
  je +

  ; Habe jetzt bislang 0 Bytes ins Paket aufgenommen. Füge Bytes hinzu, bis das Paket 8 Bytes groß ist oder der Datenstrom zu Ende ist !
- mov.b @r11+, Sendepaketpuffer+2(r10) ; Um Längenbyte+PID nach hintengeschoben
  inc r10

;  write " Current: "
;  writeword r11
;  write " End: "
;  writeword r12
;  writeln ""

  cmp #8, r10
  je ++

  cmp r11, r12
  jne -

+ ; Daten sind zu Ende gegangen, Paket ist noch nicht voll.
  clr &DatatoTransmit        ; Aktuell nichts mehr zu übertragen
  clr &DatatoTransmitOffset  ; Offset Null für die nächste Sendung.
;  writeln "Daten zu Ende, Paket noch nicht voll"
+ ; Daten für dieses Paket fertig.

  ; Prüfe: 

  add r10, &DatatoTransmitOffset

  inc r10 ; Länge + 1 Byte für PID dazu !
  mov.b r10, &Sendepaketpuffer ; Länge im Puffer vermerken


;------------------------------------------------------------------------------
; plusCRC: ; Calculates the CRC checksum of one sting and applies that to a counted string.  
         ; Unknown if it works correctly, as USB-CRC-implementation seems to be somewhat different to standard CRC-16.
         ; Berechnet die Prüfsumme eines Strings und hängt diese am Ende an.
;------------------------------------------------------------------------------       
  ;push r10 ; Address             Adresse
  ;push r11 ; Bytes left          Zahl der noch folgenden Bytes
  ;push r12 ; Data                Datenbyte, welches gerade geprüft wird
  ;push r13 ; CRC sum in progress CRC-Zwischenregister

  mov #Sendepaketpuffer, r10

  mov.b @r10+, r11 ; Zahl der folgendes Bytes holen  Fetch length
  incd.b -1(r10)   ; Stringlänge um Zwei erhöhen     Increase length for two bytes

  ; CRC-16 nach USB-Art, mit Startwert -1 und invertiertem Ausgang.
  ; CRC-16 hopefully in USB-style, with start ffff and inverted output.

  mov #0FFFFh, r13  ; Anfangswert setzen   Start value, special to USB ffff instead of 0 as usual in crc implementations
  
  inc r10 ; PID überlesen
  dec r11 ; Einen weniger
  jz ++   ; Leere Pakete sind auch möglich !

- mov.b @r10+, r12 ; Hole Datenbyte                      Fetch data byte
  xor r12, r13     ; In den Zwischenstand hinein         Xor into CRC sum
  mov #8, r12      ; Bit-Schleifenzähler initialisieren  Bit count to eight

- clrc                                                   ; Rotate right, shift in zeros.
  rrc r13        ; Rechts rotieren, Null reinschieben.
 
  jnc +
    xor #0A001h, r13  ; CRC-Polynom anwenden             ; Apply crc poly
+ dec r12                                                ; Dec bit count left
  jnz -

  dec r11                                                ; Dec byte count left
  jnz --

+ inv r13  ; Special to USB: CRC is inverted at the end.
  
  ; Bei USB: Prüfsumme als Big-Endian anhängen !

  mov.b r13, 0(r10) ; Low-Teil an String anhängen             Apply CRC sum to the string in buffer  Low part
  swpb r13
  mov.b r13, 1(r10) ; High-Teil anhängen                      High part


  pop r13
  pop r12
  pop r11
  pop r10

  ; Fertig mit der Paketvorbereitung !
  ; Markiere es als sendefertig:
  mov #Sendepaketpuffer, &ReadyforTransmit
  ret
