; SOUND port, pin, frequency, duration
; Generates squarewave tones (notes) of the specified frequency and
; duration. This demonstration program shows how to use lookup tables to
; play tunes or effects from data stored in program memory.

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

	org	8
pin	ds	1	; Pin number to use as output (0-7).
freq	ds	1	; Frequency of note.
duratn	ds	1	; Duration of note.
f_copy	ds	1	; Copy of freq variable.
temp	ds	1	; Variable to stretch duratn to 16 bits.
index	ds	1	; Index to tables of notes, lengths.

	org	0

; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b).
Pinz	jmp	pc+w
	retw	1,2,4,8,16,32,64,128

; Table of notes for the demonstration tune "Charge."
Notes	jmp	pc+w
	retw	166,189,204,212,204,212

; Table of note durations for the demonstration tune "Charge."
Lengths jmp	pc+w
	retw	52,52,52,82,45,190

start	mov	!ra, #0 ; Output for speaker.
	clr	temp	; Clear LSB of duratn counter.
	clr	index	; Clear the note counter.
:loop	mov	w,index ; Look up note in table.
	call	Notes
	mov	freq,w	; Store note in freq.
	mov	w,index ; Look up length in table.
	call	Lengths
	mov	duratn,w	; Store in duratn.
	mov	pin,#3	; Select pin 3 of port
	mov	w,#0	; RA for speaker output
	call	Sound	; Play the sound.
	inc	index	; Advance to next sound in tables.
	cjbe	index,#5,:loop	; IF index <= 5, play note
	jmp	$	; ELSE end.

; Upon entry, the desired speaker pin must already be set up as an output.
; The w register contains a number representing the output port (0-2) for
; RA through RC. Variable "pin" contains the pin number (0-7). The variables
; freq and duratn are the frequency and duration of the note.

; Upon exit, duratn will be cleared to 0, but freq will be intact. If you require the
; value of duratn for some other purpose, copy it before calling Sound. The
; variable pin and the file-select register (fsr) will also have be altered.

Sound	not	duratn	; Convert to two's complement.
	inc	duratn
	mov	fsr,w	; Point to the port number.
	add	fsr,#RA ; Add offset for port RA.
	mov	w,pin	; Now look up pin number in
	call	Pinz	; table and get bit mask.
	mov	pin,w	; Put the mask into pin.
	mov	f_copy,freq	; Use copy so freq value is saved.
	snz	; If freq is 0, clear pin to
	clr	pin	; produce a silent delay.
:loop	mov	w,pin	; f_copy=f_copy+1: IF f_copy<=255
	incsz	f_copy	; THEN w=pin ELSE w=0
	clr	w	; (makes a silent delay if freq = 0)
	XOR	indirect,w	; indirect=indirect XOR w
	mov	w,f_copy	; If f_copy has rolled over to 0,
	snz	; reload it with the value of freq.
	mov	w,freq
	mov	f_copy,w
	inc	temp	; Increment LSB of duratn counter.
	snz	; Overflow? Yes: Increment duratn and
	incsz	duratn	; if it overflows, return. Else loop.
	jmp	:loop
	ret

	end