;Bikey Talkie
;by Tim Kyle
.include "tn45def.inc" ;Includes the ATTiny45/65/85 definitions file
.org 0x0000 ;Places the following code from address 0x0000

;The Following instructions and "nop"s are for handling interrupts.
rjmp RESET ;Take a Relative Jump to the RESET Label
rjmp Interrupt0 ;Handle an interrupt input on INT0 or PortB Pin 2 (case pin 7)
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
reti ;return from any other interrupts that might be thrown. They are not important.

Interrupt0:
ldi r16, 0x10 ; this represents about 1/3 of a second for a dit
rcall dah ; this produces a time delay long enough to handle switch bounce.
ldi r18, 0x00 ; Zero reference (there are easier ways I know)
cpse r18, r22 ; Was the unit on or off? If equal (off) skip 1 instruction
rjmp RESET
pop r20 ;pop the old program counter from the stack. It isn't important. R20 is a convenient place to put it.
sei ;Turn interrupts back on
rjmp LOOP

RESET: ;Reset Label
;Turn off all unnecessary clocks, timers, etc
ldi r16, 0x0F
out PRR, r16

;The momentary button is connected to DIP pin 8 (INT0, Port B Pin 2). It should normally be pulled up.
;The LED is connected directly between pin 3 (Port B pin 4) and Vcc. PWM replaces the resistor.
ldi r22, 0x00 ; Tell the interrupt process that the light is currently off.
sei ; set Global Interrupt Enable
;A high in the DDRB means we will use the pin for output, zeros will configure the pin for input.
;Unused pins will be configured as outputs to avoid unnecessary interrupts being thrown.
;Pin7 Pin6 Pin5 Pin4 Pin3 Pin2 Pin1 Pin0
;  1	1	 1	  1	   1	0	 1	  1	 = FB
ldi r16, 0xFB 
out DDRB, r16 ;Store this value in The PORTB Data direction Register
;A high in the PortB register would enable the pull up resistor for an input pin.
;A high in the PortB register sets an output pin high. (in this case turning off the light)
;Pin7 Pin6 Pin5 Pin4 Pin3 Pin2 Pin1 Pin0
;  0	0	 0	  1	   0	1	 0	  0	 = 14
ldi r16, 0x14 
out PORTB, r16
 
;GIMSK is the global interrupt mask. 
ldi r16, 0x40 ;Enable INT0 Interrupt
out GIMSK, r16 ;Enable INT0 Interrupt

;MCUCR Register
;00100001=21
;
;7 BODS - R/O Brown Out Detector during Sleep (page 38)
;6 PUD - Pull up disable (page 55,57)
;5 SE - Sleep Enable (Page 39) Set right before power down and unset right after power up.
;4 SM1 -Sleep Mode (page 39)
;3 SM0 -Sleep Mode (page 39)
;2 BODSE - R/O Brown Out Detector Sleep Disable (page 38)
;1 ISC01 - Interrupt sense control
;0 ISC00
;
;ISC
;00-Low Level Causes Interrupt
;01-Toggle Causes Interrupt
;10-Falling Edge
;11-Raising Edge
;
;SM
;00-Idle
;01-ADC Noise Reduction
;10-Power-down
;11-Reserved

;BODS  PUD  SE   SM1  SM0 BODSE ISC01 ISC00
;  0	0	 1	  1	   0	0	  0	    0	 = 30


ldi r16, 0x30 ;This is where the edge trigger for the interrupt and the sleep stuff is hiding.
out MCUCR, r16
sleep



LOOP: ;Loop Label
ldi r22, 0xff ; Tell the interrupt process that the light is currently on.
ldi r16, 0x08 ; this represents about 1/3 of a second for a dit
rcall dit
rcall dit
rcall dah
rcall dah
rcall dit
rcall dit
rcall space
rcall dah
rcall dah
rcall dit
rcall dit
rcall dah
rcall dah
rcall space
rjmp LOOP

;Subroutine Space. Tim Kyle Rev 0.1
;Waits the amount of time necessary for an interword space
;Uses Registers R16, R17 courteously (restores their previous values before return)
;Uses Labels 
;Requires: Wait
Space:
push r16 ;Save previous contents of R16 to the stack.
push r17 ;Save previous contents of R17 to the stack.
rcall WaitOff
rcall WaitOff
rcall WaitOff
pop r17
pop r16
ret


;Subroutine Dah. Tim Kyle Rev 0.1
;Waits the amount of time necessary for one dah
;Uses Registers R16, R17 courteously (restores their previous values before return)
;Uses Labels 
;Requires: Wait
Dah:
push r16 ;Save previous contents of R16 to the stack.
push r17 ;Save previous contents of R17 to the stack.
ldi r17, 0x04 ;Load Light On (the 4 keeps the button pull up resistor held high)
out PORTB, r17 ;Turn Light On
rcall WaitOn
rcall WaitOn
rcall WaitOn
ldi r17, 0x14 ;Turn Light Off (the 4 keeps the button pull up resistor held high)
out PORTB, r17 ;Turn Light Off
rcall WaitOff
pop r17
pop r16
ret


;Subroutine Dit. Tim Kyle Rev 0.1
;Waits the amount of time necessary for one dit
;Uses Registers R16, R17 courteously (restores their previous values before return)
;Uses Labels 
;Requires: Wait
Dit:
push r16 ;Save previous contents of R16 to the stack.
push r17 ;Save previous contents of R17 to the stack.
ldi r17, 0x04 ;Load Light On (the 4 keeps the button pull up resistor held high)
out PORTB, r17 ;Turn Light On
rcall WaitOn
ldi r17, 0x14 ;Turn Light Off (the 4 keeps the button pull up resistor held high)
out PORTB, r17 ;Turn Light Off
rcall WaitOff
pop r17
pop r16
ret


;Subroutine WaitOff. Tim Kyle Rev 0.1
;Waits the number of cycles specified in R16 * 1027 + 26 with the light off
;Uses Registers R16, R17, R18 courteously (restores their previous values before return)
;Uses Labels Wait, Wait1, Wait2
;If R16 is set to 0, cycles=28
;Requires: None
WaitOff:
push r16 ;Save previous contents of R16 to the stack.
push r17 ;Save previous contents of R17 to the stack.
push r18 ;Save previous contents of R18 to the stack.
ldi r17, 0x00 ;r17 is set to 0 because it is decremented to 255 before the compare is done.
ldi r18, 0x00 ;r18 is used as the 0 register to compare with because I opted to use cpse.
cpse r16, r18 ;if a zero is passed in R16 exit (otherwise it would be equivalent to 0xFF+1)
rjmp Wait1
rjmp Wait2
Wait1:
nop
nop
nop
nop
nop
nop
nop
dec r17
cpse r17, r18 ;Compare R1 and R3 (always 0) if they are equal skip the next instruction.
rjmp WAIT1
dec r16
cpse r16, r18
rjmp WAIT1
pop R18 ;Return previous contents of R18
pop R17 ;Return previous contents of R17
pop R16 ;Return previous contents of R16
ret
Wait2:
pop R18 ;Return previous contents of R18
pop R17 ;Return previous contents of R17
pop R16 ;Return previous contents of R16
ret
;End of Wait Subroutine

;Subroutine WaitOn. Tim Kyle Rev 0.1
;Waits the number of cycles specified in R16 * 1027 + 26 with the light on
;Uses Registers R16, R17, R18 courteously (restores their previous values before return)
;Uses Labels Wait, Wait1, Wait2
;If R16 is set to 0, cycles=28
;Requires: None
WaitOn:
push r16 ;Save previous contents of R16 to the stack.
push r17 ;Save previous contents of R17 to the stack.
push r18 ;Save previous contents of R18 to the stack.
push r19
push r20
ldi r17, 0x00 ;r17 is set to 0 because it is decremented to 255 before the compare is done.
ldi r18, 0x00 ;r18 is used as the 0 register to compare with because I opted to use cpse.
ldi r19, 0x04 ;Zeros for turning light on (the 4 keeps the button pull up resistor held high)
ldi r20, 0x14 ;One for Turning Light off (the 4 keeps the button pull up resistor held high)
cpse r16, r18 ;if a zero is passed in R16 exit (otherwise it would be equivalent to 0xFF+1)
rjmp WaitOn1
rjmp WaitOn2
WaitOn1:
out PORTB, r20 ;Turn Light off
nop
;nop
;nop
;nop
;nop
;nop
;nop
out PORTB, r19 ;Turn Light On
dec r17
cpse r17, r18 ;Compare R1 and R3 (always 0) if they are equal skip the next instruction.
rjmp WaitOn1
dec r16
cpse r16, r18
rjmp WaitOn1
pop R20 ;Return previous contents of R20
pop R19 ;Return previous contents of R19
pop R18 ;Return previous contents of R18
pop R17 ;Return previous contents of R17
pop R16 ;Return previous contents of R16
ret
WaitOn2:
pop R20 ;Return previous contents of R20
pop R19 ;Return previous contents of R19
pop R18 ;Return previous contents of R18
pop R17 ;Return previous contents of R17
pop R16 ;Return previous contents of R16
ret
;End of WaitOn Subroutine