;   filename: RVB2.ASM
;
;
;	This code is an optimized version of the reverberation algorithm
;	found in file RVB1.ASM.  This program makes use of the parallel
;	move functionality of the 56001.  While this tends to make the
;	code less readible, it does increase its speed.  This version
;	is intended to run on the DSP56000ADSx (Application Development
;	System) board rev #2, with memory expansion.  The memory should 
;	be configured figured for 8K p-memory, 16K x-memory, 8K y-memory.
;	
;	Further gains in algorithm speed could be realized on boards not
;	constrained by the ADS memory limitations.  For example, due to
;	the need for 4 comb filters' worth of storage (each using about 
;	3500 samples - which takes 4096 samples each after using the DSM
;	statement) to fit into the 16K available for the x memory, we
;	must start at address $0000.  Thus, we are forced to use all the
;	56000's internal memory for sample storage.  Without being forced
;	to start sample storage at $0000, the 56000's internal memory could
;	be used for coefficient storage, allowing better use of parallel 
;	x and y moves.
;
;				Motorola DSP Group
;				Austin, Texas
;
;**************************************************************************
; This program was originally available on the Motorola DSP bulletin board
; and is provided under a DISCLAIMER OF WARRANTY available from Motorola
; DSP Operation, 6501 William Cannon Dr. W Austin, Texas  78735-8598.
;**************************************************************************
;--------------------------------------------------------------------------
;	This reverberation program is a variation of the reverberation
;	system and structures as described by James Moorer's article entitled
;	 'About this Reverberation Business', Computer Music Journal,
;	 3(2):13-28, 1979
;
;	Structure is:
;							.----------.
;			     .------------.   .-----.	| All Pass |
;			  +->| Comb Filter|-->| SUM |-->| Reverb   |
;   Note: All Comb	  |  |   #1       |   '-----'	|	   |
;	  Filters	  |  '------------'   ^  ^  ^	'----------'
;	  Have a 1st	  |  .------------.   |  |  |	     |
;	  Order IIR 	  |->| Comb Filter|---+  |  |	 .---V---.
;	  LPF in their	  |  |   #2       |      |  |	 | align |
;	  feedback	  |  '------------'      |  |	 | delay |
;	  loop		  |  .------------.      |  |	 '---|---'
;			  |->| Comb Filter|------+  |	     |
;			  |  |   #3       |         |	   -----   reverb	  
;			  |  '------------'         |	    \./     gain
;			  |  .------------.         |	     |
;			  |->| Comb Filter|---------+	     |
;			  |  |   #4       |	     	     |
;			  |  '------------'		     |
;			  |				     V
;	  .------------.  |        FIR gain          .----------.
;   	  |   Early    |  |         |\               |          |
;input -->| Reflection |--+---------|  >------------>|  summer  |--- output
;      |  |    FIR     |	    |/               |          |
;      |  '------------'                             '----------'      
;      |		 		                  ^
;      |		 		                  |
;      |		             dry gain             |
;      |			      |\                  |
;      +------------------------------|  >----------------+
;				      |/
;	
;.............................................................................
;  COMB FILTER SUB STRUCTURE:
;				 .-------.
;      comb i        .-----.     | long  | 		      comb i
;      input  ------>| sum |---->| delay |-------+--------->  output
;		     '-----'     '-------'	 |
;			^		         |
;			|		  	 V
;			|       /| 	      .-----.
;			+-----<   ------------| sum |<--------+
;				\|	      '-----'	      |
;			     fdbck i		 |	     / \  lpf i gain
;			      gain		 V	   /_____\
;			      gain           .----------.     |
;					     | 1 sample |     |
;					     |	delay	|-----+
;					     '----------'
;.............................................................................
;   UNIT (ALL PASS) REVERBERATOR STRUCTURE:
;   based on Schroeder as outlined in Griesinger: 'Practical Processors and
;   Programs for Digital Reverberation', Audio in Digital Times, 7th AES
;   conference, Toronto, Ontario, 1989
;   (the structure outlined in Moorer is a variation of this)
;
;   			        -g
;			     |\
;		 +---------->|  >--------------------------+
;		 |	     |/				   |
;		 |					   |
;		 |					   V
;    unit	 |    .-----.   .--------.       |\     .-----.	      unit
;    input ------+--->| sum |-->| delay  |--+--->|  >-->| sum |-----> output
;		      '-----'   '--------'  |    |/     '-----'
;			 ^		    |	1-g**2
;			 |     g	    | 
;			 |       /|	    |
;			 +-----<  |---------+
;			         \|
;
;...........................................................................
;
;	one multi-tap fir structure - to handle early reflections
;
;	followed by 4 parallel comb (iir) filters (each comb having 
;	a first order LPF in its feedback loop
;	
;	followed by an 'allpass' reverberator whose output is then 
;	delayed so that its first output follows after the last "early
;	reflection" output 
;
;__________________________________________________________________________
;
	opt cex
	page 132

;--------------------------------------------------constant declarations
adc	equ	$ffef		; ADC address
dac	equ	$ffef		; DAC address

ntap	equ	7		; number of taps 
ntapp10 equ	ntap+10		; # taps + 10 for other variables
tapmod	equ	ntapp10-1	; number of taps minus 1	 
dlymx 	equ	4000		; length of delay line in samples

cmbdly1 equ	2205		; COMB FILTER CONSTANTS
cmbdly2 equ	2690		; delay values (in samples)
cmbdly3 equ	3175
cmbdly4 equ	3440

cmbmod1 equ	  3490 	        ; modulo for comb #1 delay line   THESE ARE
cmbmod2 equ	  3490 		;   "         "   #2   "   "	  ALSO THE 
cmbmod3	equ	  3490 		;   "         "   #3  "    "	  MAX DELAYS
cmbmod4	equ	  3490 		;   "         "   #4  "    "	  ALLOWED FOR
				;				  CHOSEN DSM
				;				  VALUE

untrvbdly	equ	265		    ; delay	UNIT (or ALLPASS)
unt_g	     	equ	0.7		    ; gains (g)    REVERBERATOR
neg_g		equ	-unt_g		    ;	    (-g)
one_m_g2	equ	(1-unt_g*unt_g)	    ;	"   (1-g**2)


cmb_g		equ	0.86		; (OVERALL) COMB FILTER FEEDBACK GAIN 
					; controls reverb decay time: smaller
					; values give quicker decay, larger
					; values yield slower decay

lpf1		equ	0.408		; lpf filter coefficient in the 
lpf2		equ	0.448		; feedback loop of the combs,
lpf3		equ	0.476		; these LPF's simulate the high freq
lpf4		equ	0.496		; attentuation in real acoustic reverb

fdbck1		equ	cmb_g*(1-lpf1)	; actual comb feedback gain
fdbck2		equ	cmb_g*(1-lpf2)	; is determined by the lpf filter
fdbck3		equ	cmb_g*(1-lpf3)	; coefficient and the overall feed-
fdbck4		equ	cmb_g*(1-lpf4)	; back gain to insure stability


aligndly		equ	1305   	; alignment delay (see diagram above)
alignmod		equ	1390	;     "     mod

	
reverbg		equ	0.35		; reverberation output gain value
firg		equ	0.15		; early rflctn FIR output gain value
dryg		equ	0.9999-(reverbg+firg) ; dry signal gain is rest  
					; CHOOSE THE MIX YOU LIKE


	org x:0
chead1	dsm 3500		; allocates 3500 data spaces for use as
chead2	dsm 3500		; comb filter tap delay lines, CHEAD1 refers
chead3	dsm 3500		; to the Comb filter HEAD (beginning) for 
chead4	dsm 3500		; filter #1  

	org x:$0f00
ofst_bf	dsm	ntapp10		; this reserves "ntap" contiguous addresses
	org x:ofst_bf		; starting at the closest appropriate modulo
ofst0	dc	1		; x address space.  Then starts filling the 
ofst1	dc	877		; offset buffer at this location with   
ofst2	dc	1561		; the right delay values for the 
ofst3	dc	1716		; EARLY REFLECTION FIR
ofst4	dc	1826
ofst5	dc	3083	  ; IMPORTANT - to get no delay for the first 
ofst6   dc	3510	  ; early reflection, use a "1".  This is due the use
			  ; of an instruction code pre-decrement, a "0" value 
			  ; will cause a maximum delay equal to the tap delay
			  ; line length

untdly	dc	untrvbdly	; define the UNIT REVERBERATOR delay
untmod	dc	untrvbdly-1	; modulo for    "         "

algndly	dc	aligndly
algnmod	dc	alignmod

	org y:$0f00
gain_bf dsm ntapp10
	org y:gain_bf		; starting at y address zero this reserves
				; "ntap" contiguous addresses then starts
				; filling these addresses with the desired
gain0	dc	0.213		; tap "gain" values for EARLY REFLECTION FIR
gain1	dc	0.217		; recommended by moorer
gain2	dc	0.174		;
gain3	dc	0.135		; it is recommended to keep many early 
gain4	dc	0.153		; reflection returns, as these are fed into
gain5	dc	0.157		; comb filters, than can contribute greatly
gain6	dc	0.051		; to the later impulse response density
c1g2	dc	fdbck1
c2g2	dc	fdbck2
c3g2	dc	fdbck3
c4g2	dc	fdbck4
c1out	ds	1
untg2	dc	neg_g
untg3	dc	one_m_g2
untg1	dc	unt_g
firgain	dc	firg
drygain	dc	dryg
rvbgain	dc	reverbg

	org y:0
c1g1	dc	lpf1		; comb #1 g1 (LPF gain)
c2g1	dc	lpf2		;      #2      
c3g1	dc	lpf3		;      #3
c4g1	dc	lpf4 		;      #4


sample	 ds 1			; input sample storage address
firout   ds 1			; early ref FIR OUTput storage address

				; COMB filter    output storage address
c2out  ds 1			;  "      "   #2   "       "       "

lpfst1	ds 1			;  "	  "   #1 Low Pass Filter state
lpfst2	ds 1			;  "	  "   #2  "    "    "      "
lpfst3  ds 1			;  "	  "   #3  "    "    "      "
lpfst4	ds 1			;  "	  "   #4  "    "    "      "

				; allocates modulo memory for the unit
udlyln	dsm	300		; (allpass) reverberator delay line 

algndlyln dsm	1400		; allocates modulo memory for alignment delay  
				; line

dlyline	dsm dlymx		; allocates mod memory for the FIR delay line

;--------------------------------------------------------------------------
     org     p:$40		; program start address

; Set up ADS board in case of force break instead of force reset
       movep #0,x:$FFFE         ;set bcr to zero
       movec #0,sp              ;init stack pointer
       movec #0,sr              ;clear loop flag

; Set up the SSI for operation with the DSP56ADC16EVB
; The following code sets port C to function as SCI/SSI
       move #$0,a0              ;zero PCC to cycle it
       movep a0,x:$FFE1
       move #$0001ff,a0
       movep a0,x:$FFE1         ;write PCC

; The following code sets the SSI CRA and CRB control registers for external
; continuous clock, synchronous, normal mode.
       move #$004000,a0         ;CRA pattern for word length=16 bits
       movep a0,x:$FFEC
       move #$003200,a0    ;CRB pattern for continous ck,sych,normal mode
       movep a0,x:$FFED    ;word long frame sync: FSL=0;ext ck/fs 

; -------------------------------------------------------------------------
				; initialize registers MO,RO, etc
	move #dlymx-1,m0	; tap line modulus
	move #dlyline,r0 	; start of delay line
	move #0,n0	 	; 

	move #tapmod,m1		; tap gain line modulo
	move #gain_bf,r1 	; tap gain & tap delay pointer

	move #cmbmod1,m2	; initialize the modulo registers
	move #cmbmod2,m3	; for comb filter buffers 1 thru 5
	move #cmbmod3,m4
	move #cmbmod4,m5

	move #cmbdly1,n2	; initialize the offset register
	move #cmbdly2,n3	; for Comb filters 1 thru 5
	move #cmbdly3,n4
	move #cmbdly4,n5

	move #chead1,r2		; initialize the pointer values for
	move #chead2,r3		; Comb filters 1 thru 5
	move #chead3,r4
	move #chead4,r5

	move x:algnmod,m6	; initialize alignment delay line
	move x:algndly,n6	; offset, modulo, and pointer registers
	move #algndlyln,r6

	move x:untmod,m7	; initialize unit reverberator (allpass)
	move x:untdly,n7	; offset, modulo, and pointer registers
	move #udlyln,r7

;---------------------------------------------------------------------------
; The following code polls the RDF flag in the SSI-SR and waits for RDF=1
; and then reads the RX register to retrieve the data from the A/D converter.
; Sample rate is controlled by DSP56ADC16 board.   

poll    jclr #7,x:$FFEE,poll     ;loop until RDF bit = 1
        movep x:adc,a            ;get A/D converter data
;---------------------------------------------------------------------------
	asr a
	asr a			; obtain data sample 
				; and shift right for headroom
	move a,y:sample		; keep dry sample

;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
; FIR for early reflections
	move a,y:(r0)-		; push sample into delay line
	clr a	x:(r1),n0	; clr A and load offset into N0
	do #ntap,fir	 	; loop over the (ntaps-1),exclude fb tap now
	   move y:(r1)+,y1	;  "   "   "   gain   "      "  Y
	   move y:(r0+n0),x1	; get delayed sample
	   mac x1,y1,a	x:(r1),n0 ; MAC gain and sample & update offset
fir  	move a,y:firout		; save FIR OUTput to y memory
;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
; COMB 1
	move x:(r2+n2),b	; get delayed sample & put in b
	move b,y:c1out		; move output to y memory space

	move y:c1g1,x1			; get LPF filter coeff
	move y:lpfst1,y1		; get LPF filter state
	mac x1,y1,b	y:(r1)+,x1	; compute LPF output
	move b,y:lpfst1			; save LPF output to LPF state

	move b,y1			; put LPF output in Y1
	mac x1,y1,a	x:(r3+n3),b	; compute feedback term in A
	move a,x:(r2)-			; store output into delay queue

;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
; COMB 2
	move y:firout,a		; comb filter #1 with LPF in feed back
	move b,y:c2out		; move output to y memory space

	move y:c2g1,x1			; get LPF filter coeff
	move y:lpfst2,y1		; get LPF filter state
	mac x1,y1,b	y:(r1)+,x1	; compute LPF output
	move b,y:lpfst2			; save LPF output to LPF state

	move b,y1			; put LPF output in Y1
	mac x1,y1,a	x:(r4+n4),b	; compute feedback term in A
	move a,x:(r3)-			; store output into delay queue

;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
; COMB 3
	move y:firout,a		; comb filter #1 with LPF in feed back
	move b,x0

	move y:c3g1,x1			; get LPF filter coeff
	move y:lpfst3,y1		; get LPF filter state
	mac x1,y1,b	y:(r1)+,x1	; compute LPF output
	move b,y:lpfst3			; save LPF output to LPF state

	move b,y1			; put LPF output in Y1
	mac x1,y1,a	x:(r5+n5),b	; compute feedback term in A
	move a,x:(r4)-			; store output into delay queue
;  -  -  -  -  -  -   -    -    -    -   -   -   -    -   -     -   - 
; COMB 4
	move y:firout,a		; 
	move b,y0

	move y:c4g1,x1			; get LPF filter coeff
	move y:lpfst4,y1		; get LPF filter state
	mac x1,y1,b	y:(r1)+,x1	; compute LPF output
	move b,y:lpfst4			; save LPF output to LPF state

	move b,y1			; put LPF output in Y1
	mac x1,y1,a	y:(r1)+,x1	; compute feedback term in A,c1out to x1
	move a,x:(r5)-			; store output into delay queue
;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
	move y:c2out,a		; add output of add four combs
	add x1,a       		;  c2out + c1out
	add x0,a	y:(r7+n7),x0	   ; +c3out
	add y0,a	y:(r1)+,y1	   ; +c4out

;	asr a	
;  -  -  -  -  -  -  -  -  -   -   -      -   -    -   -  -  -    -  
; 				all pass unit reverberator				
	move a,x1
	mpy x1,y1,b	y:(r1)+,y1

	mac x0,y1,b	y:(r1)+,y1	; MAC into b for output (B CONTAINS OUTPUT)
	move b,y:(r6)-

	mac x1,y1,a	y:(r6+n6),y1
	move a,y:(r7)-		; put new+g*delayed into delay line/inc r7
;  -    -     -     -     -     -     -     -     -     -     -      -     -
	move y:rvbgain,y0
	mpy y1,y0,a	y:(r1)+,x1

	move y:firout,y1
	mac x1,y1,a	y:(r1)+,x1

	move y:sample,y1	; mix in the dry sample
	mac x1,y1,a		; A now contains the output sample
	asr a
;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
; Write DSP56ADC16 A/D converter data to the PCM-56

           move a,x:dac          ;write the PCM-56 D/A via SSI xmt reg.
           jmp poll                ;loop indefinitely

           end
