; Morse Code ID'er for Repeater Controller Project
; 1998 EDGE - Written by Chris Cole
; Released under the GPL - you are free to use and/or modify this code in any way.
; OSC = 10 MHz --> 1 cyc = 0.4 uS
; pins: 1=PTT, 4=10kohm-->+5V, 5=gnd, 14=+5V, 17=COS, 18=morse tone out

	TITLE	"REPEATER CONTROLLER"
	LIST	P=16F84

#include "C:\progra~1\mplab\p16f84.inc"

	__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON

;----------------------------------------------------------------------------------------------------
#define	cosPin	PORTA,0	; COS in at RA0  (PIN 17)
#define	tonePin	PORTA,1	; Tone out at RA1  (PIN 18)
#define	pttPin	PORTA,2	; PTT out at RA2  (PIN 1)
;----------------------------------------------------------------------------------------------------
RAMbase	EQU	0x0c			;
;----------------------------------------------------------------------------------------------------
; DNOP - Double NOP. Delay of 2 cycles, takes only one instruction
DNOP		MACRO				;
		LOCAL	Label			;
Label		GOTO	Label+1		;
		ENDM				;
;----------------------------------------------------------------------------------------------------
; Delay3W - Delay 3 * W cycles, three instructions
Delay3W	MACRO				;
		LOCAL	Label			;
		MOVWF	delay			;
Label		DECFSZ delay		;
		GOTO	Label			;
		ENDM				;
;----------------------------------------------------------------------------------------------------
CBLOCK	RAMbase			;
		letterIndex			;
		letter			;
		morseIndex			; used for table index as well as bit index in the coded byte
		morse				;
		toneDuration		;
		delay				;
		delayCount1			;
		delayCount2			;
		SecCount			;
		repeaterState		; status byte:  bit0=traffic sensed (others unused so far...)
						;               bit1=had 1 full 10 minute silence period
ENDC						;
;----------------------------------------------------------------------------------------------------
	ORG	0				;
	GOTO	Main				;
; make sure tables do not go over 0x100 byte boundaries!!! 
;----------------------------------------------------------------------------------------------------
LetterTable					;
	addwf	PCL				;
	retlw	'K'				; MUST be capital letters.  Spaces are acceptable.
	retlw	'B'				;
	retlw	'8'				;
	retlw	'K'				;
	retlw	'W'				;
	retlw	'M'				;
	retlw	'/'				;
	retlw	'R'				;
	retlw	0				; null terminator - signifies end of string.
;----------------------------------------------------------------------------------------------------
MorseTable					;
	addwf	PCL				;
	retlw	0x6d	; / 0110 1101 05	;
	retlw	0x05	; 0 0000 0101 05	;
	retlw	0x85	; 1 1000 0101 85	;
	retlw	0xc5	; 2 1100 0101 c5	;
	retlw	0xe5	; 3 1110 0101 e5	;
	retlw	0xf5	; 4 1111 0101 f5	;
	retlw	0xfd	; 5 1111 1101 fd	;
	retlw	0x7d	; 6 0111 1101 7d	;
	retlw	0x3d	; 7 0011 1101 3d	;
	retlw	0x1d	; 8 0001 1101 1d	;
	retlw	0x0d	; 9 0000 1101 0d	;
	retlw	0x00	; - 0000 0000 00	; can remove these fillers if alpha-check can 'fix' index.
	retlw	0x00	; - 0000 0000 00	;
	retlw	0x00	; - 0000 0000 00	;
	retlw	0x00	; - 0000 0000 00	;
	retlw	0x00	; - 0000 0000 00	;
	retlw	0x00	; - 0000 0000 00	;
	retlw	0x00	; - 0000 0000 00	;
	retlw	0x82	; A 1000 0010 82	;
	retlw	0x74	; B 0111 0100 74	;
	retlw	0x54	; C 0101 0100 54	;
	retlw	0x63	; D 0110 0011 63	;
	retlw	0x81	; E 1000 0001 81	;
	retlw	0xe4	; F 1101 0100 e4	;
	retlw	0x23	; G 0010 0011 23	;
	retlw	0xf4	; H 1111 0100 f4	;
	retlw	0xc2	; I 1100 0010 c2	;
	retlw	0x84	; J 1000 0100 84	;
	retlw	0x43	; K 0100 0011 43	;
	retlw	0xb4	; L 1011 0100 b4	;
	retlw	0x02	; M 0000 0010 02	;
	retlw	0x42	; N 0100 0010 42	;
	retlw	0x03	; O 0000 0011 03	;
	retlw	0x94	; P 1001 0100 94	;
	retlw	0x24	; Q 0010 0100 24	;
	retlw	0xa3	; R 1010 0011 a3	;
	retlw	0xe3	; S 1110 0011 e3	;
	retlw	0x01	; T 0000 0001 01	;
	retlw	0xc3	; U 1100 0011 c3	;
	retlw	0xe4	; V 1110 0100 e4	;
	retlw	0x83	; W 1000 0011 83	;
	retlw	0x64	; X 0110 0100 64	;
	retlw	0x44	; Y 0100 0100 44	;
	retlw	0x34	; Z 0011 0100 34	;
;----------------------------------------------------------------------------------------------------
Main						;
	bsf	STATUS, RP0			;
	movlw	b'11001'			; RA0 is input, RA1 & RA2 are outputs.  The rest stay as inputs.
	movwf	TRISA				;
	movlw	b'11111111'			; RB all as inputs.
	movwf	TRISB				;
	bcf	STATUS, RP0			;
Init						; set initial pinstates and internal counters, etc.
	clrw					; w=0
	bcf	pttPin			; make sure pttPin is initially low.
	bcf	repeaterState,1		; clear full silenced period bit
DoID						;
	bcf	repeaterState,0		; clear traffic sense bit
	bsf	pttPin			; set the PTT pin high for the CWID transmission
	call	Idle2Sec			; give transmit enough time to come up before we ID
	clrf	letterIndex			; using i as the index
LetterLoop					; table read - obtain the text to send out as morse code
	movf	letterIndex,w		; get the index into the table
	incf	letterIndex			; increment the table index
	call	LetterTable			; get the next letter
	movwf	letter			; store the returned value
	movf	letter			;
	btfsc	STATUS,Z			; skip if zero is clear
	  goto	Idle10Min		; if not, then we're done here
	addlw	0-0x2F				; subtract 2F from ascii
value
	movwf	morseIndex			; store the returned value
	xorlw	0xf0				; was it a space character?
	btfsc	STATUS,C			; skip if carry is clear
	  goto	ContLetterLoop	; if not, then we're done here
	call	LetterDelay			;
	call	LetterDelay			; spacing between words
	goto	LetterLoop			;
ContLetterLoop				;
	movf	morseIndex,w		; put the morse code index into w register
	call	MorseTable			; get the morse code for this letter
	movwf	morse				; store the returned value
	call	DoMorse			; turn the code into dit's and dah's
	goto	LetterLoop			;
DoMorse					;
	movf	morse,w			; copy the morse code byte (has code and length info)
	andlw	0x07				; strip off the code info, leaving the length
	movwf	morseIndex			; store the length
MorseLoop					;
	rlf	morse				; rotates MSB of code into carry bit
	movlw	0x40				; set tone duration 
	btfss	STATUS,C			; skip if carry is set
	  addlw	0x80			; make tone duration longer for a 'dah'
	movwf	toneDuration		;
	call	DoTone			;
	decf	morseIndex			;
	btfss	STATUS,Z			;
	  goto	MorseLoop		;
	call	LetterDelay			; normal pause between letters
	return				;
DoTone					; uses 'toneDuration' variable
	movwf	toneDuration		; [1] <-- denotes clock cycles (for timing!)
ToneDelayLoop1				;
	movlw	0xa0				; [1]				; [*64 / *192]
	movwf	delayCount1			; [1]				;
	bsf	tonePin			; [1]				;
ToneDelayLoop2				;				;
	movlw	0x02				; [1]		; [*160]	;
	Delay3W				; [3*2]	;		;
	decfsz	delayCount1		; [1]		;		;
	  goto	ToneDelayLoop2	; [2]		;		;
	movlw	0xa0				; [1]				;
	movwf	delayCount1			; [1]				;
	bcf	tonePin			; [1]				;
ToneDelayLoop3				;				;
	movlw	0x02				; [1]		; [*160]	;
	Delay3W				; [3*2]	;		;
	decfsz	delayCount1		; [1]		;		;
	  goto	ToneDelayLoop3	; [2]		;		;
	decfsz	toneDuration	; [1]				;
	  goto	ToneDelayLoop1	; [2]				;
	movlw	0xff				; [1] now for the delay after each 'dit' or 'dah'
	movwf	delayCount1			; [1]
	bcf	tonePin			; [1]
ToneDelayLoop4				;
	movlw	0xe0				; [1]		; [*255]
	Delay3W				; [3*224]	;
	decfsz	delayCount1		; [1]		;
	  goto	ToneDelayLoop4	; [2]		;
	return				; [2]
LetterDelay					;
	movlw	0x4				; [1]
	movwf	delayCount1			; [1]
LetterDelayLoop1				;				; [*4]
	movlw	0xff				; [1]				;
	movwf	delayCount2			; [1]				;
LetterDelayLoop2				;				;
	movlw	0xff				; [1]		; [*255]	;
	Delay3W				; [3*255]	;		;
	decfsz	delayCount2		; [1]		;		;
	  goto	LetterDelayLoop2	; [2]		;		;
	decfsz	delayCount1		; [1]				;
	  goto	LetterDelayLoop1	; [2]				;
	return				; [2]
;----------------------------------------------------------------------------------------------------
Idle10Min					; Wait for 10 minutes (tune timing for this later)
	bcf	pttPin			; Bring the PTT pin low - we're done sending the ID
	movlw	0xfe				;
	movwf	SecCount			;
Idle10MinLoop				;
	call	Idle2Sec			; calls 2.35 sec delay 255 times (that's 10 minutes!)
	decfsz	SecCount		;
	  goto	Idle10MinLoop	;
	btfsc	repeaterState,0		; test traffic bit
	  goto	DoID			; ID right away (may be in the middle of a transmission)
	btfsc	repeaterState,1		; test silenced period bit
	  goto	WaitHigh		; No need to ID again until the next transmission.
	bsf	repeaterState,1		; set full silenced period bit
	goto	DoID				;
;----------------------------------------------------------------------------------------------------
WaitHigh					;
	btfss	cosPin			; wait for COS to go high
	  goto	WaitHigh		;
WaitLow					;
	btfsc	cosPin			; wait for cosPin to go low again
	  goto	WaitLow		;
	bcf	repeaterState,1		; clear full silenced period bit
	goto	DoID				;
;----------------------------------------------------------------------------------------------------
Idle2Sec					; actually waits for 2.35 seconds
	movlw	0x1e				; [1]
	movwf	delayCount1			; [1]
	btfsc	cosPin			; [1]		; Look at COS
	  bsf	repeaterState,0		; [1]		; If traffic sensed, set the corresp. status bit
Idle2SecDelayLoop1			;
	movlw	0xff				; [1]				; [*30]
	movwf	delayCount2			; [1]				;
Idle2SecDelayLoop2			;				;
	movlw	0xff				; [1]		; [*255]	;
	Delay3W				; [3*255]	;		;
	decfsz	delayCount2		; [1]		;		;
	  goto	Idle2SecDelayLoop2; [2]		;		;
	decfsz	delayCount1		; [1]				;
	  goto	Idle2SecDelayLoop1; [2]				;
	return				;
;----------------------------------------------------------------------------------------------------
	END					;

