;; SIRC Receiver
;;
;; Copyright 2011-2012 - By Michael Kohn
;; http://www.mikekohn.net/
;; mike to mikekohn.net
;;
;; Listen on a 40kHz IR receiver for SIRC communication and speak
;; the code (plus 1) if it's a sony CD player remote or speak the
;; temperature if it's address 7.

.include "msp430g2x31.inc"

RAM equ 0x0200
SD_COMMAND equ 0x0200
COMMAND equ 0x0210
ADDRESS equ 0x0212
DATA_IN equ 0x0224

CMD0 equ 0x40
CMD1 equ 0x41
;CMD17 equ (0x40|17)
CMD17 equ 0x51
SOUND_RATE equ 2000
IR_RATE equ 200

START_LEN equ 160    ; adjust for cpu speed
BIT_ONE equ 80
BIT_ZERO equ 40

;  r4 = count on / off
;  r5 = state = 1 (on) , 0 (off)
;  r6 =
;  r7 =
;  r8 = current offset in flash block (div by 4)
;  r9 = Sound data, temp for clock
; r10 = temp for main routine
; r11 =
; r12 =
; r13 = interupt jump routine
; r14 = temp in functions (interrupt)
; r15 = temp in functions (interrupt)

; 0 = 0.6ms, 9600 cycles    48 interrupts
; 1 = 1.2ms, 19200 cycles   96 interrupts
; S = 2.4ms, 38400 cycles  192 interrupts

  .org 0xf800
start:
  ;; Turn off watchdog
  mov.w #(WDTPW|WDTHOLD), &WDTCTL

  ;; Please don't interrupt me
  dint

  ;; r13 points to which interrupt routine should be called
  mov.w #null_interrupt, r13

  ;; Set up stack pointer
  mov.w #0x0280, SP

  ;; Set MCLK to 16 MHz with DCO 
  mov.b #DCO_7, &DCOCTL
  mov.b #RSEL_14, &BCSCTL1
  ;mov.b #DCO_3, &DCOCTL
  ;mov.b #RSEL_15, &BCSCTL1
  mov.b #0, &BCSCTL2

  ;; Set up output pins
  ;; P1.0 = Data in for IR
  ;; P1.2 = /CS for DAC
  ;; P1.3 = /CS for SD
  mov.b #0x0e, &P1DIR        ; P1.1, P1.2, P1.3
  mov.b #(8|4), &P1OUT

  ;; Set up SPI
  mov.b #(USIPE7|USIPE6|USIPE5|USIMST|USIOE|USISWRST), &USICTL0
  mov.b #USICKPH, &USICTL1
  mov.b #(USIDIV_7|USISSEL_2), &USICKCTL ; div 128, SMCLK
  bic.b #USISWRST, &USICTL0      ; clear reset

  ;; Set up Timer
  mov.w #SOUND_RATE, &TACCR0
  mov.w #(TASSEL_2|MC_1), &TACTL ; SMCLK, DIV1, COUNT to TACCR0
  mov.w #CCIE, &TACCTL0
  mov.w #0, &TACCTL1

  ;; FIXME - Remove this I think
  ;mov.b #0, &USISRL
  ;mov.b #8, &USICNT

  call #send_sd_init

  ;; Setup to read SD block
  mov.w #0x0000, r8
  mov.b #CMD17, &SD_COMMAND+0

  ;; Okay, I can be interrupted now
  eint

  ;mov.b #1, r10
  ;call #say

  call #clear_mem
  mov.w #IR_RATE, &TACCR0

main:
  bit.b #1, P1IN
  jz start_count
  jmp main

start_count:
  mov.b #0, r5
  mov.b #0, r4
  mov.w #count_on_interrupt, r13
keep_counting:
  cmp.b #1, r5
  jne keep_counting

  mov.w #null_interrupt, r13
  cmp.w #START_LEN, r4   ; 192 should be 2.4ms
  jge start_bit
  mov.w r4, 0(r6)
  cmp.w #DATA_IN+22, r6
  jeq calc_data
  add.w #2, r6
  jmp main

calc_data:
  mov #DATA_IN, r6
  mov.w #0, &COMMAND
  mov.w #0, &ADDRESS

bang_command:
  rra.b &COMMAND
  cmp.w #BIT_ONE, 0(r6)
  jl command_0
  bis.b #64, &COMMAND
command_0:
  add.w #2, r6
  cmp.w #DATA_IN+14, r6
  jne bang_command

bang_address:
  rra.b &ADDRESS
  cmp.w #BIT_ONE, 0(r6)
  jl address_0
  bis.b #16, &ADDRESS
address_0:
  add.w #2, r6
  cmp.w #DATA_IN+24, r6
  jne bang_address

  mov #DATA_IN, r6

  cmp.b #0x07, &ADDRESS
  jeq say_temp
  cmp.b #0x11, &ADDRESS
  jne main

  mov.b &COMMAND, r10
  inc.b r10
  call #say
  mov.w #IR_RATE, &TACCR0
  jmp main

say_temp:
  mov.b #105, r10   ; The temperature is
  call #say
  bit.b #64, &COMMAND
  jz not_negative
  mov.b #126, r10   ; minus
  call #say
  bis.b #128, &COMMAND
  xor.b #-1, &COMMAND
  add.b #1, &COMMAND

not_negative:
  mov.b &COMMAND, r10
  call #say
  mov.b #109, r10   ; degrees celcious
  call #say
  mov.w #IR_RATE, &TACCR0
  jmp main

start_bit:
  call #clear_mem
  jmp main

clear_mem:
  mov.w #0, r4
  mov.w #0, r5
  mov.w #DATA_IN, r6
clear_again:
  mov.w #0, 0(r6)
  inc.w r6
  cmp.w #DATA_IN+64, r6
  jne clear_again
  mov.w #DATA_IN, r6
  ret

say:
  ;; Read block 1 or 2 using index to find address of sound
  mov.w #SOUND_RATE, &TACCR0
  mov.b #0, &SD_COMMAND+1
  mov.b #0, &SD_COMMAND+2
  mov.b #0, &SD_COMMAND+3
  mov.b #0, &SD_COMMAND+4
  rla.w r10
  rla.w r10
  cmp.w #512, r10
  jl not_second_block
  sub.w #512, r10
  add.b #2, &SD_COMMAND+3
not_second_block:
  call #spi_get_char
  call #spi_get_char
  bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  call #send_sd_command

  ;; Read data token
not_fe_yet_say:
  call #spi_get_char
  cmp.b #0xfe, r15
  jne not_fe_yet_say

  mov.w r10, r11
pre_flush_indexes:
  call #spi_get_char
  dec.w r11
  jne pre_flush_indexes

  mov.w #SD_COMMAND+1, r11
read_sound_address:
  call #spi_get_char
  mov.b r15, 0(r11)
  inc.w r11
  cmp.w #SD_COMMAND+5, r11
  jne read_sound_address

  mov.w #512, r11
  sub.w r10, r11
  sub.w #2, r11     ; 512-address+4-2 (4 bytes for address and 2 for CRC)

  ;; Flush CRC
pre_flush_end:
  call #spi_get_char
  dec.w r11
  jne pre_flush_end

  bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable)

  mov.w #0, r11                  ; flag
  mov.w #0, r8                   ; sound byte count
  mov.w #0x3801, r9              ; sound byte
  mov.w #0, TAR
  mov.w #sound_player_interrupt, r13

keep_playing_sound:
  cmp.w #0, r11
  jeq keep_playing_sound
  ret

delay_min_1ms:
  mov.w #16000, r15       ; Wait at least 1ms (this is much longer)
wait_1ms:
  dec r15
  jnz wait_1ms
  ret

;; send_sd_init()
;; Trashes r15

send_sd_init:
  bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable on purpose)
  call #delay_min_1ms

  ;; send 80 bits of 1's out of SPI
  mov.w #5, r15
send_80_1s:
  mov.w #0xffff, &USISR
  mov.b #(USI16B|16), &USICNT    ; Flush out 16 bits
wait_spi_0:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_0
  dec r15
  jnz send_80_1s

  mov.b #CMD0, &SD_COMMAND+0     ; Reset SD card
  mov.b #0, &SD_COMMAND+1
  mov.b #0, &SD_COMMAND+2
  mov.b #0, &SD_COMMAND+3
  mov.b #0, &SD_COMMAND+4
  mov.b #0x95, &SD_COMMAND+5     ; CRC 0x95
  mov.b #0xff, &SD_COMMAND+6
  mov.b #0xff, &SD_COMMAND+7

retry_reset:
  call #delay_min_1ms

  bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  call #send_sd_command
  bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable)
  cmp.b #1, r15
  jne retry_reset

  mov.b #CMD1, &SD_COMMAND+0     ; Init SD card
  mov.b #0x00, &SD_COMMAND+5     ; CRC 0
retry_init:
  ;; BEGIN WTF
  mov.b #0xff, r15
  call #spi_send_char
  ;; END WTF


  bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  call #send_sd_command
  bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable)
  cmp.b #0, r15
  jne retry_init

done_init:
  bis.b #8, &P1OUT               ; SD /CS = 1 (disable)
  bis.b #USISWRST, &USICTL0      ; reset SPI
  mov.b #(USIDIV_0|USISSEL_2), &USICKCTL ; div 1, SMCLK full speed now
  bic.b #USISWRST, &USICTL0      ; enable SPI
  ret

;; send_sd_command()
;; Trashes r15, r14
send_sd_command:
  mov.w #SD_COMMAND, r14
sd_next_byte:
  mov.b @r14+, &USISRH
  mov.b @r14+, &USISRL
  mov.b #(USI16B|16), &USICNT    ; Read in 16 bits
wait_spi_cmd:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_cmd
  cmp.w #SD_COMMAND+8, r14
  jne sd_next_byte
  mov.b &USISRL, r15
  ret

; spi_send_char(r15)
spi_send_char:
  mov.b r15, &USISRL
  mov.b #8, &USICNT
wait_spi_read_write:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_read_write
  mov.b &USISRL, r15
  ret

; spi_get_char()
spi_get_char:
  mov.b #0xff, &USISRL
  mov.b #8, &USICNT
wait_spi_get_char:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_get_char
  mov.b &USISRL, r15
  ret

timer_interrupt:
  br r13

null_interrupt:
  reti

count_on_interrupt:
  bit.b #1, P1IN
  jnz ir_is_off
  inc.w r4
  reti

ir_is_off:
  mov.w #1, r5
  reti

sound_player_interrupt:
  ;cmp.w #0xffff, r9
  ;jeq flush_sound_toilet
  bit.w #0x8000, r9
  jne flush_sound_toilet

  bic.b #0x04, &P1OUT            ; DAC /CS = 0 (enable)
  mov.w r9, &USISR
  mov.b #(USI16B|16), &USICNT    ; Read in 16 bits
wait_spi_4:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_4
  bis.b #0x04, &P1OUT            ; DAC /CS = 1 (disable)

  cmp.w #0, r8
  jne read_word

  ;; flush CRC
  bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  mov.w #0xffff, &USISR
  mov.b #(USI16B|16), &USICNT    ; Read in 16 bits
wait_spi_flush:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_flush
  ;bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable)

  add.b #2, &SD_COMMAND+3
  jnc ready_send
  inc.b &SD_COMMAND+2
  jnc ready_send
  inc.b &SD_COMMAND+1
ready_send:
  ;bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  call #send_sd_command

  ;; Read data token
not_fe_yet:
  mov.b #0xff, &USISRL
  mov.b #8, &USICNT    ; Read in 8 bits
wait_spi_fe:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_fe
  cmp.b #0xfe, &USISRL
  jne not_fe_yet

  mov.w #128, r8
read_word:
  dec r8

  bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  mov.w #0xffff, &USISR
  mov.b #(USI16B|16), &USICNT    ; Read in 16 bits
wait_spi_3:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_3
  mov.w &USISR, r9
  bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable)

  reti

flush_sound_toilet:
  mov.w #1, r11
  bic.b #0x08, &P1OUT            ; SD /CS = 0 (enable)
  mov.w #null_interrupt, r13
  add.w r8, r8
  add.w #1, r8    ; flush CRC too
flush_more_poop:
  mov.w #0xffff, &USISR
  mov.b #(USI16B|16), &USICNT    ; Read in 16 bits
wait_spi_flush_poop:
  bit.b #USIIFG, &USICTL1
  jz wait_spi_flush_poop
  dec r8
  jnz flush_more_poop
  bis.b #0x08, &P1OUT            ; SD /CS = 1 (disable)
  reti

spi_interrupt:
  ;; shouldn't happen
  reti

  org 0xffe8
vectors:
  dw spi_interrupt
  dw 0
  dw 0
  dw 0
  dw 0
  dw timer_interrupt       ; Timer_A2 TACCR0, CCIFG
  dw 0
  dw 0
  dw 0
  dw 0
  dw 0
  dw start                 ; Reset



