Now, here's my program-in-progress that reads in a text string
(contained in the retw sequence "msg") and bangs it out
in brisk Morse code. I've entered "PARALLAX" as a sample
message, but you can change it. Make sure your new message
is all CAPS and contains no unusual characters beyond numbers, 
letters and common punctuation. Put the message length into 
the variable str_cnt. 

The program assumes that your clock speed is 4 MHz, and that
a piezo speaker is connected between ra.0 and ra.1. (It'll
work at the downloader's default 8 MHz, but <<boy>> is it 
fast!)

There are two interesting features to the program. The first
is the encoding method for Morse. Characters in Morse consist
of one to six "elements," which are short or long beeps that 
you've heard of as "dots" and "dashes." I decided on a data
structure that uses the upper five bits of a byte to represent
the pattern of dots and dashes, and the lower to represent the
number. For instance, the letter F is dot-dot-dash-dot (written
.._.). The byte representing Morse F is: 0010X100, where X is a 
don't-care bit. For consistency, I've encoded all the don't-
cares as 0s. So if you look below at the retw table called 
Code_char, you'll see that the sixth entry is 36, the decimal
value of 00100100. 

The program section starting at ":call_code"	reads these Morse 
bytes and converts them into handsomely sent code. It first
reads the lower three bits and stores the number in a counter. 
For 00100100, it reads 4. Then it enters a loop in which it 
rotates the Morse byte to the left, placing the high bit into
carry. If carry is one, it calls a routine to send a dash. 
If carry is zero, it calls dot. It goes through this loop 
the number of times prescribed by the counter, in this case 4. 

There's always a fly in the ointment to keep your code from
being perfectly clean, as this case is no exception. The fly
is: what to do about six-element Morse characters? With only 
five bits to represent elements, that could be a real problem. 
However, the three lower length bits represent some unused
capacity. They can represent numbers 0 through 7, but I only 
need 1 through 6. So here's how the six-element exceptions are
handled: a six-el character ending in a dot has its first five
bits handled as before, and is assigned a count of six. a six-el
character ending in a dash also has the usual first five bits, 
but is assigned a count of seven. Four lines of code take care 
of this business. 

The other routine of interest in this thicket of GOTOs is "Beep."
It's at the very end. This routine accepts a frequency (freq) and
duration (duratn) as arguments, and produces a tone of the 
corresponding nature. The neat thing is that the duration is 
unaffected by the frequency, and it does <<not>> use the rtcc. 
The frequency is variable over an 8-bit range, while the duration
is 16-bit. (I only use the upper 8 bits in this case, since I'm
interested in relatively long beeps.)

One other useful feature is that a frequency of 0 results in a 
silent delay the same length that the corresponding beep would 
have been. This is critical to the Morse sending routines, because
the dot is the basic unit of timing. Silences between elements
must be one dot length long; between cahracters, three dots; and 
between words, seven dots. 

Beep accomplishes its work by putting either all ones or all 
zeros into a variable that gets XOR'd into port ra to strum the
speaker. The variable is loaded with ones only when a copy of the 
freq variable is decremented to zero in multiple passes through 
a timing loop. The speaker gets strummed, freq is reloaded into the 
copy, ane the loop starts again. The loop also decrements the 16-bit
duration with each trip. When the duration runs out, ret is executed. 

I haven't had a chance to make sure that all decision paths through 
Beep's main loop are the same length. I need to do so just to be tidy.
(OK, and to eliminate some possible audio distortion. Maybe.) 
However, you'll find that it works as advertised. It probably uses 
more RAM than it should, but I haven't gone a-cuttin' yet. I am 
getting better about marking my recyclable variables in the 
declaration; anything marked temporary counter is available for 
other routines to use, as long as they don't expect the contents
to be intact after a call to Beep. 

To run the program, just lop off this note and assemble the rest of 
this file. Have fun!

Scott Edwards




; ******************************************************************
spkr	=	ra
	org	8
freq	ds	1
duratn	ds	1
f_temp	ds	1	; temporary counter
d_hi	ds	1	; temporary counter
d_lo	ds	1	; temporary counter
tgl	ds	1	; temporary counter
t_pat	ds	1	; temporary variable
m_char	ds	1	; Morse character
a_char	ds	1	; ASCII character
index1	ds	1	; counter
index2	ds	1	; counter
str_cnt	ds	1	; no. of characters in string

; Device data and reset vector
	device	pic16c55,xt_osc,wdt_off,protect_off
	reset	start
	org	0

start	mov	!ra, #0
	mov	ra, #010101010b
	mov	str_cnt, #8	; <<-- Put the message length here
	mov	index2, #0
:get_char	mov	w, index2
	call	msg
	mov	a_char, w
	cjbe	a_char, #33, :space
	cje	a_char, #36, :space
	cje	a_char, #37, :space
	cje	a_char, #42, :space
	cje	a_char, #60, :space
	cje	a_char, #62, :space
	cje	a_char, #64, :space
	cjae	a_char, #123, :space
	cjbe	a_char, #64, :adjust
	
:Call_code	call	Code_char
	mov	m_char, w
	mov	index1, m_char
	and	index1, #00000111b
	csne	index1, #6		; Handle six-element
	clrb	m_char.2		; cases. If index1 = 6
	csne	index1, #7		; then character ends in dot.
	dec	index1		; If it's 7, end is dash. 
:loop	rl	m_char
	jc	:send_dah
	jmp	:send_dit
:reenter	djnz	 index1, :loop
	call	Char_sp
:fm_space	inc	 index2
	djnz	 str_cnt, :get_char

:endless	goto	:endless

:send_dah	call	Dah
	goto	:reenter
:send_dit	call	Dit
	goto	:reenter
	inc	index2
	djnz	str_cnt, :get_char
:space	call	Word_sp
	goto	:fm_space
:adjust	add	a_char, #57
	goto	:Call_code

Dit	mov	duratn, #15
	mov	freq, #42
	call	Beep
	mov	freq, #0
	call	Beep
	ret

Dah	mov	duratn, #45
	mov	freq, #42
	call	Beep
	mov	duratn, #15
	mov	freq, #0
	call	Beep
	ret

Char_sp	mov	duratn, #45
	mov	freq, #0
	call	Beep
	ret	

Word_sp mov	duratn, #105
	mov	freq, #0
	call	Beep
	ret	

msg	jmp	pc+W
	retw	'P', 'A', 'R', 'A', 'L', 'L', 'A', 'X'

Code_char	sub	a_char, #65
	mov	w, a_char
	jmp	pc+W
	retw	66, 132, 164, 131, 1, 36, 195, 4
	retw	2, 116, 163, 68, 194, 130, 227, 100	
	retw	212, 67, 3, 129, 35, 20, 99, 148
	retw	180, 196, 78, 23, 23, 23, 207, 126
	retw	181, 183, 23, 85, 207, 135, 87, 149
	retw	253, 125, 61, 29, 13, 5, 133, 197
	retw	229, 245, 230, 174, 23, 141, 23, 54, 23

Beep	mov	t_pat, #255
	mov	f_temp, freq
	jnz	:cont
	mov	t_pat, #0
:cont	mov	d_hi, duratn
	clr	d_lo
	mov	tgl, t_pat
:main	xor	spkr, tgl
:delay	nop
	nop
	djnz	f_temp, :noRoll_1
	mov	f_temp, freq
	mov	tgl, t_pat
:dur_lo	sub	d_lo, #1
	jc	:noRoll_2
	sub	d_hi, #1
	sc
	ret
:loop	jmp	:main

:noRoll_1	clr	tgl
	jmp	:dur_lo

:noRoll_2	nop
	nop
	jmp	:main

