;
	.ECHO	"i2cscan\n"
;
; I2C BUS SCANNER
; MARCO MACCAFERRI, HTTPS://WWW.MACCASOFT.COM
; HBIOS VERSION BY PHIL SUMMERS (B1ACKMAILER) DIFFICULTLEVELHIGH@GMAIL.COM
;
PCFECB		.EQU	0
PCFDUO		.EQU	1
P8X180		.EQU	0
SC126		.EQU	0
SC137		.EQU	0
;
#IF (PCFECB)
I2C_BASE 	.EQU  	0F0H
PCF_ID   	.EQU  	0AAH
CPU_CLK	 	.EQU  	12
;
PCF_RS0  	.EQU  	I2C_BASE
PCF_RS1  	.EQU  	PCF_RS0+1
PCF_OWN	 	.EQU  	(PCF_ID >> 1)        	; PCF'S ADDRESS IN SLAVE MODE
#ENDIF
;
#IF (PCFDUO)
I2C_BASE 	.EQU  	056H
PCF_ID   	.EQU  	0AAH
CPU_CLK	 	.EQU  	12
;
PCF_RS0  	.EQU  	I2C_BASE
PCF_RS1  	.EQU  	PCF_RS0+1
PCF_OWN	 	.EQU  	(PCF_ID >> 1)        	; PCF'S ADDRESS IN SLAVE MODE
#ENDIF
;
#IF (P8X180)
I2C_BASE	.EQU	0A0h
_sda		.EQU	0
_scl		.EQU	1
_idle		.EQU	00000011B
#ENDIF
;
#IF (SC126)
I2C_BASE	.EQU	0Ch
_sda		.EQU	7
_scl		.EQU	0
_idle		.EQU	10001101B
#ENDIF
;
#IF (SC137)
I2C_BASE	.EQU	20h
_sda		.EQU	7
_scl		.EQU	0
_idle		.EQU	10000001B
#ENDIF
;
;-----------------------------------------------------------------------------
;
	.org	100h

	ld	sp,stack

	ld	hl,signon
	call	_strout

	ld	c,' '
	call	_cout
	call	_cout
	call	_cout

; display x axis header 00-0F

	xor	a
	ld	(x),a
	ld	b, 16
lp1:	ld	c,' '
	call	_cout
	ld	a,(x)
	ld	c,a
	inc	a
	ld	(x),a
	call	_hexout
	djnz	lp1
	call	_eolout

; start of line loop 00-07

	xor	a		; display
	ld	(y),a		; y-axis
	ld	(addr),a	; prefix
	ld	d,8
lp3b:	ld	a,(y)
	ld	c,a
	add	a,10h
	ld	(y),a
	call	_hexout
	ld	c,':'
	call	_cout

; set up x axis loop

	xor	a
	ld	(x),a
	ld	e,16
lp2b:	push	de

	ld	c,' '
	call	_cout

; i2c challenge
; . issue device start command
; . write address to device
; . issue device stop command.
; . delay
; . display response

;	call	_i2c_start
;	ld	a,(addr)
;	ld	c,a
;	call	_i2c_write
;	ld	(rc),a
;	call	_i2c_stop

	CALL	PCF_WAIT_FOR_BB
	JP	NZ,PCF_BBERR
;	
	LD	A,(addr)
        OUT	(PCF_RS0),A
        CALL	PCF_START	; GENERATE START CONDITION
;
	ld	bc,100		; delay
lp6:	nop
	dec	bc
	ld	a,c
	or	b
	jr	nz,lp6

	CALL	PCF_WAIT_FOR_ACK; AND ISSUE THE SLAVE ADDRESS
	or	a
	jp	nz,lp4f

	ld	c,'-'		; display no
	call	_cout		; response
	call	_cout
	jp	lp5f

lp4f:	ld	a,(addr)	; adjust address
	ld	c,a		; and display it
	srl	c
	call	_hexout

lp5f:	ld	a,(addr)	; next address
	add	a,2		; adjust for 
	ld	(addr),a	; 7-bit

	CALL	PCF_STOP

	pop	de		; check if
	dec	e		; reached end
	jp	nz,lp2b		; of line
	call	_eolout
	
	dec	d		; loop until
	jp	nz,lp3b		; all done

	jp	0

signon:	.db	"I2C Bus Scanner"
#IF (PCFECB)
	.DB	" - PCF8584 (ECB)"
#ENDIF
#IF (PCFDUO)
	.DB	" - PCF8584 (Duodyne)"
#ENDIF
#IF (SC126)
	.DB	" - SC126"
#ENDIF
#IF (SC137)
	.DB	" - SC137"
#ENDIF
	.db	13, 10, 13, 10, 0, "$"8

_strout:
st1:	ld	a,(hl)		; display
	CP	'$'		; zero 
	ret	z		; terminated 
	ld	c,a		; string
	call	_cout
	inc	hl
	jp	st1

_hexout:			; display
	ld	a,c		; A in hex
	srl	a
	srl	a
	srl	a
	srl	a
	add	a,30h
	cp	3Ah
	jp	c,h1
	add	a,7
h1:	ld	h,a
	ld	a,c
	and	0Fh
	add	a,30h
	cp	3Ah
	jp	c,h2
	add	a,7
h2:	ld	l,a
	ld	c,h
	call	_cout
	ld	c,l
	call	_cout
	ret

_eolout:			; newline
	ld	c,13
	call	_cout
	ld	c,10
	call	_cout
	ret

_cout:				; character
	push	af		; output
	push	bc
	push	de
	push	hl
	ld	e,c
	ld	c,02h
	call	5
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret

;-----------------------------------------------------------------------------
#IF (PCFECB | PCFDUO)
_i2c_start:
PCF_START:
        LD     A,PCF_START_  
	OUT    (PCF_RS1),A
	RET
#ELSE
;_i2c_start:
	ld	a,_idle		; issue 
	out	(I2C_BASE),a	; start
				; command
	res	_sda,a
	out	(I2C_BASE),a
	nop
	nop
	res	_scl,a
	out	(I2C_BASE),a

	ld	(oprval),a
	ret
#ENDIF
;
;-----------------------------------------------------------------------------
;
; CONTROL REGISTER BITS
;
PCF_PIN  	.EQU  10000000B
PCF_ES0  	.EQU  01000000B
PCF_ES1  	.EQU  00100000B
PCF_ES2  	.EQU  00010000B
PCF_EN1  	.EQU  00001000B
PCF_STA  	.EQU  00000100B
PCF_STO  	.EQU  00000010B
PCF_ACK  	.EQU  00000001B
;
; STATUS REGISTER BITS
;
;PCF_PIN  	.EQU  10000000B
PCF_INI   	.EQU  01000000B   ; 1 if not initialized 
PCF_STS   	.EQU  00100000B
PCF_BER   	.EQU  00010000B
PCF_AD0   	.EQU  00001000B
PCF_LRB   	.EQU  00001000B
PCF_AAS   	.EQU  00000100B
PCF_LAB   	.EQU  00000010B
PCF_BB    	.EQU  00000001B
;
PCF_START_    	.EQU  (PCF_PIN | PCF_ES0 | PCF_STA | PCF_ACK)
PCF_STOP_     	.EQU  (PCF_PIN | PCF_ES0 | PCF_STO | PCF_ACK)
;
; TIMEOUT AND DELAY VALUES (ARBITRARY)
;
PCF_PINTO	.EQU	65000
PCF_ACKTO	.EQU	65000
PCF_BBTO	.EQU	65000
PCF_LABDLY	.EQU	65000
;
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,(PCF_RS1)	; 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
;
;-----------------------------------------------------------------------------
;
; 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,(PCF_RS1)
        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	_strout
	CALL	_eolout
	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$"
;
;-----------------------------------------------------------------------------
#IF (PCFECB | PCFDUO)
_i2c_stop:  
PCF_STOP: 
	LD   	A,PCF_STOP_	; issue 
        OUT  	(PCF_RS1),A     ; stop
        RET                     ; command
#ELSE
;_i2c_stop:			
	ld	a,(oprval)	
	res	_scl,a		
	res	_sda,a
	out	(I2C_BASE),a

	set	_scl,a
	out	(I2C_BASE),a
	nop
	nop
	set	_sda,a
	out	(I2C_BASE),a

	ld	(oprval),a
	ret
;
_i2c_write:			; write
	ld	a,(oprval)	; to i2c
				; bus
	ld	b,8
i2c1:	res	_sda,a
	rl	c
	jr	nc,i2c2
	set	_sda,a
i2c2:	out	(I2C_BASE),a
	set	_scl,a
	out	(I2C_BASE),a

#IF	(SC126=0)
	ld	d,a
i2c3:	in	a,(I2C_BASE)
	bit	_scl,a
	jr	z,i2c3
	ld	a,d
#ENDIF
	res	_scl,a
	out	(I2C_BASE),a
	djnz	i2c1

	set	_sda,a
	out	(I2C_BASE),a
	set	_scl,a
	out	(I2C_BASE),a

	ld	d,a
i2c4:	in	a,(I2C_BASE)
#IF	(SC126=0)
	bit	_scl,a
	jr	z,4b
#ENDIF
	ld	c,a
	ld	a,d

	res	_scl,a
	out	(I2C_BASE),a
	ld	(oprval),a
	
	xor	a
	bit	_sda,c
	ret	z
	inc	a

	ret
#ENDIF
oprval:	.db	0
x:	.db	0
y:	.db	0
addr:	.db	0
rc:	.db	0

	.fill	128
stack:
	.end
