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


; DatatoTransmit   contains address of a string or zero, if there is no transmission at the moment.
; ReadyforTransmit contains address of next packet to send or zero if idle, in which case NAKs are transmitted. 
;                  Interrupt routine reads this, transmits contents and clears this pointer after transmission.
; The task of this code is to take data and generate packets out of it.
; Every run gives one packet, Watch for Data0/Data1 toggling !

; 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  Initialisations and needed variables

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          No Packet ready for transmission
  clr &DatatoTransmit        ; Keine Daten in der Warteschlange  No data waiting for beeing packaged
  clr &DatatoTransmitOffset  ; Offset Null                       Offset zero
  ret

 endif

;------------------------------------------------------------------------------
PacketGenerator:
;------------------------------------------------------------------------------

  tst &ReadyforTransmit
  jz +  ; Do nothing while there is still a processed data packet ready for transmission.
    ret ; Solange noch Daten zum Senden bereitliegen, nichts tun.

+ ; Als erstes prüfen, ob DatatoTransmit Null ist. Wenn ja --> NAK !
  tst &DatatoTransmit ; Check if there is data that could be packaged !
  jnz +
    ret

+ ; Ja, es sind noch Daten da, und die müssen übertragen werden.
  ; Yes, there is something that should be packaged.

  xor #usb_pid_data0_xor_data1, &Data_PID_toggle ; Implementiere das Data0/Data1-Wechseln. usb_pid_data0_xor_data1  Data toggling
  mov.b &Data_PID_toggle, &Sendepaketpuffer+1 ; PID des Paketes setzen. Set PID of packet

  ; Maximal 8 Bytes aus den Sendedaten ins Paket übernehmen. Stuff a maximum of 8 bytes in one packet.
  push r10 ; Number of already processed bytes
  push r11 ; Datenlesezeiger  Pointer for buffer read
  push r12 ; Pointer to the end of the data chunk that is about to be packaged
  push r13 ; Only pushed for use in CRC routine

  clr r10  ; Zahl der schon ins Paket gelegten Bytes  Zero bytes already processed
  
  mov &DatatoTransmit, r11       ; Adresse der ausgehenden Daten ; Address of outgoing data

  mov.b @r11, r12                ; Hole die Länge des Datensatzes                 Fetch length of data
  add r11, r12                   ; Adresse dazu --> Ende des Datensatzes - 1      Add address --> End of data - 1
  inc r12                        ; r12 ist jetzt die Endadresse des Datensatzes.  End of data in r12
  

  add &DatatoTransmitOffset, r11 ; Offset dazu, dort ist die Übertragung gerade.  Add offset, skip bytes that are already transmitted
  inc r11                        ; Aktuelle Lesestelle.                           Add one gives place to read next byte

  ; Daten sind zu Ende, wenn r11=r12 gilt.  Transmission is finished if r11=r12
  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 +         ; If that already at the beginning, then the last packet was full on end of data. Generate an empty packet to signal end !

  ; For now, I have included zero bytes. Add more, until packet is 8 bytes in size or data is exhausted.
  ; 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  Shift further for later insertion of length and PID
  inc r10

  cmp #8, r10
  je ++

  cmp r11, r12
  jne -

+ ; Daten sind zu Ende gegangen, Paket ist noch nicht voll. Data is over, packet is not full yet.
  clr &DatatoTransmit        ; Aktuell nichts mehr zu übertragen     Transmitter has nothing more to do
  clr &DatatoTransmitOffset  ; Offset Null für die nächste Sendung.  Clear offset
+ ; Daten für dieses Paket fertig.

  add r10, &DatatoTransmitOffset
  inc r10 ; Länge + 1 Byte für PID dazu !  Length + 1 Byte for PID
  mov.b r10, &Sendepaketpuffer ; Länge im Puffer vermerken Store length into buffer

  mov #Sendepaketpuffer, r10 ; Adresse vermerken  Address of buffer for CRC generation
  call #plusCRC
 
  pop r13
  pop r12
  pop r11
  pop r10

  ; Fertig mit der Paketvorbereitung ! Packet generation finished.
  ; Markiere es als sendefertig:       Mark it as ready for transmission !
  mov #Sendepaketpuffer, &ReadyforTransmit
  ret

;------------------------------------------------------------------------------
plusCRC: ; Calculates the CRC checksum of one sting and applies that to a counted string.  
         ; Uses r10, r11, r12 and r13, doesn't push and pop. 
         ; Expects address of buffer to be CRCed in r10.
         ; Berechnet die Prüfsumme eines Strings und hängt diese am Ende an.
;------------------------------------------------------------------------------       
  ; r10 ; Address             Adresse des Puffers, der die CRC-Summe bekommen soll
  ; r11 ; Bytes left          Zahl der noch folgenden Bytes
  ; r12 ; Data                Datenbyte, welches gerade geprüft wird
  ; r13 ; CRC sum in progress CRC-Zwischenregister

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

  ret
