title "Hardware shakeout program and software library prototype"
subtitle "Hardware shakeout program and software library prototype"

; Building initial routine library while testing hardware.

	radix dec
	processor 16f628a
	include "p16f628a.inc"
	__CONFIG 0x3f62			; HS oscillator
	errorlevel -302

	constant XTAL= 18432000		; multiple of 300

	# BRGH= 1
	constant H115200= 9		; async bit rate constants
	constant H19200= 59
	constant H9600= 119
	constant H4800= 239
	# BRGH= 0
	constant L19200= 14
	constant L9600= 29
	constant L4800= 59
	constant L2400= 119
	constant L1200= 239

	constant __DLY10uS= 11		; 10uS loop constant
	constant __DLY100uS= 125	; 100uS loop constant


; Model 01a IO constants.

; NOTE: These are for the unrevised board!!!

; Serial Data output, in common with all on-board peripherals.
	constant SD= 0			; PORTA, RA0, SR data,

; Shift register outputs PORTA, PORTB, PORTC.
	constant SRA= 1			; PORTA, RA1, SR A clock,
	constant SRB= 2			; PORTA, RA2, SR B clock,
	constant SRC= 5			; portB, PB5, SRC clock NOT PORTA!

; MAX515 DAC bits.
	constant DAC= 3			; PORTA, RA3, DAC select,
	constant SCLK= 4		; PORTA, RA4, DAC CLK,

; Onboard UART pins, and 01a-defined support pins.
	constant RXD= 1			; portB, PB1, hardware UART RX,
	constant TXD= 2			; portB, PB2, hardware UART TX,
	constant CTS= 7			; portB, PB7, conventional, CTS,
	constant RTS= 6			; portB, PB6, conventional, RTS,

; We reserve the 16 bytes at the top of the register section for
; the library routines, because they don't need a banksel.

	constant LIBMEM= h'70'		; start of library routine reserved
	constant LIBLEN= h'80' - LIBMEM ; it's length

;	constant mS= h'70'
;	constant mSlo= h'70'
;	constant mShi= h'71'		; ISR two byte millisec timer,
;	constant __ISRF= h'72'		; ISR flags
;	constant switches= h'73'	; readsw() single switches, bit field
;	constant swval= h'74'		; readsw() rotary switch, value
;	constant __J= h'75'		; various
;
;	constant __DC1= h'7c'		; dlynnnxS () counter,
;	constant __DC2= h'7d'		; dlynnnxS () counter,
;	constant __ISRW= h'7e'		; ISR save W,
;	constant __ISRS= h'7f'		; ISR save STATUS,

; -------------------------------------------------------------------------

	cblock LIBMEM

	mS:2, mSlo, mShi	; ISR two byte millisec timer,
	__ISRF:1		; ISR flags, see below.
	switches:1		; readsw() single switches, bit field
	swval:1			; readsw() rotary switch, value

	__DC1:1			; dlynnnxS () counter,
	__DC2:1			; dlynnnxS () counter,
	__J:1			; various
	__ISRW:1		; ISR save W,
	__ISRS:1		; ISR save STATUS,

	endc

; Constants for readsw().

	constant SWPORT= SRC		; port that scans switches, PORTx
	constant SWPOLE= 6		; switch pole, PBx
	constant SWMASK= 0		; mask for rotary switch bits

; ISR code constants. See "PIC BRG calculator.sxc" for timer counts.

	constant __ISRPS= b'10000100'	; TMR0 prescale 32, Fosc/4
	constant __ISRINC= 1		; __ISRF bit: mS timer has changed,
	constant __ISR1MS= 144		; __ISRF bit: TMR0 count for 1mS int,

;
; End of Model 01a constants.
; ============================================================


	cblock h'20'
	pattern:1
	lastSW:1
	XDAC:2, XDAChi, XDAClo		; note Big Endian order
	YDAC:2, YDAChi, YDAClo
	endc

; -------------------------------------------------------------------------

	org 0
	goto __MAIN

	org 4				; ISR not always used, but
	goto __ISR			; this overhead is minimal.

; -------------------------------------------------------------------------

; Main entry point; clear the memory we reserve for the
; library routines, turn off some hardware.

__MAIN:	
	movlw LIBMEM & 255		; clear lib routine memory
	movwf FSR
	movlw LIBLEN
__m1	clrf INDF
	incf FSR, f
	addlw 255
	btfss STATUS, Z
	goto __m1

	banksel CMCON			; disable the analog
	movlw 7				; comparator, which
	movwf CMCON			; frees up PBx bits.

; -------------------------------------------------------------------------

	call SRinit			; ready SR outputs,
	call DACinit			; init DAC as necessary,

	movlw 32			; 2400 baud
	call serinit

	clrf pattern
	clrf XDAChi
	clrf YDAChi
	clrf XDAClo
	clrf YDAClo

	bcf STATUS, RP1
	bsf STATUS, RP0
	movlw 0
	movwf TRISB

	bcf STATUS, RP0
foo2:
	bsf PORTB, 7
	bcf PORTB, 7
	incf pattern, f
	movf pattern, w

	call incDAC
	movlw XDAC
	call outDAC			; output to DACs,

	movf pattern, w
	call outA			; output to PORTA,

	movf pattern, w
	call serout

	movlw 1
	call dly10mS
	goto foo2

	movlw 1

	call readsw			; scan switches,

	movf switches, w		; if switches different
	xorwf lastSW, w			; than last time,
	btfsc STATUS, Z
	goto foo2

	movf switches, w
	movwf lastSW

	movlw "S"
	call serout
	movlw "W"
	call serout
	movlw "="
	call serout
	movf swval, w
	addlw "0"			; output switch #,
	call serout
	movlw 13
	call serout
	movlw 10
	call serout
	goto foo2


; Generate a pretty pattern for LEDs.

makepat:
	incf pattern, f
	movlw 1
	call dly10mS
	return


; Increment the DACs.

incDAC:	incf XDAClo, f
	btfsc STATUS, Z
	incf XDAChi, f
	incf YDAClo, f
	btfsc STATUS, Z
	incf YDAChi, f
	return





; ------------------------------------------------------------------
; ISR CODE:
;
; Timer value is from the PIC BRG spreadsheet.
; Timer0 module data is in the PIC datasheet, PDF page 47.
;
; Interrupt service routine code. By default the interrupt system
; is disabled. Calling ISRinit() starts the 1mS software timer,
; using Timer0 (TMR0).  When TMR0 reaches terminal count, this
; code sets the __ISRINC flag, then increments the 16-bit timer 'mS'
; (mSlo, mShi). The normal thread code can use this flag to check
; that the two 8-bit halves of the mS timer are in phase (eg. the
; case where an interrupt occurs between accesses of the two halves).

ISRinit:
	banksel INTCON
	clrf INTCON				; disable all interrupts,

	banksel OPTION_REG
	movlw __ISRPS				; set TMR0 prescaler,
	movwf OPTION_REG
	call __ISRTMR0				; start TMR0 int,
	bsf INTCON, GIE				; global interrupt enable.
	return


;------------------------------------------------------------
;
; This is the entry point for interrupts. Carefully save W and
; STATUS, then determine the source and dispatch to the right
; handler.

__ISR:	movwf __ISRW				; enter ISR,
	swapf STATUS, w				; enter ISR,
	movwf __ISRS

; Test each of the sources and dispatch.
	btfsc INTCON, T0IF			; if Timer0,
	call __ISRTMR0				; call it,

; Return from interrupt, registers saved properly.

__ISRRET:
	swapf __ISRS, w
	movwf STATUS
	swapf __ISRW, f
	swapf __ISRW, w
	bcf INTCON, T0IF
	retfie
;
;------------------------------------------------------------



; Handle a Timer0 interrupt; increment the counter, set the
; 'incremented' flag, and re-set the timer to interrupt
; again. Is also used to init Timer0 in the first place.

__ISRTMR0:
	incf mSlo, f				; increment the timer,
	btfsc STATUS, Z				; propagate carry to MS,
	incf mShi, F
	bsf __ISRF, __ISRINC			; timer changed,
	movlw __ISR1MS				; count for 1mS overflow,
	movwf TMR0				; set down count,
	bcf INTCON, T0IF			; clear interrupt.
	return


; Delay W 10 uS intervals. This was hand calibrated on a
; 20 MHz HS oscillator. It measured precisely 2.5mS at
; W=250; 1.0mS at W=100; 101uS at W=10, and 11.8uS at
; W=1.

dly10uS	movwf __DC1
__dly1	movlw __DLY10uS
__dly2	addlw 255
	btfss STATUS, Z
	goto __dly2
	nop
	nop
	nop
	decfsz __DC1, f
	goto __dly1
	return

; Delay W 100 uS intervals. This was hand calibrated on a
; 20 MHz HS oscillator. It measured precisely 25mS at
; W=250; 10mS at W=100; 1.01mS at W=10; 100uS at W=1.

dly100uS
	movwf __DC1
__dly3	movlw __DLY100uS
__dly4	addlw 255
	btfss STATUS, Z
	goto __dly4
	decfsz __DC1, f
	goto __dly3
	return

; Delay W 10mS intervals.

dly10mS	movwf __DC2
__dly5	movlw 100
	call dly100uS
	decfsz __DC2, f
	goto __dly5
	return


; Initialize the UART for asyncrhonous in and out
; and set the bit rate. This sets PB2 (TD) as output,
; PB1 (RXD) as input and sets the bit rate to the factor
; W.  See PIC16F628a data sheet PDF page 73-75.

serinit
	banksel TRISB			; bank
	bsf TRISB, RXD			; 1  RB1 is input, RD
	bcf TRISB, TXD			; 1  RB2 is output, TD

	bcf TXSTA, BRGH			; 1  set LOW B.R. range,
	movwf SPBRG			; 1  set bit rate divisor,

	bsf TXSTA, TXEN			; 1  Tx enable

	banksel RCSTA
	bsf RCSTA, SPEN			; 0  Rx enable,
	bsf RCSTA, CREN			; 0  continuous Rx,

	return

; Output W to the UART.

serout	banksel PIR1
__so1	btfss PIR1, TXIF		; Tx full
	goto __so1			; wait...
	movwf TXREG			; output to transmitter.
	return


; Output W to PORTA's shift register.

outA:	movwf __J			; the word to output,
	movlw 8

oa1	bcf PORTA, SD			; set SR data input to 0 
	btfsc __J, 7			; or 1 to match MSB,
	bsf PORTA, SD

	bsf PORTA, SRA			; pulse the clock,
	bcf PORTA, SRA
	rlf __J, f			; shift data left,

	addlw 255
	btfss STATUS, Z
	goto oa1

	return

outB:	movwf __J			; the word to output,
	movlw 8
ob1	bcf PORTA, SD			; set SR data input to 0 
	btfsc __J, 7			; or 1 to match MSB,
	bsf PORTA, SD
	bsf PORTA, SRB			; pulse the clock,
	rlf __J, f			; shift data left,
	bcf PORTA, SRB
	addlw 255
	btfss STATUS, Z
	goto ob1
	return

outC:	movwf __J			; the word to output,
	movlw 8
oc1	bcf PORTA, SD			; set SR data input to 0 
	btfsc __J, 7			; or 1 to match MSB,
	bsf PORTA, SD

	bsf PORTB, SRC			; pulse the clock,
	rlf __J, f			; shift data left,
	bcf PORTB, SRC
	addlw 255
	btfss STATUS, Z
	goto oc1
	return

; Ready the IO pins necessary to write to the SRs.
; SD, SRA, SRB and SRC all become outputs.

SRinit:
	banksel TRISA
	bcf TRISA, SD
	bcf TRISA, SRA
	bcf TRISA, SRB
	bcf TRISB, SRC			; note not PORTA!
	banksel PORTA
	return


;
; Read up to 8 switches tied to SR C outputs (switch contacts)
; with the pole tied to pin7, and store the result in 'switches'.
; Switches should be debounced in hardware, there is a 1mS delay to
; allow the debounce RC to charge/discharge. This polls the switches
; once per call, assumes this routine is called repeatedly in loops.
; The results of the first call are garbage, since this routine
; relies on the previous call to have initialized the SR. Modifies J.
; 
; __J is our loop counter and bit-position tester; we shift it
; left until it's 0.

readsw	movlw 1					; seed loop ctr/switch value J
	movwf __J
	clrf switches				; assume none closed,
	bsf PORTB, SD				; clock a 1 into LSB 1st time,

_raw1	bsf PORTB, SWPORT			; clock bit over,
	bcf PORTB, SWPORT	
	bcf PORTB, SD				; shift in 0's

; This makes the set loop too slow, and we miss rotary encoder
; state changes! Now if it were interrupt driven...
;	call _P1msec				; brief delay for C on sw line,

	btfsc PORTB, SWPOLE			; if switch is ON (pulls to gnd)
	goto _raw2
	movf __J, w				; get test bit,
	iorwf switches, f			; set "switch" ON,
_raw2	bcf STATUS, 0				; clear carry before shift in
	rlf __J, f				; move 'em over,
	btfss STATUS, 0				; until bit moves to carry,
	goto _raw1				; repeat;
;
; Now turn the rotary switch bits into an index, eg. position number.
; For a 3-pos switch, values run 1, 2, 4; convert this to 0, 1, 2
; by finding the first bit set. For failsafe, we shift in 1's from the left
; to ensure termination.
;
	movf switches, w			; get switches read,
	andlw SWMASK				; mask off our bits,
	movwf __J				; save for test,
	clrf swval				; set initial value,
_raw3	btfsc __J, 0				; if switch on,
	return					; clear WDT, return val.

	bsf __J, 7				; (failsafe)
	rrf __J, f				; shift to next bit,
	incf swval, f				; next value,
	goto _raw3				; keep looking.



; Initialize the DAC system. Since this should be done
; soon after __MAIN, the DAC X and Y values will be zero.

DACinit:
	banksel TRISA
	bcf TRISA, SCLK			; SCLK is an output,
	bcf TRISA, DAC			;
	bcf TRISA, SD			; etc.
	banksel PORTA
	bcf PORTA, SCLK			; SCLK must be low when /CS transits
	bsf PORTA, DAC			; deassert /CS
	return

; Driver for Maxim MAX515 10-bit DACs, two daisy chained.
;
; Output the four bytes pointed to by W to the DACs. The bytes
; are assumed to be X msb, X lsb, Y msb, Y lsb.  Refer to the MAX515
; datasheet; each chip, wired in series, wants 16 bits total; four
; 0-fill bits, 10 data bits, two dummy LSBs, two trailing bits to
; round to 16. The dummy LSBs for X and the leading 0's for Y are
; output in one call.
;
; Modifies J, I.
;
outDAC	movwf FSR			; set pointer,
	bcf PORTA, DAC			; assert DAC /CS,
	bcf STATUS, IRP			; 9th address bit
	movf INDF, w			; X DAC hi,
;	movf XDAChi, w	;FOO
	andlw b'00000011'		; we need at least 4 0's before MS bits,
	movwf __J			; X MSBs to J, upper bits 0's,
	rlf __J, f
	rlf __J, f			; now four 0's precede X MSBs,
	movlw 6				; 4 fill + 2 data, from MS end,
	call _outJ			; output those,

	incf FSR, f
	movf INDF, w			; X DAC low,
;	movf XDAClo, w	; FOO
	movwf __J
	movlw 8
	call _outJ			; output 8 LSBs,

; The X DAC still needs two dummy LSBs; we'll output them
; when we issue Y MSBs.

	incf FSR, f			; point to Y hi,
	movf INDF, w			; Y DAC hi
;	movf YDAChi, w	; FOO
	andlw b'00000011'		; enforce format,
	movwf __J			; Y MSBs to J, upper bits 0's,
	movlw 8
	call _outJ			; output 2 dummy LSBs, 4 fill, 2 data,

	incf FSR, f
	movf INDF, w			; Y DAC low,
;	movf YDAClo, w	; FOO
	movwf __J
	movlw 8
	call _outJ			; output 8 Y LSBs,

	clrf __J
	movlw 2				; now final 2 dummy LSBs.
	call _outJ

	bcf PORTA, SD			; just cleaner on the scope...
	bsf PORTA, DAC			; deassert /CS
	return

;
; Output (reg. W) bits of (J) to the DAC, starting
; with the MSB.
; Modifies I, J.
;
_outJ	bcf PORTA, SD			; data=0...
	btfsc __J, 7			; if MSB true,
	bsf PORTA, SD			; ...data=1.
	bsf PORTA, SCLK
	bcf PORTA, SCLK			; clock it
	rlf __J, f			; (move MSB to carry to test)
	addlw 255			; decrement W,
	btfss STATUS, Z
	goto _outJ
	return





	end


