;*****************************************************************************************
; The NJQRP "Badger" Smartbadge IDer
; 
; Version 1.0   3/8/2002   Original issue, pre-Atlanticon - (no dit upon entry to Cal mode)
;
; Version 1.1	3/15/2002  a) One dit upon entry to Cal mode
;			   b) Added KEYLINE control
;
; Version 1.2	3/23/2002  a) Two dits upon entry to Cal mode
;			   b) Added switch debounce
;
; Version 1.3	11/5/2002  a) Three dits upon entry to Cal mode
;			   b) Removed "special mode J" (no longer meaningful)
;			   c) Put initial default speed to slower value (approx 10 wpm)
;			   d) Enabled "free form text with spaces" to be sent, 
;			      no longer ending at first space but at terminating zero.
;			   e) Set unused IRDA expansion port bits (RB6, RB7, RA0, RA1, RA2)
;			      as inputs with pullup resistors enabled.  Decreases
;			      standby and sleep current (to 0 ua and 0.86 ma, respectively),
;			      thus extending battery life.
;
; Copyright 2002  G. Heron, N2APB  All Rights Reserved
; Permission is granted solely for personal, non-profit use.
; 
; George Heron, N2APB
; 2419 Feather Mae Court
; Forest Hill, MD  21050
; USA
;
;*****************************************************************************************
		device	SX18L,OSC1MHz,stackx_optionx	;SX-Key device directives
		id	'BadgerV1' 
		reset	reset_entry			;set reset vector

;*********************************************************************************
; Global Register definitions
;*********************************************************************************
		org     $08		;start of program registers

isr_temp	ds	1		;temp variable used by the ISR
cntr_2		ds	1
int_cntr	ds	1
achar		ds	1		;ASCII callsign character being converted
mchar		ds	1		;Morse equivalent of the character
flag1		ds	1		;holder for 8 flag bits
flag2		ds	1		;holder for 8 more flag bits
buffptr		ds	1		;pointer to callsign buffer
int_value	ds	1		;interrupt reload value
dirty		ds	1		;location determines if 1st power-on after battery insertion
pb_result	ds	1
pb_timer	ds	1
speed		ds	1

;*****************************************************************************************
; System Equates

LED		equ	rb.0		;LED on port B bit 0
PIEZO		equ	rb.1		;Piezo on port B bit 1
KEYLINE		equ	rb.2		;KeyLine
P_BUTTON	equ	rb.3		;Pushbutton on port B bit 3
dit_pin		equ	rb.5		;Dit paddle pin
dah_pin		equ	rb.4		;Dah paddle pin

DEFAULT_SPEED	equ	75		;relative speed of transmitted code (default = 75 = 10wpm)
SPACE		equ	' '

BEEPFLAG	equ	flag1.0		;flag bit indicates when Piezo toggle (beep) desired in ISR
BEEPSTATE	equ	flag1.1		;flag bit indicates state of Piezo bit, for toggle purposes
CONTINUOUS	equ	flag1.2		;flag bit indicates when continuous mode in effect
CALIBRATE	equ	flag1.3		;flag bit indicates when calibrate mode in effect
SPECIAL		equ	flag1.4		;flag bit indicates when special mode in effect
dit_memory	equ	flag1.5		;memory bit for dit paddle
dah_memory	equ	flag1.6		;memory bit for dah paddle

USE_LED		equ	flag2.0		;flag bit indicates use of LED
USE_PIEZO	equ	flag2.1		;flag bit indicates use of PIEZO
IAMBIC_KEYER	equ	flag2.2		;flag bit set = Iambic Keyer.  Bit reset = Straight key
IAMBIC_A	equ	flag2.3
IAMBIC_B	equ	flag2.4

;*****************************************************************************************
; Interrupt Service Routine
;
; Interrupt Frequency = (Cycle Frequency / -(retiw value))  For example:
; With a retiw value of -37 and an oscillator frequency of 250 KHz (1 MHz in compatibility mode),
; this code runs every 148us. (4us instruction cycle period x 37 = 148us.)
; Since this ISR toggles the Piezo control bit during each pass when BEEPFLAG is set, 
; the Piezo period is 2 x 148us = 296us, which sounds at 3.37 KHz
; Note: the internal R/C clock generator is untrimmed, and varies with Vdd and temp, so the
; frequency may not be exactly as calculated. Coupled with piezo device variations, one
; should experiment with the RETIW value to obtain the loudest volume from the Piezo device.

		org     $0

interrupt	mov	w,m			;Save the m register.
		mov	isr_temp,w

		mode	$09			;reset the interrupt flag
		mov	!rb,#%00000000

	; Interrupt Counter: Used for basic timing in wait_10ms timer routine

int_timer	dec	int_cntr

	; Toggle the PIEZO bit if BEEPFLAG is set

		jnb	BEEPFLAG,isr_end	;no beep required, so just exit ISR

		jb	BEEPSTATE,isr_2		;toggle PIEZO bit
		setb	BEEPSTATE
		setb	PIEZO
		jmp	isr_end
isr_2		clrb	beepstate
		clrb	PIEZO

	; Set Interrupt Rate and exit

isr_end		mov	w,isr_temp		;Restore the m register.
		mov	m,w
		mov	w,int_value		;load interrupt reload value, set by calibration routine
		retiw				;return from the interrupt

;*****************************************************************************************
; RESET VECTOR -- Program execution begins here on power-up, after a reset, or after a wake-up

reset_entry	mode	$0C		
		mov	!rb,#%11110111		;enable schmitt trigger on pushbutton rb.3
		mode	$0E		
		mov	!rb,#%00000111		;enable pull-ups on pushbutton rb.7, rb.6, rb.5, rb4, rb.3
		mov	!ra,#%00000000		;enable pull-ups on unused ra.0-ra.3 bits
		mode 	$0F
		mov	!rb,#%11111000		;set LED rb.0 (out), PIEZA rb.1 (out), KEYLINE rb.2 (out)
						;pushbutton rb.3 (in), rb.4 dit (in), and rb.5 dah pin (in)
		mov	!ra,#%11111111		;set ra.0, ra.1, ra.2 and ra.3 as inputs
		setb	PIEZO
		mode	$0A	
		mov	!rb,#%11111111		;interrupt on falling edge of pushbutton on rb.3
		mode	$0B		
		mov	!rb,#%11110111		;enable wake-up on pushbutton rb.3
		mode	$09		
		mov	!rb,#%00000000		;clear register to allow new interrupts

		clr	flag1

		mov     !option,#%00011111      ;enable rtcc interrupt and wreg

		clrb	KEYLINE			;turn off keyline to transmitter initially

	; Detect if this is the first time run since putting in battery.
	; If so, execute the following code.  (Dirty location is set in calibrate routine,
	; causing us never to execute this code after calibration has occurred.)

		cje	dirty,#34,main
		setb	USE_LED			;use LED initially
		setb	USE_PIEZO		;use PIEZO initially
		mov	speed,#DEFAULT_SPEED
		call	@calibrate_tone

;*****************************************************************************************
; MAIN PROGRAM  
;*****************************************************************************************

main		call	@char_space		;debounce the switch
		jb	P_BUTTON,main_1
		jmp	main

main_1		mov	buffptr,#callsign	;init the callsign buffer pointer

main_loop	call	@getnextchar		;get next callsign char into achar
		jz	callsign_done
		cjne	achar,#SPACE,convert_it	;good char, not a space, go convert and annunciate
		call	@letter_space		;yes, Continuous mode. Wait letter_space time
		jmp	main_loop		;and getnextchar to continue past space

convert_it	call	@mlookup		;lookup Morse code equivalent, put into mchar
shift_it	clc				;clear carry, which goes into b7
		rr	mchar			;rotate b0 into CY
		jc	play_dah		;'1' = dah; '0' = dit
		call	@dit			;play the dit
		jmp	check_done
play_dah	call	@dah			;play the dah

	; If pushbutton pressed during callsign, abort the operation and go to sleep

check_done	jb	P_BUTTON,check_done_1	;if PB pressed during call, cancel modes and goto sleep
		clrb	CONTINUOUS
		clrb	CALIBRATE
		clrb	SPECIAL
		sleep

	; Done with character?

check_done_1	cse	mchar,#1		;done with char?
		jmp	shift_it		;not done yet
		call	@char_space		;done with char, wait inter-char time
		jmp	main_loop		;go get next char in callsign	

	; Done with Callsign ... see if in continuous mode

callsign_done	jnb	CONTINUOUS,check_PB	;Continuous mode not already set, check for active PB
		call	@two_letter_space	;Continuous mode is active, so wait in between passes
		jmp	main			;and go loop again

	; Check pushbutton & do desired item when PB release after menu letter

check_PB	call	@two_letter_space	;wait some to allow user to select menu
		snb	P_BUTTON		;is pushbutton being pressed?
		sleep				;no, go to sleep until next Start pushbutton

		call 	@letter_space		;menu selected
		call	@two_letter_space	;slight pause for release (signifies selection)
		call	@dit			;send "A"
		call	@dah
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_2
		clrb	CONTINUOUS		;menu A = NORMAL mode
		clrb	IAMBIC_A
		sleep

check_PB_2	call	@dah			;send "B"
		call	@dit
		call	@dit
		call	@dit	
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_3
		setb	CONTINUOUS		;menu B = BEACON mode	
		clrb	IAMBIC_A
		jmp	main			;and go loop

check_PB_3	call	@dah			;send "C"
		call	@dit
		call	@dah
		call	@dit
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_4	;menu C = CALIBRATE mode
		clrb	IAMBIC_A
		jmp	@calibrate_tone		;and go to calibrate

check_PB_4	call	@dah			;send "D"
		call	@dit
		call	@dit
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_5	;menu D = IAMBIC KEYER mode
		setb	IAMBIC_A
		jmp	@keyer			;and go do keyer

check_PB_5	call	@dit			;send "E"
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_6	;menu E = SET SPEED mode
		jmp	@set_speed

check_PB_6	call	@dit			;send "F"
		call	@dit
		call	@dah
		call	@dit
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_7	;menu F = STRAIGHT KEY mode
		clrb	IAMBIC_A
		jmp	@straightkey		;and go do straight key

check_PB_7	call	@dah			;send "G"
		call	@dah
		call	@dit
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_8	;menu G = PIEZO mode
		setb	USE_PIEZO
		clrb	USE_LED		
		clrb	IAMBIC_A
		jmp	main

check_PB_8	call	@dit			;send "H"
		call	@dit
		call	@dit
		call	@dit
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_9
		setb	USE_LED			;menu H = LED mode
		clrb	USE_PIEZO
		clrb	IAMBIC_A		
		jmp	main

check_PB_9	call	@dit			;send "I"
		call	@dit
		call	@two_letter_space	;slight pause for release (signifies selection)
		jnb	P_BUTTON,check_PB_10
		setb	USE_LED			;menu I = LED & PIEZO mode	
		setb	USE_PIEZO
		clrb	IAMBIC_A	
		jmp	main

check_PB_10	sleep


;*************************************************************************
;*************************************************************************
; PAGE 1
;*************************************************************************
;*************************************************************************

		org	$200				;page 1 memory

;*************************************************************************
; mlookup - Morse Lookup Routine
;           ASCII value in achar is index into table
;           Returned value in W is the code for Morse character

mlookup		sub	achar,#$30		;strip get binary offset into table
		mov	w,achar
		call	mtable			;get Morse equivalent
		mov	mchar,w			;and store it
		retp
		
;*************************************************************************
;      MORSE CODE LOOKUP TABLE
;
; This table contains the codes used to represent Morse letters.
; 

mtable		jmp	PC+W
		retw	%00111111	; 0
		retw	%00111110	; 1
		retw	%00111100	; 2
		retw	%00111000	; 3
		retw	%00110000	; 4
		retw	%00100000	; 5
		retw	%00100001	; 6
		retw	%00100011	; 7
		retw	%00100111	; 8
		retw	%00101111	; 9
		retw	%00000000	; null
		retw	%00000000	; null
		retw	%00000000	; null
		retw	%00000000	; null
		retw	%00000000	; null
		retw	%00000000	; null
		retw	%00000000	; null
		retw	%00000110	; A
		retw	%00010001	; B
		retw	%00010101	; C
		retw	%00001001	; D
		retw	%00000010	; E
		retw	%00010100	; F
		retw	%00001011	; G
		retw	%00010000	; H
		retw	%00000100	; I
		retw	%00011110	; J
		retw	%00001101	; K
		retw	%00010010	; L
		retw	%00000111	; M
		retw	%00000101	; N
		retw	%00001111	; O
		retw	%00010110	; P
		retw	%00011011	; Q
		retw	%00001010	; R
		retw	%00001000	; S
		retw	%00000011	; T
		retw	%00001100	; U
		retw	%00011000	; V
		retw	%00001110	; W
		retw	%00011001	; X
		retw	%00011101	; Y
		retw	%00010011	; Z

;*******************************************************************************
; Get Next Char -- returns next character from beaconbuffer in W

getnextchar	mov	w,buffptr		;point lower 8 bits of indirect address to buffer						
		mov	m,#6			;point upper 4 bits of indirect addr to page 6 at end of program
		iread				;read a char from the buffer into M:W
		mov	m,#$0F			;reset the mode register
		test	w			;at end of buffer?
		jz	gnc3			;yes, exit now with Z=1
		inc	buffptr			;else, increment pointer
		mov	achar,w			;and store callsign char
gnc3		retp				;exit


;*******************************************************************************
; Keyer Mode

keyer		setb	LED			;stop things initially
		clrb	BEEPFLAG
		clrb	dit_memory		;clear memories
		clrb	dah_memory
		call	@letter_space
		call	@letter_space
		
wait_paddle	sb	P_BUTTON
		sleep				;sleep if pushbutton set
		jnb	dit_pin,do_dit		;if dit paddle hit, go do dit
		snb	dah_pin			;if dah paddle hit, go do dah
		jmp	wait_paddle		;await paddle movement

do_dah		clrb	dah_memory		;clear dah memory
		call	@dah			;do a dah
		jb	dit_memory,do_dit	;if dit memory set, go do a dit
		jmp	wait_paddle		;and go wait again

do_dit		clrb	dit_memory		;clear dit memory
		call	@dit			;do a dit
		jb	dah_memory,do_dah	;if dah memory set, go do a dah
		jmp	wait_paddle		;and go wait again

;*******************************************************************************
; Straight Keyer Mode

straightkey	jnb	dah_pin, do_tone
		jnb	dit_pin,do_tone
		sb	P_BUTTON
		sleep
		clrb	BEEPFLAG
		jmp	straightkey
do_tone		setb	BEEPFLAG
		jmp	straightkey
		

;*************************************************************************
;*************************************************************************
; PAGE 2
;*************************************************************************
;*************************************************************************

		org	$400				;page 2 memory

;*************************************************************************
; Calibrate_Tone - Sound continuous tone while reading potentiometer and adjusting 
;             interrupt timing to tweak frequency, thus peaking piezo response.
;             New interrupt timing value saved in uninitialized register location
;             to retain and use upon next wake-up from sleep.  (Register contents
;             lost upon loss of power/battery removal.)

calibrate_tone	
		call	@dit			;one dit at start signifies version 1.1
		call	@letter_space
		call	@dit			;second dit signifies version 1.2
		call	@letter_space
		call	@dit			;third dit signifies version 1.3
		call	@letter_space

		mov	int_value,#-37		;starting point
		setb	BEEPFLAG
		clrb	LED

cal_2		call	get_press
		test	pb_timer
		jz	cal_5			;looooong press = exit calibration loop
		
		cjne	pb_result,#1,cal_3	;one press?
		inc	int_value		;yes, increase tone
		jmp	cal_2			;and loop

cal_3		dec	int_value		;no, 2 presses = decrease tone
		jmp	cal_2			;and loop

cal_5		setb	LED
		mov	dirty,#34
		sleep

;--------------------------------
; get_press - gets one or two pushbutton presses from the user.

get_press	snb	P_BUTTON		;await initial press
		jmp	get_press
get_press_x	call	@wait_10ms_F

		mov	pb_timer,#100
x1		jb	P_BUTTON,x2		;continue if PB released quickly
		call	@wait_10ms_F
		decsz	pb_timer
		jmp	x1
		ret				;looong PB hold, ret with Z=1

x2		mov	pb_timer,#100
x3		jnb	P_BUTTON,x4		;continue if PB pushed again
		call	@wait_10ms_F
		decsz	pb_timer
		jmp	x3
		mov	pb_result,#1		;return as one PB tap
		mov	pb_timer,#100		;and put timer back to non-zero
		ret

x4		sb	P_BUTTON		;await release of tap #2
		jmp	x4
		mov	pb_result,#2		;return as two PB taps
		ret

;*************************************************************************
; Morse Speed Set - Sound continuous stream of dits while adjusting the speed
;			variable. One tap of PB increases speed.  Two taps 
;			reduces speed. Long hold of PB exits mode.

set_speed	call	@dit
		snb	P_BUTTON
		jmp	set_speed
		

speed_2		call	get_press_x
		test	pb_timer
		jz	speed_5			;looooong press = exit calibration loop
		
		cjne	pb_result,#1,speed_3	;one press?
		dec	speed			;yes, increase Morse speed
		dec	speed
		dec	speed
		dec	speed
		jmp	set_speed		;and loop

speed_3		inc	speed			;no, 2 presses = decrease Morse speed
		inc	speed
		inc	speed
		inc	speed
		jmp	set_speed		;and loop

speed_5		sleep

;*************************************************************************
;*************************************************************************
; PAGE 3
;*************************************************************************
;*************************************************************************

		org	$600				;page 3 memory

;-----------
; Dit

dit		snb	USE_LED
		clrb	LED			;turn LED on
		setb	KEYLINE			;key transmitter

		snb	USE_PIEZO
		setb	BEEPFLAG

		mov	cntr_2,#10
dit_1		call	wait_10ms
		sb	dah_pin			;dah paddle tapped during dit sounding? (i.e, iambic operation)
		setb	dah_memory		;yes, set the dah memory
		decsz	cntr_2
		jmp	dit_1

		setb	LED			;turn off LED
		clrb	KEYLINE			;transmitter off
		clrb	BEEPFLAG
		call	wait_10ms

		mov	cntr_2,#10
dit_2		sb	dah_pin			;dah paddle tapped during dit sounding? (i.e, iambic operation)
		setb	dah_memory		;yes, set the dah memory
		call	wait_10ms
		decsz	cntr_2
		jmp	dit_2
		retp
;-----------
; Dah

dah		snb	USE_LED
		clrb	LED			;turn LED on
		setb	KEYLINE			;key transmitter

		snb	USE_PIEZO
		setb	BEEPFLAG

		mov	cntr_2,#30
dah_1		sb	dit_pin			;dit paddle tapped during dit sounding? (i.e, iambic operation)
		setb	dit_memory		;yes, set the dit memory
		call	wait_10ms
		decsz	cntr_2
		jmp	dah_1

		setb	LED			;turn LED off
		clrb	KEYLINE			;transmitter off
		clrb	BEEPFLAG
		call	wait_10ms

		mov	cntr_2,#10
dah_2		sb	dit_pin			;dit paddle tapped during dit sounding? (i.e, iambic operation)
		setb	dit_memory		;yes, set the dit memory
		call	wait_10ms
		decsz	cntr_2
		jmp	dah_2
		retp
;-----------
; char_space

char_space	mov	cntr_2,#15
c_sp_2		call	wait_10ms
		decsz	cntr_2
		jmp	c_sp_2
		retp
;-----------
; letter_space

letter_space	mov	cntr_2,#30
ltr_sp_2	call	wait_10ms
		decsz	cntr_2
		jmp	ltr_sp_2
		retp

;----------
; two_letter_space

two_letter_space call 	letter_space
		call	letter_space
		retp

;-----------
; wait 10 ms

wait_10ms	mov	int_cntr,speed
wait_10ms_a	test	int_cntr
		jnz	wait_10ms_a
		ret

;-----------
; wait 10 ms, called from Far (out_of_page)

wait_10ms_F	mov	int_cntr,speed
wait_10ms_aF	test	int_cntr
		jnz	wait_10ms_aF
		retp

;****************************************************************
; CALLSIGN USED FOR THIS BADGE

callsign	DW 'N2APB HI',0


END					;End of code


