;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;
;   COPYRIGHT (C) 1994 KEN STATON    ;
;         ALL RIGHTS RESERVED        ;
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;

;
;*************************************************************************
;
;
; PLAY MORSE CHARACTER 
; INPUT: CHARACTER INDEX IN ACC
; MODIFIES REGISTERS: DPTR, ACC, R5/R6/R7 in SEND
; MODIFIES Variables: DUR
;

; DECODE SIZE:
; IF BIT 7 CLR AND BIT 6 CLR, FIVE BITS (CHARACTER IS NUMBER OR PROSIGN)
; IF BIT 7 CLR AND BIT 6 SET, SIX BITS ( CHARACTER IS PUNCTUATION OR PROSIGN)
; IF BIT 7 SET (CHARACTER IS LETTER)
; THEN IF BIT 6 CLR AND BIT 5 CLR, ONE BIT
; THEN IF BIT 6 CLR AND BIT 5 SET, TWO BITS
; THEN IF BIT 6 SET AND BIT 5 CLR, THREE BITS
; THEN IF BIT 6 SET AND BIT 5 SET, FOUR BITS

; LEFT SHIFT UNTIL SPECIFIED NUMBER OF BITS LEFT
; THEN SHIFT EACH BIT INTO CARRY & SOUND DURATION BASED ON CARRY


;MORSE
	CLR	INT_EN		;INTS OFF

	MOV     DPTR,#CODE_TBL
	MOVC    A,@A+DPTR

CASE1   ; IF (B7=0) AND (B6=0) {3: RLC A + 5: CALL SEND}

	JB      ACC7,CASE2
	JB      ACC6,CASE2
	RLC     A
	RLC     A
	RLC     A
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   CHAR_DLY
	LJMP	M_EXIT

CASE2   ; IF (B7=0) AND (B6=1) {2: RLC A + 6: CALL SEND}
	
	JB      ACC7,CASE3
	JNB     ACC6,CASE3
	RLC     A
	RLC     A
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   CHAR_DLY
	LJMP	M_EXIT

CASE3   ; IF (B7=1) AND (B6=0) AND (B5=0) {7: RLC A + 1: CALL SEND}

	JNB     ACC7,CASE4
	JB      ACC5,CASE4
	JB      ACC6,CASE4
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	LCALL   SEND
	LCALL   CHAR_DLY
	LJMP	M_EXIT

CASE4   ; IF (B7=1) AND (B6=0) AND (B5=1) {6: RLC A + 2: CALL SEND}

	JNB     ACC7,CASE5
	JB      ACC6,CASE5
	JNB     ACC5,CASE5
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   CHAR_DLY
	LJMP	M_EXIT

CASE5   ; IF (B7=1) AND (B6=1) AND (B5=0) {5: RLC A + 3: CALL SEND}

	JNB     ACC7,CASE6
	JNB     ACC6,CASE6
	JB      ACC5,CASE6
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   CHAR_DLY
	LJMP	M_EXIT

CASE6   ; IF (B7=1) AND (B6=1) AND (B5=1) {4: RLC A + 4: CALL SEND}

	JNB     ACC7,CASE0
	JNB     ACC6,CASE0
	JNB     ACC5,CASE0
	RLC     A
	RLC     A
	RLC     A
	RLC     A
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   ELEM_DLY
	LCALL   SEND
	LCALL   CHAR_DLY
	LJMP	M_EXIT

CASE0   
	LJMP	M_EXIT		;ERROR HERE; SHOULD NEVER BE CASE 0

M_EXIT	SETB	INT_EN
	RET			; END MORSE

;*******************************************************************
;
; SEND SUBROUTINE
;
; INPUT IN A. RLC ONE BIT ON EXIT.
; ALSO MODIFIES R5, R6, R7
;

SEND    
	RLC     A       	; PUT BIT TO SEND IN CARRY!
	JNC	DIT_TONE	; SEND returns at DIT_TONE
	LJMP	DAH_TONE	;  or  returns at DAH_TONE

;
; DIT DELAY
; Unit element.
; See page 19-4 of 1994 ARRL HANDBOOK.
;
; PARIS word length = 50 units
; Includes: 
;   Space between words of 7 units.  
;   Space btw elements is 1 unit.
;   Space btw characters is 3 units.
;
; Speed (wpm) = (#dots/min)/25
; 2 * (#dots/min) = #units/min
; #dots/min = #units/(2min)
; Speed (wpm) = (#units/min)/50
; min = 60sec
; Speed (wpm) * 50 = (#units/min)
; Speed (wpm) * 50/60 = (#units/sec)
; Unit time = sec/#units
; Speed (wpm) * 50/60 = 1/(Unit time)
; Unit time = 1/(Speed (wpm) * 50/60)
; Unit time (mS) = (Unit time)*1000; 
; Unit time (mS) = 1000/(Speed (wpm) * 50/60)
;
; Unit time (mS) = 1200/(Speed (wpm))
; Speed (wpm) = 1200/(Unit time (mS))
;
; Min Speed (wpm) = 255mS Unit time => (1200/0.255) = 4.7 wpm.
; Max Speed (wpm) =  20mS Unit time => (1200/0.020) =  60 wpm.
;
; Could reduce both min & max by letting fspd & cspd durations
; represent 2mS increments instead of 1mS increments...this is
; necessary for c=2.4wpm for 5@16
;
; Farnsworth timing sends elements (dots,dashes) at a specified
; faster speed (f), then increases the delay between characters (c)
; to slow the effective rate (to s).  Specify both f and s speeds.
; The actual slowed rate must be calculated (c_speed).
;
; Using PARIS as the benchmark, 
; (4*3)+7=19 units are sent at c_speed,
; so 31 units are sent as f_speed.
;
; s = (f*31 + c*19)/50
; c = (s*50 - f*31)/19
; where f,s,c are in mS
;
;	     5@16		 13@18		      20@23
; f: 16   wpm =  75mS/dit | 18 wpm = 67mS/dit  | 23 wpm = 52mS/dit
; s:  5   wpm = 240mS/dit | 13 wpm = 92mS/dit  | 20 wpm = 60mS/dit
; c:  2.4 wpm = 509mS/dit |  9 wpm = 132mS/dit | 16 wpm = 73mS/dit
;
; 16wpm elements,  5wpm rate -> f=16, s=05, c= 2.4  (DEFAULT)
; 18wpm elements, 13wpm rate -> f=18, s=13, c= 9
; 23wpm elements, 20wpm rate -> f=23, s=20, c= 16
;
; CODEX word length = 60 units.
;

;*******************************************************************
;
; DIT_TONE
;

DIT_TONE

;
; SINE WAVE VERSION
;

	MOV	DUR,FSPD
DIT2	
	ACALL	SINE_CYC

	DJNZ	DUR,DIT2
	MOV	P1,#DC
	RET

;*******************************************************************
;
; DAH_TONE = 3 * DIT
;

DAH_TONE

	MOV	R7,#3		
DAH_LOOP	
	MOV	DUR,FSPD
DAH2
	ACALL	SINE_CYC

	DJNZ	DUR,DAH2

	DJNZ	R7,DAH_LOOP

	MOV	P1,#DC
	RET

;*******************************************************************
; 
; ELEMENT DELAY
;

ELEM_DLY
	MOV     R7,FSPD
	LCALL   DELAY
	RET

;*******************************************************************
;
; CHARACTER DELAY
;

CHAR_DLY
	MOV	R5,#3
CD_LOOP
	MOV     R7,CSPD
	LCALL   DELAY2
	DJNZ	R5,CD_LOOP
	RET

;*******************************************************************
;
; WORD DELAY
;

WORD_DLY
	MOV	R5,#7
WD_LOOP
	MOV     R7,CSPD
	LCALL   DELAY2
	DJNZ	R5,WD_LOOP
	RET

;*******************************************************************
;
; DELAY FOR R7 number of mS
; Modifies R6, R7
;

DELAY
D_LOOP
	MOV     R6,#ONE_MS      ; USE R6 FOR DELAY SCRATCH 
	DJNZ    R6,$
	DJNZ	R7,D_LOOP
	RET

;*******************************************************************
;
; DELAY FOR (R7 * 2) number of mS
; Used for CSPD Delays since they exceed 255 max mS count
; for R7 input to DELAY 
; Modifies R6, R7
;

DELAY2
D_LOOP2
	MOV     R6,#ONE_MS      ; USE R6 FOR DELAY SCRATCH 
	DJNZ    R6,$
	MOV     R6,#ONE_MS      ; USE R6 FOR DELAY SCRATCH 
	DJNZ    R6,$
	DJNZ	R7,D_LOOP2
	RET

;*******************************************************************
;
; SINE_CYC
;
; 500 Hz single cycle.  1/8 mS per step.  500 Hz -> 2 mS period.
; 2 mS / (1/8 mS / step) = 16 steps
;

SINE_CYC
	MOV	P1,#S0
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S1
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S2
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S3
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S4
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S5
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S6
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S7
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S8
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S9
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S10
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S11
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S12
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S13
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S14
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	MOV	P1,#S15
	MOV	R6,#EIGTH_MS
	DJNZ	R6,$

	RET				; END SINE_CYC

	NOP
	
