
	title	ace3a.asm
;**********************************************************************************************
;	Written by Mike Bolton
;
;	This program is for an accessory only encoder. It scans a matrix of
;	on / off switches, each with series diodes and detects changes in the 
;	switch position. Accessory packets are sent to set one of each
;	output pair dependent on the switch position. Only one packet sequence is 
;	sent for each change. If there has been no change, an idle packet is sent.
;	A 4 to 16 line multiplexer is used to drive 16 columns. 8 rows are 
;	sensed by the PIC input lines. This gives a maximum of 128 switches.
;	Each set of four switches has a unique DCC address. Column 1 rows 1 to 4
;	are address 0, column 1 rows 5 to 8 are address 1 etc. allowing for upto
;	32 separate decoders, each with 4 output pairs. Only the first 6 bits of
;	the accessory decoder address range are used. The top 3 bits are zero.
;	When the encoder is first switched on, all switches are interrogated and
;	their positions complemented. This gives the impression that each has changed
;	so the encoder sends packets to all outputs to set them to the switch positions.
;	This is essential for solenoid point motors as they can be in any position
;	at switch-on. There is a manual reset button which also sets all points
;	correctly in case any have stuck in the wrong position.
;	This program first sends a'deactivate' packet for the opposite of a pair, 
;	an intermediate idle and then an 'activate'.  
;	This gives a true  bistable on / off action for 'tortoises' or lights 
;	but is equally suitable for CDU operated solenoids where the decoder
;	'on' time is short. (The deactivate is irrelevant here).
;	
;
;	No aspects of the program are copyright and it may be freely used
;	copied, modified or distributed in whole or part. Acknowledgement of the original
;	author would be appreciated. 
;
;
;
;	27/05/02	Rewritten for 16F628
;			dccace2 working  28/05/02
;	06/10/02	dccace3 is dccace2 with deactivate
;			for tortoises, lights etc. 
;	12/10/02	Tested and seems to work
;	27/01/03	Added comparator turnoff  
;	10/01/06    Now renamed ace3a  for 16F628A            
;			
;*********************************************************************

	processor	16F628A
	include		"p16f628a.inc"
	__CONFIG	h'3F61' ; PUTimer on, WDT off, XT clock
				; 4 Mhz Crystal
;




;	working registers

	CBLOCK 0x0020

	temp		;temp
	spare		;spare
	colcnt		;column counter
	byte1		;first bute of packet
	byte2		;second byte of packet
	byte3		;third byte of packet (for future use)
	err1		;error byte
	bitcnt		;bit count in packet
	bytcnt		;in packet
	temp1	
	temp2	
	temp3	
	fcrtmp	
	count0		;number of packets
	count4		;extra counter
	rowcnt		;row counter
	bitnum		;sets bit in row (shift register)
	adrcnt		;address counter
	byttmp		;temp for byte in deactivate
	adrtmp		;temp for address

	table0		;table for row data (each column)
	table1	
	table2	
	table3	
	table4	
	table5	
	table6	
	table7	
	table8	
	table9	
	tableA	
	tableB	
	tableC	
	tableD	
	tableE	
	tableF	

	ENDC

;*************************************************************************
;
	org		0
	goto	setup	;reset vector
;
	org	04	;interrupt vector
	retfie		;return from interrupt
;
;************************************************************************
;
;start of program. Sets up ports etc.


	org	10
	
setup	clrf	PORTA
	
	bsf	STATUS,RP0	;set to page 1
	movlw	B'00000000'	;set up TMR0 (divide by 2)
	movwf	OPTION_REG
	movlw	B'11100000'	;set port a (pa4 is data out)
				;pa0 column 0
				;pa1 column 1
				;pa2 column 2
				;pa3 column 3
				;pa4 DCC data out to decoders
	movwf	TRISA
	movlw	B'11111111'	;set up port b 
				;reads row data
	movwf	TRISB
	bcf	STATUS,RP0	;back to page 0
	movlw	B'00000111'	;
	movwf	CMCON		;turn comparators OFF

	movlw	B'00010000'	;set port a
	movwf	PORTA
	clrf	colcnt
;
;	end of setup

	movlw	0		;goto init routine
	goto	loop
;
;***************************************************************

;
;	main jump table	

main	bcf	INTCON,T0IF	;clear RTCC flag
main1	addwf	PCL,F
	goto	init		;0
	goto	p2          	;1    packet routine
	goto	one1		;2
	goto	zero1		;3
	goto	zero2		;4
	goto	one2		;5
	goto	nxtby1		;6
	goto	pr1		;7	preamble 
	goto	pr3		;8
	goto	pr4		;9 
	goto	scand		;10	scan
	goto	scanu		;11
	goto	rollu		;12
	goto	putd		;13
	goto	set2		;14 
	goto	h1		;15
	goto	h2		;16

;******************************************************************
;
;	timing loop. Waits for RTCC, resets and jumps
	
loop	btfss	INTCON,T0IF	;wait loop
	goto	loop
	movwf	temp		;save w
	movlw	0xE6		;reset TMR0
	movwf	TMR0
	movf	temp,W		;recover w
	goto	main
;
;******************************************************************
;
;	Initialise sequence
;	reads all switches and complements them
;	so it appears as if they have all changed
;	cycles through all outputs of all decoders
;	so starting settings are correct


init	movlw	.10
	movwf	count0		;send 10 reset packets
	call	reset 
	movlw 	.10
	call	idle		;send 10 idle packets
	clrf	count0		;
init1	movlw	table0		;point to table
	addwf	colcnt,W
	movwf	FSR
	movf	PORTB,W		;get row data
	movwf	INDF		;put in table
	incf	colcnt,W	;increment column
	andlw	B'00001111'	;columns 0 to 15
	movwf	colcnt
	movf	PORTA,W		;get port a 
	andlw	B'11110000'	;mask address
	iorwf	colcnt,W	;get new column address
	movwf	PORTA		;select new column
	comf	INDF,F		;complement row data
	
	incf	count0,F	;one packet
	call	idle		;to give delay
	movf	colcnt,F	;last column?
	btfss	STATUS,Z
	goto	init1
	clrf	adrcnt		;set address to zero
		
	
;***********************************************************
;
;	main read loop
;	
scan	movlw	table0		;point to table
	addwf	colcnt,W
	movwf	FSR
	movf	PORTB,W		;get row data
	movwf	temp3		;save
	movlw	.10             ;scand
	goto	loop 		;wait
scand   bcf	PORTA,4		;output down
	movf	temp3,W		;recover row data
	xorwf	INDF,W		;compare with old data
	btfss	STATUS,Z	;no change
	goto	change
scan1	movlw	.11		;scanu
	goto	loop          	;wait
scanu	bsf	PORTA,4       	;output up
	incf	colcnt,W	;increment column
	incf	adrcnt,F	;address goes in twos
	incf	adrcnt,F
	andlw	B'00001111'	;columns 0 to 15
	movwf	colcnt
	movf	PORTA,W		;get port a 
	andlw	B'11110000'	;mask address
	iorwf	colcnt,W	;get new column address 
	iorlw	B'00010000'	;keep dcc output
	movwf	PORTA		;select new column
	
	
	incf	count0,F	;one packet 
	call	idle		;to give delay
	movf	colcnt,F	;last column?
	btfss	STATUS,Z
	goto	scan
	clrf	adrcnt		;reset address
	goto	scan
				;
;	here if row data is different from table
;
change	clrf 	bitnum
	movwf	temp1		;difference data
	movwf	temp2
	addlw	B'00001111'	;is it lower nibble
	btfss	STATUS,DC	;yes
	goto	higher
	bsf	STATUS,Z	;low nibble
	call	sort
	
	call	set1		;set up packet
	
	movlw	.16
	goto	loop
h2	bcf	PORTA,4	;output down	

;	check for higher nibble 

higher	movlw	B'11110000'	;any difference
	andwf	temp1,W
	btfsc	STATUS,Z	;yes
	goto	scan1
	bcf	STATUS,Z	;hi nibble
	call	sort

	swapf	bitnum,F
	call	set1		;set up packet 
	
	movlw	.15
	goto	loop
h1	bcf	PORTA,4		;output down

	goto	scan1		;loop again

	
	
;*****************************************************
;
;	subroutines
;
;	
	
;******************************************************
;
;	sends a complete reset packet
;	number of packets in count0
;
reset	movlw	B'00000000'
	movwf	byte1
	movlw	B'00000000'
	movwf	byte2
	movlw	3
	movwf	bytcnt
	call	preamb
	call	packet
	decfsz	count0,F
	goto	reset 
    	return 
;
;******************************************************
;
;	sends a complete idle packet
;	number of packets in count0
    
idle	movlw	B'11111111'
	movwf	byte1
	movlw	B'00000000'
	movwf	byte2
	movlw	3
	movwf	bytcnt 
	call	preamb
	call	packet
	decfsz	count0,F
	goto	idle
	return
;
;****************************************************************
;
;	main packet routine
;
;	sends a packet without preamble
;	data to be set in byte1 to byte3 etc
;	number of bytes in packet in bytcnt
;	routine works out the error byte itself
;	adds start bits 
;	must arrive with the DCC output high
;
;			 
packet	movlw	byte1    	;point FSR to start of bytes
	movwf	FSR
	movf	bytcnt,0	;get no of bytes
	movwf	temp
	decf	temp,F
	decf	temp,F
	movf	INDF,0		;get 1st byte
	incf	FSR,F 
p1	xorwf	INDF,0		;work out error
	incf	FSR,F 
	decfsz	temp,F
	goto	p1 
	movwf	INDF		;error byte
	movlw	byte1 		;reset index
	movwf	FSR
	
	

		
ploopa	btfss	INTCON,T0IF  	;wait for RTCC
	goto	ploopa 
	

	movlw	0xED		;sets zero for 100 usec.
	movwf	TMR0
	bcf	INTCON,T0IF

	movlw	.9
	movwf	bitcnt		;set bitcount (8 bits)
	movlw	.1		;wait for RTCC in main loop
	goto	loop		;jump to p2
p2	bcf	PORTA,4		;output lo (first half of start bit complete)
;
;	second half of start bit
;
		 
ploop2	btfss	INTCON,T0IF
	goto	ploop2 
	movlw	0xED		;reset TMR0
	movwf	TMR0 
	bcf	INTCON,T0IF
	
;
;	setup for a byte before end of start bit
;
nxt1	decfsz	bitcnt,1	;last bit?
	goto	nxtbit		;no so next bit
	goto	nxtbyt		;yes so next byte
		
nxtbit	rlf	INDF,1		;roll the data
	btfss	STATUS,C
	goto	zero		;wait for RTCC then jump to 
				;a one or a zero

	movlw	.2		;one1
	goto	loop
;
;	bit is a zero
;
zero	movlw	.3		;zero1
	goto	loop
	
zero1	bsf	PORTA,4		;output up 

z1	btfss	INTCON,T0IF  	;miss a count as it is a zero
	goto	z1
	movlw	0xED		;reset TMR0 
	movwf	TMR0		;value to make zero = 100usec.
	bcf	INTCON,T0IF

	movlw	.4 		;wait again then goto zero2
	goto	loop
	
zero2  	bcf	PORTA,4		;output down (end of first half)
;
;	second half of a zero
;
    	
z2	btfss	INTCON,T0IF  	;miss a count
	goto	z2
	movlw	0xED		;reset TMR0
	movwf	TMR0
	bcf	INTCON,T0IF
	
z3	goto	nxt1
;
;	bit is a one	
;

one1	bsf	PORTA,4		;output up
	movlw	.5       	;one2
	goto	loop 
;
;	second half of a one
; 
one2	bcf	PORTA,4   	;output down 
	goto	nxt1
;
;	get next byte
;
	
nxtbyt	incf	FSR,1
	movlw	.9
	movwf	bitcnt
	movlw	.6 		;nxtby1
	goto	loop
nxtby1	bsf	PORTA,4		;output up
	decfsz	bytcnt,F	;last byte	
	goto	ploopa		;start bit of next byte

	return			;no more bytes in packet


;*************************************************************
;
;sends a passive preamble of 14 cycles
;arrive with output hi
;leaves with output hi  (ready for packet)


preamb	movlw	.14		;14 preamb cycles
	movwf	count4
	movlw	.7	     	;pr1
	goto	loop
pr1 	bcf	PORTA,4		;output down
	decfsz	count4,F
	goto	pr2
	movlw	.8        	;pr3
	goto	loop
pr3	bsf	PORTA,4      	;output up
	return            	;end of preamble

pr2	movlw	.9       	;pr4
	goto	loop
pr4	bsf	PORTA,4       	;output up
	movlw	.7		;pr1
	goto	loop 

;
;**********************************************************
;
;	sort out bit to change in accessory packet
;	depending on change of matrix switch
;

	

sort	btfsc	STATUS,Z	;arrives with w 0 or 1
	goto	lownib
	swapf	temp1,F
	movf	temp1,W
	movwf	temp2
	swapf	temp3,F		;was high nibble
	movf	adrcnt,W    	;set address
	addlw	1           	;odd address
	movwf	byte1
	goto	nibset
lownib	movf	adrcnt,W   	;set address
	movwf	byte1	

nibset	movlw	B'00001111'	;mask nibble
	andwf	temp2,F		;get difference
	movlw	3		;no of bits in nibble
	clrf	count4
	clrf	bitnum
	incf	bitnum,F	;starts with 1
	movlw	.12
	goto	loop		;wait
rollu	bsf	PORTA,4		;output up
roll	rrf	temp2,F		;which bit
	btfsc	STATUS,C
	goto	putbit
	rlf	bitnum,F
	incf	count4,F	
	incf	count4,F
	goto	roll
putbit	movlw	.13
	goto	loop
putd	bcf	PORTA,4		;output down
	movlw	table0		;reset index
	addwf	colcnt,W
	movwf	FSR
	movf	temp3,W		;get new row data
	andlw	B'00001111'	;mask
	andwf	bitnum,W	;what is new bit
	btfsc	STATUS,Z	;is it a 1
	goto	azero
	movf	count4,W
	addlw 	1		;odd number
	movwf	byte2
	bcf	STATUS,Z
	return
azero	movf	count4,W
	movwf	byte2
	bsf	STATUS,Z
	return	
;
;************************************************************
;
;
;	set1 sets new bit in table and then sends
;	a deactivate packet to opposite of a pair,
;	sends an intermediate idle and then an
;	activate packet to the required output


set1	btfsc	STATUS,Z	;skip if data a zero
	goto	chzero
	movf	bitnum,W	;get bit to change
	iorwf	INDF,F		;put in table
	goto	setadr
chzero	comf	bitnum,W	;get bit to change
	andwf	INDF,F		;put in table	



setadr	bsf	byte1,7
	bcf	byte1,6		;accessory address
	movf	byte1,W
	movwf	adrtmp		;save
	movlw	B'11110000'
	iorwf	byte2,W		;accessory deactivate
	movwf	byttmp
	movlw	.14
	goto	loop
set2	bsf	PORTA,4		;output up
	movlw	B'00000001'
	xorwf	byttmp,W
	movwf	byte2
	call	preamb
    	movlw	3
    	movwf	bytcnt		
	call	packet		;send deactivate previous
	movlw	1
	movwf	count0		;one idle packet
	call	idle
	movf	adrtmp,W	;recover address
	movwf	byte1
	movf	byttmp,W	;recover original activate
	movwf	byte2
	bsf	byte2,3		;activate
	call	preamb
    	movlw	3
    	movwf	bytcnt		
	call	packet		;send change
	
last	return
                

	end
