;    Port Locations
;    PORTB
;          0: in, button1
;          1: in, Function select button (on INT0)
;          2: in, button 2         "
;          3: in, button 3       "
;          4: out,LED Function 1  LOW=LED on
;          5: out,LED Function 2    "
;          6: out,LED Function 3    "
;          7: out,LED Function 4    "
;
;          
;    PORTD General
;          0: in, Rotator pulse 0  (not on INT0)
;          1: out, TXD, data output
;          2: in,  INT0, wake up from any button
;          3: in,  INT1, wake up from rotator pulse 1
;          4: out, power for RF Transmitter LOW=RF on
;          5: in, button 4
;          6: wakeup from rotator hold
;
;    Commands that are sent:
;    lower nibble: 0001   function 1 activated
;                  0010   function 2 activated  
;                  0100   function 3 activated  
;                  1000   function 4 activated  
;
;    upper nibble: 0000:   rotator left
;                  0001:   rotator right
;                  1110:   button 1 pressed
;                  1100:   button 2 pressed
;                  1010:   button 3 pressed
;                  1000:   button 4 pressed
;
; General Description:
; During stand-by the processor is in sleep mode. It wakes up by a low level
; interrupt on INT0. Sleep mode can only be terminated by level interrupt.
; 
; Bits in PORTD
.equ RFTransmitter=4
.equ OffTime=225               ; about 1600ms at 1.8 Mhz

.include "2313def.inc"
.def argument0=r17             ; Argument for subroutine calls
.def keys=r14                  ; Keys read from port
.def DataToSend=r13
.def temp1=r20
.def temp=r16
.def FunctionStatus=r19        ; contanis which function is active
.def ProgStatus=r21
;**************************** code begins here *********************************
       rjmp reset
       rjmp AnyButtonPressed   ; external interrupt 0
       rjmp Rotator1           ; external interrupt 1
       reti                    ; timer 1 capture
       reti                    ; timer 1 compare
       rjmp PrepareSleep       ; timer 1 overflow
       reti                    ; timer 0 overflow
       reti                    ; uart rx complete
       reti                    ; uart urd empty
       reti                    ; uart tx complete
       reti                    ; comperator
;*************************** Interrupt handlers ********************************
; This function is called ecah time a button is pressed
;*******************************************************************************
AnyButtonPressed:
       rcall ResetSleepCounter
       rcall HoldWakeUpPin
       rcall EdgeInterrupt
       rcall SetLEDs
       rcall GetKeys           ; Get the button status into keys register
       sbrc keys,0
       rcall Button1Pressed    ; Function select
       sbrc keys,1
       rcall FunctionSelect    ; Function select
       sbrc keys,2
       rcall Button2Pressed    ;
       sbrc keys,3
       rcall Button3Pressed    ;
       sbrc keys,4
       rcall Button4Pressed    ;
       sbrc keys,5
       rcall Rotator0
       ldi argument0,250            ; time = (255-temp) * prescale * clock
       rcall Wait1ms
       ldi temp,0xFF           ; Clear interrupts that occurd due to button
       out GIFR,temp           ; ringing
       reti
;*************************** EXTINT1 *******************************************
; This function is called each time the wheel is rotated     
;*******************************************************************************
Rotator1:
       rcall ResetSleepCounter
       rcall HoldWakeUpPin
;       rcall EdgeInterrupt
;       rcall SetLEDs
       ldi argument0,250        ; time = (255-temp) * prescale * clock
       rcall Wait1ms
       in temp,PIND
       lsr temp
       in temp1,MCUCR
       eor temp,temp1
       sbrc temp,2             ; Then lets look if INT1 is still low or high
       rjmp EndRotation        ; otherwise end
       in temp,PIND        
       rol temp                ; shift direction into upper nibble
       rol temp
       rol temp
       rol temp
       andi temp,0b00010000    ; select just Bit0 (shifted)
       in temp1,PIND           ; Get PORTD
       rol temp1               ; shift INT1 into bit 5
       andi temp1,0b00010000   ; Get just INT1
       eor temp,temp1          ; Mix for Direction
       or  temp,FunctionStatus ; load Function into lower nibble
       mov argument0,temp      ; load result into argumenf for sub call
       rcall SendData          ; send out the data
       in temp,MCUCR           ; toggle the interrpt sensivity from falling
       ldi temp1,0b00000100    ; to rising edge and vice versa
       eor temp,temp1
       out MCUCR,temp
EndRotation:
       ldi temp,0xFF           ; Clear interrupts that occurd due to button
       out GIFR,temp           ; ringing
       reti
;******************************* GoAsleep **************************************
PrepareSleep:
       rcall LevelInterrupt
       rcall ReleaseWakeUpPin
       in temp,MCUCR
       ori temp,0b00110000     ; set sleep enable
       out MCUCR,temp
       rcall SwitchOffLEDs
       cbi PORTD,0             ; Switch off Pull up Resistors for Rotator
       cbi PORTD,3
       ldi ProgStatus,0x01
       reti       
;********************************* SendData ************************************
SendData:
       mov DataToSend,argument0
       cbi PORTD,RFTransmitter ; Switch on RF Transmitter
       ldi argument0,250
       rcall Wait1ms
       mov argument0,DataToSend       
       out UDR,argument0       ; Send Command Code 5 times
       rcall UDREWait
       out UDR,argument0
       rcall UDREWait
       out UDR,argument0
       rcall UDREWait
       out UDR,argument0
       rcall UDREWait
       out UDR,argument0
       rcall UDREWait
       ldi argument0,245
       rcall Wait1ms
       sbi PORTD,RFTransmitter ; shut down RF Transmitter
       ret
;******************************* GetKeys ********************************
; The routine loads the port status into the register keys
; keys 0: button 3         high=button pressed
;      1: function select
;      2: button 1 
;      3: button 2
;      4: button 4
;      5: rotator pulse 0
;      6:
;      7:

GetKeys:
       ldi temp,0x00
       mov keys,temp           ; no button pressed so far
       ldi argument0,250            ; time = (255-temp) * prescale * clock
       rcall Wait1ms           ; First lets wait until buttons have stabilized
       sbic PIND,2             ; Then lets look if INT0 is still low
       rjmp EndGetKeys         ; otherwise end
       in temp1,PINB           ; load keyboard status into temp
       com temp1               ; Convert to positive logic
       andi temp1,0x0F         ; Mask all bits with leds
;       ldi argument0,250            ; time = (255-temp) * prescale * clock;
;       rcall Wait1ms
;       in temp,PINB            ; laod keyboard again
;       com temp
;       andi temp,0x0F
;       cp temp1,temp           ; compare the two loaded values
;       brne EndGetKeys         ; goto end if they are not equal
       mov temp,temp1
       in temp1,PIND           ; now get the status from the other port
       com temp1               ; positive logic
       ror temp1               ; shift button4 to bit 4
       andi temp1,0b00010000   ; get just button 4       
       or temp,temp1           ; merge
;       in temp1,PIND           ; now get the status from the other port for rot
;       com temp1               ; positive logic
;       rol temp1
;       rol temp1               ; 0 0 0 0 0 0 0 0
;       rol temp1
;       rol temp1
;       rol temp1
;       andi temp1,0b00100000
;       or temp,temp1            ; merge rot into keys
       mov keys,temp           ; otherwise load keyboard into result register
EndGetKeys:
       ret             
;**************************** UDRWait ***********************************
; The function waits until UDRE is set to one
;************************************************************************
UDREWait:
       sbis USR,UDRE
       rjmp UDREWait
       ret
;****************************** TXCWait ****************************************
TXCWait:
       sbis USR,TXC
       rjmp TXCWait
       ret

;***************************** Button1pressed **********************************
Button1Pressed:
       ldi temp,0b11100000
       or temp,FunctionStatus
       mov argument0,temp
       rcall SendData
       ret
;***************************** Button2pressed **********************************
Button2Pressed:
       ldi temp,0b11000000
       or temp,FunctionStatus
       mov argument0,temp
       rcall SendData
       ret
;***************************** Button3pressed **********************************
Button3Pressed:
       ldi temp,0b10100000
       or temp,FunctionStatus
       mov argument0,temp
       rcall SendData
       ret
;***************************** Button4pressed **********************************
Button4Pressed:
       ldi temp,0b10000000
       or temp,FunctionStatus
       mov argument0,temp
       rcall SendData
       ret
;******************************** Rotator0 *************************************
Rotator0:
;       in temp,PIND
;       rol temp,
;       andi temp,0b00010000
;       or  temp,FunctionStatus ; load Function into lower nibble
;       mov argument0,temp      ; load result into argumenf for sub call
;       rcall SendData          ; send out the data
;       ret
;***************************** FunctionSelect **********************************
FunctionSelect:
       lsr FunctionStatus
       brcc NotAtEnd
       ldi FunctionStatus,0x02 ; 0x80 for all 4 funcs, here two are disabled 
NotAtEnd:
       rcall SetLEDs
       ret
;****************************** SetLEDs *********************************
SetLEDs:
       mov temp, FunctionStatus; Get the function status
       rol temp                ; shift into upper nibble to match port locations
       rol temp
       rol temp
       rol temp
       andi temp,0b11110000    ; take just the used functions
       com temp                ; LEDs are on with LOW
       ori temp,0b00001111     ; hold pull ups for buttons active
       out PORTB,temp
       ret
;***************************** SwitchOffLEDs ***********************************
SwitchOffLEDs:
       ldi temp,0b11111111
       out PORTB,temp
       ret
;******************************* Wait1ms ***************************************
Wait1ms:
       mov temp,argument0
       out TCNT0,temp
       ldi temp,0b00000101     ; Counter 0 counts on clk/1024
       out TCCR0,temp
       in temp,TIMSK
       ori temp,0b00000010     ; Timer 0 overflow enables interrupt
       out TIMSK,temp
       in temp,MCUCR
       ori temp,0b00100000     ; set idle enable
       out MCUCR,temp
       ldi temp,0b00000000     ; disable external INT0 and INT1 because it 
       out GIMSK,temp          ; may not hit during sleep
       ldi temp,0xFF           ; Clear interrupts that occurd due to button
       out GIFR,temp           ; ringing
       sei                     ; We have to set this because we are inside
       sleep                   ; an interrupt routine
       cli                     ; disable again
       ldi temp,0b11000000     ; enable external INT0 and INT1 again
       out GIMSK,temp
       ldi temp,0b00000000     ; Counter 0 stop
       out TCCR0,temp
       in temp,TIMSK
       andi temp,0b11111101     ; Timer 0 overflow interrupt disabled
       out TIMSK,temp
       ret
;******************************* ResetSleepTimer *******************************
ResetSleepCounter:
       sbi PORTD,0             ; Switch on Pull up Resistors for Rotator
       sbi PORTD,3
       ldi temp,OffTime        ; Starting value for counter 1
       ldi temp1,0x00
       out TCNT1H,temp
       out TCNT1L,temp1
       ldi ProgStatus,0x00
       ret
;**************************** HoldWakeUpPin ************************************
HoldWakeUpPin:
       cbi PORTD,6
       sbi DDRD,6
       ret
;***************************** RepeaseWakeUpPin ********************************
ReleaseWakeUpPin:
       sbi PORTD,6
       nop
       nop
       nop
       cbi DDRD,6
       ret
;****************************** EdgeInterrupt **********************************
EdgeInterrupt:
;       in temp,MCUCR
;       andi temp,0b11111010    ; set bit 0 and 2 to zero
;       ori  temp,0b00001010    ; set bit 1 and 3 to one
       ldi temp,0b00001010
       out MCUCR,temp
       ret
;***************************** LevelInterrupt **********************************
LevelInterrupt:
;       in temp,MCUCR
;       andi temp,0b11110000    ; low level on INT0, INT1 generates interrupt
       ldi temp,0b00001000
       out MCUCR,temp
       ret
;********************************** init ***************************************
init:
       ldi temp,0b11110000     ; Set inputs for buttons and outputs for LEDs
       out DDRB,temp
       ldi temp,0x0F           ; Aktivate all pull up resistors for buttons
       out PORTB,temp
       ldi temp,0b11010010     ; set Port D output except INT0,INT1, P0
       out DDRD,temp
       ldi temp,0b00101101     ; Aktivate all pull up resistors
       out PORTD,temp
       sbi UCR,TXEN            ; Enable Transmit
       ldi temp,23             ; set 4800 baud at 1.8MHz
       out UBRR,temp
       sbi ACSR,ACD	       ; shut down comparator to save power
       ldi temp,0b11000000     ; enable external INT0 and INT1
       out GIMSK,temp
       rcall EdgeInterrupt
       ldi temp,0xFF           ; Clear all accidentally occured interrupts
       out GIFR,temp
       ldi FunctionStatus,0x01 ; Function one activated
       rcall SetLEDs
       rcall HoldWakeUpPin
       sbi PORTD,RFTransmitter ; Switch off RF Transmitter
       ldi temp,0b00000101     ; Prescaler Counter 1 1024
       out TCCR1B,temp
       rcall ResetSleepCounter
       ldi temp,0b10000000     ; Timer1 overflow Interupt enable
       out TIMSK,temp
       sei                     ; enable interrupts globally
       ret
;******************************** main **********************************
reset:  
       ldi temp,RAMEND         ; Initialize stack pointer
       out SPL,temp
       rcall init              ; Initialize CPU and variables
mloop: 
       sbrc ProgStatus,0
       sleep
       rjmp mloop
       
