; WRITTEN BY 		G.C.Hartnup
; Copyright Reserved	G.C.Hartnup
; DATE	June 9 2020
;*********************** 
;****CURRENT VERSION**** issue 1.0  9/6/20
;***********************
;		 	
; FILE SAVED AS 	COACH.ASM
;                   SIMILAR TO PL.ASM
; FOR 		PIC 12f629
	list	p=12f629
	errorlevel -302	;omit memory bank warning messages
; 4.00 MHz Internal Clock 
; INSTRUCTION CLOCK 	1.00 MHz,  Tick = 1uS 
;
; FUNCTION	DCC control coach lights
;		Programmed as accessory decoder
;		Links on the board select o/p channel
;		program provides 4 outputs
;		strict startbit and checksum required
;		owing to track noise on dcc data
;-----------
;---Ports---
;-----------
;these may be changed to suit board layout
#define	_in	GPIO,5		; dcc input 
#define	_ch0	GPIO,1		; output ch0
#define	_ch1	GPIO,2		; output ch1
#define	_ch2	GPIO,0		; output ch2
#define	_ch3	GPIO,4		; output ch3
;so	ports 0, 1, 2, 4 are o/p,   ports 3, 5 are i/p
_tris	equ	0x28		;i/o port direction setting
;
;---------------
;---Variables---
;---------------
cnt	equ	0x30		;bit counter
cnt2	equ	0x31		;counter
address	equ	0x33		;received address
_data	equ	0x34		;received data
byte	equ	0x35		;value returned by getbyte
instr	equ	0x36		;received instruction
errb	equ	0x37		;received error byte
_temp	equ	0x38
CC	equ	0x39		;arith routines data
DD	equ	0x3a
EE	equ	0x3b
FF	equ	0x3c
v	equ	0x3d		;received state
myaddr	equ	0x3e		;decoder page address
posn	equ	0x3f
;
;----------
;---data---
;----------
blo	equ	0xdf		;frame pulse length
bhi	equ	0xb1
;
; include file with Chip Details and Mnemonic Equates
        include "p12F629.inc"	
	list 	mm = OFF, st=OFF	;some warning messages suppressed
;
;------------------------
;--- Set Configuration---
;------------------------
; Code protection off
; Watchdog timer on
; PWRTE Power Restart enable
; Internal Oscillator
; Master Clear OFF (Makes Pin 3 available, MCLR tied to Vdd)
	__CONFIG   _CP_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT  & _WDT_OFF & _MCLRE_OFF
;
;-------------------
;---Program Start---
;-------------------
	org 	0
start	bcf     STATUS,5      ; set up Memory Bank 0
	goto	init
	org	4
;----------------------		
;---Library Routines---
;----------------------
;read EEROM
; on entry w holds EERAM address
; on exit w holds data
RRAM	bsf	STATUS,5     	; set up Memory Bank 1
	bcf	EECON1,WREN
	movwf	EEADR           	; set address
	bsf	EECON1,RD       	; do a read
	movf	EEDATA,w       	; data to w
	bcf	STATUS,5  	; set up Memory Bank 0
	return			
;Write EEROM
; on entry 	CC contains EERAM address
;		DD contains data to write
WRAM	movf	CC,w
	bsf   	STATUS,5     	; set up memory Bank 1
	movwf	EEADR	   	;where
	bcf	STATUS,5     	; set up Memory Bank 0
	movf	DD,w	   	;value to be stored
	bsf   	STATUS,5     	; set up memory Bank 1 
	movwf	EEDATA
	bsf	EECON1,WREN
	movlw   	0x55             ;do write sequence
	movwf   	EECON2          ;       /
	movlw   	0xaa            ;      / 
	movwf   	EECON2          ;     /  
	bsf     	EECON1,WR       ;initiate write 
	btfsc	EECON1,WR		;loop until write completed
	goto	$-1
	bcf	STATUS,5		;set up memory bank 0
	return
;-----------------		
;---Subroutines---
;-----------------
;
;----------------------
;---get pulse length---
;----------------------
;measure from low edge to next low edge
;returns 1 or 0 in w
;if timer1 overflows, dcc controller has probably tripped
; _in is the input port
getbit	clrwdt
	btfsc	_in
	goto	getbit
pb1	clrwdt
	btfss	_in		;is it lo?
	goto	pb1
	clrf	TMR1L		;zero value of timer       
	clrf	TMR1H		;clear msb
	bsf     T1CON,TMR1ON    ;start timer 1
pb2	clrwdt
	btfsc	_in
	goto	pb2
        	bcf     T1CON,TMR1ON    ;stop Timer 1
	movf	TMR1L,w		;get lo byte
	movwf	CC
	decf	CC,f		;less 1
	movf	TMR1H,w		;get hi byte
	movwf	DD
;determine if pulse is a one or a zero
;returns 1 or 0 in w
	bnz	d1	;far too long
	movlw	0x4d	;less than 77 microseconds
	subwf	CC,w
	bc	d1	;it is a nought
	movlw	1	;it is a one
	return
d1	clrw	;report nought
	return
;get 8 bits from the data stream, returned in byte 
getbyte	clrwdt
	clrf	byte
	bcf	PIR1,0	;clear overflow flag
	movlw	0xf8
	movwf	cnt	;to count bits
gb1	call	getbit	;get bit from dcc stream
	movwf	FF
	rrf	FF,f	;bit into C
	rlf	byte,f	;hence into byte
	incfsz	cnt,f	;test for 8 bits
	goto 	gb1
	return
;--------------------------------
;---- RESET & INITIALISATION ----
;--------------------------------
;this section configures the PIC
init	bcf	STATUS,5 		; set up Memory Bank 0
	btfss	STATUS,4		;was it wdt restart?
	goto	bp		;yes, don't change anything, stop servos resetting
	clrf	INTCON		;inhibit interrupts
	movlw	0x07
	movwf	CMCON		;turn off comparator
	clrwdt
	bsf	STATUS,5   	; set up memory Bank 1
;	call 	3ffh		;calibrate internal clock
;	movwf	OSCCAL
	movlw	0x8f
	movwf	OPTION_REG	;set watchdog and pullups
	movlw	_tris		;set port directions
	movwf	TRISIO
	movwf	WPU
	bcf	STATUS,5   	; set up Memory Bank 0
	movlw	0x01
	movwf	T1CON		;setup timer 1
	movlw	0		;EERAM relative address
	call	RRAM		;read EERAM
	movwf	myaddr
;------------------------
;---Initialise Signals---
	clrf	posn	;set memory pointer to 0
	bcf	_ch0	;lights off
	bcf	_ch1
	bcf	_ch2
	bcf	_ch3
;now start up
;-----------------------
;---look for Preamble---
;-----------------------
;re-enter here after wdt timeout
;look for preamble, >10 successive "1"s
bp	clrf 	cnt
p1	clrwdt
	bcf	PIR1,0	;clear overflow flag
	call 	getbit	;result returned in w
	iorlw	0	;test for "1"
	bz	bp	;it wasn't a "1",start again
	incf	cnt,f	;increment counter
	movlw	0x0a	;ten
	subwf	cnt,w
	bnz	p1	;less than 10, not valid yet
;valid preamble, continue counting
pb16	call	getbit
	incf	cnt,f	;increment counter
	iorlw	0	;look for end
	bnz	pb16
;test preamble length > 36 bits (service mode)
	movlw	0x24
	subwf	cnt,w
	bc	q2		;service mode detected	
;-----------------------
;--- Get Data Bytes ---
;-----------------------
;we have already received start bit so get a data byte
p2	call	getbyte
;now look for a zero in next startbit
	call	getbit
	iorlw	0
	bnz	bp	;failed start again
	movf	byte,w
	movwf	errb
	andlw	0x7f	;remove bit 7
	movwf	address
;get data
	call 	getbyte	;result returned in byte
	movf	byte,w
	xorwf	errb,f
;look for a zero in next startbit
	call	getbit	;returned in w
	iorlw	0
	bnz	bp	;failed start again
	movlw	7
	andwf	byte,w	;mask off unwanted bits
	movwf	_data
;get error byte and test
	call 	getbyte	;result returned in byte
;look for a one in startbit	(end of packet)
	call	getbit	;returned in w
	xorlw	1
	bnz	bp	;failed, don't bother
	movf	byte,w
	xorwf	errb,f
	bnz	bp
;check page address
	movf	myaddr,w
	subwf	address,w	;compare with device page number
	bnz	bp	;fail, so round again
;process instruction
;we have valid data, so check it out
;on or off?
	movlw	0x01
	andwf	_data,w	;LSB contains state, "1" or "0"
	movwf	v	;put state into v
;now find which channel
	rrf	_data,f	;dump lsb into carry
	movlw	0x03	;mask sub ch address bits (lowest 2 bits)
	andwf	_data,f	;sub channel address now in _data
	bz	nort	;else it is channel 1, 2 or 3
;channel 1, 2 or 3 ?
	movlw	1		;if using more channels
	xorwf	_data,w		;mask above set to 3
	bz	wun
	movlw	2
	xorwf	_data,w
	bz	too
	movlw	3
	xorwf	_data,w
	bz	tree
	goto	bp	;covers non valid channel glitch
;channel 0
nort	bsf	_ch0	;on
	btfsc	v,0
	bcf	_ch0	;off
	goto	bp
;channel 1
wun	bsf	_ch1	;on
	btfsc	v,0
	bcf	_ch1	;off
	goto	bp
;channel 2
too	bsf	_ch2	;on
	btfsc	v,0
	bcf	_ch2	;off
	goto	bp
;channel 3
tree	bsf	_ch3	;on
	btfsc	v,0
	bcf	_ch3	;off
	goto	bp	
;------------------
;---Service Mode---
;------------------	
;we have already received start bit so get a data byte
q2	call	getbyte
	movf	byte,w
	movwf	_data	;
	movwf	errb
;now look for a zero in next startbit
	call	getbit
	iorlw	0
	bnz	bp	;failed, start again (exit service mode)
;get data
q3	call 	getbyte	;result returned in byte
;look for a zero in next startbit
	call	getbit	;returned in w
	iorlw	0
	bnz	bp	;failed, start again 
	movf	byte,w
	movwf	address
	xorwf	errb,f
;get error byte and test
	call 	getbyte	;result returned in byte
;look for a one in startbit	(end of packet)
	call	getbit	;returned in w
; test error byte  (not really needed)
	xorlw	1
	bnz	bp	;failed, start again
	movf	byte,w
	xorwf	errb,f
	bnz	bp	;failed, start again
; read  instruction
	movf	_data,w
	andlw	0xfc	;strip lowest 2 bits
	xorlw	0x7c
	bnz	bp	;not write cv
;now put data into EERAM
	movf	address,w
	bz	bp	;exit, not valid
	movwf	DD	;data to write
	clrf	CC	;eeram address zero
	call	WRAM
	goto	bp	;exit
;-----------------------
;---Reserve Copyright---
;-----------------------	
	da	"Copyright Reserved, G.C.Hartnup: June 2020"
	da	"Issue 1.0  9/6/20"
;-----------
;---EERAM---
;-----------
	org	2100H
	de	2	;cv1 =  my address, page 2
			;port addresses 5, 6, 7, 8
	end
