;   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
