;**********************************************************************
; alpha.asm  010915 by K.I NPC                                        *
;**********************************************************************
; ePLL writer for PIC16F873                                           *
;                                                                     *
;**********************************************************************


	list      p=16f873            ; list directive to define processor
	#include <p16f873.inc>        ; processor specific variable definitions
	
	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _WRT_ENABLE_ON & _LVP_OFF & _CPD_OFF

; CP		: flash program memory Code Protect [ALL,HALF,UPPER_256,OFF]
; CPD		: data memory Code Protect [ON,OFF]
; WDT		: Watch Dog Timer [ON,OFF]
; BODEN		: BrownOut reset ENable [ON,OFF]
; PWRTE		: PoWeR up Timer Enable (72ms) [ON,OFF]
; HS OSC	: High Speed mode OSC
; XT OSC	; XT OSC (normal speed)
; LP OSC	; Low Power OSC
; RC OSC	: RC mode OSC
; WRT_ENABLE	: flash program memory WRiTe ENABLE [ON,OFF]
; LVP		: Low Voltage Programming [ON,OFF]
; DEBUG		: incircuit DEBUG mode [ON,OFF]


;***** MACRO DEFINITIONS
;----------------------------------------------------------------------

Message		MACRO	TABLE
		movlw	low(TABLE)
		call	print_lcd
		ENDM

;***** CONSTANT DEFINITIONS
;----------------------------------------------------------------------

PADIR		EQU	B'00000000'	;TRISA	1:In, 0:Out
PBDIR		EQU	B'00000000'	;TRISB	1:In, 0:Out
PCDIR		EQU	B'10111111'	;TRISC	1:In, 0:Out
PAINI		EQU	B'00000000'	;PORTA Initial value
PBINI		EQU	B'00000000'	;PORTB Initial value
PCINI		EQU	B'00000000'	;PORTC Initial value
ADCON0_INI	EQU	B'00000000'	;ADCON0 Initial value
ADCON1_INI	EQU	B'00000111'	;ADCON1 Initial value
OPTION_INI	EQU	B'10000000'	;OPTION Initial value

LED		EQU	0		;LCD port bit
RW		EQU	1		;LCD port bit
RS		EQU	2
E		EQU	3

DOWN		EQU	0		;PUSH button
LEFT		EQU	1
RIGHT		EQU	2
UP		EQU	3
CANCEL		EQU	4
OK		EQU	5

LE		EQU	0		;ePLL interface
DAT		EQU	1
CLK		EQU	2
EPCLK		EQU	3
VPP		EQU	4
VPPCON		EQU	5

;----------------------------------------------------------------------
		RADIX	DEC

		CBLOCK	20H
;----------------------------------------------------------------------
		w_temp
		status_temp
		pclath_temp

		table_index		; table index
		wait_cn			; wait routine counter
		wait_cn2		; wait routine counter2
		lcd_adrs		; LCD address
		lcd_data		; LCD data
		lcd_rdata		; LCD read data
		sw_data			; Switch data
		line			; general buffer
		cnt			; general counter
		cnt2			; general counter
		ee_adrs			; EEPROM address
		ee_data			; EEPROM data
		s_data			; send data
		n0,n1,n2,n3,n4,n5,n6,n7	; N-counter
		r0,r1,r2,r3,r4,r5,r6,r7	; R-counter
;----------------------------------------------------------------------
		ENDC


;**********************************************************************
		ORG     0x000             ; processor reset vector
		clrf    PCLATH            ; ensure page bits are cleared
  		goto    main              ; go to beginning of program

		ORG     0x004             ; interrupt vector location
		movwf   w_temp            ; save off current W register contents
		movf	STATUS,w          ; move status register into W register
		bcf     STATUS,RP0        ; ensure file register bank set to 0
		movwf	status_temp       ; save off contents of STATUS register

;----------------------------------------------------------------------
;		interrupt routine
;----------------------------------------------------------------------
;		interrupt routine here!
		

		bcf     STATUS,RP0        ; ensure file register bank set to 0
		movf    status_temp,w     ; retrieve copy of STATUS register
		movwf	STATUS            ; restore pre-isr STATUS register contents
		swapf   w_temp,f
		swapf   w_temp,w          ; restore pre-isr W register contents
		retfie                    ; return from interrupt

;----------------------------------------------------------------------
;		Main routine
;----------------------------------------------------------------------
main
		call	setup_port
		call	setup_lcd
		call	setup_counter

		Message	msg_start
		movlw	1
		call	blink_led

		call	test_switch

set_count
		movlw	2
		call	blink_led
		call	clear_lcd
		clrf	PORTA
		Message	msg_set_count

		movlw	16
		movwf	cnt
		movlw	n0
		movwf	FSR
set_count_loop
		movf	INDF,0
		call	write_lcd
		incf	FSR,1
		decfsz	cnt,1
		goto	set_count_loop

		call	input_lcd
write_count
		movlw	3
		call	blink_led
		call	clear_lcd
		bsf	PORTA,VPP
		Message	msg_write

		call	test_switch
		btfsc	sw_data,CANCEL
		goto	set_count
		btfss	sw_data,OK
		goto	write_count

		call	send_ncount
		call	send_rcount
		call	send_vpp
rec_count
		movlw	16
		movwf	cnt
		movlw	n0
		movwf	FSR
rec_count_loop
		movf	INDF,0
		movwf	ee_data
		movf	FSR,0
		addlw	-n0
		movwf	ee_adrs
		call	write_eeprom
		incf	FSR,1
		decfsz	cnt,1
		goto	rec_count_loop

		goto	set_count


;-----------------------------------------------------------------------------------
;	Setup Port
;-----------------------------------------------------------------------------------
setup_port
		movlw	ADCON0_INI
		movwf	ADCON0

		movlw	PAINI
		movwf	PORTA
		movlw	PBINI
		movwf	PORTB
		movlw	PCINI
		movwf	PORTC

		bsf	STATUS,RP0

		movlw	ADCON1_INI
		movwf	ADCON1

		movlw	PADIR
		movwf	TRISA
		movlw	PBDIR
		movwf	TRISB
		movlw	PCDIR
		movwf	TRISC
 
		movlw	OPTION_INI
		movwf	OPTION_REG
 
		bcf	STATUS,RP0

		return

;-----------------------------------------------------------------------------------
;	Setup LCD module (SC1602)
;-----------------------------------------------------------------------------------
setup_lcd

		movlw	15			; wait 15ms
		call	wait_ms
		
		bcf	PORTB,RS		; RS='L'
		movlw	00110000b
		call	write_lcdx
		movlw	5			; wait 4.1ms
		call	wait_ms
		
		movlw	00110000b
		call	write_lcdx
		call	wait_100us		; wait 100us
	
		movlw	00110000b
		call	write_lcdx
		
		movlw	00100000b
		call	write_lcdx
	;
		movlw	00101000b	;4bit,2line,5x7dot
		call	write_lcd
		
		movlw	00001000b	;display off
		call	write_lcd

		movlw	00000001b	;Clear display
		call	write_lcd

		call	wait_2ms	; wait 2ms
		
		movlw	00000110b	;000001IS, Inc/Dec,Shift
		call	write_lcd
		
		movlw	00001111b	;00001DCB, Disp,Curs,Blink
		call	write_lcd

		movlw	0
		movwf	lcd_adrs

		return

;-----------------------------------------------------------------------------------
;	Setup Counter
;-----------------------------------------------------------------------------------
setup_counter
		movlw	16
		movwf	line

		movlw	n0
		movwf	FSR
		movlw	ee_ncounter
		movwf	ee_adrs
setup_c_loop
		call	read_eeprom	;copy EEPROM-->File Reg.
		movwf	INDF

		incf	FSR
		incf	ee_adrs
		decfsz	line,1
		goto	setup_c_loop

		return

;-----------------------------------------------------------------------------------
;	Blink LED
;-----------------------------------------------------------------------------------
blink_led
		movwf	line
blink_led_loop
		bsf	PORTB,0			; LED ON
		call	wait_250ms		; wait 250ms
		bcf	PORTB,0			; LED OFF
		call	wait_250ms		; wait 250ms

		decfsz	line,1
		goto	blink_led_loop

		return

;-----------------------------------------------------------------------------------
;		LCD write routine half
;-----------------------------------------------------------------------------------
write_lcdx
		movwf	lcd_data
write_lcdx0
		movf	PORTB,0
		movwf	line
		movlw	0x0f
		andwf	line,1
		movlw	0xf0
		andwf	lcd_data,0
		iorwf	line,0
		movwf	PORTB			; output PORTB
		
		bsf	PORTB,E			; E='H'
		bcf	PORTB,E			; E='L'
lcd_skip
		call	wait_40us		; wait 40us

		return

;-----------------------------------------------------------------------------------
;		LCD write routine
;-----------------------------------------------------------------------------------
write_lcd
		movwf	lcd_data
write_lcd0
		call	write_lcdx0
		
		swapf	lcd_data,1
		call	write_lcdx0
		swapf	lcd_data,1

		return

;-----------------------------------------------------------------------------------
;		LCD read routine half
;-----------------------------------------------------------------------------------
read_lcdx
		movlw	0x0f
		andwf	lcd_rdata,1

		bsf	PORTB,E			; E='H'
		call	wait_40us		; wait 40us
		
		movlw	0xf0
		andwf	PORTB,0
		iorwf	lcd_rdata,1

		bcf	PORTB,E			; E='L'

		return

;-----------------------------------------------------------------------------------
;		LCD read routine	(TEST, Don't use)
;-----------------------------------------------------------------------------------
read_lcd
		bsf	PORTB,RS		; RS='H'
		bsf	PORTB,RW		; RW='H'
		clrf	lcd_rdata
read_lcd0
		call	read_lcdx
		
		swapf	lcd_rdata,1
		call	read_lcdx
		swapf	lcd_rdata,1

		bcf	PORTB,RW		; RW='L'

		return

;-----------------------------------------------------------------------------------
;		LCD clear display
;-----------------------------------------------------------------------------------
clear_lcd
		bcf	PORTB,RS		; RS='L'
		movlw	00000001b
		call	write_lcd
		call	wait_2ms		; wait 2ms

		return

;-----------------------------------------------------------------------------------
;		Move cursor
;-----------------------------------------------------------------------------------
curs_lcd
		movwf	lcd_adrs
curs_lcd0
		bcf	PORTB,RS		; RS='L'
		movlw	0x4f
		andwf	lcd_adrs,1
		movlw	0x80			; DDRAM adrs set mode
		iorwf	lcd_adrs,0
		call	write_lcd
		call	wait_40us		; wait 40us

		bsf	PORTB,RS		; RS='H'

		return

;-----------------------------------------------------------------------------------
;		repair hex
;-----------------------------------------------------------------------------------
repair_hex
		movlw	':'
		subwf	INDF,0
		skpnz
		retlw	'A'

		movlw	'/'
		subwf	INDF,0
		skpnz
		retlw	'F'

		movlw	'@'
		subwf	INDF,0
		skpnz
		retlw	'9'

		movlw	'G'
		subwf	INDF,0
		skpnz
		retlw	'0'

		movfw	INDF
		return
	
;-----------------------------------------------------------------------------------
;		repair FSR
;-----------------------------------------------------------------------------------
repair_fsr
		movlw	n0-1
		subwf	FSR,0
		skpnz
		retlw	n0+11

		movlw	n0+4
		subwf	FSR,0
		skpnz
		retlw	n0+8

		movlw	n0+7
		subwf	FSR,0
		skpnz
		retlw	n0+3

		movlw	n0+12
		subwf	FSR,0
		skpnz
		retlw	n0

		movfw	FSR
		return
	
;-----------------------------------------------------------------------------------
;		clk one shot	(CLK : about 25kHz)
;-----------------------------------------------------------------------------------
clk_one
;		call	wait_100ms		;>>>>>>>> for DEBUG
		call	wait_40us		; wait 40us
		bsf	PORTA,CLK
;		call	wait_100ms		;>>>>>>>> for DEBUG
		call	wait_40us		; wait 40us
		bcf	PORTA,CLK

		return
;-----------------------------------------------------------------------------------
;		send hex MSB first
;-----------------------------------------------------------------------------------
send_hex
		clrw
		btfsc	INDF,6
		movlw	-7
		addwf	INDF,0
		movwf	s_data

		movlw	4		;send 4bit = 1nibble
		movwf	cnt
send_hex_loop
		btfsc	s_data,3
		bsf	PORTA,DAT
		btfss	s_data,3
		bcf	PORTA,DAT
		call	clk_one
		rlf	s_data,1

		decfsz	cnt,1
		goto	send_hex_loop

		return
	
;-----------------------------------------------------------------------------------
;		send hex MSB first
;-----------------------------------------------------------------------------------
send_hex4
		movlw	4		; send 4nibble = 2byte
		movwf	cnt2
send_hex4_loop
		call	send_hex
		incf	FSR,1
		decfsz	cnt2,1
		goto	send_hex4_loop

		return
	
;-----------------------------------------------------------------------------------
;		send LE
;-----------------------------------------------------------------------------------
send_le
		call	wait_100us		; wait 100us
		bsf	PORTA,LE
;		call	wait_100ms		;>>>>>>>> for DEBUG
		call	wait_40us		; wait 40us
		bcf	PORTA,LE
		call	wait_100us		; wait 100us

		return

;-----------------------------------------------------------------------------------
;		send counter
;-----------------------------------------------------------------------------------
send_ncount
		movlw	n0
		movwf	FSR
		call	send_hex4
		bcf	PORTA,DAT
		call	clk_one

		call	send_le

		Message	msg_wr_ncount
		call	wait_500ms

		return

;-----------------------------------------------------------------------------------
;		send counter
;-----------------------------------------------------------------------------------
send_rcount
		movlw	r0
		movwf	FSR
		call	send_hex4
		bsf	PORTA,DAT
		call	clk_one
		bcf	PORTA,DAT

		call	send_le

		Message	msg_wr_rcount
		call	wait_500ms

		return
	
;-----------------------------------------------------------------------------------
;		send vpp	(EPCLK : about 800Hz)
;-----------------------------------------------------------------------------------
send_vpp
		bsf	PORTA,VPPCON
		call	wait_100us		; wait 100us

		movlw	4			; 4 pulse
		movwf	cnt
send_vpp_loop
		bsf	PORTA,EPCLK
;		call	wait_100ms		;>>>>>>>> for DEBUG
		call	wait_625us		; wait 625us
		bcf	PORTA,EPCLK
;		call	wait_100ms		;>>>>>>>> for DEBUG
		call	wait_625us		; wait 625us

		decfsz	cnt,1
		goto	send_vpp_loop

		call	wait_100us		; wait 100us
		bcf	PORTA,VPPCON

		Message	msg_wr_vpp
		call	wait_500ms

		return
	
;-----------------------------------------------------------------------------------
;		wait
;-----------------------------------------------------------------------------------
wait_40us
		movlw	40/5			; wait 40us
		goto	wait_us
wait_100us
		movlw	100/5			; wait 100us
		goto	wait_us
wait_625us
		movlw	625/5			; wait 625us
		goto	wait_us
wait_2ms
		movlw	2			; wait 2ms
		goto	wait_ms
wait_100ms
		movlw	100			; wait 100ms
		goto	wait_ms
wait_1s
		call	wait_500ms		; wait 1s
wait_500ms
		call	wait_250ms		; wait 500ms
wait_250ms
		movlw	250			; wait 250ms
		goto	wait_ms
	
;-----------------------------------------------------------------------------------
;		LCD input (HEX data in Line 2)
;-----------------------------------------------------------------------------------
input_lcd
		movlw	0x40
		call	curs_lcd
		movlw	n0
		movwf	FSR
input_lcd0

		call	test_switch
		btfsc	sw_data,UP
		incf	INDF,1
		btfsc	sw_data,DOWN
		decf	INDF,1
		btfsc	sw_data,RIGHT
		incf	FSR,1
		btfsc	sw_data,LEFT
		decf	FSR,1
		btfsc	sw_data,OK
		return

		call	repair_hex
		movwf	INDF
		call	repair_fsr
		movwf	FSR

		addlw	-n0
		addlw	0x40
		call	curs_lcd
		movf	INDF,0
		call	write_lcd
		call	curs_lcd0

		goto	input_lcd0

;-----------------------------------------------------------------------------------
;		get table data
;-----------------------------------------------------------------------------------
get_table
		movlw	0x0f
		movwf	PCLATH
		movfw	table_index
		movwf	PCL

;-----------------------------------------------------------------------------------
;		print LCD
;-----------------------------------------------------------------------------------
print_lcd
		movwf	table_index
print_lcd0
		bsf	PORTB,RS		; RS='H'

		call	get_table
		movwf	lcd_data
		movlw	$>>8
		movwf	PCLATH
		movf	lcd_data,0
		skpnz
		return

		addlw	-1
		skpnz
		goto	lcd_line1

		addlw	-1
		skpnz
		goto	lcd_line2

		call	write_lcd0
		incf	table_index,1
		goto	print_lcd0
lcd_line2
		movlw	0x40
lcd_line1
		call	curs_lcd
		incf	table_index,1
		goto	print_lcd0

;-----------------------------------------------------------------------------------
;		wait ms	
;-----------------------------------------------------------------------------------
wait_ms
		movwf	wait_cn
wait_ms0
		movlw	247		;1;249
		movwf	wait_cn2	;1;

wait_ms1	nop			;1;
		decfsz	wait_cn2,1	;1;
		goto	wait_ms1	;2;4usx249

		decfsz	wait_cn,1	;1;
		goto	wait_ms0	;2;wait_cn*1001us

		return

;-----------------------------------------------------------------------------------
;		wait us
;-----------------------------------------------------------------------------------
wait_us
		movwf	wait_cn
wait_us0
		goto	$+1		;2;
		decfsz	wait_cn,1	;1;
		goto	wait_us0	;2;wait_cn*5us

		return

;-----------------------------------------------------------------------------------
;		Test Switch	sw-->line
;-----------------------------------------------------------------------------------
test_switch
test_sw_on
		comf	PORTC,0
		andlw	0x3f
		skpnz
		goto	test_sw_on
		movwf	sw_data
		movlw	30
		call	wait_ms
		comf	PORTC,0
		andlw	0x3f
		xorwf	sw_data,0
		skpz
		goto	test_sw_on
test_sw_off
		comf	PORTC,0
		andlw	0x3f
		skpz
		goto	test_sw_off
		movlw	30
		call	wait_ms
		comf	PORTC,0
		andlw	0x3f
		skpz
		goto	test_sw_off
		
		return

;-----------------------------------------------------------------------------------
;		Read EEPROM
;-----------------------------------------------------------------------------------
read_eeprom
		movf	ee_adrs,0
		bsf	STATUS, RP1	;
		bcf	STATUS, RP0	; Bank 2
		movwf	EEADR		; Data Memory Address to read
		bsf	STATUS, RP0	; Bank 3
		bcf	EECON1, EEPGD	; Point to DATA memory
		bsf	EECON1, RD	; EEPROM Read
		bcf	STATUS, RP0	; Bank 2
		movf	EEDATA, 0	; W = EEDATA
		bcf	STATUS, RP1	; Bank 0

		return

;-----------------------------------------------------------------------------------
;		Write EEPROM
;-----------------------------------------------------------------------------------
write_eeprom
		bsf	STATUS, RP1	;
		bcf	STATUS, RP0	; Bank 2
		movf	ee_adrs,0	;
		movwf	EEADR		; Data Memory Address to write
		movf	ee_data,0	;
		movwf	EEDATA		; Data Memory Value to write
		bsf	STATUS, RP0	; Bank 3
		bcf	EECON1, EEPGD	; Point to DATA memory
		bsf	EECON1, WREN	; Enable writes
		bcf	INTCON, GIE	; Disable Interrupts
		movlw	0x55		;
		movwf	EECON2		; Write 55h
		movlw	0xAA		;
		movwf	EECON2		; Write AAh
		bsf	EECON1, WR	; Set WR bit to begin write
		bsf	INTCON, GIE	; Enable Interrupts
		bcf	EECON1, WREN	; Disable writes
		btfsc	EECON1, WR	; if WR=0 then skip
		goto	$-1		;
		bcf	EECON1,EEIF	;
;		btfss	EECON1,WRERR	; if error then skip

		bcf	STATUS, RP0	;
		bcf	STATUS, RP1	; Bank 0
		Message	msg_wr_eeprom

		return

;-----------------------------------------------------------------------------------
		ORG	0x0f00	; Message string
;-----------------------------------------------------------------------------------

msg_start
		DT	1,"NPC ePLL writer",2," v.011008 by K.I",0
msg_set_count
		DT	1,"Ncount  Rcount",2,0
msg_write
		DT	1,"Write ePLL data?",2,0
msg_wr_ncount
		DT	2," write Ncounter!",0
msg_wr_rcount
		DT	2," write Rcounter!",0
msg_wr_vpp
		DT	2," write Vpp!     ",0
msg_wr_eeprom
		DT	2," write EEPROM!  ",0

;-----------------------------------------------------------------------------------
		ORG	0x2100	; (EEPROM default data)
;-----------------------------------------------------------------------------------
ee_ncounter
		DE	"0123    ABCD    "

;-----------------------------------------------------------------------------------

		END