;**********************************************************************
;   This file is a basic code template for assembly code generation   *
;   on the PICmicro PIC16F877. This file contains the basic code      *
;   building blocks to build upon.                                    *  
;                                                                     *
;   If interrupts are not used all code presented between the ORG     *
;   0x004 directive and the label main can be removed. In addition    *
;   the variable assignments for 'w_temp' and 'status_temp' can       *
;   be removed.                                                       *                         
;                                                                     *
;   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 MPLAB V4.00 and MPASM V2.20.00.      *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Filename:		step_u32.asm                                  *
;    Date:              10/16/2002                                    *
;    File Version:      3 (with serial port/interupt stuff)           *
;                                                                     *
;    Author:            Bill O'Donnell                                *
;    Company:           UNLV Physics Dept.                            *
;                                                                     * 
;                                                                     *
;**********************************************************************
;                                                                     *
;    Files required: p16f877.inc                                      *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Notes:  This program microsteps (32 u_steps/step) stepper motors *
;            for a coil winding apparatus for Cornelius's lab         *
;                                                                     *
;	Have the watchdog timer on, low voltage programming off,      *
;	oscillator set for XT (4MHz), power up timer enabled,         *
;       code protect off, low voltage programming off, and flash      *
;	programming disabled.                                         *
;                                                                     *
;**********************************************************************


	list      p=16f877            ; list directive to define processor
	#include <p16f877.inc>        ; processor specific variable definitions
	
;	__CONFIG _CP_OFF & _WDT_ON & _BODEN_ON & _PWRTE_ON & _RC_OSC & _WRT_ENABLE_ON & _LVP_ON & _CPD_OFF

; '__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.


;***** VARIABLE DEFINITIONS
w_temp		EQU	0x20	; variable used for context saving 
status_temp	EQU	0x21	; variable used for context saving
fsr_temp	EQU	0x34	; variable used for context saving

COIL_A		EQU	0x22	; offset into microstepping lookup table
COIL_B		EQU	0x23	; for the four coils (unipolar stepper)
COIL_C		EQU	0x24
COIL_D		EQU	0x25
DIR		EQU	0x28	; direction for count LSB (0 = CW, 1 = CCW)
				; bit 1 dir of sideways motor (same or op of bit 0)
TEMP1		EQU	0x29	; temp variable
TEMP2		EQU	0x2A	; temp variable
TEMP3		EQU	0x2B	; temp variable
ADADDR		EQU	0x2C	; address for which A/D converter (0-7)
CNT		EQU	0x2D	; counter variable
BITCNT		EQU	0x2E	; bit counter
RATIO		EQU	0x2F	; turn ratio for sidways motion (Motor #3)
RATCNT		EQU	0x30	; counter to keep track of ratio counts
HUND		EQU	0x31	; hundreds digit counter for BCD conversion
TEN		EQU	0x32	; tens digit counter for BCD conversion
ONE		EQU	0x33	; ones digit counter for BCD conversion
;			0x34      used above
BYTCNT		EQU	0x35	; byte counter for serial input
REM_DIR		EQU	0x36	; same as DIR (above) but for remote input
REM_SPD		EQU	0x37	; used for remote speed control
REM_RAT		EQU	0x38	; remote turn ratio for sidways motion
REM_SS		EQU	0x39	; remote start stop (bit 0 = 0 run,1 stop)

; serial command info stored from 0x3D - 0x3F
; step info for the four motors are stored from 0x40 - 0x5D
; whole step# (0-3) for motor one is at 0x40
; u_step# (0-31) for motor one is at 0x41
; whole step counter (0-200) for motor one is at 0x42
; LSB BCD (0-9) for motor one is at 0x43
; next BCD (0-9) for motor one is at 0x44
; next BCD (0-9) for motor one is at 0x45
; MSB BCD (0-9) for motor one is at 0x46
; motor two is from 0x47 - 0x4D
; motor three is from 0x4E - 0x54
; motor four is from 0x55 - 0x5B


;***** PIN MASK DEFINITIONS
CSLD1		EQU	d'0'	; CS/LD1 load line for A/D (RD0)
CSLD2		EQU	d'1'	; CS/LD1 load line for A/D (RD1)
DOUT		EQU	d'2'	; Data out (Din on A/D) (RD2)
SCLK		EQU	d'3'	; serial clock line for A/D (RD3)
ADRST		EQU	d'4'	; reset line on A/D (RD4) (open drain - 10K pullup)
INSTP		EQU	d'1'	; step input (RE1) (0=step motor, 1=nothing)
INDIR		EQU	d'2'	; direction input (RE2) (0 = CCW, 1 = CW)
RS		EQU	d'5'	; LCD reg select (0=instruc, 1=data) (RD7)
ENBL		EQU	d'6'	; enable for LCD (RD5)
RDWT		EQU	d'7'	; read = 1, write = 0 (RD6)
UPLIM		EQU	d'1'	; upper limit photo gate (RA1) active low
LOLIM		EQU	d'2'	; lower limit photo gate (RA2) active low
RMT		EQU	d'3'	; remote (serial) (RA3) 1=yes, 0=no
DISABL		EQU	d'0'	; when low disable MTR#4 sideways (RC0)
RATUP		EQU	d'1'	; inc ratio for sidways (RC1) active low
RATDN		EQU	d'2'	; dec ratio for sidways (RC2) active low
SDIR		EQU	d'3'	; sideways direction input (RC3) (0 = same as DIR,0)

;***** ADDRESS DEFINITIONS
M1AD		EQU	d'1'	; address for motor 1 (first A/D low addr)
M2AD		EQU	d'5'	; address for motor 2 (first A/D high addr)
M3AD		EQU	d'17'	; address for motor 3 (second A/D low addr)
M4AD		EQU	d'21'	; address for motor 4 (second A/D high addr)
SERAD		EQU	0x3D	; address for serial byte storage
M1FSR		EQU	0x40	; address for motor 1 lookup table
M2FSR		EQU	0x47	; address for motor 2 lookup table
M3FSR		EQU	0x4E	; address for motor 3 lookup table
M4FSR		EQU	0x56	; address for motor 4 lookup table


;**********************************************************************
		ORG     0x000             ; processor reset vector
		clrf    PCLATH            ; ensure page bits are cleared
  		goto    main              ; go to beginning of program


		ORG     0x004             ; interrupt vector location
		movwf   w_temp            ; save off current W register contents
		movf	STATUS,w          ; move status register into W register
		bcf     STATUS,RP0        ; ensure file register bank set to 0
		movwf	status_temp       ; save off contents of STATUS register

		; isr code can go here or be located as a call subroutine elsewhere
		;--------------------------------------------------------
		movf	FSR,w		; move FSR register into W register
		movwf	fsr_temp	; save off contents of FSR register

		call	serprt		; read serial port and decode command

		movf	fsr_temp,w	; retrieve copy of FSR register
		movwf	FSR		; restore pre-isr FSR register contents
		;--------------------------------------------------------

		bcf     STATUS,RP0        ; ensure file register bank set to 0
		movf    status_temp,w     ; retrieve copy of STATUS register
		movwf	STATUS            ; restore pre-isr STATUS register contents
		swapf   w_temp,f
		swapf   w_temp,w          ; restore pre-isr W register contents

		; re-enable serial port interupt
;		bsf	RCSTA,CREN	; enable usart continuous receive
		bsf	STATUS,RP0	; bank 1
		movlw	b'00100000'	; re-enable usart receive interupt
		movwf	PIE1
		bcf	STATUS,RP0	; bank 0

		retfie                    ; return from interrupt

;**********************************************************************

; returns the value to send to the D/A converter to micro-step the motor

; the following basic code produced the values for the lookup table
; FOR i = 0 TO 32
;   x! = SIN(i * (3.14159 / 2!) / 32!)
;   y! = x! * 255
;   IF (y! > (INT(y!) + .499)) THEN
;     z% = INT(y!) + 1
;   ELSE
;     z% = INT(y!)
;   END IF
;
; '  PRINT y!, z%
;   PRINT z%
;
; NEXT i
			; call with the offset into the table in W
ustep	addwf	PCL,1	; add value in W to the program counter low and jump
 	retlw	d'0'	; to the proper location in the table (0-32)
 	retlw	d'13'	; return the value in W
	retlw	d'25'
 	retlw	d'37'
	retlw	d'50'
 	retlw	d'62'
	retlw	d'74'
 	retlw	d'86'
	retlw	d'98'
 	retlw	d'109'
	retlw	d'120'
 	retlw	d'131'
	retlw	d'142'
 	retlw	d'152'
	retlw	d'162'
 	retlw	d'171'
	retlw	d'180'
 	retlw	d'189'
	retlw	d'197'
 	retlw	d'205'
	retlw	d'212'
 	retlw	d'219'
	retlw	d'225'
 	retlw	d'231'
	retlw	d'236'
 	retlw	d'240'
	retlw	d'244'
 	retlw	d'247'
	retlw	d'250'
 	retlw	d'252'
	retlw	d'254'
 	retlw	d'255'
	retlw	d'255'

;**********************************************************************

; set I/O directions and default values
portio	
	; port A (all inputs, RA6,7 not implimented)
	bsf	STATUS,RP0		; bank 1
	movlw	b'00111111'
	movwf	TRISA			; Port A all inputs
	bcf	STATUS,RP0		; bank 0
	movlw	b'00000000'
	movwf	PORTA			; Port A all outputs low

	; port B (RB7-RB0 outputs for LCD data)
	bsf	STATUS,RP0		; bank 1
	movlw	b'00000000'
	movwf	TRISB			; Port B all outputs
	bsf	OPTION_REG,NOT_RBPU	; disable port B week pull-ups
	bcf	STATUS,RP0		; bank 0
	movlw	b'00000000'
	movwf	PORTB			; Port B all outputs low

	; port C (all inputs)
	bsf	STATUS,RP0		; bank 1
	movlw	b'10111111'
	movwf	TRISC			; RC6 RS232 out, rest inputs
	bcf	STATUS,RP0		; bank 0
	movlw	b'01000000'
	movwf	PORTC			; all outputs low except stop bit

	; port D (all outputs) RD5, RD6, RD7 outputs for LCD control
	bsf	STATUS,RP0		; bank 1
	movlw	b'00000000'
	movwf	TRISD			; Port D all outputs
	bcf	STATUS,RP0		; bank 0
	movlw	b'00000000'
	movwf	PORTD			; Port D all outputs low

	; default values for port D
	bsf	PORTD,CSLD1		; disable A/D #1
	bsf	PORTD,CSLD2		; disable A/D #2
	bcf	PORTD,SCLK		; clear A/D clock line
	bcf	PORTD,DOUT		; clear data out line
	bsf	PORTD,ADRST		; don't reset the A/D

	; port E (RE2-RE0 inputs, PSP port disabled)
	bsf	STATUS,RP0		; bank 1
	movlw	b'00000111'		; set for general purpose I/O
	movwf	TRISE			; all inputs
	bcf	STATUS,RP0		; bank 0
	movlw	b'00000000'
	movwf	PORTE			; all outputs low

	return

;*************************************************************************

; A/D converter set up
adset
	movlw	b'10000001'		; clock/32, channel 0 selected
	movwf	ADCON0			; A/D converter on
	bsf	STATUS,RP0		; bank 1
	movlw	b'00001110'		; left justified output
	movwf	ADCON1			; RA0 analog, rest digital
	bcf	STATUS,RP0		; bank 0
	return

;*************************************************************************

; interrupt set up
intset
	bcf	PIR1,RCIF		; clear USART rec. int. flag
;	bcf	INTCON,GIE		; disable all interrupts
;	bcf	INTCON,PEIE		; disable all peripheral interrupts
	movlw	b'11000000'		; enable only peripheral interupts
	movwf	INTCON
	bsf	STATUS,RP0		; bank 1
	movlw	b'00000000'		; disable more individual interupts
	movwf	PIE2
	movlw	b'00100000'		; enable usart receive interupt only
	movwf	PIE1
	bcf	STATUS,RP0		; bank 0
	return

;**********************************************************************

; timer and watchdog set up
tmrset
	; use prescaler with watchdog timer 0 (set for divide by 128)
	bsf	STATUS,RP0		; bank 1
	bcf	OPTION_REG,T0CS		; TMR0 uses FOSC/4
	bcf	OPTION_REG,T0SE		; TMR0 increments on rising edge
	bsf	OPTION_REG,PSA		; use prescaler with watch dog timer
	bsf	OPTION_REG,PS2		; set MSB of prescaler
	bsf	OPTION_REG,PS1		; set middle bit of prescaler
	bsf	OPTION_REG,PS0		; set LSB of prescaler
	bcf	STATUS,RP0		; bank 0

	; use prescaler with timer 1 (set for divide by 8 - timer1 ON)
	; timer1 is used for the delay between pulses to the stepper motor
	bsf	T1CON,T1CKPS1		; set MSB of prescaler
	bsf	T1CON,T1CKPS0		; set LSB of prescaler
	bcf	T1CON,T1OSCEN		; inverter off (save power)
	bsf	T1CON,T1SYNC		; don't sync w/external clock
	bcf	T1CON,TMR1CS		; use FOSC/4 for clock input
	bsf	T1CON,TMR1ON		; timer 1 ON

	; postscaler /16, prescaler /16, timer2  ON)
	; total delay should be 1uS * 16 * 16 * 20 = ~5ms W/4MHz clock
	bsf	T2CON,TOUTPS3		; set MSB of postscaler
	bsf	T2CON,TOUTPS2		; set bit 2 of postscaler
	bsf	T2CON,TOUTPS1		; set bit 1 of postscaler
	bsf	T2CON,TOUTPS0		; set LSB of postscaler
	bsf	T2CON,TMR2ON		; timer 2 ON
	bsf	T2CON,T2CKPS1		; set MSB of prescaler
	bsf	T2CON,T2CKPS0		; set LSB of prescaler
	bsf	STATUS,RP0		; bank 1
	movlw	d'20'			; roll-over every 20 clock cycles
	movwf	PR2			; load delay register
	bcf	STATUS,RP0		; bank 0
	clrf	BYTCNT			; zero byte counter
	return

;**********************************************************************

; serial port setup
; RA5 = RX, RA4 = TX  (with 4MHz clock use SPBRG = 12 for 19.2K baud)
; with BRGH = 1 (high speed) -> SPBRG = Fosc/(16 + (x + 1))
usart
	bsf	STATUS,RP0	; bank 1
	movlw	d'12'
	movwf	SPBRG		; default spbrg = 12 -> 19.2K baud
	movlw	b'00100100'	; BRGH = 1 so baud = clk/(16*(x+1))
	movwf	TXSTA		; master clk, 8 bit, async mode, enable port
	bcf	STATUS,RP0	; bank 0
	movlw	b'10010000'
	movwf	RCSTA		; 8 bit, async mode, continuous receive
	return

;**********************************************************************

; put stepper motor pointers in default (power up) position

m_def	; pulse the A/D reset lines low to reset A/D (all outputs 0)
	bcf	PORTD,ADRST	; reset A/D converters
	nop			; small delay for safety
	nop			; small delay for safety
	bsf	PORTD,ADRST	; don't reset the A/D

	; clear everything (all motors to step 0, u_step 0, rotation count 0)
	movlw	0x40		; address of motor lookup table
	movwf	FSR		; point to lookup table
	movlw	d'28'		; four motors, 7 variables each
	movwf	CNT		; count through all 28
up11	clrf	INDF		; clear each one
	incf	FSR,1		; point to next location
	decfsz	CNT,1		; check if all 8 locations cleared
	goto	up11		; if not, stay in loop

	; initialize all motors with above data
	movlw	M1AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M1FSR		; addr for whole step# for motor 1
	movwf	FSR
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)

	movlw	M2AD		; A/D address & CS/LD info for motor 2
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M2FSR		; addr for whole step# for motor 2
	movwf	FSR
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)

	movlw	M3AD		; A/D address & CS/LD info for motor 3
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M3FSR		; addr for whole step# for motor 3
	movwf	FSR
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)

	movlw	M4AD		; A/D address & CS/LD info for motor 4
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M4FSR		; addr for whole step# for motor 4
	movwf	FSR
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)

	return

;**********************************************************************

; initialize the LCD display (1 line,16 char, 5X7 dot characters)
; call >15ms after LCD power up
lcdset
	; send this command four times with ~8ms between commands
	movlw	d'4'		; loop four times
	movwf	CNT		; put in counter

up33	movlw	b'00110000'	; function set command
	call	wrtins		; write instruction command to LCD
	movlw	d'252'		; delay ~(256-#)*2ms = 8ms
	call	delay		; delay between instructions

	clrwdt			; clear the watch dog timer
	decfsz	CNT,1		; check if done all four times
	goto	up33		; if not stay in loop

	movlw	b'00001000'	; function set command (display off)
	call	wrtins		; write instruction command to LCD
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions

	movlw	b'00000001'	; function set command (clear display)
	call	wrtins		; write instruction command to LCD
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions

	movlw	b'00000110'	; function set command (entry mode set)
	call	wrtins		; write instruction command to LCD
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions

	movlw	b'00001100'	; function set command (display on)
	call	wrtins		; write instruction command to LCD
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
	clrwdt			; clear the watch dog timer
	return

;**********************************************************************

; write instruction command to LCD
; command should be in w before calling
wrtins
	movwf	PORTB		; put command on LCD data lines
	bcf	PORTD,RS	; clear register select line (instruction)
	bcf	PORTD,RDWT	; clear read/write line (write)
	nop			; small delay for safety
	bsf	PORTD,ENBL	; set enable line
	nop			; small delay for safety
	bcf	PORTD,ENBL	; clear enable line (clock data to LCD)
	nop			; small delay for safety
	bsf	PORTD,RDWT	; set read/write line (write)
	return

;**********************************************************************

; write data to the LCD
; data should be in w before calling
wrtdat
	movwf	PORTB		; put data on LCD data lines
	bsf	PORTD,RS	; set register select line (data)
	bcf	PORTD,RDWT	; clear read/write line (write)
	nop			; small delay for safety
	bsf	PORTD,ENBL	; set enable line
	nop			; small delay for safety
	bcf	PORTD,ENBL	; clear enable line (clock data to LCD)
	nop			; small delay for safety
	bsf	PORTD,RDWT	; set read/write line (write)
	return

;**********************************************************************

;	call with FSR pointing to step# for current motor
ldcoil	movf	INDF,0		; w = current step value
	btfss	STATUS,Z	; if current step is not 0
	goto	step1		; then skip down
step0	clrf	COIL_D		; coil D off
	clrf	COIL_B		; coil B off
	incf	FSR,1		; point to current u_step value
	movf	INDF,0		; w = current u_step value
	movwf	COIL_A		; into coil A
	sublw	d'32'		; out of phase component
	movwf	COIL_C		; into coil C
	return
step1	decfsz	INDF,0		; check if current step is 1
	goto	step2		; if not skip down
	clrf	COIL_A		; coil A off
	clrf	COIL_D		; coil D off
	incf	FSR,1		; point to current u_step value
	movf	INDF,0		; w = current u_step value
	movwf	COIL_C		; into coil C
	sublw	d'32'		; out of phase component
	movwf	COIL_B		; into coil B
	return
step2	movf	INDF,0		; w = current step value
	sublw	d'2'		; check if step 2
	btfss	STATUS,Z	; if current step is not 2
	goto	step3		; then skip down
	clrf	COIL_A		; coil A off
	clrf	COIL_C		; coil C off
	incf	FSR,1		; point to current u_step value
	movf	INDF,0		; w = current u_step value
	movwf	COIL_B		; into coil B
	sublw	d'32'		; out of phase component
	movwf	COIL_D		; into coil D
	return
step3	clrf	COIL_B		; coil B off
	clrf	COIL_C		; coil C off
	incf	FSR,1		; point to current u_step value
	movf	INDF,0		; w = current u_step value
	movwf	COIL_D		; into coil D
	sublw	d'32'		; out of phase component
	movwf	COIL_A		; into coil A
	return

;**********************************************************************

; step a motor counter clockwise one u_step
; ADADDR should be set to 1, 5, 17, or 21 (motor 1, 2, 3, or 4)
; FSR <- whole step # for motor (i.e. 0x40 for mtr 1, 0x47 for mtr 2, etc)

stepccw	incf	FSR,1		; point to u_step offset
	decf	INDF,1		; dec u_step offset into table
	btfss	INDF,7		; if no underflow (i.e. > 0)
	goto	dwn11		; jump down
	movlw	d'31'		; if underflow, point to end-1
	movwf	INDF
	decf	FSR,1		; point to whole step #
	incf	INDF,1		; inc whole step # (take care of u overflow)
	btfsc	INDF,2		; if count >= 3
	clrf	INDF		; zero whole step count (whole overflow) 
	call	inc_cnt		; increment the whole step counter
	return

dwn11	decf	FSR,1		; point back to whole step #
	return

;**********************************************************************

; step a motor clockwise one u_step
; ADADDR should be set to 1, 5, 17, or 21 (motor 1, 2, 3, or 4)
; FSR <- whole step # for motor (i.e. 0x40 for mtr 1, 0x42 for mtr 2, etc)

stepcw	incf	FSR,1		; point to u_step offset
	incf	INDF,1		; inc u_step offset into table
	movlw	d'32'		; if offset
	subwf	INDF,0		; = 32 then overflow
	btfss	STATUS,Z	; if no overflow (i.e. < 33)
	goto	dwn12		; jump down

	clrf	INDF		; if overflow point to first u_step
	decf	FSR,1		; point to whole step #
	decf	INDF,1		; dec whole step # (take care of u overflow)
	movlw	d'3'
	btfsc	INDF,7		; if count < 0 (MSB will be set)
	movwf	INDF		; then coun = 3 
	call	dec_cnt		; decrement the whole step counter
	return

dwn12	decf	FSR,1		; point back to whole step #
	return

;**********************************************************************

; update the digital to analog converter driving the stepper motors
; ADADDR should be set to 1, 5, 17, or 21 (motor 1, 2, 3, or 4) before
; calling

updtdac
	movlw	0x22		; address of coil A pointer
	movwf	FSR		; put in pointer register
	movlw	d'4'		; loop for four coils on A/D
	movwf	CNT		; put in counter variable
	movf	ADADDR,0	; w = A/D address
	movwf	TEMP3		; store address and put back at end

up1	movf	INDF,0		; current coil u_step offset to W
	call	ustep		; W = A/D value (0-255)
	call	fmtdat		; format 16 bit data (0,addr,byte,000)
	call	sndad		; send formatted data to A/D
	incf	FSR,1		; inc pointer to next coil
	incf	ADADDR,1	; inc temp A/D address counter
	decfsz	CNT,1		; dec the counter
	goto	up1		; if not zero stay in loop

	movf	TEMP3,0		; w = A/D address
	movwf	ADADDR		; restore original A/D address
	return

;**********************************************************************

; formats data for A/D.  Call with value for A/D in W and address of
; A/D in ADADDR.  16 bit result in TEMP1 & TEMP2
; Note: using most significant 8 bits on a 10 bit converter
fmtdat	movwf	TEMP2		; store data in temp2
	swapf	ADADDR,0	; w = ADDR 0000 or ADDR 0001
	movwf	TEMP1		; temp1 = w
	bcf	TEMP1,0		; TEMP1 = ADDR 0000 (ignore CS/LD line info)
	swapf	TEMP2,0		; w = LSB MSB of data
	andlw	0x0F		; w = 0 MSB
	iorwf	TEMP1,1		; temp1 = ADDR MSB
	swapf	TEMP2,0		; w = LSB MSB of data
	andlw	0xF0		; w = LSB 0000
	movwf	TEMP2		; temp2 = LSB 0000
	return

;**********************************************************************

; sends 16 bits of formated data stored in temp1 & temp2 to A/D converter
sndad	movlw	d'8'		; bit count for # bits to shift
	movwf	BITCNT		; store in bitcounter
	bcf	PORTD,SCLK	; serial clock low

up3	; bit 4 of ADADDR = 0 for first A/D converter, 1 for second
	btfss	ADADDR,4	; check which A/D to send data to
	bcf	PORTD,CSLD1	; enable A/D #1 (M1 & M2)
	btfsc	ADADDR,4	; check which A/D to send data to
	bcf	PORTD,CSLD2	; enable A/D #2 (M3 & M4)

	nop			; delay for safety
	bcf	PORTD,DOUT	; clear output bit
	btfsc	TEMP1,7		; if MSB is 1 then
	bsf	PORTD,DOUT	; set output bit
	nop			; delay for safety
	bsf	PORTD,SCLK	; serial clock high
	rlf	TEMP1,1		; rotate toward the MSB
	bcf	PORTD,SCLK	; serial clock low

	decfsz	BITCNT,1	; check if all 8 bits sent
	goto	up3		; if not stay in loop

	movlw	d'8'		; bit count for # bits to shift
	movwf	BITCNT		; store in bitcounter

up4	bcf	PORTD,DOUT	; clear output bit
	btfsc	TEMP2,7		; if MSB is 1 then
	bsf	PORTD,DOUT	; set output bit
	nop			; delay for safety
	bsf	PORTD,SCLK	; serial clock high
	rlf	TEMP2,1		; rotate toward the MSB
	bcf	PORTD,SCLK	; serial clock low

	decfsz	BITCNT,1	; check if all 8 bits sent
	goto	up4		; if not stay in loop

	bsf	PORTD,CSLD1	; disable A/D #1 (load A/D if was low)
	bsf	PORTD,CSLD2	; disable A/D #2 (load A/D if was low)

	return

;**********************************************************************

; 1/2 second max delay with a 4MHz clock
; call with w = value for TMR1 (big # = small delay)
delay	clrf	TMR1L		; reset timer 1 low byte
	bcf	PIR1,TMR1IF	; reset timer 1 overflow bit
	movwf	TMR1H		; load timer 1 high byte from w
;	clrf	TMR1H
up5	clrwdt
	btfss	PIR1,TMR1IF	; wait until bit high before leaving loop
	goto	up5
	return

;**********************************************************************

; small dealy (about w * 124us + 4us) (4MHz clock so 1us per instruction)
smdel	movwf	CNT		; use CNT as the counter variable
up6	movlw	d'30'		; do inner loop 30 times
	movwf	TEMP3		; use temp3 as inner loop counter
up7	clrwdt			; clear watch dog timer
	decfsz	TEMP3,1		; check if done 30 times
	goto	up7		; if not stay in inner loop
	decfsz	CNT,1		; check if done w times
	goto	up6		; if not stay in outer loop
	return			; else return


;**********************************************************************

; incs full step counter (ADADDR * FSR should be set before calling)
; FSR <- whole step # for motor (i.e. 0x40 for mtr 1, 0x47 for mtr 2, etc)
inc_cnt
	movf	FSR,0		; w = FSR
	movwf	TEMP3		; store FSR (put back at end)
	movlw	d'2'		; from 0-3 step counter to 0-200 step counter
	addwf	FSR,1		; point to whole step counter

	; inc step counter
	incf	INDF,1		; inc whole step counter
	movlw	d'200'		; if 200 then overflow (200 steps/rev)
	subwf	INDF,0		; if result is zero then = 200
	btfss	STATUS,Z	; if no overflow
	goto	endinc		; jump down

	; inc LSB of BCD rotation counter if needed
	clrf	INDF		; if overflow clear step counter
	incf	FSR,1		; point to LSB BCD rotation counter
	incf	INDF,1		; inc LSB of rotation counter
	movlw	d'10'		; if 10 then overflow (0-9)
	subwf	INDF,0		; if result is zero then = 10
	btfss	STATUS,Z	; if no overflow
	goto	endupd		; jump down

	; inc next digit of BCD rotation counter if needed
	clrf	INDF		; if overflow clear step counter
	incf	FSR,1		; point to LSB BCD rotation counter
	incf	INDF,1		; inc LSB of rotation counter
	movlw	d'10'		; if 10 then overflow (0-9)
	subwf	INDF,0		; if result is zero then = 10
	btfss	STATUS,Z	; if no overflow
	goto	endupd		; jump down

	; inc next digit of BCD rotation counter if needed
	clrf	INDF		; if overflow clear step counter
	incf	FSR,1		; point to LSB BCD rotation counter
	incf	INDF,1		; inc LSB of rotation counter
	movlw	d'10'		; if 10 then overflow (0-9)
	subwf	INDF,0		; if result is zero then = 10
	btfss	STATUS,Z	; if no overflow
	goto	endupd		; jump down

	; inc MSB of BCD rotation counter if needed
	clrf	INDF		; if overflow clear step counter
	incf	FSR,1		; point to LSB BCD rotation counter
	incf	INDF,1		; inc LSB of rotation counter
	movlw	d'10'		; if 10 then overflow (0-9)
	subwf	INDF,0		; if result is zero then = 10
	btfss	STATUS,Z	; if no overflow
	goto	endupd		; jump down

	; if it gets here counter >9999 and overflowed so reset
	clrf	INDF		; clear MSB (all others are 0 if got here)


endupd	call	updtlcd		; update LCD if one full rotation
endinc	movf	TEMP3,0		; get original FSR value
	movwf	FSR		; restore original FSR
	return

;**********************************************************************

; decs full step counter (ADADDR * FSR should be set before calling)
; FSR <- whole step # for motor (i.e. 0x40 for mtr 1, 0x47 for mtr 2, etc)
dec_cnt
	movf	FSR,0		; w = FSR
	movwf	TEMP3		; store FSR (put back at end)
	movlw	d'2'		; from 0-3 step counter to 0-200 step counter
	addwf	FSR,1		; point to whole step counter

	; dec step counter
	decf	INDF,1		; dec whole step counter
	comf	INDF,0		; if underfloww FF -> 00 when compliment
	btfss	STATUS,Z	; if no overflow
	goto	enddec		; jump down

	; dec LSB of BCD rotation counter if needed
	movlw	d'199'		; if underflow
	movwf	INDF		; 0 -> 199 (200 steps/rev)
	incf	FSR,1		; point to LSB BCD rotation counter
	decf	INDF,1		; dec LSB of rotation counter
	btfss	INDF,7		; if underflow (FF)
	goto	endud2		; jump down

	; dec LSB of BCD rotation counter if needed
	movlw	d'9'		; if underflow
	movwf	INDF		; 0 -> 9
	incf	FSR,1		; point to LSB BCD rotation counter
	decf	INDF,1		; dec LSB of rotation counter
	btfss	INDF,7		; if underflow (FF)
	goto	endud2		; jump down

	; dec LSB of BCD rotation counter if needed
	movlw	d'9'		; if underflow
	movwf	INDF		; 0 -> 9
	incf	FSR,1		; point to LSB BCD rotation counter
	decf	INDF,1		; dec LSB of rotation counter
	btfss	INDF,7		; if underflow (FF)
	goto	endud2		; jump down


	; dec MSB of BCD rotation counter if needed
	movlw	d'9'		; if underflow
	movwf	INDF		; 0 -> 9
	incf	FSR,1		; point to LSB BCD rotation counter
	decf	INDF,1		; dec LSB of rotation counter
	btfss	INDF,7		; if underflow (FF)
	goto	endud2		; jump down

	; if it gets here counter 0000 -> 9999
	movlw	d'9'		; if underflow
	movwf	INDF		; 0 -> 9 (all others are 9 if got here)

endud2	call	updtlcd		; updated the LCD once per rotation
enddec	movf	TEMP3,0		; get original FSR value
	movwf	FSR		; restore original FSR
	return

;**********************************************************************

; updates the LCD display with the # of rotations of motor 1
updtlcd
	; set DD RAM address to 0
	movlw	b'10000000'	; set DD RAM address to 0
	call	wrtins		; write instruction command to LCD
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
;	call	smdel		; delay about 100us

	movlw	d'4'		; loop four times
	movwf	CNT		; put in counter

	movlw	M1FSR		; address for motor # 1 data table
	addlw	d'6'		; jump to end of table
	movwf	FSR		; point to most significant BCD counter
up44	movlw	0x30		; ascii offset for the number 0
	addwf	INDF,0		; w = ascii offset + BCD # = ascii # 0 - 9
	call	wrtdat		; send data to LCD display
	decf	FSR,1		; point to next least significant byte
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between data writes
;	call	smdel		; delay about 100us
	decfsz	CNT,1		; update counter
	goto	up44		; if all digits aren't done stay in loop
	return

;**********************************************************************

	; step motor #2 (take up or feed out slack for winding coil)
stpmtr2	movlw	M2AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M2FSR		; addr for whole step# for motor 1
	movwf	FSR
	btfss	DIR,0		; check direction input
	call	stepccw		; step the pointer counter clockwise
	btfsc	DIR,0		; check direction input
	call	stepcw		; step the pointer clockwise
stpmtr4
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)
	clrwdt			; clear watch dog timer
	return

;**********************************************************************

; takes care of overflow in sideways step counter RATCNT and steps M3
ovflw	clrf	RATCNT		; reset counter
	movlw	M4AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M4FSR		; addr for whole step# for motor 1
	movwf	FSR
	btfss	DIR,1		; check sideways direction bit
	call	stepccw		; step the pointer counter clockwise
	btfsc	DIR,1		; check sideways direction bit
	call	stepcw		; step the pointer clockwise
	call	stpmtr4		; step motor #4
	return

;**********************************************************************

; takes care of underflow in sideways step counter RATCNT and steps M3
udflw	decf	RATIO,0		; reset counter to ratiocount - 1
	movwf	RATCNT
	movlw	M4AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M4FSR		; addr for whole step# for motor 1
	movwf	FSR
	btfss	DIR,1		; check sideways direction bit
	call	stepccw		; step the pointer counter clockwise
	btfsc	DIR,1		; check sideways direction bit
	call	stepcw		; step the pointer clockwise
	call	stpmtr4		; step motor #4
	return

;**********************************************************************

; disables motor 4 and pauses winding action (user can turn lead screw)
dismot4	movlw	M4AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M4FSR		; addr for whole step# for motor 1
	movwf	FSR
	clrf	COIL_A		; coil A off
	clrf	COIL_B		; coil B off
	clrf	COIL_C		; coil C off
	clrf	COIL_D		; coil D off
	call	updtdac		; send data to A/D (turn off motor)

disup	clrwdt			; clear watch dog timer
	btfss	PORTC,DISABL	; check if disable button is still pressed
	goto	disup		; if yes stay in loop

	; else turn on motor with same value as before
	movlw	M4AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M4FSR		; addr for whole step# for motor 1
	movwf	FSR
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)
	return

;**********************************************************************

; inc the ratio for sideways movement (MTR#4)
incrat	movlw	0xFF		; max ratio (incase overflow)
	incf	RATIO,1		; increment ratio
	btfsc	STATUS,Z	; if overflowed
	movwf	RATIO		; reset to 255
	call	ratlcd		; display ratio info on LCD
	return

;**********************************************************************

; dec the ratio for sideways movement (MTR#4)
decrat	movlw	d'1'		; min ratio (incase underflow)
	decf	RATIO,1		; decrement ratio
	btfsc	STATUS,Z	; if zero
	movwf	RATIO		; reset to 1
	call	ratlcd		; display ratio info on LCD
	return

;**********************************************************************

; display ratio info on LCD
ratlcd
	; set DD RAM address to 0
;	movlw	b'10000000'	; set DD RAM address to 0
	movlw	0x8B		; set DD RAM addr to pos 12 (1-20)
	call	wrtins		; write instruction command to LCD
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions

	; send text "RATIO " to LCD
	movlw	"R"		; Text to send
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
	movlw	"A"		; Text to send
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
	movlw	"T"		; Text to send
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
	movlw	"I"		; Text to send
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
	movlw	"O"		; Text to send
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions
	movlw	" "		; Text to send
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between instructions

	; convert RATIO to BCD
cvt	clrf	HUND		; clear hundreds digit
	clrf	TEN		; clear tens digit
	clrf	ONE		; clear ones digit
	movf	RATIO,0		; w = ratio
up24	movwf	TEMP3		; temp = w
	incf	HUND,1		; add one to hundreds digit
	movlw	d'100'		; decimal 100
	subwf	TEMP3,0		; w = temp count - 100
	btfsc	STATUS,C	; if C = 0 negative result
	goto	up24		; if neg then go down

	decf	HUND,1		; remove last count because result was neg
	movf	TEMP3,0		; w = temp (restore real value)
up25	movwf	TEMP3		; temp = w
	incf	TEN,1		; add one to tens digit
	movlw	d'10'		; decimal 10
	subwf	TEMP3,0		; w = temp count - 10
	btfsc	STATUS,C	; if C = 0 negative result
	goto	up25		; if neg then go down

	decf	TEN,1		; remove last count because result was neg
	movf	TEMP3,0		; w = temp (restore real value)
	movwf	ONE		; store ones digit

	movlw	0x30		; ascii offset for the number 0
	addwf	HUND,0		; w = ascii offset + BCD # = ascii # 0 - 9
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between data writes
	movlw	0x30		; ascii offset for the number 0
	addwf	TEN,0		; w = ascii offset + BCD # = ascii # 0 - 9
	call	wrtdat		; send data to LCD display
	movlw	d'255'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between data writes
	movlw	0x30		; ascii offset for the number 0
	addwf	ONE,0		; w = ascii offset + BCD # = ascii # 0 - 9
	call	wrtdat		; send data to LCD display
	clrwdt			; clear watchdog timer
	movlw	d'150'		; delay ~(256-#)*2ms = 2ms
	call	delay		; delay between data writes
	return

;**********************************************************************

; read serial port and decode command
serprt
	; test if this is the start of a new command or part of an old one
	clrf	TMR2		; reset TMR2 for next byte
	incf	BYTCNT,1	; increment the byte counter
	btfsc	PIR1,TMR2IF	; check if time between bytes was too long
	clrf	BYTCNT		; if yes reset byte counter to zero
	bcf	PIR1,TMR2IF	; clear overflow bit incase it's set

	; if bytcnt > 2 (ser command takes 3 bytes 0,1, & 2) read & exit
	movf	BYTCNT,w	; w = bytcnt
	sublw	d'2'		; w = 2 - bytcnt
	btfss	STATUS,C	; if carry = 0 then result negative
	goto	done		; if negative, skip down and exit

	movlw	SERAD		; starting addr of serial command table
	addwf	BYTCNT,w	; w = addr + offset
	movwf	FSR		; put in pointer register
	movf	RCREG,w		; w = received byte from serial port
	movwf	INDF		; store received byte in table
;	bcf	RCSTA,CREN	; clear error bit just incase

	movf	BYTCNT,w	; w = bytcnt
	sublw	d'2'		; w = 2 - bytcnt
	btfsc	STATUS,Z	; if zero bit set then bytcnt = 2
	call	sercmd		; decode serial command
	return

done	; shouldn't get here unless bytcnt > 2 (an error)
	movf	RCREG,w		; read byte from receive register
;	bcf	RCSTA,CREN	; clear error bit just incase
	bcf	BYTCNT,7	; make sure can't overflow from 255 -> 0
	return

;**********************************************************************

; decodes the serial command and take appropriate action
; call when all three serial bytes are stored in 0x3D-0x3F
sercmd
	bsf	PIR1,TMR2IF	; set overflow bit (next byte new cmd)
	movlw	SERAD		; load command table starting address
	movwf	FSR		; put in pointer addr
	btfsc	INDF,3		; test bit 3
	goto	dwn1		; if one, jump down
	; commands 0-7
	btfss	INDF,2		; test bit 2
	goto	cmd_sd		; if zero, step direction command
	btfss	INDF,1		; test bit 1
	goto	cmd_hd		; if zero, horizontal direction command
	goto	cmd_sp		; if one, speed command
dwn1	; commands 8-15
	btfsc	INDF,2		; test bit 2
	goto	dwn2		; if one, jump down
	btfss	INDF,1		; test bit 1
	goto	cmd_rt		; if zero, ratio command
	goto	cmd_tn		; if one, turn number command
dwn2	btfss	INDF,1		; test bit 1
	goto	cmd_ss		; if zero, start stop command
	goto	cmd_as		; if one, auto wind command
	return			; if not a legal command return

;**********************************************************************

; serial step direction command (cmd byte = 0000001X)
; read will return either 0 or 1 (a single byte)
; write should have either a 0 or 1 as the 1st data byte (2nd data ignored)
cmd_sd	btfsc	INDF,0		; test if read or write command
	goto	dwn3		; if one jump down

	; read step direction and send to serial port
	movlw	d'0'		; w = 0
	btfsc	REM_DIR,0	; check rotation bit for step direction
	movlw	d'1'		; if dir bit = 1 then w = 1
	movwf	TXREG		; send byte out serial port
	return

dwn3	; write step direction and send 0 as response
	bcf	REM_DIR,0	; set remote direction bit
	incf	FSR,1		; point to first data byte
	btfsc	INDF,0		; test if data byte is 1 or 0
	bsf	REM_DIR,0	; if zero then clear bit of REM_DIR
	movlw	d'0'		; w = 0
	movwf	TXREG		; send byte out serial port (done)
	return

;**********************************************************************

; serial horizontal direction command (cmd byte = 0000010X)
; read will return either 0 or 1 (a single byte)
; write should have either a 0 or 1 as the 1st data byte (2nd data ignored)
cmd_hd	btfsc	INDF,0		; test if read or write command
	goto	dwn4		; if one jump down

	; read step direction and send to serial port
	movlw	d'0'		; w = 0
	btfsc	REM_DIR,1	; check horizontal direction bit
	movlw	d'1'		; if dir bit = 1 then w = 1
	movwf	TXREG		; send byte out serial port
	return

dwn4	; write horizontal direction and send 0 as response
	bcf	REM_DIR,1	; set remote direction bit
	incf	FSR,1		; point to first data byte
	btfsc	INDF,0		; test if data byte is 1 or 0
	bsf	REM_DIR,1	; if zero then clear bit of REM_DIR
	movlw	d'0'		; w = 0
	movwf	TXREG		; send byte out serial port (done)
	return

;**********************************************************************

; serial speed command (cmd byte = 0000011X)
; read will return a single byte (1-255) - the current value of REM_DEL
; write should have a single byte (1-255) as the 1st data byte
cmd_sp	btfsc	INDF,0		; test if read or write command
	goto	dwn5		; if one jump down

	; read step direction and send to serial port
	movf	REM_SPD,w	; put remote speed in w
	movwf	TXREG		; send byte out serial port
	return

dwn5	; write horizontal direction and send 0 as response
	incf	FSR,1		; point to first data byte
	movf	INDF,w		; put remote speed in w
	movwf	REM_SPD		; update remote speed value
	movlw	d'0'		; w = 0
	movwf	TXREG		; send byte out serial port (done)
	return

;**********************************************************************

; remote ratio command (cmd byte = 0000100X)
; read will return a single byte (1-255) - the current value of REM_RAT
; write should have a single byte (1-255) as the 1st data byte
cmd_rt	btfsc	INDF,0		; test if read or write command
	goto	dwn6		; if one jump down

	; read ratio and send to serial port
	movf	REM_RAT,w	; put remote speed in w
	movwf	TXREG		; send byte out serial port
	return

dwn6	; write horizontal direction and send 0 as response
	incf	FSR,1		; point to first data byte
	movf	INDF,w		; put remote ratio in w
	movwf	REM_RAT		; update remote ratio value
	movwf	RATIO		; put remote ratio in ratio
	call	ratlcd		; update LCD display with ratio info
	movlw	d'0'		; w = 0
	movwf	TXREG		; send byte out serial port (done)
	return

;**********************************************************************

cmd_tn
	return

;**********************************************************************

; serial start stop command (cmd byte = 0000110X)
; read will return either 0 or 1 (a single byte) (0=start, 1=stop)
; write should have either a 0 or 1 as the 1st data byte (2nd data ignored)
cmd_ss	btfsc	INDF,0		; test if read or write command
	goto	dwn7		; if one jump down

	; read start stop byte and send to serial port
	movf	REM_SS,w	; put stop start byte in w
	movwf	TXREG		; send byte out serial port
	return

dwn7	; write start stop byte and send 0 as response
	bcf	REM_SS,0	; set start stop bit
	incf	FSR,1		; point to first data byte
	btfsc	INDF,0		; test if data byte is 1 or 0
	bsf	REM_SS,0	; if zero then clear bit of REM_SS
	movlw	d'0'		; w = 0
	movwf	TXREG		; send byte out serial port (done)
	return

;**********************************************************************

cmd_as
	return

;**********************************************************************

main
	call	portio		; set port I/O directions
	call	m_def		; set default motor position
	clrwdt			; clear watch dog timer
	call	intset		; disable all interupts
	call	tmrset		; set up timers and watchdog
	call	usart		; set up the serial port
	call	adset		; set up A/D converter
	movlw	d'241'		; delay ~(256-#)*2ms
	call	delay		; delay for ~30ms before calling LCD setup
	call	lcdset		; set up LCD display
	movlw	d'22'		; step motor #3 once for every
	movwf	RATIO		; 22 steps of motor #1
	clrf	RATCNT		; clear ratio counter
	call	ratlcd		; display ratio count on LCD

	; set remote default values
	clrf	REM_DIR		; default
	movlw	d'1'		; legal speed (delay) values 1-255 
	movwf	REM_SPD		; default - go fast
	movlw	d'22'		; legal ratio values 1-255
	movwf	REM_RAT		; default - maybe 30 gauge wire?
	clrf	REM_SS		; default is stop (if 1 then turn)

up2
	bsf	ADCON0,GO	; digitize ch0 (result ready in 100us)

	; check if should disable sideways motor (MTR#4)
	btfss	PORTC,DISABL	; check if disable button is pressed
	call	dismot4		; disable motor 4

	btfsc	PORTA,RMT	; if remote bit set
	goto	skp34		; jump down

	; if remote bit not set, check if should inc or dec RATIO
	btfss	PORTC,RATUP	; check if ratio up button is pressed
	call	incrat		; inc ratio
	btfss	PORTC,RATDN	; check if ratio down button is pressed
	call	decrat		; dec ratio
;	goto	skp34

skp33	; if remote bit set, put remote ratio into ratio
;	movf	REM_RAT,w	; put remote ratio in W
;	movwf	RATIO		; put remote ratio in ratio
;	call	ratlcd		; update LCD display with ratio info

skp34	clrwdt			; clear watch dog timer
	btfsc	PORTA,RMT	; if remote bit set
	goto	skp35		; jump down
	btfsc	PORTE,INSTP	; if input high
	goto	up2		; stay in loop
	goto	skp36

skp35	btfsc	REM_SS,0	; check remote start stop bit
	goto	skp34		; if high stay in loop

	; check if remote or local control
	; if local - use DIR (set by input switches)
	; if remote - use REM_DIR to set DIR
	; update direction control bit
skp36	movlw	b'00000011'	; SDIR=1, DIR=1
	btfss	PORTC,SDIR	; check sideways direction input
	movlw	b'00000001'	; if low SDIR=0, DIR=1
	movwf	DIR		; load dir bit and sdir bit
	btfss	PORTE,INDIR	; check rotation direction input
	comf	DIR,1		; if DIR=0 reverse bits (0->1 & 1->0)
	movf	REM_DIR,w	; w = remote direction
	btfsc	PORTA,RMT	; if remote bit set
	movwf	DIR		; replace DIR with REM_DIR

	; step the motor
	movlw	M1AD		; A/D address & CS/LD info for motor 1
	movwf	ADADDR		; addr=1, 5, 17, or 21 (mtr1, 2, 3, or 4)
	movlw	M1FSR		; addr for whole step# for motor 1
	movwf	FSR
	btfss	DIR,0		; check direction input
	call	stepccw		; step the pointer counter clockwise
	btfsc	DIR,0		; check direction input
	call	stepcw		; step the pointer clockwise
	call	ldcoil		; load values into coils a-d
	call	updtdac		; send data to A/D (step the motor)
	clrwdt			; clear watch dog timer

	; check direction and photogate bits
	btfsc	PORTA,RMT	; check if in remote mode
	goto	skp4		; if in remote mode skip down
	btfsc	DIR,0		; check dir bit
	goto	high1		; if dir bit high skip down
low1	btfsc	PORTA,LOLIM	; check if uper limit photogate is high
	call	stpmtr2		; if yes then step the motor
	btfsc	PORTA,LOLIM	; if photogate still not blocked
	goto	low1		; stay in loop and turn motor some more
	goto	skp3		; if not skip down

skp4	btfsc	REM_DIR,0	; if in remote mode check rotation dir bit
	goto	high1		; if dir = 1 then step motor
	goto	low1		; if dir = 0 then check photo gates

high1	btfss	PORTA,LOLIM	; check if lower limit photogate is low
	call	stpmtr2		; if yes then step the motor
	btfss	PORTA,LOLIM	; if photogate still blocked
	goto	high1		; stay in loop and turn motor some more

	; check if need to move motor #4 (sidways movement)
skp3	btfsc	DIR,1		; test sideways direction bit
	goto	skp11		; skip down if 1

	incf	RATCNT,1	; if dir = 0 then inc ratcnt
	movf	RATIO,0		; compare it to the ratio
	subwf	RATCNT,0
	btfsc	STATUS,Z	; if they are the same (overflow)
	call	ovflw		; clear counter and take step
	goto	skp12

skp11	movlw	d'1'		; decimal one
	subwf	RATCNT,1	; dec ratio counter as needed
	btfss	STATUS,C	; if c = 0 then neg (underflow)
	call	udflw		; if yes then reset counter and take step

	; have A/D result control stepping speed 1 (1K log pot on input)
skp12	movf	ADRESH,0	; w = A/D ch0 result
	addlw	d'1'		; add 1 (delay about 128us)
	btfsc	STATUS,Z	; check if result zero
	movlw	d'255'		; if yes then set delay to max
	btfsc	PORTA,RMT	; check if in remote mode
	movf	REM_SPD,w	; if yes put remote speed in W
	call	smdel		; delay between u_steps

	goto	up2

		END		; directive 'end of program'

