;************************************************************************
; TaPRK IR-LINK-RECEIVER (PC-ELIMINATOR;) VER 2004-01-18
;************************************************************************

	LIST P = 16F84A,  R = HEX		;Prosessortype, radix
	INCLUDE	"p16f84A.inc"			;w,f,porta,portb etc. 
	ERRORLEVEL -224, -302			;prevents error from
						;"tris"-command
        __CONFIG _PWRTE_ON & _HS_OSC & _WDT_OFF   

;************************************************************************
; SHEMATIC DIAGRAM
;************************************************************************

;	          		  +---+_+---+
;		TMW-RL1	PORTA.2 <-|1      18|-> PORTA.1 TMW_Strobe
;		TMW-RL2	PORTA.3 <-|2      17|<- PORTA.0	TSOP1738 act.lo
;		TMW-ACK	PORTA.4 ->|3      16|-- XTAL
;			    Vdd --|4      15|-- XTAL
;			Vss(GND)--|5      14|-- Vdd (+5V)
;	TMW_REQ		PORTB.0 ->|6/INT  13|-> PORTB.7	LED
;	Strobe_Disp	PORTB.1 <-|7      12|<- PORTB.6	N/C
;	Comm.Ser.data	PORTB.2 <-|8      11|<- PORTB.5	N/C
;	Comm.Ser.clock	PORTB.3 <-|9      10|<- PORTB.4	LED1
;		    	          +---------+

;************************************************************************
; MAIN IDEA
;************************************************************************
;
;	RC5: 
;		based on Holger Klabunde's design
;	Binary to BCD: 
;		directly from Microchip Embedded control Handbook
;	Sending data to TMW: 
;		based on M.Brandt's idea
;	Clearing memory:
;		J. Niinikoski (based on PIC16F84 data sheet)
;	Timing
;	======
;	26us cycle of IR-LED on/off gives 38,5kHz
;	16 cycles for 1/2 bit -> 16 x 26us= 416 us
;	Prescaler of 1:8 makes TMR0 advance every 8us (@4MHz crystal)
;	1/2 bit needs 416us / 8 = 52 ticks of TMR0
;	1/4 bit needs  52 ticks / 2 = 26 ticks of TMR0
;
;	This is loosely following Philips RC5, exept:
;
;	-- Bit length 2 X 416us!
;	-- Only one start bit, and it is "0"
;	-- Only 10 data bits:
;		- 2 for transmitter ID and
;		- 8 for payload
;	-- Added parity bit
;	-- Total of 11 bits
;
;	This is presumably the same as Lorell Joiner's
;	setup, done with hardwired UART some 10 years ago, but instead
;	of using 250 kHz carrier and on/off bits, I use the 
;	Bit formation same as RC5 at 36...38 kHz carrier:
;
;	+---+                 +---+
;	|   |     =0          |   |    =1
;       +   +---+         +---+   +
;
;	TSOP1738 receiver module inverts the above
;
;	Serial output to 7-segment display and LPT-connector to
;	be connected to TMWDCC hardware (PC-eliminator!)
;
;	4094 serial-in-parallel-out will be used. Two branches for 
;	address/func/speed step display and other to mimic
;	LPT-port for TMW-hardware. Both branches may use
;	common data and clock, strobe sent only to correct branch.
;
;	To operate TMW-DCC hardware directly the following assumptions
;	are made:
;	The IR-transmitters shall send three types of packets;
;
;		(1) DCC Address byte packets
;		(2) DCC Speed byte packets
;		(3) DCC Functions group 1 packets
;
;	These will be stored in respective registers within PIC
;	The output of this device will be 8 bit parallel data
;	suitable to mimic the LPT output connected to TMW-hardware.
;	The output will always be 8 bits wide, and the DCC packet
;	therefore needs to be broken into 8 bit sections including
;	preamble and byte separation bits following the idea of
;	M.Brandt of the DOS TSR driver:
;
;	byte0:	PPPPPPPP	Preamble 8 first bits
;	byte1:	PPPPPPPP	Preamble 8 next bits
;	byte2:	PPPPP0AA	Preamble 5 bits, sep, 2 addr.bits
;	byte3:	AAAAAA0D	6 addr. bits, sep 1 data bit
;	byte4:	DDDDDDD0	7 data bits, sep
;	byte5:	EEEEEEEE	Error byte
;
;	This gives correct preamble according to old NMRA specs!
;	(The 2002 version requests command stations to send at least
;	14 preamble bits, so a byte0: PPPPPPPP is added later)
;
;	The above 6 bytes must be sent in sequence when demanded by
;	TMW-DCC hardware. The time allowed to deliver the byte is 
;	close to the time the previous byte took (900us minimum)
;	This means that the request signal needs to be polled at minimum
;	900us frequency. If polled at half IR bit frequency (416 us) 
;	we are in safe waters! 
;
;	As the Request signal is low only briefly (active low) 
;	the pulse is best recognized from the interrupt flag of 
;	RB0, the INTF flag of INTCON.
;
;	Note, that there is no need for actual interrupt
;	nor any interrupt handler: the INTF (Interrupt Flag)
;	will be set irrespective of interrupts being generally
;	allowed or not! The INTF must be made to set on falling
;	edge of RB0, thus INTEDG (interrupt Edge Select) must 
;	be "0"
;
;	As an added task it would be benfical to have the code also
;	set the unused loco address speed settings to zero.
;	This becomes the 5th cab. (not implemented yet!
;	The system will send all speed packets followed by
;	all Function packets and return. This may be too much for the 
;	decoders, so propably the frequency of function packets will
;	be reduced into one out of four...
;
;	=============================================================
;	The IR transmitters will send the packets as follows:
;
;	ADDRESS:	CC0AAAAAAAP	CC	=transmitter code
;					A..A	=address
;					P	=packet parity
;
;	[Strip transmitter number and parity bits to get the 
;	proper NMRA-DCC 1..127 address data byte "0AAAAAAA"]
;	-------------------------------------------------------------
;	14/28SPEED:	CC11DsSSSSP	CC	=transmitter code
;					D	=directon bit		
;					s	=F0 / LSB speed
;					S	=speed bits
;					P	=packet parity
;
;	[Strip transmitter number and parity bits and replace first 
;	1 w. 0 to get the proper NMRA-DCC 14/28 speed step speed 
;	instruction data byte "01DsSSSS"]
;	-------------------------------------------------------------
;	GROUP 1 FUNC:	CC10MFFFFFP	CC	=transmitter code
;					M	=14/28 step Mode
;					F	=function status bits		
;					P	=packet parity
;
;	[Strip transmitter number and parity bits and clear M
;	to get the proper NMRA-DCC Multi-Function Group 1 instruction
;	data byte "100FFFFF". 14/28 mode is sent separately here to make 
;	sure speed=0  is interpreted correctly: address change should 
;	only allowed if speed is zero!]
;	-------------------------------------------------------------
;
;	This means that the IR transmitters will send 11 bit messages:
;	two bits for IR throttle identification, 8 bits for payload
;	and one bit parity. The RC5-type code in itself contributes
;	much of the intregity check of the data, so 1 bit seems to
;	be enough!
;
;	The system must also be capable of generating NMRA-DCC
;	IDLE and Reset packets at startup! 
;
;	The above mentioned IR-packet's payload's are stored as is
;	in resp. registers and the modified bits are cleared only
;	at the time the byte is to be sent. This way the 14/28 mode
;	may be stored in Func byte ;)
;					
;	==============================================================
;	5-PIN STEREO DIN DISP. UNIT 
;	--------------------------------------------------------------
;		1 CK	 Orange  [Biltema
;		4 GND	 Green	  keyboard
;		2 DATA	 Blue	  extension
;		5 +5V	 White	  cord
;		3 STROBE Black	  colours]

;	==============================================================
;	3.5mm STEREO IR-MODULE CONNECTOR 
;	--------------------------------------------------------------
;		Tip	Data
;		Ring	+
;		Sleeve	GND
;************************************************************************
; UPDATES AND BUGFIXES
;************************************************************************
; [2003-11-19] Added constant display of address, speed, and functions
;              in case no new packets from IR remotes for 2 seconds
; [2004-01-08] Found error in speed display routines possibly causing
;              stack overflows and probably the reason of occasionally
;              loosing control of locos!
;************************************************************************
; STATICS
;************************************************************************

	#DEFINE TSOP		PORTA, 0 	;IR receiver
	#DEFINE TMW_REQ		PORTB, 0 	;TMW-REQUEST

	#DEFINE TMW_STROBE	PORTA, 1 	;TMW data Strobe out
	#DEFINE S_STROBE	PORTB, 1 	;7-SEG Strobe
	#DEFINE S_DATA		PORTB, 2 	;7-SEG Data
	#DEFINE S_CLOCK		PORTB, 3 	;7-SEG Clock

	#DEFINE RELAY1		PORTA, 2
	#DEFINE RELAY2		PORTA, 3
	#DEFINE ACK		PORTA, 4
	
	#DEFINE	LED1		PORTB, 7	;Pilot light
	#DEFINE HALFBIT		.203 		;= 255-52
	#DEFINE QUADBIT		.229		;= 255-26

	;Need to add the relay control and emg. button input!
	;This will never handle programming!

;************************************************************************
; VARIABLES
;************************************************************************

	CBLOCK H'0C'
		Data1		;Data from IR-receiver
		Data2		;waiting for conversion to 7-seg...
		Parity		;Parity
		Err		;error flag from ReadBit
		bitcount	;bit counter

		count_gp	;freewheeling counter
		blink		;blink on/off

		temp		;long call assist

		expay_0		;previous data packet
		speed_0		;cab 0 speed (4 bits)
		func_0		;cab 0 functions
		addr_0		;cab 0 address

		expay_1		;previous data packet
		speed_1		;cab 1 speed (4 bits)
		func_1		;cab 1 functions
		addr_1		;cab 1 address

		expay_2		;previous data packet
		speed_2		;cab 2 speed (4 bits)
		func_2		;cab 2 functions
		addr_2		;cab 2 address

		expay_3		;previous data packet
		speed_3		;cab 3 speed (4 bits)
		func_3		;cab 3 functions
		addr_3		;cab 3 address

		;------------------------------------------------
		; NOTE the speed, func,and address bytes must
		; be kept in above mentioned order as the code
		; may access those by just adjusting FSR up/down!
		;------------------------------------------------

		tmw_0		;TMW data bytes containing
		tmw_1		;the actual NMRA-DCC
		tmw_2		;packets incorporating
		tmw_3		;Preamble, Address, Data
		tmw_4		;Error bytes and byte
		tmw_5		;separation bits
		tmw_bytecount	;bytecount
		cab_sel		;cab to be served out
		s_BITCOUNT	;shifting out bit counter
		s_DATA1		;shift out data
		power_on	;dcc-initializin packet count
		IR_ADDRESS	;IR-transmitter ID
		IR_DATA		;IR-transmitted payload
		MSD		;temp for bin-BCD-conversion
		LSD		;temp for bin-BCD-conversion
		idle1		;idle precounter
		idle2		;idle counter for intermediate add disp.
		idle_cab	;the cab to display while idle
	ENDC

;************************************************************************
;RESTART/INT-START
;************************************************************************

	ORG     0		; Restart vector
	goto	main

	ORG	4		; INT vector
	retfie

;------------------------------------------------------------------------
; Initialize I/O ports, Interrupts, Timers etc
;------------------------------------------------------------------------
init


	movlw	b'11100001'	;RB0/INT=in, 1..7=out
	tris	portb		;

	movlw	b'00000001'	;RA0=in, RA1..4=out
	tris	porta		;

				;easy to recall:
				;Input -> I -> 1
				;Output-> O -> 0

	clrf	porta		;clear Port A (Shift reg output)
	clrf	portb		;Port B on (LED test)

	CLRWDT			;
	bsf	STATUS, RP0	;
	movlw	b'01000010'	;PullUp=on, INTEDG=hi, PreScaler= 1:8
	movwf	OPTION_REG	;
	bcf	STATUS, RP0	;

ramc	movlw	0x0c		; start of 16F84 ram
	movwf	fsr
ramc1	clrf	indf		; clear indirect location
	incf	fsr, f		; next location
	btfss	fsr, 7		; cleans up to 0x7f
	goto	ramc1		; [From J.Niinikoski]

ramdone
	movlw	.30		;NMRA-DCC system's
	movwf	power_on	;initilaising packets

	movfw	DATA1		;clear w

	movlw	b'11111111'	;preamble
	call	so_byte		;to track
	bsf	tmw_strobe	;strobe out
	nop
	bcf	tmw_strobe	;done strobing

	movlw	b'10001100'	; E
	call	so_byte		;
	movlw	b'11101111'	; -
	call	so_byte		;
	movlw	b'10011100'	; C
	call	so_byte		;
	movlw	b'00001110'	; P
	call	so_byte		;
	bsf	S_STROBE	;strobe out (display "PC-E")
	nop			;wait for slow C-MOS
	bcf	S_STROBE	;strobing done
	
	bsf	RELAY1		;switch on power to rails

	return

;------------------------------------------------------------------------
; Binary to BCD [Microchip: Embedded control Handbook, maths]
;------------------------------------------------------------------------

bin_to_BCD
	clrf	MSD
	movwf	LSD
gtenth	movlw	.10
	subwf	LSD, w
	skpc
	return
	movwf	LSD
	incf	MSD, f
	goto	gtenth

;------------------------------------------------------------------------
; Read whole bit, set/clear error reset TMR0, count parity
;------------------------------------------------------------------------

ReadBit
	movlw	0xFF		;Set error state,
	movwf	ERR		;will be cleared if bit correctly read

	call	tmw_request	;TMW-DCC requesting new data?

rb1	btfss	INTCON, T0IF	;wait for timer to overflow
	goto 	rb1		;not yet

	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	btfsc	TSOP		;test 1st halfbit state
	goto 	read1		;was high -> beginning of "1"

read0
	call	tmw_request	;TMW-DCC requesting new data?

r0	btfss	INTCON, T0IF	;wait for timer to overflow
	goto 	r0		;not yet

	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	call	tmw_request	;TMW-DCC requesting new data?

	btfss	TSOP		;test 2nd halfbit, must be high (set)
	return			;was not, error, return

	bcf	STATUS, C	;have read a "0"
	rlf	DATA1, f	;move it into DATA1
	clrf	ERR		;clear error state

	call	tmw_request	;TMW-DCC requesting new data?

	return

read1
	call	tmw_request	;TMW-DCC requesting new data?

r1	btfss	INTCON, T0IF	;wait for timer to overflow
	goto 	r1		;not yet

	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	btfsc	TSOP		;test 2nd halfbit, must be low (clear)
	return			;was not, error, return

	bsf	STATUS, C	;have read a "1"
	rlf	DATA1, f	;move it into DATA1
	incf	Parity, f	;add 1 to parity
	clrf	ERR		;clear error state

	call	tmw_request	;TMW-DCC requesting new data?

	return


;------------------------------------------------------------;
; MUX out TMW-DATA                                           ;
;------------------------------------------------------------;
tmw_request

	;----------------------------------------------------;
	;bytes needs to be sent always when the TMW-hardwre
	;requests. If there is no request then return!
	;----------------------------------------------------;

	btfss	INTCON, INTF	;Has TMW-hw requested data?
	return			;no, return

	;----------------------------------------------------;
	;Bytes are sent in lots of 6. In case the 
	;tmw-bytecount is non-zero the sending of bytes of   
	;present packet (whatever it is) is still pending
	;and must be finished until tmw-bytecount is zero.
	;Jump directly to sending the byte!
	;----------------------------------------------------;

	tstf	tmw_bytecount	;Still pending packet bytes
	skpz			;
	goto	send_packet	;Yes, go and send it

	;----------------------------------------------------;
	;Power-on routine needs to send at least 20 RESET and;
	;10 IDLE packets. In case the power-on counter is
	;zero, then we can go further, but else these packets
	;need to be sent directly. Load the packets and jump
	;to sending byte!
	;----------------------------------------------------;

	tstf	power_on	;Test if all power_on packets
	skpnz			;are sent
	goto	tmw_cab_sel	;yes, lets load the cabs instead
	decf	power_on, f	;decrement power_on-counter
	movlw	.11		;no. Lets see if RESET's are sent
	subwf	power_on, w	;by subtracting 11 from power_on
	skpc			;no carry if power_up<=10
	goto	load_idle	;carry set, time for IDLEs
	goto	load_reset	;no carry goto sending RESETS 
	
load_idle
	;----------------------------------------------------------;
	; IDLE	ADDR:	11111111                                   ;
	;	DATA:	00000000                                   ;
	;	ERR:	11111111                                   ;
	;----------------------------------------------------------;

	movlw	b'11111111'	;preamble beginnings
	movwf	tmw_0		;preamble fills tmw_0
	movwf	tmw_1		;and tmw_1
	movlw	b'11111011'	;rest of pre, sep, and start of address
	movwf	tmw_2		;to tmw_2
	movlw	b'11111100'	;rest of address, sep, and start of data
	movwf	tmw_3		;to tmw_3
	movlw	b'00000000'	;rest of data and sep
	movwf	tmw_4		;to tmw_4
	movlw	b'11111111'	;error byte
	movwf	tmw_5		;to tmw_5

	movlw	.6		;six bytes to a packet
	movwf	tmw_bytecount	;

	goto	send_packet	;and out with it!

load_reset
	;----------------------------------------------------------;
	; RESET	ADDR:	00000000                                   ;
	;	DATA:	00000000                                   ;
	;	ERR:	00000000                                   ;
	;----------------------------------------------------------;

	movlw	b'11111111'	;preamble beginnings
	movwf	tmw_0		;preamble fills tmw_0
	movwf	tmw_1		;and tmw_1
	movlw	b'11111000'	;rest of pre, sep, and start of address
	movwf	tmw_2		;to tmw_2
	movlw	b'00000000'	;rest of address, sep, and start of data
	movwf	tmw_3		;to tmw_3
	movwf	tmw_4		;and rest of data and sep to tmw_4
	movwf	tmw_5		;and error to tmw_5

	movlw	.6		;six bytes to a packet
	movwf	tmw_bytecount	;

	goto	send_packet	;and out with it!

tmw_cab_sel
	;----------------------------------------------------;
	;If we get here, it means that power-up is done and 
	;all the bytes of pending packet are sent!
	;----------------------------------------------------;
	;The cab counter runs from 8 to 0 (twice the number
	;of cabs) if the counters top bit is high a speed 
	;packet needs to be sent, else functions packet is 
	;to be sent.
	;First it must be checked if the cab counter is zero.
	;if so, it must start again from 8 
	;else the counter is decremented
	;----------------------------------------------------;

	decf	cab_sel,w	;change the number
	andlw	b'00000111'	;clear top
	movwf	cab_sel		;and return	

	;----------------------------------------------------;
	;Now the bit 2 needs to be examined:
	;1=speed, 0=function
	;----------------------------------------------------;

	btfss	cab_sel, 2	;
	goto 	send_func	;

send_speed
	;----------------------------------------------------;
	;Speed branch!
	;----------------------------------------------------;
	;The two LSB of cab counter will determine which
	;addres byte is loaded for transferring into tmw_x
	;If address byte is zero, load idle packet instead! 
	;If speed byte is zero, load idle packet instead! 
	;
	; NOTE: this routine must clear the MSB of speed_x
	; prior sending
	;----------------------------------------------------;

	movfw	cab_sel		;get the cab select
	andlw	b'00000011'	;clear top to get cab number
	call	lookup_IR_address
	movwf	FSR		;get INDF point to address
	tstf	INDF		;is it zero?
	skpnz			;
	goto	load_idle	;yes, skip this cab!	

	movlw	b'11111111'	;preamble beginnings
	movwf	tmw_0		;fills tmw_0: 'PPPPPPPP'
	movwf	tmw_1		;and tmw_1:'PPPPPPPP'
	movlw	b'11111000'	;rest of pre, sep, and start of address
	movwf	tmw_2		;to tmw_2: 'PPPPP0AA'
	btfsc	INDF, 7		;check address high end bytes
	bsf	tmw_2, 1	;in case set change resp. bit
	btfsc	INDF, 6		;check address high end bytes
	bsf	tmw_2, 0	;in case set change resp. bit
	movfw	INDF		;
	movwf	tmw_3		;
	movwf	tmw_5		;move to err-byte also!
	rlf	tmw_3, f	;
	rlf	tmw_3, f	;
	bcf	tmw_3, 0	;
	bcf	tmw_3, 1	;tmw_3: 'AAAAAA0D' 

	movfw	cab_sel		;get the cab select
	andlw	b'00000011'	;clear top to get cab number
	call	lookup_IR_speed	;
	movwf	FSR		;get INDF point to speed byte
	tstf	INDF		;is it zero? (This is tested
	skpnz			;in case no speed has ever set!)
	goto	load_idle	;yes, skip this cab!	

	movfw	INDF		;
	andlw	b'01111111'	;clear top bit (IR flag!)
	movwf	tmw_4		;tmw_4 now has NMRA speed byte
	xorwf	tmw_5, f	;set xor of w(speed) and tmw_5(addr)
	rlf	tmw_4, f	;tmw_4= 'DDDDDDD0'
	bcf	tmw_4, 0	;and clear lsb (if carry was set!)
	

	movlw	.6		;six bytes to a packet
	movwf	tmw_bytecount	;

	goto	send_packet	;and out with it!

send_func
	;----------------------------------------------------;
	;Func branch!
	;----------------------------------------------------;
	;The two LSB of cab counter will determine which
	;address byte is loaded for transferring into tmw_x
	;If address byte is zero, load idle packet instead!
	;If func byte is zero, load idle packet instead!
	;
	; NOTE: this routine must clear bit 5 of func_x
	; prior sending
	;----------------------------------------------------;

	movfw	cab_sel		;get the cab select
	andlw	b'00000011'	;clear top to get cab number
	call	lookup_IR_address
	movwf	FSR		;get INDF point to address
	tstf	INDF		;is it zero?
	skpnz			;
	goto	load_idle	;yes, skip this cab!	

	movlw	b'11111111'	;preamble beginnings
	movwf	tmw_0		;fills tmw_0: 'PPPPPPPP'
	movwf	tmw_1		;and tmw_1:'PPPPPPPP'
	movlw	b'11111000'	;rest of pre, sep, and start of address
	movwf	tmw_2		;to tmw_2: 'PPPPP0AA'
	btfsc	INDF, 7		;check address high end bytes
	bsf	tmw_2, 1	;in case set change resp. bit
	btfsc	INDF, 6		;check address high end bytes
	bsf	tmw_2, 0	;in case set change resp. bit
	movfw	INDF		;
	movwf	tmw_3		;
	movwf	tmw_5		;move to err-byte also!
	rlf	tmw_3, f	;
	rlf	tmw_3, f	;
	bsf	tmw_3, 0	;NMRA G 1 func MSB =always set
	bcf	tmw_3, 1	;tmw_3: 'AAAAAA0D' 

	movfw	cab_sel		;get the cab select
	andlw	b'00000011'	;clear top to get cab number
	call	lookup_IR_func	;
	movwf	FSR		;get INDF point to func byte
	tstf	INDF		;is it zero? (This is tested
	skpnz			;in case no func's has ever set!)
	goto	load_idle	;yes, skip this cab!	

	movfw	INDF		;w = IR func byte
	andlw	b'11011111'	;clear IR 14/28 flag!
	movwf	tmw_4		;tmw_4= NMRA G1 func byte
	xorwf	tmw_5, f	;set xor of w(func) and tmw_5(addr)
	rlf	tmw_4, f	;tmw_4= 'DDDDDDD0'
	bcf	tmw_4, 0	;and clear lsb (if carry was set!)
	
	movlw	.6		;six bytes to a packet
	movwf	tmw_bytecount	;

	goto	send_packet	;and out with it!

send_packet
	;----------------------------------------------------;
	;Sending byte
	;decrement bytecount and so_byte and 
	;clear INTF and strobe out
	;----------------------------------------------------;
	decf	tmw_bytecount,w	;decrement bytecount
	movwf	tmw_bytecount	;and save it
	call	lookup_tmw_byte	;get the byte to be sent
	movwf	FSR		;move to FTR to get
	movfw	INDF		;correct data to
	call	so_byte		;shift out the byte
	bsf	tmw_strobe	;strobe out
	nop
	bcf	tmw_strobe	;done strobing
	bcf	INTCON, INTF	;clear the request flag
	return	

	;----------------------------------------------------;
	;DONE!
	;----------------------------------------------------;

;-----------------------------------------------------------------------
;shift out one byte from w (uses S_DATA1 and S_BITCOUNT)
;-----------------------------------------------------------------------

so_byte
	movwf	S_DATA1		;put w put out for shifting
	movlw	.8		;all 8 bits
	movwf	S_BITCOUNT	;

sob1	rlf	S_DATA1, F	;rotate through carry (MSB first) and 
	btfsc	STATUS, C	;in case of carry shift out 1 
	goto	so1		;carry=1
so0				;carry=0

	bcf	S_DATA		;data  lo
	bsf	S_CLOCK		;clock hi
	nop			;wait for slow C-MOS
	bcf	S_CLOCK		;clock lo

	goto	sob2
so1

	bsf	S_DATA		;data  hi
	bsf	S_CLOCK		;clock hi
	nop			;wait for slow C-MOS
	bcf	S_CLOCK		;clock lo

sob2	decfsz	S_BITCOUNT, F	;all bits sent?
	goto 	sob1		;not yet, shift out more
	return			;yes, this byte shifted out
	
;------------------------------------------------------------------------
; MAIN PROGRAMME
;------------------------------------------------------------------------
main
	call 	init		;intialize ports
m1
	;----------------------------------------------------------------
	;First the TSOP is waited for a sign, and if seen, will jump to
	;trying to make sense of it, else will loop here, and just check
	;often enough if TMW requests data
	;----------------------------------------------------------------

	btfss	TSOP		;wait for beginning of the broadcast
	goto 	m2		;found, start interpreting

	btfss	INTCON, T0IF	;No -- Has timer overflowed?
	goto 	m1		;Not yet

	movlw	HALFBIT		;Yes -- new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	call	tmw_request	;TMW-DCC requesting new data?

	;----------------------------------------------------
	;Idle 1 -> idle 2 is used for displaying the addresses
	;of cabs if no IR commands have arrived recently (2 sec).
	;This is for tracking the propable error of system
	;loosing cab addresses!
	;----------------------------------------------------
	decfsz	idle1, f	;change the number
	goto	m1		;nothing to display yet!
	decfsz	idle2, f	;change the number
	goto	m1		;nothing to display yet!
	movlw	.9		;new seed to idle2 ->
	movwf	idle2		;approx. 2 sec
	incf	idle_cab, w	;rotate the idle cab diplay number
	movwf	idle_cab	;and return it to file
	andlw	b'00000011'	;clear top bits and make this as if
	movwf	IR_ADDRESS	;just received packet from this throttle


	btfss	idle_cab, 2	;every second idle round for address
	goto	idle_address	;and every second to speed
	btfss	idle_cab, 3	;every second idle round for address
	goto	idle_speed	;and every second either to speed
	goto	idle_func	;or to func

idle_address
	call 	lookup_ir_address
	movwf	FSR
	movfw	INDF
	goto	disp_address	;display cab address status

idle_speed
	call 	lookup_ir_speed
	movwf	FSR
	movfw	INDF
	movwf	IR_DATA
	goto	disp_speed	;display cab speed status
idle_func
	call 	lookup_ir_func
	movwf	FSR
	movfw	INDF
	movwf	IR_DATA
	goto	disp_func	;display cab function status

m2
	;--------------------------------------------------------------
	;TSOP has seen the light! Adjust timer so that we'll hit
	;in the middle of first halfbit (quadbit) and try to
	;read 0 for start bit
	;--------------------------------------------------------------
	bsf	LED1		;Some data, put LED off (set)
	clrf	idle1		;clear idle counter1
	movlw	.18		;new seed to idle2: ->
	movwf	idle2		;approx. 2 sec 
	movlw	QUADBIT		;advance 1/4 bit to hit middle of
	movwf	TMR0		;1st half-bit of start-bit ("0")
	bcf	INTCON, T0IF	;just in case: clear Timer overflow flag

	call	ReadBit		;read whole start-bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	btfsc	DATA1, 0	;last read bit (LSB of DATA1) must
	goto 	m1		;be "0" as it was the start bit

IR_cab
	;------------------------------------------------------------
	;Got the start bit, now read the IR throttles ID
	;(two bits)
	;-----------------------------------------------------------
	movlw	2		;read 2 bits (four cabs)
	movwf	BITCOUNT
	clrf	DATA1		;clear read DATA1
	clrf	Parity		;clear parity

m3c	call 	ReadBit		;read whole DATA1 bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	decfsz	BITCOUNT, f	;All DATA1 bits read?
	goto	m3c		;not yet, read more

	movfw	DATA1		;all DATA1 read
	andlw	b'00000011'	;and clear top (safety matter!)
	movwf	IR_ADDRESS	;move to safety

IR_payload
	;-------------------------------------------------------
	;Time for receiving payload, read 8 bits
	;-------------------------------------------------------

	movlw	8		;read 8 bits
	movwf	BITCOUNT
	clrf	DATA1		;clear read DATA1
				;not clearing parity!

m3p	call 	ReadBit		;read whole DATA1 bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	decfsz	BITCOUNT, f	;All DATA1 bits read?
	goto	m3p		;not yet, read more

	movfw	DATA1		;all DATA1 read
	movwf	IR_DATA		;move to safety


IR_parity
	;-----------------------------------------------------
	;All that remains is to get the Parity bit
	;-----------------------------------------------------
	call	ReadBit		;read parity bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	btfsc	Parity, 0	;Parity bit 0 must now be clear
	goto	m1		;if not return to square one
	bcf	LED1		;Sensible data, light the LED (clear bit)

Check_previous


	goto 	Parse_payload

	;-----------------------------------------------------
	; All payloads of different cabs are stored in individual
	; ex-payload-files and when next payload from this cab
	; arrives it is compaired with the previous one.
	; If they match we have good packets ;)
	;-----------------------------------------------------


	movfw	IR_ADDRESS	;lets get the cab ID to w
	call	lookup_expay	;get previous payload address to w
	movwf	FSR		;and make it the indirect register,
	movfw	IR_DATA		;get the new payload
	xorwf	INDF, w		;test if equal to old (from this cab!)
	btfsc	status, z	;Was it equal?
	goto	parse_payload	;yes, jump forward
	movfw	IR_DATA		;nay, fetch the new payload again
	movwf	INDF		;and make it the next ex-payload
	goto 	m1		;and forget this packet


Parse_payload
	;----------------------------------------------------
	;Got full packet, start parsing:
	;
	;	0XXXXXXX => Address
	;	10XXXXXX => Function
	;	11XXXXXX => Speed
	;------------------------------------------------------

	movfw	IR_ADDRESS	;lets get the cab ID to w
				;to be handy while working with payload
	btfss	IR_DATA, 7	;test if payload was address?
	goto	got_address	;Yes (clear), go with it

	btfss	IR_DATA, 6	;No (set), is payload a speed packet?
	goto	got_func	;No (clear), so it must be func packet
	goto	got_speed	;Yes (set) go for speed

got_address
	call	lookup_IR_address
				;Lets get the cab's address register
	movwf	FSR		;and make it the indirect register,
	movfw	IR_DATA		;load payload to accumulator
	movwf	INDF		;and further to correct cab's addr. reg!


	;goto	change_addr	;speed zero



	;-------------------------------------------------------------
	; First let's test that the speed of this cab is zero
	; (SSSS = 0000)
	;-------------------------------------------------------------
	movfw	IR_ADDRESS	;lets get the cab ID to w
	call	lookup_ir_speed	;and get the speed file address to w
	movwf	FSR		;and make it the indirect register,
	movfw	INDF		;get the speed byte
	andlw	b'00001111'	;mask hi nibble away
	skpnz
	goto	change_addr	;speed zero
	goto 	disp_err

	;-------------------------------------------------------------
	; This routine checks that no other cab has the same
	; address, and in case it has it silently clears the address
	; of the found other cab's equal address (steals the loco!)
	; BUT only in case the speed of that cab is also set to zero!
	;
	; Another solution could be to warn and not capture, but this will
	; propably cause more havoc as one should find which cab has the
	; address in use (could be displayed instead of address!)
	; but where the heck is this cab now (no wires!)
	;-------------------------------------------------------------
change_addr

	call	tmw_request	;TMW-DCC requesting new data?

steal_cab0
	movfw	IR_DATA		;Load new address to w
	xorwf	addr_0, w	;and compare with addr_x.
	btfss	status, z	;Was it equal?
	goto	steal_cab1	;nay, lets try next cab
	movfw	speed_0		;yes, let's see that speed
	andlw	b'00001111'	;is zero before stealing!
	skpz
	goto	disp_err	;no, forget it!
	clrf	addr_0		;yes, cleared

steal_cab1
	movfw	IR_DATA		;Load new address to w
	xorwf	addr_1, w	;and compare with addr_x.
	btfss	status, z	;Was it equal?
	goto	steal_cab2	;nay, lets try next cab
	movfw	speed_1		;yes, let's see that speed
	andlw	b'00001111'	;is zero before stealing!
	skpz
	goto	disp_err	;no, forget it!
	clrf	addr_1		;yes, cleared

steal_cab2
	movfw	IR_DATA		;Load new address to w
	xorwf	addr_2, w	;and compare with addr_x.
	btfss	status, z	;Was it equal?
	goto	steal_cab3	;nay, lets try next cab
	movfw	speed_2		;yes, let's see that speed
	andlw	b'00001111'	;is zero before stealing!
	skpz
	goto	disp_err	;no, forget it!
	clrf	addr_2		;yes, cleared

steal_cab3
	movfw	IR_DATA		;Load new address to w
	xorwf	addr_3, w	;and compare with addr_x.
	btfss	status, z	;Was it equal?
	goto	stealing_done	;nay, lets try next cab
	movfw	speed_3		;yes, let's see that speed
	andlw	b'00001111'	;is zero before stealing!
	skpz
	goto	disp_err	;no, forget it!
	clrf	addr_3		;yes, cleared

stealing_done
	movfw	IR_ADDRESS	;get the cab number
	call	lookup_IR_address
				;find corresponding addr_x
	movwf	FSR		;and make it the indirect register,
	movfw	IR_DATA		;load payload to accumulator
	movwf	INDF		;and further to correct cab's addr. reg!

	call	tmw_request	;TMW-DCC requesting new data?

disp_address
	call	bin_to_BCD	;make decimal
	movfw	LSD		;get lower digit
	call	lookup_7_seg	;get 7-seg of the low nibble
	call 	so_byte		;and shift out byte
	movfw	MSD		;get higher digit
	call	lookup_7_seg	;get 7-seg of the high nibble
	call 	so_byte		;and shift out byte
	movlw	b'00001010'	;light 'A'!
	call 	so_byte		;and shift out byte
	movfw	IR_ADDRESS	;get cab number
	addlw	0x1		;increment to get laymen's numbers ;)
	call	lookup_7_seg	;change to 7-segment
	call 	so_byte		;and shift out byte
	bsf	S_STROBE	;strobe out
	nop			;wait for slow C-MOS
	bcf	S_STROBE	;strobing done
	goto	m1		;and back to square 1

got_speed
	call	lookup_IR_speed	;Lets get the cab's speed register
	movwf	FSR		;and make it the indirect register,
	movfw	IR_DATA		;and load it to accumulator
	movwf	INDF		;and further to correct cab's speed. reg!

	;-------------------------------------------------------------
	; The FL should only be displayed as dp with speed/func.
	; Functions F1..F4 should be displayed as short or long
	; vertical bar at F-display like "2FiiIi." (cab 2, F2+FL set)
	;-------------------------------------------------------------

disp_speed
	incf	FSR, f		;move FSR to point to func_x byte
	btfss	INDF, 5		;Skip if speed mode = 28 step
	goto	disp_14s

disp_28s
	;------------------------------------------------------------
	; 28 speed steps, first test if SSSS of the speed is equal
	; to zero (0000) and if so just get it out (little "s"
	; makes no difference, 00000 and 00001 (SSSSs) are both "stop"
	; second, test if its emg break (0001) and display "ES"
	; and again little s makes no difference.
	; Else decrement the speed by two, and put it out
	;-------------------------------------------------------------


	movfw	IR_DATA		;Load new address to w
	andlw	b'00001111'	;leave the 4 LSB speed bits
	skpnz			;was speed zero?
;##	call	disp_28s2	;yes, ready for bin_to BCD ## ERROR ##
	goto	disp_28s2	;yes, ready for bin_to BCD 
	xorlw	b'00000001'	;no, compare with Emg.Stop.
	skpz			;Was it Emg.Stop?
	goto	disp_28s1	;no, display value -2
	movlw	b'10001001'	;yes, (z set), load "S"
	btfsc	INDF, 4		;test the lights (INDF->func_x)
	andlw	b'11110111'	;lights on, light (clear) the dp
	call 	so_byte		;and shift out byte
	movlw	b'10001100'	;load "E"
	call 	so_byte		;and shift out byte
	goto 	disp_s1		;and get the cab and dir out as well


disp_28s1

	rlf	IR_DATA, w	;get to w, but shifted left
	andlw	b'00011110'	;leave the 4 LSB speed bits
	btfsc	IR_DATA, 4	;test little "s"
	iorlw	b'00000001'	;set the lsb of w
	addlw	0xFD		;add FD (equal to decrement 2)
disp_28s2
	call	bin_to_BCD	;make decimal
	movfw	LSD		;get lower digit
	call	lookup_7_seg	;get 7-seg of the low nibble
	btfsc	INDF, 4		;Test Lights, FSR->func_x, dp on if on!
	andlw	b'11110111'	;yes, light (clear) dp
	call 	so_byte		;and shift out byte
	movfw	MSD		;get higher digit
	call	lookup_7_seg	;get 7-seg of the high nibble
	call 	so_byte		;and shift out byte
	goto 	disp_s1		;and get the cab and dir out as well

disp_14s
	;------------------------------------------------------------
	; 14 speed steps, first test if the speed is equal
	; to zero (0000) and if so just get it out
	; second, test if its emg break (0001) and display "ES"
	; else decrement the speed by one, and put it out
	;-------------------------------------------------------------
	movfw	IR_DATA		;Load new address to w
	andlw	b'00001111'	;leave the 4 LSB speed bits
	skpnz			;was speed zero?
;##	call	disp_14s2	;yes, ready for bin_to BCD ## ERROR ##
	goto	disp_14s2	;yes, ready for bin_to BCD
	xorlw	b'00000001'	;no, compare with Emg.Stop.
	skpz			;Was it Emg.Stop?
	goto	disp_14s1	;no, display value -1
	movlw	b'10001001'	;yes, (z set), load "S"
	btfsc	IR_DATA, 4	;test lights (little "s")
	andlw	b'11110111'	;light the lights (clear) dp bit w
	call 	so_byte		;and shift out byte
	movlw	b'10001100'	;load "E"
	call 	so_byte		;and shift out byte
	goto 	disp_s1		;and get the cab and dir out as well

disp_14s1
	movfw	IR_DATA
	andlw	b'00001111'	;leave the 4 MSB speed bits
	addlw	0xFF		;add FE (equal to decrement 1)
disp_14s2	
	call	bin_to_BCD	;make decimal
	movfw	LSD		;get lower digit
	call	lookup_7_seg	;get 7-seg of the low nibble
	decf	FSR, f		;get FSR to point to speed
	btfsc	INDF, 4		;Test for Lights (dp on if on!)
	andlw	b'11110111'	;yes, light (clear) dp
	call 	so_byte		;and shift out byte
	movfw	MSD		;get higher digit
	call	lookup_7_seg	;get 7-seg of the high nibble
	call 	so_byte		;and shift out byte
disp_s1
	movlw	b'11111111'	;show blank as msb of speed
	btfss	IR_DATA, 5	;test direction bit (set=fwd)
	movlw	b'11101111'	;no - light '-' instead (clear=rev)
	call 	so_byte		;and shift out byte
	movfw	IR_ADDRESS	;get cab number
	addlw	0x1		;increment to get laymen's numbers ;)
	call	lookup_7_seg	;change to 7-segment
	call 	so_byte		;and shift out byte
	bsf	S_STROBE	;strobe out
	nop			;wait for slow C-MOS
	bcf	S_STROBE	;strobing done
	goto	m1		;and back to square 1

got_func
	call	lookup_IR_func	;Lets get the cab's address register
	movwf	FSR		;and make it the indirect register,
	movfw	IR_DATA		;load payload to accumulator
	movwf	INDF		;and further to correct cab's addr. reg!

disp_func
	btfsc	IR_DATA, 5	;Skip if speed mode = 14 step
	goto	disp_28f

disp_14f
	;-----------------------------------------------------
	; Leftmost 7-segment first:
	; set bottom bars on (0), test for function
	; bits and test F1 and F2 and light(0) resp top bars
	; test the FL and set (0) dp 
	;------------------------------------------------------
	movlw	b'11111010'	;all but bottom bars off
	btfsc	IR_DATA, 0	;testing F1
	andlw	b'01111111'	;clear (light) right top bar
	btfsc	IR_DATA, 1	;testing F1
	andlw	b'11011111'	;clear (light) left top bar
	decf	FSR, f		;get INDF to point to speed
	btfsc	INDF, 4		;check the F5/FL is on
	andlw	b'11110111'	;light dp
	call 	so_byte		;and shift out byte

	movlw	b'11111010'	;all but bottom bars off
	btfsc	IR_DATA, 2	;testing F1
	andlw	b'01111111'	;clear (light) right top bar
	btfsc	IR_DATA, 3	;testing F1
	andlw	b'11011111'	;clear (light) left top bar
	call 	so_byte		;and shift out byte

	goto	disp_f1		;

disp_28f

	movlw	b'11111010'	;all but bottom bars off
	btfsc	IR_DATA, 0	;testing F1
	andlw	b'01111111'	;clear (light) right top bar
	btfsc	IR_DATA, 1	;testing F1
	andlw	b'11011111'	;clear (light) left top bar
	btfsc	INDF, 4		;check the F5/FL is on
	andlw	b'11110111'	;light dp
	call 	so_byte		;and shift out byte

	movlw	b'11111010'	;all but bottom bars off
	btfsc	IR_DATA, 2	;testing F1
	andlw	b'01111111'	;clear (light) right top bar
	btfsc	IR_DATA, 3	;testing F1
	andlw	b'11011111'	;clear (light) left top bar
	call 	so_byte		;and shift out byte

disp_f1
	movlw	b'10001110'	;light 'F'!
	call 	so_byte		;and shift out byte
	movfw	IR_ADDRESS	;get cab number
	addlw	0x1		;increment to get laymen's numbers ;)
	call	lookup_7_seg	;change to 7-segment
	call 	so_byte		;and shift out byte
	bsf	S_STROBE	;strobe out
	nop			;wait for slow C-MOS
	bcf	S_STROBE	;strobing done
	goto	m1		;and back to square 1

disp_err
	movlw	b'01110111'	; !
	call	so_byte		;
	movlw	b'11101110'	; r
	call	so_byte		;
	movlw	b'11101110'	; r
	call	so_byte		;
	movlw	b'10001100'	; E
	call	so_byte		;
	bsf	S_STROBE	;strobe out (display "Err!")
	nop			;wait for slow C-MOS
	bcf	S_STROBE	;strobing done
	goto	m1

;---------------------------------------------------------------
;Lookup-table for 7-seg display, uses 4094 shift registers
;---------------------------------------------------------------
	ORG     0x300		; last page

Lookup_7_seg
	movwf	temp		;Make sure the
	movlw   0x03		;jump of retlw
        movwf   pclath		;gets to the correct
        movfw   temp		;page in memory

	andlw	0x0F		;clear top to prevent going past
	addwf	pcl,f		;
	retlw	b'00011000'	; 0
	retlw	b'01111011'	; 1
	retlw	b'00101100'	; 2
	retlw	b'00101001'	; 3	 77777
	retlw	b'01001011'	; 4	6     8
	retlw	b'10001001'	; 5	6     8
	retlw	b'10001000'	; 6	6     8
	retlw	b'00111011'	; 7      55555
	retlw	b'00001000'	; 8	1     3
	retlw	b'00001001'	; 9	1     3
	retlw	b'00001010'	; A	1     3  44
	retlw	b'11001000'	; b	 22222   44
	retlw	b'10011100'	; C
	retlw	b'01101000'	; d
	retlw	b'10001100'	; E
	retlw	b'10001110'	; F
	retlw	b'11101111'	; -
	retlw	b'10001111'	; P

;---------------------------------------------------------------
;Lookup-tables for cab to be served 
;---------------------------------------------------------------

lookup_expay
	movwf	temp		;Make sure the
	movlw   0x03		;jump of retlw
        movwf   pclath		;gets to the correct
        movfw   temp		;page in memory

	andlw	0x03		;clear top to prevent going past
	addwf	pcl,f		;
	retlw	expay_0		;point to cab 0's previous payload
	retlw	expay_1		;point to cab 1's previous payload
	retlw	expay_2		;point to cab 2's previous payload
	retlw	expay_3		;point to cab 3's previous payload

lookup_IR_address
	movwf	temp		;Make sure the
	movlw   0x03		;jump of retlw
        movwf   pclath		;gets to the correct
        movfw   temp		;page in memory

	andlw	0x03		;clear top to prevent going past
	addwf	pcl,f		;
	retlw	addr_0		;point to cab 0's DCC address storage
	retlw	addr_1		;point to cab 1's DCC address storage
	retlw	addr_2		;point to cab 2's DCC address storage
	retlw	addr_3		;point to cab 3's DCC address storage

lookup_IR_speed
	movwf	temp		;Make sure the
	movlw   0x03		;jump of retlw
        movwf   pclath		;gets to the correct
        movfw   temp		;page in memory

	andlw	0x03		;clear top to prevent going past
	addwf	pcl,f		;
	retlw	speed_0		;point to cab 0's DCC speed storage
	retlw	speed_1		;point to cab 1's DCC speed storage
	retlw	speed_2		;point to cab 2's DCC speed storage
	retlw	speed_3		;point to cab 3's DCC speed storage

lookup_IR_func
	movwf	temp		;Make sure the
	movlw   0x03		;jump of retlw
        movwf   pclath		;gets to the correct
        movfw   temp		;page in memory

	andlw	0x03		;clear top to prevent going past
	addwf	pcl,f		;
	retlw	func_0		;point to cab 0's DCC func storage
	retlw	func_1		;point to cab 1's DCC func storage
	retlw	func_2		;point to cab 2's DCC func storage
	retlw	func_3		;point to cab 3's DCC func storage

lookup_TMW_byte
	movwf	temp		;Make sure the
	movlw   0x03		;jump of retlw
        movwf   pclath		;gets to the correct
        movfw   temp		;page in memory

	andlw	0x07		;clear top to prevent going past
	addwf	pcl,f		;
	retlw	tmw_5		;point to tmw-byte 5
	retlw	tmw_4		;point to tmw-byte 4
	retlw	tmw_3		;point to tmw-byte 3
	retlw	tmw_2		;point to tmw-byte 2
	retlw	tmw_1		;point to tmw-byte 1
	retlw	tmw_0		;point to tmw-byte 0
	retlw	tmw_0		;in case overrun!
	retlw	tmw_0		;in case overrun!

	end

