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

  include "mspregister.asm"  ; Register definitions
  include "datastack.asm"    ; Defines a second stack in r4 used here only for printing messages
  
  org 0F000h                 ; Start somewhere... Target is msp430g2452 at the moment.

; Connections to Pins on Port 1:
  ; Simply the Anode of a LED on P1.0 for RS232 like transmissions.

; Connections to Pins on Port 2:

;syncout  equ P2OUT
;syncled  equ 1 ; P2.0  Anode of LED that shows syncinc including 100 Ohms
;               ; P2.1  Cathode of this LED, soldered in this way with brighness measurements and Ledcomm in mind...
;               ;       Always driven low here.


debug_messages equ 1 ; Soll eine LED Meldungen auf RS232-Art ausgeben ?
; debug_messages_protocol_loop equ 1 ; Soll die Protokollschleife ihren Standpunkt vermelden ?

;------------------------------------------------------------------------------
; Leitungen und Elektrisches
;------------------------------------------------------------------------------

toggle_syncled macro ; Preserve clock cycles if you want to remove this !
  xor.b #1, &P2OUT
  endm

usbout equ P2OUT
usbdir equ P2DIR
usbin  equ P2IN

usbminus equ 4  ; P2.2 D- over 50 Ohms with 1.5 kOhm pullup, driven to simulate connect and disconnent by P2.5
usbplus  equ 8  ; P2.3 D+ over 50 Ohms

usbpullup equ 32 ; P2.5 


;------------------------------------------------------------------------------
; Konstanten, Pakete und Beschreibungen
;------------------------------------------------------------------------------

; USB PID values
USB_PID_SETUP   equ 02dh
USB_PID_OUT     equ 0e1h
USB_PID_IN      equ 069h
USB_PID_DATA0   equ 0c3h
USB_PID_DATA1   equ 04bh
USB_PID_ACK     equ 0d2h
USB_PID_NAK     equ 05ah
USB_PID_STALL   equ 01eh

usb_pid_data0_xor_data1 equ 088h ; Zum Wechseln zwischen Data0/Data1



usb_paket_ack: .byte 1, usb_pid_ack
usb_paket_nak: .byte 1, usb_pid_nak

usb_paket_data1_leer_status: .byte 3, 04Bh, 000h, 000h
usb_paket_get_status:        .byte 5, 04Bh, 0, 0, 0FEh, 04Fh


Device_Descriptor:                 .byte 18,   012h, 001h, 010h, 001h, 0FFh, 0FFh, 0FFh, 008h, 012h, 034h, 056h, 078h, 042h, 043h, 001h, 002h, 000h, 001h

Configuration_Descriptor_neun:     .byte  9,      9,    2,  9+9,    0,    1,    1,    0, 080h,   50
Configuration_Descriptor_achtzehn: .byte 18,      9,    2,  9+9,    0,    1,    1,    0, 080h,   50,    9,    4,    0,    0,    0, 0FFh, 0FFh, 0FFh,    0

; usb_get_status_reply:              .byte 2, 0, 0

String_Descriptor_0: .byte 4,   04, 03, 09, 04 ; English-US als Standardsprache

; 4 Bytes
; 3 = String Descriptor Type
; Lang-ID Code 0 in zwei Bytes... 

;String_Descriptor_1: .byte 18,   18, 3,   77, 0,   97, 0,  116, 0,  116, 0,  104, 0,  105, 0,  97, 0,  115, 0 ; Matthias
;String_Descriptor_2: .byte 10,   10, 3,   77, 0,  111, 0,  105, 0,  110, 0 ; Moin

String_Descriptor_1: .byte 28,   28, 3,   77, 0,  97, 0,  116, 0,  116, 0,  104, 0,  105, 0,  97, 0,  115, 0,  32 ; Achte auf eine gerade Anzahl von Bytes ! Sonst 0-padding !
                     .byte                                                                                         0,  75, 0,  111, 0,  99, 0,  104, 0  ; Matthias Koch
String_Descriptor_2: .byte 22,   22, 3,   77, 0,  101, 0,  99, 0,  114, 0,  105, 0,  109, 0,  117, 0,  115, 0, 45 ; Achte auf eine gerade Anzahl von Bytes ! Sonst 0-padding !
                     .byte                                                                                         0,  66, 0 ; Mecrimus-B


; Unicode-16-Encoded String
; Es gibt dann einen Get-Descriptor-Request mit Code 3 für Strings.


;------------------------------------------------------------------------------
; Speicherkarte
;------------------------------------------------------------------------------

; Memory map: (Stimmt nicht !)

; 200 - 20F Data Stack   Only used for Forth message printing routines
; 210 - 23F Return Stack
; 240 - 24F Space for Variables
; 250 - 27F Buffers. At the end of memory to not currupt stack and variables in case of buffer overflow.

Receivebufferpointer     equ 242h
ReadyforTransmit         equ 244h
Aktuelle_Adresse         equ 246h
DatatoTransmit           equ 248h
DatatoTransmitOffset     equ 24Ah
Data_PID_toggle          equ 24Ch
Neue_Adresse             equ 24Eh

Sendepaketpuffer equ 0250h ; Läuft nie über, denn ich fülle ihn selbst !


; Empfangs-Puffer ganz ans Ende des Speichers legen, denn sie neigen dazu, manchmal überzulaufen.
Datenpaketpuffer      equ 02D0h ; Weniger vom Überlauf betroffen
Krimskramspaketpuffer equ 02E0h ; Ganz ans Ende ! Mehr vom Überlauf betroffen...

  ifdef debug_messages
    include "rs232-over-led.asm"
    include "messages.asm"
  endif
 
 include "Receiver-18MHz.asm"
 include "Transmitter-18MHz.asm"
 include "Paketverteiler.asm"

  ifdef debug_messages
PrintBuffer:
  mov #16, r6       ; Number of bytes to print
  
- mov.b @r5+, r7
  writebyte r7
  write " "
  dec r6
  jnz -

  write " | "
  ret
  endif

Reset:
  mov #WDTPW+WDTHOLD, &WDTCTL ; Stop watchdog  
  mov #0240h, sp              ; Init Stackpointer
  mov #0210h, r4              ; Init Datastackpointer, only used for messages...

; Neue Speicherbelegung: 200-220 Datenstack, 220-240 Returnstack.
; Variablen bei 240-250, Puffer an 250.

  ; Prepare Port 1
  clr.b &P1OUT
  bis.b #255, &P1DIR

  ; Prepare Port 2
  clr.b &P2OUT
  mov.b #1+2, &P2DIR  ; Prepare Pins for the Sync-LED, Anode and Cathode driven low. USB lines inputs.

  mov.b &CALBC1_1MHZ, &BCSCTL1   ; Set DCO
  mov.b &CALDCO_1MHZ, &DCOCTL    ;   to 1 MHz. To see if external clock is properly used, as between 1 MHz and 15 MHz is a big difference.


  
  ; Select LFXT1 (HF external clock mode) for MCLK
  bic #OSCOFF, sr              ; Turn on osc.
  bis.b #XTS, &BCSCTL1         ; HF mode
  mov.b #LFXT1S_3, &BCSCTL3     ; External clock source
  
- bic.b #OFIFG, &IFG1          ; Clear OFIFG
  mov #0FFh,R15                ; Delay
- dec R15
  jnz -
  bit.b #OFIFG, &IFG1          ; Re-test OFIFG
  jnz --                       ; Repeat test if needed
  bis.b #SELM_3, &BCSCTL2 ; Select LFXT1CLK
 

    
Mainloop:           ; Print contents of buffer (empty RAM in first run als welcome message), then connect and try to catch packets.

  ifdef debug_messages

  mov #Krimskramspaketpuffer, r5  ; Address of Buffer
  call #Printbuffer
  
  mov #Datenpaketpuffer, r5  ; Address of Buffer
  call #Printbuffer
  
  writeln ""

  ; Pufferspeicher löschen:
  mov #Datenpaketpuffer, r5
- mov.b #00h, @r5 ; Ehemals AA
  inc r5
  cmp #Krimskramspaketpuffer+16, r5
  jne -
  
  endif
  
  ; Los geht's ! Jetzt wird verbunden, jetzt wird es ernst.
  
  
  ; Initialisierung des Paketverteilers und dessen Variablen:

  mov #usb_pid_data0, &Data_PID_Toggle ; Erster Anfang festgelegt...
  clr &ReadyforTransmit      ; Kein Sendebereites Paket
  clr &DatatoTransmit        ; Keine Daten in der Warteschlange
  clr &DatatoTransmitOffset  ; Offset Null
     
  mov #Krimskramspaketpuffer, &Receivebufferpointer ; Vor dem ersten Einlauf den Puffer korrekt setzen !
  clr.b &Datenpaketpuffer ; Länge im Datenpaketpuffer auf Null setzen - keine bereitliegenden Daten für die Protokollschleife.
  
  clr &Aktuelle_Adresse ; Am Anfang hat das Ding Adresse Null
  clr &Neue_Adresse     ; Und die neue Adresse ist ebenso noch nicht gesetzt.
  
  bis.b #usbpullup, &P2OUT  ; "Connect to USB" via enabling the D- pullup resistor.
  bis.b #usbpullup, &P2DIR
 
  nop4
  
  ;mov.b #usbminus, &P2IES ; Reagiere auf high-to-low-Flanke an D-
  ;mov.b #usbminus, &P2IE  ; Interrupt für die Leitung aktivieren
  ;clr.b &P2IFG            ; Keine wartenden Interrupts
  
  clr.b &P2IES            ; Reagiere auf low-to-high-Flanke an D+
  mov.b #usbplus, &P2IE   ; Interrupt für die Leitung aktivieren
  clr.b &P2IFG            ; Keine wartenden Interrupts  
  
  eint                    ; Start !
        
; Protokollschleife:
Innenschleife:
  ; SE0 in der Leitung, außerhalb eines Paketes ? Das bedeutet: Reset !    
  ; Genauer: SE0 for more than 2.5 us is a reset.
  ; Specification: 10ms Reset !
  
  mov #40, r10 ; Schleifendurchläufe
  
- bit.b #usbplus+usbminus, &usbin ; Leitungen prüfen
  jc + ; Aussprung, wenn mindestens eine Leitung derweil high geht.
  dec r10
  jnz -
    ; Reset !
    clr &Aktuelle_Adresse ; Am Anfang hat das Ding Adresse Null
    clr &Neue_Adresse     ; Und die neue Adresse ist ebenso noch nicht gesetzt. 
    ifdef debug_messages_protocol_loop
    write "R"      
    endif
+ ; Fertig  
  
;  call #Protokollhelferlein

  jmp Innenschleife
  
  
Ausbrechen: ; Hier kommen unbekannte Setup-Pakete und andere Seltsame Fälle an.

  ifdef debug_messages
  write " Ad: "
  writebyte &Aktuelle_Adresse
  write " . "
  endif
  
  jmp Mainloop

  include "Protokollschleife.asm"
    
;------------------------------------------------------------------------------
null_handler: ; Fängt unbenutzte Interrupts auf
;------------------------------------------------------------------------------
  reti


 org 0FFE0h ; Interrupttabelle mit eigenen Routinen

  .word   null_handler         ; 01: 0FFE0  Unbelegt
  .word   null_handler         ; 02: 0FFE2  Unbelegt
  .word   null_handler         ; 03: 0FFE4  Port 1
  .word   usb_sync             ; 04: 0FFE6  Port 2
  .word   null_handler         ; 05: 0FFE8  Unbelegt
  .word   null_handler         ; 06: 0FFEA  Für das Sammeln von Werten aus dem AD-Wandler: Temperatur und Vcc !
  .word   null_handler         ; 07: 0FFEC  USCI Transmit  - Hierrüber läuft das Terminal.
  .word   null_handler         ; 08: 0FFEE  USCI Receive   - Werde sie also nicht benutzen.
  .word   null_handler         ; 09: 0FFF0  Für den Überlauf-Interrupt in der Zeitnehmer-Routine.
  .word   null_handler         ; 10: 0FFF2  Für das Wecken nach einer Pause und den Taktlauscher
  .word   null_handler         ; 11: 0FFF4  Für die Zeitzählung
  .word   null_handler         ; 12: 0FFF6  Komparantor
  .word   null_handler         ; 13: 0FFF8  Überlauf-Interrupt
  .word   null_handler         ; 14: 0FFFA  Normales Wecken
  .word   null_handler         ; 15: 0FFFC  NMI. Vorsichtshalber erstmal nicht rausgeführt.
  .word   Reset                ; 16: 0FFFE  Für den Start
  