; Program SSC_DEMO.SRC (Mini SSC educational demo).
; Written in Parallax dialect of PIC assembly language.
; This program is a demo version of the firmware for the Mini SSC,
; a device that controls eight radio-control servos in accordance with 
; instructions recieved over a 2400- or 9600-baud serial hook-up. The 
; commercial Mini SSC also recognizes individual board IDs, allowing up
; to 32 controller (256 servos!) on one serial port. This demo version 
; is not addressable, works at a fixed rate of 2400 baud, and controls 
; just on e servo. However, it has the same basic structure as the full-up
; program. It is provided for novice PIC users interested in a method for
; Running two time-sensitive processes concurrently.

; Serial data is accepted through ra.3; servo pulses (1 to 2 ms, repeated
; approx. 60 Hz) are output at rb.0. This program runs propertly on a 
; PIC16C61, 71, or 84 with an 8 MHz clock. At the time this program was 
; written, the 16C61 was new, and PASMX/PEPX did not recognize it as a 
; valid device. Fortunately, the '61 programs just find from code generated 
; for a 71 or 84 (provided you don't use the special features of those
; devices). If you program a 61, be sure that PEPX is set for a 71 - setting
; it for an 84 will cause programming errors.

	device pic16c71, xt_osc, wdt_off, protect_off, pwrt_off
	id 'SVDM'

;Symbols (constants) used in the program.
one_ms	=	6	; value to load into RTCC for 1-ms delay
ser_pin	=	ra.3	; Serial-input pin
stb24	=	44	; Start delay for 2400 bps @ 8 Mhz
bit24	=	164	; Full delay for 2400 bps @ 8 Mhz

; Variable declarations. Variables for this program begin at address
; 0C hex, placing them above the PIC's special-function registers.
	org	0Ch
svo_PC	ds	1	; Program segment counter for servo ISR
w_copy	ds	1	; Copy of w register made by ISR


delay_cnt	ds	1		; Counter for serial delay routines.
bit_cnt		ds	1		; Number of received bits.
rcv_byte	ds	1		; The received byte
temp		ds	1		; Reusable temporary storeage
position	ds	1		; Position value.
end_frame	ds	1		; Counter for end of frame delay


; Upon reset the PIC first executes the code located at address 0. In this
; demo, it jumps to the beginning of the foreground program located at
; "start" When an interrupt occurs, the PIC jumps to address 4. This is 
; where the interrupt handler is located.


		org	0
		jmp start		; The reset vector.
		org	4		; The interrupt handler.


; The interrupt-service routine (ISR). When RTCC overflows, the resulting
; interrupt sends the program here. This ISR stores the w and status
; registers, then jumps to the program segment stored in svo_PC.
; The segments load RTCC and start and stop the servo pulses
; based on the data found in the variable position. Once finished, they
; store the address of the next segment to be executed, restore the
; registers, and execute the reti (return from interrupt) instruction.


do_servos
		clrb	rtif		; Reset timer-interrupt flag.
		mov	w_copy,w	; Save the status and W registers.
		mov	w,<>status	; Swap status into w.
		mov	st_copy,w	
		mov	w,svo_PC	; Move servo program couter to w.
		jmp	w		; Jump to segment pointed to by svo_PC
; Segment zero starts the servo pulse by weiring a high to rb.0 and
; setting RTCC to interrupt again after a 1-ms delay, it loads segment 1
; into tho "servo program counter" that do_servos used to jump to the
; correct segment of the routine.


seg_0		setb	rb.0		; Initiate pulse
		mov	rtcc,#one_ms	; Set up 1-ms delay.
		mov	svo_PC#seg_1	; Execute next segment at 1-ms timeout
		mov	w,<>st_copy	; Restore the status register and W.
		mov	status, w
		swap	w_copy
		mov	w,<>w_copy	; Return from interrupt.
		reti


seg_1		mov	w,/position	; Load the inverse of the position value
		mov	rtcc,w		; into the rtcc to create delay
		mov	svo_PC,#seg_2	; Execute next segment after timeout
		mov	w,<>st_copy	; Restore the status register and W.
		mov	status,w
		swap	w_copy
		mov	w,<>w_copy
		reti			; Return from interrupt


seg_2		clrb	rb.0		; End the pulse.
		mov	w.position	; Load the non-inverted position value
		mov	rtcc.w		; into rbcc to create a pad delay
		mov	svo_PC,#seg_3	; Execute next segment after time out.
		mov	end_frame,#14	; Wait 14 ms before seg_0 (see seg_3)
		mov	w,<>st_copy	; Restore the status register and W.
		mov	status,w	
		swap	w_copy
		mov	w,<>w_copy
		reti			; Return from interrupt


seg_3		mov	rtcc,#one_ms	; Set up a 1-ms delay
		djnz	end_frame,:cont	; Repeat this segment untill end_frame=0
		mov	svo_PC,#seg_0	; When end_frame runs out, point to seg_0
:cont		mov	w,<>st_copy	; Restore status register and W.
		mov	status,w
		swap	w_copy
		mov	w,<>w_copy
		reti			; Return from interrupt 


; Start marks the beginning of the initialization code that sets up the
; program. It configures the RTCC to increment off the system clock
; through a divide-by-8 prescaler. At 8 MHz, that makes one RTCC tick
; equal to 4 microseconds - the desired resolution of our servo-pulse
; generator. Start also sets the servo routine's program counter to
; segment 0.


start		mov	svo_PC,#seg_0	; Point to base segment of ISR
		setb	rp0		; Switch to register page 1.
		clr	wdt		; Transfer the prescaler to RTCC.
		mov	option,#10000010b ; and set it for /8
		clrb	rp0		; Back to page 0.
		mov	intcon,#10100000b ; Turn on RTCC interrupt.
main		mov	lra,#1000b	; Serial input is ra.3.
		mov	lrb,#0		; make rb all outputs.
:start_bit				; Receive serial data.
		snb	ser_pin		; Look for start bit.
		jmp	:start_bit	; No start bit? keep watching.
		call	serl_in		; Get the serial data.
		mov	delay_cnt,#bit24 ; Stop-bit delay.
		call	delay		; Wait for stop bit.
		mov	posiiton,rcv_byte ; Move data to position
		goto	main		; Endless loop.


; Subroutine to read in serial data from the port bit ser_pin.
; Note that there's no stop-bit delay. Callers may incorporate
; this delay into other operation.


serl_in
		mov	bit_cnt,#8	; Eight data bits.
clr		rcv_byte		; Get ready for new data.
		mov	delay_cnt,#stb24 ; Start-bit delay
		call 	delay		; Wait untill first data bit.
:rcv		mov	delay_cnt,#bit24 ; Full-bit delay.
		call	delay		; Wait for bit.
		movb	c,ser_pin	; Put data bit into carry.
		rr	rcv_byte	; Rotate bit into byte.
		djnz	bit_cnt,:rcv	; Not full? Get next bit.
		ret


; Simple do-nothing loop for variable length dealys. Program must
; logad delay_cnt before calling this routine.


                                               delay		jmp	$+1
		djnz	delay_cnt,delay
		ret