;CPLAY.SRC 


;Copyright 2/15/2002 Robert W. Adams, 6597 Lucerne Ct, Redding, CA 96001
;This software is available as FREEWARE for personal use only with no WARRANTY whatsoever. Commercial 
;use is strictly prohibited without written permission from the author.

;------------------------------------------------------------------------------------------------------

		device	sx28l, stackx_optionx, osc4mhz, turbo
		reset	start
;--------------------------------------------------------------------------------------------
; Global Register definitions

		org     $08

temp		ds	1    		; Temporary variable
flags		ds	1		; Flags to indicate various things..
byte		ds 	1	
idx		ds 	1

		
rx_flag		equ	flags.1		; Indicates the reception of a bit from the UART
MENUBTNFLG	equ	flags.2		;used in interrupt routine to indicate that menu button pressed
MENUBTNHIT	equ	flags.3		;indicates menubutton hit
GOUPBTNHIT	equ	flags.4
DNBTNHIT	equ	flags.5

;-------------------------------------------------------------------------------------------------
;bank equates use in fsr or bank cmd
MAINBANK	equ	$10
CWBANK 		equ	$30
TIMERBANK	equ	$50
SERIALBANK	equ	$70
STRINGBANK	equ	$90
SPIBANK		equ	$B0
MENUBANK	equ	$D0
BTODBANK	equ	$F0

;--------------------------------------------------------------------------------------------
;binary to decimal registers and equates

		org	BTODBANK
dividend	ds	2
divisor		ds	2
quotient	ds	2
counter		ds	1
decdigit	ds	5		;decimal digits
binum		ds	2		;binary number to be converted to decimal
btodidx		ds	1
ndecdigits	ds	1


;---------------------------------------------------------------------------------------------
;Serial registers & equates

		org     SERIALBANK
tx_high		ds      1	;hi byte to transmit
tx_low		ds      1	;low byte to transmit
tx_count	ds      1	;number of bits sent
tx_divide	ds      1	;xmit timing (/16) counter
rx_count	ds      1	;number of bits received
rx_divide	ds      1	;receive timing counter
rx_byte		ds      1	;buffer for incoming byte
string		ds	1	;used by send_string to store the address in memory
hex		ds	1

rx_pin          equ     ra.3	;UART receive input
;tx_pin          equ     ra.3	;UART transmit output

;----------------------------------------------------------------------------------------------
;16 bit timer registers & equates also 40usec timer
		org	TIMERBANK
timer		ds	1	;timer control
timerhb		ds	1	;16 bit timer
timerlb		ds	1
dseconds	ds	1	;# of seconds to delay in DelaySeconds
timerstart	equ	timer.0
timerdone	equ	timer.1
tm1		ds	1		;40 us timer

;-----------------------------------------------------------------------------------------------
;string registers and equates
		org	STRINGBANK
string_addr_hb	ds 	1
string_addr_lb	ds 	1

;-------------------------------------------------------------------------------------------------------------
;menu registers & equates

		org MENUBANK
MENUBTN		equ	ra.2
GOUPBTN		equ	rb.0
DNBTN		equ	rb.1
DOWNLDLED	equ	rb.7
SPEEDLED	equ	rb.2
TEXTLED		equ	rb.3
BUTTONHB	equ	14		;delay for button debounce
BUTTONLB	equ	1
MENUQTY		equ	4		;# of items in menu

menusel		ds	1

;--------------------------------------------------------------------------------------------------------------
;cw registers
		org	CWBANK
cwbyte		ds	1		;dot dash pattern where 0=dot 1=dash (W: 0110 0000)
ncwbits		ds	1		;number of bits (dots - dashes) to send (above = 3)
asciibyte	ds	1		;ascii code for chr to send
cwstr_addr_hb	ds	1		;address of string to send
cwstr_addr_lb	ds	1
cwbit		equ	cwbyte.7	;next bit to send as dot or dash
ndotpds		ds	1		;# dot periods (ie dash = 3 dot periods, etc)
cwdotimer	ds	2		;hb & lb of dot time delay (see cw equates)
cwwpm		ds	1		;index to dot timer table
cwfreqidx	ds	1		;used in interrupt routine to generate 800HZ cw tone
ncharspacedots	ds	1		;# of dots in char space - will vary when in Farnsworth mode
nspacedots	ds	1		;# of dots in space - will vary when in Farnsworth mode
cwdiv		ds	1		;used to calc cwdotimer & cwdotimer+1 in divide routine

;----------------------------------------------------------------------------------------------
;spi registers and equates (spi is interface to 24c64 serial eeproms
		org	SPIBANK
spibyte		ds	1		;byte sent to 24c64 control, address, & data
spidata		ds	1		;data to be written to 24c64 adress
spibitct	ds	1
spistatus	ds	1
spiaddr		ds	2
spicontrol	ds	1
spidx		ds	1
spiaddrhld	ds	2		;hold spiaddr in SpiMemToLCD
spinchrs	ds	1
ACK		equ	spistatus.0	;ACK bit from 24c64
ENDMEMFLG	equ	spistatus.1	;1 when memory is filled up to ENDMEMHB
SDA		equ	ra.0
SCL		equ	ra.1
RACONFIG	equ	%00001100	;1 = input 
RAACKCONFIG	equ	%00001101
CONTROL		equ	%10100000
SPIADDRMASK	equ	%00001110

;24c64 eeprom memory address constants
ENDMEMHB	equ	$3F		;end of eeprom = $3FFF IncSpiAddr resets spiaddr to 0 at 3F00 leaving 256 bytes at end
					;of eeprom for storage (cw speed, menu prompts, etc
ENDOFCWCHR	equ	$24		;character ($) sent from pc on rs232 to indicate end of cw file
ENDFILEMEMLB	equ	$00		;end of file marker ($) to make sure cw send does not read past $3F00
ENDFILEMEMHB	equ	$3F
CWSPEEDMEMLB	equ	$01		;store cw speed in wpm (cwtimeridx = speed in wpm)
CWSPEEDMEMHB	equ	$3F
PWRSPIADDRLB	equ	$04
PWRSPIADDRHB	equ	$3F
ONETIMEHB	equ	$3F		;write ONETIME to this address in eeprom on first write to eeprom
ONETIMELB	equ	$06
ONETIME		equ	%01100101

;------------------------------------------------------------------------------------------------

PAGE0		equ	$000		;0 - 511
PAGE1		equ	$200		;512 - 1023
PAGE2		equ	$400		;1024 - 1535
PAGE3		equ	$600		;1536 - 2047

;-------------------------------------------------------------------------------------------
SPACE 		equ	$20		;ascii codes
LITTLEZ		equ	$7A
PERIOD		equ	$2E
COMMA		equ	$2C
QUESTION	equ	$3F
STAR		equ	$2A
PERCENT		equ	$25		;used to trigger contest code wait
LINEFEED	equ	$0A

SPACECWBYTE	equ	$F8		;
;-------------------------------------------------------------------------------------------
;cw equates

CWFREQCOUNT	equ	6		;1/(104e-6 x 12) = 801HZ
MINCHRWPM	equ	18		;used for Farnsworth method send chrs @ 18wpm with char spaces at slower speed
ONEPTTWOSECHB	equ	45		;<dotime = 1.2sec/wpm>   <1.2sec/104 usec = 11538 which is hb = 45 & lb = 18
ONEPTTWOSECLB	equ	18		;the 104 usec is the interrupt routine rate

NDOTSINDASH 	equ	3		;# of dot times in dash
NDOTSBTWNDDS	equ	1		;# of dot times between dots & dashes
NDOTSBTWNCHRS	equ	2		;# of dot times between chrs  (actually willbe NDOSTBTWNDDS + NDOTSBTWNCHRS) = 3
NDOTSINSPACE	equ	2		;# of dot times in space between words
					;word space = NDOSTBTWNDDS + NDOTSBTWNCHRS + NDOTSINSPACE + NDOTSBTWNCHRS) = 7
MAXCWIDX	equ	80		;max idx to cw speed table (idx = cw speed in wpm)
MINCWIDX	equ	5		;min
DOTIME18WPMHB	equ	2		;dot time @ 18 wpm = 1.2/wpm or 11538/wpm based on interrupt every 104 usec
DOTIME18WPMLB	equ	129		;this sets dot and dash times for Farnsworth speeds ( < 18wpm)
	
ONESECHB 	equ	38
ONESECLB 	equ	143	;function DelaySeconds hb & lb for one second delay
TWOMSECHB	equ	1
TWOMSECLB	equ	19
EIGHTMSECHB	equ	1
EIGHTMSECLB	equ	77

;--------------------------------------------------------------------------------------------
CWKEYRC0	equ	rb.5		;cw key output
CWFREQOUT	equ	rb.4		;800HZ tone output
;DOTSWITCH	equ	rc.5
;DASHSWITCH	equ	rc.6
PWROFF		equ	rb.6		;goes to 0 when pwr shut off (input)

;--------------------------------------------------------------------------------------------
; Program constants


int_period	equ	208	; Interrupt to operate at .25us x 208 = 52us with rtcc set for/2 = 104 usec
_enter		equ	13	; ASCII value for carridge return
_line_feed	equ	10	; ASCII value for a line feed


;2400 baud (for slower baud rates, increase the RTCC prescaler) rtcc prescale = 1 which gives baud rate of 1200
baud_rate_24	=	8			;for 2400 baud rate generation
start_delay_24	=	13

;----------------------------------------------------------------------------------------------------
; SX18AC/20AC/28AC Mode addresses
; *On SX18/20/28, all registers addressed via mode are write only, with the exception of
; CMP and WKPND which do an exchange with W.

; Exchange addresses
CMP		equ	$08		;Exchange Comparator enable/status register with W
WKPND		equ	$09		;Exchange MIWU/RB Interrupts pending with W

; Port setup (read) addresses
WKED_W		equ	$0A		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	$0B		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	$0C		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	$0D		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W		equ	$0E		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W		equ	$0F		;Write Port Direction


;----------------------------------------------------------------------------------------------------
; Interrupt Service Routine
;--------------------------------------------------------------------------------------------------------

interrupt	org     PAGE0

;--------------------------------------------------------------------------------------------------------

:interrupt	;800HZ cw tone output code
		bank	CWBANK
		jnb	CWKEYRC0,:notone	;if cw key pin is a 0 do not send tone
		dec	cwfreqidx
		jnz	:endtone

		jb	CWFREQOUT,:clearit	;toggle output pin for 800 hz tone
		setb	CWFREQOUT
		jmp	:reload
:clearit	clrb	CWFREQOUT

:reload		mov	cwfreqidx,#CWFREQCOUNT
		jmp	:endtone

:notone		clrb	CWFREQOUT
:endtone
		
		;rs232 recieve code
		bank	SERIALBANK		;switch to serial register bank
:receive        sb	rx_pin			;get current rx bit
	        clc				; 
	        snb	rx_pin			; 
	        stc				; 
		test    rx_count                ;currently receiving byte?
		sz				; 
		jmp	:rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc				;skip ahead if not start bit
		mov     rx_count,w              ;it is, so renew bit count
		mov     w,#start_delay_24	;ready 1.5 bit periods
		mov     rx_divide,w		; 
:rxbit          decsz	rx_divide		;middle of next bit?
          	jmp	:rxdone			;
		mov	w,#baud_rate_24		;yes, ready 1 bit period
		mov	rx_divide,w		; 

		dec     rx_count                ;last bit?
		sz                              ;if not
		rr      rx_byte                 ; then save bit
		snz                             ;if so,
		setb    rx_flag                 ; then set flag
:rxdone						; else, exit

;-------------------------------------------------------------------------------------------------------
;16 bit timer
		bank	TIMERBANK
		sb	timerstart		;skip timer routine if timerstart is not set
		jmp	:end
;		decsz	timerlb		;PROBLEM!! when lb is 0 it is decremented to 255 on 1st pass thus adding 255 to count!!
;		jmp	:end

		cje	timerlb,#0,:testhb	;seems to work speed OK at 45wpm which is hb=1, lb=0  1/7/02
		dec	timerlb
		jmp	:end

:testhb		dec	timerlb
		test 	timerhb
		sz		
		jmp	:else
		setb	timerdone
:else		dec	timerhb
:end
;------------------------------------------------------------------------------------------------------------
;set MENUBTNHIT flag if menu button pressed (grounded)

		bank 	MENUBANK
		sb	MENUBTN
		setb	MENUBTNHIT

;-----------------------------------------------------------------------------------------------------------
;check for pwr down if so save current spi address
		jnb	PWROFF,isr_end		;a +5 on PWROFF rc.7 triggers pwr off sequence
		call	@SavePwrDnAddr		;saves current spi address & goes into sleep mode

;---------------------------------------------------------------------------------------------------------
; End of the Interrupt Service Routine

isr_end		mov	w,#-int_period		;refresh RTCC on return
		retiw				;return from the interrupt

;-------------------------------------------------------------------------------------------------------
;jump table
TimerDelay	jmp	@_TimerDelay
SendBinNum	jmp	@_SendBinNum
Divide		jmp	@_Divide
BinToDec	jmp	@_BinToDec
get_byte	jmp	@_get_byte

GetDotTimes	jmp	@_GetDotTimes
SendCWfromMem	jmp	@_SendCWfromMem
SendString	jmp	@_SendString
KeyChar		jmp	@_KeyChar
DotSend		jmp	@_DotSend
DashSend	jmp	@_DashSend	
SpaceSend	jmp	@_SpaceSend
DotTimer	jmp	@_DotTimer
ToneOn		jmp	@_ToneOn
ToneOff		jmp	@_ToneOff
DelaySeconds	jmp	@_DelaySeconds
GetCWTable	jmp	@_GetCWTable
ChrSpaceDotTable	jmp@_ChrSpaceDotTable
SpaceDotTable		jmp@_SpaceDotTable
GetCWData	jmp	@_GetCWData
CWSpeedUp	jmp	@_CWSpeedUp
CWSpeedDn	jmp	@_CWSpeedDn
SaveCWSpeed	jmp	@_SaveCWSpeed			;at end of spi page 1
SendCWSpeed	jmp	@_SendCWSpeed			;at end of spi page 1

SpiRS232toMem	jmp	@_SpiRS232toMem
SpiClrMem	jmp	@_SpiClrMem
SpiWrite	jmp	@_SpiWrite
SpiRead		jmp	@_SpiRead
SpiAddrInc	jmp	@_SpiAddrInc
SpiWriteAddr	jmp	@_SpiWriteAddr
SpiDelay	jmp	@_SpiDelay
SpiStart	jmp	@_SpiStart
SpiStop		jmp	@_SpiStop
SpiSendByte	jmp	@_SpiSendByte
SpiReadByte	jmp	@_SpiReadByte
SpiACK		jmp	@_SpiACK
SpiMemUp	jmp	@_SpiMemUp
SpiMemDn	jmp	@_SpiMemDn
SpiAddrSend	jmp	@_SpiAddrSend
SaveSpiAddress	jmp	@_SaveSpiAddress
RestoreSpiAddress	jmp	@_RestoreSpiAddress
SavePwrDnAddr	jmp	@_SavePwrDnAddr

Menu1		jmp	@_Menu1
Menu2		jmp	@_Menu2
Menu3		jmp	@_Menu3
Menu4		jmp	@_Menu4
IncMenuSel	jmp	@_IncMenuSel
DelayButton	jmp	@_DelayButton
StartTimer	jmp	@_StartTimer
WaitButtons	jmp	@_WaitButtons



;-------------------------------------------------------------------------------------------------------

; Program execution begins here on power-up or after a reset

start

; Init ports
		mode	DDIR_W			;point MODE to write DDIR register
		mov	w,#%00001100		;Setup RA Direction register, 0 = output, 1 = input		
		mov	!ra,w	
		mov	w,#%01000011
		mov	!rb,w			

		mode	PLP_W			;mode = pullup enable
		mov	!ra,#%11111011		;(0 = enable ) (1 = disable) menu
		mov	!rb,#%10111100		;pwroff, Go+, - 

		mov 	w,#%00000000		;12/5/03 configure port c just to be sure
		mov 	!rc,w
		mov	rc,#%00000000


; Clear all Data RAM locations NOTE: Will get random chr in get_byte routine if not done! 3/3/01

		clr	fsr			;reset all ram banks
:zero_ram	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram

		;rtcc prescale set for /2 to convert 2400 baud params to 1200
		mov	w,#%10000000			
		mov	!option,w

		bank	CWBANK
		clrb	CWFREQOUT		;cw 800HZ output pin off
		call	@ToneOff		;cw tone off

		bank	SPIBANK
		clrb	ENDMEMFLG

		bank	MENUBANK
		clrb	MENUBTNHIT

;------------------------------------------------------------------------------------------------------------
;main code
		bank	SPIBANK				;write end of file ($) at 3F00 to make sure cw send stops here
		mov	spiaddr,#ENDFILEMEMLB		
		mov	spiaddr+1,#ENDFILEMEMHB
		mov	spidata,#ENDOFCWCHR
		call	@SpiWrite

		bank	SPIBANK				;check for ONETIME indicating that eeprom has been written to at least once
		mov	spiaddr,#ONETIMELB		
		mov	spiaddr+1,#ONETIMEHB
		call	@SpiRead

		cje	spidata,#ONETIME,:continue		;if ONETIME then continue

		;default to 20wpm
:first		bank	SPIBANK
		mov	spidata,#20			
		mov	spiaddr,#CWSPEEDMEMLB		
		mov	spiaddr+1,#CWSPEEDMEMHB
		call	@SpiWrite

		;default to spi address of $0000
		bank	SPIBANK
		mov	spiaddr,#PWRSPIADDRLB
		mov	spiaddr+1,#PWRSPIADDRHB
		mov	spidata,#0
		call	@SpiWrite
		inc	spiaddr
		mov	spidata,#0
		call	@SpiWrite

		mov	spiaddr,#ONETIMELB		;write ONETIME to indicate that eeprom was programmed at least once
		mov	spiaddr+1,#ONETIMEHB
		mov	spidata,#ONETIME
		call	@SpiWrite


		;read spi address saved at pwr down or written durng first program of eeprom
:continue	bank	SPIBANK
		mov	spiaddr,#PWRSPIADDRLB
		mov	spiaddr+1,#PWRSPIADDRHB
		call	@SpiRead
		mov	spiaddrhld,spidata
		inc	spiaddr
		call	@SpiRead
		mov	spiaddrhld+1,spidata
		call	@RestoreSpiAddress

		;read cw speed saved at pwr down or written during first program of eeprom
		bank	SPIBANK				;check for valid cw speed at CWSPEEDMEMLB,HB
		mov	spiaddr,#CWSPEEDMEMLB		
		mov	spiaddr+1,#CWSPEEDMEMHB
		call	@SpiRead
		bank	SPIBANK				;GetDotTime uses cwwpm (cw wpm speed) to set lb,hb of cwdotime			
		mov	temp,spidata
		bank	CWBANK
		mov	cwwpm,temp
		call	@GetDotTimeS
		call	@RestoreSpiAddress

		clrb	ENDMEMFLG

		bank	MENUBANK
		mov	menusel,#1			;start with menu item #1 send cw
		jmp	@MainMenu


;-------------------------------------------------------------------------------------------------------------
; UART Subroutines

; Function: get_byte
; Get byte via serial port and echo it back to the serial port
; INPUTS:
;	-NONE
; OUTPUTS:
;	-received byte in rx_byte

_get_byte	bank	SERIALBANK
:here		sb	rx_flag			;wait till byte is received
		jmp	:here
		clrb    rx_flag			;reset the receive flag
		mov     w,rx_byte		;store byte (copy using W)
		retp

;-------------------------------------------------------------------------------------------------------------
;16 bit interrupt timer delay function - on call place timer values in timerhb & timerlb
_TimerDelay	
		bank	TIMERBANK
		clrb	timerstart
		clrb 	timerdone
		setb	timerstart
:tst1		sb	timerdone		;skip next instr if timerdone is 1
		jmp	:tst1
		retp

;------------------------------------------------------------------------------------------------------------------------------
;converts a 2 byte binary number to a n digit decimal number n = ndigits
;input to BinToDec = binum least significant byte = binum
;decdigit least significant digit = decdigit
;outputs ascii code for dec #s to cw 
;ndecdigits = # of decimal digits to convert and send

_SendBinNum
	call	@BinToDec
	BANK	BTODBANK

;	mov	decdigit,#5
;	mov	decdigit+1,#7

	mov	FSR,#decdigit		;point FSR to decdigit register NOTE: must use bank instruction so that correct register is accessed
	add	FSR,#1			;point fsr to most significant decimal digit
	mov	btodidx,#2		;want to go thru loop 2 times

:loop
	add	IND,#$30		;convert decdigit+n to ascii
	mov	temp,IND		;MUST put into temp since bank instruction will point IND to an udesired register (NOT decdigit)
	bank	CWBANK
	mov	asciibyte,temp
	call 	@KeyChar
	bank	CWBANK
	mov	ndotpds,ncharspacedots
	call	@DotTimer		;delay between chrs


	BANK	BTODBANK	
	dec	FSR			;display next lower order digit
	djnz	btodidx,:loop
	
;	clrb	DOWNLDLED			;TEMP ************************
;:here	jmp	:here

	retp

;---------------------------------------------------------------------------------------------------------------------------
;converts a 16 bit binary number (binum) to a 5 digit decimal number in (decdigit - decdigit+4)

;1) divide binary number by 10 remainder will be lowest order decimal digit decdigit
;2) use quotient from each previous divide and divide that by 10 & each remainder is next higher order decimal digit
;3) when quotient is 0 the conversion is complete

_BinToDec
	BANK	BTODBANK
	clr	decdigit			;MUST clear outside of loop since loop may not loop thru all decdigits
	clr	decdigit+1
	clr	decdigit+2
	clr	decdigit+3
	clr	decdigit+4
	mov	FSR,#decdigit			;point FSR to decdigit0

	mov	dividend,binum
	mov	dividend+1,binum+1
	jmp	:init				;on first loop dividend is the binary number to be converted to decimal

:loop
	mov	dividend,quotient		;divide each succeeding quotient by 10
	mov	dividend+1,quotient+1
:init	mov	divisor,#10
	mov	divisor+1,#0
	call	@Divide
	mov	IND,dividend			;remainder (in divident) is decimal digit n
	inc	FSR

	cjne	quotient,#0,:loop		;if quotient == 0 conversion is done
	cjne	quotient+1,#0,:loop

	retp

;---------------------------------------------------------------------------------------------------------------------------
;16bit x 16bit divide (quotient = dividend\divisor)
;NOTE: after division finished remainder is in dividend (dividend/divisor)

;	quotient = dividend/divisor	
;	divide algorithm steps
;	1) Set the quotient to 0
;	2) Shift the divisor to the left until the topmost bit is a 1
;	3) Remember how many shifts you performed in step 2 and add 1 to this count
;	4) Shift the quotient to the left (multiply by 2)
;	5) Compare the dividend and the divisor; if the dividend is greater than or equal to the divisor, subtract
;	the divisor from the dividend and add 1 to the quotient
;	6) Shift the divisor to the right
;	7) Subtract 1 from the count and if not zero, return to step 4


_Divide
		BANK	BTODBANK
		clr	counter
		clc

		;shift divisor to left until highest order bit == 1
:loop		rl	divisor
		rl	divisor+1
		inc	counter
		jnb	divisor+1.7,:loop		
		inc 	counter			;adjust count

		clr	quotient
		clr	quotient+1


:dloop		cje	counter,#0,:done
		clc
		rl	quotient
		rl	quotient+1

		cjb	dividend+1,divisor+1,:dloop1	;if dividend < divisor then goto :dloop
		cja	dividend+1,divisor+1,:subit
		cjb	dividend,divisor,:dloop1

:subit
		sub	dividend,divisor		;dividend = dividend-divisor
		subb	dividend+1,/status.0		
		sub	dividend+1,divisor+1

		add	quotient,#1
		addb	quotient+1,status.0

:dloop1
		dec counter
		clc
		rr	divisor+1
		rr	divisor
		jmp	:dloop

:done		retp


;------------------------------------------------------------------------------------------------------

		org	PAGE1

;---------------------------------------------------------------------------------------------------------
;get bytes from rs232 and put into memory until ENDOFCW ($) received from pc or ENDADDRHB is reached in mem

_SpiRS232toMem
		bank	SERIALBANK
		clrb    rx_flag			;reset the receive flag in case extraneous bits received on RS232
		
		bank	SPIBANK
		mov	spiaddr,#0
		mov	spiaddr+1,#0

:top		call 	@get_byte
		mov	temp,w			;bank stmt below will mess up w register

		bank	SPIBANK
		mov	spidata,temp
		call	@SpiWrite
		call	@SpiAddrInc			;incr address
		cje	spidata,#ENDOFCWCHR,:resetit	;stop receiving on rs232 if char ($) received
		
		sb	ENDMEMFLG	;if end of memory flag set from SpiIncAddr stop receiving on rs232
		jmp	:top

:resetit	bank	SPIBANK
		mov	spiaddr,#0	;reset eeprom pointer to 0
		mov	spiaddr+1,#0
		clrb	ENDMEMFLG
		clrb	DOWNLDLED	;download led off to indicate end of file received
		retp

;---------------------------------------------------------------------------------------------------------------------
;clear memory up to last 256 bytes - takes approx 60 sec

_SpiClrMem
		bank	SPIBANK
		mov	spiaddr,#0
		mov	spiaddr+1,#0

:top		jb	ENDMEMFLG,:resetit		;if end of memory flag set from SpiIncAddr stop
		mov	spidata,#ENDOFCWCHR
		call	@SpiWrite
		call	@SpiAddrInc			;incr address
		jmp	:top

:resetit	clrb	ENDMEMFLG			;clear end of mem flag 
		mov	spiaddr,#0			;point to begin of mem
		mov	spiaddr+1,#0
		retp

;--------------------------------------------------------------------------------------------------------
;write byte(spidata) to specified address(spiaddr) in 24c64
_SpiWrite
		call	@SpiWriteAddr			;NOTE: SpiWriteAddr sets SPIBANK & sets up control w/ addr & calls start
		mov	spibyte,spidata			;write data byte
		call	@SpiSendByte
		call	@SpiStop
		call 	@SpiDelay

:loop		call	@SpiStart			;poll 24c64 to find if write operation done (abt 5msec)
		mov	spibyte,spicontrol		;spicontrol will have correct addr from SpiWriteAddr
		call	@SpiSendByte

		snb	ACK				;if ACK = 0 write cycle is finished
		jmp 	:loop

		retp
;-------------------------------------------------------------------------------------------------------
;read byte(spidata) from specified address(spiaddr, spiaddr+1) in 24c64
_SpiRead
		call	@SpiWriteAddr			;NOTE: SpiWriteAddr sets SPIBANK & sets up control w/ addr & calls start
		setb	spicontrol.0			;set control for read

		call	@SpiStart
		mov	spibyte,spicontrol		;control byte for read 
		call	@SpiSendByte

		call	@SpiReadByte			;read byte into spibyte
		mov	spidata,spibyte
		call	@SpiStop
		retp

;---------------------------------------------------------------------------------------------------------
;incr spiaddr & check for end address - if end found reset address to $0000
_SpiAddrInc
		bank	SPIBANK
		add	spiaddr,#1
		addb	spiaddr+1,status.0		;add carry bit to hb

		cjne	spiaddr+1,#ENDMEMHB,:end	;only check hb so that last 256, 512, etc bytes can be saved
		setb	ENDMEMFLG
		mov	spiaddr,#0
		mov	spiaddr+1,#0

:end		retp

;--------------------------------------------------------------------------------------------------------
;write address to 24c64 for data read or write
_SpiWriteAddr
		bank	SPIBANK
		mov	spicontrol,spiaddr+1		;need 3 high order bits of address in spicontrol
		rr	spicontrol			;rotate 3 high oder bits into bit3 - bit1
		rr	spicontrol
		rr	spicontrol
		rr	spicontrol
		and	spicontrol,#SPIADDRMASK		;zero all bits that resulted from rotate thru carry other than address
		or 	spicontrol,#CONTROL
		clrb	spicontrol.0			;read write bit 0 for write

		call	@SpiStart
		mov	spibyte,spicontrol		;write control byte
		call	@SpiSendByte

		mov	spibyte,spiaddr+1		;write address high byte
		call	@SpiSendByte

		mov	spibyte,spiaddr			;write address low byte
		call	@SpiSendByte
		retp

;--------------------------------------------------------------------------------------------------------
_SpiDelay	bank	TIMERBANK
		mov 	tm1,#4			;approx 3 usec delay - check with scope
:loop		decsz	tm1
		jmp 	:loop
		bank	SPIBANK
		retp

;------------------------------------------------------------------------------------------------------
_SpiStart	bank	SPIBANK
		setb	SDA
		setb	SCL
		nop			;24c64 needs 600ns setup & hold times
		nop
		clrb	SDA		;bring SDA low while SCL high for start
		nop
		nop
		clrb	SCL
		retp

;-------------------------------------------------------------------------------------------------------
_SpiStop		bank	SPIBANK
		clrb	SDA
		setb	SCL
		nop			;24c64 needs 600ns setup & hold times
		nop
		setb	SDA		;bring SDA high while SCL high for stop
		nop
		nop
		clrb	SCL
		retp

;------------------------------------------------------------------------------------------------------
;send byte most significant bit first
_SpiSendByte	bank	SPIBANK
		mov	spibitct,#8
		mov	temp,spibyte
:loop		snb	spibyte.7		;if(spibyte.7 == 1) then SDA = 1
		setb	SDA
		sb	spibyte.7		;if(spibyte.7 == 0) then SDA = 0
		clrb	SDA

		call 	@SpiDelay
		setb	SCL			;clock high for 24c64 to accept data
		call 	@SpiDelay
		clrb	SCL
		rl	spibyte			;shift << byte to send next bit
		decsz	spibitct
		jmp	:loop

		call	@SpiACK
		mov	spibyte,temp		;restore original spibyte
		retp

;----------------------------------------------------------------------------------------------------------
_SpiReadByte	bank	SPIBANK
		mode	DDIR_W			;make SDA an input
		mov	w,#RAACKCONFIG
		mov	!ra,W

		mov	spibyte,#0
		mov	spibitct,#8

:loop		rl	spibyte			;shift << byte (NOTE: will not affect spibyte on first pass since spibyte = 0
		setb	SCL			;clock high for 24c64 to send bit
		call 	@SpiDelay
		snb	SDA			;if(SDA == 1) then spibyte.0 = 1
		setb	spibyte.0
		sb	SDA			;if(SDA == 0) then spibyte.0 = 0
		clrb	spibyte.0

		clrb	SCL
		call 	@SpiDelay
		
		decsz	spibitct
		jmp	:loop

		mode	DDIR_W			;restore SDA to output
		mov	w,#RACONFIG
		mov	!ra,W
		call	@SpiACK
		retp

;------------------------------------------------------------------------------------------------------------
_SpiACK		bank	SPIBANK
		mode	DDIR_W			;make SDA an input
		mov	w,#RAACKCONFIG
		mov	!ra,W

		call 	@SpiDelay
		setb	SCL			;clock high for 24c64 to send ack on SDA (need to read?)
		call 	@SpiDelay

		snb	SDA			;if(SDA == 1) then ACK = 1
		setb	ACK
		sb	SDA			;if(SDA == 0) then ACK = 0
		clrb	ACK

		clrb	SCL

		mode	DDIR_W			;restore SDA to output
		mov	w,#RACONFIG
		mov	!ra,W
		retp

;-------------------------------------------------------------------------------------------------------------------
;move up in eeprom by 256 bytes & check for $ in any byte of spiaddr+1

_SpiMemUp
		bank	SPIBANK
		mov	spiaddr,#0			;advance on 256 byte segments
		inc	spiaddr+1			;incr hb of spiaddr
		
:top		call	@SpiRead			;if endofcw ($) is encountered dec spiaddr+1
		cje	spidata,#ENDOFCWCHR,:decit
		incsz	spiaddr
		jmp	:top

		jmp	:ret1		
:decit		dec	spiaddr+1

:ret1		retp

;----------------------------------------------------------------------------------------------------------------
;move back in eeprom by 256 bytes

_SpiMemDn
		bank	SPIBANK
		mov	spiaddr,#0			;back up on 256 byte segments
		cje	spiaddr+1,#0,:showit		;do not back up past 0
		dec	spiaddr+1
:showit		
		retp

;---------------------------------------------------------------------------------------------------
;send hb of spi address

_SpiAddrSend
		bank	SPIBANK
		mov	temp,spiaddr+1			;get hb of current spi addr

		BANK	BTODBANK
		mov	binum,temp
		mov	binum+1,#0
		call	@SendBinNum
		retp

;----------------------------------------------------------------------------------------------------
;save current spi address to spiaddrhld

_SaveSpiAddress
		bank	SPIBANK
		mov	spiaddrhld,spiaddr		;save spiaddr
		mov	spiaddrhld+1,spiaddr+1	
		retp
;----------------------------------------------------------------------------------------------------
;restore spi address from spiaddrhld

_RestoreSpiAddress
		bank	SPIBANK
		mov	spiaddr,spiaddrhld		;save spiaddr
		mov	spiaddr+1,spiaddrhld+1	
		retp

;------------------------------------------------------------------------------------------------------
;Save current spi address on power down (PWROFF rc.7 = +5) and go to sleep 

_SavePwrDnAddr
		call	@SaveSpiAddress			;see above will set bank also
		mov	spiaddr,#PWRSPIADDRLB
		mov	spiaddr+1,#PWRSPIADDRHB
		mov	spidata,spiaddrhld		;get lb of current address & save
		call	@SpiWrite			
		inc	spiaddr
		mov	spidata,spiaddrhld+1		;get hb of current address & save
		call	@SpiWrite			
		sleep

;------------------------------------------------------------------------------------------------------------------
;Delay for number of seconds


_DelaySeconds	bank 	TIMERBANK

:loop		clrb	timerstart
		clrb	timerdone
		mov 	timerlb,#ONESECLB		;approx 1 sec delay
		mov	timerhb,#ONESECHB
		setb	timerstart

:chktim		sb	timerdone		;wait for timer (timerdone bit = 1)
		jmp	:chktim
		decsz	dseconds
		jmp	:loop
		retp
;-----------------------------------------------------------------------------------------------------
;save changed cw speed to eeprom

_SaveCWSpeed
		call	@GetDotTimes			;update cwdotime hb,lb based on cwwpm 
		bank	CWBANK
		mov	temp,cwwpm
		bank	SPIBANK
		mov	spidata,temp
		call	@SaveSpiAddress			;save current spi address
		mov	spiaddr,#CWSPEEDMEMLB		;address for cwwpm to be saved to
		mov	spiaddr+1,#CWSPEEDMEMHB
		call	@SpiWrite
		call	@RestoreSpiAddress

		retp

;------------------------------------------------------------------------------------------------------------------------------
;send current cw speed

_SendCWSpeed
		bank	SPIBANK
		call	@SaveSpiAddress			;save current spi address
		mov	spiaddr,#CWSPEEDMEMLB		;address for cwwpm to be saved to
		mov	spiaddr+1,#CWSPEEDMEMHB
		call	@SpiRead
		mov	temp,spidata

		BANK	BTODBANK
		mov	binum,temp
		mov	binum+1,#0

		call	@SendBinNum
		call	@RestoreSpiAddress

		retp

;--------------------------------------------------------------------------------------------------------------------------------------
		org	PAGE2

;-------------------------------------------------------------------------------------------------------------------------------------
		;returns dot-dash pattern & number of dots-dashes combined into 8 bits
		;bits 7-3 are dot dash pattern, bits 2-0 are # of dots-dashes
		;example R = 0100 0011 the leading 010 is dot dash dot bits 2-0 indicates 3 dots-dashes
		;calling sub GetCWdata must trap chrs with more than 5 dots-dashes [.(2E) ,(2C) ?(3F) *(2A)]
		;calling sub GetCWdata must also trap space
		;first 20 unused entries removed so calling program must subtract 20 from # put into w
_GetCWTable	jmp	pc+w

			;$20  $21  $22  $23  $24  $25  $26  $27  $28  $29  $2A  $2B  $2C  $2D  $2E  $2F
			;sp                                                 *    +    ,    -    . 
		retw	 $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $55, $00, $8D, $00, $00

			;$30  $31  $32  $33  $34  $35  $36  $37  $38  $39  $3A  $3B  $3C  $3D  $3E  $3F
			; 0    1    2    3    4    5    6    7    8    9                              ?
		retw	 $FD, $7D, $3D, $1D, $0D, $05, $85, $C5, $E5, $F5, $00, $00, $00, $00, $00, $00

			;$40  $41  $42  $43  $44  $45  $46  $47  $48  $49  $4A  $4B  $4C  $4D  $4E  $4F
			;      A    B    C    D    E    F    G    H    I    J    K    L    M    N    O
		retw	 $00, $42, $84, $A4, $83, $01, $24, $C3, $04, $02, $74, $A3, $44, $C2, $82, $E3

			;$50  $51  $52  $53  $54  $55  $56  $57  $58  $59  $5A  $5B  $5C  $5D  $5E  $5F
			; P    Q    R    S    T    U    V    W    X    Y    Z   
		retw	 $64, $D4, $43, $03, $81, $23, $14, $63, $94, $B4, $C4, $00, $00, $00, $00, $00

			;$60  $61  $62  $63  $64  $65  $66  $67  $68  $69  $6A  $6B  $6C  $6D  $6E  $6F
			;      a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
		retw	 $00, $42, $84, $A4, $83, $01, $24, $C3, $04, $02, $74, $A3, $44, $C2, $82, $E3

			;$70  $71  $72  $73  $74  $75  $76  $77  $78  $79  $7A
			; p    q    r    s    t    u    v    w    x    y    z
		retw	 $64, $D4, $43, $03, $81, $23, $14, $63, $94, $B4, $C4

;------------------------------------------------------------------------------------------------------------------
;dot period = 1.2/wpm
;#of 104 usec periods = 11538/wpm

;from arrl code-std file the following is used to calc character & word spaceing for Farnsworth speeds (< 18wpm)
;s = overall speed in wpm
;c = character speed in wpm, we will use 18wpm for the Farnsworth threshold
;Ta = (60*c - 37.2*s)/(s*c) using s = 18 Ta = (1080 - 37.2*s)/(18*s)
;Tc = character spacing in seconds = (3*Ta)/19
;Tw = word spacing in seconds = (7*Ta)/19
;char space in dot periods = (Tc/dot period) - 1 (-1 because there is always a dot period after each dot or dash)
;these table entries were calculated in excel FarnsCalcs spreadsheet

_ChrSpaceDotTable
		jmp	pc+w
		
			;0	1	2	3	4	5	6	7	8	9
		retw	22,	22,	22,	22,	22,	22,	18,	14,	12,	10

			;10	11	12	13	14	15	16	17
		retw	8,	7,	6,	5,	4,	4,	3,	2


;NOTICE --> when sending a char then a space the following occurs:
;<char> <char space> <space> <char space> there is a char space after space so 
;space in dot periods = (Tw/dot period) - 2*#char dot periods from above table then -1 for dot period after last dot or dash

_SpaceDotTable
		jmp	pc+w
		
			;0	1	2	3	4	5	6	7	8	9
		retw	9,	9,	9,	9,	9,	9,	7,	6,	5,	5

			;10	11	12	13	14	15	16	17
		retw	4,	4,	3,	3,	3,	3,	2,	2

;-----------------------------------------------------------------------------------------------------------------
;set timing parameters for dot time, char space, & space

_GetDotTimes
		bank	CWBANK
		cjbe	cwwpm,#MAXCWIDX,:chkspeed	;make sure range of above table not exceeded
		mov	cwwpm,#MAXCWIDX

:chkspeed	cjb	cwwpm,#MINCHRWPM,:farns	;jump if wpm (cwwpm) < Farnsworth speed (MINCHRWPM)

		;set up parameters for Farnsworth wpm >= MINCHRWPM (18wpm)
		mov	ncharspacedots,#NDOTSBTWNCHRS	;# of dot periods between chars
		mov	nspacedots,#NDOTSINSPACE	;# of dot periods in space

		;<dotime = 1.2sec/wpm>   <1.2sec/104 usec = 11538 which is hb = 45 & lb = 18
		;the 104 usec is the interrupt routine rate
		;quotient = dividend/divisor: cwdotimer = 11538/cwwpm  NOTE:cwwpm = wpm
		BANK	BTODBANK
		mov	dividend,#ONEPTTWOSECLB
		mov	dividend+1,#ONEPTTWOSECHB
		BANK	CWBANK
		mov	temp,cwwpm
		BANK	BTODBANK
		mov	divisor,temp
		mov	divisor+1,#0
		call	@Divide

		mov	temp, quotient
		BANK	CWBANK
		mov	cwdotimer,temp
		BANK	BTODBANK
		mov	temp, quotient+1
		BANK	CWBANK
		mov	cwdotimer+1,temp
		jmp	:ret


		;set up parameters for Farnsworth wpm < MINCHRWPM (18wpm)
:farns		mov	cwdotimer,#DOTIME18WPMLB
		mov	cwdotimer+1,#DOTIME18WPMHB

		mov	w,cwwpm
		call	@ChrSpaceDotTable
		mov	ncharspacedots,w
		mov	w,cwwpm	
		call	@SpaceDotTable
		mov	nspacedots,w

:ret		retp

;------------------------------------------------------------------------------------------------------------------
_SendCWfromMem	bank	CWBANK

		bank	SPIBANK
;		mov	spiaddr,#0
;		mov	spiaddr+1,#0

:loop		bank	SPIBANK
		call	@SpiRead
		bank 	SPIBANK

		cjne	spidata,#LINEFEED,:nolcd	;convert line feed to space 
		mov	spidata,#SPACE

:nolcd		bank 	SPIBANK
		call	@SpiAddrInc			;incr address
		cje	spidata,#ENDOFCWCHR,:resetit	;exit if ($ = end of file) encountered

		mov	temp,spidata
		bank	CWBANK
		mov	asciibyte,temp

		call	@KeyChar	
		bank	CWBANK

:dotit		bank	CWBANK
		mov	ndotpds,ncharspacedots
		call	@DotTimer		;delay between chrs
		
		bank	MENUBANK		;exit if menu button hit
		snb	MENUBTNHIT
		jmp	:endit

		jmp	:loop

:resetit	bank	SPIBANK
		clrb	ENDMEMFLG			;clear end of mem flag 
		mov	spiaddr,#0			;point to begin of mem
		mov	spiaddr+1,#0
		mov	spinchrs,#0			;spi nchrs to display on top LCD line = 0

:endit		clrb	MENUBTNHIT
		retp

;-----------------------------------------------------------------------------------------------------
;on entry string_addr_hb contains high 4 bits of 12 bit address of string table
;on entry string_addr_lb contains low 8 bits of 12 bit address of string table

_SendString	bank	CWBANK

:loop		mov	m,cwstr_addr_hb
		mov	w,cwstr_addr_lb
		iread				;reads address m:w where m is high 4 bits & w is low 4 bits
		mov	asciibyte,w		;check for string terminating 0
		cje	asciibyte,#0,:eloop
					
		call	@KeyChar	
		bank	CWBANK
		mov	ndotpds,ncharspacedots
		call	@DotTimer		;delay between chrs
		
		inc	cwstr_addr_lb		;this will increment thru table
		jmp	:loop

:eloop		retp


;----------------------------------------------------------------------------------------------------------
;Key characters including space
_KeyChar	bank 	CWBANK
		call	@GetCWdata		;returns dot-dash pattern in cwbyte & # of dot-dashes in ncwbits

		cjne	cwbyte,#SPACECWBYTE,:toploop	;check for space
		call	@SpaceSend
		jmp	:endret

:toploop	sb	cwbit			;test high order bit of cwbyte for dot or dash
		call	@DotSend
		snb	cwbit
		call	@DashSend

		mov	w,<<cwbyte
		mov	cwbyte,w
		decsz	ncwbits
		jmp	:toploop
		
:endret		retp

;---------------------------------------------------------------------------------------------------------

_DotSend	bank	CWBANK
		call 	@ToneOn			;tone on for 1 dot period
		

:dtime		mov	ndotpds,#1
		call	@DotTimer
		call	@ToneOff		;tone off for NDOTSBTWNDDS (1) dot periods (does space between dots & dashes)
		mov	ndotpds,#NDOTSBTWNDDS
		call	@DotTimer

		retp				

;-----------------------------------------------------------------------------------------------------------

_DashSend	bank 	CWBANK

		call	@ToneOn			;tone on for 1 dash period
:dtime		mov	ndotpds,#NDOTSINDASH
		call	@DotTimer
		call	@ToneOff			;tone off for NDOTSBTWNDDS (1) dot periods (does space between dots & dashes)
		mov	ndotpds,#NDOTSBTWNDDS
		call	@DotTimer

		retp

;----------------------------------------------------------------------------------------------------------------

_SpaceSend	bank 	CWBANK
		call	@ToneOff			;tone off for NDOTSINSPACE (1) dot periods (does space between words)
		mov	ndotpds,nspacedots
		call	@DotTimer

		bank	CWBANK
:end		retp

;----------------------------------------------------------------------------------------------------------------
;ndotpds contains the number of times to delay for one Dot period

_DotTimer	bank 	TIMERBANK
		clrb	timerstart
		clrb	timerdone

		bank	CWBANK			;cwdotimer contains lb,hb of current dot time delay
		mov	temp,cwdotimer
		bank	TIMERBANK
		mov	timerlb,temp
		bank	CWBANK
		mov	temp,cwdotimer+1
		bank	TIMERBANK
		mov	timerhb,temp

		setb	timerstart

:chktim		sb	timerdone		;wait for timer (timerdone bit = 1)
		jmp	:chktim

		bank	CWBANK			;NOTE: on exit will be in CWBANK
		decsz	ndotpds
		jmp	_DotTimer
		retp
;------------------------------------------------------------------------------------------------------------------
;turn cw tone on and off

_ToneOn		bank	CWBANK
		setb	CWKEYRC0
		retp

_ToneOff	bank	CWBANK
		clrb	CWKEYRC0
		retp

;---------------------------------------------------------------------------------------------------------

		;return cwbyte (dot dash pattern) and ncwbits (# of dots & dashes)
		;make sure that asciibyte is >=$20(space) & <=7A(z)
		;subtract 20 from asciibyte to compensate for first 20 unused entries in table 
		;check for .(2E) ,(2C) ?(3F) *(2A)
_GetCWdata	bank	CWBANK

		;make sure asciibyte is in allowed range [SPACE =< asciibyte <= LITTLEZ]
		cjb	asciibyte,#SPACE,:InError
		cja	asciibyte,#LITTLEZ,:InError


:SpaceCheck	;check for SPACE 
		cse	asciibyte,#SPACE
		jmp	:PeriodCheck
		mov	cwbyte,#SPACECWBYTE		;kludged dot-dash pattern for space
		mov	ncwbits,#0
		jmp	:endsub

:PeriodCheck	;check for PERIOD which cannot be put in table (6 dot-dashes + 3#bits > 8bits)
		cse	asciibyte,#PERIOD
		jmp	:CommaCheck
		mov	cwbyte,#$54		;dot-dash pattern for period
		mov	ncwbits,#6
		jmp	:endsub

:CommaCheck	;check for COMMA which cannot be put in table (6 dot-dashes + 3#bits > 8bits)
		cse	asciibyte,#COMMA
		jmp	:QuestionCheck
		mov	cwbyte,#$CC		;dot-dash pattern for comma
		mov	ncwbits,#6
		jmp	:endsub

:QuestionCheck	;check for QUESTION which cannot be put in table (6 dot-dashes + 3#bits > 8bits)
		cse	asciibyte,#QUESTION
		jmp	:StarCheck
		mov	cwbyte,#$30		;dot-dash pattern for question
		mov	ncwbits,#6
		jmp	:endsub

:StarCheck	;check for STAR which cannot be put in table (6 dot-dashes + 3#bits > 8bits)
		cse	asciibyte,#STAR
		jmp	:WaitCheck
		mov	cwbyte,#$14		;dot-dash pattern for star (...-.-)
		mov	ncwbits,#6
		jmp	:endsub

:WaitCheck	;% means delay until (Go+) button hit
		cjne	asciibyte,#PERCENT,:AllOtherCheck		;is asciibyte==% 
		mov	cwbyte,#SPACECWBYTE		;kludged dot-dash pattern for space
		mov	ncwbits,#0
:here		jb	GOUPBTN,:here					;wait for button press
		jmp	:endsub

:AllOtherCheck	mov	w,#$20			;offset for table
		mov 	w,asciibyte-w		;asciibyte is index to table - w now holds adjusted index to table
		call	@GetCWTable
		mov	cwbyte,w
		and	w,#%00000111		;mask to get ncwbits
		mov	ncwbits,w
		jmp	:endsub

:InError	mov	cwbyte,#SPACECWBYTE		;added 12/5/03 to fix problem with Hypertext sending
		mov	ncwbits,#0			;invalid ascii codes. change invalid codes to spaces

:endsub		retp

;-------------------------------------------------------------------------------------------------------------------
;increase cw speed by 1 wpm

_CWSpeedUp
		bank	CWBANK
		inc	cwwpm			;increase cw wpm by 1 (cwwpm = wpm)	
		cjbe	cwwpm,#MAXCWIDX,:savit
		mov	cwwpm,#MAXCWIDX		;max allowed cw wpm

:savit		call	@SaveCWSpeed
		call	@GetDotTimes			;update cwdotime hb, lb, nchrspacedots, nspacedots based on cwwpm 

		retp

;----------------------------------------------------------------------------------------------------------------
;decrease cw speed by 1 wpm

_CWSpeedDn
		bank	CWBANK
		dec	cwwpm			;decrease cw wpm by 1 (cwwpm = wpm)	
		cjae	cwwpm,#MINCWIDX,:savit
		mov	cwwpm,#MINCWIDX		;min allowed cw wpm

:savit		call	@SaveCWSpeed
		call	@GetDotTimes			;update cwdotime hb, lb, nchrspacedots, nspacedots based on cwwpm 

		retp

;--------------------------------------------------------------------------------------------------------------------------------

;All menu functions are in this bank
		org	PAGE3

;------------------------------------------------------------------------------------------------------------------------------
MainMenu
		;call appropriate menu based on number in menusel
		;will loop thru here continuously after jmp from main

		bank	MENUBANK
		clrb	DOWNLDLED
		clrb	SPEEDLED
		clrb	TEXTLED
		clrb	GOUPBTNHIT
		clrb	DNBTNHIT
		clrb	MENUBTNHIT


:toploop	BANK	MENUBANK
		cjne	menusel,#1,:Mnu2
		call	@Menu1			;send cw
		clrb	MENUBTNHIT

:Mnu2		BANK	MENUBANK
		cjne	menusel,#2,:Mnu3
		call	@Menu2			;download
		clrb	MENUBTNHIT

:Mnu3		BANK	MENUBANK
		cjne	menusel,#3,:Mnu4	;cw speed up/dn
		call	@Menu3			
		clrb	MENUBTNHIT
		
:Mnu4		BANK	MENUBANK
		cjne	menusel,#4,:toploop	;text position up/dn
		call	@Menu4			
		clrb	MENUBTNHIT

		jmp	:toploop

;---------------------------------------------------------------------------------------------------------------------------------
;send cw
_Menu1

		bank	MENUBANK
		clrb	DOWNLDLED
		clrb	SPEEDLED
		clrb	TEXTLED

:loop		jnb	MENUBTN,:IncMenuSel		;if MENUBTN hit increment menu selection #
		jnb	GOUPBTN,:sendcw			;send cw
		jmp	:loop

:sendcw		
		call	@SendCWfromMem			;go back to menu button loop and wait for go button to start over again
		jmp	:loop

:IncMenuSel	call	@IncMenuSel
		retp

;----------------------------------------------------------------------------------------------------------------------
;download new text file


_Menu2
		bank	MENUBANK
		setb	DOWNLDLED
		clrb	SPEEDLED
		clrb	TEXTLED

:loop		jnb	MENUBTN,:IncMenuSel		;if MENUBTN hit increment menu selection #
		jnb	GOUPBTN,:dwnload		;download
		jmp	:loop

:dwnload
		call	@SpiRS232toMem			;download

:IncMenuSel	call	@IncMenuSel
		retp

;----------------------------------------------------------------------------------------------------------------------
;cw speed up/dn if both up & dn buttons pressed send current cw speed
;

_Menu3
		bank	MENUBANK
		clrb	DOWNLDLED
		setb	SPEEDLED
		clrb	TEXTLED
		bank	TIMERBANK
		clrb	timerstart
		clrb 	timerdone

:loop
		bank	MENUBANK
		clrb	GOUPBTNHIT
		clrb	DNBTNHIT
		clrb	MENUBTNHIT
		

:toploop	bank	MENUBANK
		jnb	MENUBTN,:IncMenuSel	;if MENUBTN hit increment menu selection #
		snb	GOUPBTN			;has GOUPBTN been pressed? will get 0 on press
		jmp	:ckdnbtn		;no check for dn button press
		setb	GOUPBTNHIT		;yes set GOUPBTNHIT
		bank	TIMERBANK
		snb	timerstart		;has timer been started?
		jmp	:ckdnbtn		;yes check for dn button press
		call	@StartTimer		;no start timer
		
:ckdnbtn	bank	MENUBANK
		snb	DNBTN			;has DNBTN been pressed? will get 0 on press
		jmp	:cktimer			;no check for timer done		
		setb	DNBTNHIT		;yes set DNBTNHIT
		bank	TIMERBANK
		snb	timerstart		;has timer been started?
		jmp	:cktimer			;yes check timer
		call	@StartTimer		;no start timer
		
:cktimer	bank	TIMER
		sb	timerdone		;if timer done then break out of loop
		jmp	:toploop

		;check for both buttons pressed
		bank	MENUBANK
		snb	GOUPBTNHIT		;has goup button been pressed?
		jmp	:ckforboth		;yes check for both buttons pressed
		jmp 	:ckeach			;no check each

:ckforboth	snb	DNBTNHIT		;has down been pressed?
		jmp	:sendcwspeed		;yes, both buttons have been pressed so send cw speed

:ckeach		snb	GOUPBTNHIT		;has goup button been pressed?
		jmp	:upcwspeed		;yes increase cw speed
		snb	DNBTNHIT		;has dn button been pressed?
		jmp	:dncwspeed		;yes decrease cw speed
		jmp	:loop			;SHOULD never reach here

:sendcwspeed	;both buttons pressed so send cw speed
		call	@SendCWSpeed

		call	@WaitButtons
		jmp	:loop

:upcwspeed	;increase cw speed
		call	@CWSpeedUp
		call	@WaitButtons
		jmp	:loop

:dncwspeed	;decrease cw speed
		call	@CWSpeedDn
		call	@WaitButtons
		jmp	:loop

:IncMenuSel	call	@IncMenuSel
		retp

;------------------------------------------------------------------------------------------------------------------
;text position up/dn if both up & dn buttons pressed send current text position

_Menu4

		bank	MENUBANK
		clrb	DOWNLDLED
		clrb	SPEEDLED
		setb	TEXTLED
		bank	TIMERBANK
		clrb	timerstart
		clrb 	timerdone

:loop		
		bank	MENUBANK
		clrb	GOUPBTNHIT
		clrb	DNBTNHIT
		clrb	MENUBTNHIT
		

:toploop	bank	MENUBANK
		jnb	MENUBTN,:IncMenuSel	;if MENUBTN hit increment menu selection #
		snb	GOUPBTN			;has GOUPBTN been pressed? will get 0 on press
		jmp	:ckdnbtn		;no check for dn button press
		setb	GOUPBTNHIT		;yes set GOUPBTNHIT
		bank	TIMERBANK
		snb	timerstart		;has timer been started?
		jmp	:ckdnbtn		;yes check for dn button press
		call	@StartTimer		;no start timer
		
:ckdnbtn	bank	MENUBANK
		snb	DNBTN			;has DNBTN been pressed? will get 0 on press
		jmp	:cktimer			;no check for timer done		
		setb	DNBTNHIT		;yes set DNBTNHIT
		bank	TIMERBANK
		snb	timerstart		;has timer been started?
		jmp	:cktimer			;yes check timer
		call	@StartTimer		;no start timer
		
:cktimer	bank	TIMER
		sb	timerdone		;if timer done then break out of loop
		jmp	:toploop

		;check for both buttons pressed
		bank	MENUBANK
		snb	GOUPBTNHIT		;has goup button been pressed?
		jmp	:ckforboth		;yes check for both buttons pressed
		jmp 	:ckeach			;no check each

:ckforboth	snb	DNBTNHIT		;has down been pressed?
		jmp	:sendtxtpos		;yes, both buttons have been pressed so send text position

:ckeach		snb	GOUPBTNHIT		;has goup button been pressed?
		jmp	:uptext			;yes move up in text file
		snb	DNBTNHIT		;has dn button been pressed?
		jmp	:dntext			;yes move back in text
		jmp	:loop			;SHOULD never reach here

:sendtxtpos	;both buttons pressed so send text position
		call	@SpiAddrSend
		call	@WaitButtons
		jmp	:loop

:uptext		;move up in text
		call	@SpiMemUp
		call	@WaitButtons
		jmp	:loop

:dntext		;move back in text
		call	@SpiMemDn
		call	@WaitButtons
		jmp	:loop

:IncMenuSel	call	@IncMenuSel
		retp

;------------------------------------------------------------------------------------------------------------------------
;increment menusel & check for being greater than MENUQTY
_IncMenuSel
		bank	MENUBANK
		inc	menusel
		cjbe	menusel,#MENUQTY,:delayit
		mov	menusel,#1

:delayit	call	@DelayButton		;debounce delay
		retp

;-----------------------------------------------------------------------------------------------------------
;delay for button debounce
_DelayButton
		bank	TIMERBANK
		mov	timerhb,#BUTTONHB
		mov	timerlb,#BUTTONLB
		call 	@TimerDelay
		bank	MENUBANK
		retp

;-----------------------------------------------------------------------------------------------------------------------------
;start button timer - do not wait for timerdone as in DelayButton
_StartTimer
		bank	TIMERBANK
		clrb	timerstart
		clrb 	timerdone
		setb	timerstart
		mov	timerhb,#BUTTONHB
		mov	timerlb,#BUTTONLB
		retp

;---------------------------------------------------------------------------------------------------------------
;wait until all buttons open (high) for button debounce period
_WaitButtons

:startit	call	@StartTimer

:loop		bank	MENUBANK
		sb	GOUPBTN		;is button pressed? (GOUPBTN = 0)
		jmp	:startit	;yes then restart timer
		sb	DNBTN		;is button pressed? (GOUPBTN = 0)
		jmp	:startit	;yes then restart timer
		
		bank	TIMER
		sb	timerdone		;if timer done then break out of loop
		jmp	:loop

		clrb	timerstart
		clrb 	timerdone
		retp





