;**************************************************************************
;*             RACAL RA1792 DISPAY DRIVER FOR 16*2 DISPLAYS               *
;**************************************************************************

;**************************************************************************
;* (C)Display Code DAVID SCHOFIELD, Capture Code MIKE STIRLING 2009       *
;*                       All rights reserved                              *
;**************************************************************************
;*          Hardw. Rev: 2                 Softw. Rev:  7.00d              *
;*          OSC.......: HS 20MHz Max.     POWER.....:  5V DC              *
;**************************************************************************

;############################################################################################################
;#		Hardware Configured As Follows																		#
;#		2x 16x2 displays with control lines on PORTA, running in 4 BIT mode. LCD Data on PORTB 0 - 4.		#
;#		Both displays connected in parallel on PORTB except for the control lines on PORTA					#
;#		Racal receiver ADDRESS bus on PORTC	0 - 4															#
;#		Racal receiver DATA bus on PORTD 0 - 7																#
;#		Number display strobe connected to PSP INT pin 9 PORT E1. E2 Pin 10 GND. E0 pulled up via 4k7		#
;############################################################################################################

		org		0x0000
		goto	main

		org		0x0004
		goto	interrupt

        LIST
        NOLIST

;==========================================================================
;
;       Verify Processor
;
;==========================================================================

        IFNDEF __16F877A
           MESSG "Processor-header file mismatch.  Verify selected processor."
        ENDIF

;==========================================================================
;
;       Register Definitions
;
;==========================================================================

W                            EQU     H'0000'
F                            EQU     H'0001'

;----- Register Files------------------------------------------------------

INDF                         EQU     H'0000'
PCL                          EQU     H'0002'
STATUS                       EQU     H'0003'
FSR                          EQU     H'0004'
PORTA                        EQU     H'0005'
PORTB                        EQU     H'0006'
PORTC                        EQU     H'0007'
PORTD                        EQU     H'0008'
PORTE                        EQU     H'0009'
PCLATH                       EQU     H'000A'
INTCON                       EQU     H'000B'
PIR1                         EQU     H'000C'

TRISA                        EQU     H'0085'
TRISB                        EQU     H'0086'
TRISC                        EQU     H'0087'
TRISD                        EQU     H'0088'
TRISE                        EQU     H'0089'
PIE1                         EQU     H'008C'
PSPMODE                      EQU     H'0004'
CMCON                        EQU     H'009C'
ADCON1                       EQU     H'009F'

;----- PIE1 Bits ----------------------------------------------------------

PSPIE                        EQU     H'0007'

;----- INTCON Bits --------------------------------------------------------

GIE                          EQU     H'0007'
PEIE                         EQU     H'0006'

;----- PIR1 Bits ----------------------------------------------------------

PSPIF                        EQU     H'0007'

;----- STATUS Bits --------------------------------------------------------

RP1                          EQU     H'0006'
RP0                          EQU     H'0005'
Z                            EQU     H'0002'
C                            EQU     H'0000'

;-------Configuration Bits-------------------

_CP_OFF                      EQU     H'3FFF'
_LVP_OFF                     EQU     H'3F7F'
_BODEN_ON                    EQU     H'3FFF'
_PWRTE_ON                    EQU     H'3FF7'
_WDT_OFF                     EQU     H'3FFB'
_HS_OSC                      EQU     H'3FFE'

			LIST

			__CONFIG        _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _HS_OSC
			ERRORLEVEL	0,	-302		;suppress bank selection messages


;----- CUSTOM Bits --------------------------------------------------------

status_save					EQU		0x20			;used to store the status reg value during interrupt
w_save						EQU		0x22			;used to store the w reg during interrupt routine

digitbase					EQU		0x30			;These files store the current state of the display digits
digit0						EQU		0x30			;when the PSP interupt is activated. These are thw raw HEX
digit1						EQU		0x31			;values read straight from the Racal DATA bus  PORTD
digit2						EQU		0x32
digit3						EQU		0x33
digit4						EQU		0x34
digit5						EQU		0x35
digit6						EQU		0x36
digit7						EQU		0x37
digit8						EQU		0x38
digit9						EQU		0x39
digita						EQU		0x3a
digitb						EQU		0x3b
digitc						EQU		0x3c
digitd						EQU		0x3d
digite						EQU		0x3e
digitf						EQU		0x3f

LCD_CTRL					Equ 	PORTA				;define PORTA as LCD control
LCD_DATA					Equ		PORTB				;define PORTB as LCD data
RACAL_NDA					Equ		PORTC				;define PORTC as Racal Number Display Address
RACAL_DATA					Equ 	PORTD				;define PORTD as Racal Display DATA
LCD_RS						Equ		0x01				;LCD handshake lines RS line on PORTA 1
LCD_E1						Equ		0x02				;display enable 1 on PORTA 2
LCD_E2						Equ		0x03				;display enable 2 on PORTA 3


			cblock	0x40	;start of general purpose registers
count						;used in looping routines
count1						;used in delay routine
counta						;used in delay routine
countb						;used in delay routine
templcd						;temp store for 4 bit mode
lcdaddress					;bit 0 set = LCD2, bit 0 clear = LCD1
bwcheck						;file used in testing for 16Khz BW
ssbcheck					;file used in SSB testing
ssbcheck2					;file used to test if still in SSB
			endc

;############################################################
;			End of Equates, setup ports and interupts		#
;############################################################

main

			BCF		STATUS,RP0		;DISSABLE A TO D and comparitors ON F877A
			BCF		STATUS,RP1		
			CLRF	PORTA
			BSF		STATUS,RP0
			MOVLW	0X06
			MOVWF	ADCON1
			MOVLW	0XCF
			MOVWF	TRISA
			movlw	0x07
			movwf	CMCON			;turn comparators off


SetPorts	bsf 	STATUS,RP0		;select bank 1
			movlw	0x00
			movwf	TRISA			;set PORTA as output			
			movwf	TRISB			;set PORTB as output
			movlw	0x0F
			movwf	TRISC			;set PORTC 0 - 3 as inputs these are the NDA (number display address) bits
			movlw	0xFF			;refered to in the RA1792 manual as N0 - N3
			movwf	TRISD			;set PORTD as inputs this is the RA1792 DATA Bus
			movlw	(1 << PSPMODE) | (1 << 2) | (1 << 1) | (1 << 0)	;set PORTD to PSP mode
			movwf	TRISE
			bsf		PIE1,PSPIE		;enable parallel slave port interupt
			bcf		STATUS,RP0		;select bank 0
			movlw	(1 << GIE) | (1 << PEIE)
			movwf	INTCON

;##################################################
;#               MAIN PROGRAM START               #
;##################################################		

			clrf	count			;clear all ports so we start at a known point
			clrf	LCD_CTRL
			clrf	LCD_DATA
			clrf	RACAL_NDA
			clrf	RACAL_DATA
			clrf	PORTE

			call	Delay100		;wait for LCD to settle

			call	LCD_Init		;setup LCD

			call	LCD1			;Clear LCD Displays to start in a known state
			call	LCD_Clr
			call	LCD2
			call	LCD_Clr

			call	LCD_Line1
			call	frqu_header		;start off with display 1 showing "Frequency  Chan" on line 1

			call	LCD_Line1
			call	bw_header		;start off with display 2 showing "Bandwidth BFO" on line 1

main_loop	call	LCD1
			call	LCD_Line2		;get display data from digit xx files, convert to ASCII and output to LCD
			movfw	digit8
			call	hex_ascii
			call	LCD_Char
			movfw	digit7
			call	hex_ascii
			call	LCD_Char
			movfw	digit6
			call	hex_ascii
			call	LCD_Char
			movfw	digit5
			call	hex_ascii
			call	LCD_Char
			movfw	digit4
			call	hex_ascii
			call	LCD_Char		
			movlw	0x2e			;Place a . on the display after the Khz point
			call	LCD_Char
			movfw	digit3
			call	hex_ascii
			call	LCD_Char
			movfw	digit2
			call	hex_ascii
			call	LCD_Char
			movfw	digit1
			call	hex_ascii
			call	LCD_Char


Chan_dis	movlw	0x20			;Place a space between freq and chan data
			call	LCD_Char
			movfw	digita			;Output Channel data
			call	hex_ascii
			call	LCD_Char
			movfw	digit9
			call	hex_ascii
			call	LCD_Char

check_ssb	movfw	digitf
			call	hex_ascii
			movwf	ssbcheck
			movlw	3fh				;load 3F into W to compare with ? for SSB
			subwf	ssbcheck		;subtract w from digit f to test for SSB;
			btfsc	STATUS,Z   		;carry flag set if mode is SSB
			call	ssb				;jump to subroutine to display SSB message


BW_dis		call	LCD2			;output Bandwidth data
;			call	LCD_Line2		;Place a space betfore the BFO frequency	
			movlw	d'2'
			call	LCD_Line2W
			
			movfw	digit0			;output bandwidth MSB			
			call	hex_ascii
			call	LCD_Char

			
check16		movfw	digitf
			movwf	bwcheck
			movlw	16h				;load 16 into W to compare with the bandwidth
			subwf	bwcheck			;subtract w from digit f of bandwidth to check if it is a 16Khz
			btfss	STATUS,Z   		;carry flag set if bandwidth is 16Khz
			call	decpoint		;jump to subroutine to add a decimal point		

			movfw	digitf			;output bandwidth LSB
			call	hex_ascii
			call	LCD_Char
	
			movlw	0x4b			;Khz notation after Bandwidth 4B 68 7A is hex for Khz
			call	LCD_Char
			movlw	0x68
			call	LCD_Char
			movlw	0x7a
			call	LCD_Char
			movlw	0x20			;Place a space to ensure the extra Z is removed when the decimal point is removed
			call	LCD_Char

			movlw	d'9'			;Place start of BFO readout
			call	LCD_Line2W

BFO_dis		movfw	digite			;output BFO ofset data
			call	hex_ascii
			call	LCD_Char
			movlw	0x2e			;Place a . to seperate the two digits
			call	LCD_Char
			movfw	digitd
			call	hex_ascii
			call	LCD_Char
			movfw	digitc
			call	hex_ascii
			call	LCD_Char
			movlw	0x4b			;Khz notation after BFO 4B 68 7A is hex for Khz
			call	LCD_Char
			movlw	0x68
			call	LCD_Char
			movlw	0x7a
			call	LCD_Char

			goto 	main_loop		;endless loop


;			#####################################################################
;			#         Main Program Ends. Subroutines and text tables start      #
;			#####################################################################

frequency	addwf	PCL, f
			retlw	'F'
			retlw	'r'
			retlw	'e'
			retlw	'q'
			retlw	'u'
			retlw	'e'
			retlw	'n'
			retlw	'c'
			retlw	'y'
			retlw	' '
            RETLW   'C'
            RETLW   'h'
            RETLW   'a'
            RETLW   'n'
			retlw	0x00

bandwidth	addwf	PCL, f
			retlw	'B'
			retlw	'a'
			retlw	'n'
			retlw	'd'
			retlw	'w'
			retlw	'i'
			retlw	'd'
			retlw	't'
			retlw	'h'
			retlw	' '
			retlw	' '
			retlw	'B'
			retlw	'F'
			retlw	'0'
			retlw	0x00

ssb_mode	addwf	PCL, f
			retlw	'S'
			retlw	'S'
			retlw	'B'
			retlw	' '
			retlw	'O'
			retlw	'p'
			retlw	'e'
			retlw	'r'
			retlw	'a'
			retlw	't'
            RETLW   'i'
            RETLW   'v'
            RETLW   'e'
			retlw	0x00

fixed_bw	addwf	PCL, f
			retlw	'F'
			retlw	'i'
			retlw	'x'
			retlw	'e'
			retlw	'd'
			retlw	' '
			retlw	'B'
			retlw	'W'
			retlw	' '
			retlw	'&'
            RETLW   ' '
            RETLW   'B'
            RETLW   'F'
            RETLW   'O'
			retlw	0x00

;		########################################################################################################
;		#         	Title subroutines. These are used to display static info on line 1 of displays 1 & 2	   #
;		########################################################################################################

frqu_header	clrf	count			;set counter register to zero
freq_title	call	LCD1			;display this message on display 1 line 1
			movf	count, w		;put counter value in W
			call	frequency		;get a character from the text table
			xorlw	0x00			;is it a zero?
			btfsc	STATUS, Z
			goto	to_main
			call	LCD_Char
			incf	count, f
			goto	freq_title
to_main		retlw	0x00

bw_header	clrf	count			;set counter register to zero
bw_title	call	LCD2			;display this message on display 2 line 1
			movf	count, w		;put counter value in W
			call	bandwidth		;get a character from the text table
			xorlw	0x00			;is it a zero?
			btfsc	STATUS, Z
			goto	to_main2
			call	LCD_Char
			incf	count, f
			goto	bw_title
to_main2	retlw	0x00

;		########################################################################################################
;		#       Subroutine to display fixed information on LCD 2 when receiver is in SSB mode				   #
;		########################################################################################################

ssb			call	LCD2			;display this message on display 1 line 1
			call	LCD_Clr
			call	LCD_Line1

ssb_header	clrf	count			;set counter register to zero
ssb_title	movf	count, w		;put counter value in W
			call	ssb_mode		;get a character from the text table
			xorlw	0x00			;is it a zero?
			btfsc	STATUS, Z
			goto	bw_line2
			call	LCD_Char
			incf	count, f
			goto	ssb_title

bw_line2	clrf	count			;set counter register to zero
			call	LCD_Line2
bw_text		movf	count, w		;put counter value in W
			call	fixed_bw		;get a character from the text table
			xorlw	0x00			;is it a zero?
			btfsc	STATUS, Z
			goto	freq_updat
			call	LCD_Char
			incf	count, f
			goto	bw_text

freq_updat	call	Delay80			;Add an 80ms delay so that SSB tunes at the same speed as other modes
			call	LCD1			;If this delay is commented out SSB will be more responsive but will make other modes look poor
			call	LCD_Line2		;get display data from digit xx files, convert to ASCII and output to LCD
			movfw	digit8
			call	hex_ascii
			call	LCD_Char
			movfw	digit7
			call	hex_ascii
			call	LCD_Char
			movfw	digit6
			call	hex_ascii
			call	LCD_Char
			movfw	digit5
			call	hex_ascii
			call	LCD_Char
			movfw	digit4
			call	hex_ascii
			call	LCD_Char		
			movlw	0x2e			;Place a . on the display after the Khz point
			call	LCD_Char
			movfw	digit3
			call	hex_ascii
			call	LCD_Char
			movfw	digit2
			call	hex_ascii
			call	LCD_Char
			movfw	digit1
			call	hex_ascii
			call	LCD_Char
			
Chan_dis2	movlw	0x20			;Place a space between freq and chan data
			call	LCD_Char
			movfw	digita			;Output Channel data
			call	hex_ascii
			call	LCD_Char
			movfw	digit9
			call	hex_ascii
			call	LCD_Char			
			

check_ssb2	movfw	digitf			;Check if still in SSB if so only update frequency
			call	hex_ascii
			movwf	ssbcheck2
			movlw	3fh				;load 3F into W to compare with ? for SSB
			subwf	ssbcheck2		;subtract w from digit f to test for SSB
			btfsc	STATUS,Z   		;carry flag set if mode is SSB
			goto	freq_updat		;jump to subroutine to display SSB message

			call	LCD2			;Clear LCD2 and reinstate BW / BFO display
			call	LCD_Clr
			call	LCD_Line1
			call	bw_header		;display 2 showing "Bandwidth BFO" on line 1
			retlw	0x00

;		#############################################
;		#			LCD routines					#
;		#############################################

LCD1		movlw	0x00			;set display to 1. lcdaddress is read by pulse_e
			movwf	lcdaddress
			retlw	0x00

LCD2		movlw	0x01			;set display to 2. lcdaddress is read by pulse_e
			movwf	lcdaddress
			retlw	0x00

;		########################################################################################################
;		#          Initialise the displays done twice to enable different setup on each display  if needed     #
;		########################################################################################################

LCD_Init	call	LCD1			;initialise display no 1
			movlw	0x20			;Set 4 bit mode
			call	LCD_Cmd

			movlw	0x28			;Set display shift
			call	LCD_Cmd

			movlw	0x06			;Set display character mode
			call	LCD_Cmd

			movlw	0x0c			;Set display on/off and cursor command
			call	LCD_Cmd

			call	LCD_Clr			;clear display
			call	Delay5
			
			call	LCD2			;initialise display no 2

			movlw	0x20			;Set 4 bit mode
			call	LCD_Cmd

			movlw	0x28			;Set display shift
			call	LCD_Cmd

			movlw	0x06			;Set display character mode
			call	LCD_Cmd

			movlw	0x0c			;Set display on/off and cursor command
			call	LCD_Cmd

			call	LCD_Clr			;clear display
			call	Delay5

			retlw	0x00

;			#####################################	
; 			#		command output routine		#
;			#####################################

LCD_Cmd		movwf	templcd
			swapf	templcd,	w	;send upper nibble
			andlw	0x0f			;clear upper 4 bits of W
			movwf	LCD_DATA
			bcf		LCD_CTRL, LCD_RS	;RS line to 0
			call	Pulse_e			;Pulse the E line high

			movf	templcd,	w	;send lower nibble
			andlw	0x0f			;clear upper 4 bits of W
			movwf	LCD_DATA
			bcf		LCD_CTRL, LCD_RS	;RS line to 0
			call	Pulse_e			;Pulse the E line high
			call 	Delay10
			retlw	0x00

;			#####################################	
; 			#  	   character output routine		#
;			#####################################
				
LCD_Char	movwf	templcd
			swapf	templcd,	w	;send upper nibble
			andlw	0x0f			;clear upper 4 bits of W
			movwf	LCD_DATA
			bsf		LCD_CTRL, LCD_RS	;RS line to 1
			call	Pulse_e			;Pulse the E line high
	
			movf	templcd,	w	;send lower nibble
			andlw	0x0f			;clear upper 4 bits of W
			movwf	LCD_DATA
			bsf		LCD_CTRL, LCD_RS	;RS line to 1
			call	Pulse_e			;Pulse the E line high
			call 	Delay5
			retlw	0x00

;			#############################################################################
; 			#		LCD Cursor CTRL Routines. Not all used but left in for final setup	#
;			#############################################################################

LCD_Line1	movlw	0x80			;move to 1st row, first column
			call	LCD_Cmd
			retlw	0x00

LCD_Line2	movlw	0xc0			;move to 2nd row, first column
			call	LCD_Cmd
			retlw	0x00

LCD_Line1W	addlw	0x80			;move to 1st row, column W
			call	LCD_Cmd
			retlw	0x00

LCD_Line2W	addlw	0xc0			;move to 2nd row, column W
			call	LCD_Cmd
			retlw	0x00

LCD_CurOn	movlw	0x0d			;Set display on/off and cursor command
			call	LCD_Cmd
			retlw	0x00

LCD_CurOff	movlw	0x0c			;Set display on/off and cursor command
			call	LCD_Cmd
			retlw	0x00

LCD_Clr		movlw	0x01			;Clear display
			call	LCD_Cmd
			retlw	0x00
			
decpoint	movlw	0x2e			;Routine to add a decimal point
			call	LCD_Char
			return
				

;			#####################################	
; 			#  	   HEX to ASCII conversion		#
;			#####################################

hex_ascii	andlw	0x0f		;mask off bottom four bits
			addlw	0x30		;add 30h to get ASCII
			return				;continue with ascii in w


;			#################################	
; 			#		Delay routines.			#
;			#################################


Delay100	movlw	d'100'		;delay 100mS
			goto	d0
Delay80		movlw	d'80'		;delay 80ms
			goto	d0
Delay50		movlw	d'50'		;delay 50mS
			goto	d0
Delay20		movlw	d'20'		;delay 20mS
			goto	d0
Delay10		movlw	d'10'		;delay 10mS
			goto	d0
Delay5		movlw	d'05'		;delay 5mS
			goto	d0
Delay1		movlw	0x01		;delay 1.000 ms (20 MHz clock)
d0			movwf	count1
d1			movlw	0xC7		;delay 1mS
			movwf	counta
			movlw	0x05
			movwf	countb
Delay_0
			decfsz	counta, f
			goto	$+2
			decfsz	countb, f
			goto	Delay_0
	
			decfsz	count1	,f
			goto	d1
			retlw	0x00


;			#############################################################	
; 			#		Pulse routines for the E line of displays 1 & 2		#
;			#############################################################


Pulse_e		btfss	lcdaddress, 0		;check if data is on display 1 or 2
			call	Pulse1				;if it's on display 1 call Pulse1
			btfss	lcdaddress, 0		;make sure we return without calling Pulse2
			retlw	0x00
			call	Pulse2				;if it's on display2 call Pulse2
			retlw	0x00

Pulse1		bsf		LCD_CTRL, LCD_E1	;toggle the E line of display 1
			nop
			bcf		LCD_CTRL, LCD_E1
			retlw	0x00

Pulse2		bsf		LCD_CTRL, LCD_E2	;toggle the E line of display 2
			nop
			bcf		LCD_CTRL, LCD_E2
			retlw	0x00


;########################################################################################################
;#           Interupt service routine triggered by -ve going pulse on PSP Interupt pin PORTE 1          #
;########################################################################################################

interrupt	movwf	w_save				;A write has occurred to the parallel slave port
			swapf	STATUS,w			;save existing state of the status and program counter regs
			clrf	STATUS
			movwf	status_save
			movf	PORTC,w				;least significant nibble is address
			andlw	0x0f
			addlw	digitbase

			movwf	FSR					;setup indirect addressing
			movf	PORTD,w				;get segment data from port D
			movwf	INDF				;save to indirectly addressed file register

			bcf		PIR1,PSPIF			;clear interupt flag

			swapf	status_save,w		;Restore STATUS reg and return from interrupt
			movwf	STATUS
			swapf	w_save,f
			swapf	w_save,w
			retfie

		end
