; PULSIN port (in w), pin, state
; Measures the width of an input pulse in 10-cycle (10 us at 4 MHz) units.
; Returns a 16-bit (0 to 65,535) value. If no pulse occurs within
; 65,535 10-cycle intervals, the routine returns an error code in w.
; Routine is edge triggered. If the input pin is already in the target state
; when the routine begins, it will wait for the state to change, then wait
; for the specified edge. The routine waits 65,535 x 10 cycles for
; each edge, so it normally returns within 0.65535 seconds (4 MHz).
; However, if each transition is delayed by close to the full 65,535 x 10
; cycles, the routine can take nearly 2 seconds to return (4 MHz).

	device	pic16c55,xt_osc,wdt_off,protect_off
	reset	start
	
	org	8
pin	ds	1		; Pin number to sample (0-7).
state	ds	1		; Trigger: 255 = 0-to-1 transition.
hiB	ds	1		; MSB of measurement.
lowB	ds	1		; LSB of measurement.

; Device data and reset vector
	org	0
; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b).

Pinz	jmp	pc+w
	retw	1,2,4,8,16,32,64,128

start	mov	!rb,#0		; Outputs to display result on LEDs.
	mov	!rc,#0
	mov	!ra, #1111b	; All inputs
	mov	state,#0	; Negative pulse.
	mov	pin,#2		; Pin 2.
	mov	w,#0		; Port ra.
	call	Pulsin
	mov	rb,lowB 	; Write Pulsin result to ports RB
	mov	rc,hiB		; and RC for viewing on LEDs.
	jmp	start		; Do it again.

; Upon entry, the desired pin must already be set up as an input.
; The w register contains a number representing the input port (0-2) for
; RA through RC. Variable pin contains the pin number (0-7). Variable state
; contains 255 for a 0-to-1 (positive) pulse, or 0 for a 1-to-0 (negative) pulse.

; Upon exit, the 16-bit count is in variables lowB and hiB.
; Variable pin contains the mask bit used by pulsin (not the pin number
; anymore, but a value with a 1 in the pin position), and state has been
; ANDed with pin. W contains the error code: 0= no error; 1= pin in
; already in target state when Pulsin was called--timeout waiting
; for transition; 2= timeout waiting for initial edge; 3= timeout waiting
; for final edge.

Pulsin	clr	lowB		; Clear the 16-bit counter.
	clr	hiB
	mov	fsr,w		; Point to the port number.
	add	fsr,#RA 	; Add offset for port RA.
	mov	w,pin		; Get bit mask from the table.
	call	Pinz
	mov	pin,w		; Put the mask into pin.
	AND	state,w		; Mask extra bits out of state.
:Hold	mov	w,indirect	; Sample the port.
	AND	w,pin		; Mask off bits other than pin.
	XOR	w,state		; Check pin against state.
	jnz	:doneH		; If pin <> state, set up for edge.
	inc	lowB		; Otherwise, lowB = lowB+1.
	snz			; Overflow in lowB?
	incsz	hiB		; Yes: hiB=hiB+1, watch for overflow.
	jmp	:Hold		; Sample the pin again.
	retw	1		; Overflow in hiB? Timed out: return.
:doneH	clr	lowB
	clr	hiB
:Edge1	mov	w,indirect	; Sample the port.
	AND	w,pin		; Mask off bits other than pin.
	XOR	w,state		; Check pin against state.
	jz	:doneE		; If pin = state, wait until change.
	inc	lowB		; Otherwise, lowB = lowB+1.
	snz			; Overflow in lowB?
	incsz	hiB		; Yes: hiB=hiB+1, watch for overflow.
	jmp	:Edge1		; Sample the pin again.
	retw	2		; Overflow in hiB? Timed out: return.
:doneE	clr	lowB
	clr	hiB
:Edge2	mov	w,indirect	; Sample the port.
	AND	w,pin		; Mask off bits other than pin.
	XOR	w,state		; Check pin against state.
	jnz	:done		; If pin <> state, we're done.
	inc	lowB		; Otherwise, lowB = lowB+1.
	snz			; Overflow in lowB?
	incsz	hiB		; Yes: hiB=hiB+1, watch for overflow.
	jmp	:Edge2		; Sample the pin again.
	retw	3		; Overflow in hiB? Timed out: return.
:done	ret			; Done: pulsewidth in hiB, lowB.
