PIC 12 bit iButton code in Parallax ASM

Dr. Imre Bartfai  says:

[this is my] iButton reader routine, especially for [PIC type] processors having only 2-level stack. The dialect is the Parallax Assembler one. 4MHz clock is assumed. When routine returns with zero, the iBData structure is filled with the appropriate data. The routine seems to be pretty robust. Please note, that the copyright is retained for me, even if the use [of] the routine is free.
;---------------------------------------------------
; This code fragment handles the Dallas iButton(TM)
;---------------------------------------------------
;
;Line must be equated as a bit also: LPort.LBit

LFloat  =       IOCfg		; line bit as input
LOut    =       1 << LBit ^ LFloat ; all bits input but LBit
iButOrg	=	$

	Org	iBData

Cnt	DS	1		; counter
TCnt	DS	1		; delay counter	
SSave	DS	1		; STATUS save cell
FamCod	DS	1		; Family code   \
SerNo	DS	6		; serial number } do not separate!
CRC	DS	1		; CRC value	/
Cmd	DS	1		; command/data cell
Acc	DS	1		; virtual accumulator

CSave	EQU	SSave.0

iBEnd	EQU	$

	Org	iButOrg

iButton Call	_Init		; reset prom
	Or	W,#0		; check for zero
	SZ			; skip if good
	RetW	1		; otherwise return
	Mov	W,#0Fh		; Read ROM command
_SndCmd	Mov	Cmd,W		; store command
	Mov	Cnt,#8		; # of bits
:loop	Rr	Cmd		; LSB first
_SendC	Mov	SSave,STATUS    ; send Cy
        Mov     !LPort,#LOut    ; turn line to output
	ClrB	Line		; low pulse
	Jmp	$+1             ; tlow1
	Jmp	$+1
	Jmp	$+1             ; tlow1
	Jmp	$+1
	JNB	CSave,:Slot     ; if zero, do not float
        Mov     !LPort,#LFloat
:Slot	Mov	Acc,#20		; for 1 usec / instruction!
:loopi	Nop                     ; loopi makes 80 usec
	DJNZ	Acc,:loopi	; sampling window
	JB	CSave,:cont	; continue if 1
        Mov     !LPort,#LFloat
:cont	DJNZ	Cnt,_SndCmd:loop
	Call	_GetByt		; receive byte
	Mov	FamCod,Cmd	; save family code
	Mov	Cnt,#6		; length of serial number
	Mov	FSR,#SerNo	; serial number address
:loop1	Call	_GetByt		; Receive SerNo
	Mov	INDF,Cmd	; received value
	Inc	FSR
	DJNZ	Cnt,:loop1
	Call	_GetByt		; receive CRC

; ----  
; From here compare received CRC in Cmd with that to be calculated
; upon FamCod & SerNo
;
CRCChk
	Mov	FSR,#FamCod	; start:
	Mov	Cnt,#7		; length
	Clr	CRC		; clear initial CRC
:loop2	Mov	W,INDF		; fetch the byte
	Mov	SSave,W		; save bits to be shifted
	Mov	TCnt,#8		; set shift=8bits
	Mov	W,SSave		;; restore result
:loop	XOr	W,CRC		; calculate CRC
	Mov	Acc,W		;; last CRC value
	Rr	Acc		; move it to carry
	JNC	:Zero		; skip if data=0
	XOr	CRC,#18h	; update the CRC value
:Zero	Rr	CRC		; position the new CRC
	Rr	SSave		; position the next bit
	Mov	W,SSave		; use the remaining bits
	DJNZ	TCnt,:loop
	Inc	FSR		; next pointer
	DJNZ	Cnt,:loop2
;------	now xchg CRC & Cmd
	Mov	W,CRC
	XOr	W,Cmd		; here exchange Cmd with W
	XOr	Cmd,W
	XOr	W,Cmd
	Mov	CRC,W
;------
	Clr	Wdt	
	CSE	CRC,Cmd		; received==calculated
	RetW	2
	RetW	0		; good
;---------------------------------------------------
_Init	Mov     !LPort,#LOut    ; turn port to output
	ClrB	Line		; master reset
	Mov	TCnt,#125	; about 500 usec
:loopi	NOp             	; loopi makes about 500 usec
	DJNZ	TCnt,:loopi
        Mov     !LPort,#LFloat
	Mov	Acc,#7		; for 1 usec / instruction!
:looph	NOp       		; looph makes 30 usec for Tpdh
	DJNZ	Acc,:looph

	Clr	Cnt		; clear counter
:pres	Mov	Acc,#3		; for 1 usec / instruction!
        NOp
:loop	NOp
	DJNZ	Acc,:loop	; 15 usec waiting
	
	JB	Line,:eoi	; if high again, end of init
	Inc	Cnt		; count length of presence pulse
	CJAE	Cnt,#20,:bad	; too long: shortcut
	Clr	Wdt
	Jmp	:pres
:eoi	CJB	Cnt,#1,:bad
	RetW	0		; iButton found
:bad	RetW	1		; iButton not found
;---------------------------------------------------
_GetByt	Mov	Cmd,#128	; hibit as exit condition
:loop	Mov     !LPort,#LOut
	ClrB	Line		; low pulse
	Jmp	$+1		; short pulse (8 usec)
	Jmp	$+1
	Jmp	$+1
	Jmp	$+1
        Mov     !LPort,#LFloat
	Jmp	$+1		; tRDV = 15 æsec
	Jmp	$+1
	Jmp	$+1
	Jmp	$+1
	Jmp	$+1
	Jmp	$+1
	Jmp	$+1
	MovB	C,Line		; line state
	Rr	Cmd		; put received bit
	Mov	SSave,Status	; save status register

	Mov	Acc,#14		; for 1 usec / instruction!
:loopi	NOp
	DJNZ	Acc,:loopi	; 60 usec waiting
	
	JNB	CSave,:loop	; hibit not reached yet?
	Ret


See also: