;
; i0.14.byte.asm
;
; Neil Gershenfeld  CBA  MIT  11/23/06
; (c) Massachusetts Institute of Technology 2006
; Permission granted for experimental and personal use;
; license for commercial use available from MIT
;

.include "tn45def.inc"

.equ buttonbit = 2 ; pushbutton bit
.equ buttonpin = PB2 ; pushbutton pin
.equ lightpin = PB0 ; light pin
.equ clickbit = 4 ; click bit
.equ clickpin = PB4 ; click pin
.equ settle_count = 10 ; counts to wait for click to settle
.equ char = 'I' ; character to send/receive

.def temp = R16 ; temporary register
.def temp1 = R17 ; temporary register
.def txbyte = R18 ; byte to transit
.def rxbyte = R19 ; receive byte
.def bitcount = R20 ; bit counter
.def clickspace = R21 ; click spacing
.def triplespace = R22 ; triple click spacing
.def count = R23 ; loop counter
.def doublecount = R24 ; double loop count

.cseg
.org 0
rjmp reset

;
; click_duration
;
click_duration:
   ldi temp, 2
   click_duration_loop:
      dec temp
      brne click_duration_loop
   ret
;
; click_delay
;
click_delay:
   ldi temp, 15
   click_delay_loop:
      dec temp
      brne click_delay_loop
   ret
;
; char_delay
; delay after sending a character
;
char_delay:
   ldi temp, 255
   char_delay_loop:
      dec temp
      brne char_delay_loop
   ret
;
; putclick
; send char in txbyte clicks
;
putclick:
   ldi bitcount, 8
   sec; set start bit
   ;
   ; get click pin ready for output
   ;
   cbi PORTB, clickpin
   ;
   ; send start clicks
   ;
   sbi DDRB, clickpin
   rcall click_duration
   cbi DDRB, clickpin
   rcall click_delay
   sbi DDRB, clickpin
   rcall click_duration
   cbi DDRB, clickpin
   rcall click_delay
   ;
   ; send data clicks
   ;
   putclick0:
      lsr txbyte; get next bit
      brcc putclick1 ; if carry set, send a 1 click
      sbi DDRB, clickpin
      rcall click_duration
      cbi DDRB, clickpin
      rcall click_delay
      cbi DDRB, clickpin
      rcall click_duration
      cbi DDRB, clickpin
      rcall click_delay
      rjmp putclick2; otherwise ...
   putclick1:
      cbi DDRB, clickpin ; ... send a 0 click
      rcall click_duration
      cbi DDRB, clickpin
      rcall click_delay
      sbi DDRB, clickpin
      rcall click_duration
      cbi DDRB, clickpin
      rcall click_delay
   putclick2:
      dec bitcount; if not all bits sent
      brne putclick0; send next bit
   ;
   ; send stop clicks
   ;
   sbi DDRB, clickpin
   rcall click_duration
   cbi DDRB, clickpin
   rcall click_delay
   sbi DDRB, clickpin
   rcall click_duration
   cbi DDRB, clickpin
   rcall click_delay
   ;
   ; return to pull-up on click pin and return
   ;
   sbi PORTB, clickpin
   ret
;
; getclick
; input an I0 byte following first click
;
getclick:
   ;
   ; delay for first click to settle
   ;
   ldi count, settle_count
   getclick_settle_start:
      dec count
      nop ; to even out timing for breq
      brne getclick_settle_start
   ;
   ; time arrivial of second start click
   ;
   ldi clickspace, settle_count
   getclick_time_start:
      inc clickspace
         breq getclick_timeout ; check for overflow
      sbic PINB, clickpin ; check for click
         rjmp getclick_time_start
   mov triplespace, clickspace
   add triplespace, clickspace
   add triplespace, clickspace
   ;
   ; decode data clicks
   ;
   clr rxbyte
   ldi bitcount, 8
   getclick_bitloop:
      ;
      ; delay for click to settle
      ;
      ldi count, settle_count
      getclick_settle:
         dec count
         nop ; to even out timing for breq
         brne getclick_settle
      ;
      ; time arrivial of next click
      ;
      ldi count, settle_count
      getclick_time:
         inc count
         breq getclick_timeout ; check for overflow
         sbic PINB, clickpin ; check for click
            rjmp getclick_time
      ;
      ; determine bit delay
      ;
      mov doublecount, count
      add doublecount, count
      cp doublecount, triplespace
      brsh getclick_zero
         ;
         ; one bit
         ;
         sec ; set carry
         ror rxbyte ; shift in carry
         ;
         ; even out 0/1 timing
         ;
         mov count, clickspace
         getclick_space:
            dec count
            nop ; to even out timing for breq
            brne getclick_space
	 ;
	 ; decrement counter and output if byte received
	 ;
         dec bitcount
      	 brne getclick_bitloop
   	     rjmp getclick_end
      getclick_zero:
         ;
         ; zero bit
         ; 
         clc ; clear carry
	 ror rxbyte ; shift in carry
	 ;
	 ; decrement counter and output if byte received
	 ;
         dec bitcount
         brne getclick_bitloop
      getclick_end:
         ;
	 ; delay for stop clicks
	 ;
         mov count, clickspace
         getclick_stop1:
            dec count
            nop ; to even out timing for breq
            brne getclick_stop1
         mov count, clickspace
         getclick_stop2:
            dec count
            nop ; to even out timing for breq
            brne getclick_stop2
	 ret
      getclick_timeout:
         ldi rxbyte, 0
	 ret
;
; blink
; blink the LED
; 
blink:
   cbi PORTB, lightpin
   ldi temp, 255
   blink_loop:
      ldi temp1, 100
      blink_loop1:
         dec temp1
         brne blink_loop1
      dec temp
      brne blink_loop
   sbi PORTB, lightpin
   ret

reset:
   ;
   ; set clock divider
   ;
   ldi temp, (1 << CLKPCE)
   ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) ; /1
   out CLKPR, temp
   out CLKPR, temp1
   ;
   ; init microcontroller
   ;
   ldi temp, high(RAMEND) ; set stack pointer to top of RAM
   out SPH, temp ; "
   ldi temp, low(RAMEND) ; "
   out SPL, temp ; "
   sbi PORTB, lightpin ; init light pin for output
   sbi DDRB, lightpin ; "
   sbi PORTB, buttonpin ; init pushbutton pin for input with pull-up
   cbi DDRB, buttonpin ; "
   sbi PORTB, clickpin ; init click pin for input with pull-up
   cbi DDRB, clickpin ; "
   ;
   ; main loop
   ;
   mainloop:
      ;
      ; wait for the button to be pressed or a click to arrive
      ;
      waitloop:
         sbis PINB, buttonbit ; check for button press
            rjmp button_pressed
         sbic PINB, clickpin ; check for click
            rjmp waitloop
      click_received:
         ;
         ; click received, blink the light if char matches
         ;
	 rcall getclick
	 cpi rxbyte, char
	    brne mainloop
	 rcall blink
	 rjmp mainloop
      button_pressed:
         ;
         ; button pressed, send char
         ;
	 ldi txbyte, char
	 rcall putclick
	 rcall char_delay
         rjmp mainloop
