;==================================================================================================
; PCF8584 HD44780  I2C LCD UTILITY
;
; SOME GENERAL INFORMATION ON LCDS CAN BE SEEN HERE : FOCUSLCDS.COM/PRODUCT-CATEGORY/CHARACTER-LCD/
;
;==================================================================================================
;
	.ECHO	"i2clcd\n"
;
#INCLUDE "pcfi2c.inc"
;
; LCD COMMANDS
;
LCDFSET .EQU 	00100000B ; 20H
LCD4BIT .EQU 	00000000B ; 00H
LCD2LIN .EQU 	00001000B ; 08H
LCDDON  .EQU 	00000100B ; 04H
LCDDMOV .EQU 	00001000B ; 07H
LCDSGRA .EQU 	01000000B ; 04H
LCDSDRA .EQU 	10000000B ; 80H
LCDEMS  .EQU 	00000100B ; 04H 
LCDELFT .EQU 	00000010B ; 03H
;
LCDPINE .EQU 	00000100B ; PIN 2
LCDPIND .EQU 	00000001B ; PIN O
;
;
; STANDARD FORMATS - 8X1, 8X2, 16X1, 16X2, 16X4, 20X1, 20X2, 20X4, 24X2, 40X1, 40X2, 40X4
;
TIMEOUT	.EQU    255

	.ORG	100H

;INIT:   CALL    PCF_INIT
;
	LD     A,0
        LD     (DEBUGF),A
;
	CALL    LCDINIT		; SETUP THE LCD THROUGH THE PCF8574 

	LD	HL,LCDDATA	; DISPLAY TEXT AT HL
	PUSH	HL
	CALL	LCDSTR
	POP	HL

	CALL	STOP		; CLOSE I2C CONNECTION
;
	RET

;-----------------------------------------------------------------------------
;
LCDLITE	.DB   00001000B  
;
LCDINIT: 
;        CALL	DEBUG
;
        LD	A,I2CLCDW	; SET SLAVE ADDRESS
        OUT	(REGS0),A
;
        LD	A,0C5H		; GENERATE START CONDITION
        OUT	(REGS1),A	; AND ISSUE THE SLAVE ADDRESS
        CALL	CHKPIN
;
;       CALL   DEBUG
;    
        LD 	HL,LCDINIT1
        LD   	B,2
        CALL 	WLN
;
        CALL     DELAY
;
        LD 	HL,LCDINIT2
        LD   	B,2
        CALL 	WLN
;
        CALL  DELAY 
;
;	NOW WE ARE IN 4 BIT MODE
;
	LD	A,+(LCDFSET | LCD4BIT | LCD2LIN)
	CALL	LCDCMD
	LD	A,+(LCDDON | LCDDMOV)
	CALL	LCDCMD
	LD	A,+(LCDEMS | LCDELFT)
	CALL	LCDCMD
	LD	A,LCDSDRA
	CALL	LCDCMD
;
	RET
;
;-----------------------------------------------------------------------------
;
WLN:    LD     A,(HL)
        OUT    (REGS0),A    	; PUT DATA ON BUS
        CALL   CHKPIN
        INC    HL
        DJNZ   WLN       
        RET
;
;-----------------------------------------------------------------------------
; DISPLAY STRING AT HL, TERMINATED BY 0
;
LCDSTR:	POP	BC		; GET THE POINTER OF 
	POP	HL		; THE TEXT TO DISPLAY
	PUSH	HL		; OFF THE STACK AND 
	PUSH	BC		; PUT IT IN HL.
;
LCDST0:	LD	A,(HL)		; GET NEXT CHARACTER TO
	OR	A		; DISPLAY BUT RETURN
	RET	Z		; WHEN TERMINATOR REACHED
	PUSH	HL
;	
	CALL	LCDATA		; OUTPUT TO LCD
	POP	HL
;	RET	C		; POINT TO NEXT 
	INC	HL		; AND REPEAT
	JR	LCDST0	
;
;-----------------------------------------------------------------------------
; SEND BYTE IN A TO LCD IN 4-BIT MODE 
;
LCDATA: PUSH   DE
	LD     D,A
        LD     A,(LCDLITE)    
        OR     +(LCDPINE | LCDPIND) 
        JP     LCDSND
LCDCMD: PUSH   DE
        LD     D,A
        LD     A,(LCDLITE)
        OR     LCDPINE
LCDSND: LD     E,A
        LD     A,D
        PUSH   BC
        LD     C,11110000B
        AND    C
        OR     E   
        LD     (LCDBUF),A
        AND    ~LCDPINE
        LD     (LCDBUF+1),A
        LD     A,D
        RLC    A  
        RLC    A
        RLC    A
        RLC    A
        AND    C
        OR     E
        LD     (LCDBUF+2),A
        AND    ~LCDPINE
        LD     (LCDBUF+3),A
;
        LD   HL,LCDBUF		; OUTPUT 1 BYTE WHICH
        LD   B,4		; REQUIRES A FOUR
        CALL WLN		; BYTE SEQUENCE
;
        POP     BC
        POP     DE
        RET
;     
LCDDATA:
     .DB   "TEST HOW BIG IS THIS LINE DOES IT WRAP",0
;
LCDINIT1:
     .DB   00110100B
     .DB   00011000B
;
LCDINIT2:
     .DB   00100100B
     .DB   00100000B
;
LCDBUF:	
     .DB   0, 0, 0, 0		; BUFFER TO HOLD 4 BYTE SEQUENCE

; FLASH DEVICE READ
;

DEVMADR	.EQU	0

READR:	LD	B,255
DLY1:	DJNZ	DLY1
;
        LD	A,D	       	; SET SLAVE ADDRESS
        OUT	(REGS0),A
;
        LD	A,0C5H		; GENERATE START CONDITION
        OUT	(REGS1),A	; AND ISSUE THE SLAVE ADDRESS
        CALL	CHKPIN
;
        LD   A,+(DEVMADR/256)
        OUT  (REGS0),A    	; PUT ADDRESS MSB ON BUS
        CALL CHKPIN
;
        LD   A,+(DEVMADR&$00FF)
        OUT  (REGS0),A    	; PUT ADDRESS LSB ON BUS
        CALL	CHKPIN
;
	LD	A,045H       	; START
	OUT	(REGS1),A
;
        LD	A,E	  	; ISSUE CONTROL BYTE + READ
        OUT	(REGS0),A
;
	CALL	READI2C		; DUMMY READ
	JR	NZ,ERREXT
;
READLP1:CALL	READI2C
;	JR	Z,ERREXT
	CP	1AH
	PUSH    AF
	CALL    COUT
	POP     AF
	JR	NZ,READLP1
;          
	LD	A,PCF_ES0
	OUT	(REGS1),A          
	CALL	CHKPIN
	IN	A,(REGS0)  
        CALL      READI2C
	CALL STOP
;
        CALL   NEWLINE
;
	RET
;
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
RESET:	LD	A,0C2H		; STOP
	OUT	(REGS1),A
	LD	B,255
DLY2:	DJNZ	DLY2
	LD	A,0C1H
	OUT	(REGS1),A
	RET


RDSTAT:	LD	BC,-1
STATLP:	IN	A,(REGS1)
	AND	1
	RET	Z
	LD	A,B
	OR	C
	DEC	BC
	JR	NZ,STATLP
	LD	A,'T'
	JP	ERREXTT
;
ERREXT: LD	A,'Q' 
	JR	ERR
 
ERREXTT: POP	HL
ERR:	CALL	COUT
        CALL   STOP  
	CALL	RESET
        RET
;
STOP:   LD   A,0C3H
        OUT  (REGS1),A
        RET
;
DELAY:  PUSH HL
        LD   HL,-1
DLOOP:  LD   A,H
        OR   L
        DEC  HL
        JR   NZ,DLOOP
        POP  HL
        RET
;
CHKPIN: IN   A,(REGS1)	; POLL FOR
        BIT  7,A		; TRANSMISSION
        JP   NZ,CHKPIN	; TO FINISH

;	IN	A,(REGS1)	; CHECK FOR
        BIT  3,A		; SLAVE
        RET  Z		; ACKNOWLEDGMENT
	LD	A,'A'
        JP   ERREXTT
;
; READ ONE BYTE FROM I2C
; RETURNS DATA IN A
; Z flag set is acknowledge received (correct operation)
;
READI2C:
	IN	A,(REGS1)	; READ S1 REGISTER
	BIT	7,A		; CHECK PIN STATUS
	JP	NZ,READI2C
	BIT	3,A		; CHECK LRB=0
	RET	NZ
	IN	A,(REGS0)	; GET DATA
	RET
;
DEBUG:  PUSH AF
        PUSH DE
	LD	A,'['
	CALL	COUT
	LD	HL,DEBUGF
	LD	A,(HL)
	INC	(HL)
	CALL	HBTHE
	LD	A,']'
	CALL	COUT
        POP  DE
        POP  AF
	RET
DEBUGF:	.DB	00H
;
;-----------------------------------------------------------------------------
;
; LINUX DRIVER BASED CODE
;
;	I2C_INB		= IN A,(REGS0)
;	I2C_OUTB	= LD A,* | OUT (REGS0),A
;	SET_PCF		= LD A,* | OUT (REGS1),A
;	GET_PCF		= IN A,(REGS1)
;	
;-----------------------------------------------------------------------------
I2C_START:
        LD     A,PCF_START_ 
	OUT    (REGS1),A
	RET
;
;-----------------------------------------------------------------------------
I2C_REPSTART:
        LD     A,PCF_START_  
	OUT    (REGS1),A
	RET
;
;-----------------------------------------------------------------------------
I2C_STOP:
        LD     A,PCF_STOP_  
	OUT    (REGS1),A
	RET
;
;-----------------------------------------------------------------------------
HANDLE_LAB:

LABDLY  .EQU    0F000H  

        LD     A,PCF_PIN  
	OUT    (REGS1),A        
        LD     A,PCF_ES0  
	OUT    (REGS1),A
;
        LD     HL,LABDLY
LABLP   LD     A,H
        OR     L
        DEC    HL  
        JR     NZ,LABLP
;
        IN     A,(REGS1)
        RET
;
;-----------------------------------------------------------------------------
WAIT_FOR_BB:
;
BBTIMO  .EQU    255
;
        LD     HL,BBTIMO
BBNOTO  IN     A,(REGS1)
        AND    PCF_BB
        RET    Z
        DEC    HL
        LD     A,H
        OR     A
        JR     NZ,BBNOTO
        CPL                 ; RET NZ IF TIMEOUT  
BBNOTB  RET                 ; RET Z IF BUS IS BUSY
;
;-----------------------------------------------------------------------------
WAIT_FOR_PIN:
;
; RETURN A=00/Z  IF SUCCESSFULL
; RETURN A=FF/NZ IF TIMEOUT
; RETURN A=01/NZ IF LOST ARBITRATION
;
PINTIMO .EQU    16000
;
        LD      HL,PINTIMO
PINNOTO IN      A,(REGS1)
	LD	(STATUS),A
	LD	B,A
        AND     PCF_PIN
        RET     Z
        DEC     HL
        LD      A,H
        OR      A
        JR      NZ,PINNOTO
        CPL                 ; RET NZ IF TIMEOUT  
PINNOTB RET                 ; RET Z IF BUS IS BUSY
;
	LD	B,A
	AND	PCF_LAB
	CALL	HANDLE_LAB
	LD	(STATUS),A
	XOR	A
	INC	A
	RET
;
STATUS	.DB	00H
;
;-----------------------------------------------------------------------------
PCF_INIT:
       LD       A,PCF_PIN   ; S1=80H: S0 SELECTED, SERIAL 
       OUT     (REGS1),A    ; INTERFACE OFF
       NOP
       IN      A,(REGS1)    ; CHECK TO SEE S1 NOW USED AS R/W
       AND     07FH         ; CTRL. PCF8584 DOES THAT WHEN ESO
       JR      NZ,INIERR    ; IS ZERO
;
       LD      A,PCF_OWN    ; LOAD OWN ADDRESS IN S0,     
       OUT     (REGS0),A    ; EFFECTIVE ADDRESS IS (OWN <<1)
       NOP
       IN      A,(REGS0)    ; CHECK IT IS REALLY WRITTEN
       CP      PCF_OWN
       JR      NZ,SETERR
;
       LD      A,+(PCF_PIN | PCF_ES1) ; S1=0A0H
       OUT     (REGS1),A               ; NEXT BYTE IN S2
       NOP
       IN      A,(REGS1)
       AND     07FH
       CP      PCF_ES1
       JR      NZ,REGERR
;
       LD      A,PCF_CLK    ; LOAD CLOCK REGISTER S2
       OUT     (REGS0),A
       NOP
       IN      A,(REGS0)    ; CHECK IT'S REALLY WRITTEN, ONLY
       AND     1FH          ; THE LOWER 5 BITS MATTER
       CP      PCF_CLK
       JR      NZ,CLKERR
;
       LD      A,PCF_IDLE_
       OUT     (REGS1),A  
       NOP
       IN      A,(REGS1)  
       CP      +(PCF_PIN | PCF_BB)
       JR      NZ,IDLERR

       RET
;
;-----------------------------------------------------------------------------
PCF_SENDBYTES: 			; HL POINTS TO DATA, BC = COUNT, A = 0 LAST A=1 NOT LAST
				; 
	LD	(LASTB),A
;
SB0:	LD	A,(HL)
	OUT	(REGS0),A
	CALL	WAIT_FOR_PIN
	JR	Z,SB1

	CP	01H		; EXIT IF ARBITRATION ERROR
	RET	Z

	CALL	I2C_STOP		; MUST BE TIMEOUT
	LD	A,055H		; ERROR
	RET

SB1:	LD	A,(STATUS)
	AND	PCF_LRB
	JR	NZ,SB2
	LD	A,055H
;
SB2:	LD	A,B
	OR	C
	INC	HL
	JR	NZ,SB0		; CHECK IF FINISHED
;	
SBGOOD:	LD	A,(LASTB)
	OR	A
	JR	NZ,DB3
	CALL	I2C_STOP
	RET
DB3:	CALL	I2C_REPSTART
	RET
;
LASTB	.DB	00H
;

;	I2C_INB		= IN A,(REGS0)
;	I2C_OUTB	= LD A,* | OUT (REGS0),A
;	SET_PCF		= LD A,* | OUT (REGS1),A
;	GET_PCF		= IN A,(REGS1)


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

INIERR LD      HL,NOPCF
       CALL    PRTSTR
       RET
;
SETERR LD      HL,WRTFAIL
       CALL    PRTSTR
       RET
REGERR LD      HL,REGFAIL
       CALL    PRTSTR
       RET
;
CLKERR LD      HL,CLKFAIL
       CALL    PRTSTR
       RET
;
IDLERR LD      HL,IDLFAIL
       CALL    PRTSTR
       RET   
;
NOPCF	.DB	"NO DEVICE FOUND",CR,LF,"$"
WRTFAIL .DB     "SETTING DEVICE ID FAILED",CR,LF,"$"
REGFAIL .DB     "CLOCK REGISTER SELECT ERROR",CR,LF,"$" 
CLKFAIL .DB     "CLOCK SET FAIL",CR,LF,"$"
IDLFAIL .DB     "BUS IDLE FAILED",CR,LF,"$"
;
#INCLUDE "i2ccpm.inc"
;
BUFFER:	.DS	256
;
        .END
