;==================================================================================================
; PCF8584/DS1307 I2C DATE AND TIME UTILITY  
;==================================================================================================
;
	.ECHO	"rtcds7\n"
;
#INCLUDE "pcfi2c.inc"
;
	.ORG  100H
;
DS7_START:
	LD	(DS7_STK),SP		; SETUP A 
	LD	SP,DS7_LOC		; LOCAL STACK
;
	CALL	DS7_INIT		; INITIALIZE AND
	JR	NZ,DS7_ERR		; RETURN WITH STATUS
;
	LD	A,(FCB+1)		; GET FIRST COMMAND LINE CHAR 
	CP	' '			; COMPARE TO BLANK. IF SO NO
	JR	Z,DS7_ST0		; ARGUMENTS SO DISLAY TIME AND DATE
;
	LD	A,(FCB+1)		; GET FIRST CHAR 
	CP	'/'			; IS IT INDICATING AN ARGUMENT
	JR	NZ,DS7_ST0		; 
;
	LD	A,(FCB+2)		; GET NEXT CHARACTER
	CP	'D'			; 
	JR	NZ,DS7_ST1		; 
;
;	/D SET DATE
;
	JR	DS7_EXIT
;
DS7_ST1:
	LD	A,(FCB+2)		; GET NEXT CHARACTER
	CP	'T'			; 
	JR	NZ,DS7_ST2		; 
;
;	/T SET TIME
;
	JR	DS7_EXIT
;
DS7_ST2:
	LD	A,(FCB+2)		; GET NEXT CHARACTER
	CP	'S'			; 
	JR	NZ,DS7_ST3		; 
;
;	/S SET TIME AND DATE
;
	JR	DS7_EXIT
;
DS7_ST3:
;
;	UNREGOGNIZED ARGUMENT
;
	JR	DS7_EXIT
;
DS7_ST0:
	CALL   DS7_RDC			; READ CLOCK DATA INTO BUFFER  
	CALL   DS7_DISP			; DISPLAY TIME AND DATE FROM BUFFER 
;
DS7_EXIT:
	LD	SP,(DS7_STK)		; RESTORE STACK
	RET				; AND EXIT
;
DS7_ERR:
	CALL	PCF_INIERR		; DISPLAY ERROR
	JR	DS7_EXIT		; END EXIT
;
DS7_STK	.DW	2			; SAVE STACK
;
;-----------------------------------------------------------------------------
; RTC READ
;
; 1.	ISSUE SLAVE ADDRESS WITH START CONDITION AND WRITE STATUS
; 2.	OUTPUT THE ADDRESS TO ACCESS. (00H = START OF DS1307 REGISTERS)
; 3.	OUTPUT REPEAT START TO TRANSITION TO READ PROCESS
; 4.	ISSUE SLAVE ADDRESS WITH READ STATUS
; 5. 	DO A DUMMY READ 
; 6.	READ 8 BYTES STARTING AT ADDRESS PREVIOUSLY SET
; 7.	END READ WITH NON-ACKNOWLEDGE
; 8.	ISSUE STOP AND RELEASE BUS 
;
DS7_RDC:
	LD	A,DS7_WRITE	; SET SLAVE ADDRESS
	OUT	(REGS0),A
;
	CALL	PCF_WAIT_FOR_BB
	JP	NZ,PCF_BBERR
;
	CALL	PCF_START	; GENERATE START CONDITION
	CALL	PCF_WAIT_FOR_PIN; AND ISSUE THE SLAVE ADDRESS
	CALL	NZ,PCF_PINERR
;
	LD     	A,0
	OUT    	(REGS0),A    	; PUT ADDRESS MSB ON BUS
	CALL	PCF_WAIT_FOR_PIN
	CALL	NZ,PCF_PINERR
;
	CALL	PCF_REPSTART    ; REPEAT START
;
	LD	A,DS7_READ	; ISSUE CONTROL BYTE + READ
	OUT	(REGS0),A
;
	CALL	PCF_READI2C	; DUMMY READ
;
	LD	HL,DS7_BUF	; READ 8 BYTES INTO BUFFER
	LD	B,8
DS7_RL1:CALL	PCF_READI2C
	LD	(HL),A
	INC	HL
	DJNZ    DS7_RL1
;
#IF (0)
	LD	A,8
	LD	DE,DS7_BUF	; DISLAY DATA READ
	CALL	PRTHEXBUF	; 
	CALL   	NEWLINE
#ENDIF
;
	LD     	A,PCF_ES0	; END WITH NOT-ACKNOWLEDGE
	OUT    	(REGS1),A       ; AND RELEASE BUS
	NOP
	IN    	A,(REGS0)
	NOP
DS7_WTPIN:	
	IN	A,(REGS1)	; READ S1 REGISTER
	BIT	7,A		; CHECK PIN STATUS
	JP	NZ,DS7_WTPIN
	CALL   	PCF_STOP
;
	IN    	A,(REGS0)
	RET

;
;-----------------------------------------------------------------------------
; DISPLAY CLOCK INFORMATION FROM DATA STORED IN BUFFER
;
DS7_DISP:
	LD	HL,DS7_CLKTBL
DS7_CLP:LD	C,(HL)
	INC	HL
	LD	D,(HL)
	CALL	DS7_BCD
	INC	HL
	LD	A,(HL)
	OR      A
	RET	Z
	CALL	COUT
	INC	HL
	JR	DS7_CLP
	RET
;
DS7_CLKTBL:
	.DB	04H, 00111111B, '/'
	.DB	05H, 00011111B, '/'
	.DB	06H, 11111111B, ' '
	.DB	02H, 00011111B, ':'
	.DB	01H, 01111111B, ':'
	.DB	00H, 01111111B, 00H
;
DS7_BCD:PUSH	HL
	LD      HL,DS7_BUF     	; READ VALUE FROM
	LD      B,0	   	; BUFFER, INDEXED BY A 
	ADD     HL,BC
	LD      A,(HL)
	AND     D	     	; MASK OFF UNNEEDED
	SRL     A
	SRL     A
	SRL     A
	SRL     A      
	ADD     A,30H
	CALL    COUT
	LD      A,(HL)    
	AND     00001111B
	ADD     A,30H
	CALL    COUT
	POP	HL
	RET
;
DS7_BUF:	.FILL	8,0	; BUFFER FOR TIME, DATE AND CONTROL

;-----------------------------------------------------------------------------
PCF_START:
	LD     A,PCF_START_  
	OUT    (REGS1),A
	RET
;
;-----------------------------------------------------------------------------
PCF_REPSTART:
	LD     A,PCF_REPSTART_  
	OUT    (REGS1),A
	RET
;
;-----------------------------------------------------------------------------
PCF_STOP:   
	LD   	A,PCF_STOP_
	OUT  	(REGS1),A
	RET
;
;-----------------------------------------------------------------------------
;
DS7_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 CTRL.
	AND	07FH	 	; PCF8584 DOES THAT WHEN ESO IS ZERO 
	RET	NZ		; PCF_INIERR 
;
	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
	RET	NZ		; PCF_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
	RET	NZ		; PCF_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
	RET	NZ		; PCF_CLKERR
;
	LD      A,PCF_IDLE_
	OUT     (REGS1),A  
	NOP
	IN      A,(REGS1)  
	CP      +(PCF_PIN | PCF_BB)
	RET	NZ		; PCF_IDLERR
;
	XOR	A
	RET
;
;-----------------------------------------------------------------------------
PCF_HANDLE_LAB:
;
	LD     A,PCF_PIN  
	OUT    (REGS1),A	
	LD     A,PCF_ES0  
	OUT    (REGS1),A
;
	LD     HL,PCF_LABDLY
PCF_LABLP:
	LD     A,H
	OR     L
	DEC    HL  
	JR     NZ,PCF_LABLP
;
	IN     A,(REGS1)
	RET
;
;-----------------------------------------------------------------------------
;
; RETURN A=00/Z  IF SUCCESSFULL
; RETURN A=FF/NZ IF TIMEOUT
; RETURN A=01/NZ IF LOST ARBITRATION
; PCF_STATUS HOLDS LAST PCF STATUS
;
PCF_WAIT_FOR_PIN:
	PUSH	HL
	LD      HL,PCF_PINTO					; SET TIMEOUT VALUE

PCF_WFP0: 
	IN      A,(REGS1)					; GET BUS
	LD	(PCF_STATUS),A					; STATUS
	LD	B,A

	DEC	HL						; HAVE WE
	LD	A,H						; TIMED OUT
	OR	L
	JR	Z,PCF_WFP1					; YES WE HAVE, GO ACTION IT

	LD	A,B						; 		
	AND     PCF_PIN						; IS TRANSMISSION COMPLETE?
	JR	NZ,PCF_WFP0					; KEEP ASKING IF NOT OR
	POP	HL						; YES COMPLETE (PIN=0) RETURN WITH ZERO
	RET
PCF_WFP1:
	LD	A,B						; DID WE LOSE ARBITRATION?
	AND	PCF_LAB						; IF A=0 THEN NO
	CPL	
	JR	NZ,PCF_WFP2					; NO 
	CALL	PCF_HANDLE_LAB					; YES GO HANDLE IT
	LD	(PCF_STATUS),A
	XOR	A						; RETURN NZ, A=01H
	INC	A
PCF_WFP2:
	POP	HL						; RET NZ, A=FF IF TIMEOUT 
	RET
;
PCF_STATUS	.DB	00H

;--------------------------------------------------------------------------------
;
; RETURN NZ/FF IF TIMEOUT ERROR
; RETURN NZ/01 IF FAILED TO RECEIVE ACKNOWLEDGE
; RETURN Z/00  IF RECEIVED ACKNOWLEDGE
;
PCF_WAIT_FOR_ACK:
	PUSH	HL
	LD	HL,PCF_ACKTO
;
PCF_WFA0:
	IN      A,(REGS1)	; READ PIN
	LD	(PCF_STATUS),A	; STATUS
	LD	B,A
;	
	DEC	HL		; SEE IF WE HAVE TIMED
	LD	A,H		; OUT WAITING FOR PIN
	OR	L		; EXIT IF
	JR	Z,PCF_WFA1	; WE HAVE
;	
	LD	A,B		; OTHERWISE KEEP LOOPING
	AND     PCF_PIN		; UNTIL WE GET PIN
	JR	NZ,PCF_WFA0	; OR TIMEOUT
;
	LD	A,B		; WE GOT PIN SO NOW
	AND	PCF_LRB		; CHECK WE HAVE
	LD	A,1
	JR	Z,PCF_WFA2	; RECEIVED ACKNOWLEDGE
	XOR	A
	JR	PCF_WFA2
PCF_WFA1:
	CPL			; TIMOUT ERROR
PCF_WFA2:
	POP	HL		; EXIT WITH NZ = FF
	RET
;	
;--------------------------------------------------------------------------------
;
;	HL POINTS TO DATA
;	DE = COUNT
;	A = 0 LAST A=1 NOT LAST
;
;
;PCF_READBYTES:			; NOT FUNCTIONAL YET

	LD	(PCF_LBF),A	; SAVE LAST BYTE FLAG
;
	INC	DE		; INCREMENT NUMBER OF BYTES TO READ BY ONE -- DUMMY READ BYTE
	LD	BC,0		; SET BYTE COUNTER
;
PCF_RBL:PUSH	BC
	CALL	PCF_WAIT_FOR_PIN	; DO WE HAVE THE BUS?
	POP	BC
	JR	Z,PCF_RB1	; YES
	CP	01H
	JR	Z,PCF_RB3	; NO - LOST ARBITRATION
	JR	PCF_RB2		; NO - TIMEOUT
;
PCF_RB1:
	LD	A,(PCF_STATUS)
	AND	PCF_LRB


	; IS THIS THE SECOND TO LAST BYTE TO GO?

	PUSH	DE		; SAVE COUNT	
	DEC	DE		; COUNT (DE) = NUMBER OF BYTES TO READ LESS 1
	EX	DE,HL		; SAVE POINTER, PUT COUNT IN DE
	XOR	A		; CLEAR CARRY FLAG
	SBC	HL,BC		; DOES BYTE COUNTER = HL (NUMBER OF BYTES TO READ LESS 1)
	EX	DE,HL		; RESTORE POINTER
	POP	DE		; RESTORE COUNT

				; Z = YES IT IS
				; NZ = NO IT ISN'T
	JR	NZ,PCF_RB4
;
PCF_RB4:LD	A,B		; IF FIRST READ DO A DUMMY 
	OR	C		; READ OTHERWISE READ AND SAVE
	JR	NZ,PCF_RB5

	IN	A,(REGS0)	; DUMMY READ
	JR	PCF_RB6

PCF_RB5:IN	A,(REGS0)	; READ AND SAVE
	LD	(HL),A
;
PCF_RB6:	; HAVE WE DONE ALL?

	PUSH	DE		; SAVE COUNT
	EX	DE,HL		; SAVE POINTER, PUT COUNT IN DE
	XOR	A		; CLEAR CARRY FLAG
	SBC	HL,BC		; DOES BYTE COUNTER = HL (NUMBER OF BYTES TO READ)
	EX	DE,HL		; RESTORE POINTER
	POP	DE		; RESTORE COUNT
;
	INC	HL		; BUFFER POINTER
	INC	BC		; COUNT
;
	JR	NZ,PCF_RBL	; REPEAT UNTIL COUNTS MATCH
	RET
;
PCF_RB2:			; TIMEOUT
	CALL	PCF_STOP
	CALL	PCF_TOERR
	RET
;
PCF_RB3:			; LOST ARBITRATION
	CALL	PCF_ARBERR
	RET
;
PCF_LBF:
	.DB	0		; LAST BYTE FLAG
;
;-----------------------------------------------------------------------------
; READ ONE BYTE FROM I2C
; RETURNS DATA IN A
; Z FLAG SET IS ACKNOWLEDGE RECEIVED (CORRECT OPERATION)
;
PCF_READI2C:
	IN	A,(REGS1)	; READ S1 REGISTER
	BIT	7,A		; CHECK PIN STATUS
	JP	NZ,PCF_READI2C
	BIT	3,A		; CHECK LRB=0
	JP	NZ,PCF_RDERR
	IN	A,(REGS0)	; GET DATA
	RET
;-----------------------------------------------------------------------------
;
; POLL THE BUS BUSY BIT TO DETERMINE IF BUS IS FREE.
; RETURN WITH A=00H/Z STATUS IF BUS IS FREE
; RETURN WITH A=FFH/NZ STATUS IF BUS
;
; AFTER RESET THE BUS BUSY BIT WILL BE SET TO 1 I.E. NOT BUSY
;
PCF_WAIT_FOR_BB:
	LD     HL,PCF_BBTO
PCF_WFBB0:
	IN     A,(REGS1)
	AND    PCF_BB
	RET    Z		; BUS IS FREE RETURN ZERO
	DEC    HL
	LD     A,H
	OR     L
	JR     NZ,PCF_WFBB0	; REPEAT IF NOT TIMED OUT
	CPL			; RET NZ IF TIMEOUT  
	RET 
;
;-----------------------------------------------------------------------------
; DISPLAY ERROR MESSAGES
;
PCF_RDERR:
	PUSH	HL
	LD	HL,PCF_RDFAIL
	JR	PCF_PRTERR
;
PCF_INIERR:
	PUSH	HL
	LD      HL,PCF_NOPCF
	JR	PCF_PRTERR
;	
PCF_SETERR:
	PUSH	HL
	LD      HL,PCF_WRTFAIL
	JR	PCF_PRTERR
;
PCF_REGERR:
	PUSH	HL
	LD      HL,PCF_REGFAIL
	JR	PCF_PRTERR
;	
PCF_CLKERR:
	PUSH	HL
	LD      HL,PCF_CLKFAIL
	JR	PCF_PRTERR
;	
PCF_IDLERR:
	PUSH	HL
	LD      HL,PCF_IDLFAIL
	JR	PCF_PRTERR 
;	
PCF_ACKERR:
	PUSH	HL
	LD      HL,PCF_ACKFAIL
	JR	PCF_PRTERR
;
PCF_RDBERR:
	PUSH	HL
	LD	HL,PCF_RDBFAIL
	JR	PCF_PRTERR
;
PCF_TOERR:
	PUSH	HL
	LD	HL,PCF_TOFAIL
	JR	PCF_PRTERR
;
PCF_ARBERR:
	PUSH	HL
	LD	HL,PCF_ARBFAIL
	JR	PCF_PRTERR
;
PCF_PINERR:
	PUSH	HL
	LD	HL,PCF_PINFAIL
	JR	PCF_PRTERR
;
PCF_BBERR:
	PUSH	HL
	LD	HL,PCF_BBFAIL
	JR	PCF_PRTERR
;
PCF_PRTERR:
	CALL	PRTSTR
	CALL	NEWLINE
	POP	HL	
	RET
;
PCF_NOPCF	.DB	"NO DEVICE FOUND$"
PCF_WRTFAIL	.DB     "SETTING DEVICE ID FAILED$"
PCF_REGFAIL 	.DB     "CLOCK REGISTER SELECT ERROR$"
PCF_CLKFAIL 	.DB     "CLOCK SET FAIL$"
PCF_IDLFAIL 	.DB     "BUS IDLE FAILED$"
PCF_ACKFAIL 	.DB	"FAILED TO RECEIVE ACKNOWLEDGE$"
PCF_RDFAIL	.DB	"READ FAILED$"
PCF_RDBFAIL	.DB	"READBYTES FAILED$"
PCF_TOFAIL	.DB	"TIMEOUT ERROR$"
PCF_ARBFAIL 	.DB	"LOST ARBITRATION$"
PCF_PINFAIL 	.DB	"PIN FAIL$"
PCF_BBFAIL	.DB	"BUS BUSY$"
;
;-----------------------------------------------------------------------------
;
#INCLUDE "i2ccpm.inc"
;
	.FILL	128
DS7_LOC:
;
	.END
