;*************************************************************************
; Read data from 000 to FFF, from a serial EEPROM 24C32
; PORTA to jump to a specific address or sequential inc-dec
;		character	address Hexadecimal
; LCD line1:	01001101	addr
; LCD line2:	M     4D	 000
;
; PIC16F84 XT=8MHZ
; Lakra Karim 23.06.2015
; LCD HD44780
; RB0-RB3 connected to LCD D4-D7, RB4->E, RB5->RS, R/W->GND
; RB7 to SDA, RB6 to SCL
; RA0 for previous address, RA1 for next address
; RA2 increment selected bit address, RA3 decrement selected bit address
; RA4 next bit address going from left to right (addr bit2 to bit0) then back to bit2
;*************************************************************************
 ;****************** COMPILATION MESSAGES & WARNINGS *********************
;ERRORLEVEL -207 	; Found label after column 1.
ERRORLEVEL -302 	; Register in operand not in bank 0.
; ERRORLEVEL -205		; Found directive in column 1
; ERRORLEVEL -202		; Argument out of range. Least significant bits used.

;***** PROCESSOR DECLARATION & CONFIGURATION *****

PROCESSOR 16F84

#include "p16f84.inc"
; embed Configuration Data within .asm File.
  __CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC

;*************************** DATA SEGMENT ********************************
 CBLOCK 0x00C                   
  del, cmd, adrs_LW, adrs_HI		; variables for passing the parameters
  temp, i, curPOS, bitDATA		; local variables (20h reserved for data, bitDATA for bits entred by the user)
 ENDC

  org 0                      		; start program at the beginning of mem
	goto start

msg1					; first message to display
 	addwf	PCL, f
 	dt "addr", 0

;*****Set up the Constants****
STATUS	equ	03h			; Address of the STATUS register
TRISA	equ     85h			; Address of the tristate register for port A
PORTA	equ 	05h			; Address of Port A
TRISB	equ     86h			; Address of the tristate register for port B
PORTB	equ     06h			; Address of Port B
LINE1	equ	0x80			; command line1 of LCD
LINE2	equ	0xC0			; command line2 of LCD

start
	bsf 	STATUS,RP0		; Switch to Bank 1
	movlw	b'11111'		; Set the Port A pins RA0-RA4
	movwf	TRISA	 		; to intputs
  	clrf    TRISB ^ 0x80      	; enable RB0-RB7 for output
  	movlw  	0x7F              	; enable internal Pull-Ups
  	movwf  	OPTION_REG ^ 0x80
	bcf    	STATUS, RP0        	; back to BANK 0
	call 	initLCD        		; initialize LCD display
	
	;call	WRITE_EEPROM
	
	goto	Main

 	
;output of the top row (msg1)
MS1	clrf 	i			
L1	movf	i, w
	call	msg1			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z	
	return				; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L1

Main	clrf	adrs_LW			; clear the address holder
	clrf	adrs_HI
	clrf	bitDATA
	movlw	d'6'			; initialize cursor position for editing
	movwf	curPOS	
	goto	READ_EEPROM

;********************************* Buttons press manager
A_INC	
	btfss	PORTA,0 		; test bit PortA0 if 1 skip next line
	Call	INC 			; increment eeprom address, if (A0=0)
	
	btfss	PORTA,1			; test PortA1 if 1 skip next line
	Call	DEC			; decrement eeprom address, if (A1=0)
	
	btfss	PORTA,2			; test PortA2 if 1 skip next line
	Call	INC_B			; move cursor to next bit address, if (A2=0)
	
	btfss	PORTA,3			; test PortA3 if 1 skip next line
	Call	DEC_B			; move cursor to previous bit address, if (A3=0)
	
	btfss	PORTA,4			; test PortA4 if 1 skip next line
	Call	OK_B			; select data and address bits for edition
	
	goto	A_INC
;********************************* Increment bit selector
INC_B					; increment selected bit
	movlw	d'6'			; position HI-bit data
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_HI_INC

	movlw	d'7'			; position LW-bit data
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_LW_INC

	movlw	d'12'			; position of hundreds
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_HUNDREDS_INC
	
	movlw	d'13'			; position of tens
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_TENS_INC
	
	movlw	d'14'			; position of ones
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_ONES_INC
	
	return
;********************************* Decrement bit selector
DEC_B					; decrement selected bit
	movlw	d'6'			; position HI-bit data
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_HI_DEC

	movlw	d'7'			; position LW-bit data
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_LW_DEC

	movlw	d'12'			; position of hundreds
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_HUNDREDS_DEC
	
	movlw	d'13'			; position of tens
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_TENS_DEC
	
	movlw	d'14'			; position of ones
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	BIT_ONES_DEC
	
	return
;********************************* Data edit bits - Increment
BIT_HI_INC
	movlw	0x10
	addwf	bitDATA,1
	movlw	0xF0
	andwf	bitDATA,w
	movwf	21h
	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return	
BIT_LW_INC
	movf	bitDATA,w
	movwf	21h
	incf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	bitDATA,1
	movf	21h,w
	addwf	bitDATA,1
	movwf	21h
 	call	HEXA_TO_LCD_WAIT
	return	
;********************************* Data edit bits - Decrement
BIT_HI_DEC
 	movlw	0x10
	subwf	bitDATA,1
 	movlw	0xF0
 	andwf	bitDATA,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return
BIT_LW_DEC
	movf	bitDATA,w
	movwf	21h
	decf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	bitDATA,1
	movf	21h,w
	addwf	bitDATA,1
	movwf	21h
 	call	HEXA_TO_LCD_WAIT
	return	
	
;********************************* Address edit bits - Decrement
BIT_HUNDREDS_DEC
	decf	adrs_HI,1
	movlw	0x0F
	andwf	adrs_HI,1
	movlw	0x0F
	andwf	adrs_HI,w
	movwf	21h
	call	HEXA_TO_LCD_WAIT
	return
BIT_TENS_DEC
	movlw	0x10
	subwf	adrs_LW,1
 	movlw	0xF0
 	andwf	adrs_LW,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return
BIT_ONES_DEC
	movf	adrs_LW,w
	movwf	21h
	decf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	adrs_LW,1
	movf	21h,w
	addwf	adrs_LW,1
	movwf	21h
 	call	HEXA_TO_LCD_WAIT
	return	

;********************************* Address edit bits - Increment
BIT_HUNDREDS_INC
	incf	adrs_HI,1
	movlw	0x0F
	andwf	adrs_HI,1
	movlw	0x0F
	andwf	adrs_HI,w
	movwf	21h
	call	HEXA_TO_LCD_WAIT
	return
	
BIT_TENS_INC
	movlw	0x10
	addwf	adrs_LW,1
 	movlw	0xF0
 	andwf	adrs_LW,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return	
BIT_ONES_INC
	movf	adrs_LW,w
	movwf	21h
	incf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	adrs_LW,1
	movf	21h,w
	addwf	adrs_LW,1
	movwf	21h
  	call	HEXA_TO_LCD_WAIT
	return	
;*****************************
HEXA_TO_LCD_WAIT
	call	HEXA_TO_LCD
  	goto	Wait

;********************************* cursor moves
OK_B					; cursor position 6 & 7 edit data, 12-14 edit address
	incf	curPOS,1
	movlw	d'15'
	subwf	curPOS,w
	btfsc	STATUS,Z
	call	INI_POS_DT		; roll back to data edit position
	movlw	d'8'
	subwf	curPOS,w
	btfsc	STATUS,Z
	call	INI_POS_ADRS		; jump to address edit position

	goto	Wait
	return
	
INI_POS_DT
	movlw	d'6'
	movwf	curPOS
	return
INI_POS_ADRS
	movlw	d'12'
	movwf	curPOS
	return


;********************************* no response for a long press
Wait	
	btfss	PORTA,0			; stay here if A0 is still pressed =0
	goto	Wait
	btfss	PORTA,1			; stay here if A2 is still pressed =0
	goto	Wait
	btfss	PORTA,2
	goto	Wait
	btfss	PORTA,3
	goto	Wait
	btfss	PORTA,4
	goto	Wait
	
	movlw	0x0F			; display ON, cursor blink
	call	writeCMD

  	movlw	LINE2
  	addwf	curPOS,w
  	call 	POS_LCD
	
	movlw	10
	movwf	del
	call 	delay
	

	goto	A_INC

;********************************* increment decrement address by 1
INC
	call	DATA_CHNG		; if data changed
	movlw	0xFF
	subwf	adrs_LW,w
	btfsc	STATUS,Z
	incf	adrs_HI,1		; if adrs_LW is equal FF
	incf	adrs_LW,1
	call 	READ_EEPROM
	return
	
DEC
	movlw	0x00
	subwf	adrs_LW,w
	btfsc	STATUS,Z
	decf	adrs_HI,1		; if adrs_LW is equal 00
	decf	adrs_LW,1
	call 	READ_EEPROM
	return
	
DATA_CHNG
	movf	20h,w			; check if data bits are changed
	subwf	bitDATA,w
	btfss	STATUS,Z
	call	WRITE_EEPROM
	return
;******************************************************************************************
WRITE_EEPROM
	call	START_DEF_EEPROM	; start definition
	call	DEVICE_ADDRESS
	bcf	PORTB,7			; 0 write
	call	CLK_EE			; clock
	call	CLK_EE			; clock, ACK
	movf	adrs_HI,w		; 1st word address
	call	WORD_ADDRESS		;
	movf	adrs_LW,w		; 2nd word address
	call	WORD_ADDRESS		;
	movf	bitDATA,w
	call	WORD_ADDRESS		; send data
	call	STOP_DEF_EEPROM		; stop definition
	return
;******************************************************************************************
READ_EEPROM
	movlw	LINE1+d'11'		; write character to 1st line LCD
	call 	POS_LCD	
	call	MS1			; 'addr' label
	
	movlw	LINE1+0			; write received Binary data to first line LCD
	call 	POS_LCD
	call	START_DEF_EEPROM	; start definition	
	call	DEVICE_ADDRESS
	bcf	PORTB,7			; 0 write
	call	CLK_EE			; clock
	call	CLK_EE			; clock, ACK
	movf	adrs_HI,w		; 1st word address
	call	WORD_ADDRESS		;
	movf	adrs_LW,w		; 2nd word address
	call	WORD_ADDRESS		;
	call	START_DEF_EEPROM	; start definition
	call	DEVICE_ADDRESS
	bsf	PORTB,7			; 1 read
	call	CLK_EE			; clock
	call	PORT_B7_IN		; switch RB7 to input
	call	CLK_EE			; clock, ACK
	
	call	RCV_DT			; receive data from EEPROM
	
	call	PORT_B7_OUT		; switch RB7 to output
	bcf	PORTB,7			;
	call	CLK_EE			; clock, ACK
	
	movlw	LINE2+0			; write character to 2nd line LCD
	call 	POS_LCD	
	movf	20h,w
	call	writeDATA		; TO_LCD
	
	movf	20h,w			; save data for edit
	movwf	bitDATA
	
	movlw	LINE2+6			; write HI-bit hex code data to 2nd line LCD
	call 	POS_LCD	
	movlw	0xF0
	andwf	20h,w
	movwf	21h
	swapf	21h,1
	call	HEXA_TO_LCD
	movlw	LINE2+7			; write LOW-bit hex code data to 2nd line LCD
	call 	POS_LCD	
	movlw	0x0F
	andwf	20h,w
	movwf	21h
	call	HEXA_TO_LCD
	
	movlw	LINE2+d'14'		; write character to 1st line LCD
	call 	POS_LCD
	
	movlw	b'00001111'
	andwf	adrs_LW,w
	movwf	21h
	call	HEXA_TO_LCD
	
	
	movlw	LINE2+d'13'		; write character to 1st line LCD
	call 	POS_LCD
		
	swapf	adrs_LW,w
	movwf	21h
	movlw	b'00001111'
	andwf	21h,1
	call	HEXA_TO_LCD
	
	movlw	LINE2+d'12'		; Hundreds digit Hexadecimal
	call 	POS_LCD
		
	movlw	b'00001111'
	andwf	adrs_HI,w
	movwf	21h
	call	HEXA_TO_LCD
	
 	movlw	LINE2+curPOS		; put back the cursor to the last position the user left it
 	call 	POS_LCD

	goto	Wait
	
;******************************************************************************************
HEXA_TO_LCD	
	call	HEXA
	call	TO_LCD	
	return

HEXA
	movlw	b'1010'
	subwf	21h,w			; sub if greater than 9
	btfsc	STATUS,C		;
	call	HEX_A_LCD		; ?>9 add alphanumeric (A-F)
	movf	21h,w
	return
HEX_A_LCD
	movlw	d'7'			; for LCD from A-F
	addwf	21h,1
	return
;****************************      
PORT_B7_IN
	bsf 	STATUS,RP0		; Switch to Bank 1
	movlw	b'10000000'		; Set the Port B pins RB7 to input
	movwf	TRISB
  	bcf    	STATUS, RP0
  	return
PORT_B7_OUT
	bsf 	STATUS,RP0		; Switch to Bank 1
	movlw	b'00000000'		; Set the Port B pins RB7 to input
	movwf	TRISB
  	bcf    	STATUS, RP0
  	return
;******************
RCV_DT
	movlw	7
	movwf	21h
	clrf	1fh			; binary data holder
 	clrf	20h			; hex data holder
	call	RD_BIT			; read bit7
	call	CLK_EE
	call	SHIFT_BIT		; read bit6-0
	decfsz	21h,1
	btfss	STATUS,Z
	goto	$+2
	goto	$-5
	return
;******************
SHIFT_BIT
	BCF	STATUS,C
	RLF	20h,1
	BCF	STATUS,C
RD_BIT
	movlw	0
	btfsc	PORTB,7			; read RB7, if 0 skip next line
	movlw	1
	movwf	1fh
	addwf	20h,1			; store data
	movf	1fh,w
	addlw	0x30			; convert Binary for LCD
	call	writeDATA		; TO_LCD			
	return
;******************
START_DEF_EEPROM
	bsf	PORTB,7			; set RB7
	bsf	PORTB,6			; set RB6
	bcf	PORTB,7			; 0 RB7
	bcf	PORTB,6			; 0 RB6
	return
;******************
STOP_DEF_EEPROM
	bsf	PORTB,6			; set RB6
	bsf	PORTB,7			; set RB7
	;bcf	PORTB,6			; 0 RB6
	;bcf	PORTB,7			; 0 RB7
	return
;******************
DEVICE_ADDRESS
	bsf	PORTB,7			; MSB
	call	CLK_EE			; clock
	bcf	PORTB,7			; 0
	call	CLK_EE			; clock
	bsf	PORTB,7			; 1
	call	CLK_EE			; clock
	bcf	PORTB,7			; 0
	call	CLK_EE			; clock
	call	CLK_EE			; clock, device address A2, (0 for A2-A0, communication with one eeprom only)
	call	CLK_EE			; clock, device address A1
	call	CLK_EE			; clock, device address A0	
	return
;******************
WORD_ADDRESS
	BCF	STATUS,C
	movwf	1fh			; put word address to register
	movlw	8			; word length
	movwf	1eh
SWA	call	SHIFT_WORD_ADDRESS
	call	CLK_EE			; clock,word address
	decf	1eh,1			; decrement 1eh counter by 1, result to 1eh
	btfsc	STATUS,Z		; jump next line when Z set to 1
	goto	JMP_1			; when Z=0 jump out
	goto	SWA			; read next word address bit
JMP_1	call	PORT_B7_IN
	call	CLK_EE			; clock, ACK
	call	PORT_B7_OUT
	return
	
SHIFT_WORD_ADDRESS
	rlf	1fh,1
	bcf	PORTB,7
	btfsc	STATUS,C
	bsf	PORTB,7
	return
;******************
CLK_EE
	bsf	PORTB,6			; set RB6
	bcf	PORTB,6			; 0 RB6
	return

;******************************************************************************************	
TO_LCD
	addlw	0x30
 	call	writeDATA
 	movlw	LINE2+curPOS		; 
	call 	POS_LCD
	return
	
POS_LCD	
	call 	writeCMD	
	movlw	30
	movwf	del
	call 	delay
	return	
	

;***********************************************************************************************	
; procedures
initLCD					; initialize LCD right after power up
	movlw	30
	movwf	del
	call 	delay			; a 15 msec delay after power is on

	movlw	3
	movwf	PORTB			; start LCD init process
	movlw	10
	movwf	del	
	call 	delay			; wait for 5 msec	

	movlw	3
	movwf	PORTB			; start LCD init process
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4
	movlw	10
	movwf	del	
	call 	delay			; wait for 5 msec

	bsf	PORTB, 4		; repeat the reset command (2nd time)
	bcf	PORTB, 4
	movlw	80			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	bsf	PORTB, 4		; repeat the reset command (3rd time)
	bcf	PORTB, 4
	movlw	80			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	movlw	2			; initialize LCD 4-bit mode
	movwf	PORTB
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4

	movlw	10
	movwf	del	
	call 	delay
	movlw	0x28			; LCD 4-bit mode, 2-line mode, char font 5x7
	call	writeCMD
	
	
	movlw	1
	call 	writeCMD		; clear and home display
	

	movlw	10			; wait for 5 msec after clearing
	movwf	del
	call 	delay

	movlw	0x06			; cursor move after each char right
	call 	writeCMD

 	movlw	0x0F			; turn on LCD and enable cursor, blinking
 	call 	writeCMD
	return

writeCMD				; write to LCD a 1-byte command in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the high 4 bits in W
	movwf	PORTB
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	80			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3
	return

writeDATA				; write to LCD a 1-byte data in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the lower 4 bits in W	
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	10			; wait for 5 msec
	movwf	del
	call 	delay
	return

delay					; a delay for del milliseconds
	movlw 	d'200'	
	sublw	1			; this loop takes 5us*200 = 1ms
	sublw	0			; for PIC16F84A @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del,f
	goto 	delay
	return

 end