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


; Empfangsroutine für 15 MHz, 10 Takte/Bit 
; Receiver        for 15 MHz, 10 cycles/bit

nrzi_r10 macro
  mov.b @r9, r10          ; [1]  NRZI
  ;                       ; [2]  <--- Sampling
  and #usbminus, r10      ; [3]
  xor r10, r11            ; [4]
  rrc.b r12               ; [5]
  endm

nrzi_r11 macro
  mov.b @r9, r11          ; [1]  NRZI                         
  ;                       ; [2]  <--- Sampling
  and #usbminus, r11      ; [3]
  xor r11, r10            ; [4]
  rrc.b r12               ; [5]
  endm


unstuff macro register, bitmask
  bit r8, r12             ; [8]   Stopferkennung, 6 Bits nacheinander kein Wechsel !  Detect stuffing, 6 bits without change...
  jnz +                   ; [9]   Datenbits hier invertiert !                         Data bits inverted here !
                          ; [10]

  mov.b @r9, register           ; [e 1]  Für die NRZI-Dekodierung Zustand wechseln.    Read state again for proper NRZI decoding
                                ; [e 2]  <--- Sampling Zustand einfach nochmal lesen
  and #usbminus, register       ; [e 3]      
  bis #80h, r12                 ; [e 4]  Oberstes Bit in Daten setzen                  Set MSB in data
                                ; [e 5]

  bis #bitmask, r13             ; [e 6] Entsprechende Stelle in Stopfmaske merken      Set changed place in stuffmask
  ;                                     Je nach Maske einen Takt mehr.                                

  if (bitmask == 1) | ( bitmask == 2 ) | ( bitmask == 4 ) | (bitmask == 8) ;           Insert one nop more if constant generator can be used
    nop2                        ; [ e 7, e 8 ]
  elseif
    nop                         ; [ e 8 ]
  endif

  nop2                          ; [e 9 - e 10]
+ ; Fertig Finished stuffing
  endm


unstuff_hurry_up macro bitmask ; Only für 1, 2, 4, 8 because of Constant Generator use
    jnz +                 ; [3 noch vorheriges Bit]  ; Bezieht sich auf die Entstopfung im letzten Bit !
                          ; [4 noch vorheriges Bit]

        mov r10, r11                  ; [es 5]  In r10 ist der Entstopf-Bit-Pinzustand. Lege ihn als Basis für die letzte Messung bereit.
        and #usbminus, r11            ; [es 6]
        bis #80h, r12                 ; [es 7]  Oberstes Bit in Daten setzen
                                      ; [es 8]
        bis #bitmask, r13             ; [es 9] Entsprechende Stelle für das vorhergehende Bit 
        nop ; (nop) je nach Bitstelle ; [es 10]   in Stopfmaske merken
        mov.b @r9, r10                ; [es 1]  Hatte in r10 das Entstopf-Bit einkassiert. Muss es ersetzen.
                                      ; [es 2] <--- Sampling !
        nop2                          ; [es 3]
        ;                             ; [es 4]  Because of this detects for unstuffing in the LAST bit, we have to juggle around registers here.

+ ; Fertig Finished stuffing
  endm

  ; Signal for Sync pattern

  ; Signal: 
  ; D-:   normally high, Sync: 01 01 01 00 
  ; D+:            low         10 10 10 11
  ; Idea: Search 1-->0 transtition, sample it precisely and look if double zero follows, else repeat.
  ; As initialisation after IRQ (after bit.b for zero) needs some time, we will normally not see the first zero here.
 

  include "Sync-15MHz.asm"

  ; ---------------------------------------------------------------------------
  ; Fertig synchronisiert, lege los ! Sync is ok, lets start !
  ; ---------------------------------------------------------------------------
  
  ; r12 muss für die Stopferkennung passend initialisiert werden !
  ; r13 = Stopfmaske wird ausgenullt
  ; r15 = Pufferzeiger setzen

  ; Habe beim ersten Einlauf Zeit, da ganz am Anfang kein Stopfen möglich ist.
  ; Can do preparations while running in, because there cannot occour stuffing on the first few bits.

  ; Register usage:
  ; r8   ; Vergleichs-Stopfmaskenmuster #11111100b    Mask for detect stuffing
  ; r9   ; P2IN-Adresse                               Address of P2IN

  ; r10  ; NRZI-Pinzustand                            Buffer for NRZI pin state
  ; r11  ; NRZI-Pinzustand                            --

  ; r12  ; Datenempfangsregister                      Data input shift register, has to be initialized to sync-pattern not($80)
  ; r13  ; Stopfmaske                                 Stuffing mask, has to be cleared

  ; r14  ; Datensicherung                             Data backup register
  ; r15  ; Pointer                                    Pointer to write into receive buffer
  
  ; Note: Data is inverted because of clever use of flags and side-effects to save cycles
  ;       Stuffing mask holds those bits that have been inverted, so that stuffing detection 
  ;       will not detect stuffing on the same bits more than one time.

  ; We have some time in the first byte of data, as stuffing cannot occour the first 5 bits.
  ; D- is used for sync and input, D+ is only used to detect packet end SE0.

  ; ---------------------------------------------------------------------------
  ; Einlauf, Erstes Bit: (0)  Run-in with preparations, first bit
  ; ---------------------------------------------------------------------------

  mov.b @r9, r10     ; [1]  NRZI             ( 3  4)  Chance counting here so that mov.b @r9 always counts on one.
  ;                  ; [2]  <--- Sampling    ( 4  5)  Center-Sampling on 5 or 6 !

  mov #7Fh, r12      ; [3]  not($80)         ( 5  6)  Initialize data, as last bit of Syncpattern may take part in stuffing.
  ;                  ; [4]                            Syncbyte spielt beim Stopfen mit !Präparation des Datenregisters für Stopferkennung.
  and #usbminus, r10 ; [5] statt bit einleiten        Wechsele hier die Zählweise !              Eine davon müsste direkt den Wert des ersten Bits liefern. Hängt vom Sync-Byte-Endzustand ab. Dort ist D- low. Wechsel --> Carry gesetzt. Kommt hin :-)
  rrc.b r12          ; [6]  und rotieren              Der Mov-Befehl kommt so immer auf [1] :-)  Erstes Datenbit aufnehmen ! Usbminus enthält das erste Datenbit invertiert. Passt :-)
                     ;      Catch first data bit, use only AND here, as last state out of sync pattern is always the same.
  push r13           ; [7]  Weitere Register sichern  Save more registers
  ;                  ; [8] 
  ;                  ; [9] 
  clr r13            ; [10]  Stopfmaske löschen   Clear stuffing mask

  ; ---------------------------------------------------------------------------
  ; Einlauf, Zweites Bit: (1)   Run-in with preparations
  ; ---------------------------------------------------------------------------

  nrzi_r11           ; [1-5]
  push r14           ; [6]  "Datensicherung" braucht keine Initialisierung.
                     ; [7]  The data backup register doesn't need initialisation.
                     ; [8]
  mov #Receivebufferpointer, r14 ; [9]  Fetch the Pointer in two steps, could be in one step @ 18 MHz/12 cycles.
                     ; [10]

  ; ---------------------------------------------------------------------------
  ; Einlauf, Drittes Bit: (2)  Run-in with preparations
  ; ---------------------------------------------------------------------------
  nrzi_r10           ; [1-5]
  push r15           ; [6]  Der Pointer für Empfangenes.
                     ; [7]  Pointer to write reveived data, has to be initialized.
                     ; [8] 
  mov @r14, r15      ; [9]  Pufferadresse in Pointer laden
                     ; [10] Second step of fetching buffer pointer

  ; ---------------------------------------------------------------------------
  ; Einlauf, Viertes Bit: (3)  Run-in with preparations
  ; ---------------------------------------------------------------------------
  nrzi_r11           ; [1-5]
  push r8            ; [6]  Stopfmaskenregister sichern
                     ; [7]
                     ; [8]
  mov #11111100b, r8 ; [9]  und Stopfmaske bereitlegen.    Prepare mask to quickly detect last 6 bits for stuffing
                     ; [10]

  ; ---------------------------------------------------------------------------
  ; Einlauf, Fünftes Bit: (4)  Einsprung ins reguläre Schema
  ; ---------------------------------------------------------------------------
  nrzi_r10                ; [1-5]
  jmp Mittendrin_im_fuenften_Bit ; [6-7] Einsprung ins reguläre Schema  Jump into regular cycle



USB_ReceiverLoop:  
  ; ---------------------------------------------------------------------------
  ; Erstes Bit: (0)  Sonderaufgabe: Stopfmaske leeren, Daten invertieren  Special task: Clear stuffmask, invert data
  ; ---------------------------------------------------------------------------
  nrzi_r10                ; [1-5]
  clr r13                 ; [8] Sonderaufgabe: Stopfmaske leeren !  Clear stuffing mask. Relevant information is still in r12 and gets shifted out by the way.
  inv r14                 ; [9] Gesichertes Datenbyte invertieren.  Invert saved data byte.
  unstuff r10, 1          ; [8-10]

  ; ---------------------------------------------------------------------------
  ; Zweites Bit: (1)  Sonderaufgabe: SE0-Check  Special task: Detect SE0
  ; ---------------------------------------------------------------------------
  mov.b @r9, r11             ; [1]  NRZI einleiten mit Sampling
                             ; [2]   <--- Sampling

  and #usbplus+usbminus, r11 ; [s3] ; SE0-Check im zweiten Bit !  Check for SE0 
                             ; [s4]
  jz usb_se0                 ; [s5]                               Which occours as low low in both USB lines
                             ; [s6]                               Unfortunately this takes whole 4 cycles...

  and #usbminus, r11         ; [7]  NRZI durchführen
  xor r11, r10               ; [8] 
  rrc.b r12                  ; [9]

  bit r8, r12                ; [10]  Stopferkennung, 6 Bits nacheinander kein Wechsel !

  ; ---------------------------------------------------------------------------
  ; Drittes Bit: (2)  Sonderaufgabe: Aufholen !  Special task: Hurry up !
  ; ---------------------------------------------------------------------------
  mov.b @r9, r10          ; [1]  NRZI einleiten mit Sampling
                          ; [2]  <--- Sampling Flags bleiben dabei erhalten !
  unstuff_hurry_up 2      ; [3-4]

  and #usbminus, r10      ; [5]  NRZI 2 Takte später durchführen. Neues Sampling in Entstopfung !
  xor r10, r11            ; [6]
  rrc.b r12               ; [7]

  unstuff r10, 4          ; [8-10]

  ; ---------------------------------------------------------------------------
  ; Viertes Bit: (3)  Sonderaufgabe: Daten in Puffer schreiben  Special task: Write data into buffer
  ; ---------------------------------------------------------------------------
  nrzi_r11                   ; [1-5]
  mov.b r14, @r15            ; [p6]  Daten in den Puffer legen !     and put freshly prepared data into.
                             ; [p7]
                             ; [p8]
                             ; [p9]
  bit r8, r12                ; [10]  Stopferkennung, 6 Bits nacheinander kein Wechsel !

  ; ---------------------------------------------------------------------------
  ; Fünftes Bit: (4)  Sonderaufgabe: Aufholen !   Special task: Hurry up !
  ; ---------------------------------------------------------------------------
  mov.b @r9, r10          ; [1]  NRZI einleiten mit Sampling
                          ; [2]  <--- Sampling Flags bleiben dabei erhalten !
  unstuff_hurry_up 8      ; [3-4]

  and #usbminus, r10      ; [5]  NRZI 2 Takte später durchführen. Neues Sampling in Entstopfung !
  xor r10, r11            ; [6]
  rrc.b r12               ; [7]

Mittendrin_im_fuenften_Bit:
  unstuff r10, 16         ; [8-10]

  ; ---------------------------------------------------------------------------
  ; Sechstes Bit: (5)  Pointer weiterrücken
  ; ---------------------------------------------------------------------------
  nrzi_r11           ; [1-5]
  inc r15            ; [6]
  nop                ; [7]
  unstuff r11, 32    ; [8-10]

  ; ---------------------------------------------------------------------------
  ; Siebtes Bit: (6)  Frei  Free of special tasks
  ; ---------------------------------------------------------------------------
  nrzi_r10           ; [1-5]
  nop2               ; [6-7]
  unstuff r10, 64    ; [8-10]

  ; ---------------------------------------------------------------------------
  ; Achtes Bit: (7)  Sonderaufgabe: Daten sichern und Stopfmaske anwenden ! Rücksprung via Stopfsprung.   
  ;                  Special task: Save data for later preparation and apply stuffmask. Jump back with stuffing-jump to save cycles !
  ; ---------------------------------------------------------------------------
  nrzi_r11                ; [1-5]

  mov r12, r14            ; [6] Datenbyte ist fertig ! Hole es ab.  Backup of Data
  bic r13, r14            ; [7] Stopfmaske darauf anwenden.         Apply stuffmask

  bit r8, r12             ; [8]  Stopferkennung, 6 Bits nacheinander kein Wechsel !                         
  jnz USB_ReceiverLoop    ; [9]  Return with stuff-detect-jump
                          ; [10]


        ; Enstopfung für letztes Bit. Unstuffing for last bit.
        mov.b @r9, r11                ; [e 1]  Für die NRZI-Dekodierung Zustand wechseln.
                                      ; [e 2]  <--- Sampling  Zustand einfach nochmal lesen
        and #usbminus, r11            ; [e 3]
        bis #80h, r12                 ; [e 4]  Oberstes Bit in Daten setzen, Datenregister ist 16 Bits breit und läuft durch !
                                      ; [e 5]  Set MSB in data, this is rotated further later.  
        nop                           ; [e 6]    Nicht in Stopfmaske vermerken,
        nop2                          ; [e 7]    da dieses Bit komplett herausgeschoben wird
        ;nop                          ; [e 8]    und die Stopfmaske sowieso gelöscht wird.
        jmp USB_ReceiverLoop          ; [e 9]
                                      ; [e 10]  In the last bit there is no need to update stuffmask, as data byte is already saved,
                                      ;         but it is necessary to set the bit in data register, as stuffing continues over byte boundaries.
                                      ;         It will be shifted out later anyway... 

  ; ---------------------------------------------------------------------------
  ; Paketende SE0:  End of packet detected.
  ; ---------------------------------------------------------------------------
  include "Receiver-Common.asm"
