.include "e:\avrtools\appnotes\tn28def.inc"
;*********************************************************************************************
;*
;* Constants
;*
;*********************************************************************************************

.equ	pulses		= 256 - 32	; 256 - pulses per bit half
.equ	numberofbits	= 30		; 2 * number of bits to transfer + 1

.equ	invPA2ovf	= (1<<3) | 2
.equ	activePA2ovf	= (2<<3) | 2
.equ	inactivePA2ovf	= (3<<3) | 2

.equ	noMOD		= 0		; MODCR value for no output
.equ	F38KHz_D25	= (2<<3) | 3	; MODCR value for 38KHz output, 25% dutycycle
.equ	F38KHz_D33	= (3<<3) | 2	; MODCR value for 38KHz output, 33% dutycycle
.equ	F38KHz_D50	= (5<<3) | 1	; MODCR value for 38KHz output, 50% dutycycle
.equ	F38KHz_D67	= (3<<3) | 4	; MODCR value for 38KHz output, 67% dutycycle
.equ	F38KHz_D75	= (2<<3) | 5	; MODCR value for 38KHz output, 75% dutycycle
	
.equ	FAULT		= 0xFF


;*********************************************************************************************
;*
;* Register definitions
;*
;*********************************************************************************************

.def	select		= R0		; Register to hold MSB of transmission
.def	command		= R1		; Register to hold LSB of transmission
.def	zero		= R2		; Register preset to zero
.def	allhigh		= R27		; Register preset to 0xFF

.def	temp		= R16		; Temporary register
.def	toggle		= R17		; Register to contain toggle bit value
.def	toggle_mask	= R18		; Register to contain toggle mask 

.def	row_scan 	= R19		; Scan value Row
.def	row_saved	= R20		; Saved value Row
.def	col_scan 	= R21		; Scan value Col
.def	col_saved	= R22		; Saved value Col

.def	ptr		= R23		; Pointer value used with the lookup table
.def	old_ptr		= R24		; Last lookup table pointer value (needed to calculate toggle bit)
.def	keys		= R25		; Value used to count number of pressed keys

.def	bitnumb		= R26		; Register which contains the number of bits to be transfered 


;*********************************************************************************************
;*
;* Interrupt Vectors
;*
;*********************************************************************************************

;.cseg
.org	0x0000
	rjmp	reset			; Reset vector

.org	LLINTaddr			;Low level Interrupt Vector Address
	rjmp	send

.org 	OVF0addr
	rjmp	bitfinished

.org codestart

;***** RC5 Lookup Table;
;*
;* Format of data should be in binary 
;* 11XSSSSSCCCCCC11
;* Here the command word is shown as 0xC003 | (SYSCODE << 8) | (command << 2)
;*
;***********************************************************************************

.equ	TV		= 0		; The system code for TV's
.equ	VCR		= 5		; The system code for VCR's

lookup:
.dw 0xC003 | (TV << 8)  | (1 << 2)	;16: Channel 1
.dw 0xC003 | (TV << 8)  | (4 << 2)	;17: Channel 4
.dw 0xC003 | (TV << 8)  | (7 << 2)	;18: Channel 7
.dw 0xC003 | (TV << 8)  | (33 << 2)	;19: Channel -
.dw 0xC003 | (VCR << 8) | (50 << 2)	;20: Fast Rewind
.dw 0xC003 | (TV << 8)  | (29 << 2)	;21: Contrast -
.dw 0xC003 | (TV << 21) | (21 << 2)	;22: Color -
.dw 0xC003 | (TV << 8)  | (17 << 2)	;23: Volume -

.dw 0xC003 | (TV << 8)  | (2 << 2)	;08: Channel 2
.dw 0xC003 | (TV << 8)  | (5 << 2)	;09: Channel 5
.dw 0xC003 | (TV << 8)  | (8 << 2)	;10: Channel 8
.dw 0xC003 | (TV << 8)  | (0 << 2)	;11: Channel 0
.dw 0xC003 | (VCR << 8) | (53 << 2)	;12: Play
.dw 0xC003 | (TV << 8)  | (28 << 2)	;13: Contrast +
.dw 0xC003 | (TV << 8)  | (20 << 2)	;14: Color +
.dw 0xC003 | (TV << 8)  | (16 << 2)	;15: Volume + 

.dw 0xC003 | (TV << 8)  | (3 << 2)	;00: Channel 3
.dw 0xC003 | (TV << 8)  | (6 << 2)	;01: Channel 6
.dw 0xC003 | (TV << 8)  | (9 << 2)	;02: Channel 9
.dw 0xC003 | (TV << 8)  | (32 << 2)	;03: Channel +
.dw 0xC003 | (VCR << 8) | (52 << 2)	;04: Fast Forward
.dw 0xC003 | (TV << 8)  | (14 << 2)	;05: Normal
.dw 0xC003 | (TV << 8)  | (12 << 2)	;06: Standby
.dw 0xC003 | (TV << 8)  | (13 << 2)	;07: Mute


;*** Reset handler **************************************************
;*
;* Executed on reset
;*
;********************************************************************

reset:

;*** Must set up Stack to be able to run in emulator..	
;	ldi    r16,0x5F+3*2    		;set up a three level deep stack
;	out  0x3d,r16


	clr	zero			; Initialize "zero" register
	ser	allhigh			; Initialize "allhigh" register
        clr     bitnumb                 ; Initialize bitcounter register

	out	DDRD,allhigh            ; Set Port D as output

	out	PORTA,allhigh	        ; No IR output, all other PORTA pins pulled high
	sbi	PACR,PA2HC		; Enable high current driver

	sbi     ICR,TOIE0		; Enable Timer Overflow

	ldi	temp,F38KHz_D25		; Set up hardware modulator
	out	MODCR,temp

	ldi	toggle_mask, 0b00100000	; Bit 5 is the toggle bit


;*** Main loop ******************************************************
;*
;* Code executed after each interrupt
;*
;********************************************************************

main_loop:
        cli                             ; Disable interrrupts
        tst     bitnumb                 ; Is transmission finished
        breq    Pwdn_mode                 
                                        ; Transmission not complete
        ldi     temp,(1<<PLUPB)|(1<<SE) ; Enable IDLE mode
                                        
        rjmp    pwdn_enable             ; Enter IDLE mode
        
Pwdn_mode:
        sbi     ICR,LLIE                ; Enable low level interrupt when transmission complete
        ldi     temp,(1<<PLUPB)|(1<<SE)|(1<<SM)
                                        ; Enable PowerDown mode
pwdn_enable:
        out     MCUCS,temp

        sei                             ; Enable interrupts
        sleep                           ; Enter powerdown
        
        rjmp    main_loop               ; Loop to top of main loop each interrupt
                                        
                                 
;*** Low level interrupt handler ************************************
;*
;* Executed on low level interrupt (key pressed, no transmission)
;*
;********************************************************************
                                 
                                        
send:
        cbi     ICR,LLIE                ; Disable low level interrupt during transmission                         

;*** find_command ***************************************************
;*
;* Scans keyboard and stores the correct word to transfer in the 
;* R1:R0 register pair.
;*
;* Registers used : temp, row_scan, col_scan
;* Flags used : C
;*
;* Format:  
;*                R1                        R0
;*
;*       St St T0 S4 S3 S2 S1 S0 - C5 C4 C3 C2 C1 C0 x1 x0
;*       ----- -- --------------   ----------------- -----
;*         |   |  |System code     |Command          |Unused
;*         |   |
;*         |   +- Toggle Bit
;*         +----- Start Bits
;*
;********************************************************************
	ldi	keys,193		; Set keys to 193, add (8*8-1) | 0xFF = 0 for valid input
	ldi	col_scan, 1		; Initialize scan of first Column
cont_col_scan:
	out	DDRD,col_scan		; Set One Column to output, the rest tristated.
	out	PORTD,zero		; Write "0" to the output, tristate all other lines
	nop
	nop
	in	row_scan,PINB		; Read response from input pins
	
	cpi	row_scan,0xff		; Any key pressed?
	brne	key_pressed		; If yes then branch to count routine

	subi	keys,-8			; if no keys pressed, add 8 to keys
	
ret_key_pressed:

	out	PORTD,allhigh		; Pull all input pins high
	out	DDRD,allhigh

	lsl	col_scan		; Check next line on the keyboard
	brcc	cont_col_scan		; If bit is not shifted through, continue scan
	
	ldi	ptr,fault		; Initialize to error value.
	tst	keys			; One, and only one zero should have been found in row scan.
	brne	ch_end			; If number of ones found != 63 then return with faulty ptr

fl2:
	inc	ptr
	lsr	row_saved
	brcs	fl2			; until: ptr contains binary value of "row"

fl3:
	sbrc	col_saved,0		; test if lsb is one -> current column contains the pressed button
	rjmp	fcont			; If correct column, value calculated
	subi	ptr, -8			; If not correct column, add 8 to pointer value
	lsr	col_saved		; Test next column
	rjmp	fl3

fcont:
	ldi	ZL,low(lookup)*2
	ldi	ZH,high(lookup)*2
	lsl	ptr			; Adjust pointer value to compensate for byte/word wide addressing
	add	ZL,ptr			; Add pointer value to lookup table base address
	adc	ZH,zero
	
	lpm				; Load low byte in transmission (last byte to transfer)
	mov	command,r0		; Move low byte to correct storage position
	inc	ZL			; Select next byte in lookup table
	lpm				; Load high byte in transmission (first byte to transfer)
	
	cp	old_ptr,ptr		; is it a new command?
	breq	same_ptr		; Do not invert togglebit if same command
	eor	toggle,toggle_mask	; Invert toggle bit
same_ptr:
	bst	toggle,5		; Copy Toggle bit to T-flag
	bld	select,5		; Insert Toggle bit into syscode byte from T-flag
	
;***********************************************************************************
;*
;* Code to start a transmit sequence
;* Transmits 14 bits, bit 1 in input command must be 1 to ensure
;* glitch free operation
;*
;***********************************************************************************
	ldi	temp,inactivePA2ovf	; Ensure no output at start of transmission
	out	TCCR0,temp		; Inserts a dummy inactive period of 288*38KHz 
					; cycles at start of each transmission
		
	ldi	bitnumb,numberofbits	; Initialize bitcounter

ch_end:
	out	DDRD,allhigh		; Set keyboard in "detect" mode
	out	PORTD,zero		; Restore passive scan pattern on keyboard

	mov	old_ptr,ptr		; Save ptr value,
	
        ret                             ; Return from interrupt

;***********************************************************************************
;*
;* Code to store away keypad data and find number of pressed keys in this column
;*
;***********************************************************************************
	
key_pressed:
	mov	col_saved,col_scan	; Store Column value
	mov	row_saved,row_scan	; Store Row value
cont_row_scan:
	sbrc	row_scan,0		; Bit 0 = 1
	inc	keys			; Increase for each logical one found
	lsr	row_scan		; Rotate row value one plase to the left
	brne	cont_row_scan		; Continue until all one's in row is gone
	rjmp	ret_key_pressed		

;*** transmit *******************************************************
;*
;* Sends the complete syscode and command stored in the register pair 
;* select:command.
;*
;********************************************************************

bitfinished:
	dec	bitnumb			; Decrease bitcounter
	breq	finished		; If all bits have been transmitted, end transmission

	ldi	temp,pulses		; Reload timer 
	out	TCNT0,temp
	
	sbrc	bitnumb,0		; Is this the second half of this bit transfer?
	rjmp	firsthalf

	ldi	temp,invPA2ovf		; If second half, Load Invert PA2 On Next Compare
	rjmp	intfin

finished:
	sbic	TCCR0,CS02		; Was last interrupt longwait?
	rjmp	end_longwait		; Signal end of transmit

	ldi	bitnumb,1		; Load bitcounter to ensure correct operation

	ldi	temp,207		; Preload counter to give 50176 cycles delay
	out	TCNT0,temp		; The sending of the next byte will give an extra delay
					; of 3456 cycles, giving a minimum of 53632 cycles between
					; transmissions
	ldi	temp,5			; Set prescaler	to CK/1024

	rjmp	intfin			

end_longwait:
	out	TCCR0,zero		; Disable timer after command and longwait
					; finished
	in	temp,PINB		; Check keybord for to ensure correct operation of toggle bit
	cpi	temp,0xFF		
	brne	NotSetFault		; If keys pressed, proceed 
	
	ldi	old_ptr,fault		; If no keys pressed, load pointer with error value to ensure correct
					; operation of the toggle bit

NotSetFault:
	ret				; Return from interrupt, transmission complete
		
firsthalf:
	lsl	command			; Move output bit to carry
	rol	select

	brcc	outlow			; Next bit is a low value
	
	ldi	temp,inactivePA2ovf	; Set next interval to output signal
	rjmp	intfin
	
outlow:
	ldi	temp,activePA2ovf	; Set next interval to output no signal

intfin:					; Return from interrupt, not finished transmission
	out	TCCR0,temp		; Set moulator options/timer prescaler
	ret
