;		IR REMOTE CONTROL
;	for Toshiba CATV Home Terminal model TCJ552B
;		Version 3.1
;	Copyright 2001 Tickets, All rights reserved
;
; * Use 3.58MHz resonator for proper operation
;
; NOTE: This code is written in Parallax SPASM assembler
;
	device	pic16f84,hs_osc,wdt_off,protect_off,pwrt_on
	ID	'IRRC'
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; DEFINITIONS
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Version
ver_major	=	'3'	; Major version
ver_minor	=	'1'	; Minor Version

; IR LED
led_out		=	PortA.4	; LED output/Active Low

time_h		=	22	; Cycles of H bit for 0.56msec
time_0		=	120	; L duration for data 0
time_1		=	250	; L duration for data 1
time_start	=	95	; Cycles of Leader (+255)

delay_repeat	=	90	; Adjust for 108msec interval
delay_gap	=	40	; Gap for first repeat code

bit_cnt		=	8	; Bit count

; Key scan
code_system	=	49h	; Toshiba
key_shift	=	1ch	; Key scan code of SHIFT
key_chup	=	0ch	;                  Channel Up
key_chdn	=	10h	;                  Channel Down
key_vrup	=	14h	;                  Volume Up
key_vrdn	=	18h	;                  Volume Down
data_shift	=	32	; Add to key code in shift mode
col0		=	PortB.4	; COLUMN scan ports (Input)
col1		=	PortB.5
col2		=	PortB.6
col3		=	PortB.7

no_key		=	0ffh	; Key matrix code for no key

; Port Settings
porta_dir	=	11100000b
portb_dir	=	11110000b
porta_ini	=	00010000b
portb_ini	=	00000000b
portb_pullup	=	OPTION.7	; 0 to enable PortB pull-up

; Control registers settings
mask_int	=	00000000b	; INTCON register
mask_opt	=	01111111b	; OPTION register

; Interrupt
int_rbif	=	INTCON.0	; PortB interrupt flag
int_toif	=	INTCON.2	; WDT
int_rbie	=	INTCON.3	; PortB interrupt enable
int_gie		=	INTCON.7	; General interrupt enable

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	org	0ch	; Top of available RAM

time_cntr	ds	1	; Cycle counter
delay_cntr	ds	1	; Delay counter
cntr_1msec	ds	1	; Counter for 1msec wait
cntr_15msec	ds	1	; Counter for 16msec wait
cntr_100msec	ds	1	; Counter for 100msec wait
cntr_1sec	ds	1	; Counter for 1sec wait
cntr_nmsec	ds	1	; Counter for N msec wait (N in W)

tx_data_buff	ds	1	; Store code to be transmitted
code_buff	ds	1	; Store result data code from key_scan
bit_cntr	ds	1	; Bit counter to send byte

key_row		ds	1	; Row for keyscan
key_column	ds	1	; Column for keyscan
key_matrix	ds	1	; Result key matrix code (0-63)
cntr_row	ds	1	; Counter for row scan
cntr_col	ds	1	; Counter for column scan
cntr_nokey	ds	1	; Counts key scan cycles to sense no-key
shift_32	ds	1	; Contains 00100000b in shift mode
flag_row	ds	1	; NZ if row scan get key press
flag_col	ds	1	; NZ if column scan get key press
flag_nokey	ds	1	; NZ if no key is pressed for a while
flag_continue	ds	1	; NZ if key press is continued

flag_error	ds	1	; 0:No error  NZ:Error

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	org	0		; Top of program
	jmp	start

interrupt_routine
	org	04h		; Interrupt vector/NOT USED
	
	reti			; End of interrupt
; Version number
	retw	'Ver.',ver_major,'.',ver_minor

;-------------------------
start
	call	init

;-------------------------
; MAIN LOOP
;-------------------------
main
	mov	PortA,#10h	; Set scan port output to L
	mov	PortB,#0h

	mov	w,PortB		; Clear PortB input mismatch
	clrb	int_rbif	; Clear RBIF for sure
	setb	int_rbie	; Enable PortB mismatch int

	sleep			; Sleep untill interrupt occurs
				; Process next instruction on interrupt
				;  No interrupt vector (04h) called
				;  since GIE is disabled
	clrb	int_rbie	; Disable PortB interrupt
	clrb	int_rbif	; Clear PortB interrupt flag

	call	delay_1msec	; Remove key bounces
	call	process_key	; Main Process

	goto	main		; Wait for next key
	
;--------------------------------------------
; KEY SCAN TABLE
; This should be placed within first 256 byte
;--------------------------------------------
key_table
	jmp	pc+w			; W contains matrix code
					; Returns data code in W
					; 55h should be ignored
	retw	12h,11h,0fh,15h,16h,1ch,55h,1dh
	retw	17h,00h,19h,14h,1bh,03h,02h,01h
	retw	1fh,06h,05h,04h,1ah,09h,08h,07h
	retw	1eh,18h,0bh,0ah,55h,10h,13h,0eh
	
	retw	0ffh,55h,43h,55h,55h,5dh,44h,4eh
	retw	55h,55h,45h,55h,55h,55h,5bh,55h
	retw	55h,55h,5ch,55h,42h,55h,5eh,55h
	retw	55h,55h,5fh,41h,55h,55h,0feh,40h

;-------------------------
; POWER ON INITIALIZE
;-------------------------
init
	mov	INTCON,#mask_int
	mov	OPTION,#mask_opt
	clrb	int_gie		; Disable GIE
				; Ports Direction
	mov	!ra,#porta_dir	; All PortA for output
	mov	!rb,#portb_dir	; RB0-3 for output, RB4-7 for output
	mov	PortA,#porta_ini	; row4-7 to L,led_out to H
	mov	PortB,#portb_ini	; row0-3 to L

	clr	shift_32	; Clear shift data (32)
	
	ret

;-------------------------
; Process Key Input
;-------------------------
process_key
	clr	flag_continue	; Prepare for new key input
:loop
	call	key_scan	; Scan keys
	
	cje	key_matrix,#no_key,:finish	; No key, do nothing

	cje	flag_continue,#0,:skip0		; Skip0 if not continue
	cje	key_matrix,#key_chup,:skip3	; Repeat Channel Up
	cje	key_matrix,#key_chdn,:skip3	; Repeat Channel Down
	cje	key_matrix,#key_vrup,:skip3	; Repeat Volume Up
	cje	key_matrix,#key_vrdn,:skip3	; Repeat Volume Down

	call	delay_15msec			; Eliminate key bounce
	goto	:skip2				; Don't process

:skip3					; Send Repeat code
	call	tx_leader		; 9.0msec leader
	call	delay_2msec		; 2.25msec gap
	call	tx_bit			; 0.56msec 1
	mov	w,#delay_repeat		; 108msec interval
	call	delay_nmsec
	goto	:skip2

:skip0
	cjne	key_matrix,#key_shift,:skip	; Skip if not shift
:wait_release
	call	key_scan	; Wait till SHIFT key is released
	cje	key_matrix,#key_shift,:wait_release
	
	mov	shift_32,#data_shift
	goto	:finish		; Shift data is set
	
:skip				; Normal transmission process
	mov	w,key_matrix	; Set key matrix code
	call	key_table	; Get Remote Control code
	mov	code_buff,w	; Set in transmit buffer
	
	call	tx_data		; Send IR

	clr	shift_32	; Reset shift mode
	
	mov	w,#delay_gap
	call	delay_nmsec	; Adjust gap before repeat code
:skip2
	mov	flag_continue,#1	; Set continue flag
	goto	:loop

:finish
	clr	flag_continue	; Key released
	call	delay_15msec	; Eliminate key bounce
	
	ret
	
;-------------------------
; KEY SCAN
;-------------------------
key_scan
	clr	key_matrix	; Initialize scan matrix

:loop1
	clr	key_column	; Prepare for new scan
	mov	PortA,#1fh	; Clear PortA
	mov	PortB,#0feh	; Set RB0 to low
	mov	cntr_col,#4	; Test four times
:loop2
	call	row_scan
	or	flag_row,#00h	; Test flag
	jnz	:finish		; Yes, key is pressed
	
	setb	c		;
	rl	PortB		; Next row
	inc	key_column	; Increment Key Matrix code
	
	djnz	cntr_col,:loop2

:loop3
	mov	PortA,#1eh	; Set RA0 to low
	mov	PortB,#0fh	; Clear PortB
	mov	cntr_col,#4	; Test four times
:loop4
	call	row_scan
	or	flag_row,#00h	; Test flag
	jnz	:finish		; Yes, key press is sensed
	
	dec	cntr_col
	jz	:skip		; Avoid RA4(LED) to be Low
	
	setb	c		; Column scan
	rl	PortA
	inc	key_column
	
	goto	:loop4
:skip
	mov	key_matrix,#no_key
	goto	:no_key		; No key is pressed
	
:finish
	mov	key_matrix,key_row
	and	key_column,#0000111b	; Use last 3 bits
	clrb	c
	rl	key_column	; Move to upward
	rl	key_column
	add	key_matrix,key_column
	add	key_matrix,shift_32	; Add 32 in shift mode
:no_key
	ret
;---------------
row_scan
	clr	flag_row
	clr	key_row
	jnb	col0,:finish
	inc	key_row
	jnb	col1,:finish
	inc	key_row
	jnb	col2,:finish
	inc	key_row
	jnb	col3,:finish
	inc	key_row
:exit
	ret

:finish
	inc	flag_row
	goto	:exit
	
;-------------------------
; TRANSMIT DATA
;-------------------------
tx_data
	call	tx_leader		; 9.0msec leader burst

	call	tx_bit1			; 4.5msec space (gap)
	call	tx_bit1
	call	tx_bit1

	mov	tx_data_buff,#code_system	; Send system code
	call	tx_byte

	mov	tx_data_buff,#code_system
	xor	tx_data_buff,#0ffh	; Invert system code
	call	tx_byte

	mov	tx_data_buff,code_buff	; Send Data code
	call	tx_byte
	
	mov	tx_data_buff,code_buff
	xor	tx_data_buff,#0ffh	; Invert data code
	call	tx_byte
	
	call	tx_bit			; End bit

;--------------------------
; TRANSMIT LEADER
;--------------------------
tx_leader			; Send 0 for 9.0msec
	mov	time_cntr,#0ffh	; Count 1
	call	:loop
	mov	time_cntr,#time_start	; Additional count
	call	:loop
	
	ret
	
:loop
	clrb	led_out		; Turn on LED
	nop
	nop
	nop
	nop
	nop
	nop
	setb	led_out		; Turn off LED
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	dec	time_cntr	; Count cycles
	jnz	:loop
	
	ret
	
;--------------------------
; TRANSMIT BYTE
;--------------------------
tx_byte				; TX data in tx_data
	mov	bit_cntr,#bit_cnt	; 8bits
	clc
:loop
	call	tx_bit		; Transmit LED pulse
	rr	tx_data_buff	; Adjust duration
	jnc	:skip0		; Skip if 0
	call	tx_bit1		; Bit 1
	jmp	:finish
:skip0
	call	tx_bit0		; Bit 0
:finish
	dec	bit_cntr
	jnz	:loop

	ret

;-------------------------
; BIT 0 DURATION
;-------------------------
tx_bit0
	mov	time_cntr,#time_0
:loop
	dec	time_cntr
	jnz	:loop
	
	ret

;-------------------------
; BIT 1 DURATION
;-------------------------
tx_bit1
	mov	time_cntr,#time_1
:loop
	dec	time_cntr
	nop
	nop
	jnz	:loop
	
	ret

;--------------------------
; TRANSMIT LED PULSE
;--------------------------
tx_bit
	mov	time_cntr,#time_h
:loop
	clrb	led_out		; Turn on LED
	nop
	nop
	nop
	nop
	nop
	nop
	setb	led_out		; Turn off LED
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	dec	time_cntr	; Count cycles
	jnz	:loop
	
	ret

;--------------------------
; DELAY ROUTINES
; (Multi entry Multi exit)
;--------------------------
delay_1sec
	mov	cntr_1sec,#10
:loop
	call	delay_100msec
	djnz	cntr_1sec,:loop
	
	ret
	
;------------
delay_nmsec
	mov	cntr_nmsec,w	; Delay W msec
:loop
	call	delay_1msec
	djnz	cntr_nmsec,:loop
	
	ret
;------------
delay_100msec
	mov	cntr_100msec,#100
:loop
	call	delay_1msec
	djnz	cntr_100msec,:loop
	
	ret
;------------
delay_15msec
	mov	cntr_15msec,#15
:loop
	call	delay_1msec
	djnz	cntr_15msec,:loop
	
	ret
;------------
delay_2msec
	call	delay_1msec
;------------
delay_1msec
	mov	cntr_1msec,#80h	; 1msec delay for 3.58MHz
:loop
	nop
	nop
	nop
	nop
	djnz	cntr_1msec,:loop
	
	ret


