;**********************************************************************
;   This file is a basic code template  for assembly code generation  *
;   on the PICmicro PIC17C44. This file contains the basic code       *
;   building blocks to build upon.                                    *  
;                                                                     *
;   If interrupts are not used all code presented for that interrupt  *
;   can be removed or commented out with a semicolon. Also the        *                         
;   interrupt code is structured for the microcontroller mode of      *
;   operation (up to 8K words of program memory) If desiring to use   *
;   this template for the extended microcontroller or microprocessor  *
;   modes (up to 64K words of program memory) the call or goto        *
;   instructions may need to be changed to the lcall or computed      *
;   long goto instructions.                                           *
;                                                                     *
;   Refer to the MPASM User's Guide for additional information on     *
;   features of the assembler (Document DS33014).                     *
;                                                                     *
;   Refer to the respective PICmicro data sheet for additional        *
;   information on the instruction set.                               *
;                                                                     *
;   Template file assembled with MPASM V2.11.01                       *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Filename:	    lockin.asm                                        *
;    Date:          7/2000                                            *
;    File Version:  6                                                 *
;                                                                     *
;    Author:        Bill O'Donnell                                    *
;    Company:       UNLV Physics Dept.                                *
;                                                                     * 
;                                                                     *
;**********************************************************************
;                                                                     *
;    Files required: p17c44.inc (in mplab directory)                  *
;                                                                     *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Notes: Digital lockin amplifier (Provides TTL sync signal out)   *
;           Set Config bits for microcontroller mode                  *
;            (internal memory only)                                   *
;           Set oscillator type to EC (External control)              *
;           Set postscaler to /64 for 12mS * 64 = 768mS               *
;           and enable watchdog time (~0.75 Second time out)          *
;           Use: AD796  100KS/S 16 bit A/D +-10V                      *
;                AD660  10uS settle to 1/2LSB 16 bit D/A +-10V        *
;                                                                     *
;           This version integrates each half cycle before digitizing *
;           There is no pre-integration hold due to memory effects    *
;           Also the uP lights an LED if the A/D is overloaded        *
;                                                                     *
;**********************************************************************

	list      p=17c44              ; list directive to define processor
	#include <p17c44.inc>          ; processor specific variable definitions
	
	__CONFIG   _XT_OSC & _WDT_OFF & _MC_MODE

; '__CONFIG' directive is used to embed configuration data within .asm file.
; The lables following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.


;*******    RAM EQUATES
; EQU <= 0x1F aren't banked (one inst movfp and pf if p<=1F)
TXDES		EQU	0x1A	; original number of bytes that need sending
CYLDES		EQU	0x1B	; desired # of low to high cycles to sum

TSUML		EQU	0x1C	; temp sum low byte
TSUMH		EQU	0x1D	; temp sum high byte
TSUMO1		EQU	0x1E	; temp sum first overflow byte
TSUMO2		EQU	0x1F	; temp sum second overflow byte

; EQU > 0x1F are banked
; note: CRET must me in the next mem loc after TSUM02 (indirect mem access)
CRET		EQU	0x20	; store a carriage return here (ascii 13)
TXCNT		EQU	0x21	; counter for # of bytes left to send
CYLCNT		EQU	0x22	; counter for low to high transitions of REF
SHDES		EQU	0x23	; desired # of bits to shift TSUM for DAC
SHCNT		EQU	0x24	; counter for shifting TSUM data
GAIN		EQU	0x25	; gain setting for analog input (0-7)

OLDREF		EQU	0x26	; 0xFF if last REF was high else 0x00

SUMLOW		EQU	0x27	; low byte of sum
SUMHIGH		EQU	0x28	; high byte of sum
SUMOVF1		EQU	0x29	; first overflow byte of sum
SUMOVF2		EQU	0x2A	; second overflow byte of sum

VOUTL		EQU	0x2B	; low byte for Vout
VOUTH		EQU	0x2C	; high byte for Vout
VOUTO1		EQU	0x2D	; first overflow byte for Vout
VOUTO2		EQU	0x2E	; second overflow byte for Vout

CMD		EQU	0x2F	; command byte read from serial port
DATA1		EQU	0x30	; first data byte read from serial port
TEMP		EQU	0x31	; temporary variable
CNT		EQU	0x32	; counter for integration time
CNTDES		EQU	0x33	; desired loop count for integration time
SKP		EQU	0x34	; counter for dead time
SKPDES		EQU	0x35	; desired loop count for dead time

RAWLOW		EQU	0x36	; low byte read from A/D
RAWHIGH		EQU	0x37	; high byte read from A/D

TEMP_WREG       EQU     0x38	; used for PUSH and POP macros
TEMP_ALUSTA     EQU     0x39
TEMP_BSR        EQU     0x3A
TEMP_PCLATH     EQU     0x3B


;*******    CONSTANTS AND MASKS
; PORT A -  RA4 & 5 (RX & TX) are used for the RS232 communication
; RA2 & 3 are used for the reset and input to the integrator
OVLED		EQU	d'1'	; A/D overflow LED on when low (open drain)

; PORT B - Note:DAHBE is inverted and fed to LBE (the DAC low byte enable)
DALDAC		EQU	d'0'	; D/A load dac (updates output)
DAHBE		EQU	d'1'	; D/A high byte enable (byte mux control)
DACS		EQU	d'2'	; D/A chip select (used to load bytes)
PWMPH		EQU	d'3'	; PWM output for phase adjust
REFCOMP		EQU	d'4'	; ref input from comparator (for phase calc)
GAIN0		EQU	d'5'	; LSB of gain setting for input amp (A0)
GAIN1		EQU	d'6'	; middle gain setting for input amp (A1)
GAIN2		EQU	d'7'	; MSB of gain setting for input amp (A2)
; A0,A1,A2 - 000->111, GAIN = 1,3,10,30,100,300,1000,3000

; PORT C - all inputs from A/D converter
; PORT D - all outputs to D/A converter

; PORT E
ADBYTE		EQU	d'0'	; A/D byte select PORTE (byte on A/D)
ADSTRB		EQU	d'1'	; A/D strobe output PORTE (R/C on A/D)
REF		EQU	d'2'	; ref input PORTE (sync in - TTL)


;*******    MACROS
; Note: canged PUSH macro - replaced all movfp with movpf and moved
; associated registers to a banked location above 0x1F
PUSH        MACRO                 ;macro for saving registers
            movpf   WREG,TEMP_WREG
            movpf   ALUSTA,TEMP_ALUSTA
            movpf   BSR,TEMP_BSR
            movpf   PCLATH,TEMP_PCLATH
            ENDM


POP         MACRO                 ;macro for restoring registers
            movfp   TEMP_PCLATH,PCLATH
            movfp   TEMP_BSR,BSR
            movfp   TEMP_ALUSTA,ALUSTA
            movfp   TEMP_WREG,WREG
            ENDM

	
;**********************************************************************
                ORG    0x000
;you may want to clear PCLATH and ALUSTA registers here      
                goto   start	


;************   INT PIN INTERRUPT VECTOR
                ORG    0x008
                PUSH              ;save specific registers
                goto   intpin_isr_handler
               

;************   TIMER0 INTERRUPT VECTOR
                ORG    0x010
                PUSH	          ;save specific registers
                goto   timer0_isr_handler
               

;************   T0CKI PIN INTERRUPT VECTOR
                ORG    0x018
                PUSH              ;save specific registers
                goto   t0cki_isr_handler
               

;************   PERIPHERAL INTERRUPT VECTOR
                ORG		0x020
                PUSH              ;save specific registers
; isr code can go here or be located as a call subroutine elsewhere
                POP               ;restore specific registers
                retfie            ;return from interrupt


;************************************************************************
;*****	MAIN PROGRAM *****
start		
	clrwdt			; clear the watchdog timer
	bsf	CPUSTA,GLINTD	; disable all interupts
	call	usart		; set up serial port
	movlb	1		; select bank 1
	movlw	d'6'		; # of cycles to sum before sending data
	movpf	WREG,CYLDES	; set desired cycle count
	movlw	d'5'		; # of characters to send out serial port
	movpf	WREG,TXDES	; set transmit count to 5 (4 bytes + CR)
	movlw	d'13'		; carriage return (ASCII 13) will be sent
	movpf	WREG,CRET	; out after the data bytes
	movlw	d'2'		; # of bits to shift right before VOUT->DAC
	movpf	WREG,SHDES	; set desired shift count (avoid overflow)
	movlw	d'0'		; A2,A1,A0 are LSB's of GAIN
	movpf	WREG,GAIN	; set gain value to 1 (000)
	call	gainst		; set the I/O pins to the new gain value
	movlw	d'22'		; loop counter for delay2 - integration
	movpf	WREG,CNTDES	; time (t = ? + 5 + 38 * CNTDES) 
	movlw	d'33'		; loop counter for delay3 - edge jitter
	movpf	WREG,SKPDES	; time (t = ? + 5 + 4 * SKPDES)

;**************************
	movlb	0		; select bank 0
	; port B: bit 0 = DALDAC, 1 = DAHBE, 2 = DACS, 3 = PWM_PH
	; port B: bit 4 = REFCOMP, 5 = GAIN0 , 6 =GAIN1 , 7 = GAIN2
	movlw	b'00010000'	; port B has one input and three outputs
	movpf	WREG,DDRB
	movlb	1		; select bank 1
	setf	DDRC,1		; port C all inputs (A/D low/high bytes)
	clrf	DDRD,1		; port D all outputs (D/A low/high bytes)
	movlw	b'11111000'	; port E three outputs 
	movpf	WREG,DDRE	; ADBYTE = 0 ,ADSTRB = 1, REF = 2

	; set pins to startup (default) state
	; set PORT E pins
	movlb	1		; select bank 1
	bcf	PORTE,ADBYTE	; select low byte of A/D
	nop
	bsf	PORTE,ADSTRB	; set A/D strobe line
	nop
	bcf	PORTE,REF	; set ref signal low
	; set PORT B pins
	movlb	0		; select bank 0
	bcf	PORTB,DALDAC	; set DAC load line
	nop
	bsf	PORTB,DAHBE	; enable low byte mux for DAC
	nop
	bsf	PORTB,DACS	; set DAC chip select
	nop
	bcf	PORTB,PWMPH	; clear output pin (PWM not enabled)
	; set input gain to 1
	bcf	PORTB,GAIN0	; clear LSB of input gain A0
	nop
	bcf	PORTB,GAIN1	; clear middle bit of input gain A1
	nop
	bcf	PORTB,GAIN2	; clear MSB of input gain A2

	bcf	RCSTA,OERR	; clear any overflow that may have occured

	; just for testing set registers to output to allow non-zero reading
	movlb	1		; select bank 1
;	clrf	DDRC,1
;	clrf	DDRD,1
;	clrf	DDRE,1

;	clear the sum and reset the cycle counter
	clrf	SUMLOW,1
	clrf	SUMHIGH,1
	clrf	SUMOVF1,1
	clrf	SUMOVF2,1

	clrf	TXCNT,1		; initially no data to send out serial
	clrf	SHCNT,1		; initially no shift needed for VOUT

	; set default values (or read from EEPROM ?)
	movpf	CYLDES,CYLCNT	; cyldes addr <= 0X1F
	movfp	CNTDES,WREG
	movpf	WREG,CNT

	; write 0x8000 to D/A to zero output (D/A clr tied high on PCB)
	clrf	PORTD,1		; set low byte to zero
	movlb	0		; select bank 0
	bcf	PORTB,DACS	; have DAC read the low byte
	nop
	bsf	PORTB,DACS
	nop
	bcf	PORTB,DAHBE	; select high byte for DAC mux
	movlb	1		; select bank 1
;	clrf	PORTD,1		; set high byte to zero
	movlw	0x80		; MSB is one (zero for bi-polar)
	movpf	WREG,PORTD	; set high byte
	movlb	0		; select bank 0
	bcf	PORTB,DACS	; have DAC read the low byte
	nop
	bsf	PORTB,DACS
	nop
	bsf	PORTB,DALDAC	; transfer 16 bit latch to outputs of DAC
	nop
	bcf	PORTB,DALDAC
	nop
	bsf	PORTB,DAHBE	; select low byte for DAC mux
	movlb	1		; select bank 1


;**********************************************************
; start of never ending loop

; high part of reference cycle
top

;refl	bcf	PORTE,REF	; set ref low

	; reset the integrator
	; S1 = LOW,  S2 = HIGH	INTEGRATE
	; S1 = HIGH, S2 = HIGH	HOLD
	; S1 = HIGH, S2 = LOW	RESET
	; new reset - keep S1 low
	movlw	b'00000000'
	movlb	0		; select bank 0
rst1	movpf	WREG,PORTA	; RA2 = S1, RA3 = S2
	movlb	1

	; read the A/D and subtract it from the sum
	movfp	PORTC,WREG	; read low byte from A/D
	bsf	PORTE,ADBYTE	; select high byte
	subwf	SUMLOW,1	; SUMLOW = SUMLOW - WREG
	movpf	WREG,RAWLOW	; store the low byte
	movfp	PORTC,WREG	; read high byte from A/D
	subwfb	SUMHIGH,1	; SUMHIGH = SUMHIGH - WREG - borrow
	movpf	WREG,RAWHIGH	; store the high byte
	clrf	WREG,0		; clear WREG but Carry set from above
	btfsc	PORTC,7		; check if number negative (MSB high)
	setf	WREG,0		; if negative extend leading ones
	subwfb	SUMOVF1,1	; SUMOVF1 = SUMOVF1 - 0 - borrow
	clrf	WREG,0		; clear WREG but Carry set from above
	btfsc	PORTC,7		; check if number negative (MSB high)
	setf	WREG,0		; if negative extend leading ones
	bcf	PORTE,ADBYTE	; select low byte for next time
	subwfb	SUMOVF2,1	; SUMOVF2 = SUMOVF2 - 0 - borrow

	; delay to give op-amp time to come to zero
	call	delay		; reset (slew rate of op-amp 3V/uS)

	call	delay3		; dead time for jitter removal

	; start the integrator
	; S1 = LOW,  S2 = HIGH	INTEGRATE
	; S1 = HIGH, S2 = HIGH	HOLD
	; S1 = HIGH, S2 = LOW	RESET
	; integration time (60 + 5 + (38 * CNTDES))*121ns @ 33.333MHz
	movlw	b'00001000'
	movlb	0		; select bank 0
si1	movpf	WREG,PORTA	; RA2 = S1, RA3 = S2
	movlb	1

	clrwdt			; clear the watchdog timer

	call	shift		; check if need to shift word for DAC

	; check if a byte has come in the serial port
	btfsc	PIR,RCIF	; if a byte is waiting to be read
	call	byterd		; read the byte

	; check if a character needs to be sent out the serial port
	call	sndout

	dcfsnz	CYLCNT,1	; if cycle counter is zero then
	goto	mvsum		; move the sum into a temporary space

	nop			; if cycle counter isn't zero then
	nop			; do some nop's instead that will take
	nop			; the same amount of time
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
mvret	; return to this point after doing mvsum

	call	delay2		; delay for rest of integration time

	; have the integrator hold the current value
	; S1 = LOW,  S2 = HIGH	INTEGRATE
	; S1 = HIGH, S2 = HIGH	HOLD
	; S1 = HIGH, S2 = LOW	RESET
	movlw	b'00001100'	; both S1 and S2 open
	movlb	0		; select bank 0
hld1	movpf	WREG,PORTA	; hold the current value
	movlb	1

	call	delay		; delay to let voltage stabilize
	call	delay4		; delay to let voltage stabilize

digs1	bcf	PORTE,ADSTRB	; strobe the A/D (trigger sample and hold
	nop
	bsf	PORTE,ADSTRB	; and begin conversion)

	call	delay3		; dead time for jitter removal

	; delay ~8us to give A/D time to convert the held voltage
	call	delay5

	nop			; to make the high & low times match
	nop

mid
;refh	bsf	PORTE,REF	; set ref high

	; reset the integrator
	; S1 = LOW,  S2 = HIGH	INTEGRATE
	; S1 = HIGH, S2 = HIGH	HOLD
	; S1 = HIGH, S2 = LOW	RESET
	; new reset - keep S1 low
	movlw	b'00000000'
	movlb	0		; select bank 0
rst2	movpf	WREG,PORTA	; RA2 = S1, RA3 = S2
	movlb	1

	; read the A/D and add it to the sum
	movfp	PORTC,WREG	; read low byte from A/D
	bsf	PORTE,ADBYTE	; select high byte
	addwf	SUMLOW,1	; SUMLOW = SUMLOW + WREG
	movpf	WREG,RAWLOW	; store the low byte
	movfp	PORTC,WREG	; read high byte from A/D
	addwfc	SUMHIGH,1	; SUMHIGH = SUMHIGH + WREG + Carry
	movpf	WREG,RAWHIGH	; store the high byte
	clrf	WREG,0		; clear WREG but Carry set from above
	btfsc	PORTC,7		; check if number negative (MSB high)
	setf	WREG,0		; if negative extend leading ones
	addwfc	SUMOVF1,1	; SUMOVF1 = SUMOVF1 + 0 + Carry
	clrf	WREG,0		; clear WREG but Carry set from above
	btfsc	PORTC,7		; check if number negative (MSB high)
	setf	WREG,0		; if negative extend leading ones
	bcf	PORTE,ADBYTE	; select low byte
	addwfc	SUMOVF2,1	; SUMOVF2 = SUMOVF2 + 0 + Carry

	; delay ~8us to give op-amp time to come to zero
	call	delay		; reset (slew rate of op-amp 3V/uS)

	call	delay3		; dead time for jitter removal

	; start the integrator
	; S1 = LOW,  S2 = HIGH	INTEGRATE
	; S1 = HIGH, S2 = HIGH	HOLD
	; S1 = HIGH, S2 = LOW	RESET
	movlw	b'00001000'
	movlb	0		; select bank 0
si2	movpf	WREG,PORTA	; RA2 = S1, RA3 = S2
	movlb	1

	clrwdt			; clear the watchdog timer

	call	shift	; check if need to shift output word for DAC

	; check if a byte has come in the serial port
	btfsc	PIR,RCIF	; if a byte is waiting to be read
	call	byterd		; read the byte

	; check if a character needs to be sent out the serial port
	call	sndout

	; update the DAC if needed (any shifting should be done by now)
	movlw	d'1'		; check if CYLCNT = 1 (last osc of ref)
	cpfseq	CYLCNT		; before start new summing cycle)
	goto	aaa		; skip to some nop's if CYLCNT <> 0

	; set sign bit for VOUTH before sending to DAC
	bcf	VOUTH,7		; sign change (A/D is 2's comp, D/A isn't)
	btfss	VOUTO2,7	; check if pos or neg
	bsf	VOUTH,7		; set or clear MSB as necessary

	; load and update the DAC
	movfp	VOUTL,PORTD	; put the low byte for DAC on port D
	movlb	0		; select bank 0
	bcf	PORTB,DACS	; have DAC read the low byte
	bsf	PORTB,DACS
	bcf	PORTB,DAHBE	; select high byte for DAC mux
	movlb	1		; select bank 1
	movfp	VOUTH,PORTD	; put the high byte for DAC on port D
	movlb	0		; select bank 0
	bcf	PORTB,DACS	; have DAC read the low byte
	bsf	PORTB,DACS
	bsf	PORTB,DALDAC	; transfer 16 bit latch to outputs of DAC
	bcf	PORTB,DALDAC
	bsf	PORTB,DAHBE	; select low byte for DAC mux
	movlb	1		; select bank 1

retdac	; return here if the above portion was skipped

	nop			; to balance high & low integration time
	call	delay2		; delay for rest of integration time

	; have the integrator hold the current value
	; S1 = LOW,  S2 = HIGH	INTEGRATE
	; S1 = HIGH, S2 = HIGH	HOLD
	; S1 = HIGH, S2 = LOW	RESET
	movlw	b'00001100'	; both S1 and S2 open
	movlb	0		; select bank 0
hld2	movpf	WREG,PORTA	; hold the current value
	movlb	1

	call	delay		; delay to let voltage stabilize
	call	delay4		; delay to let voltage stabilize

digs2	bcf	PORTE,ADSTRB	; strobe the A/D (trigger sample and hold
	nop
	bsf	PORTE,ADSTRB	; and begin conversion)

	call	delay3		; dead time for jitter removal

	; delay ~8us to give A/D time to convert the held voltage
	call	delay6

	goto top


;*****************************************************************


;	nop's to sync when not updating the DAC
aaa	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	goto	retdac


;******	move the sum into a temporary holding area until it can be sent
;	to the serial port and shifted and sent to the DAC
mvsum	; should be in bank 1 when accessing TSUM and SUM variables
	; move sum into temp storage for serial port sending
	movfp	SUMLOW,TSUML	; store the low byte in TSUML
	movfp	SUMHIGH,TSUMH	; store the high byte in TSUMH
	movfp	SUMOVF1,TSUMO1	; store first overflow byte in TSUMO1
	movfp	SUMOVF2,TSUMO2	; store second overflow byte in TSUMO2

	; move sum into regs to shift before sending to DAC 
	movpf	TSUML,VOUTL	; store the low byte in VOUTL
	movpf	TSUMH,VOUTH	; store the high byte in VOUTH
	movpf	TSUMO1,VOUTO1	; store first overflow byte in VOUTO1
	movpf	TSUMO2,VOUTO2	; store second overflow byte in VOUTO2

	movfp	SHDES,WREG	; reset the shift counter
	movpf	WREG,SHCNT
	clrf	SUMLOW,1	; clear the sum registers
	clrf	SUMHIGH,1
	clrf	SUMOVF1,1
	clrf	SUMOVF2,1
	movpf	CYLDES,CYLCNT	; reset the cycle counter
	movpf	TXDES,TXCNT	; reset the transmit word counter
	goto	mvret


;****** send a byte out the serial port if needed and buffer empty
sndout
	clrf	WREG,0		; W = 0
	cpfsgt	TXCNT		; if TXCNT > 0 (still need to send)
	goto	del14

	btfss	PIR,TXIF	; check if the transmit reg is empty
	goto	del13		; if not empty don't send byte - goto del1

	decf	TXCNT,1		; dec number of bytes needing to be sent
	movfp	TXCNT,WREG	; W = counter offset
	sublw	CRET		; W = addr of CRET - counter offset
	movpf	WREG,FSR0	; put W in the pointer register
	movfp	INDF0,WREG	; W = value in reg pointed to by FSR
	movlb	0		; select bank 0
	movpf	WREG,TXREG	; load the transmit register
	movlb	1		; select bank 1
	return

del14	nop			; delay so time through sndout is the
	nop			; same weather or not a byte needs to 
del13	nop			; be sent or weather or not the buffer
	nop			; is full
	nop
	nop
	nop
	nop
	nop
	return


;****************************************************************
; skip does some things and takes about 8us to complete
delay
	clrwdt			; clear the watchdog timer

	call	shift	; check if need to shift output word for DAC

	; check if a byte has come in the serial port
	btfsc	PIR,RCIF	; if a byte is waiting to be read
	call	byterd		; read the byte

	; check if a character needs to be sent out the serial port
	call	sndout

delay4	call	rawchk		; check for A/D out of range
	nop
	nop

;delay4	movlw	d'6'		; delay loop of nop's
;	movpf	WREG,TEMP
;skplp1	decfsz	TEMP,1
;	goto	skplp1

	return


;****************************************************************
; skip does some things and takes about 8us to complete
delay5
	clrwdt			; clear the watchdog timer

	; check if a character needs to be sent out the serial port
	call	sndout

	; check if a byte has come in the serial port
	btfsc	PIR,RCIF	; if a byte is waiting to be read
	call	byterd		; read the byte

	nop
	nop
	nop
	nop
	nop
	nop

refh	bsf	PORTE,REF	; set ref high

	call	shift	; check if need to shift output word for DAC

	nop
	nop
	movlw	d'4'		; delay loop of nop's
	movpf	WREG,TEMP
skplp2	decfsz	TEMP,1
	goto	skplp2

	return


;****************************************************************
; skip does some things and takes about 8us to complete
delay6
	clrwdt			; clear the watchdog timer

	; check if a character needs to be sent out the serial port
	call	sndout

	; check if a byte has come in the serial port
	btfsc	PIR,RCIF	; if a byte is waiting to be read
	call	byterd		; read the byte

	nop
	nop
	nop
	nop
	nop
	nop

refl	bcf	PORTE,REF	; set ref low

	call	shift	; check if need to shift output word for DAC

	nop
	nop
	movlw	d'4'		; delay loop of nop's
	movpf	WREG,TEMP
skplp3	decfsz	TEMP,1
	goto	skplp3

	return


;****************************************************************
; delay while doing integration
; delay from call to return = (5 + 38 * CNTDES) instructions 
delay2
	movfp	CNTDES,WREG	; get the desired count	
	movpf	WREG,CNT	; and put it in CNT
topdel
	clrwdt			; clear the watchdog timer

	call	shift	; check if need to shift output word for DAC

	call	sndout		; send char out port if needed

	; check if a byte has come in the serial port
	btfsc	PIR,RCIF	; if a byte is waiting to be read
	call	byterd		; read the byte

	decfsz	CNT,1		; check if still need to stay in loop
	goto	topdel		; if yes stay in loop else
	return			; return


;****************************************************************
; delay before and after reference edge transition
; delay from call to return (5 + 4 * CNTDES) * 121ns @ 33.333MHz
delay3
	movfp	SKPDES,WREG	; get the desired count
	movpf	WREG,SKP	; and put it in the counter
td3
	clrwdt			; clear the watchdog timer
	decfsz	SKP,1		; check if still need to stay in loop
	goto	td3		; if yes stay in loop else
	return			; return


;****************************************************************
; check if the A/D is maxed out at +/- 10V (0111.../1000...)
rawchk
	movfp	RAWHIGH,WREG	; w = rawhigh
	btfsc	WREG,7		; check if MSB is high or low
	goto	skpraw		; if high skip down

	bsf	WREG,7		; if MSB was low - set it high
	andwf	RAWLOW,0	; rawlow = 11111111
	comf	WREG,0		; set zero bit if all ones (OVFL)
	btfss	ALUSTA,Z	; check if zero bit is set
	goto	ledoff		; if not set line high (LED off)

	; if zero bit set then overflow
ledon	movlb	0		; select bank 0
	movpf	PORTA,TEMP	; read port a
	bcf	TEMP,OVLED	; turn on overflow LED
	movfp	TEMP,PORTA	; write result to port
	movlb	1		; select bank 1
	return

skpraw	bcf	WREG,7		; if MSB was high - set it low
	iorwf	RAWLOW,0	; rawlow = 00000000 (set Z if zero)
	btfsc	ALUSTA,Z	; check if zero bit is set
	goto	ledon		; if yes then set line low (LED on)

ledoff	movlb	0		; select bank 0
	movpf	PORTA,TEMP	; read port a
	bsf	TEMP,OVLED	; turn out overflow LED
	movfp	TEMP,PORTA	; write result to port
	movlb	1		; select bank 1
	return


;****************************************************************
;****** set up the usart for async opperation (2400 - 57.6Kbaud)
; spbrg = 216,107,53,26,8 -> baud rate = 2400,4800,9600,19.2K,57.6K
; RA5 = RX, RA4 = TX  (Values are correct for 33.333MHz clock)
usart
	movlw	d'8'
	movpf	WREG,SPBRG	; default spbrg = 8 -> 57.6K baud
	movlw	b'10100000'
	movpf	WREG,TXSTA	; master clk, 8 bit, async mode, enable port
	movlw	b'10010000'
	movpf	WREG,RCSTA	; 8 bit, async mode, continuous receive
	return


;******	reads a two byte command from the serial port
byterd
	btfss	PIR,RCIF	; check if byte waiting to be read
	goto	byterd		; stay in loop until byte comes in
	movlb	0		; select bank 0
	bcf	TXSTA,TXEN	; disable any previous serial transmission
	movfp	RCREG,WREG	; read first byte
	movlb	1		; select bank 1
	movpf	WREG,CMD	; store the command byte
	clrwdt			; clear the watchdog timer
bylp	btfss	PIR,RCIF	; wait here until next byte is received
	goto	bylp
	movlb	0		; select bank 0
	movfp	RCREG,WREG	; read second byte
	movlb	1		; select bank 1
	movpf	WREG,DATA1	; store the first data byte
	clrwdt			; clear the watchdog timer

	; decode the command byte and jump to proper subroutine
	btfsc	CMD,7		; check if sync cycle number command
	call	srsync		; set or read the # of sync cycles
	btfsc	CMD,6		; check if baud rate command
	call	srbaud		; set or read the baud rate
	btfsc	CMD,5		; check if bit shift command
	call	srshft		; set or read # shift before loading DAC
	btfsc	CMD,4		; check if gain command
	call	srgain		; set or read the gain value
	btfsc	CMD,3		; check if # of samp per 1/2 cycle command
	call	srsamp		; set or read the # of samples
	btfsc	CMD,2		; check if # of samp to skip on each side of ref
	call	srskp		; set or read the # of samples

;	tstfsz	CMD		; check if restart data collection command
;	goto	byterd		; if not stay in loop (wait for next command)
	
	movlb	0		; select bank 0
	bsf	TXSTA,TXEN	; re-enable transmit side of serial port
	movlb	1		; select bank 1
	return


;******	sets or reads the number of sync cycles to sum before sending data
srsync
	btfss	CMD,0		; check if read or write command
	goto	syrd		; if read command skip down

	; do this part if write command
	clrf	WREG,0
	cpfsgt	DATA1		; if new cycle count is zero
	return			; then return without changing value
	movfp	DATA1,CYLDES	; CYLDES = new cycle count value
	return

syrd	; do this part if read command
	movfp	CYLDES,WREG	; put desired cycle count in WREG
	movlb	0		; select bank 0
	movpf	WREG,TXREG	; put in transmit reg
	bsf	TXSTA,TXEN	; re-enable the serial port (send byte)
	movlb	1		; select bank 1
	return


;******	sets or reads the baud rate
srbaud	
	btfss	CMD,0		; check if read or write command
	goto	bdrd		; if read command skip down

	; do this part if write command
	; check if legal value for baud rate
	movlw	d'5'		; 5 legal values in the lookup table
	movpf	WREG,TEMP	; temp will be the counter variable
up2	movfp	TEMP,WREG	; put count in WREG
	call	bdlkup		; get next legal value for SPBRG
	cpfseq	DATA1		; skip if DATA1 has legal value
	goto	dwn2
	goto	dwn3
dwn2	decfsz	TEMP,1		; check if done all values in table yet
	goto	up2		; if not stay in loop
	return			; if no value matched exit without change

dwn3	; if legal value then change the baud rate
	movfp	DATA1,WREG	; put new cycle count value in WREG
	movlb	0		; select bank 0
	movpf	WREG,SPBRG	; put WREG in baud rate register
	movlb	1		; select bank 1
	return

bdrd	; do this part if read command
	movlb	0		; select bank 0
	movpf	SPBRG,TXREG	; TXREG = desired baud rate
	bsf	TXSTA,TXEN	; re-enable the serial port (send byte)
	movlb	1		; select bank 1
	return


;******	lookup table for leagal baud rate settings (33.333MHz clock)
bdlkup	; returns with value for SPBRG (called with offset in WREG)
	addwf	PCL,1		; jump to location in lookup table
	retlw	d'0'		; dummy line (PCL >= 1 & PCL <= 5)
	retlw	d'216'		; 2400
	retlw	d'107'		; 4800
	retlw	d'53'		; 9600
	retlw	d'26'		; 19.2K
	retlw	d'8'		; 57.6K


;******	sets or reads the number of bits to shift the sum before sending
srshft	; it to the DAC (legal values 0-15 bits)
	btfss	CMD,0		; check if read or write command
	goto	sysf		; if read command skip down

	; do this part if write command
	movlw	d'16'		; check that the value is less than 16
	cpfslt	DATA1
	return			; if the number is > 16 don't change
	movfp	DATA1,WREG	; w = new shift value
	movpf	WREG,SHDES	; SHDES has new shift value
	return

sysf	; do this part if read command
	movfp	SHDES,WREG	; put desired shift value in WREG
	movlb	0		; select bank 0
	movpf	WREG,TXREG	; put in transmit reg
	bsf	TXSTA,TXEN	; re-enable the serial port (send byte)
	movlb	1		; select bank 1
	return


;******	sets or reads the gain setting for the analog input 
srgain	;legal values are 0-7 (1,3,10,30,100,300,1000,3000)
	btfss	CMD,0		; check if read or write command
	goto	sygn		; if read command skip down

	; do this part if write command
	movlw	d'8'		; check that the value is less than 8
	cpfslt	DATA1
	return			; if the number is > 8 don't change
	movfp	DATA1,WREG	; w = new gain value
	movpf	WREG,GAIN	; GAIN has new value
	call	gainst		; set the I/O pins for the new gain
	return

sygn	; do this part if read command
	movfp	GAIN,WREG	; put current gain value in WREG
	movlb	0		; select bank 0
	movpf	WREG,TXREG	; put in transmit reg
	bsf	TXSTA,TXEN	; re-enable the serial port (send byte)
	movlb	1		; select bank 1
	return


;******	sets the I/O pins for the current gain setting
gainst	movfp	GAIN,WREG	; put gain setting in temp variable
	movpf	WREG,TEMP
	swapf	TEMP,1		; change high and low order nibbles
	rlcf	TEMP,1		; shift gain bits to high order bits

	; needed because port B is in bank 0 so copy temp variable
	; into bank zero
	movfp	TEMP, WREG	; move temp in bank 1 into Wreg
	movlb	0		; select bank 0
	movpf	WREG, TEMP	; put Wreg into temp in bank 0

	; set or clear LSB
	btfsc	TEMP,GAIN0	; check if new bit of GAIN is low
	bsf	PORTB,GAIN0	; if not set pin high
	btfss	TEMP,GAIN0	; check if new bit of GAIN is high
	bcf	PORTB,GAIN0	; if not clear pin
	; set or clear middle bit
	btfsc	TEMP,GAIN1	; check if new bit of GAIN is low
	bsf	PORTB,GAIN1	; if not set pin high
	btfss	TEMP,GAIN1	; check if new bit of GAIN is high
	bcf	PORTB,GAIN1	; if not clear pin
	; set or clear MSB
	btfsc	TEMP,GAIN2	; check if new bit of GAIN is low
	bsf	PORTB,GAIN2	; if not set pin high
	btfss	TEMP,GAIN2	; check if new bit of GAIN is high
	bcf	PORTB,GAIN2	; if not clear pin

	movlb	1		; select bank 1
	return


;******	sets or reads the integration loop counter (for 1/2 cycle)
srsamp
	btfss	CMD,0		; check if read or write command
	goto	sysp		; if read command skip down

	; do this part if write command
	clrf	WREG,0
	cpfsgt	DATA1		; if new sample count is zero
	return			; then return without changing value
	movfp	DATA1,WREG	; CNTDES = new sample count value
	movpf	WREG,CNTDES
	return

sysp	; do this part if read command
	movfp	CNTDES,WREG	; put desired sample count in WREG
	movlb	0		; select bank 0
	movpf	WREG,TXREG	; put in transmit reg
	bsf	TXSTA,TXEN	; re-enable the serial port (send byte)
	movlb	1		; select bank 1
	return


;******	sets or reads the delay loop counter (per half cycle)
srskp
	btfss	CMD,0		; check if read or write command
	goto	sksp		; if read command skip down

	; do this part if write command
	clrf	WREG,0
	cpfsgt	DATA1		; if new sample count is zero
	return			; then return without changing value
	movfp	DATA1,WREG	; SKPDES = new sample count value
	movpf	WREG,SKPDES
	return

sksp	; do this part if read command
	movfp	SKPDES,WREG	; put desired skip count in WREG
	movlb	0		; select bank 0
	movpf	WREG,TXREG	; put in transmit reg
	bsf	TXSTA,TXEN	; re-enable the serial port (send byte)
	movlb	1		; select bank 1
	return


;******	shifts VOUT one bit right (keeps MSB the same for sign)
shift
	clrf	WREG,0
	cpfsgt	SHCNT	; check if bits still need to be shifted
	goto	skp22	; if not skip down

	bcf	ALUSTA,C	; clear carry bit before rotate
	btfsc	VOUTO2,7	; check if MSB is set or clear
	bsf	ALUSTA,C	; if set then set carry before rotate
	rrcf	VOUTO2,1	; rotate all four bytes
	rrcf	VOUTO1,1
	rrcf	VOUTH,1
	rrcf	VOUTL,1
	decf	SHCNT,1		; decrement the shift counter
	return

skp22	nop
	nop
	nop
	nop
	nop
	nop
	nop
	return


;****** INT PIN INTERRUPT SERVICE HANDLER
intpin_isr_handler

;isr code goes here


                POP               ;restore registers
                retfie            ;return from interrupt


;****** TIMER0 INTERRUPT SERVICE HANDLER
timer0_isr_handler

;isr code goes here

                
                POP               ;restore registers
                retfie            ;return from interrupt


;****** T0CKI INTERRUPT SERVICE HANDLER
t0cki_isr_handler

;isr code goes here


                POP               ;restore registers
                retfie            ;return from interrupt

                END	          ;required directive

