   	TITLE		"Accessory encoder PC interface"
; filename Ace_PC1.asm

;	By Mike Bolton
;	Started 03/07/04
;	Tested and working  14/07/04. Uses PCB Ace_PC1
;	
;	Interfaces a serial data connection to the MERG accessory encoder	
;	Accessory encoder is unmodified		 
;	Interface has a jumper / switch to change between PC and control panel		 
;	Serial at 9600B. No handshake, 9 way D connector
;	Command with ASCII string
;	S and point number sets point one way. (S = straigth)
;	T and point number sets the other way.	(T = turn)
;	Point numbers 1 to 128
;	1 to 4 is decoder address 0, 5 to 8 is address 1 etc.
;	Used PIC 18F4220 with internal clock at 8 MHz

; Assembly options
	LIST	P=18F4220,r=hex,N=75,C=120,T=ON

	include		"p18f4220.inc"

;set config registers
	
	__CONFIG	_CONFIG1H,	B'00001000'	;oscillator internal, RA6, RA7 as I/O
	__CONFIG	_CONFIG2L,	B'00000010'	;brown out on voltage = 4.5, and PWT on	
	__CONFIG	_CONFIG2H,	B'00000000'	;watchdog time and enable. WDT is off
	__CONFIG	_CONFIG3H,	B'10000001'	;MCLR enable, PB as digital I/O	
	__CONFIG	_CONFIG4L,	B'10000001'	;B'10000001'  for 	no debug
	__CONFIG	_CONFIG5L,	B'00001111'	;code protection (off)	
	__CONFIG	_CONFIG5H,	B'11000000'	;code protection (off)	
	__CONFIG	_CONFIG6L,	B'00001111'	;write protection (off)	
	__CONFIG	_CONFIG6H,	B'11100000'	;write protection (off)	
	__CONFIG	_CONFIG7L,	B'00001111'	;table read protection (off)	
	__CONFIG	_CONFIG7H,	B'01000000'	;boot block protection (off)

;	processor uses 8 MHz internal clock

;********************************************************************************
MOD			equ	4			;Mode pin RC4 (CP is hi, PC is lo)
EN			equ	0			;RC0 is matrix enable, lo is matrix, hi is PC
RXD			equ	7			;RXD on port c pin 7
Baudrate	equ	.51			;for 9600 Baud

;****************************************************************
;	define RAM storage
	
	CBLOCK	0		;file registers
	Mode			;mode 0 is no PC, mode 1 is PC enabled
	Colcnt			;column counter
	Point			;pointer for data stack
	Col_a			;column read from port A
	Col_b			;column read from port B
	Bitcnt			;bit pattern for columns
	Rmode			;mode for serial input
	Chr_tmp			;temp for serial char input
	Chr_cnt			;count number of chars
	Set_dir			;point direction
	Number			;point number
	Dely			;delay counter
	Bit				;bit to set / clear in stack register
	Temp			;temp store
	Stack			;array of point positions
	Stack1			;full list for debugging purposes
	Stack2
	Stack3
	Stack4
	Stack5
	Stack6
	Stack7
	Stack8
	Stack9
	Stack10
	Stack11
	Stack12
	Stack13
	Stack14
	Stack15
	String			;ASCII digits for point number (1 to 128)
	String1
	String2	
	
	ENDC
;Stack	res	.16		;stack for output data for each column
;String	res	3		;hold ascii number	
	
	CBLOCK	h'80'
		
	ENDC


	

;****************************************************************
;
;		start of program code

		ORG		0000h
		nop						;for debug
		goto	setup

		ORG		0008h
		goto	hpint			;high priority interrupt

		ORG		0018h	
		goto	hpint			;low priority interrupt


;*******************************************************************

		ORG		0020h			;start of program

;	high priority interrupt. 
;	
;	

hpint	movf	RCREG,W			;get serial byte
		movwf	Chr_tmp
		tstfsz	Rmode			;what mode?
		bra		ser1
		movlw	'S'				;straight?
		cpfseq	Chr_tmp
		bra		next
		clrf	Set_dir
int2	incf	Rmode
		clrf	Chr_cnt
		retfie	1				;not a valid char
next	movlw	'T'
		cpfseq	Chr_tmp
		retfie	1
		incf	Set_dir
		bra		int2
ser1	movlw	0x0D			;?carraige return
		cpfseq	Chr_tmp
		bra		num
		tstfsz	Chr_cnt			;no chars?
		bra		parse
		bra		back
parse	movlw	4
		cpfslt	Chr_cnt			;no more than 3 chars
		bra		back
		movf	POSTDEC1,W		;dummy to decrement FSR1
		decf	Chr_cnt,F
		rlncf	Chr_cnt,W		;double the jump
		addwf	PCL				;convert number to binary
		bra		one
		bra		two
		movf	POSTDEC1,W		;get last char
		movwf	Number
		movlw	.10
		mulwf	POSTDEC1
		movf	PRODL,W
		addwf	Number,F
		movlw	1
		subwf	INDF1,W
		bnz		back			;can't be other than one
		movlw	.100
		addwf	Number,F
		bra		putin
two		movf	POSTDEC1,W		;get last char
		movwf	Number
		movlw	.10
		mulwf	POSTDEC1
		movf	PRODL,W
		addwf	Number,F
		bra		putin		
one		movf	POSTDEC1,W		;get last char
		movwf	Number

putin	movf	Number,F		;is it zero?
		btfsc	STATUS,Z
		bra		back			;abort
		decf	Number,F		;point 1 = 0
		btfsc	Number,7		;127 max
		bra		back	
		clrf	Point
		rrcf	Number,F
		rrcf	Point,F
		rrcf	Number,F
		rrcf	Point,F
		rrcf	Number,F		;Number now has stack register
		rrcf	Point,F
		rrncf	Point,F
		swapf	Point,F			;Point has the bit number in the register (0 to 7)
		incf	Point,F
		movlw	B'00001111'
		andwf	Number,F		;mask
		movlw	1
		movwf	Bit
nxtbit	decf	Point,F			;this sets a bit in Bit corresponding to the point
		bz		done
		rlncf	Bit,F
		bra		nxtbit
done	tstfsz	Set_dir			;is it straight or turn?
		bra		turn
		comf	Bit
		movf	Number,W
		movf	PLUSW0,W
		andwf	Bit,F			;clear bit
		movf	Number,W
		movff	Bit,PLUSW0
		bra		back

turn	movf	Number,W
		movf	PLUSW0,W
		iorwf	Bit,F			;set bit
		movf	Number,W
		movff	Bit,PLUSW0
		bra		back
	
num		movf	Chr_tmp,W
		rcall	ascii
		bnc		back			;not a number
		movwf	POSTINC1
		incf	Chr_cnt
		retfie	1


back	clrf	Rmode
		clrf	Chr_cnt
		lfsr	1,String		;reset pointer
	
		retfie	1

ascii	addlw	0xC6		;check for number
		bc		over		;not a number
		addlw	0x0A
		bnc		over
		return
over	bcf		STATUS,C
		return	
		
			
			

;**************************************************************
;
;		

lpint	
		retfie	
						

;*********************************************************************

setup	movlw	B'01110011'			;set oscillator
		movwf	OSCCON				;8 MHz internal
		clrf	LATA				;set port A
		movlw	B'11111111'			;A is all inputs
		movwf	TRISA
		clrf	LATB				;set port B
		movlw	B'11111111'			;B is all inputs
		movwf	TRISB
		clrf	LATC				;set port C
		movlw	B'10010000'			;C serial, RC7 is RXD, RC0 is matrix enable, RC4 is mode detect
		movwf	TRISC
		clrf	LATD				;set port D
		movlw	B'11111111'			;D is all inputs on reset 
		movwf	TRISD
		clrf	LATE				;set port E
		movlw	B'00000000'			;E is all outputs (port not used)
		movwf	TRISE
		clrf	ADCON0				;A/D off
		movlw	0x0F
		movwf	ADCON1				;A/D all digital inputs
		bsf		RCON,	IPEN		;enable high priority interrupts
		movlw	7
		movwf	CMCON				;turn off comparator
		clrf	EECON1				;ensure data EEPROM
		movlw	B'00000100'			;set USART, baud rate range high
		movwf	TXSTA
		movlw	Baudrate			;set baud rate to 9600
		movwf	SPBRG
		bsf		RCSTA,SPEN
		bsf		PIE1,RCIE			;enable RXD interrupt
		bsf		IPR1,RCIP			;RXD is high priority
		bsf		RCSTA,CREN
		
		
		clrf	Rmode
		clrf	Mode
		clrf	Col_a
		clrf	Col_b
		movlw	.16
		movwf	Point
	
		lfsr	0,Stack				;set stack pointer in FSR0
		
clear	clrf	POSTINC0			;clear stack
		decfsz	Point
		bra		clear
		lfsr	0,Stack				;reset stack pointer
		
		lfsr	1,String			;set string pointer in FSR1
		

		clrf	PIR1				;clear USART
		movlw	B'11000000'
		movwf	INTCON				;set interrupt enable

;		main scan loop

loop_a	btfss	PORTC,MOD			;what mode?
		bra		pc1
		tstfsz	Mode				;if mode is 0 already set to CP
		bra		pc2					;otherwise set port to CP
loop_a0	comf	PORTA,W				;get port A data
		bz		loop_b				;no cols. in port A set
		cpfseq	Col_a				;any change from last time?
		bra		loop_a1				;yes so continue
		bra		loop_a				;try again	
loop_a1	rcall	delay				;delay for edges
		comf	PORTA,W				;read again
		movwf	Col_a				;new data to Col_a
		clrf	Colcnt				;which column is it?
		clrf	Bitcnt
		incf	Bitcnt				;Bit count starts at one
loop_a2	cpfseq	Bitcnt				;Is it this bit?
		bra		loop_a3				;no
		movf	Colcnt,W			;get raw column bit (0 to 15)
		movwf	EEADR				;mapping in EEPROM
		bcf		EECON1,EEPGD
		bsf		EECON1,RD
		movf	EEDATA,W
		comf	PLUSW0,W			;get byte from stack and invert
		movwf	Temp
		rlncf	Temp,F				;adjust for pinout
		rlncf	Temp,W
		movwf	PORTD				;put out
		bra		loop_a				;repeat

loop_a3	incf	Colcnt,F			;try next column bit
		rlncf	Bitcnt
		bra		loop_a2	

loop_b	clrf	Col_a
		comf	PORTB,W				;get B input
;		andlw	B'00111111'			;for debugger which uses PB6 and PB7
		cpfseq	Col_b				;any change from last time?
		bra		loop_b1				;yes so continue
		bra		loop_a				;try again	
loop_b1	rcall	delay
		comf	PORTB,W	
;		andlw	B'00111111'			;debug
		movwf	Col_b				;new data to Col_b
		clrf	Colcnt				;which column is it?
		clrf	Bitcnt
		incf	Bitcnt				;Bit count starts at one
loop_b2	cpfseq	Bitcnt				;Is it this bit?
		bra		loop_b3				;no
		movf	Colcnt,W			;get raw column bit (0 to 15)
		addlw	8					;offset
		movwf	EEADR				;mapping in EEPROM
		nop
		bsf		EECON1,RD
		movf	EEDATA,W
		comf	PLUSW0,W			;get byte from stack
		movwf	Temp
		rlncf	Temp,F				;adjust for pinout
		rlncf	Temp,W
		movwf	PORTD				;put out
		bra		loop_a				;repeat

loop_b3	incf	Colcnt,F			;try next column bit
		btfsc	Colcnt,3			;limit to 7
		bra		loop_a				;as above
		rlncf	Bitcnt
		bra		loop_b2

pc1		movf	Mode,F				;is mode set?
		bnz		loop_a0				;is in PC mode
		
		rcall	pcset
		bra		loop_a0				;back

pc2		rcall	pcunset
		bra		loop_a0				;back

delay	movlw	.20
		movwf	Dely
del1	decfsz	Dely
		bra		del1
		return

pcset	bsf		PORTC,EN		;CP buffer to tri-state
		clrf	TRISD			;PORTD to output
		incf	Mode,F			;set mode to PC
		return

pcunset	setf	TRISD			;port to inputs
		bcf		PORTC,EN		;CP buffer on
		clrf	Mode			;set to CP
		return	

		

	ORG 0xF00000			;EEPROM data. Column translation table
	de	.10,.11
	de	.8,.9
	de	.14,.15
	de	.13,.12
	de	.0,.2
	de	.1,.3
	de	.4,.5
	de	.6,.7
	


		end
