;;======================================================================;;
;;			Lokmaus TCO Interface		 		;;
;;======================================================================;;
;;									;;
;; Program:		XbusTCO -- Lokmaus TCO Interface		;;
;; Code:		Paco Caņada ( http://www.fut.es/~fmco )		;;
;; Platform:		Microchip PIC16F628, 8 Mhz			;;
;; Date:		25.08.2005					;;
;; First release:	25.08.2005					;;
;; LastDate:		16.02.2006					;;
;;									;;
;;======================================================================;;

; This program is for an accessory only encoder for Lenz XBus based on Mike Bolton design.
; It scans a matrix of on / off switches, each with series diodes and detects changes 
; in the switch position. Accessory packets are sent to LokMaus to set one of each
; output pair dependent on the switch position.
; A 4 to 16 line multiplexer 4515 is used to drive 16 columns. 7 rows are 
; sensed by the PIC input lines. This gives a maximum of 105 switches and a dip-switch.
; When the encoder is first switched on, the dip-swicht is read to get an Xbus address
; when a communication with LokMaus is stablished all switches are interrogated and
; their positions saved.
;
; Processor PIC 16F628 running at 8MHz.
;
; Revisions:
;
; 25/08/2005	Start of writing code.
; 01/09/2005	First prototype
; 02/09/2005	Communication works!
; 14/10/2005	Added Lokmaus/Lenz dip-switch. Tested with Lokmaus and LZV100 v.3.5
; 23/11/2005	Handle messages when waiting to deactivate turnout
; 16/02/2006	Changes in read switch routines
	

; ----- Definitions

#define		__VERNUM	0x01
#define		__VERREV	"B"
#define		__VERDAY	0x16
#define		__VERMONTH	0x02
#define		__VERYEAR	0x06


                list    p=16F628,r=hex

		errorlevel -302
		errorlevel -306

	        INCLUDE "P16F628.INC"

                __FUSES _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF  & _HS_OSC


#define		RAMINI0		0x020		; 80 bytes
#define		RAMINI1		0x0A0		; 80 bytes
#define		RAMINI2		0x120		; 48 bytes
#define		RAMINT		0x070		; 16 bytes


; ----- Macros

SEL_BANK_0:	macro	
		bcf	STATUS,RP0		; macros for data access
		bcf	STATUS,RP1
		endm

SEL_BANK_1:	macro	
		bsf	STATUS,RP0
		bcf	STATUS,RP1
		endm

SEL_BANK_2:	macro	
		bcf	STATUS,RP0
		bsf	STATUS,RP1
		endm

SEL_BANK_3:	macro	
		bsf	STATUS,RP0
		bsf	STATUS,RP1
		endm

#define		SEL_IBANK_0	bcf	STATUS,IRP

#define		SEL_IBANK_1	bsf	STATUS,IRP

#define		DNOP		goto	$+1

; --- Constant values

FXTAL		equ	D'8000000'
BaudRate	equ	D'62500'		; Xbus serial speed

HIGH_BAUD	equ	(((D'10'*FXTAL/(D'16'*BaudRate))+5)/D'10')-1		; BRGH = 1
LOW_BAUD	equ	(((D'10'*FXTAL/(D'64'*BaudRate))+5)/D'10')-1		; BRGH = 0


RA_TRIS         equ     0xF0			; RA4,RA5,RA6,RA7: inputs & c 4515
RB_TRIS         equ     0xFE			; all inputs and serial port: RB0=TXRX
RA_INI          equ     0x00			; all zero, TXRX=0 receive
RB_INI          equ     0x00			; all zero
INTC_INI	equ	0xC0			; GIE enable, PEIE enable
PIE1_INI	equ	0x20			; RX interrupt
OPTION_INI	equ	0x04			; Option register: timer prescaler 1:8, pull-ups
						; Port B Pull-up:       0   enabled
						; External int edge:    0   falling
						; Timer trigger source: 0   clock
						; Timer trigger edge:   0   rising
						; Prescaler assignment: 0   Timer0
						; Prescaler value :   100   1:32


; --- E/S Ports

; --- Port A

; RA0	4515 A
; RA1	4515 B
; RA2	4515 C
; RA3	4515 D
; RA4	Row 0
; RA5	Row 1


; --- Port B

; RB0	TXRX
; RB1	RXD
; RB2	TXD
; RB3	Row 2
; RB4	Row 3
; RB5	Row 4
; RB6	Row 5
; RB7	Row 6

; --- Pin assignement

OUTPORT		equ	PORTA
KEYPORT		equ	PORTB

COMPORT		equ	PORTB
TXRX		equ	0			; 1: enable, 0: disable RS485 transmision
RXD		equ	1
TXD		equ	2

DIP_COL		equ	0			; 4515 column for DIP switches

; --- Xbus constants

BROADCAST	equ	0x60			; broadcast call byte



; --- EEPROM Section

#define		EE_INI		0x00



; ----- Variables

; --- Internal RAM Section

; --- Top on all banks

INT_W		equ	RAMINT+0x00		; Interrupt variables.
INT_STAT	equ	RAMINT+0x01
INT_PCLATH	equ	RAMINT+0x02
INT_FSR		equ	RAMINT+0x03


EEDATA0		equ	RAMINT+0x0B		; EEPROM shadow variables
EEADR0		equ	RAMINT+0x0C	


; --- Bank 0

BUFRXINI	equ	RAMINI0+0x00		; first input buffer location
HEADER		equ	RAMINI0+0x00		; Xbus packet
DATA1		equ	RAMINI0+0x01
DATA2		equ	RAMINI0+0x02
DATA3		equ	RAMINI0+0x03
DATA4		equ	RAMINI0+0x04

BUFRXEND	equ	RAMINI0+0x11		; last reception buffer location

ROW		equ	RAMINI0+0x18
COL		equ	RAMINI0+0x19
TURNOUT		equ	RAMINI0+0x1A

DUMMY		equ	RAMINI0+0x1C		; Temporary values
TEMP		equ	RAMINI0+0x1D
TEMP2		equ	RAMINI0+0x1E
COUNT		equ	RAMINI0+0x1F

CALLBYTE	equ	RAMINI0+0x20		; call byte byte from command station
MY_ADR		equ	RAMINI0+0x21		; XpressNet Address for normal inquiry messages
TXPOS		equ	RAMINI0+0x22		; Serial Buffer output pointers
TXCNT		equ	RAMINI0+0x23
RX_XOR		equ	RAMINI0+0x24		; Serial incoming checksum
RX_LEN		equ	RAMINI0+0x25		; Incomming message length
XFLAGS		equ	RAMINI0+0x26		; XpressNet flags
XBUS_VER	equ	RAMINI0+0x27		; XpressNet version
XBUS_STA	equ	RAMINI0+0x28		; Command Station type
FLAGS		equ	RAMINI0+0x29		; Flags

DCC_STATUS	equ	RAMINI0+0x2F		; Command Station Status

BUFKBINI	equ	RAMINI0+0x30		; Key buffer
BUFKBEND	equ	RAMINI0+0x3F
BUFKBOLD	equ	RAMINI0+0x40
BUFKBOLDEND	equ	RAMINI0+0x4F

; --- Bank 1

; --- Buffers

BUFTXINI	equ	RAMINI1+0x00		; XpressNet Serial output buffer
;BUFTXEND	equ	RAMINI1+0x1F

;----- Flags

#define		NEW_TX		XFLAGS,0	; new message to command station
#define		TXMIT		XFLAGS,1	; transmiting a message
#define		RCVING		XFLAGS,2	; receiving a message
#define		NEW_MSG		XFLAGS,3	; received new message to us

#define		EmergStop	DCC_STATUS,0	; System in Emergency Stop
#define		EmergOff	DCC_STATUS,1	; System in Emergency Off
#define		AutoStart	DCC_STATUS,2	; System in auto start-up. Not used
#define		ServiceMode	DCC_STATUS,3	; System in Service Mode

#define		LOKMAUS		FLAGS,0		; Using with Lokmaus


; --------------- Program Section --------------------------------------


		org	0x000

PowerUp:
		clrf	INTCON			; Disable all interrupts
		clrf	PCLATH			; Tables on page 0
		clrf	STATUS			; reset flags
		goto	INIT


; ----- ISR (Interrupt Service Routines)

		org	0x004

Interrupt:
		movwf	INT_W			; 1 Save registers W, STATUS, PCLATH, FSR
		swapf	STATUS,w		; 2
		clrf	STATUS			; 3 Ensure port / timer access in bank 0
		movwf	INT_STAT		; 4
;		movf	PCLATH,w		; 
;		movwf	INT_PCLATH		; save PCLATH, interrupt uses page 0
;		clrf	PCLATH			; 
		movf	FSR,w			; Save FSR
		movwf	INT_FSR

IntRx:
		btfss	PIR1,RCIF		; serial receive interrupt?
		goto	IntTx

		btfss	RCSTA,OERR		; yes, handle RX errors
		btfsc	RCSTA,FERR
		goto	IntError

		btfss	RCSTA,RX9D		; is the call byte?
		goto	IntRxData		; no
IntRxCallByte:
		bcf	RCVING			; yes, abort message in progress
		clrf	RX_LEN
		movf	RCREG,w			; get call byte and save it
		movwf	CALLBYTE
		btfsc	NEW_MSG			; decoded last message?
		goto	IntRestore		; no, ignore this call byte
		xorlw	BROADCAST		; broadcast call byte? ('P1100000')
		btfsc	STATUS,Z
		goto	IntRxPacket		; yes, new message
		movf	CALLBYTE,w		; message to us?
		xorwf	MY_ADR,w		;     'P10AAAAA' anything to transmit?
		btfsc	STATUS,Z
		goto	IntRxSend		; yes, answer before 80us
		xorlw	b'10100000'		; no, 'P11AAAAA' message to us?
		btfsc	STATUS,Z
		goto	IntRxPacket		; yes, new message
		xorlw	b'01100000'		; no, 'P00AAAAA' acknowledge?
		btfsc	STATUS,Z
		goto	IntRxAck		; yes, send ack
		goto	IntRestore		; no, discard

IntRxPacket:
		clrf	RX_XOR			; set receiving a message
		bsf	RCVING
		goto	IntRestore
		
IntRxData:
		movlw	BUFRXINI		; save data
		addwf	RX_LEN,w
		movwf	FSR
		movf	RCREG,w			; get data byte to clear interrupt
		btfss	RCVING			; are we receiving a packet for us?
		goto	IntRestore
		movwf	INDF			; yes, save data
		xorwf	RX_XOR,f		; checksum
		incf	RX_LEN,f		; next byte
		movlw	BUFRXINI		; check end of message
		movwf	FSR
		movf	INDF,w			; get header byte
		andlw	0x0F			; length to receive
		addlw	0x02			; plus header and xor-byte
		xorwf	RX_LEN,w		; equal to received length?
		btfss	STATUS,Z
		goto	IntRestore		; no, receive more bytes
IntRxEnd:
		bcf	RCVING			; yes, end of receiving data
		movf	RX_XOR,w
		btfsc	STATUS,Z		; xor-byte correct?
		bsf	NEW_MSG			; yes, received new packet
		goto	IntRestore
	
IntRxAck:					; Acknowledge transmision (send 0x20, 0x20)
		bsf	COMPORT,TXRX		; MAX485 TX enable
		movlw	0x20
		movwf	TXREG
		SEL_BANK_1
IntTxAck:
		btfss	TXSTA,TRMT		; wait until end of byte transmited
		goto	IntTxAck
		SEL_BANK_0
		movlw	0x20			; send second byte
		movwf	TXREG
		goto	IntTxEnd		; wait until transmited

IntRxSend:
		btfss	NEW_TX			; new message to command station?
		goto	IntRestore		; no
		bsf	COMPORT,TXRX		; MAX485 TX enable
		SEL_BANK_1
		bsf	PIE1,TXIE		; Enable TX interrupts
		SEL_BANK_0		
		clrf	TXPOS
		bsf	TXMIT			; now transmiting
		goto	IntTxByte

IntError:
		movf	RCREG,w			; on error re-init receiver
		bcf	RCSTA,CREN
		bsf	RCSTA,CREN

IntTx:
		btfss	PIR1,TXIF		; serial transmit interrupt?
		goto	IntRestore		; no, nothing to do
		btfss	TXMIT			; yes, still transmiting?
		goto	IntTxEnd		; no, disable TX interrupts

IntTxByte:
		movlw	BUFTXINI		; calc buffer position
		addwf	TXPOS,w
		movwf	FSR
		movf	INDF,w			; get byte from buffer
		movwf	TXREG			; and send it
		incf	TXPOS,f
		decfsz	TXCNT,f			; last byte?
		goto	IntRestore		; no
		bcf	NEW_TX			; yes, stop transmiting
		bcf	TXMIT
IntTxEnd:
		SEL_BANK_1
IntTxDone:
		btfss	TXSTA,TRMT		; wait until end of last byte transmited
		goto	IntTxDone
		bcf	PIE1,TXIE		; Disable TX interrupts
		DNOP
		DNOP
		DNOP
		SEL_BANK_0		
		bcf	COMPORT,TXRX		; MAX485 TX disable

IntRestore:
		movf	INT_FSR,w		;  Restore the Context Registers and Return
		movwf	FSR
;		movf	INT_PCLATH,w
;		movwf	PCLATH
		swapf	INT_STAT,w
		movwf	STATUS
		swapf	INT_W,f
		swapf	INT_W,w
		retfie



; ----- Tables on first 256 bytes

BitConvert:
		andlw	0x07
		addwf	PCL,f			; Convert bit position to byte mask
		retlw	0x01
		retlw	0x02
		retlw	0x04
		retlw	0x08
		retlw	0x10
		retlw	0x20
		retlw	0x40
		retlw	0x80

TurnoutAddress:
		movf	COL,w
		andlw	0x0F
		addwf	PCL,f			; convert column to initial turnout
		retlw	d'106'
		retlw	d'1'
		retlw	d'8'
		retlw	d'15'
		retlw	d'22'
		retlw	d'29'
		retlw	d'36'
		retlw	d'43'
		retlw	d'50'
		retlw	d'57'
		retlw	d'64'
		retlw	d'71'
		retlw	d'78'
		retlw	d'85'
		retlw	d'92'
		retlw	d'99'

; ----- Initialization

INIT:
		clrf	RCSTA			; disable UART

		movlw   RA_INI			; Set ports
		movwf   PORTA                         
		movlw   RB_INI
		movwf   PORTB
		SEL_BANK_1
		movlw	RB_TRIS			; Set port B I/O configuration
		movwf   TRISB
		movlw   RA_TRIS         	; Set port A I/O configuration
		movwf   TRISA
		movlw	PIE1_INI
		movwf	PIE1			; peripheral interrupts
		movlw   OPTION_INI      	; PORTB pull-ups enabled, Timer configuration
		movwf   OPTION_REG
		movlw	0x0B			; internal oscillator to 4MHz default
		movwf	PCON
		movlw	0x00			; VRR off
		movwf	VRCON
		SEL_BANK_0
		movlw	0x07			; All digital inputs
		movwf	CMCON                          

		movlw	DIP_COL			; select DIP row
		movwf	OUTPORT

		movlw	0x31			; Timer 1. Run, set 1:8 Prescaler
		movwf	T1CON
		movlw	0x06			; Timer 2. Run with 1:16 Prescaler
		movwf	T2CON

		movlw	RAMINI0			; Clear variables bank0
		movwf	FSR
ClearRAM:
		clrf	INDF
		incf	FSR,f
		btfss	FSR,7
		goto	ClearRAM

		call	SetMyAddress		; get Xbus address
		call	UART_INI		; Init peripheals
		bcf	COMPORT,TXRX		; MAX485 TX disable just to be sure
		movlw	INTC_INI		; Set interrupts
		movwf	INTCON

		call	GetVersion		; get command station version & type

		clrf	TMR1L			; time to transmit to command station
		clrf	TMR1H
		bcf	PIR1,TMR1IF
FindStation:
		btfsc	NEW_MSG			; new message received?
		call	HandleMsg		; yes, handle it
		btfsc	PIR1,TMR1IF
		call	NotFound		; timeout

		btfsc	NEW_TX			; packet sended, command station present
		goto	FindStation

;		bsf	T1CON,TMR1ON		; Timer1 on to be sure
		call	GetStatus		; get current status
		clrf	TMR1L			; time to transmit changes to command station
		clrf	TMR1H
		bcf	PIR1,TMR1IF

; --- Main Loop


MainLoop:
		call	ReadKeyNew		; init buffer
		call	SaveKeys
		clrf	ROW
		movlw	0x01
		movwf	COL
		movwf	PORTA
		clrf	TMR0
		bcf	INTCON,T0IF		; wait time
Loop:
		btfsc	NEW_MSG			; new message received?
		call	HandleMsg		; yes, handle it

		btfsc	INTCON,T0IF		; every 1 ms
		call	CheckKey		; Read one key

		goto	Loop



; --- No Command Station avaiable

NotFound:
		bcf	PIR1,TMR1IF
		movf	OUTPORT,w		; blink DIP LED
		andlw	0x0F
		xorlw	DIP_COL
		movlw	DIP_COL+1
		btfss	STATUS,Z
		movlw	DIP_COL
		movwf	OUTPORT
		return


; --- Incoming messages


HandleMsg:


Handle6X:
		movf	HEADER,w
Handle61:
		xorlw	0x61
		btfss	STATUS,Z
		goto	Handle6X_2
Handle61_0:
		movf	DATA1,w
		btfss	STATUS,Z
		goto	Handle61_1

;		btfsc	EmergOff		; second transmision
;		goto	HandleEnd
		bcf	ServiceMode		; Track power off
		bsf	EmergOff
		bcf	EmergStop
HandleOff:
		clrf	PORTA			; on LED
		goto	HandleEnd
				
Handle61_1:
		xorlw	0x01
		btfss	STATUS,Z
		goto	Handle61_2

		bcf	ServiceMode		; normal operations resume
		bcf	EmergOff
		bcf	EmergStop
HandleResume:
		movf	COL,w			; off LED
		movwf	PORTA
		clrf	TMR1H			; wait a moment
		clrf	TMR1L
		bcf	PIR1,TMR1IF
		goto	HandleEnd
Handle61_2:
		xorlw	(0x01 ^ 0x02)
		btfss	STATUS,Z
		goto	HandleEnd

;		btfsc	ServiceMode		; second transmision
;		goto	HandleEnd
		bsf	ServiceMode		; Service Mode entry
		bcf	EmergOff
		bcf	EmergStop
HandleServ:

		goto	HandleEnd
Handle6X_2:
		xorlw	(0x61 ^ 0x62)
		btfss	STATUS,Z
		goto	Handle6X_3
Handle62:
		movf	DATA1,w
		xorlw	0x22
		btfss	STATUS,Z
		goto	HandleEnd

		movf	DATA2,w			; Commnad station status response
		movwf	DCC_STATUS

;		btfsc	ServiceMode		; set correct status
;		goto	HandleServ
		btfsc	EmergOff
		goto	HandleOff
;		btfsc	EmergStop
;		goto	HandleStop
		goto	HandleResume

		goto	HandleEnd
		


Handle6X_3:
		xorlw	(0x62 ^ 0x63)
		btfss	STATUS,Z
		goto	Handle8X
Handle63:
		movf	DATA1,w
		xorlw	0x21
		btfss	STATUS,Z
		goto	HandleEnd

		movf	DATA2,w			; software version
		movwf	XBUS_VER
		movf	DATA3,w
		movwf	XBUS_STA
		goto	HandleEnd
		
Handle8X:
		movf	HEADER,w
Handle81:
		xorlw	0x81
		btfss	STATUS,Z
		goto	HandleEnd

;		btfsc	EmergStop		; second transmision
;		goto	HandleEnd
		bcf	ServiceMode		; Emergency stop
		bcf	EmergOff
		bsf	EmergStop
HandleStop:
		goto	HandleEnd


HandleEnd:
		bcf	NEW_MSG			; message processed
		return


; --- Request messages


GetVersion:
		movlw	0x21			; get software version request
		call	SendHeader
		movlw	0x21
		call	SendData
		goto	SendMsg
		
GetStatus:
		movlw	0x21			; get status request
		call	SendHeader
		movlw	0x24
		call	SendData
		goto	SendMsg

;Estop:
;		movlw	0x80			; Emergency stop
;		call	SendHeader
;		goto	SendMsg


Resume:
		movlw	0x21			; Resume operations request
		call	SendHeader
		movlw	0x81
		call	SendData
		goto	SendMsg
		

SetAccessory:
		movlw	0x52
		call	SendHeader
		decf	TURNOUT,w		; turnout 1.. -> 0..
		movwf	TEMP
		rrf	TEMP,f
		rrf	TEMP,w
		andlw	0x1F

		btfsc	LOKMAUS			; Lokmaus starts at 1
		addlw	0x01

		call	SendData
		decf	TURNOUT,w
		andlw	0x03
		movwf	TEMP
		rlf	TEMP,w
		andlw	0x06
		iorlw	0x80			; only activate outputs

		iorlw	0x08			; activate

		btfsc	TURNOUT,7
		iorlw	0x01			; set straigt/diverge
		call	SendData
		call	SendMsg

		clrf	TMR1H			; reset timer for CDU
		clrf	TMR1L
SetAccOff:
		btfsc	NEW_MSG			; new message received?
		call	HandleMsg		; yes, handle it

		btfss	TMR1H,7
		goto	SetAccOff

		movlw	0x52
		call	SendHeader
		decf	TURNOUT,w		; turnout 1.. -> 0..
		movwf	TEMP
		rrf	TEMP,f
		rrf	TEMP,w
		andlw	0x1F

		btfsc	LOKMAUS			; Lokmaus starts at 1
		addlw	0x01

		call	SendData
		decf	TURNOUT,w
		andlw	0x03
		movwf	TEMP
		rlf	TEMP,w
		andlw	0x06
		iorlw	0x80			; deactivate output

		btfsc	TURNOUT,7
		iorlw	0x01			; set straigt/diverge
		call	SendData
		goto	SendMsg

; --- XpressNet messages routines


SendHeader:
		movwf	DUMMY
SendHeaderW:
		btfsc	NEW_MSG			; new message received?
		call	HandleMsg		; yes, handle it
		btfsc	NEW_TX			; last message sended?
		goto	SendHeaderW		; no, wait
		clrf	TXPOS			; xor byte
		clrf	TXCNT			; byte count
		movf	DUMMY,w
SendData:
		movwf	TEMP
		movlw	BUFTXINI
		addwf	TXCNT,w
		movwf	FSR
		movf	TEMP,w
		movwf	INDF
		xorwf	TXPOS,f
		incf	TXCNT,f
		return

SendMsg:
		movf	TXPOS,w			; Xor byte
		call	SendData
		bsf	NEW_TX			; transmit this
		return


SetMyAddress:
		movlw	DIP_COL			; read DIP switches
		movwf	OUTPORT
		DNOP
		DNOP
		DNOP
		comf	KEYPORT,w		; xxxxx???	Xbus address
		movwf	TEMP
		rrf	TEMP,f
		rrf	TEMP,f
		rrf	TEMP,w			; ???xxxxx

		bcf	LOKMAUS			; Read Lokmaus/Lenz mode switch-dip
		btfsc	PORTA,4
		bsf	LOKMAUS

		andlw	0x1F			; address beetwen 1 to 31
		iorlw	0x40			; anything to transmit default value
		movwf	MY_ADR
		movwf	TEMP			;	0000 0011
		swapf	TEMP,W			;	0011 0000
		xorwf	TEMP,F			;	0011 0011
		rrf	TEMP,W			;	X001 1001 1
		xorwf	TEMP,F			;	X010 1010
		btfsc	TEMP,2
		incf	TEMP, F
		rrf	TEMP,w			;	XX01 0101 0    ; C flag = parity (1=odd)
		btfsc	STATUS,C
		bsf	MY_ADR,7
		return



; ----- Keyboard routines


CheckKey:
		bcf	INTCON,T0IF
		btfsc	EmergOff
		return
		movf	COL,w			; Buffer address
		addlw	BUFKBINI
		movwf	FSR

		swapf	PORTA,w			; read Row 0 & Row 1:	??xxDCBA -> DCBA??xx
		andlw	0x03			; 000000xx
		movwf	TEMP2
		rrf	PORTB,w			; read Rows 2 to 6:  xxxxx??? -> ?xxxxx??
		andlw	0x7C			; 0xxxxx00
		iorwf	TEMP2,f			; 0xxxxxxx

CheckRowKey:
		clrf	PCLATH
		movf	ROW,w			; select bit
		call	BitConvert		;			w	TEMP	TEMP2	INDF
		movwf	TEMP			; save mask			00000010 		for ROW=1
		andwf	TEMP2,f			;					000000x0
		xorlw	0xFF			;			11111101
		andwf	INDF,f			; clear bit in buffer				??????0?
		movf	TEMP2,w			;			000000x0	
		iorwf	INDF,f			; save bit					??????x?

		movlw	d'16'			; old value
		addwf	FSR,f
		movf	INDF,w			; check changes		????????
		xorwf	TEMP2,w			;			??????y?
		andwf	TEMP,w			; mask			000000y0
		btfsc	STATUS,Z
		goto	CheckKeyEnd

SendKey:
		btfss	PIR1,TMR1IF		; Timer 1 every ((XTAL/4):8)/65536 aprox. 0.25s
		return

;		decf	COL,w			; COL=1, ROW=0?
;		iorwf	ROW,w
;		btfsc	STATUS,Z
;		goto	SendAll			; yes, send all turnouts

		call	TurnoutAddress		; ((COL - 1) * 7) + 1
		addwf	ROW,w
		movwf	TURNOUT
		movf	INDF,w			; old value
		andwf	TEMP,w			; mask
		btfss	STATUS,Z
		bsf	TURNOUT,7
		call	SetAccessory
		
		clrf	TMR1H			; reset timer for CDU
		clrf	TMR1L
		bcf	PIR1,TMR1IF

CheckKeyEnd:
		bsf	INTCON,T0IF		; no wait time
		incf	ROW,w			; next ROW (0..7)
		andlw	0x07
		movwf	ROW
		btfss	STATUS,Z
		return
		clrf	TMR0			; init timer
		bcf	INTCON,T0IF
		incf	COL,w			; next COL (0..15)
		andlw	0x0F	
		btfsc	STATUS,Z		; all readed?
		goto	AllKeysReaded		; yes
		movwf	COL
		movwf	PORTA
		return
AllKeysReaded:
		movlw	0x01
		movwf	COL			; next begin on COL1
		movwf	PORTA
SaveKeys:
		movlw	d'16'			; save previous keys status
		movwf	COUNT
		movlw	BUFKBINI
		movwf	FSR
SaveKeyNxt:
		movf	INDF,w			; get old key data
		movwf	TEMP
		movlw	d'16'
		addwf	FSR,f			; new address in other buffer
		movf	TEMP,w
		movwf	INDF			; save
		movlw	d'16'
		subwf	FSR,f			; restore address
		incf	FSR,f			; next byte
		decfsz	COUNT,f
		goto	SaveKeyNxt
		return


ReadKeyNew:
		movlw	d'16'			; save previous keys status
		movwf	COUNT
		movlw	BUFKBINI
		movwf	FSR
		clrf	COL
ReadKeyNewNxt:
		movf	COL,w
		movwf	PORTA			; 4515 column select, LED off
		clrf	TMR0
		bcf	INTCON,T0IF	
ReadKeyNewW:
		btfsc	NEW_MSG			; new message received?
		call	HandleMsg		; yes, handle it
		btfss	INTCON,T0IF		; wait time
		goto	ReadKeyNewW

		swapf	PORTA,w			; read Row 0 & Row 1
		andlw	0x03			; 00xx0000 -> 000000xx
		movwf	INDF
		rrf	PORTB,w			; read Rows 0 to 4
		andlw	0x7C			; 0xxxxx00
		iorwf	INDF,f			; 0xxxxxxx

		incf	COL,f
		incf	FSR,f
		decfsz	COUNT,f
		goto	ReadKeyNewNxt
		return


; ----- Serial port routines (XBus)

UART_INI:
		bcf	COMPORT,TXRX		; Disable TX
		clrf	XFLAGS			; clear XpressNet flags
		clrf	RX_LEN			; clear input buffer
		SEL_BANK_1
		movlw	HIGH_BAUD
		movwf	SPBRG
		movlw	0x64			; 9 bit, Enable TX, BRGH=1, TXD9=0
		movwf	TXSTA
		SEL_BANK_0
		movlw	0xD0			; Enable RX, 9 bit
		movwf	RCSTA
		clrw				; Settling time
SETTLE:
		addlw	0xFF
		btfss	STATUS,Z
    		goto 	SETTLE
		movf 	RCREG,w         	; flush receive buffer
		movf 	RCREG,w
		movf 	RCREG,w
		return

;------------------------------------------------------------------------------------------------



; ----- EEPROM default values


		org	0x2100

		dt	"Xbus-TCO"
		dt	"F.Caņada"
		dt	(__VERDAY   >> 4)  +0x30
		dt	(__VERDAY   & 0x0F)+0x30,"/"
		dt	(__VERMONTH >> 4)  +0x30
		dt	(__VERMONTH & 0x0F)+0x30,"/"
		dt	(__VERYEAR  >> 4)  +0x30
		dt	(__VERYEAR  & 0x0F)+0x30

	end
