;;======================================================================;;
;;			DCC Command Station Project 			;;
;;======================================================================;;
;;									;;
;; Program:		DCC_Gen_628 -- DCC generator			;;
;; Code:		Paco Caņada					;;
;; Platform:		Microchip PIC16F628, 4 Mhz			;;
;; Date:		06.08.2002					;;
;; First release:	18.08.2002					;;
;; LastDate:		24.02.2004					;;
;;									;;
;;======================================================================;;

; Retail version of DCC_Gen for use with MiniDCC hardware
;
; DCC Command Station version without PC comunications Lenz XBus v.2.3 (9600b)
; 14, 28 and 128 speed steps. FL,F0..F4 loco function support.
; Interrupt driven DCC signal generation, you can forget all about DCC timings!!
; This version without Stretching zeroes for DC operation on Loco address 0
; Routing: 26 routes (A..Z), up to 127 turnouts
; Turnout control: 1..99, locos DCC: 1..99
; Clock with 16 scale values from 1:1 to 60:1
;
; Processor PIC 16F628 running at 4MHz, isn't enough faster but works, better 8Mhz in full version of DCC_Gen
;
; All DCC timings are based on 58us interrupts. When no data is avaiable automatically an Idle
; packet is generated. The main program only has to fill the buffer when it is empty, if not
; an Idle packet is sended to track, this maintains DC control (full version) and continuous DCC generation.
;
;
; If you are interested in computer control of your trains, download full version of DCC_Gen
; that gives you Lenz Xbus v.2 & S88 based feedback modules support.

; Note: Maybe some product names are trademark of their respectives companies.

; 06/08/2002	Start of writing code. Interrupt driven DCC generator.
; 10/08/2002	Xbus v.2.3 basic functions writed
; 13/08/2002	Changed Speed & dir treatment
; 14/08/2002	Changed to locomotive stack
; 18/08/2002	Local operation completed
; 19/08/2002	First running prototype
; 21/08/2002	DC loco running, DCC generation OK for CV prog and accesory decoders
; 24/08/2002	First test of PC comunications. Tested with RailRoad & Co. and Koploper. Good results!
; 26/08/2002	Xbus loco control & information (14,27,28 steps). Added Xbus CV programming.
; 27/08/2002	Loco info bugs removed.
; 28/08/2002	Added language & slow/fast selection of DC speed. Now Play route uses TMR1 for timming.
; 03/09/2002	Corrected bug when transmit buffer full.
; 04/07/2003	Added scale time clock
; 10/01/2004	Inform to PC on changes in turnouts
; 19/01/2004	Added infrared remote control (RC5) support
; 24/02/2004	Retail version for PIC16F628 in MiniDCC hardware

; ----- Definitions

#define		__VERNUM	0x02
#define		__VERDAY	0x24
#define		__VERMONTH	0x02
#define		__VERYEAR	0x04


                list    p=16F628,r=hex

		errorlevel	-302		; suppress Bank warnings


	        INCLUDE "P16F628.INC"

                __FUSES _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF  & _INTRC_OSC_NOCLKOUT


#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'4000000'		; Crystal frequency

DCC_HalfBit	equ	D'58'			; DCC half bit duration

RTC_NUM		equ	D'256'-(((FXTAL * DCC_HalfBit)/D'4000000') - D'8')

; PortA
;DCC_OUT	equ	0
;E_LCD		equ	1
;KEY_S1		equ	2		; Emergency Stop
;KEY_S2		equ	3		; Select key
;TEST		equ	4

; RB0..RB3	LCD data out
; RB4		RS
; RB0..RB3	Keyboard out
; RB4..RB7	Keyboard in

RA_TRIS         equ     0x1C		; 
RB_TRIS         equ     0x00		; RB7..4: Keyb input, RB3..RB0: Keyb output & LCD data inp.
RA_INI          equ     0x00		; 
RB_INI          equ     0x00		; LCD & keyboard

PIE1_INI	equ	0x00		; peripheal interrupt
OPTION_INI	equ	0x08		; Option register, no timer prescaler, pull-ups
INTC_INI	equ	0xA0		; GIE, T0IE enable



; --- Pin assignement

; MiniDCC hardware:
;
; RA0		DCC out
; RA1		E for LCD
; RA2		Emergency stop key
; RA3		Select key
;
; RB0..RB3	LCD data out
; RB4		RS for LCD
; RB0..RB3	Keyboard out
; RB4..RB7	Keyboard in


OUTPORT		equ	PORTA
DCC_OUT		equ	0
E_LCD		equ	1

SWPORT		equ	PORTA
ESTOP		equ	2
SEL		equ	3

KEYPORT		equ	PORTB

LCDPORT		equ	PORTB
RS		equ	4

; --- DCC constants


SPREAMB		equ	d'15'			; Short preamble bits + end bit
LPREAMB		equ	d'25'			; Long  preamble bits + end bit

ZERO_LNG	equ	0x02			; Zero half bit length 2*58us: 116us

MaxLoco		equ	d'8'			; Controls up to 8 locos. 
						; You can change from 2 to 16

RTE_MEM		equ	0x01			; Routes memory initial EEPROM address
PLAY_DELAY	equ	d'2'			; delay playing routes aprox. 0.5 sec

; --- Other


MENU_SPEED	equ	0			; ID Menus
MENU_FUNC	equ	1
MENU_PLAY	equ	2
MENU_SERV	equ	3
MENU_CV		equ	4
MENU_REC	equ	5
MENU_CLK	equ	6
MENU_ESTOP	equ	7


CHR_N		equ	0			; LCD Graphic characters
CHR_14		equ	1
CHR_12		equ	2
CHR_2		equ	3
CHR_8		equ	4
CHR_FWD		equ	5
CHR_BKW		equ	6

CHR_DEF		equ	7			; number of chars defined

FIRSTLINE	equ	0x80
SECONDLINE	equ	0xC0


; ----- Variables

; --- Top on all banks Section

INT_W		equ	RAMINT+0x00		; Interrupt variables. Last RAM locations
INT_STAT	equ	RAMINT+0x01
INT_PCLATH	equ	RAMINT+0x02
INT_FSR		equ	RAMINT+0x03

EEDATA0		equ	RAMINT+0x04		; EEPROM shadow variables
EEADR0		equ	RAMINT+0x05

; --- Bank 0

HALFBIT		equ	RAMINI0+0x00		; current half bit
PRECOUNT	equ	RAMINI0+0x01		; preamble bits count

DCC_TIMER	equ	RAMINI0+0x06		; Half bit duration in 58us
DCC_BITCNT	equ	RAMINI0+0x07		; Current sended DCC byte bit count

DCC_DATA1	equ	RAMINI0+0x08		; DCC data buffer to send
DCC_DATA2	equ	RAMINI0+0x09		; 
DCC_DATA3	equ	RAMINI0+0x0A		; 
DCC_DATA4	equ	RAMINI0+0x0B		; 
;DCC_DATA5	equ	RAMINI0+0x0C		; 
DCC_CTRL	equ	RAMINI0+0x0D		; 

INT_DATA1	equ	RAMINI0+0x0E		; Interrupt. DCC data currently sended
INT_DATA2	equ	RAMINI0+0x0F		; 
INT_DATA3	equ	RAMINI0+0x10		; 
INT_DATA4	equ	RAMINI0+0x11		; 
;INT_DATA5	equ	RAMINI0+0x12		; 
INT_CTRL	equ	RAMINI0+0x13		; 

KEY		equ	RAMINI0+0x14		; key pressed
KEYFLAG		equ	RAMINI0+0x15		; Keyboard flags
DEBOUNCE	equ	RAMINI0+0x16		; debounce time
NEWKEY		equ	RAMINI0+0x17		; new key pressed

LCD_DATA	equ	RAMINI0+0x18		; Internal Data sended to LCD
TEMP		equ	RAMINI0+0x19		; Temporary value
COUNT		equ	RAMINI0+0x1A		; general counter
R0		equ	RAMINI0+0x1B		; General & Bin to BCD registers
R1		equ	RAMINI0+0x1C
R2		equ	RAMINI0+0x1D
LSB		equ	RAMINI0+0x1E
MSB		equ	RAMINI0+0x1F

DCC_STATUS	equ	RAMINI0+0x2F		; Command Station Status
LOCOCNT		equ	RAMINI0+0x30		; Loco counter
LOCOPTR		equ	RAMINI0+0x31		; Loco data statck pointer
CURRPTR		equ	RAMINI0+0x32		; Current Controlled Loco pointer
CURRADR		equ	RAMINI0+0x33		; Current Loco address
CURRSPD		equ	RAMINI0+0x34		; Current Loco speed
CURRFNC		equ	RAMINI0+0x35		; Current Loco functions
FLAGS		equ	RAMINI0+0x36		; General flags
INIFLAGS	equ	RAMINI0+0x37		; Start up flags
FSR_TEMP	equ	RAMINI0+0x38		; Temporal register

MENU		equ	RAMINI0+0x3A		; Current menu
UNITS		equ	RAMINI0+0x3B		; Units of typed number
DIZAIN		equ	RAMINI0+0x3C		; Dizaines of typed number
NUMBER		equ	RAMINI0+0x3D		; Typed number in bin

RTE_INDEX	equ	RAMINI0+0x42		; current route index
ROUTE		equ	RAMINI0+0x43		; current route
RTE_INI		equ	RAMINI0+0x44		; Initial address of route
TURNOUT		equ	RAMINI0+0x45		; current turnout
PLAY_TMR	equ	RAMINI0+0x46		; Play timer delay

CVHIGH		equ	RAMINI0+0x47		; CV programing
CVLOW		equ	RAMINI0+0x48
CVDATA		equ	RAMINI0+0x49
CVTYPE		equ	RAMINI0+0x4A

S_HOUR		equ	RAMINI0+0x4B		; System time
S_MIN		equ	RAMINI0+0x4C
S_QUARTER	equ	RAMINI0+0x4D



; --- Buffers

LocoStack	equ	RAMINI1+0x00		; Locomotive stack order (0x00..0x07...0x0F)
LocoBase	equ	RAMINI1+0x10		; Locomotive information (0x10..0x2F...0x4F)
LocoAd		equ	0
LocoSpeed	equ	1
LocoFunc	equ	2
LocoHead	equ	3

; --- Flags

#define		SEND_PREAMB	FLAGS,0		; Sending preamble bits
#define		STARTBIT	FLAGS,1		; Sending Start bit
#define		DC_POL		FLAGS,2		; DC bias polarity
#define		I2CREPST	FLAGS,3		; Next is I2C repeated start
#define		RXEMPTY		FLAGS,4		; Serial receive buffer empty
#define		LAST_TRN	FLAGS,5		; Find last turnout in route
#define		PLAYING		FLAGS,6		; Playing a route
#define		MENU_CHG	FLAGS,7		; Menu has changed

#define		KeyHit		KEYFLAG,0	; Key hitted
#define		ServKey		KEYFLAG,1	; Key need service
#define		DbnceOn		KEYFLAG,2	; Key debounce time
#define		KeyRep		KEYFLAG,3	; Key repeated
#define		FlagRS		KEYFLAG,4	; RS status

;#define		DC_LINEAL	INIFLAGS,7	; DC lineal aproximation
;#define		ENGLISH		INIFLAGS,6	; Language selection
							; 0..3:	Scale Time

#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

Loco128		equ	7			; CURRFNC bit definition
Loco28		equ	6
LocoDir		equ	5
LocoFL		equ	4
LocoF4		equ	3
LocoF3		equ	2
LocoF2		equ	1
LocoF1		equ	0




; ------ Program Section

		org	0x000

PowerUp:
		clrf	STATUS			; ensure we are at bank 0
		clrf	PCLATH			; ensure page bits before goto !!
		clrf	INTCON			; disable all interrupts
		goto	Start



; --- Interrupt every 58us (DCC one half bit duration)


		org	0x004

Interrupt:
		movwf	INT_W			; 1 Save registers W, STATUS, PCLATH
		swapf	STATUS,w		; 2
		clrf	STATUS			; 3 Ensure port / timer access in bank 0
		movwf	INT_STAT		; 4
		movlw	RTC_NUM			; 5 Reload timer for 58us
		movwf	TMR0			; 6
		movf	PCLATH,w		; 
;		movwf	INT_PCLATH		; save PCLATH, interrupt uses page 0
;		clrf	PCLATH			; 

Int_DCC_Out:
		btfss	HALFBIT,0		; out last calculated DCC bit
		bcf	OUTPORT,DCC_OUT
		btfsc	HALFBIT,0
		bsf	OUTPORT,DCC_OUT

Int_DCC:
		decfsz	DCC_TIMER,f		; wait end of half bit
		goto	EndInt
		incf	DCC_TIMER,f		; reload DCC timer with 1 half bit length default
		incf	HALFBIT,f		; set current half bit

		btfss	HALFBIT,0		; goto to current hi/lo half bit routine
		goto	Int_High_Half

Int_Low_Half:
		movf	INT_CTRL,w		; Sending?
		btfss	STATUS,Z	
		goto	Int_Low_Bit		; yes. Send low half bit

		movf	DCC_CTRL,w		; no. New packet avaiable?
		btfsc	STATUS,Z
		goto	Int_Idle		; no, send IDLE packet.

		andlw	0x07			; yes, save count & clear preamble type bit.
		movwf	INT_CTRL
		movf	DCC_DATA1,w		; save data values
		movwf	INT_DATA1
		movf	DCC_DATA2,w
		movwf	INT_DATA2
		movf	DCC_DATA3,w
		movwf	INT_DATA3
		movf	DCC_DATA4,w
		movwf	INT_DATA4
;		movf	DCC_DATA5,w		; DATA5 actually not used
;		movwf	INT_DATA5
		movlw	SPREAMB
		btfsc	DCC_CTRL,7		; set preamble count
		movlw	LPREAMB
		movwf	PRECOUNT
		bsf	SEND_PREAMB		; now sending a preamble
		clrf	DCC_CTRL		; set free DCC buffer
		goto	Int_Pre_Low_Half	; this is first half bit of preamble
		
Int_Low_Bit:
		btfsc	SEND_PREAMB		; sending preamble?
		goto	Int_Pre_Low_Half	; yes, send low half bit of preamble

		btfsc	STARTBIT		; do we have to send start bit?
		goto	Int_Start_Low		; yes, send it

		btfsc	INT_DATA1,7		; if bit is one, nothing to do
		goto	Int_One_Low
Int_Start_Low:
;Int_Zero_Low:
		movlw	ZERO_LNG		; put current bit length
		movwf	DCC_TIMER		; put calc length only on zero
		goto	EndInt
		
Int_High_Half:
		btfsc	SEND_PREAMB		; sending preamble?
		goto	Int_Pre_High_Half	; yes, send high half bit of preamble

		btfsc	STARTBIT		; do we have to send start bit?
		goto	Int_Start_High		; yes, send it

		movlw	ZERO_LNG		; put current bit length
		btfss	INT_DATA1,7
		movwf	DCC_TIMER		; put calc length only on zero

;		rlf	INT_DATA5,f		; prepare next bit
		rlf	INT_DATA4,f
		rlf	INT_DATA3,f
		rlf	INT_DATA2,f
		rlf	INT_DATA1,f

		decfsz	DCC_BITCNT,f		; check end of byte
		goto	EndInt
		bsf	STARTBIT		; byte sended, send start bit
		decf	INT_CTRL,f		; check end of packet
		goto	EndInt

Int_Start_High:
		bcf	STARTBIT		; next is normal bit
		movlw	0x08
		movwf	DCC_BITCNT		; set bit count
;Int_Zero_High:
		movlw	ZERO_LNG		; put current bit length
		movwf	DCC_TIMER		; put calc length, start bit is always 0
		goto	EndInt

Int_Pre_High_Half:
		decf	PRECOUNT,f		; check preamble bits sended
		btfsc	STATUS,Z
		bcf	SEND_PREAMB
		btfsc	STATUS,Z
		bsf	STARTBIT		; send start bit after preamble
		goto	EndInt

Int_Idle:
		movlw	SPREAMB			; put IDLE packet
		movwf	PRECOUNT
		bsf	SEND_PREAMB
		movlw	0xFF
		movwf	INT_DATA1
		clrf	INT_DATA2
		movwf	INT_DATA3
		movlw	0x03
		movwf	INT_CTRL		; 3 bytes to send
;		goto	Int_Pre_Low_Half	; this is first half bit of preamble

Int_Pre_Low_Half:		
Int_One_Low:

EndInt:
;		movf	INT_PCLATH,w
;		movwf	PCLATH
		bcf	INTCON,T0IF		; Clear Timer interrupt bit
		swapf	INT_STAT,w
		movwf	STATUS
		swapf	INT_W,f
		swapf	INT_W,w
		retfie


; --- Menu Printing  ( Be sure is in first 256 bytes of program code)


ShowMenu:
		clrf	PCLATH
		bcf	MENU_CHG
		movf	MENU,w
		andlw	0x07
		addwf	PCL,f

		goto	ShowLocoSel		; 0
		goto	ShowLocoCtrl		; 1
		goto	ShowPlayRte		; 2
		goto	ShowService		; 3
		goto	ShowCV			; 4
		goto	ShowRecRte		; 5
		goto	ShowClock		; 6
		goto	ShowEstop		; 7

	
; ----- Power Up Section ( Be sure is in first 2K program code)

Start:
		movlw	0x07			; PIC 16F628, all digital
		movwf	CMCON                          
		clrf	RCSTA			; disable UART
		movlw   RA_INI			; Set ports
		movwf   PORTA                         
		movlw   RB_INI
		movwf   PORTB
		SEL_BANK_1
		movlw	PIE1_INI
		movwf	PIE1			; peripheral interrupts
		movlw	RB_TRIS
		movwf   TRISB
		movlw   RA_TRIS         	; Set port A I/O configuration
		movwf   TRISA
		movlw   OPTION_INI      	; PORTB pull-ups disabled, Timer configuration
		movwf   OPTION_REG
		movlw	0x0B			; internal oscillator to 4MHz
		movwf	PCON
		SEL_BANK_0
		clrf	TMR0			; init timers
		clrf	CCP1CON
		movlw	0x21			; Timer 1. Run with 1:4 Prescaler (4MHz)
		movwf	T1CON
		clrf	T2CON			; Timer 2. Shut off

		movlw	RAMINI0		; Clear variables
		movwf	FSR
ClearRAM:
		clrf	INDF
		incf	FSR,f
		btfss	FSR,7
		goto	ClearRAM
DCC_INIT:
;		clrf	HALFBIT			; low half bit first
;		clrf	DCC_CTRL		; empty DCC buffer
;		clrf	INT_CTRL
;		clrf	FLAGS
;		clrf	DCC_STATUS
		movlw	0x01
		movwf	DCC_TIMER		; init DCC bit duration

		call	LCD_INI
		call	LCDDEF
		movlw	0x00			; get ini flags
		call	EERead
		movwf	INIFLAGS

		movlw	INTC_INI		; Set interrupt TMR0
		movwf	INTCON

		call	DCCReset
		call	InitStack		; 'Select Loco:  00'
		clrf	NUMBER			; '--: STOP . -----'
		movlw	0xFF
		movwf	CURRADR
		movlw	0x01
		movwf	CURRSPD
		movlw	b'01100000'		; 28 steps, forward, FL,F1..F4 off
		movwf	CURRFNC
		call	GetRecentPtr
		movwf	CURRPTR

		movlw	'A'			; init routes vars
		movwf	ROUTE
		call	GetRteFirst
		bcf	PLAYING
		call	ReloadScale		; scale time reload

		movlw	d'15'			; Show Welcome
		call	LCDSTR
		movlw	SECONDLINE
		call	LCDCON
		movlw	d'0'	
		call	LCDSTR
		movlw	d'250'
		movwf	COUNT
WelWait:
		call	Delay4ms
		decfsz	COUNT,f
		goto	WelWait

		movlw	MENU_SPEED		; Show loco selection menu
		movwf	MENU
		call	ShowMenu


; --- Main Loop


MainLoop:
;		bcf	EmergOff		; Normal operations
;		bcf	EmergStop
;		bcf	ServiceMode
		clrf	DCC_STATUS
OpsMode:
		btfsc	ServiceMode
		goto	PgmMode

		movlw	MaxLoco			; send loco speed & direction
		movwf	LOCOCNT
OpsModeLoco:
		call	SendDCCLoco
		decfsz	LOCOCNT,f
		goto	OpsModeLoco

		call	DCC_Idle		; give little time. Ensure feedback, keyboard & comunic

		movlw	MaxLoco
		movwf	LOCOCNT
OpsModeFunc:
		call	SendDCCFunc		; send loco functions
		decfsz	LOCOCNT,f
		goto	OpsModeFunc

		call	DCC_Idle		; give a little time

		btfsc	MENU_CHG
		call	ShowMenu		; show new menu if PC changed
		goto	OpsMode

PgmMode:
		call	DCCReset		; Service mode
		movlw	0x00
		movwf	CURRSPD			; stop locos
		call	SpeedAllLocos
ServMode:
		call	DCC_Idle		; give a little time. Ensure feedback, keyboard & comunic

		btfss	ServiceMode
		goto	MainLoop		; if not in service mode goto normal operations

		btfsc	MENU_CHG
		call	ShowMenu		; show new menu if PC changed
		goto	ServMode


; --- Main DCC Loop

SendDCCFunc:
		decf	LOCOCNT,w		; get loco counter for position
		call	GetLocoPtr
		movwf	FSR
		movwf	LOCOPTR
		incf	INDF,w			; Is free? Loco=FFh
		btfsc	STATUS,Z
		return				; yes, nothing to do
		call	WaitDCC
		call	DCCFunc			; send loco functions
		goto	SendDCCCommon

SendDCCLoco:
		decf	LOCOCNT,w		; get loco counter for position
		call	GetLocoPtr
		movwf	FSR
		movwf	LOCOPTR
		incf	INDF,w			; Is free? Loco=FFh
		btfsc	STATUS,Z
		return				; yes, nothing to do
		call	WaitDCC
		call	DCCLoco			; send loco data
SendDCCCommon:
		call	ScanKeyb		; scan keyboard
		btfsc	ServKey
		call	ServiceKey		; if key pressed, exe key function
		btfss	PIR1,TMR1IF		; Timer 1 every ((XTAL/4):4)/65536 aprox. 0.25s
		return
		bcf	PIR1,TMR1IF
		btfsc	PLAYING
		call	PlayRoute		; play route
		call	Clock			; clock tick
		return

DCC_Idle:
		call	WaitDCC			; insert a DCC idle packet
		movlw	0xFF
		movwf	DCC_DATA1
		movwf	DCC_DATA3
		clrf	DCC_DATA2
		movlw	0x03
		movwf	DCC_CTRL
		goto	SendDCCCommon		; Ensure feedback, keyboard & comunic
		
WaitDCC:
		movf	DCC_CTRL,w		; wait until we can send a DCC packet
		btfsc	STATUS,Z
		return
		goto	WaitDCC


; --- Stack operations


InitStack:
		movlw	MaxLoco
		movwf	LOCOCNT
		movlw	LOW (LocoStack)
		movwf	FSR
ClearStack:
		decf	LOCOCNT,w		; Clear stack order: MaxLoco..0
		movwf	INDF
		incf	FSR,f
		decfsz	LOCOCNT,f
		goto	ClearStack
		movlw	MaxLoco
		movwf	LOCOCNT
		movlw	LOW (LocoBase)
		movwf	FSR
ClearLoco:
		movlw	0xFF			; Clear loco data address
		movwf	INDF
		movlw	0x04
		addwf	FSR,f
		decfsz	LOCOCNT,f
		goto	ClearLoco
		return


AddLoco:
						; R0: Address, R1: Speed, R2: Functions
		movlw	MaxLoco
		movwf	COUNT
AddLocoNxt:
		movlw	LOW(LocoStack)-1
		addwf	COUNT,w
		movwf	FSR
		movf	INDF,w			; get stack pos
		movwf	TEMP
		rlf	TEMP,f
		rlf	TEMP,w
		andlw	0xFC
		addlw	LOW(LocoBase)		; get loco data
		movwf	FSR
		incf	INDF,w
		btfss	STATUS,Z
		goto	AddLocoFull		; if not free, next one
AddLocoData:
		movf	R0,w			; save data
		movwf	INDF			; address
		incf	FSR,f
		movf	R1,w
		movwf	INDF			; speed
		incf	FSR,f
		movf	R2,w
		movwf	INDF			; 'SSDFFFFF' SS: steps, D: dir, F: Fx
		decf	COUNT,w

PutRecent:		
		movwf	COUNT			; w = recent one 0..MaxLoco-1
		movf	COUNT,w			; if 0 nothing to do
		btfsc	STATUS,Z
		retlw	0x00
		addlw	LOW (LocoStack)
		movwf	FSR
		movf	INDF,w
		movwf	TEMP
		movlw	LOW (LocoStack)
		movwf	FSR
		movf	TEMP,w
PutRecentNxt:
		xorwf	INDF,f			; exchange w and INDF
		xorwf	INDF,w
		xorwf	INDF,f
		incf	FSR,f
		decfsz	COUNT,f
		goto	PutRecentNxt
		movwf	INDF
		retlw	0x00			; return first location stack

AddLocoFull:
		decfsz	COUNT,f
		goto	AddLocoNxt
EraseOldest:		
		movlw	MaxLoco			; get oldest loco
EraseOlder:
		movwf	COUNT
		addlw	LOW (LocoStack)-1
		movwf	FSR
		movf	INDF,w			; get stack pos
		movwf	TEMP
		rlf	TEMP,f
		rlf	TEMP,w
		andlw	0xFC
		addlw	LOW(LocoBase)		; get loco data
		movwf	FSR
		movf	INDF,w
		xorwf	CURRADR,w
		btfss	STATUS,Z
		goto	AddLocoData		; save new if not currently operated.
		decf	COUNT,w
		goto	EraseOlder		; save in not oldest


GetRecentPtr:
		movlw	0x00			; get recent
GetPointer:
		addlw	LOW(LocoStack)		; w = stack pos
GetStackPtr:
		movwf	FSR
		movf	INDF,w			; get stack pos
GetLocoPtr:
		movwf	TEMP
		rlf	TEMP,f
		rlf	TEMP,w
		andlw	0xFC
		addlw	LOW(LocoBase)		; get loco data pointer
		return

FindLoco:
		movwf	LSB			; find w loco in stack from recent one
		movlw	0xFF
		movwf	COUNT
FindLocoNxt:
		incf	COUNT,f
		movlw	MaxLoco
		xorwf	COUNT,w
		btfsc	STATUS,Z
		retlw	0xFF			; not found
		movf	COUNT,w
		call	GetPointer
		movwf	FSR
		movf	INDF,w
		xorwf	LSB,w
		btfss	STATUS,Z
		goto	FindLocoNxt
		movf	COUNT,w
		return


EstopAllLocos:
		movlw	0x01			; E. stop speed
SpeedAllLocos:
		movwf	TEMP			; w= Speed
		movlw	LOW(LocoBase)+LocoSpeed
		movwf	FSR
		movlw	MaxLoco
		movwf	COUNT
SpdAllLoc:
		movf	TEMP,w
		movwf	INDF
		movlw	0x04
		addwf	FSR,f
		decfsz	COUNT,f
		goto	SpdAllLoc
		return

LocoRecall:
		call	GetRecentPtr		; get recent loco data
		movwf	CURRPTR
		movwf	FSR
		movf	INDF,w
		movwf	CURRADR			; address
		incf	FSR,f
		movf	INDF,w
		movwf	CURRSPD			; speed
		incf	FSR,f
		movf	INDF,w
		movwf	CURRFNC			; functions
		return



; --- LCD routines


LCD_INI:
		call	Delay4ms		; 10ms delay to reset LCD
		call	Delay4ms
		call	Delay2ms
		movlw	0x33			; set 8 bit mode
		call	LCDCON
		movlw	0x33			; set 8 bit mode
		call	LCDCON
		movlw	0x33			; set 8 bit mode
		call	LCDCON
		movlw	0x20			; set 4 bit mode
		call	LCDCON
		movlw	0x06			; incr dir. no display shift
		call	LCDCON
		movlw	0x0C			; display on, cursor/flash off
		call	LCDCON
;		movlw	0x02			; home
;		call	LCDCON
ClearLCD:
		movlw	0x01			; Clear LCD
		call	LCDCON
		goto	Delay2ms

LCDLONG:
		movwf	LSB			; Show bin byte 0..9999
		call	BIN2BCD
		movf	R1,w
		call	LCDBCD			; thousands & cents
		movf	R0,w
		goto	LCDBCD			; tens & units

LCDBYTE:
		clrf	MSB			; Show bin byte 0..255
LCDWORD:
		movwf	LSB			; Show bin byte 0..999
		call	BIN2BCD
		movf	R1,w
		call	LCDBCDL			; cents
		movf	R0,w
		goto	LCDBCD
LCDBIN:
		movwf	LSB			; Show bin: 0..99 
		clrf	MSB
		call	BIN2BCD
		movf	R0,w
LCDBCD:
		movwf	TEMP			; out 2 BCD digits
		swapf	TEMP,w
		andlw	0x0F			; out BCD tens
		iorlw	0x30
		call	LCDCHR
		movf	TEMP,w
LCDBCDL:
		andlw	0x0F			; out BCD units
		iorlw	0x30
LCDCHR:
		bsf	FlagRS			; send char to LCD
		goto	LCDOUT
LCDCON:
		bcf	FlagRS			; send command to LCD
LCDOUT:
		movwf	LCD_DATA
		movlw	0x06			; delay 60us
		call	Delay10us
		call	LCDSEND
LCDSEND:
		swapf	LCD_DATA,f		; changes due to hardware
		movf	LCD_DATA,w		; send as 4 bit mode
		andlw	0x0F	
		btfsc	FlagRS
		iorlw	0x10

		movwf	LCDPORT
		bsf	OUTPORT,E_LCD
		DNOP
		DNOP
		bcf	OUTPORT,E_LCD
		return

LCDDEF:
		movlw	0x40			; start of CG RAM
		call	LCDCON
		movlw	(CHR_DEF * 8)		; 7 chars, 8 bytes each (5x8 matrix)
		movwf	COUNT
LCDDEFN:
		movlw	HIGH (NEWLCDCHR)
		movwf	PCLATH
		movf	COUNT,w
		sublw	(CHR_DEF * 8)
		call	NEWLCDCHR
		call	LCDCHR
		decfsz	COUNT,f
		goto	LCDDEFN
		movlw	FIRSTLINE
		goto	LCDCON


		
LCDSTR:
					; Print w string
		movwf	LSB		
		movlw	HIGH (String)
		movwf	PCLATH
		bcf	STATUS,C		; w * 2
		rlf	LSB,f
		movf	LSB,w
		call	String
		movwf	MSB			; PCLATH
		incf	LSB,w	
		call	String
		movwf	LSB			; PCL
LCDSTRCHR:
		call	GetChar
		movwf	TEMP
		btfsc	TEMP,7
		return
		call	LCDCHR
		incf	LSB,f			; next character
		btfsc	STATUS,Z
		incf	MSB,f
		goto	LCDSTRCHR

GetChar:
		movf	MSB,w			; goto MSB,LSB
		movwf	PCLATH
		movf	LSB,w
		movwf	PCL


BIN2BCD:                       
		bcf	STATUS,C	; 16 bit bin to 5 digit BCD
		movlw	0x10                          
		movwf	COUNT
		clrf	R0                          
		clrf	R1                          
		clrf	R2   
LOOP16:                       
		rlf	LSB,f                          
		rlf	MSB,f                          
		rlf	R0,f                          
		rlf	R1,f                          
		rlf	R2,f                          
		decfsz	COUNT,f                          
		goto	ADJDEC
		return
ADJDEC:                         
		movlw	R0                          
		call	ADJBCD
		movlw	R1                          
		call	ADJBCD
		movlw	R2                          
		call	ADJBCD
		goto	LOOP16 
ADJBCD:                        
		movwf 	FSR                           
		movlw 	0x3                           
		addwf 	INDF,W                         
		movwf	TEMP
		btfsc	TEMP,3                      
		movwf	INDF                           
		movlw	0x30                          
		addwf	INDF,W                         
		movwf	TEMP
		btfsc	TEMP,7                      
		movwf	INDF                           
		return


; --- Print routines

ShowClock:
		movlw	FIRSTLINE		; Show Clock setup
		call	LCDCON
		movlw	d'16'
		call	LCDSTR
		movlw	SECONDLINE
		call	LCDCON
		movlw	d'14'
		call	LCDSTR
		movlw	SECONDLINE
		call	LCDCON
		movf	S_HOUR,w		; 'HH:MM    SS:1  '
		call	LCDBIN
		movlw	':'
		call	LCDCHR
		movf	S_MIN,w
		call	LCDBIN
		movlw	0xCA
		call	LCDCON
		call	W2Scale
		call	LCDBIN
		movlw	':'
		call	LCDCHR
		movlw	'1'
		goto	LCDCHR

ShowCV:
		movlw	FIRSTLINE		; Show CV parameters
		call	LCDCON
		movf	CVTYPE,w
		movwf	TEMP
		movlw	d'5'			; CVTYPE=0
		decf	TEMP,f
		btfsc	STATUS,Z
		movlw	d'6'			; CVTYPE=1
		decf	TEMP,f
		btfsc	STATUS,Z
		movlw	d'7'			; CVTYPE=2
		decf	TEMP,f
		btfsc	STATUS,Z
		movlw	d'8'			; CVTYPE=3
;		decf	TEMP,f
;		btfsc	STATUS,Z
;		movlw	d'11'			; CVTYPE=4
		call	LCDSTR
		movlw	d'9'
		call	LCDSTR
		movlw	SECONDLINE
		call	LCDCON
		movf	CVHIGH,w
		movwf	MSB			; normalize CV number
		incf	CVLOW,w
		btfsc	STATUS,Z
		incf	MSB,f
		call	LCDLONG
		movlw	'-'
		call	LCDCHR
		movf	CVDATA,w
		call	LCDBYTE
		movlw	' '
		call	LCDCHR
		movlw	' '
		call	LCDCHR
		movf	CVHIGH,w		; show page/reg
		movwf	MSB
		movf	CVLOW,w
		movwf	LSB
		rrf	MSB,f
		rrf	LSB,f
		rrf	MSB,f
		rrf	LSB,f
		incf	LSB,w			; normalize page
		call	LCDBYTE
		movlw	'-'
		call	LCDCHR
		movf	CVLOW,w
		andlw	0x03
		goto	LCDBCD


ShowRecRte:
		movlw	FIRSTLINE		; Rec route
		call	LCDCON
		movlw	d'10'
		call	LCDSTR
		call	CountRemain
		call	LCDBYTE
		movlw	SECONDLINE
		call	LCDCON
		movlw	d'13'
		call	LCDSTR
		goto	ShowRoute

ShowPlayRte:
		movlw	FIRSTLINE		; Play route
		call	LCDCON
		movlw	d'3'
		call	LCDSTR
		movf	S_HOUR,w		; show current scaled time
		call	LCDBIN
		movlw	':'
		call	LCDCHR
		movf	S_MIN,w
		call	LCDBIN

		movlw	SECONDLINE
		call	LCDCON
		movlw	d'12'
		call	LCDSTR
ShowRoute:
		movf	ROUTE,w			; 'A-001: 01/'
		call	LCDCHR
		movlw	'-'
		call	LCDCHR
		movf	RTE_INDEX,w
		call	LCDBYTE
		movlw	':'
		call	LCDCHR
		movlw	' '
		call	LCDCHR
		movf	NUMBER,w
		call	LCDBIN
		movlw	0x7C			; 'I'
		btfsc	TURNOUT,7
		movlw	'/'
		goto	LCDCHR


ShowService:
		movlw	FIRSTLINE
		call	LCDCON
		movlw	d'4'			; Show Service Mode
		call	LCDSTR
		goto	ShowBlankL2
		
ShowEstop:
		movlw	FIRSTLINE
		call	LCDCON
		movlw	d'11'			; Show Emergency Stop
		call	LCDSTR
ShowBlankL2:
		movlw	SECONDLINE		; Show blank line
		call	LCDCON
		movlw	d'14'
		goto	LCDSTR

ShowLocoCtrl:
		movlw	FIRSTLINE
		call	LCDCON
		movlw	d'2'			; Show Loco Control
		call	LCDSTR
		movf	S_HOUR,w		; show current scaled time
		call	LCDBIN
		movlw	':'
		call	LCDCHR
		movf	S_MIN,w
		call	LCDBIN
		goto	LocoShow		; show loco data
ShowLocoSel:
		movlw	FIRSTLINE
		call	LCDCON
		movlw	d'1'			; Show Select Loco
		call	LCDSTR
		movf	NUMBER,w
		call	LCDBIN

LocoShow:
		movlw	SECONDLINE		; Show Loco address & FL
		call	LCDCON

		incf	CURRADR,w		; loco Selected?
		btfss	STATUS,Z
		goto	LocoShwNum		; no, '--'
		movlw	'-'
		call	LCDCHR
		movlw	'-'
		call	LCDCHR
		goto	LocoShwData		
LocoShwNum:
		movf	CURRADR,w		; yes, Show address
		call	LCDBIN
LocoShwData:
		movlw	':'
		call	LCDCHR
		movlw	' '
		call	LCDCHR
		movf	CURRSPD,w		; emergency stop?
		xorlw	0x01
		btfss	STATUS,Z
		goto	LocoShwSpd
		movlw	'S'			; yes, 'STOP'
		call	LCDCHR
		movlw	'T'
		call	LCDCHR
		movlw	'O'
		call	LCDCHR
		movlw	'P'
		call	LCDCHR
		goto	LocoShwStep
LocoShwSpd:
		movlw	CHR_FWD			; no, dir & speed
		btfss	CURRFNC,LocoDir
		movlw	CHR_BKW
		call	LCDCHR
		movf	CURRSPD,w
		btfsc	STATUS,Z
		goto	LocoSpdNum
		addlw	0xFF			; normalize for 14 & 128 steps
		btfsc	CURRFNC,Loco28
		addlw	0xFE			; normalize for 28 steps
LocoSpdNum:
		call	LCDBYTE
LocoShwStep:
		movlw	' '
		call	LCDCHR
		movlw	CHR_14			; Char 14 steps
		btfsc	CURRFNC,Loco28
		movlw	CHR_2			; Char 28 steps
		btfsc	CURRFNC,Loco128
		movlw	CHR_12			; char for 128 steps
		call	LCDCHR
		movlw	' '			; Char 14 steps
		btfsc	CURRFNC,Loco28
		movlw	CHR_8			; Char 28 steps
		btfsc	CURRFNC,Loco128
		movlw	CHR_8			; char for 128 steps
		call	LCDCHR
		movlw	'-'
		btfsc	CURRFNC,LocoFL		; show FL,F1,F2,F3,F4
		movlw	'*'
		call	LCDCHR
		movlw	'-'
		btfsc	CURRFNC,LocoF1
		movlw	'1'
		call	LCDCHR
		movlw	'-'
		btfsc	CURRFNC,LocoF2
		movlw	'2'
		call	LCDCHR
		movlw	'-'
		btfsc	CURRFNC,LocoF3
		movlw	'3'
		call	LCDCHR
		movlw	'-'
		btfsc	CURRFNC,LocoF4
		movlw	'4'
		goto	LCDCHR



; --- Delays

Delay4ms:
		call	Delay2ms
Delay2ms:
		movlw	d'200'			; Delay 200 * 10us = 2ms
Delay10us:
		DNOP				; 1,2	Delay w * 10us
		DNOP				; 3,4
		DNOP				; 5,6

		addlw	0xFF			; 7
		btfss	STATUS,Z		; 8
		goto	Delay10us		; 9,10
		return



; --- Keyboard Section


ScanKeyb:
		btfss	SWPORT,ESTOP		; scan Stop Key
		goto	StopPress
		btfss	SWPORT,SEL		; scan Sel Key
		goto	SelPress

		btfss	DbnceOn			; debounce time?
		goto	Scan			; no, scan keyboard
		decfsz	DEBOUNCE,f		; yes, wait debouncing
		return
		bcf	DbnceOn			; end debounce
		movlw	d'70'			; first time auto repeat
		movwf	DEBOUNCE
		return
		
Scan:
		bsf	STATUS,RP0		; changes due to hardware
		movlw	0xF0
		movwf	KEYPORT
		bcf	STATUS,RP0

		movlw	0xF7			; Init Column
		movwf	TEMP
		clrf	KEY
ScanNext:
		movf	TEMP,w
		movwf	KEYPORT			; out column
		DNOP
		DNOP
		comf	KEYPORT,w		; a 1 indicates a key pressed
		andlw	0xF0
 		btfsc	STATUS,Z		; key pressed?
		goto	NoKey			; no

		btfsc	KeyHit			; yes, last key released?
		goto	KeyRepeat		; no
		bsf	KeyHit
		bcf	KeyRep
		movwf	TEMP
		rlf	TEMP,f
		btfsc	STATUS,C
		goto	KeyValue		; Row 0
		incf	KEY,f
		rlf	TEMP,f
		btfsc	STATUS,C
		goto	KeyValue		; Row 1
		incf	KEY,f
		rlf	TEMP,f
		btfsc	STATUS,C
		goto	KeyValue		; Row 2
		incf	KEY,f			; Row 3
KeyValue:
		movf	KEY,w
		movwf	NEWKEY
		bsf	DbnceOn
		movlw	0x06
		movwf	DEBOUNCE
KeyReturn:
		bsf	ServKey			; key need service
		goto	SetKeyPort		; **

NoKey:
		movlw	0x04			; next column 
		addwf	KEY,f
		rrf	TEMP,f			; next column bit
		btfsc	STATUS,C		; all columns readed?
		goto	ScanNext		; no, next
		bcf	KeyHit			; set no key pressed
		bcf	KeyRep
SetKeyPort:
		bsf	STATUS,RP0		; all outs default
		clrf	KEYPORT
		bcf	STATUS,RP0
		return

KeyRepeat:
		decfsz	DEBOUNCE,f		; wait auto repeat time
		goto	SetKeyPort		; **
		bsf	KeyRep
		movlw	d'12'			; next time auto repeat
		movwf	DEBOUNCE
		goto	KeyReturn		; set key pressed
	

StopPress:
		btfsc	DbnceOn			; exit if debounce time
		return
		btfsc	ServiceMode		; in Service Mode do as SEL
		goto	SelPress
		btfsc	EmergStop
		goto	ResumePress		; if in emergency stop, resume
		bsf	DbnceOn
		movlw	0x08
		movwf	DEBOUNCE		; set long debounce time
		bsf	EmergStop
		call	EstopAllLocos		; emergency stop
		call	LocoRecall
		movlw	MENU_ESTOP
		movwf	MENU
		goto	SetNumber
ResumePress:
		bsf	DbnceOn
		movlw	0x06
		movwf	DEBOUNCE		; set debounce time
Key_Resume:
		bcf	EmergStop		; 
		movlw	MENU_SPEED		; show menu loco control
		movwf	MENU
		movf	CURRADR,w
		movwf	NUMBER
		goto	SetNumber


SelPress:
		btfsc	DbnceOn			; exit if debounce time
		return
		bsf	DbnceOn
		movlw	0x06
		movwf	DEBOUNCE		; set debounce time
		btfsc	ServiceMode
		goto	SelServPress		; if service mode, menu service
		btfsc	EmergStop		; if emergency stop, resume
		goto	ResumePress
		incf	MENU,w			; next menu
		andlw	0x03
		movwf	MENU
		xorlw	MENU_FUNC		; in control loco menu ?
		btfss	STATUS,Z
		goto	SetNumber		; no
		incf	CURRADR,w		; yes, current loco unassigned?
		btfsc	STATUS,Z
		incf	MENU,f			; yes, skip menu control
		goto	SetNumber

SelServPress:
		incf	MENU,f
		movlw	MENU_ESTOP
		xorwf	MENU,w
		movlw	MENU_SERV
		btfsc	STATUS,Z
		movwf	MENU
		btfsc	STATUS,Z
		bcf	ServiceMode
SetNumber:
		movf	MENU,w
		xorlw	MENU_SPEED
		btfss	STATUS,Z
		goto	SetNumber1
		incf	CURRADR,w		; prevent error showing number
		btfss	STATUS,Z
		movf	CURRADR,w
		goto	SetNumberEnd
SetNumber1:
		xorlw (MENU_SPEED ^ MENU_PLAY)
		btfss	STATUS,Z
		xorlw (MENU_PLAY ^ MENU_REC)
		btfss	STATUS,Z
		goto	SetNumberEnd

		call	GetRteFirst
		movf	TURNOUT,w
		andlw	0x7F
SetNumberEnd:
		movwf	NUMBER
		clrf	DIZAIN
		goto	ShowMenu



; --- Keys routines


Number_9:
		incf	UNITS,f
Number_8:
		incf	UNITS,f
Number_7:
		incf	UNITS,f
Number_6:
		incf	UNITS,f
Number_5:
		incf	UNITS,f
Number_4:
		incf	UNITS,f
Number_3:
		incf	UNITS,f
Number_2:
		incf	UNITS,f
Number_1:
		incf	UNITS,f
Number_0:
		bcf	STATUS,C
		rlf	DIZAIN,f		; DIZ * 2
		movf	DIZAIN,w
		rlf	DIZAIN,f		; DIZ * 4
		rlf	DIZAIN,f		; DIZ * 8
		addwf	DIZAIN,w		; DIZ * 8 + DIZ * 2 = DIZ * 10
		addwf	UNITS,w
		movwf	NUMBER			; NUMBER = DIZ * 10 + UNIT
		movf	UNITS,w
		movwf	DIZAIN
		clrf	UNITS
		goto	ShowMenu


Key_EnterSrv:
		bsf	ServiceMode
		movlw	MENU_CV
		movwf	MENU
		clrf	CVHIGH
		clrf	CVLOW
		clrf	CVDATA
		clrf	CVTYPE
		goto	ShowMenu

K_SaveFlags:
		movf	INIFLAGS,w		; Save Ini flags
		movwf	EEDATA0
		movlw	0x00
		goto	SetParm
		

K_EnterLoco:
		movf	NUMBER,w		; is in stack?
		btfsc	STATUS,Z		; adress <> 0
		return
		call	FindLoco
		movwf	TEMP
		incf	TEMP,w
		btfss	STATUS,Z
		goto	K_LocoRecall		; yes, recall
		movf	NUMBER,w		; no, add to stack
		movwf	R0
		movwf	CURRADR
		clrf	R1			; stop loco
		clrf	CURRSPD
		movlw	b'01100000'		; 28 steps, forward, FL,F1..F4 off default
		movwf	R2
		movwf	CURRFNC
		call	AddLoco
		call	GetRecentPtr
		movwf	CURRPTR
		goto	ShowMenu

K_LocoRecall:
		movf	TEMP,w			; recall loco data
		call	PutRecent		; put as recent one
		call	LocoRecall
		goto	ShowMenu


K_NxtLoco:
		movlw	MaxLoco
		movwf	COUNT
FindNxtLoc:
		decf	COUNT,w			; search from end of stack
		call	GetPointer
		movwf	FSR
		incf	INDF,w			; is free?
		btfss	STATUS,Z
		goto	FindedNxtLoco		; no, assigned
		decfsz	COUNT,f			; yes, next one
		goto	FindNxtLoc
		goto	ShowMenu		; current, is the only one
FindedNxtLoco:
		decf	COUNT,w
		call	PutRecent		; put as recent one
		call	LocoRecall		; recall data
		goto	ShowMenu


K_DelLoco:
		incf	CURRADR,w		; only if loco selected
		btfsc	STATUS,Z
		return
		movf	CURRSPD,w		; only if stopped
		btfss	STATUS,Z
		return
		movf	CURRPTR,w		; Delete current Loco from stack
		movwf	FSR
		movlw	0xFF
		movwf	INDF			; free stack location
		movwf	CURRADR
		movlw	b'01100000'
		movwf	CURRFNC			; 28 steps, forward, Fx off
		incf	FSR,f
		movwf	INDF
		movlw	0x01
		movwf	CURRSPD			; emergency stop
		incf	FSR,f
		movwf	INDF
		goto	ShowMenu


Key_FL:
		movlw	0x10
		goto	Key_Fx
Key_F4:
		movlw	0x08
		goto	Key_Fx
Key_F3:
		movlw	0x04
		goto	Key_Fx
Key_F2:
		movlw	0x02
		goto	Key_Fx
Key_F1:
		movlw	0x01
Key_Fx:
		xorwf	CURRFNC,f		; change bit
		movf	CURRPTR,w		; save in Stack
		addlw	LocoFunc
		movwf	FSR
		movf	CURRFNC,w
		movwf	INDF			; save in Loco data
		goto	ShowMenu		; show change

Key_ChgTrn:
		movf	NUMBER,w		; change turnout
		btfsc	TURNOUT,7
		iorlw	0x80
		movwf	TURNOUT
		btfss	TURNOUT,7		; goto to other state
		goto	TrnThrow
		goto	TrnNormal



K_SpdDwn:
		incf	CURRADR,w		; only if loco selected
		btfsc	STATUS,Z
		return
		movf	CURRSPD,w		; Speed down
		btfss	STATUS,Z
		goto	K_SpdDwn1
		btfsc	KeyRep			; stop at speed 0
		return
		bcf	CURRFNC,LocoDir		; if spd=0 then backward
		goto	K_Speed	
K_SpdDwn1:
		btfss	CURRFNC,LocoDir
		goto	K_Speed
		goto	K_Brake

K_SpdUp:
		incf	CURRADR,w		; only if loco selected
		btfsc	STATUS,Z
		return
		movf	CURRSPD,w		; Speed Up
		btfss	STATUS,Z
		goto	K_SpdUp1
		btfsc	KeyRep			; stop at speed 0
		return
		bsf	CURRFNC,LocoDir		; if spd=0 then forward
		goto	K_Speed
K_SpdUp1:
		btfss	CURRFNC,LocoDir
		goto	K_Brake
		goto	K_Speed

K_Speed:
		movf	CURRSPD,w
		btfsc	CURRFNC,Loco128
		goto	K_Spd128
		btfsc	CURRFNC,Loco28
		goto	K_Spd28
K_Spd14:
		btfsc	STATUS,Z
		incf	CURRSPD,f		; skip emergency stop
		incf	CURRSPD,f
		movlw	0x10			; max. step
		xorwf	CURRSPD,w
		btfsc	STATUS,Z
		decf	CURRSPD,f
		goto	UpdateSpd

K_Spd128:
		btfsc	STATUS,Z
		incf	CURRSPD,f		; skip emergency stop
		incf	CURRSPD,f
		movlw	0x80			; max. step
		xorwf	CURRSPD,w
		btfsc	STATUS,Z
		decf	CURRSPD,f
		goto	UpdateSpd

K_Spd28:
		movlw	0x04			; skip emergency & stop
		subwf	CURRSPD,w
		movlw	0x03
		btfss	STATUS,C
		movwf	CURRSPD
		incf	CURRSPD,f
		movlw	0x20			; max. step
		xorwf	CURRSPD,w
		btfsc	STATUS,Z
		decf	CURRSPD,f
		goto	UpdateSpd

K_Brake:
		movf	CURRSPD,w
		btfsc	CURRFNC,Loco128
		goto	K_Brk128
		btfsc	CURRFNC,Loco28
		goto	K_Brk28
K_Brk14:
K_Brk128:
		decf	CURRSPD,f		; skip emergency stop
		movlw	0x01
		xorwf	CURRSPD,w
		btfsc	STATUS,Z
		clrf	CURRSPD			; stop normal
		goto	UpdateSpd
K_Brk28:
		decf	CURRSPD,f		; skip emergency stop & unused
		movlw	0x04
		subwf	CURRSPD,w
		btfss	STATUS,C
		clrf	CURRSPD
		goto	UpdateSpd

K_ChgStep:
		incf	CURRADR,w		; only if loco selected
		btfsc	STATUS,Z
		return
		movf	CURRSPD,w		; only if stopped
		btfss	STATUS,Z
		return
		movlw	b'10000000'		; change step 14,28,128
		btfss	CURRFNC,Loco128
		movlw	b'01000000'
		btfsc	CURRFNC,Loco28
		movlw	b'11000000'
		xorwf	CURRFNC,f
		goto	UpdateSpd

K_BrakeStop:
		clrf	CURRSPD			; Stop a loco with programed decelaration
UpdateSpd:
		movf	CURRPTR,w		; save in Stack
		addlw	LocoSpeed
		movwf	FSR
		movf	CURRSPD,w
		movwf	INDF
		incf	FSR,f
		movf	CURRFNC,w
		movwf	INDF			; save in Loco data
		goto	ShowMenu


K_IncCV:
		incf	CVLOW,f
		btfsc	STATUS,Z
		incf	CVHIGH,f
		goto	K_CVNorm
K_DecCV:
		movf	CVLOW,w			; if CV=1 then CV=512
		iorwf	CVHIGH,w
		btfsc	STATUS,Z
		bsf	CVHIGH,1
		movf	CVLOW,w
		btfsc	STATUS,Z
		decf	CVHIGH,f
		decf	CVLOW,f
K_CVNorm:
		movlw	0x03			; normalize CV
		andwf	CVHIGH,f
		goto	ShowMenu

K_IncCVData:
		incf	CVDATA,f
		goto	ShowMenu
K_DecCVData:
		decf	CVDATA,f
		goto	ShowMenu


K_CVDir:
		movlw	0x01			; select type of CV programming
		goto	K_CVType
K_CVPag:
		movlw	0x02
		goto	K_CVType
K_CVPhy:
		movlw	0x03
K_CVType:
		movwf	CVTYPE
		goto	ShowMenu


K_IncIndex:
		call	IncIndex
		goto	GetNewTrn

K_DecIndex:
		call	DecIndex
		goto	GetNewTrn
K_IncRoute:
		call	NextRoute
		goto	GetNewTrn

K_DecRoute:
		call	PrevRoute
GetNewTrn:
		movf	TURNOUT,w
		andlw	0x7F
		movwf	NUMBER
		clrf	DIZAIN
		goto	ShowMenu


K_PlayRoute:
		movlw	0x01			; Play current route
		movwf	RTE_INDEX
		movlw	PLAY_DELAY
		movwf	PLAY_TMR
		bsf	PLAYING
		return

K_IncHour:
						; increment hours
		call	IncHours
		goto	ShowMenu

K_IncMin:
		call	IncMinutes		; increment minutes
		goto	ShowMenu

K_IncScale:
		incf	INIFLAGS,w		; next scale time value
		andlw	0x0F
		movwf	TEMP
		movlw	0xF0
		andwf	INIFLAGS,w
		iorwf	TEMP,w
		movwf	INIFLAGS
		call	ReloadScale
		goto	ShowMenu
		


; --- Routes Section


TrnNormal:
		bcf	TURNOUT,7
		goto	TrnSet
TrnThrow:
		bsf	TURNOUT,7
TrnSet:
		call	WaitDCC
		movlw	0x03			; turn out address starts at 1
		addwf	TURNOUT,w		; '10AAAAAA' '1AAACDDD'. A=0, C=1: activate
		movwf	TEMP			; 
		rlf	TEMP,w
		andlw	0x06
		iorlw	0xF8			; max. 99 turnouts
		btfsc	TURNOUT,7
		iorlw	0x01
		movwf	DCC_DATA2
		rrf	TEMP,f
		rrf	TEMP,w
		andlw	0x1F			; max. 99 turnouts
		iorlw	0x80
		movwf	DCC_DATA1
		xorwf	DCC_DATA2,w
		movwf	DCC_DATA3
		movlw	0x03
		movwf	DCC_CTRL
PlayShow:
		movf	MENU,w			; show only in turnout menus
		xorlw	MENU_PLAY
		btfss	STATUS,Z
		xorlw	(MENU_PLAY ^ MENU_REC)
		btfss	STATUS,Z
		return
		goto	GetNewTrn


PlayRoute:
		decfsz	PLAY_TMR,f
		return
		movlw	PLAY_DELAY
		movwf	PLAY_TMR
		decf	RTE_INI,w		; get turnout data
		addwf	RTE_INDEX,w
		call	EERead
		movwf	TURNOUT
		bsf	LAST_TRN
		bcf	PLAYING
		iorlw	0x00
		btfsc	STATUS,Z		; check if end of route
		goto	PlayShow
		bsf	PLAYING			; no end, continue playing
		call	TrnSet			; set & show
		incf	RTE_INDEX,f		; inc index
		return
		



CountRemain:
		movlw	0x7F			; max. memory free
		movwf	COUNT
		clrf	TEMP			; free counter
CountRem:
		movf	COUNT,w
		addlw	RTE_MEM - 1		; memory offset - 1
		call	EERead
		iorlw	0x00
		btfss	STATUS,Z
		goto	CountRemEnd	
		incf	TEMP,f
		decfsz	COUNT,f
		goto	CountRem
CountRemEnd:
		movf	TEMP,w		
		return


MakeRoom:
		sublw	0x7F			; w = Address to write
		movwf	COUNT
		btfsc	STATUS,Z		; if last, return
		return
		movlw	0x7E			; last location -1
		movwf	TEMP
MakeRoomNxt:
		movf	TEMP,w			; get current data
		call	EERead
		movwf	EEDATA0
		incf	TEMP,w
		call	SetParm			; put in next
		decf	TEMP,f
		decfsz	COUNT,f
		goto	MakeRoomNxt
		incf	TEMP,w			; restore address
		movwf	EEADR0
		return


DelHole:
		movwf	TEMP			; w = address to free
		sublw	0x7F
		movwf	COUNT
		btfsc	STATUS,Z
		goto	DelHoleEnd		; if last, clear it
DelHoleNxt:		
		incf	TEMP,w			; get next data
		call	EERead
		movwf	EEDATA0
		movf	TEMP,w
		call	SetParm			; put in current
		incf	TEMP,f
		decfsz	COUNT,f
		goto	DelHoleNxt
DelHoleEnd:
		clrf	EEDATA0			; clear last location
		movlw	0x7F
		goto	SetParm

PrevRoute:
		movlw	'A'			; if route 'A' get ROUTE 'A'
		xorwf	ROUTE,w
		btfss	STATUS,Z
		decf	ROUTE,f
		goto	GetRteFirst
NextRoute:
		movlw	'Z'
		xorwf	ROUTE,w
		btfss	STATUS,Z
		incf	ROUTE,f
GetRteFirst:
		movlw	0x01
		movwf	RTE_INDEX
FindRoute:
		movlw	RTE_MEM			; Ini of Routes memory
		movwf	RTE_INI
		movlw	'A'
		subwf	ROUTE,w
		btfsc	STATUS,Z
		goto	GetRteTrn		; if 'A', we are
		movwf	COUNT
FindNxtRte:
		movf	RTE_INI,w
		incf	RTE_INI,f	
		call	EERead			; if 0 then is end of one route
		iorlw	0x00
		btfss	STATUS,Z
		goto	FindNxtRte
		decfsz	COUNT,f
		goto	FindNxtRte		; next one
GetRteTrn:
		bcf	LAST_TRN
		decf	RTE_INI,w		; calc turnout address
		addwf	RTE_INDEX,w
		call	EERead
		movwf	TURNOUT
		iorlw	0x00
		btfsc	STATUS,Z
		bsf	LAST_TRN		; if 0 then is end of route
		return

IncIndex:
		btfss	LAST_TRN		; if previous wasn't last, inc
		incf	RTE_INDEX,f
		goto	GetRteTrn

DecIndex:
		decf	RTE_INDEX,w		; dec to 1
		btfss	STATUS,Z
		movwf	RTE_INDEX
		goto	GetRteTrn


K_EnterRoute:
		movf	NUMBER,w
		btfsc	STATUS,Z
		goto	DelRoute
		decf	RTE_INI,w
		addwf	RTE_INDEX,w
		movwf	EEADR0
		call	EERead			; prevent deletion of end route marker
		iorlw	0x00
		btfss	STATUS,Z
		goto	K_EnterRte1
		movf	EEADR0,w
		call	MakeRoom
		incf	RTE_INDEX,f
K_EnterRte1:
		movf	NUMBER,w
		btfsc	TURNOUT,7
		iorlw	0x80
		movwf	EEDATA0
		movf	EEADR0,w
		call	SetParm
		call	GetRteTrn		; load turnout
		goto	GetNewTrn		; show it


DelRoute:
		decf	RTE_INI,w
		addwf	RTE_INDEX,w
		movwf	EEADR0
		call	EERead			; prevent deletion of end route marker
		iorlw	0x00
		btfsc	STATUS,Z
		goto	ShowMenu
		movf	EEADR0,w
		call	DelHole
		call	GetRteTrn		; load turnout
		goto	GetNewTrn		; show it




; --- Internal EEPROM routines

EERead:
		SEL_BANK_1
		movwf	EEADR			; Read EEPROM, w = ADR
		bsf	EECON1,RD
        	movf    EEDATA,w
		bcf	STATUS,RP0		; Bank 0
		return				; w = DATA

SetParm:                   
		movwf	EEADR0		
		call	EERead			; Write EEPROM, w = ADR, EEDATA0 = DATA
		xorwf	EEDATA0,w
		btfsc	STATUS,Z                       
		return
EEWrite:
		SEL_BANK_1
		movf	EEADR0,w
		movwf	EEADR
		movf	EEDATA0,w
		movwf	EEDATA
		bsf	EECON1,WREN		; Enable writting EEPROM
EEWr0:
		bcf	INTCON,GIE		; Clear interrupts
		btfsc	INTCON,GIE
		goto	EEWr0
		movlw	0x55
		movwf	EECON2
		movlw	0xAA
		movwf	EECON2
		bsf	EECON1,WR
		bsf	INTCON,GIE		; Set interrupts
EEWr1:
		btfsc	EECON1,WR		; wait until write complete
		goto	EEWr1
		bcf	EECON1,WREN		; Disable writting EEPROM
		bcf	STATUS,RP0		; bank 0
		return


; --- DCC Section


DCCReset:
		movlw	d'20'			; 20 reset packets
		movwf	COUNT
DCCRes1:
		movf	DCC_CTRL,w		; Wait for buffer free
		btfss	STATUS,Z
		goto	DCCRes1
		clrf	DCC_DATA1		; Put reset packet
		clrf	DCC_DATA2
		clrf	DCC_DATA3
		movlw	0x03			; 3 bytes to send
		movwf	DCC_CTRL
		decfsz	COUNT,f
		goto	DCCRes1
		return


DCCLoco:
		movf	LOCOPTR,w
		movwf	FSR
		movf	INDF,w			; Send loco speed & dir & FL
		movwf	R0			; address
		incf	FSR,f
		movf	INDF,w
		movwf	R1			; speed
		incf	FSR,f
		movf	INDF,w
		movwf	TEMP
		movlw	b'01000000'		; '01DFSSSS' default
		movwf	R2
		btfsc	TEMP,LocoDir
		bsf	R2,5			; dir
		btfsc	TEMP,LocoFL
		bsf	R2,4			; FL
		btfsc	TEMP,Loco128
		goto	DCCLoco128
		btfss	TEMP,Loco28
		goto	DCCLoco14
		goto	DCCLoco28


DCCLoco14:
		movf	R0,w			; loco address = 0
		movwf	DCC_DATA1
		movf	R1,w			; speed
		andlw	0x0F
		iorwf	R2,w			; Dir & FL
		movwf	DCC_DATA2
		xorwf	DCC_DATA1,w
DCC_Send3:
		movwf	DCC_DATA3
		movlw	0x03
		movwf	DCC_CTRL
		return

DCCLoco28:			
		movf	R0,w			; loco address = 0
		movwf	DCC_DATA1
		movlw	0x1F			; speed
		andwf	R1,f
		movf	R1,w			; prevent half error in E.Stop
		xorlw	0x01
		btfsc	STATUS,Z
		incf	R1,f
		rrf	R1,w			; calc half step
		btfsc	STATUS,C
		iorlw	0x10
		andlw	0x1F
		btfsc	R2,5			; set dir
		iorlw	0x20
		iorlw	0x40			; command '01DSSSSS'
		movwf	DCC_DATA2
		xorwf	DCC_DATA1,w
		goto	DCC_Send3


DCCLoco128:
		movf	R0,w			; loco address = 0
		movwf	DCC_DATA1
		movlw	b'00111111'		; '001CCCCC' Advanced operations
		movwf	DCC_DATA2		; CCCCC=11111: 128 speed step control
		movf	R1,w
		andlw	0x7F			; speed
		btfsc	R2,5			; dir
		iorlw	0x80
		movwf	DCC_DATA3
		xorwf	DCC_DATA2,w
		xorwf	DCC_DATA1,w
		movwf	DCC_DATA4
		movlw	0x04
		movwf	DCC_CTRL
		return

		
DCCFunc:
		movf	LOCOPTR,w
		movwf	FSR
		movf	INDF,w			; Loco functions
		movwf	DCC_DATA1		; function group one instructions (100)
		incf	FSR,f
		incf	FSR,f
		movf	INDF,w
		andlw	0x1F
		iorlw	0x80
		movwf	DCC_DATA2
		xorwf	DCC_DATA1,w
		goto	DCC_Send3


; --- CV Programing Section

K_CVPrg:
						; program CV with selected type
CVProg:
		movf	CVTYPE,w		; CV Programming
CVPrgDir:
		xorlw	0x01
		btfss	STATUS,Z
		goto	CVPrgPag
						; Direct Mode
		call	DCCReset
		call	CVWriteDirect
		call	CVWriteDirect
		goto	DCCReset

CVPrgPag:
		xorlw	(0x01 ^ 0x02)
		btfss	STATUS,Z
		goto	CVPrgPhy
						; Paged Mode
		call	DCCReset
		movf	CVHIGH,w		; calc page/reg
		movwf	MSB
		movf	CVLOW,w
		movwf	LSB
		rrf	MSB,f
		rrf	LSB,f
		rrf	MSB,f
		rrf	LSB,f
		incf	LSB,f
		call	CVPagePreset
		call	DCCReset
		movf	CVLOW,w
		andlw	0x03
		movwf	LSB
		call	CVPhyWrite
		call	DCCReset
		movlw	0x01
		movwf	LSB
		call	CVPagePreset
		goto	DCCReset

CVPrgPhy:
		xorlw	(0x02 ^ 0x03)
		btfss	STATUS,Z
		return
						; Physical Mode
		call	DCCReset
		movf	CVLOW,w
		movwf	LSB
		call	CVPhyWrite
		goto	DCCReset

CVWriteDirect:
		movf	DCC_CTRL,w		; wait to write
		btfss	STATUS,Z
		goto	CVWriteDirect
		movf	CVHIGH,w
		andlw	0x03			; just to be sure
		iorlw	0x7C			; '0111CCAA', CC=11: write
		movwf	DCC_DATA1
		movf	CVLOW,w
		movwf	DCC_DATA2
		movf	CVDATA,w
		movwf	DCC_DATA3
		xorwf	DCC_DATA2,w
		xorwf	DCC_DATA1,w
		movwf	DCC_DATA4
		movlw	0x84			; 4 bytes, long preamble
		movwf	DCC_CTRL
		return

CVPagePreset:
		movlw	0x06			; repeat 6 times
		movwf	COUNT
CVPageW:
		movf	DCC_CTRL,w		; wait to write
		btfss	STATUS,Z
		goto	CVPageW
		movlw	0x7D			; '0111CRRR' C=1: write, RRR=101: page register
		movwf	DCC_DATA1
		movf	LSB,w
		movwf	DCC_DATA2
		xorwf	DCC_DATA1,w
		movwf	DCC_DATA3
		movlw	0x83			; long preamble, 3 bytes
		movwf	DCC_CTRL
		decfsz	COUNT,f
		goto	CVPageW
		return


CVPhyWrite:
		movlw	0x06			; repeat 6 times
		movwf	COUNT
CVPhyW:
		movf	DCC_CTRL,w		; wait to write
		btfss	STATUS,Z
		goto	CVPhyW
		movf	LSB,w
		andlw	0x07
		iorlw	0x78			; '0111CRRR' C=1: write, RRR: register
		movwf	DCC_DATA1
		movf	CVDATA,w
		movwf	DCC_DATA2
		xorwf	DCC_DATA1,w
		movwf	DCC_DATA3
		movlw	0x83			; long preamble, 3 bytes
		movwf	DCC_CTRL
		decfsz	COUNT,f
		goto	CVPhyW
		return


; --- Clock section


Clock:
		btfss	ServiceMode
		decfsz	S_QUARTER,f		; count scaled quarters of seconds
		return
IncMinutes:
		incf	S_MIN,f			; every scaled minute, increment time
		movlw	d'60'
		xorwf	S_MIN,w
		btfss	STATUS,Z
		goto	ReloadScale
		clrf	S_MIN
IncHours:
		incf	S_HOUR,f
		movlw	d'24'
		xorwf	S_HOUR,w
		btfsc	STATUS,Z
		clrf	S_HOUR
ReloadScale:
		call	W2QSec
		movwf	S_QUARTER

ShowTime:
		movf	MENU,w			; show new time if necesary
		xorlw	MENU_FUNC
		btfsc	STATUS,Z
		bsf	MENU_CHG
		xorlw	(MENU_FUNC ^ MENU_PLAY)
		btfsc	STATUS,Z
		bsf	MENU_CHG
		return




; ----- Data Tables

;		org	0x620

String:
		addwf	PCL,f
		retlw	HIGH (MSG_WELL)		; 0
		retlw	LOW  (MSG_WELL)
		retlw	HIGH (MSG_SEL)		; 1
		retlw	LOW  (MSG_SEL)
		retlw	HIGH (MSG_CTRL)		; 2
		retlw	LOW  (MSG_CTRL)
		retlw	HIGH (MSG_ROUTE)	; 3
		retlw	LOW  (MSG_ROUTE)
		retlw	HIGH (MSG_SERV)		; 4
		retlw	LOW  (MSG_SERV)
		retlw	HIGH (MSG_CV)		; 5
		retlw	LOW  (MSG_CV)
		retlw	HIGH (MSG_CVDIR)	; 6
		retlw	LOW  (MSG_CVDIR)
		retlw	HIGH (MSG_CVPAG)	; 7
		retlw	LOW  (MSG_CVPAG)
		retlw	HIGH (MSG_CVPHY)	; 8
		retlw	LOW  (MSG_CVPHY)
		retlw	HIGH (MSG_CVPGRG)	; 9
		retlw	LOW  (MSG_CVPGRG)
		retlw	HIGH (MSG_FREE)		; 10
		retlw	LOW  (MSG_FREE)
		retlw	HIGH (MSG_ESTOP)	; 11
		retlw	LOW  (MSG_ESTOP)
		retlw	HIGH (MSG_PLAY)		; 12
		retlw	LOW  (MSG_PLAY)
		retlw	HIGH (MSG_REC)		; 13
		retlw	LOW  (MSG_REC)
		retlw	HIGH (MSG_BLANK)	; 14
		retlw	LOW  (MSG_BLANK)
		retlw	HIGH (MSG_DCC)		; 15
		retlw	LOW  (MSG_DCC)
		retlw	HIGH (MSG_CLK)		; 16
		retlw	LOW  (MSG_CLK)


; ------------- English messages 

;		dt	"0123456789012345"

MSG_DCC:
		dt	" DCC-Gen 16F628 "	,0x80
MSG_WELL:
                dt	" by  F.M.CA",CHR_N,"ADA ",0x80
MSG_SEL:
		dt	"Select Loco:  "	,0x80
;		dt	"03: <000   *----"
MSG_CTRL:
		dt	"Loco       "		,0x80
;		dt	"03: <000   *1234"
MSG_ROUTE:
		dt	"Routes     "		,0x80
;		dt	"Play A-001: 01 /"
MSG_SERV:
		dt	"Service Mode   ",CHR_FWD,0x80
MSG_CV:
		dt	"CV   --- "		,0x80
;		dt	"0001-000  001-00"
MSG_CVDIR:
		dt	"CV Direct"		,0x80
MSG_CVPAG:
		dt	"CV Paged "		,0x80
MSG_CVPHY:
		dt	"CV Phys. "		,0x80
MSG_CVPGRG:
		dt	" Pag/Rg"		,0x80
MSG_FREE:
		dt	"Routes Free: "		,0x80
;		dt	"Rec.  A-001: 01/"
MSG_ESTOP: 
		dt	"Emergency  Stop!"	,0x80
MSG_PLAY:
		dt	"Play  "		,0x80
MSG_REC:
		dt	"Rec.  "		,0x80
MSG_BLANK:
		dt	"                "	,0x80
MSG_CLK:
		dt	"Time      Scale "	,0x80
;		dt	"12:23     60:1  "	,0x80


; LCD Grafic Characters 

NEWLCDCHR:
		addwf	PCL,f

		dt	0x0E,0x00,0x11,0x19,0x15,0x13,0x11,0x00		; Ņ
		dt	0x00,0x00,0x15,0x15,0x17,0x11,0x11,0x00		; 14
		dt	0x00,0x00,0x17,0x11,0x17,0x14,0x17,0x00		; 12
		dt	0x00,0x00,0x07,0x01,0x07,0x04,0x07,0x00		; 2
		dt	0x00,0x00,0x1C,0x14,0x1C,0x14,0x1C,0x00		; 8
		dt	0x10,0x18,0x1C,0x1E,0x1C,0x18,0x10,0x00		; ->
		dt	0x02,0x06,0x0E,0x1E,0x0E,0x06,0x02,0x00		; <-


; Time tables

W2QSec:
		movlw	HIGH (W2QSec)		; clock ticks
		movwf	PCLATH
		movf	INIFLAGS,w
		andlw	0x0F
		addwf	PCL,f

		retlw	d'229'			; 1:1
		retlw	d'114'			; 2:1
		retlw	d'76'			; 3:1
		retlw	d'57'			; 4:1
		retlw	d'46'			; 5:1
		retlw	d'38'			; 6:1
		retlw	d'32'			; 7:1
		retlw	d'29'			; 8:1
		retlw	d'23'			; 10:1
		retlw	d'19'			; 12:1
		retlw	d'15'			; 15:1
		retlw	d'11'			; 20:1
		retlw	d'10'			; 24:1
		retlw	d'8'			; 30:1
		retlw	d'6'			; 40:1
		retlw	d'4'			; 60:1

W2Scale:
		movlw	HIGH (W2Scale)		; values for menu printing
		movwf	PCLATH
		movf	INIFLAGS,w
		andlw	0x0F
		addwf	PCL,f

		retlw	d'1'
		retlw	d'2'
		retlw	d'3'
		retlw	d'4'
		retlw	d'5'
		retlw	d'6'
		retlw	d'7'
		retlw	d'8'
		retlw	d'10'
		retlw	d'12'
		retlw	d'15'
		retlw	d'20'
		retlw	d'24'
		retlw	d'30'
		retlw	d'40'
		retlw	d'60'

; ------------- Service to Keyboard Tables


;		7 8 9 ENT			; This is the keyboard we use
;		4 5 6 UP
;		1 2 3 DWN  STP
;		< 0 > FNC  SEL



;		RB0 RB1 RB2 RB3
;	RB7	 C   8   4   0			; These are the keys according to NEWKEY 
;	RB6	 D   9   5   1
;	RB5	 E   A   6   2    -
;	RB4	 F   B   7   3    -



ServiceKey:
		bcf	ServKey
		movlw	HIGH (ServSELEC)
		movwf	PCLATH
		movlw	0x0F
		andwf	NEWKEY,f
		swapf	MENU,w
		andlw	0x70
		addwf	NEWKEY,w
		addwf	PCL,f



;		org	0x0780

ServSELEC:
		goto	K_EnterLoco	; C
		goto	K_ChgStep	; D
		goto	K_DelLoco	; E
		goto	Key_FL		; F = NEWKEY in MENU Loco Selection
		goto	Number_3	; 8
		goto	Number_6	; 9
		goto	Number_9	; A
		goto	K_SpdUp		; B
		goto	Number_2	; 4
		goto	Number_5	; 5
		goto	Number_8	; 6
		goto	Number_0	; 7
		goto	Number_1	; 0
		goto	Number_4	; 1
		goto	Number_7	; 2
		goto	K_SpdDwn		; 3
ServCTRL:
		return			; C
		goto	K_NxtLoco	; D
		goto	K_BrakeStop	; E
		goto	Key_FL		; F = NEWKEY in MENU Loco Control
		goto	Key_F3		; 8
		return			; 9
		return			; A
		goto	K_SpdUp		; B
		goto	Key_F2		; 4
		return			; 5
		return			; 6
		return			; 7
		goto	Key_F1		; 0
		goto	Key_F4		; 1
		return			; 2
		goto	K_SpdDwn	; 3
ServPLAY:
		goto	K_PlayRoute	; C
		goto	K_IncRoute	; D
		goto	K_DecRoute	; E
		goto	Key_ChgTrn	; F = NEWKEY in MENU Route Playing
		goto	Number_3	; 8
		goto	Number_6	; 9
		goto	Number_9	; A
		goto	K_DecIndex	; B
		goto	Number_2	; 4
		goto	Number_5	; 5
		goto	Number_8	; 6
		goto	Number_0	; 7
		goto	Number_1	; 0
		goto	Number_4	; 1
		goto	Number_7	; 2
		goto	K_IncIndex	; 3
ServSERV:
		goto	Key_EnterSrv	; C
		return			; 0
		return			; 1
		return			; 2
		return			; 3
		return			; 4
		return			; 5
		return			; 6
		return			; 7
		return			; 8
		return			; 9
		return			; A
		return			; B
		return			; D = NEWKEY in MENU Service Mode
		return			; E
		return			; F
ServCV:
		goto	K_CVPrg		; C
		goto	K_IncCV		; D
		goto	K_DecCV		; E
		return			; F = NEWKEY in MENU CV Programing
		goto	K_CVPhy		; 8
		return			; 9
		return			; A
		goto	K_DecCVData	; B
		goto	K_CVPag		; 4
		return			; 5
		return			; 6
		return			; 7
		goto	K_CVDir		; 0
		return			; 1
		return			; 2
		goto	K_IncCVData	; 3
ServREC:
		goto	K_EnterRoute	; C
		goto	K_IncRoute	; D
		goto	K_DecRoute	; E
		goto	Key_ChgTrn	; F = NEWKEY in MENU Route Recording
		goto	Number_3	; 8
		goto	Number_6	; 9
		goto	Number_9	; A
		goto	K_DecIndex	; B
		goto	Number_2	; 4
		goto	Number_5	; 5
		goto	Number_8	; 6
		goto	Number_0	; 7
		goto	Number_1	; 0
		goto	Number_4	; 1
		goto	Number_7	; 2
		goto	K_IncIndex	; 3
ServClk:
		goto	K_SaveFlags	; C ENT
		goto	K_IncMin	; D +M
		return			; E -M
		goto	K_IncScale	; F +Scale
		return			; 8
		return			; 9
		return			; A
		return			; B -H
		return			; 4
		return			; 5
		return			; 6
		return			; 7
		return			; 0
		return			; 1
		return			; 2
		goto	K_IncHour	; 3 +H
ServESTOP:
		goto	Key_Resume	; C
		return			; 0
		return			; 1
		return			; 2
		return			; 3
		return			; 4
		return			; 5
		return			; 6
		return			; 7
		return			; 8
		return			; 9
		return			; A
		return			; B
		return			; D = NEWKEY in MENU Emergency Stop
		return			; E
		return			; F


; ---------------------- Internal EEPROM -------------------------------------

		org	0x2100

		dw	0x40		; Start-up flags:
					;		DC slow
					;		English
					;		Scale Time 1:1


		org	0x2100 + RTE_MEM

		fill	0x00,d'128' - RTE_MEM	; Clear Turnout Routes Memory


		end

