; SEROUT port (in w), pin, baud, polarity, data
; Transmits data serially at the specified rate and polarity out the specified port
; pin. Data bytes to be transmitted must be arranged in memory starting at the
; address "buffer-1" and working downward (e.g., 30,29,28...). The number of
; bytes to transmit must be stored at the address labeled "buffer." A companion
; routine, BIN_ASC, converts 16-bit numbers to this form, optionally adding a
; decimal point. See the listing for Serout ...(format # data).
; Serout uses the common data format of no parity, 8 data
; bits, 1 stop bit (N81).

; Device data and reset vector
	device	pic16c55,xt_osc,wdt_off,protect_off
	reset	start

buffer	=	31	; String storage at end of memory.
B2400	=	30	; Bit delay for 2400 baud at 4MHz.
B1200	=	62	; Bit delay for 1200 baud at 4MHz.
B600	=	126	; Bit delay for 600 baud at 4MHz.
B300	=	254	; Bit delay for 300 baud at 4MHz.

	org	8
baud	ds	1	; Baud rate.
pin	ds	1	; Pin number (0-7).
port	ds	1	; Port number (0-2).
temp1	ds	1	; Temporary counter for Serout.
temp2	ds	1	; Temporary counter for Serout.
temp3	ds	1	; Temporary counter for delay.
polarity	=	PA2	; Polarity: 0=true, 1=inverted.
; Note that the polarity flag is the unused page-address bit of the PIC 16C5x
; series. If you port this program to another type of PIC, or another routine uses
; this bit, you may have to reassign it.

	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

; Subroutine used by Serout to send a bit with the specified polarity (true or
; inverted) and timing (300, 600, 1200, or 2400 bps).
emitBit mov	fsr,port	; Point to the output port.
	mov	temp3,baud	; Load baud rate into temp counter.
	mov	w,status	; Copy carry bit (data bit) into w.
	snb	polarity	; If polarity = 1 then invert w.0.
	XOR	w,#1
	AND	w,#1	; Copy w.0 back to carry bit.
	clc	; (This roundabout method of inverting
	snz	; carry is needed since "XOR status,w"
	stc	; doesn't work properly.)

; This loop deserves some explanation. The first time through, it sets or clears
; the serial output bit. Thereafter, it's just a delay loop.
:loop	mov	w,pin	; Get the pin mask.
	sc	; Data in carry bit.
	OR	indirect,w	; If carry = 0 then set bit.
	mov	w,/pin
	snc
	AND	indirect,w	; Else if carry = 1 then clear bit.
	jmp	$+1	; Two-cycle nops for time delay.
	jmp	$+1
	djnz	temp3,:loop	; Number of trips through loop
	ret	; set by baud rate in temp3.

; Start of demo program. Upon power up or reset, the PIC transmits the string
; "OK" via pin 2 of port ra at 2400 baud, inverted polarity

start	clr	ra	; High is a start bit, so hold ra low.
	mov	!ra, #0 ; All outputs.
	mov	buffer,#2	; Put test string "OK"
	mov	buffer-1,#'O'	; into the buffer, starting with the
	mov	buffer-2,#'K'	; number of characters.
	setb	polarity	; Inverted for direct connection.
	mov	baud,#B2400	; 2400 baud.
	mov	pin,#2	; Pin 2.
	mov	port,#0 ; of port ra.
	call	Serout	; Send the data.
	jmp	$	; Endless loop

; Upon entry, the desired pin must already be set up as an output.
; The w register contains a number representing the output port (0-2) for
; RA through RC. Variable "pin" contains the pin number (0-7). The
; variable baud must contain a number representing the baud rate.
; The polarity flag must be set (1) for true, or cleared (0) for inverted
; output. A data string consisting of the number of bytes, followed by the
; bytes arranged downward in memory, must be at the location "buffer."
; Upon return, the data in the buffer will be unchanged, but the 0th entry
; (representing the string length) will have been decremented to 0.

Serout	add	port,#RA	; Add offset for port RA.
	mov	w,pin
	call	Pinz	; Get bit mask from the table.
	mov	pin,w	; Put the mask into pin.
	mov	temp1,#buffer-1 ; Point to first data address.
:xmit	clc	; Start bit into carry
	call	emitBit ; Send start bit.
	mov	temp2,#8	; Send 8 data bits.
:bits	mov	fsr,temp1	; Point to data byte.
	rr	indirect	; Rotate bit 0 into carry.
	call	emitBit ; Send the data bit.
	djnz	temp2,:bits	; All 8 bits sent? No: send more bits.
	mov	fsr,temp1	; Point to the data byte.
	rr	indirect	; Rotate it back into original position.
	sb	polarity	; If it was inverted, invert it again to
	NOT	indirect	; restore original.
	stc	; Move stop bit into carry
	call	emitBit ; Send the stop bit.
	dec	temp1	; Point to next data byte.
	djnz	buffer,:xmit	; All bytes sent? No: send more bytes.
	ret	; Yes: return.
