Listing 1:  Assembly Program for COP8 Ultrasonic Rangefinder Module

;--------------------------------------------------------------------------
; soncop3.asm:  This code performs the following operations:
;	1) Sets up Timer 1, Comparator 1, and UART.
;	2) Waits for address from host (UART receives character).
;	3) If address matches module's address, module sends acknowledge.
;	3) Pre-loads Timer 1 with 40000 (microseconds).
;	4) Transmits an ultrasonic (40KHz) pulse 250 microseconds long.
;	5) Begins input-capture on T1A.
;	6) Waits at least 40ms (to allow stray echoes to die down).
;	7) Scales input-capture time to distance in inches.
;	8) Sends the distance data byte out of the UART.
;
;
; I/O Usage:
;--------------------------------------------------------------------------
;                     COP8SGR740
;                 +----------------+
;       INIT---1--|C2            C1|--40--TXNEG
;          X---2--|C3            C0|--39--TXPOS
;          X---3--|G4/SO     G3/T1A|--38--CMPOUT
;          X---4--|G5/SK     G2/T1B|--37--X
;          X---5--|G6/SI     G1/WDO|--36--X
;      XTAL1---6--|G7/CKO    G0/INT|--35--X
;      XTAL2---7--|CKI       RESET#|--34--VCC
;        VCC---8--|VCC          GND|--33--GND
;          X---9--|F0            D7|--32--X
;        REF--10--|F1/CMP1IN-    D6|--31--X
;       ECHO--11--|F2/CMP1IN+    D5|--30--X
;     CMPOUT--12--|F3/CMP1OUT    D4|--29--X
;          X--13--|F4/CMP2IN-    D3|--28--X
;          X--14--|F5/CMP2IN+    D2|--27--X
;          X--15--|F6/CMP2OUT    D1|--26--X
;          X--16--|F7            D0|--25--X
;          X--17--|L0        L7/T3B|--24--X
;       TENA--18--|L1        L6/T3A|--23--X
;      TDATA--19--|L2        L5/T2B|--22--X
;      RDATA--20--|L3        L4/T2A|--21--X
;                 +----------------+
;
;
;--------------------------------------------------------------------------
	.title soncop3
	.incld cop8sgr.inc
	;

BASE_ADDR = 0x20			; base address of module

	.sect dsect, ram
RAW:	.dsb 2			; [0-1]
	RAWLO = RAW
	RAWHI = RAW + 1
TOT: .dsb 2			; [2-3]
	TOTLO = TOT
	TOTHI = TOT + 1
RMD: .dsb 2			; [4-5]
	RMDLO = RMD
	RMDHI = RMD + 1
	.endsect

	.sect memcnt, reg
CNTR: .dsb 1
	.endsect

	.sect code, rom, abs=0
start:
	ld	PORTCC, #07	; C2-C0 = output  C3 = input
	ld	PORTCD, #02	; C1(TXNEG)=high  C2(INIT),C0(TXPOS)=low

stcmp:
	sbit	CMP1OUT, PORTFC	; CMP1OUT = output
	rbit	CMP1INP, PORTFC	; CMP1IN- = input
	rbit	CMP1INN, PORTFC	; CMP1IN+ = input
	sbit	CMP1EN, CMPSL	; enable comparator 1 operation
	sbit 	CMP1OE, CMPSL	; enable comparator 1 output
	ld	PORTFD, #00	; all inputs Hi-Z

sttim:
	rbit	T1A, PORTGC	; T1A = input
	rbit 	T1A, PORTGD	; T1A = Hi-Z
	
sturt:
	sbit	1, PORTLC		; enable L1 as output for TXE
	sbit	ETDX, ENUI	; enable uart TDX operation
	sbit	TDX, PORTLC	; enable uart TDX output
	sbit	CHL1, ENU		; enable 9-bit data mode
	ld	PSR, #060		; load prescaler with 6.5
	ld	BAUD, #09		; load divisor with N-1 = 10-1 = 9
				; for CKI=10MHz, this yields 9600bps.

waitaddr:
	ifbit 	RBFL, ENU		; if char was received
	jp	checkaddr1	; then check for address
	jp	waitaddr		; else wait here
	
checkaddr1:
	x	A, RBUF		; get char from RBUF (clears RBFL)
	ifbit 	RBIT9, ENUR	; if 9th bit = 1
	jp	checkaddr2	; then check for correct address
	jp	waitaddr		; else wait for next char

checkaddr2:
	ifne	A, #BASE_ADDR	; if addr doesn't match
	jp	waitaddr		; wait for another address
	
checktbmt1:
	ifbit 	TBMT, ENU		; if transmit buffer is empty
	jp	sendack		; then go ahead and send acknowledgment
	jp	checktbmt1	; else wait

sendack:
	sbit	1, PORTLD		; TXE=1
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	x	A, TBUF		; and send acknowledgement
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay

checktbmt2:
	ifbit 	TBMT, ENU		; if transmit buffer is empty
	jp	waitxmtg1		; then check XMTG flag
	jp	checktbmt2	; else wait

waitxmtg1:
	ifbit 	XMTG, ENUR	; if still transmitting acknowledgment
	jp	waitxmtg1		; then wait here
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	rbit	1, PORTLD		; else TXE=0
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	
ldtim:				; load T1 with 40000 cycles (=40ms)
	ld	TMR1LO, #040	; load T1 lower byte with 40h
	ld	TMR1HI, #09C	; load T1 upper byte with 9Ch

stim:
	ld	PSW, #00		; disable interrupts
	ld	CNTRL, #040	; configure T1 for input capture mode, positive T1A

; Transmit for 250us.
;
	ld	B, #0a		; B=10
tx_pulse:	
	ld	PORTCD, #05	; TXPOS,INIT=high, TXNEG=low (3 cycles)
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	ld	PORTCD, #06	; TXNEG,INIT=high, TXPOS=low (3 cycles)
	nop
	nop
	nop
	nop
	drsz	B		; repeat until B=0 (3 cycles)
	jp	tx_pulse		; (3 cycles)

	rbit	#2, PORTCD	; INIT=low
	
waitcap:
	ifbit 	T1PNDA, PSW	; if capture occurred
	jp	stocap		; then go store capture value
	ifbit 	T1C0, CNTRL	; else if underflow occurred
	jp	stoerr		; then go store error value
	jp	waitcap		; else keep waiting

stoerr:
	rbit	T1C0, CNTRL	; reset T1C0 to disable counting
	rbit	T1PNDA, PSW	; reset T1PNDA just in case
	ld	A, #0EE		; load A with EE
	x	A, TOTLO		; save value in TOTLO
	ld	A, #0EE		; load A with EE
	x	A, TOTHI		; save value in TOTHI
	jp	testerr		; send it out

stocap:
	ld	A, T1RALO		; read T1RA low byte
	x	A, RAWLO		; save value in RAWLO
	ld	A, T1RAHI		; read T1RA high byte
	x	A, RAWHI		; save value in RAWHI

waitund:
	ifbit 	T1C0, CNTRL	; if 40ms is finished
	jp	tmrdone		; then send out value
	jp	waitund		; else wait for underflow	

tmrdone:
	rbit	T1C0, CNTRL	; reset T1C0 to disable counting
	rbit	T1PNDA, PSW	; reset T1PNDA for next time
	
subtrct:
	ld	A, #040		; A = LSB of 40000
	x	A, TOTLO		; TOTLO = LSB of 40000
	ld	A, #09C		; A = MSB of 40000
	x	A, TOTHI		; TOTHI = MSB of 40000
	sc			; set carry (clear borrow)
	ld	X, #RAWLO		; set X ptr to address of RAWLO
	ld	B, #TOTLO		; set B ptr to address of TOTLO
subloop:
	ld	A, [X+]		; load A with subtrahend (RAWLO)
	x	A, [B]		; exchange
	subc	A, [B]		; A = TOTLO - RAWLO
	x	A, [B+]		; exchange
	ifbne 	#4		; if B ptr != end of TOT
	jp	subloop		; then repeat
	
div:
	ld	A, #094		; A = LSB of 148
	x	A, RAWLO		; RAWLO = LSB of 148
	ld	A, #00		; A = MSB of 148
	x	A, RAWHI		; RAWHI = MSB of 148
	ld	CNTR, #16		; no leading zero means decimal
	
	
; division routine to scale time into inches
; distance (inches) = time (microseconds) / 148 (microseconds/inch)
;
; This routine is taken from the COP8 Feature Family User's Manual,
; page B-26.
;
	ld	B, #5
	ld	[B-], #0
	ld	[B], #0
	ld	X, #4
	
lshft:
	rc
	ld	B, #2
	ld	A, [B]
	adc	A, [B]
	x	A, [B+]
	ld	A, [B]
	adc	A, [B]
	x	A, [B+]
	ld	A, [B]
	adc	A, [B]
	x	A, [B+]
	ld	A, [B]
	adc	A, [B]
	x	A, [B+]

tsubt:
	sc
	ld	B, #0
	ld	A, [X+]
	subc	A, [B]
	ld	B, #1
	ld	A, [X-]
	subc	A, [B]
	ifnc
	jp	test

subt:
	ld	B, #0
	ld	A, [X]
	subc	A, [B]
	x	A, [X+]
	ld	B, #1
	ld	A, [X]
	subc	A, [B]
	x	A, [X-]
	ld	B, #2
	sbit	0, [B]

test:
	drsz	CNTR
	jmp	lshft


; Time to write out value to UART.
; First check for measurement out-of-range (if one byte won't hold
; entire distance).
;
testerr:
	ld	A, TOTHI		; load MSB of TOT into A
	ifne	A, #00		; if MSB is not zero
	jp	outrange		; then send out-of-range code out
	ld	A, TOTLO		; else send LSB of TOT out
	jp	checktbmt3	; and jump to transmit
	
outrange:
	ld	A, #0FF		; A = out-of-range code

checktbmt3:
	ifbit 	TBMT, ENU		; if transmit buffer is empty
	jp	tdxnow		; then go ahead and send value out
	jp	checktbmt3	; else wait

tdxnow:
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	sbit	1, PORTLD		; TXE=1
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	x	A, TBUF		; and send distance

checktbmt4:
	ifbit 	TBMT, ENU		; if transmit buffer is empty
	jp	waitxmtg2		; then check for XMTG flag
	jp	checktbmt4	; else wait

waitxmtg2:
	ifbit 	XMTG, ENUR	; if still transmitting distance
	jp	waitxmtg2		; then wait here
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	rbit	1, PORTLD		; else TXE=0
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	nop			; 1usec delay
	jp	waitaddr		; go back to idle

	.endsect
	.end start