;*******************************************************************
;               	       E-LAN MODULE
;			      DCC Programmer
;				   MERG
;			         prog.asm
;  						Issue 1
;  			 			Date  6/11/98
; 						Written by J.G.Eato   
;
;****************************************************************************************
;		            Amendment History
;
; Initial Version. 
; Issue 1A
;
; 17/1/99
; Issue 1B  Bug: After Binary selected during programming, consequent reading of CVs only
;		 allowed CV address entry in binary digits.
;	    Fix:"Binary invalid flag" cleared before requesting Read CV address
;
;           Bug: Entering binary CV value, when an address of more than 1 digit entered,
;		 input of CV value was off the LCD screen.
;	    Fix: Remove <space> padding after CV and "="
;
; 19/1/99
; Issue 1C  Add: Error trapping added to ensure 0 < CV Address <=1024 and CV value <=255
;
; 25/1/99
; Issue 1D  	 Paged  / Direct read and write  routines rewritten to put repeated code 
;		 into a subroutine, to recover program space.
;
;	    OBS: Direct bit reading could not differentiate between a CV read of 255 and 
;		 no decoder ie neither return an ACK. 
;	    Fix: Make a direct byte read after determining the  CV value, a decoder should
;		 reply with an ACK
;
;	    Bug: Occasional switch bouce when reading keyboard
;	    Fix: Delay found to be 500uS because wrong subroutine called. Delay_500 was called
;		 instead of X_Delay_500. Debounce value also increased from 50mS to 100mS
;
;		 Half a second delay introduced between entering request and power being
;		 applied to the track. 
; 13/2/99
; Issue 1E	 Half second delay removed
;		 Power on sequence modified to give a string of 20 reset packets.
;		 Original gave simply DCC zero bits which did not put the MERG
;		 decoders into DCC mode so causing an 'Overload' state.
;		 Changed interrupt timer reload value from EC to EA so a 1 bit
;		 is 58 microsec. (NMRA standard)
;
; 1/5/99
; Isue 1F	Number of reset packets reduced to 14 so overload test occurs
;		after 100 millisec as per NMRA recommendation. Timer
;		put back to EC to maintain compatibility with ZTC (which does not
;		program at 58 microsecs)
;
; 7/5/01	
; Issue 1G	Mods by Mike Bolton
;		Bit direct option removed. Direct Mode is now bit read by default.
;		Removed separate ZTC direct mode. (use page mode)
;		Added long address programming in one step.  
;		If CV17 selected, allows 4 digit number and programs CV17 and CV18
;		with long address. Individual numbers can be entered in CV17 and
;		CV18 in binary mode. Tested 12/5/01
;
; 19/12/02
; progg2	As progg but modified for use with PIC 16F627 or 16F628
; progg2a	As progg2 but for 16F628A
; Issue 2H
; 16/05/06	Version text updated to 2H	
; progg2H	As progg2a but with added reset packets on direct bit read  (13/05/06)
;			Now works with Digitrax in direct mode.
;			Added one reset packet in page mode read so it worked with ACC6 (slow ACK?) 09/02/07
; prog2k	Progg2H but mods to direct read sequence. Checks for direct read.
;			Now checks bits for both 0 and 1. Direct byte verify not used.
;			Modified the  ACK detection to make it less noise prone
;			Added three reset packets before each bit verify sequence
;			Tested with Tsunami, ESU and QSI sound decoders
;****************************************************************************************
;
                LIST    p=16F628A ; PIC16F628A is the target processor
	__CONFIG	h'3F21'  ; PUTimer on, WDT off, XT clock  
				 ; 4 Mhz Crystal
	include		"p16f628A.inc"

				; ***********************
				;  Register Declarations
				; ***********************



		CBLOCK 0x20

	Mem_Start		; Start of memory
	
	Buffer			; Keyboard Input buffer
	Buffer_1		; Keyboard Input buffer
	Buffer_2		; Keyboard Input buffer
	Buffer_3		; Keyboard Input buffer
	Buffer_4		; Keyboard Input buffer
	Buffer_5		; Keyboard Input buffer
	Buffer_6		; Keyboard Input buffer
	Buffer_7		; Keyboard Input buffer
	Counter			; Temporary counter. Only to be used within a subroutine.
	Temp_Store		; Temporary store. Only to be used within a subroutine.
	Number_Counter		; Keyboard input Routine
	Number_Max		; Keyboard input Routine
	Hex_Convert		; Hex to 2 ascii characters
	CV_Address		; CV Packet formation
	CV_Value		; CV Packet formation
	CV_Checksum		; CV Packet formation
	CV_Counter		; CV Packet formation
	CV_Byte			; CV Packet formation
	CV_Read			; CV Packet formation
	CV_Bit_Counter		; CV Packet formation
	CV_Register		; CV Packet formation
	Key			; Translated Value of Key pressed
	Text_Offset		; Used when outputting text string to LCD 
	Text_Store		; Used when outputting text string to LCD 
	Delay			; System Delay
	X_Delay			; System Delay
	DCC_Buffer		; DCC Output routine
	DCC_Register		; DCC Output routine
	DCC_Counter		; DCC Output routine 
	Bit_Flag		; DCC Out indicates if "0" or "1"
	DEC_MSB			; Used in Hex to decimal Conversion
	DEC_LSB1		; Used in Hex to decimal Conversion
	DEC_LSB			; Used in Hex to decimal Conversion
	Flag_Register		; General flag register
	Base_Flag		; Number base Decimal or Binary
	Ack_Count		; Counter for Ack duration
	CV_Address_Temp	; Temp for CV address
	CV_Byte_Temp	; Temp for packet hi byte
	W_TEMP			; W register interrupt "stack"
	S_TEMP			; Status register interrupt "stack"
	Mem_Stop		; End of memory

	ENDC


				;***********************
				; Literal Declarations
				;***********************

				; Key transalation values
Key_0		equ	0
Key_1		equ	1
Key_2		equ	2
Key_3		equ	3
Key_4		equ	4
Key_5		equ 	5
Key_6		equ	6
Key_7		equ	7
Key_8		equ	8
Key_9		equ	9
Escape		equ	0A
Delete		equ	0B
Program		equ	0C
Read		equ	0D
Mode		equ	0E
Enter		equ	0F

				; PORTA

LCD_RS		equ	0	; LCD Register Select
LCD_E		equ	1	; LCD Display Enable
Sounder		equ	2	; Error and acknowledgement bleep
Power		equ	3	; Turn on power to Service track
DCC_Out		equ	4	; DCC Ouput waveform

				; PORTB

Row_1		equ	0	; Keyboard
Overload	equ	0	; Overload Input
Row_2		equ	1	; Keyboard
DCC_Ack		equ	1	; Acknowledge
Row_3		equ	2	; Keyboard
Row_4		equ	3	; Keyboard

Column_1	equ	4	; Keyboard
Column_2	equ	5	; Keyboard
Column_3	equ	6	; Keyboard
Column_4	equ	7	; Keyboard

				; GENERAL



				; Flag_Register

Tx_Ready	equ	0	; Indicates status of transmit register (Ready to send)
Start_Bit	equ	1	; Indicates if a start bit is required with this byte
Preamble	equ	2	; Indicates this byte is preamble
Ack		equ	3	; Set indicates ack received
Mode_Set	equ	4	; Selects Paged or Direct mode
Long		equ	5	; For long address mode
Bit_Verify	equ	6	; Bit Verification flag
Overload_Flag	equ	7	; Indicates overload

				; Base_Flag

Binary		equ	0	; Set if binary input selected
Binary_Valid	equ	1	; Set if Binary input is valid
True_Ack	equ	2		;flag if genuine ack pulse

				; *********
				;  Vectors
				; *********
	ORG 0000
BEGIN	NOP
	GOTO	Initialise
			
	ORG 0004
	GOTO	SERVICE_INTERRUPT ; Interrupt Vector

				; ******************
				;  LCD Text Strings
				; ******************
Text_DCC	
	RETLW	44		; D
	RETLW	43		; C
	RETLW	43		; C
	RETLW	0A0		; <SPACE>+ End of text

Text_Program
	RETLW	50		; P
	RETLW	52		; R
	RETLW	4F		; O
	RETLW	47		; G
	RETLW	52		; R
	RETLW	41		; A
	RETLW	0CD		; M + End of text

Text_Programmer
	RETLW	4D		; M
	RETLW	45		; E
	RETLW	0D2		; R + End of text

Text_Version

	RETLW	47		; G
	RETLW	20		; <SPACE>
	RETLW	56		; V
	RETLW	45		; E
	RETLW	52		; R
	RETLW	2E		; .
	RETLW	32		; 2
	RETLW	0CB		; K + End of text

Text_Read
	RETLW	52		; R
	RETLW	45		; E
	RETLW	41		; A
	RETLW	44		; D
	RETLW	0A0		; <SPACE>+ End of text

Text_CV
	RETLW	43		; C
	RETLW	0D6		; V+ End of text

Text_Overload
	RETLW	4F		; O
	RETLW	56		; V
	RETLW	45		; E
	RETLW	52		; R
	RETLW	4C		; L
	RETLW	4F		; O
	RETLW	41		; A
	RETLW	0C4		; D + End of text

Text_Error
	RETLW	20		; <SPACE>
	RETLW	45		; E
	RETLW	52		; R
	RETLW	52		; R
	RETLW	4F		; O
	RETLW	0D2		; R + End of text

Text_Equals

	RETLW	0BD		; = + End of text

Text_Mode
	RETLW	4D		; M
	RETLW	4F		; O
	RETLW	44		; D
	RETLW	45		; E
	RETLW	0A0		; <SPACE>+ End of text

Text_OK
	RETLW	20		; <SPACE>
	RETLW	4F		; O
	RETLW	4B		; K 
	RETLW	20		; <SPACE>
	RETLW	20		; <SPACE>
	RETLW	0A0		; <SPACE>+ End of text

Text_Page

	RETLW	50		; P
	RETLW	41		; A
	RETLW	47		; G
	RETLW	45		; E
	RETLW	0A0		; <SPACE>+ End of text

Text_Direct
	RETLW	44		; D
	RETLW	49		; I
	RETLW	52		; R
	RETLW	45		; E
	RETLW	43		; C
	RETLW	54		; T
	RETLW	0A0		; <SPACE>+ End of text



Text_No_Ack
	RETLW	20		; <SPACE>
	RETLW	4E		; N
	RETLW	4F		; O
	RETLW	20		; <SPACE>
	RETLW	41		; A
	RETLW	43		; C
	RETLW	0CB		; K+ End of text
	
Text_No_Direct
	RETLW	20		; <SPACE>
	RETLW	4E		; N
	RETLW	4F		; O
	RETLW	20		; <SPACE>
	RETLW	44		; D
	RETLW	49		; I
	RETLW	52		; R
	RETLW	45		; E
	RETLW	43		; C
	RETLW	0D4		; T + End of texr

				; ***********************************
				;  Keyboard translation Lookup table
				; ***********************************
Translate_key			

	MOVFW	Temp_Store	
	ADDWF	PCL,F		; add offset to program counter

	RETLW	Program		; Column 4 Row 1		Keyboard Layout.
	RETLW	Read		; Column 4 Row 2		1	2	3	Prog
	RETLW	Mode		; Column 4 Row 3		4	5	6	Read
	RETLW	Enter		; Column 4 Row 4		7 	8	9	Mode
	RETLW	Key_3		; Column 3 Row 1		Escape	0	Delete	Enter
	RETLW	Key_6		; Column 3 Row 2
	RETLW	Key_9		; Column 3 Row 3
	RETLW	Delete		; Column 3 Row 4
	RETLW	Key_2		; Column 2 Row 1
	RETLW	Key_5		; Column 2 Row 2
	RETLW	Key_8		; Column 2 Row 3	
	RETLW	Key_0		; Column 2 Row 4
	RETLW	Key_1		; Column 1 Row 1
	RETLW	Key_4		; Column 1 Row 2
	RETLW	Key_7		; Column 1 Row 3
	RETLW	Escape		; Column 1 Row 4


				; *********************************************
				;  4 bit Hex to Ascii translation Lookup table
				; *********************************************
H_A_Convert
	ADDWF	PCL,F		; add offset to program counter

	RETLW	30		; 0
	RETLW	31		; 1
	RETLW	32		; 2
	RETLW	33		; 3
	RETLW	34		; 4
	RETLW	35		; 5
	RETLW	36		; 6
	RETLW	37		; 7
	RETLW	38		; 8
	RETLW	39		; 9
	RETLW	41		; A
	RETLW	42		; B
	RETLW	43		; C
	RETLW	44		; D
	RETLW	45		; E
	RETLW	46		; F

				; ****************
				;  Key Jump Table
				; ****************

Key_Jump			; Decode key

	ADDWF	PCL,F		; add offset to program counter

	GOTO	GN_Numeric	; 0
	GOTO	GN_Numeric	; 1
	GOTO	GN_Numeric	; 2
	GOTO	GN_Numeric	; 3
	GOTO	GN_Numeric	; 4
	GOTO	GN_Numeric	; 5
	GOTO	GN_Numeric	; 6
	GOTO	GN_Numeric	; 7
	GOTO	GN_Numeric	; 8
	GOTO	GN_Numeric	; 9
	GOTO	GN_Escape	; Escape
	GOTO	GN_Delete	; Delete
	GOTO	GN_Error	; Program
	GOTO	GN_Error	; Read
	GOTO	GN_Mode		; Mode
	GOTO	GN_Enter	; Enter


				; *************
				;  Setup Ports
				; *************

SETUP_PORTS			; 0 =Output	1 = Input

				; Set Port B 
				; Bit 7 Output	Keyboard Column 4 / LCD Display Bit 7
				; Bit 6 Output	Keyboard Column 3 / LCD Display Bit 6
				; Bit 5 Output	Keyboard Column 2 / LCD Display Bit 5
				; Bit 4 Output	Keyboard Column 1 / LCD Display Bit 4
				; Bit 3 Input	Keyboard Row 4    / Acknowledge
				; Bit 2 Input	Keyboard Row 3    / Overload
				; Bit 1 Input	Keyboard Row 2
				; Bit 0	Input	Keyboard Row 1
			
				; Port B setup = 0F
		
				; Set Port A bit 
				; Bit 4	Output 	DCC Output
				; Bit 3	Output	Power on	
				; Bit 2 Output	Sounder
				; Bit 1	Output  LCD Enable
				; Bit 0	Output  LCD Register Select

				; Port A setup = 00
	movlw	7		;turn off comparators
	movwf	CMCON	

	BSF	STATUS,5	; Select Bank 1

	MOVLW	0F		; Set Port B		
	MOVWF	TRISB		;

	MOVLW	00		; Set Port A		
	MOVWF	TRISA		;
 			
	BCF	STATUS,5	; Select Bank 0

	RETURN

	
				; *************
				;  Setup Timer
				; *************
				; Set timer to give a 58uS interrupt
SETUP_TIMER

	BSF	STATUS,RP0	; Select Bank 1

	MOVLW	b'01010000'	; Bit 7 Port B Pull up enable
				; Bit 5 Internal clock
				; Bit 3 Prescaler assigned to Timer
				; Bits 0-2 Prescaler ratio 2:1
	MOVWF	OPTION_REG

	BCF	STATUS,RP0	; Select Bank 0

				; ******************
				;  Setup Interrupts
				; ******************
SETUP_INTERRUPT

	BSF	INTCON,T0IE	; Enable TIMER 1 interrupt
	BSF	INTCON,GIE	; Globally enable interrupts

	MOVLW	1
	MOVWF	DCC_Counter	; Data Tx bit counter

	CLRF	Bit_Flag

	RETURN

 				; ********************
				;  Service Interrupts
				; ********************
				; Interrupts serviced every 58uS
SERVICE_INTERRUPT

	MOVWF	W_TEMP		; Save W Register
	SWAPF	STATUS,W	
	MOVWF	S_TEMP		; Save Status Register	

	BCF	STATUS,RP0	; Select Bank 0
	BCF	INTCON,T0IF	; Clear Timer interrupt flag

	NOP			; 1uS pad

	MOVLW	0xEA		; Reset timer to 29uS
	MOVWF	TMR0		; Note Prescaler set to 2:1
				; giving interrupt every 58uS for NMRA
				; Note. Use EC to give 52uS  (for ZTC)
				; compatibility

				; ***************
				;  Timed Interupt
				; ***************

	MOVFW	Bit_Flag
	ADDWF	PCL,F		; add offset to program counter

	GOTO	First_Edge
	GOTO	Second_Edge_Bit1
	GOTO	Second_midpoint_Bit0
	GOTO	Second_Edge_Bit0
	GOTO	First_midpoint_Bit0

First_Edge

	MOVLW	10
	XORWF	PORTA,F		; toggle data line

	MOVLW	01
	BTFSS	DCC_Register,7	; Check next data bit
	MOVLW	04	
	MOVWF	Bit_Flag	; Bit Flag = 1 if data bit 1
				; Bit Flag = 5 if data bit 0
	MOVLW	8
	XORWF	DCC_Counter,W
	BNZ	FE_Exit

	BTFSC	Flag_Register,Start_Bit
	GOTO 	FE_Exit		
				; If Start bit flag is clear then send a start bit 
	MOVLW	4
	MOVWF	Bit_Flag
	BSF	Flag_Register,Start_Bit	; Set Start_Bit Flag

	GOTO	Exit

FE_Exit

	RLF	DCC_Register,F	; Rotate DCC data byte for next bit
	GOTO	Get_Data

Second_Edge_Bit1		; Output a "1"

	MOVLW	10
	XORWF	PORTA,F		; toggle data line
	DECF	Bit_Flag,F
	GOTO	Exit

Second_midpoint_Bit0		; Output a "0"


	CLRF	Bit_Flag
	GOTO	Exit

Second_Edge_Bit0

	MOVLW	10
	XORWF	PORTA,F		; toggle data line

First_midpoint_Bit0

	DECF	Bit_Flag,F
	GOTO	Exit
			
Get_Data

	DECFSZ	DCC_Counter,F
	GOTO	Exit
				; Reload  Tx Register every 8 bits

	MOVFW	DCC_Buffer	; Copy buffer into Tx Register
	MOVWF	DCC_Register		

	MOVLW	8
	MOVWF	DCC_Counter	; Reload loop counter

	BSF	Flag_Register,Tx_Ready	; Set Tx Ready Flag

Exit
	MOVF	Ack_Count,F	;is ack counter up?
	BNZ		Exit2		;do nothing
	BSF		Base_Flag,True_Ack
	GOTO	Exit1
Exit2
	BTFSS	PORTB,DCC_Ack	;is there an ack? (ack is lo)
	GOTO	Ack_Set
	BTFSC	Flag_Register,Ack	;already set?
	GOTO	Ack_Reset			;glitch so start timer again
Ack_Set 
	BTFSS	Flag_Register,Ack	;already set
	BSF		Flag_Register,Ack	;no, so set it
	DECF	Ack_Count,F			;reduce the time by 58uSec
	GOTO	Exit1
Ack_Reset
	BCF		Flag_Register,Ack	;reset ack timing to start
	MOVLW	48
	MOVWF	Ack_Count
Exit1
	SWAPF	S_TEMP,W	
	MOVWF	STATUS		; Reload Status register

	SWAPF	W_TEMP, F
	SWAPF	W_TEMP, W	; Reload W register

	BSF	INTCON,GIE	; Globally enable interrupts

	RETFIE

				; *************
				;  500uS Delay
				; *************

Delay500			;  With 4Mhz clock.

	MOVLW	0A4
	MOVWF	Delay

Delay500_Loop

	DECFSZ	Delay,F
	GOTO	Delay500_Loop

	RETURN
				; *******
				;  Delay
				; *******

X_Delay500			; Delay = 'W' * 500uS

	MOVWF	X_Delay

X_Delay500_Loop

	CALL	Delay500
	DECFSZ	X_Delay,F
	GOTO	X_Delay500_Loop

	RETURN

				; ******************
				;  DCC Send a Byte
				; ******************

DCC_Send_Byte
				; On Entry W holds the DCC byte


;	BTFSS	PORTB,DCC_Ack	; If acknowledge flagged by Loco set ack flag
;	BSF	Flag_Register,Ack


	BTFSS	Flag_Register,Tx_Ready
	GOTO	DCC_Send_Byte	; Loop until Tx ready flag set

	MOVWF	DCC_Buffer	; Load DCC Buffer

	BCF	Flag_Register,Tx_Ready	; Clear Tx ready Flag

	BTFSC	Flag_Register,Preamble	; If clear this byte is preamble	
	BCF	Flag_Register,Start_Bit	; If clear a start bit will be sent with this byte

	RETURN

				; ******************
				;  LCD Write a Byte
				; ******************
LCD_Write_Ascii

	ADDLW	30

LCD_Write			; on entry W holds the byte

	MOVWF	Temp_Store	; save the byte

	MOVLW	0F
	ANDWF	PORTB,F		; Clear the data lines
	
	MOVLW	0F0		; Upper nibble
	ANDWF	Temp_Store,W
	IORWF	PORTB,F		; Put data on DB4,7

	BSF	PORTA,LCD_E	; Pulse LCD enable high.
	BCF	PORTA,LCD_E

	MOVLW	0F
	ANDWF	PORTB,F		; Clear the data lines

	SWAPF	Temp_Store,F	; Lower nibble
	MOVLW	0F0
	ANDWF	Temp_Store,W
	IORWF	PORTB,f		; Put data on DB4,7

	BSF	PORTA,LCD_E	; Pulse LCD enable high.
	BCF	PORTA,LCD_E

	MOVLW 	0D
	MOVWF	Delay

LCD_E_Loop

	DECFSZ	Delay,F		; 45 uS delay
	GOTO	LCD_E_Loop

	RETURN
				; ************
				;  LCD Delete
				; ************
LCD_Delete

	BCF	PORTA,LCD_RS	; Selects Instruction register

	MOVLW	10		; Shift cursor left
	CALL	LCD_Write

	BSF	PORTA,LCD_RS	; Selects data register

	MOVLW	20
	CALL	LCD_Write	; Print <space>

	BCF	PORTA,LCD_RS	; Selects Instruction register

	MOVLW	10		; Shift cursor left
	CALL	LCD_Write


	BSF	PORTA,LCD_RS	; Select Data register

	RETURN


				; ********
				;  LCD CR
				; ********
LCD_CR

	BCF	PORTA,LCD_RS	; Selects Instruction register

	MOVLW	0C0		; CR /LF
	CALL	LCD_Write

	BSF	PORTA,LCD_RS	; Selects data register

	RETURN
				; **********
				;  LCD Home
				; **********
LCD_HOME

	BCF	PORTA,LCD_RS	; Selects Instruction register

	MOVLW	02		; Home
	CALL	LCD_Write

	BSF	PORTA,LCD_RS	; Selects data register

	RETURN
				; ************************
				;  Initialise LCD Display
				; ************************
INITIALISE_LCD

	BCF	PORTA,LCD_RS
	BCF	PORTA,LCD_E	; Selects Instruction register

	MOVLW	1E
	CALL	Delay500	; wait 15mS

	MOVLW	33
	CALL	LCD_Write
	MOVLW	32
	CALL	LCD_Write
	MOVLW	28
	CALL	LCD_Write
	MOVLW	06
	CALL	LCD_Write
	MOVLW	0C
	CALL	LCD_Write
	MOVLW	01
	CALL	LCD_Write

	MOVLW	64		; Wait 50mS
	CALL	X_Delay500

	BSF	PORTA,LCD_RS	; Select Data register

	RETURN

				; ******************
				;  Print LCD String
				; ******************

LCD_String			; On Entry W holds address of String

	MOVWF	Text_Offset
LS_Loop1
	CALL	LS_Sub1
	MOVWF	Text_Store	; Save character so it can be tested
	MOVLW	7F
	ANDWF	Text_Store,W	; Clear top bit
				; Send Character to LCD
	CALL	LCD_Write

	INCF	Text_Offset,F	; Move offset to next character
	MOVFW	Text_Offset

	BTFSS	Text_Store,7	; If character bit 7=1 then end of text reached
	GOTO	LS_Loop1
	RETURN
LS_Sub1
	MOVWF	PCL
				; *********************************************
				;  Convert an HEX byte to 2 ascii characters
				;  And write to LCD Display
				; *********************************************
Hex_ascii_LCD			; On Entry "W" holds hex byte

	MOVWF	Hex_Convert
	SWAPF	Hex_Convert,F	
	MOVLW	0F		; Get MSB nibbles
	ANDWF	Hex_Convert,W	
	CALL	H_A_Convert	; Convert to ascii
	CALL	LCD_Write	; Send To Display

	SWAPF	Hex_Convert,F	
	MOVLW	0F		; Get LSB nibbles
	ANDWF	Hex_Convert,W	
	CALL	H_A_Convert	; Convert to ascii
	CALL	LCD_Write	; Send To Display

	RETURN

				; *********************************************
				;  Convert an HEX byte to 3 BCD characters
				;  And write to LCD Display
				; *********************************************
Hex_BCD				; On Entry "W" holds hex byte

	MOVWF	Counter		; Save hex value
	INCF	Counter,F

	CLRF	DEC_MSB
	CLRF	DEC_LSB1
	CLRF	DEC_LSB

HBCD_Loop1

	DECFSZ	Counter,F
	GOTO	HBCD_Skip1

	MOVLW	30
	ADDWF	DEC_MSB,W
	CALL	LCD_Write

	MOVLW	30
	ADDWF	DEC_LSB1,W
	CALL	LCD_Write

	MOVLW	30
	ADDWF	DEC_LSB,W
	CALL	LCD_Write

	RETURN

HBCD_Skip1

	INCF	DEC_LSB,F

	MOVLW	0A		; if the byte is greater than 9 then add 6
	SUBWF	DEC_LSB,W
	SKPC
	GOTO	HBCD_Loop1

	INCF	DEC_LSB1,F
	CLRF	DEC_LSB	

	MOVLW	0A		; if the byte is greater than 9 then add 6
	SUBWF	DEC_LSB1,W
	SKPC
	GOTO	HBCD_Loop1

	INCF	DEC_MSB,F
	CLRF	DEC_LSB1
	
	GOTO	HBCD_Loop1
				; ****************************************
				;  Convert 4 Digit Decimal to 2 byte Hex
				; ****************************************

				; On Entry Decimal number held in Buffer(MSB),Buffer1,
				; Buffer2 and Buffer3
				; On Exit Hex number held in Buffer6(MSB), Buffer7(LSB)
Convert_to_Hex

	CLRF	Buffer_6		; Clear temporary store
	CLRF	Buffer_7		; Clear temporary store

	MOVFW	Buffer		; Get MSB
	MOVWF	Counter	
	MOVLW	03		; 1000d = 03E8h
	MOVWF	Buffer_5	; Buffer_5 used as a temporary store
	MOVLW	0E8		; Load Thousands counter
	CALL	CtH_Sub1

	CLRF	Buffer_5

	MOVFW	Buffer_1
	MOVWF	Counter		; 100d = 64h
	MOVLW	64		; Load Hundreds counter
	CALL	CtH_Sub1

	MOVFW	Buffer_2
	MOVWF	Counter		; 10d = 0Ah
	MOVLW	0A		; Load Tens counter
	CALL	CtH_Sub1

	MOVFW	Buffer_3	; Add units
	ADDWF	Buffer_7,F
	SKPNC
	INCF	Buffer_6,F

	RETURN	

CtH_Sub1
	MOVWF	Temp_Store
	INCF	Counter,F		; Adjust counter

CtH_Loop1
	DECFSZ	Counter,F
	GOTO	CtH_Skip1
	RETURN

CtH_Skip1
	MOVFW	Temp_Store
	ADDWF	Buffer_7,F	; LSB
	SKPNC
	INCF	Buffer_6,F

	MOVFW	Buffer_5
	ADDWF	Buffer_6,F	; MSB

	GOTO	CtH_Loop1

				; *********************************
				;  Display CV value on LCD Display
				; *********************************
Display_CV

	CALL	Hex_BCD		; Display CV value in decimal

	CALL	LCD_HOME

	MOVLW	0A		; Delay wait for LCD
	CALL	X_Delay500

	MOVLW	Text_Read	; Display CV Read 
	CALL	LCD_String

	MOVLW	20		; print <space>
	CALL	LCD_Write
	MOVLW	62		; print "b"
	CALL	LCD_Write

				; Display CV in Binary value  
	MOVLW	8
	MOVWF	CV_Counter

DCV_Loop1
	
	MOVLW	30		; ascii "0"

	RLF	CV_Value,F
	SKPNC
	MOVLW	31		; ascii "1"

	CALL	LCD_Write

	DECFSZ	CV_Counter,F
	GOTO	DCV_Loop1

	RETURN

				; ***************
				;  Scan Keyboard
				; ***************

				; D0	Row 1	 Input
				; D1	Row 2	 Input
				; D2	Row 3	 Input
				; D3	Row 4	 Input
				; D4	Column 1 Output
				; D5	Column 2 Output
				; D6	Column 3 Output
				; D7	Column 4 Output

Scan_Keyboard			; On exit: Key holds a translated value for the key pressed, or
				; Bit 7 of Key is set if no key pressed.	
	MOVLW	0F0
	IORWF	PORTB,F		; Deselect all columns
	BSF	Key,7		; Set key pressed bit

	BCF	PORTB,Column_1	; Select Column 1
	CLRW			; Load W with column preset value (Bits 2 and 3)
	CALL	Select_Column

	BSF	PORTB,Column_1	; Deselect Column 1
	BCF	PORTB,Column_2	; Select Column 2
	MOVLW	4		; Load W with column preset value (Bits 2 and 3)
	CALL	Select_Column

	BSF	PORTB,Column_2	; Deselect Column 2
	BCF	PORTB,Column_3	; Select Column 3
	MOVLW	8		; Load W with column preset value (Bits 2 and 3)
	CALL	Select_Column

	BSF	PORTB,Column_3	; Deselect Column 3
	BCF	PORTB,Column_4	; Select Column 4
	MOVLW	0C		; Load W with column preset value (Bits 2 and 3)
	CALL	Select_Column

	RETURN

Select_Column

	BTFSS	Key,7		; If key bit 7 is clear then a key is already detected
	RETURN			

	MOVWF	Key		; Store preset Key value 
	CALL	Scan_Row

	BTFSC	Key,7		; If key bit 7 is set no key pressed
	RETURN

Got_a_Key

	MOVLW	64		; Wait 50mS for switch bounce
	CALL	X_Delay500
 
	MOVFW	Key
	MOVWF	Temp_Store	; Temporarily store the key value
			
GaK_Loop
				; Now wait for the key to be released
	CLRF	Key
	CALL	Scan_Row
	BTFSS	Key,7
	Goto	GaK_Loop

	MOVLW	64		; Wait 50mS for switch bounce
	CALL	X_Delay500

	MOVFW	Temp_Store
	CALL	Translate_key	; Translate key
	MOVWF	Key		; Store key value

	RETURN

Scan_Row

	MOVLW	080		; Set top bit (Default)

	BTFSS	PORTB,Row_1	; Value 0
	MOVLW	0
	BTFSS	PORTB,Row_2	; Value 1
	MOVLW	1
	BTFSS	PORTB,Row_3	; Value 2
	MOVLW	2
	BTFSS	PORTB,Row_4	; Value 3
	MOVLW	3
	ADDWF	Key,F		; Add to preset key value(Bits 0 and 1)

	RETURN			; If Key bit 7 is set on exit then there is no key pressed


				; ************
				;  Get Number
				; ************

Get_Number			; On entry W holds the maximum number of digits to enter

	MOVWF	Number_Max	; Maximum number of digits

	MOVLW	8
	MOVWF	Number_Counter	; Tempory loop counter

	MOVLW	Buffer		; Setup indirect memory pointer
	MOVWF	FSR
	MOVLW	0

GN_Loop1			; Clear the Buffer

	MOVWF	INDF

	INCF	FSR,F

	DECFSZ	Number_Counter,F
	GOTO	GN_Loop1
				; Number counter is now used to count the number of key entries.
GN_Loop2

	CALL	Scan_Keyboard	; Loop until a key is pressed
	BTFSC	Key,7
	GOTO	GN_Loop2

	MOVFW	Key
	GOTO	Key_Jump	; Translate Key into an action

GN_Error			; Key is rejected not a valid Key

	CALL	Bleep		; Error so bleep
	GOTO	GN_Loop2

GN_Numeric			; The key is numeric


	BTFSS	Base_Flag,Binary_Valid
	GOTO	GN_OK		; Skip if entering CV address only valid for CV value

	BTFSS	Base_Flag,Binary
	GOTO	GN_OK		; Skip if binary mode not set

	MOVLW	1		; Binary Mode, Only 0 and 1 valid
	ANDWF	Key,F
	
GN_OK
	MOVFW	Number_Max	; Ensure maximum character count is not reached
	XORWF	Number_Counter,W
	SKPNZ
	GOTO	GN_Loop2	; Reject

	MOVLW	Buffer		; Set buffer address to store digit
	ADDWF	Number_Counter,W
	MOVWF	FSR

	MOVFW	Key
	MOVWF	INDF		; Store the Digit
	CALL	LCD_Write_Ascii	; Display the digit

	INCF	Number_Counter,F
	GOTO	GN_Loop2

GN_Delete

	MOVFW	Number_Counter
	SKPNZ
	GOTO	GN_Loop2	; If count =0, there is nothing to delete

	DECF	Number_Counter,F
	CALL	LCD_Delete

	MOVLW	Buffer		; Set buffer address to store digit
	ADDWF	Number_Counter,W
	MOVWF	FSR
	MOVLW	0		; clear the last character from the buffer
	MOVWF	INDF

	GOTO	GN_Loop2

GN_Escape

	SETC

	RETURN


GN_Mode
	BTFSS	Base_Flag,Binary_Valid
	GOTO	GN_Error	; Skip if entering CV address only valid for CV value

	MOVLW	0
	XORWF	Number_Counter,F
	BNZ	GN_Error	; If character count is not 0 then mode is an illegal character

	BTFSC	Base_Flag,Binary
	GOTO	GNM_Decimal

GNM_Binary

	BSF	Base_Flag,Binary

	MOVLW	62
	CALL	LCD_Write
        BCF	Flag_Register,Long	;no double write if in binary
	MOVLW	8		; Maximum Character count =8  ie 0 to 11111111
	GOTO	Get_Number

GNM_Decimal

	BCF	Base_Flag,Binary

	CALL	LCD_Delete

	MOVLW	4		;4 chars if long address
	BTFSS	Flag_Register,Long
	MOVLW	3               ; Maximum Character count =3  ie 0 to 255
	GOTO	Get_Number

GN_Enter			; Enter key pressed

	CLRW
	XORWF	Number_Counter,W
	BZ	GN_Error	; Error if character count = 0

	MOVLW	3	
	XORWF	Number_Max,W
	SKPNZ
	INCF	Number_Max,F	; Required input was 3 characters, this needs to be increased
				; to 4 now as the Hex converter is set to convert 4 characters.

	MOVFW	Number_Counter
	SUBWF	Number_Max,F
	BZ	GN_Exit

GN_Loop3			; This routines adds all the leading zeros ie adjust an address
				; to 4 numbers, adjusts a value to 4 numbers and binary to 8 numbers

	MOVLW	7
	MOVWF	Counter	
	MOVLW	Buffer_6
	MOVWF	FSR

GN_Loop4

	MOVFW	INDF
	INCF	FSR,F
	MOVWF	INDF

	DECF	FSR,F
	DECF	FSR,F

	DECFSZ	Counter,F	
	GOTO	GN_Loop4
	
	INCF	FSR,F
	MOVLW	0
	MOVWF	INDF

	DECFSZ	Number_Max,F
	GOTO	GN_Loop3

	BTFSS	Base_Flag,Binary_Valid
	GOTO	GN_Exit

	BTFSS	Base_Flag,Binary	
	GOTO	GN_Exit
				; If in binary mode rewrite value in 8 bits

GN_Loop5
	CALL LCD_Delete
	DECFSZ	Number_Counter,F
	GOTO	GN_Loop5

	MOVLW	8
	MOVWF	Number_Counter
	
	MOVLW	Buffer
	MOVWF	FSR

GN_Loop6
	MOVFW	INDF
	CALL	LCD_Write_Ascii
	INCF	FSR,F
	DECFSZ	Number_Counter,F
	GOTO	GN_Loop6

GN_Exit
	CLRC

	RETURN
				; ************
				;  Read Input
				; ************
Read_Input

	CALL	Scan_Keyboard
	BTFSC	Key,7
	RETURN			; Return, no key detected
				; "Key" holds key pressed value

	CALL	INITIALISE_LCD

	MOVLW	Program	
	XORWF	Key,W
	BZ	Input_Program

	MOVLW	Read	
	XORWF	Key,W
	BZ	Input_Read

	MOVLW	Mode	
	XORWF	Key,W
	BZ	Input_Mode

	MOVLW	Escape	
	XORWF	Key,W
	BZ	Input_Escape

	MOVLW	Text_Error	; Display Error message
	CALL	LCD_String

	CALL	Bleep		; Error so bleep

	RETURN

				; *******************
				; Get the CV ADDRESS
				; *******************

Get_CV_Address

	CALL	LCD_String

	CALL	LCD_CR

	MOVLW	Text_CV
	CALL	LCD_String

	BCF	Base_Flag,Binary_Valid  ; Only Decimal input permitted

	MOVLW	4		; Maximum Character count =4  ie 1 to 1024

	CALL 	Get_Number	; Get CV Number

	BC	Input_Escape	; Carry set if escape pressed

	CALL	Convert_to_Hex

	MOVFW	Buffer_7	; Buffer_7 holds hex converted value
	MOVWF	CV_Address	; Save CV address in Hex

	MOVFW	Buffer_6
	MOVWF	CV_Byte		; Save 2 upper bits of address

	BNZ	VA_Skip1

	MOVFW	Buffer_7
	BNZ	VA_Skip1

	GOTO	VA_Error	; Error if address is 0

VA_Skip1
	MOVFW	Buffer_6	
	SUBLW	3
	BC	VA_Skip2

	MOVLW	0
	XORWF	Buffer_7,W
	BZ	VA_Skip2

VA_Error
	MOVLW	Text_Error	; Display Error message
	CALL	LCD_String

	CALL	Bleep		; Error so bleep

	SETC
	RETURN

VA_Skip2
	MOVLW	0
	XORWF	Buffer_6,W	;Hi bits must be clear if long address
	BNZ	VA_Skip3
	MOVLW	.17		;Is it a long address
	SUBWF	Buffer_7,W
	BNZ	VA_Skip3
	BSF	Flag_Register,Long
VA_Skip3
	CLRC
	RETURN


				; ************
				;  CV Program
				; ************
Input_Program			

	MOVLW	Text_Page
	BTFSC	Flag_Register,Mode_Set
	MOVLW	Text_Direct
	CALL	LCD_String

                            
	BCF	Flag_Register,Long
	MOVLW	Text_Program	; Display CV Program initial screen

	CALL	Get_CV_Address

	SKPNC			; Carry clear if CV address valid
	RETURN

	MOVLW	Text_Equals
	CALL	LCD_String

	BSF	Base_Flag,Binary_Valid  ; Decimal or Binary input permitted

	BTFSS	 Base_Flag,Binary	
	GOTO	IP_Decimal

	MOVLW	62
	CALL	LCD_Write	; "b" binary indicator

	MOVLW	8		; Maximum Character count =8  ie 0 to 11111111
	CALL Get_Number		; Get CV Number

	BC	Input_Escape	; Carry set if escape pressed

	BTFSS	Base_Flag,Binary	
	GOTO	IP_Decimal_skip

IP_Binary_skip

	MOVLW	8		; Convert binary to hex
	MOVWF	Counter	

	MOVLW	Buffer_7
	MOVWF	FSR

	CLRF	CV_Value	; CV_Value will hold hex value

IPB_Loop1

	CLRC
	BTFSC	INDF,0
	SETC

	RRF	CV_Value,F

	DECF	FSR,F

	DECFSZ	Counter,f	
	GOTO	IPB_Loop1

	MOVFW	CV_Value
	MOVWF	Buffer_7	; Buffer 7 now holds the value in hex

	GOTO	IP_Prog 

IP_Decimal
	MOVLW	4		;If long address then 4 chars
	BTFSS	Flag_Register,Long
	MOVLW	3		; Maximum Character count =3  ie 0 to 255
	CALL Get_Number		; Get CV Number

	BC	Input_Escape	; Carry set if escape pressed

	BTFSC	Base_Flag,Binary	
	GOTO	IP_Binary_skip

IP_Decimal_skip

	CALL	Convert_to_Hex	; On return Buffer_7 holds the CV value
	BTFSC	Flag_Register,Long
	GOTO	IP_Prog		;If long address, buffer 6 has hi byte
	MOVFW	Buffer_6
	BNZ	VA_Error	; If buffer 6 not 0 then value is greater than 255

IP_Prog

	BTFSC	Flag_Register,Mode_Set
	GOTO	CV_Write_Direct

				; *********************
				;  Write CV Paged Mode 
				; *********************
CV_Write_Paged

	CALL	Calc_Page_Reg	; Calculate the page and register

	MOVLW	7D
	MOVWF	CV_Byte

	MOVFW	CV_Address	; Load page value
	MOVWF	CV_Value

	CALL	Power_On_Cycle	
	BTFSC	Flag_Register,Overload_Flag
	RETURN			; Return if overload detected

	CALL	Packet_RW_Paged	; 8 Program Packets

CVWP_Skip1

;	CALL	Recovery_Time
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset	

	BCF	Flag_Register,Ack ; Clear the Ack Flag
	BCF	Base_Flag,True_Ack

	MOVLW	78
	IORWF	CV_Register,W	
	MOVWF	CV_Byte
	BTFSC	Flag_Register,Long
	GOTO	CVWP_Skip5
	MOVFW	Buffer_7	; Buffer_7 holds hex converted value
	MOVWF	CV_Value	; Save CV value in Hex
CVWP_Skip1a
	CALL	Packet_RW_Paged	; 8 Program Packets

CVWP_Skip2

	CALL	ACK_Test

	BCF	Flag_Register,Ack
	BTFSC	Base_Flag,True_Ack
	GOTO	CVWP_Skip3

	MOVLW	Text_No_Ack
	CALL	LCD_String

	RETURN

CVWP_Skip3
	BTFSC	Flag_Register,Long
	GOTO	CVWP_Skip4
	MOVLW	Text_OK
	CALL	LCD_String
	CALL	Bleep

	RETURN

CVWP_Skip4
	MOVLW	.18		;set next address
	MOVWF	CV_Address
	BCF	Flag_Register,Long
	GOTO	CV_Write_Paged

CVWP_Skip5
	MOVFW	Buffer_6	;get hi byte of long address
	IORLW	B'11000000'
	MOVWF	CV_Value
	GOTO	CVWP_Skip1a

ACK_Test
	CALL	Power_Off_Cycle

	CALL	LCD_HOME

	MOVLW	0A		; Delay wait for LCD
	CALL	X_Delay500

	MOVLW	Text_Program	; Display CV Program
	CALL	LCD_String

	MOVLW	20		; print <space>
	CALL	LCD_Write
	RETURN
	

				; **********************
				;  Write CV Direct Mode
				; **********************
CV_Write_Direct

	
	MOVLW	1		; Subtract 1 from the address
	SUBWF	CV_Address,F	; ie CV1= 00
	SKPC	
	SUBWF	CV_Byte,F	; 

CVD_Skip

;	CALL	SETUP_TIMER

	CALL	Power_On_Cycle	
	BTFSC	Flag_Register,Overload_Flag
	RETURN			; Return if overload detected

	MOVLW	7C		; Setup first packet byte
	IORWF	CV_Byte,F
	BTFSC	Flag_Register,Long
	GOTO	CVD_Skip5
	MOVFW	Buffer_7
	MOVWF	CV_Value
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
CVD_Skip2
	BCF		Base_Flag,True_Ack	; clear the ACK flag
	CALL	Packet_RW_Direct	; 8 Program Packets

	
	
	

	BTFSC	Base_Flag,True_Ack
	GOTO	CVD_Skip3

	MOVLW	Text_No_Ack
	CALL	LCD_String

	RETURN

CVD_Skip3
	BTFSC	Flag_Register,Long
	GOTO	CVD_Skip4
	CALL	ACK_Test
	MOVLW	Text_OK
	CALL	LCD_String
	CALL	Bleep

	RETURN

CVD_Skip4
	MOVLW	.18
	MOVWF	CV_Address
	BCF	Flag_Register,Long
	GOTO	CV_Write_Direct

CVD_Skip5
	MOVFW	Buffer_6
	IORLW	B'11000000'
	MOVWF	CV_Value
	GOTO	CVD_Skip2



				; *********
				;  CV Read
				; *********
Input_Read

	MOVLW	Text_Page
	BTFSC	Flag_Register,Mode_Set
	MOVLW	Text_Direct
	CALL	LCD_String	; Print Direct or paged

	
	MOVLW	Text_Read	; Display CV Read initial screen

	CALL	Get_CV_Address

	SKPNC			; Carry clear if CV address valid
	RETURN

	MOVLW	Text_Equals
	CALL	LCD_String

	BTFSC	Flag_Register,Mode_Set
	GOTO	CV_Read_Direct

				; *********************
				;  Read CV Paged Mode 
				; *********************
CV_Read_Paged

	CALL	Calc_Page_Reg	; Calculate the page and register

	MOVLW	7D
	MOVWF	CV_Byte

	MOVFW	CV_Address	; Load page value
	MOVWF	CV_Value

	CALL	Power_On_Cycle	
	BTFSC	Flag_Register,Overload_Flag
	RETURN			; Return if overload detected
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	BCF	Flag_Register,Ack ; Clear the Ack Flag
	BCF	Base_Flag,True_Ack

	CALL	Packet_RW_Paged	; 8 Page Program Packets
	

CVRP_Skip1

	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	
	
	BCF	Flag_Register,Ack ; Clear the Ack Flag
	BCF	Base_Flag,True_Ack

	MOVLW	0
	MOVWF	CV_Bit_Counter	

	MOVLW	70
	IORWF	CV_Register,W	
	MOVWF	CV_Byte

CVRP_255_Loop

	MOVFW	CV_Bit_Counter	
	MOVWF	CV_Value
	BCF		Base_Flag,True_Ack
	CALL	Packet_RW_Paged	; 8 Program Packets
	CALL	Packet_Reset	; added 09/02/07 to allow for late ACK
	BTFSC	Base_Flag,True_Ack
	GOTO	CVRP_Skip2

	INCF	CV_Bit_Counter,F	
	BNZ		CVRP_255_Loop
	GOTO	CVVRP_Exit

CVRP_Skip2

	MOVFW	CV_Bit_Counter
	MOVWF	CV_Value

CVRP_Skip3

	CALL	Packet_Reset	;needed if there was an ACK

	MOVFW	CV_Value	; CV Value put into W when entering this routine at CVRP_Skip3

	CALL	Display_CV

	CALL	Power_Off_Cycle

	CALL	Bleep

	RETURN

CVVRP_Exit

	CALL	Power_Off_Cycle

	MOVLW	Text_No_Ack
	CALL	LCD_String

	RETURN
				; ****************
				;  Read CV Direct 
				; ****************
CV_Read_Direct


	MOVLW	1		; Subtract 1 from the address
	SUBWF	CV_Address,F	; ie CV1= 00
	SKPC	
	SUBWF	CV_Byte,F
	MOVFW	CV_Address
	MOVWF	CV_Address_Temp		;for use after direct mode check
	MOVFW	CV_Byte
	MOVWF	CV_Byte_Temp



				; *********************************
				;  Read CV Direct Bit Verification
				; *********************************

CV_Read_DBit

	CALL	Power_On_Cycle
	BTFSC	Flag_Register,Overload_Flag
	RETURN			; Return if overload detected
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Direct_Test			;check if direct mode is supported
	SUBLW	0
	BZ		CV_Read_DBit_1		;is valid direct mode
	GOTO	No_Direct			;not valid direct
	
CV_Read_DBit_1
	MOVFW	CV_Address_Temp		;restore original address
	MOVWF	CV_Address
	MOVFW	CV_Byte_Temp		
	MOVWF	CV_Byte

	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	MOVLW	0E7
	MOVWF	CV_Value

	MOVLW	78		; Setup first packet byte
	IORWF	CV_Byte,F

	MOVLW	8
	MOVWF	CV_Bit_Counter		;8 tries

	CLRF	CV_Read				;final value of CV
	
CVVD_Loop2
	BCF		Base_Flag,True_Ack
	CALL	Packet_RW_Direct	; 8 Program Packets
	CALL 	Packet_Reset		; reset packet every time
	BTFSC	Base_Flag,True_Ack
	GOTO	CVVD_Skip1
	BSF		CV_Value,3			;check for a 1 bit
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	BCF		Base_Flag,True_Ack
	CALL	Packet_RW_Direct	; 8 Program Packets
	CALL 	Packet_Reset		; reset packet every time
	BCF		CV_Value,3			; for next time
	BTFSC	Base_Flag,True_Ack
	GOTO	CVVD_Skip1a
							;no ack so bit is a 1
	GOTO	CVVRP_Exit		;no ack on either 0 or 1  (was CVVD_Skip2)

CVVD_Skip1
	;CALL	Recovery_Time
	CALL	Packet_Reset
	CALL	Packet_Reset	;needs more than one for QSI
	CLRC
	GOTO	CVVD_Skip2		; bit was a 0

CVVD_Skip1a
	;CALL	Recovery_Time
	CALL	Packet_Reset
	CALL	Packet_Reset		
	SETC					; bit was a 1

CVVD_Skip2

	RLF	CV_Read,F	; Rotate carry into "read" CV value

;	MOVLW	0A		; Delay wait for decoder to remove acknowledge
;	CALL	X_Delay500

	DECF	CV_Value,F	; Decrement CV value to read next bit
	
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset

	DECFSZ	CV_Bit_Counter,F
	
	GOTO	CVVD_Loop2

CVVD_Exit

	MOVFW	CV_Read		; Save the CV value
	MOVWF	CV_Value

;	BCF	CV_Byte,3
;	BSF	CV_Byte,2	; Change CV byte to Read byte not Bit

;	CALL	Packet_RW_Direct	; 8 Program Packets
;	BTFSC	Base_Flag,True_Ack
	GOTO	CVRP_Skip3

	GOTO	CVVRP_Exit
	
No_Direct
	CALL	Power_Off_Cycle

	MOVLW	Text_No_Direct
	CALL	LCD_String

	RETURN
				; ************
				;  Input Mode
				; ************
Input_Mode

	MOVLW	Text_Mode
	CALL	LCD_String

	BTFSS	Flag_Register,Mode_Set
	GOTO	Direct_Mode
        GOTO 	Paged_Mode
	
Direct_Mode

	BSF	Flag_Register,Mode_Set

	MOVLW	Text_Direct
	CALL	LCD_String

	RETURN

Paged_Mode

	BCF	Flag_Register,Bit_Verify
	BCF	Flag_Register,Mode_Set	

	MOVLW	Text_Page
	CALL	LCD_String

	RETURN

Bleep
	BSF	PORTA,Sounder	; Turn off Bleep

	MOVLW	0FF		; Delay 
	CALL	X_Delay500

	BCF	PORTA,Sounder	; Turn off Bleep

	RETURN

Input_Escape

	CALL	INITIALISE_LCD

	MOVLW	Text_DCC	; Initialisation String
	CALL	LCD_String

	MOVLW	Text_Program
	CALL	LCD_String

	MOVLW	Text_Programmer
	CALL	LCD_String

	SETC

	RETURN
				; **************************************
				; Place a Reset Packet in the DCC Queue
				; **************************************
Packet_Reset

	MOVLW	0FF		; Preamble
	CALL	DCC_Send_Byte

	BCF	Flag_Register,Preamble	; Flag clear this is preamble

	MOVLW	0FF		; Preamble
	CALL	DCC_Send_Byte

	MOVLW	00		; First byte
	CALL	DCC_Send_Byte

	BSF	Flag_Register,Preamble	; Flag set this is data

	MOVLW	00		; Second byte
	CALL	DCC_Send_Byte
	MOVLW	00		; Third byte
	CALL	DCC_Send_Byte

	RETURN

				; **********************
				;  Power On / Off Cycle
				; **********************
Power_On_Cycle

	MOVLW	.14		;14 reset packets
	MOVWF	CV_Counter



POC_Loop	

	MOVLW	0C8		; Wait 100mS for switch bounce
	CALL	X_Delay500

	

	CALL	SETUP_TIMER	; Start interrupts

	BSF	PORTA,Power	; Turn on power to service track

	BCF	Flag_Register,Overload_Flag

POC_Loop2

	CALL	Packet_Reset	; 14 Reset Packets
	DECFSZ	CV_Counter,f
	GOTO	POC_Loop2	

	

	BTFSS	PORTB,Overload	; After 100msec check the overload input
	GOTO	Overload_Detected

	CALL	Recovery_Time

	RETURN

Power_Off_Cycle

	CALL	Recovery_Time

	BCF	PORTA,Power	; Turn off power to service track

	BCF	INTCON,GIE	; Globally disable interrupts


	RETURN

Recovery_Time

	MOVLW	0A		;	10 Reset Packets
	MOVWF	CV_Counter

POC_Loop1

	CALL	Packet_Reset	; 
	DECFSZ	CV_Counter,F
	GOTO	POC_Loop1	

	RETURN

Overload_Detected

	BCF	PORTA,Power	; Turn off power to service track

	BCF	INTCON,GIE	; Globally disable interrupts

	BSF	Flag_Register,Overload_Flag

	CALL	INITIALISE_LCD

	MOVLW	Text_Overload	; Display Overload message
	CALL	LCD_String

	CALL	Bleep		; Bleep

	MOVLW	55		;  100mS pause
	CALL	X_Delay500

	CALL	Bleep		; Bleep

	RETURN

                                ; *******************************************
                                ;  Packet to Program/Read/Verify a CV Direct
                                ; *******************************************
Packet_RW_Direct
				; On entry	CV_Byte holds first packet byte (0111CCAA)
				;		CV_Address holds Address byte
				;		CV_Value hold data byte
				; Packet sent up to 8 times
				;
	BCF	Flag_Register,Ack ; Clear the Ack Flag
	BCF	Base_Flag,True_Ack
	
	

	MOVLW	8
	MOVWF	CV_Counter
	MOVLW	48
	MOVWF	Ack_Count
	

PRWD_Loop1
	
	

	CALL	P_RW_Direct	; 8 Program Packets
	BTFSC	Base_Flag,True_Ack
	GOTO	PRWD_Skip1

	DECFSZ	CV_Counter,F
	GOTO	PRWD_Loop1

PRWD_Skip1

	RETURN

P_RW_Direct

	MOVLW	0FF		; Long Preamble
	CALL	DCC_Send_Byte

	BCF	Flag_Register,Preamble	; Flag clear this is preamble

	MOVLW	0FF		; Long Preamble
	CALL	DCC_Send_Byte

	MOVLW	0FF		; Long Preamble
	CALL	DCC_Send_Byte

	MOVFW	CV_Byte		; First Byte
	MOVWF	CV_Checksum
	CALL	DCC_Send_Byte

	BSF	Flag_Register,Preamble	; Flag set this is data

	MOVFW	CV_Address	; Second Byte
	XORWF	CV_Checksum,F
	CALL	DCC_Send_Byte

	MOVFW	CV_Value	; Third Byte
	XORWF	CV_Checksum,F
	CALL	DCC_Send_Byte

	MOVFW	CV_Checksum	; Forth Byte
	CALL	DCC_Send_Byte

	RETURN

                                ; **********************************************
                                ;  Calculate the Page and Register (Paged Mode)
                                ; **********************************************
Calc_Page_Reg

	MOVLW	1		; Calculate Register and Page
	SUBWF	CV_Address,F
	SKPC	
	SUBWF	CV_Byte,F	; Decrement address by 1

	MOVLW	3		; Get register
	ANDWF	CV_Address,W
	MOVWF	CV_Register	

	CLRC			; Divide by 4
	RRF	CV_Byte,F
	RRF	CV_Address,F

	CLRC
	RRF	CV_Byte,F
	RRF	CV_Address,F
				 
	INCF	CV_Address,F	; CV_Address now holds the page number

	RETURN

                                ; *******************************************
                                ;  Packet to Program/Read a CV in Paged Mode
                                ; *******************************************
Packet_RW_Paged
				; On entry	CV_Byte holds First Byte
				;		CV_Value holds data byte
				; Packet sent up to 8 times
				;
	BCF	Flag_Register,Ack ; Clear the Ack Flag
	BCF	Base_Flag,True_Ack
	
	
	

	MOVLW	8
	MOVWF	CV_Counter
	MOVLW	48
	MOVWF	Ack_Count



PRWP_Loop1

	CALL	P_RW_Paged	; 8 Program Packets
	BTFSC	Base_Flag,True_Ack
	GOTO	PRWP_Skip1

	DECFSZ	CV_Counter,F
	GOTO	PRWP_Loop1

PRWP_Skip1

	RETURN

P_RW_Paged


	MOVLW	0FF		; Long Preamble
	CALL	DCC_Send_Byte

	BCF	Flag_Register,Preamble	; Flag clear this is preamble

	MOVLW	0FF		; Long Preamble
	CALL	DCC_Send_Byte

	MOVLW	0FF		; Long Preamble
	CALL	DCC_Send_Byte

	MOVFW	CV_Byte		; First Byte
	MOVWF	CV_Checksum
	CALL	DCC_Send_Byte

	BSF	Flag_Register,Preamble	; Flag set this is data

	MOVFW	CV_Value	; Second Byte
	XORWF	CV_Checksum,F
	CALL	DCC_Send_Byte

	MOVFW	CV_Checksum	; Third Byte
	CALL	DCC_Send_Byte

	RETURN
    
 				; **************************
 				;	Routine to check if direct mode is valid
 				;	Returns with W = 0 if valid
 				;	Returns with W = 1 if not
 				;	************************
 				
 				
Direct_Test
	MOVLW	0E7
	MOVWF	CV_Value

	MOVLW	78			; Setup first packet byte
	MOVWF	CV_Byte
	
	MOVLW	7
	MOVWF	CV_Address	;read CV 8

	MOVLW	8
	MOVWF	CV_Bit_Counter		;8 tries

	CLRF	CV_Read				;final value of CV
	
DTST_Loop2
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
	BCF		Base_Flag,True_Ack
	CALL	Packet_RW_Direct	; 8 Program Packets
	CALL 	Packet_Reset		; reset packet every time
	BTFSC	Base_Flag,True_Ack
	GOTO	DTST_Skip1
	BSF		CV_Value,3			;check for a 1 bit
	CALL	Packet_Reset
	CALL	Packet_Reset
	CALL	Packet_Reset
;	CALL	Packet_Reset
	BCF		Base_Flag,True_Ack
	CALL	Packet_RW_Direct	; 8 Program Packets
	CALL 	Packet_Reset		; reset packet every time
	BCF		CV_Value,3			; for next time
	BTFSC	Base_Flag,True_Ack
	GOTO	DTST_Skip1
							
	RETLW	1		;no ack on either 0 or 1 
DTST_Skip1
	CALL	Recovery_Time	
	RETLW 	0		;is ack so valid direct mode
	

   				; ************************
				;  Program Initialisation
				; ************************
Initialise


	MOVLW	Mem_Start
	MOVWF	FSR

Init_Loop			; Clear Workspace memory

	CLRW
	MOVWF	INDF
	INCF 	FSR,F
	MOVLW	Mem_Stop
	XORWF	FSR,W
	BNZ	Init_Loop

	CALL 	SETUP_PORTS

	BCF	PORTA,Power	; Turn off power to service track

	BCF	PORTA,Sounder	; Turn off Bleep

	CALL	Input_Escape	; Prints top line of LCD Display

	CALL	LCD_CR

	MOVLW	Text_Programmer
	CALL	LCD_String

	MOVLW	Text_Version
	CALL	LCD_String

	CALL 	Bleep

LOOP				; Main Program Loop

	CALL	Read_Input	; Check for input

	GOTO LOOP

	END
