#define x16F690

#ifdef x16F690
#include <p16F690.inc>
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
#endif

TRISKBD			equ	TRISC
PORTKBD			equ	PORTC
TRISSTAT		equ	TRISA
PORTSTAT		equ	PORTA
PORTCOLS		equ	PORTA
TRISROWS		equ	TRISB
PORTROWS		equ	PORTB
LED_BIT			equ	5
DIAL_SPEED_ADDR	equ	0xFF
;PORTKBD			equ	0x26

KBD_SCAN_SPEED	equ	0x80
K_0				equ	0x00
K_1				equ	0x01
K_2				equ	0x02
K_3				equ	0x03
K_4				equ	0x04
K_5				equ	0x05
K_6				equ	0x06
K_7				equ	0x07
K_8				equ	0x08
K_9				equ	0x09
K_SEQ			equ	0x0A
K_RST			equ	0x0B
K_ENT			equ	0x0C
K_DEL			equ	0x0D
K_TKO			equ	0x0E
K_ABT			equ	0x0F

#define			DIAL_SPEED_OFFSETT	0x03


DELAY MACRO DELAY_LENGTH
	movlw	DELAY_LENGTH
	movwf	TMP0
Delay_0
	decf	TMP0,f
	btfss	STATUS,Z
	goto	Delay_0
	ENDM

	cblock 0x20
	ROW_INDEX
	COL_INDEX
	BTN_ID
	KEY_ID
	BANK_ID
    TRG_BANK_ID
	NUM_LEN
	TMP0
	TMP1
	TMP2
    COUNTER
	DATA_EE_ADDR
	DATA_EE_DATA
	DELAY_0
	DELAY_1
	DELAY_COUNTER
	DIAL_SPEED
	NUM_ARRAY:0x10
	endc

	org 0x00
	goto	Main
	org 0x04
	goto	Main

Main
	call	InitPorts
	movlw	0xFF
	movwf	PORTSTAT
	goto	S_Normal

IfJump MACRO KEY,STATE
	movlw	KEY
	subwf	KEY_ID,w
	btfsc	STATUS,Z
	goto	STATE
	ENDM

S_Normal
	call	GetKey
	IfJump	K_ABT,S_CopyNumber
	IfJump	K_ENT,S_WaitForID
	IfJump	K_TKO,S_SetDialSpeed
	IfJump	K_DEL,S_DirectDial
	goto	S_Dial

S_CopyNumber
	bcf		PORTSTAT,LED_BIT
	call	GetKey
	bsf		PORTSTAT,LED_BIT
	IfJump	K_ABT,S_Normal
	movf	KEY_ID,w
	movwf	BANK_ID
	bcf		PORTSTAT,LED_BIT
	call	GetKey
	bsf		PORTSTAT,LED_BIT
	IfJump	K_ABT,S_Normal
	movf	KEY_ID,w
	movwf	TRG_BANK_ID
	; Convert BANK_ID and TRG_BANK_ID to EEPROM addresses
    swapf	BANK_ID,w
	andlw	0xF0
	movwf	BANK_ID
    swapf	TRG_BANK_ID,w
	andlw	0xF0
	movwf	TRG_BANK_ID
	; Set the counter
	movlw	0x10
	movwf	COUNTER
S_CopyNumber_CopyDigit
	movf	BANK_ID,w
	movwf	DATA_EE_ADDR
	call	EEPROM_Read
	movf	TRG_BANK_ID,w
	movwf	DATA_EE_ADDR
	call	EEPROM_Write
	incf	BANK_ID,f
	incf	TRG_BANK_ID,f
	decf	COUNTER,f
	btfss	STATUS,Z
	goto	S_CopyNumber_CopyDigit
	goto	S_Normal

S_DirectDial
	; Load dial speed from EEPROM
	movlw	DIAL_SPEED_ADDR
	movwf	DATA_EE_ADDR
	call	EEPROM_Read
	movf	DATA_EE_DATA,w
	movwf	DIAL_SPEED
	movlw	DIAL_SPEED_OFFSETT
	addwf	DIAL_SPEED,f
	bcf		STATUS,C
	rlf		DIAL_SPEED,f
	bcf		STATUS,C
	rlf		DIAL_SPEED,f
S_DirectDial_NextDigit
	bcf		PORTSTAT,LED_BIT
	call	GetKey
	bsf		PORTSTAT,LED_BIT
	IfJump	K_ABT,S_Normal
	IfJump	K_ENT,S_Normal
	IfJump	K_TKO,S_Normal
	IfJump	K_DEL,S_Normal
	movf	KEY_ID,w
	call	DialDigit
	goto	S_DirectDial_NextDigit

S_SetDialSpeed
	bcf		PORTSTAT,LED_BIT
	call	GetKey
	bsf		PORTSTAT,LED_BIT
	IfJump	K_ABT,S_Normal
	movf	KEY_ID,w
	andlw	0x07
	xorlw	0x07
	movwf	DATA_EE_DATA
	movlw	DIAL_SPEED_ADDR
	movwf	DATA_EE_ADDR
	call	EEPROM_Write
	goto	S_Normal

S_Dial
	; Load dial speed from EEPROM
	movlw	DIAL_SPEED_ADDR
	movwf	DATA_EE_ADDR
	call	EEPROM_Read
	movf	DATA_EE_DATA,w
	movwf	DIAL_SPEED
	movlw	DIAL_SPEED_OFFSETT
	addwf	DIAL_SPEED,f
	bcf		STATUS,C
	rlf		DIAL_SPEED,f
	bcf		STATUS,C
	rlf		DIAL_SPEED,f
	; Load number from EEPROM
	movlw	0x10
	movwf	NUM_LEN
	swapf	KEY_ID,w
	andlw	0xF0
	movwf	DATA_EE_ADDR
S_Dial_DialDigit
	call	EEPROM_Read
	movlw	0xFF
	subwf	DATA_EE_DATA,w
	btfsc	STATUS,Z
	goto	S_Normal
	movf	DATA_EE_DATA,w
	call	DialDigit
	incf	DATA_EE_ADDR,f
	decf	NUM_LEN,f
	btfss	STATUS,Z
	goto	S_Dial_DialDigit
	goto	S_Normal

S_WaitForID
	call	GetKey
	IfJump	K_ABT,S_Normal
	IfJump	K_ENT,S_Normal
	movf	KEY_ID,w
	movwf	BANK_ID
	clrf	NUM_LEN
	goto	S_CollectNumber

S_CollectNumber
	bcf		PORTSTAT,LED_BIT
	call	GetKey
	bsf		PORTSTAT,LED_BIT
	IfJump	K_ABT,S_Normal
	IfJump	K_ENT,S_StoreNumber
	movf	NUM_LEN,w
	addlw	NUM_ARRAY
	movwf	FSR
	movf	KEY_ID,w
	movwf	INDF
	incf	NUM_LEN,f
	movlw	0x0F
	subwf	NUM_LEN,w
	btfss	STATUS,Z
	goto	S_CollectNumber
	goto	S_StoreNumber

S_StoreNumber
	clrf	TMP0
	swapf	BANK_ID,w
	andlw	0xF0
	movwf	DATA_EE_ADDR
	movlw	NUM_ARRAY
	movwf	FSR
S_StoreNumber_WriteDigit
	movf	TMP0,w
	subwf	NUM_LEN,w
	btfsc	STATUS,Z
	goto	S_StoreNumber_WriteEnd
	movf	INDF,w
	movwf	DATA_EE_DATA
	call	EEPROM_Write
	incf	FSR,f
	incf	DATA_EE_ADDR,f
	incf	TMP0,f
	goto	S_StoreNumber_WriteDigit
S_StoreNumber_WriteEnd
	movlw	0xFF
	movwf	DATA_EE_DATA
	call	EEPROM_Write
	goto	S_Normal

EEPROM_Read
	banksel	DATA_EE_ADDR
	movf	DATA_EE_ADDR,w
	banksel	EEADR
	movwf	EEADR
	banksel	EECON1
	bcf		EECON1, EEPGD
	bsf		EECON1, RD
	banksel	EEDAT
	movf	EEDAT,w
	banksel	DATA_EE_DATA
	movwf	DATA_EE_DATA
	return

EEPROM_Write
	banksel	DATA_EE_ADDR
	movf	DATA_EE_ADDR,w
	banksel	EEADR
	movwf	EEADR
	banksel	DATA_EE_DATA
	movf	DATA_EE_DATA,w
	banksel	EEDAT
	movwf	EEDAT
	banksel	EECON1
	bcf		EECON1, EEPGD
	bsf		EECON1, WREN
	bcf		INTCON, GIE
	movlw	0x55
	movwf	EECON2
	movlw	0xAA
	movwf	EECON2
	bsf		EECON1, WR
	bsf		INTCON, GIE
EEPROM_Write_WaitForConfirmation
	btfsc	EECON1, WR
	goto	EEPROM_Write_WaitForConfirmation
	bcf		EECON1, WREN
	banksel	DATA_EE_ADDR
	return

InitPorts
	; Initialize KBD port
	banksel	PORTKBD
	clrf	PORTKBD
	clrf	PORTSTAT
	banksel	ANSEL
	clrf	ANSEL
	clrf	ANSELH
	banksel	TRISKBD
	movlw	0xF0
	movwf	TRISKBD
	clrf	TRISSTAT
	clrf	TRISROWS
	banksel	PORTKBD
	call	Mute
	return

GetKey
	call	KbdScan
	btfsc	STATUS,Z
	goto	GetKey
	movwf	KEY_ID
	call	KbdScan
	btfsc	STATUS,Z
	goto	GetKey
	subwf	KEY_ID,w
	btfss	STATUS,Z
	goto	GetKey
GetKey_WaitForNoKeyPressed
	call	KbdScan
	btfss	STATUS,Z
	goto	GetKey_WaitForNoKeyPressed
	sublw	0xFF
	btfss	STATUS,Z
	goto	GetKey
	movf	KEY_ID,w
	return

KbdScan
	; If button is down, returns its ID in the W register
    ; If no button is down, returns 0xFF in the W register and Z=1
	; If more than one btn is down, returns 0xFE in W and Z=1
    movlw	0x01
	movwf	ROW_INDEX
KbdScan_NextRow
	movlw	0xFF
	xorwf	ROW_INDEX,w
	movwf	PORTKBD
	DELAY	KBD_SCAN_SPEED
	movlw	0xF0
	andwf	PORTKBD,w
	xorlw	0xF0
	btfss	STATUS,Z
	goto	KbdScan_FoundKeyDown
	bcf		STATUS,C
	rlf		ROW_INDEX,f
	movlw	0x0F
	andwf	ROW_INDEX,f
	btfss	STATUS,Z
	goto	KbdScan_NextRow
	goto	KbdScan_ReturnNone
KbdScan_FoundKeyDown
	movwf	COL_INDEX
	swapf	COL_INDEX,f
	clrf	BTN_ID
	movf	ROW_INDEX,w
	call	IndexToID
	btfsc	STATUS,Z
	goto	KbdScan_ReturnInvalid
	movwf	BTN_ID
	bcf		STATUS,C
	rlf		BTN_ID,f
	bcf		STATUS,C
	rlf		BTN_ID,f
	movf	COL_INDEX,w
	call	IndexToID
	btfsc	STATUS,Z
	goto	KbdScan_ReturnInvalid
	xorwf	BTN_ID,f
	movf	BTN_ID,w
	bcf		STATUS,Z
	return
KbdScan_ReturnInvalid
	movlw	0xFE
	movwf	BTN_ID
	bsf		STATUS,Z
	return
KbdScan_ReturnNone
	movlw	0xFF
	movwf	BTN_ID
	bsf		STATUS,Z
	return

IndexToID
	; Converts the lower 4 bits of the W register to 2-bit ID;
	; Sets Z flag if invalid
	movwf	TMP0
	movlw	0x0F
	andwf	TMP0,f
	clrf	TMP1
	movlw	0x01
	movwf	TMP2
IndexToID_0
	movf	TMP2,w
	subwf	TMP0,w
	btfsc	STATUS,Z
	goto	IndexToID_ReturnID
    incf	TMP1,f
	movlw	0x0F
	andwf	TMP2,w
	btfsc	STATUS,Z
	goto	IndexToID_ReturnInvalid
	bcf		STATUS,C
	rlf		TMP2,f
	goto	IndexToID_0
IndexToID_ReturnInvalid
	movlw	0xFF
	bsf		STATUS,Z
	return
IndexToID_ReturnID
	movf	TMP1,w
	bcf		STATUS,Z
	return

DialDigit
	andlw	0x0F
	call	LookupDTMFPins
	movwf	TMP0
	btfss	TMP0,0
	bcf		PORTCOLS,0
	btfss	TMP0,1
	bcf		PORTCOLS,1
	btfss	TMP0,2
	bcf		PORTCOLS,2
	btfss	TMP0,4
	bcf		PORTROWS,4
	btfss	TMP0,5
	bcf		PORTROWS,5
	btfss	TMP0,6
	bcf		PORTROWS,6
	btfss	TMP0,7
	bcf		PORTROWS,7
	bcf		TMP0,LED_BIT
	btfsc	PORTSTAT,LED_BIT
	bsf		TMP0,LED_BIT
	bsf		PORTSTAT,LED_BIT
	call	ToneDelay
    call	Mute
	bcf		PORTSTAT,LED_BIT
	call	IntraToneDelay
	btfsc	TMP0,LED_BIT
	bsf		PORTSTAT,LED_BIT
	return

Mute
	bsf		PORTCOLS,0
	bsf		PORTCOLS,1
	bsf		PORTCOLS,2
	bsf		PORTROWS,4
	bsf		PORTROWS,5
	bsf		PORTROWS,6
	bsf		PORTROWS,7
	return

ToneDelay
IntraToneDelay
	movf	DIAL_SPEED,w
	movwf	DELAY_COUNTER
ToneDelayLoop
	call	UnitDelay
	movf	DELAY_COUNTER,f
	btfsc	STATUS,Z
	return
	decf	DELAY_COUNTER,f
	goto	ToneDelayLoop

UnitDelay
	movlw	0xFF
	movwf	DELAY_0
	movlw	0x03
	movwf	DELAY_1
UnitDelay_0
	decfsz	DELAY_0, f
	goto	$+2
	decfsz	DELAY_1, f
	goto	UnitDelay_0
	return

	org	0x201
LookupDTMFPins
	movwf	TMP0
	movlw	0x02
	movwf	PCLATH
	movf	TMP0,w
	addwf	PCL,f
	retlw	b'01111101'		; 0
	retlw	b'11101110'		; 1
	retlw	b'11101101'		; 2
	retlw	b'11101011'		; 3
	retlw	b'11011110'		; 4
	retlw	b'11011101'		; 5
	retlw	b'11011011'		; 6
	retlw	b'10111110'		; 7
	retlw	b'10111101'		; 8
	retlw	b'10111011'		; 9
	retlw	b'01111110'		; *
	retlw	b'01111011'		; #
	retlw	b'11111111'		;
	retlw	b'11111111'		;
	retlw	b'11111111'		;
	retlw	b'11111111'		;
	END


	END