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


; Senderoutine für 15 MHz, 10 Takte/Bit
; Transmitter for 15 MHZ, 10 cycles/bit
  


; Könnte einen Takt in dem Makro sparen, wenn ich usbplus+usbminus=usbmaske im Register halte.
; Saved one cycle by holding usbplus+usbminus in r11.

; ---------------------------------------------------------------------------
usb_tx_bit macro ; Benötigt 7 Takte; Gibt Carry-Flag nach USB-Art aus.  Needs 7 cycles, transmits Carry in style of usb.
; ---------------------------------------------------------------------------
  jnc +                           ; [1] Wechsel bei 0 !
                                  ; [2]

  inc r13                               ; [3] Kein Leitungswechsel durchführen...
  cmp #6, r13                           ; [4] Prüfe, ob die maximale Zahl von Bits ohne Wechsel durchgelaufen ist.  Check whether we had 6 bits without change...
                                        ; [5] Wenn ja, füge einen Extrawechsel ein, so aber mache normal weiter.    --> Insert a stuff-bit
  jne ++                                ; [6] 
                                        ; [7] Wenn kein Stopfen anliegt, mache einfach normal bei [8] weiter.
  
  nop2                                        ; [8] Stopfbit präparieren !
  ;nop                                        ; [9]
  nop2                                        ; [10]
  ;nop                                        ; [1]
  nop                                         ; [2]

+ xor.b r11, &P1OUT               ; [3] Leitungswechsel durchführen !
                                  ; [4] (4 Takte)
                                  ; [5]
                                  ; [6]  <--- Wechsel !
  clr r13                         ; [7] Stopfzähler löschen   Clear stuff counter

+ 
  endm
; ---------------------------------------------------------------------------

; ---------------------------------------------------------------------------
usb_tx_bit_later macro ; Benötigt 7 Takte; Gibt Carry-Flag nach USB-Art aus.  Needs 7 cycles, transmits Carry in style of usb.
                         ; Wechsel erfolgt einen Takt später !                  Change in line one cycle later.
; ---------------------------------------------------------------------------
  jnc +                           ; [1] Wechsel bei 0 !
                                  ; [2]

  inc r13                               ; [3] Kein Leitungswechsel durchführen...
  cmp #6, r13                           ; [4] Prüfe, ob die maximale Zahl von Bits ohne Wechsel durchgelaufen ist.
                                        ; [5] Wenn ja, füge einen Extrawechsel ein, so aber mache normal weiter.
  jne ++                                ; [6] 
                                        ; [7] Wenn kein Stopfen anliegt, mache einfach normal bei [8] weiter.
  
  nop2                                        ; [8] Stopfbit präparieren !
  ;nop                                        ; [9]
  nop2                                        ; [10]
  ;nop                                        ; [1]
  nop                                         ; [2]

+ clr r13                         ; [3] Stopfzähler löschen                       Here clr and xor are interchanged.
  xor.b r11, &P1OUT               ; [4] Leitungswechsel durchführen !
                                  ; [5] (4 Takte)
                                  ; [6] 
                                  ; [7]  <--- Wechsel !

+ 
  endm
; ---------------------------------------------------------------------------

USB_Transmit:

  ; Nur zu Probezwecken, falls ich von außen einspringe !
  push r10  ; Spare register to split fetching of next data byte up into two parts for timing reasons, could be done in once @ 18 MHz/12 cycles
  push r11  ; XOR mask, usbplus+usbminus
  push r12  ; Data beeing transmitted
  push r13  ; Stuffing counter
  push r14  ; Length left to transmit
  push r15  ; Pointer to fetch data


  ; Komme herein und habe bereits die Register gesichert. 
  ; Schließlich wird ausschließlich direkt auf einkommende Pakete geantwortet,
  ; nie wird selbst die Initiative ergriffen.

  ; As pakets are only transmitted upon request of the host, register pushing will be merged with the receiver routine to save cycles.
  ; But for now, as transmitting may be for test purposes, they are independent of each other. 


  ; Note on counting: XOR takes effect on count 6.
  
  mov #usbplus+usbminus, r11 ; (2)  ; Maske präparieren       Prepare mask for fast xor access

  ; Leitungen präparieren
  bic.b #usbplus,  &P1OUT  ; (4/5) Welche Leitung ist in Ruhe high, welche low ?      Set pin states silently to USB Idle state
  bis.b #usbminus, &P1OUT  ; (4)   Idle=J: Minus high, Plus low.

  bis.b r11, &P1DIR ; (4) Übernimm die Leitungen ! Ausgänge !                         Take lines under control !

  ; Pointer und Länge präparieren  

  ; Stopfzähler präparieren (Wegen des Sync-Feldes und dessen Wechseln eigentlich unnötig!?)
  clr r13 ; (1)  Clear stuffing counter

  ; Sync-Feld präparieren
  mov #80h, r12 ; (2)  Prepare first Byte of each transmission: Sync-Byte
  
  mov &Transmitbufferpointer, r15 ; (3)  Pufferanfang als Pointer bereitlegen    Fetch transmitbufferpointer
  mov.b @r15+, r14      ; (2) Längenbyte holen.                                  Fetch length of transmission
  inc r14               ; (1) und um eins Erhöhen, weil das Sync-Byte auch mitzählt. one more, because sync has to be transmitted first


USB_TransmitLoop:
  rra r12                        ; [10*] Daten weiterrotieren
  ; ---------------------------------------------------------------------------
  ; Erstes Bit (0) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben                    Send one bit
  nop2                           ; [8]   Platz für Sonderaufgaben !
  ;nop                           ; [9]  Platz für Sonderaufgaben !
  rra r12                         ; [10]  Daten weiterrotieren            Rotate next bit in place.
  ; ---------------------------------------------------------------------------
  ; Zweites Bit (1) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben
  nop2                           ; [8]   Platz für Sonderaufgaben !
  ;nop                           ; [9]  Platz für Sonderaufgaben !
  rra r12                         ; [10]  Daten weiterrotieren
  ; ---------------------------------------------------------------------------
  ; Drittes Bit (2) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben
  nop2                           ; [8]   Platz für Sonderaufgaben !
  ;nop                           ; [9]  Platz für Sonderaufgaben !
  rra r12                         ; [10]  Daten weiterrotieren
  ; ---------------------------------------------------------------------------
  ; Viertes Bit (3) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben
  nop2                           ; [8]   Platz für Sonderaufgaben !
  ;nop                           ; [9]  Platz für Sonderaufgaben !
  rra r12                         ; [10]  Daten weiterrotieren
  ; ---------------------------------------------------------------------------
  ; Fünftes Bit (4) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben
  nop2                           ; [8]   Platz für Sonderaufgaben !
  ;nop                           ; [9]  Platz für Sonderaufgaben !
  rra r12                         ; [10]  Daten weiterrotieren
  ; ---------------------------------------------------------------------------
  ; Sechstes Bit (5) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben
  mov.b @r15+, r10               ; [8]   Nächstes Byte bereitlegen                Fetch next data byte into r10
                                 ; [9]
  rra r12                         ; [10]  Daten weiterrotieren
  ; ---------------------------------------------------------------------------
  ; Siebstes Bit (6) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit                     ; [1-7] Bit ausgenben
  rrc.b r12                        ; [8]   Letztes Datenbit aus altem Byte holen         Rotate last Bit (could be rra r12, only historic reason)
  mov r10, r12                   ; [9]   Nächstes Byte an die richtige Stelle schieben.  Put next byte in place
  ; ---------------------------------------------------------------------------          One cycle less than usual to have enough time in next bit !
  ; Achtes Bit (7) verschicken:
  ; ---------------------------------------------------------------------------
  usb_tx_bit_later             ; [10-6] Bit ausgeben                                   This handles the lines one cycle later... So timing is preserved.
  dec r14                        ; [7]    Kommt noch etwas ?                             Decrement length
  jnz USB_TransmitLoop          ; [8]    Wenn ja, weitermachen.                          Anything left ? Jump back quickly !
                                 ; [9]                    
  nop                            ; [10]   Ansonsten SE0 präparieren. 
  ; ---------------------------------------------------------------------------  
  ; SE0 ausgeben, Ende                                                           
  ; ---------------------------------------------------------------------------  
  nop2                           ; [1]
  ;nop                           ; [2]
  bic.b r11, &P1OUT              ; [3] SE0 bereitlegen und zwei Bitzeiten lang halten.   SE0, both lines low, for two bit times
                                 ; [4]
                                 ; [5]
                                 ; [6] <--- hier !                                       SE0 in lines here.

  pop r15                        ; [7] 
                                 ; [8]  Könnte wunderbar all die Register zurückholen ;-)  Time to fetch back registers while SE0...
  pop r14                        ; [9]
                                 ; [10]  

  pop r13                        ; [1]
                                 ; [2]
  pop r12                        ; [3]
                                 ; [4]
  pop r11                        ; [5]
                                 ; [6]
  pop r10                        ; [7]
                                 ; [8]
  nop2                           ; [9]
  ;nop                           ; [10]

  nop                            ; [1]
  bic.b #usbplus+usbminus, &P1DIR ; [2] SE0 zurücknehmen, Leitungen loslassen    Release lines, take back SE0.
                                 ; [3]
                                 ; [4]
                                 ; [5]
                                 ; [6] <--- hier !                               Lines released here.
  ret                            ; Fertig !  Finished !



; Outtakes:
  ifdef 0
;------------------------------------------------------------------------------
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 &TransmitBufferPointer, r10 ; Stringadresse holen  Fetch address of counted string in buffer    
  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
  
- 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.
  
  ; swpb r13 ; Bei USB: Prüfsumme als Big-Endian anhängen !   Maybe byte order has to be swapped ?

  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
  ret
  
  endif