
;*****************************************************************************
;*                           *** Vocoder ***                                 *
;*                                                                           *
;*  Dieses DSP56001 Programm analysiert das unterabgetastete rechte          *
;*  Eingangssignal mit 9 Bandpssen, einem Tiefpaá und einem Hochpaá.        *
;*  Die jeweiligen maximalen Amplitudenwerte werden zwischengespeichert      *
;*  und laufen mit einer einstellbaren Geschwindigkeit zurck ( hnlich      *
;*  wie ein Spektrumsanalysator).                                            *
;*  Vor der Unterabtastung findet keine Tiefpaáfilterung statt. Deshalb      *
;*  kann es zu Aliasing-Fehlern bei der Analyse kommen. Diese sind           *
;*  aber bei Sprach-Eingangssignalen nicht strend. Ein analoger Tiefpaá     *
;*  kann natrlich vorgeschaltet werden.                                     *
;*  Diese Werte dienen dann als Lautstrke-Regler fr die Synthesefilter, die*
;*  parallel mit dem linken Eingangssignal gespeist werden. Hier findet keine*
;*  Unterabtastung statt. Die aufsummierten Filtersignale werden mit dem     *
;*  rechten Kanal gemischt und auf dem rechten und linken Kanal ausgegeben.  *
;*                                                                           *
;*  Misch-Volume und Rcklaufgeschwindigkeit knnen von der Romeo-Oberflche *
;*  aus eingestellt werden. Dazu gibt es die Oberflchendefinition in dem    *
;*  File vocoder.rhi                                                         *
;*                                                                           *
;*  Mit den Werten fr die verschiedenen Gten, Eckfrequenzen und der Filter-*
;*  anzahl kann beliebig experimentiert werden. Die hier gewhlten Werte sind*
;*  nach einiger Erprobung und nach Geschmack festgelegt worden.             *
;*                                                                           *
;*                                                           j.stelkens 4.94 *
;*****************************************************************************

 page 255,24
  OPT MEX,CEX  ; Listfile enthlt Macros
;****************************** Assemblier-Schalter **************************

BA_HOST  EQU 0               ; 1=Synthese-Kanal wird vom Hostinterface bedient
BA_DURCH EQU 0               ; 1=Die Analyse-Ergebnisse werden nicht abgespeichert
BA_CYCL  EQU 0               ; 1=in Adresse x:7000 steht die verbleibende
                             ;   Rechenleistung (mit Romeo-Software anschauen)

;******************************* Peripherie-Adressen **************************

IPR	 EQU $FFFF           ; INTERRUPT-PRIORITY-REGISTER
BCR	 EQU $FFFE           ; BUS-CONTROL-REGISTER
CRA	 EQU $FFEC           ; SSI-CONTROL-REGISTER A
CRB	 EQU $FFED           ; SSI-CONTROL-REGISTER B
PCC	 EQU $FFE1           ; PORT_C-CONTROL-REGISTER
PCDDR    EQU $FFE3	     ; PORT_C-DATA-DIRECTION-REGISTER
PCD      EQU $FFE5	     ; PORT_C-DATA-REGISTER
SSISR	 EQU $FFEE           ; SSI-STATUS-REGISTER
SSITX	 EQU $FFEF           ; SSI-TRANSMIT-REGISTER
SSIRX	 EQU $FFEF           ; SSI-RECEIVE-REGISTER
HRX      equ $FFEB           ; Host-Empfangsregister
HTX      equ $FFEB           ; Host-Senderegister
PBC      equ $FFE0
HCR      equ $FFE8           ; Host-Control/Interruptregister
HSR      equ $FFE9           ; Host-Statusregister
SCR      equ $FFF0           ; SSI Controlregister


PERIPORT equ $FFC8           ; Peripherieport, der von Romeo

;*****************************************************************************
;*                                                                           *
;*    Zeiger:                                                                *
;*                                                                           *
;*    r1 -> Zeiger auf Analyse-Koeffizienten                                 *
;*    r6 -> Zeiger auf Analyse-Delay                                         *
;*    r0 -> Zeiger auf Synthese-Koeffizienten                                *
;*    r7 -> Zeiger auf Synthese-Delay                                        *
;*    r4 -> Zeiger auf Analyseergebnisse                                     *
;*    r5 =  Zeiger auf nchsten Frame                                        *
;*    r3 =  Rechenregister                                                   *
;*    r2 =  Level-Zeiger                                                                        *
;*                                                                           *
;*   Speicheraufteilung der Filter:                                          *
;*                                                                           *
;*   x:      Koeffizienten                                                   *
;*    :     ...                                                              *
;*    :     ...                                                              *
;*    9    -á  2                                                             *
;*    8     æà 2                                                             *
;*    7     ç  2                                                             *
;*    6     åà 2                                                             *
;*    5     à  2                                                             *
;*    4    -á  2                                                             *
;*    3     æà 2                                                             *
;*    2     ç  2                                                             *
;*    1     åà 2                                                             *
;*    0     à  2                                                             *
;*                                                                           *
;*    y:    Delay                                                            *
;*    :                                                                      *
;*    :                                                                      *
;*    7     Y2(n-2)                                                          *
;*    6     X2(n-1)                                                          *
;*    5     Y2(n-1)                                                          *
;*    4     X2(n-2)                                                          *
;*    3     Y1(n-2)                                                          *
;*    2     X1(n-1)                                                          *
;*    1     Y1(n-1)                                                          *
;*    0     X1(n-2)                                                          *
;*****************************************************************************


;***************************** Filter-Konstanten ****************************

pi       equ 3.141592654                 ; pi mal nen dicken Hammer
fsF      equ 44100.0                     ; System Abtast-Frequenz
Nbp      equ 9                           ; Anzahl Bandpsse
NFilter  equ Nbp+2                       ; Anzahl Filter insgesamt

AFilterN equ 2                           ; Anzahl gerechneter Analyse-Filter pro Abtastung
DownSmpl equ NFilter/@cvf(AFilterN)      ; Downsampel-Faktor fr Analyse
fsA      equ fsF/@cvf(DownSmpl)          ; Abtast-Frequenz der Analyse

fstart   equ 200.0                       ; Frequenz des Tiefpaá
fTpmult  equ 1.1                         ; Frequenz-Abstand des 1.Bandpaá
fHpmult  equ 1.1                         ; Frequenz-Abstand des Hochpaá
fBpmult  equ 1.32                        ; Frequenz-Abstand der Bandpsse
QBp      equ 5.0                         ; Gte Bandpsse
QTp      equ 1.5                         ; Gte Tiefpaá
QHp      equ 1.5                         ; Gte Hochpaá

;********************* Macro  Tiefpaá-Koeffizienten *************************

TpKoeff  macro Kfs,Kf0,KQ    ; Kfs = Abtast-Frequenz
                             ; Kf0 = Filter-Frequenz
                             ; KQ  = Filter-Gte

f0       set fstart          ; Anfangs-Frequenz

O0       set 2*pi*Kf0/Kfs
be1      set 1-@tan(O0/(2*KQ))
be2      set 1+@tan(O0/(2*KQ))
beta     set 0.5*be1/be2
gamma    set (0.5+beta)*@cos(O0)
alfa     set (0.5+beta-gamma)*0.25

         dc alfa
         dc alfa
         dc gamma
         dc 2*alfa
         dc -beta
         endm

;******************* Macro    Bandpaá-Koeffizienten *************************

BpKoeff  macro Kfs,Kf0,KQ    ; Kfs = Abtast-Frequenz
                             ; Kf0 = Filter-Frequenz
                             ; KQ  = Filter-Gte


O0       set 2*pi*Kf0/Kfs
be1      set 1-@tan(O0/(2*KQ))
be2      set 1+@tan(O0/(2*KQ))
beta     set 0.5*be1/be2
alfa     set (0.5-beta)*0.5
gamma    set (0.5+beta)*@cos(O0)

         dc alfa
         dc -alfa
         dc gamma
         dc 0
         dc -beta
         endm

;******************* Macro    Hochpaá-Koeffizienten *************************

HpKoeff  macro Kfs,Kf0,KQ    ; Kfs = Abtast-Frequenz
                             ; Kf0 = Filter-Frequenz
                             ; KQ  = Filter-Gte

O0       set 2*pi*Kf0/Kfs
be1      set 1-@tan(O0/(2*KQ))
be2      set 1+@tan(O0/(2*KQ))
beta     set 0.5*be1/be2
gamma    set (0.5+beta)*@cos(O0)
alfa     set (0.5+beta+gamma)*0.25

         dc alfa
         dc alfa
         dc gamma
         dc -2*alfa
         dc -beta
         endm

;******************* Macro Filterkoeffizienten *******************************

FKoeff    macro fsK                ; fsK = Abtastfrequenz

fs        set fstart
          TpKoeff fsK,fs,QTp       ; BpKoeff fsK,fs,QBp
fs        set fs*fTpmult

          dup Nbp

          BpKoeff fsK,fs,QBp
fs        set fs*fBpmult
          endm

fs        set fs/fBpmult*fHpmult
          HpKoeff fsK,fs,QHp
          endm


;***************************** Flags ****************************************

FSAMPL    EQU 0              ; 1 = neues Sampl da
FLEVEL    EQU 1              ; 1 = bitte Leveldaten an PC senden



;****************************** X-Variablen *********************************
 org x:0

xSAVEAD   ds  1                 ; X-Zwischenspeicher
OutR      ds  1                 ; Rechen-Ausgang rechts
OutRZ     ds  1                 ; Zwischenspeicher fr Rechen-Ausgang rechts

FLAGS  	  dc  $0                ; Flag-Register

DataIn    ds 1                  ; Zwischenspeicher fr SSI-Input
InL       ds 1                  ; Rechen-Eingang Links
OutL      ds 1                  ; Rechen-Ausgang Links

AToggel   dc DelayA             ; Toggel-Register fr Delay-Adressierung
AGrenz    dc DelayA+NFilter*4   ; Grenze der Delay-Adressierung
Ar4Rett   ds 1                  ; Zwischenspeicher fr r4 = Ergebniszeiger
r3Rett    ds 1                  ; Zwischenspeicher fr r3
m3Rett    ds 1                  ; Zwischenspeicher fr m3

Volume    dc  0                 ; 16 Werte, die von der Romeo-Oberflche aus
Rueckl    dc  0.998             ; eingestellt werden knnen
          dup 14                ; Volume= Volume des Orginalsignals
          dc 0                  ; Rueckl= Analyse-Hllkurven-Geschwindigkeit
          endm

Level    dup 16                 ; Hier kommen 16 Levelwerte rein
         dc  0
         endm

  org x:64                      ; Achtung NFilter*5 >=2^k
KoeffA    FKoeff fsA            ; Filter-Koeffizienten Analyse  (s.o.)

  org x:128
KoeffS    FKoeff fsF            ; Filter-Koeffizienten Synthese  (s.o.)



;****************************** Y-Variablen *********************************

 org y:0
xSAVEADl   ds  1              ;  X-Zwischenspeicher
InRZ       ds  1              ;  Zwischenspeicher fr Rechen-Input rechts
InR        ds  1              ;  Rechen-Input rechts

 org  y:32
Multipl                       ; Analyseergebnisse
           dup NFilter
           dc  $7fffff
           endm
           dup 16-NFilter
           dc 0               ; wegen Levels
           endm

 org  y:64
DelayA     ds NFilter*4       ; Delay-Lines (2 Taps) fr Analyse (s.o.)
                              ; NFilter <=16 !
 org  y:128
DelayS     ds NFilter*4       ; Delay-Lines fr Synthese (s.o.)



        org p:$0              ; Programm-Adresse p:0
        jmp <START            ; Reset-Vektor

        org p:$C
        jsr    <Adda          ; SSI-Vektor

        org p:$E
        jsr  <Adda            ; SSI-Exception-Vektor

     IF BA_HOST==0
        org p:$12*2
        jsr    <HRegler       ; Host-Command-Vektor
        org p:$14*2
        bset #FLEVEL,x:FLAGS  ; PC erbittet Leveldaten
        nop
     ENDIF




        org p:$40             ; Programmbeginn


;****************************************************************************
;*             Host Command Vektor fr Datenbertragung der Regler         *
;****************************************************************************

HRegler  jsr <Lampe
         move r3,x:r3Rett
         move m3,x:m3Rett
         move #$ffff,m3
         movep  x:HRX,x1                ; HRX vorsichtshalber leerlesen
         move #Volume,r3
         do #16,HRLoop                  ; 16 mal Regler reinholen
         jclr   #0,x:HSR,*              ; warten, bis HRDF gesetzt ist
         movep  x:HRX,x:(r3)+
HRLoop   move x:r3Rett,r3
         move x:m3Rett,m3
         jsr <Lampe
         rti

;****************************************************************************
;*             Host Command Levelwerte                                      *
;****************************************************************************

HLevel      jsr <Lampe
            bclr #FLEVEL,x:FLAGS            ; Flagge einholen
            move #Multipl,r2                ; Adresse des Analyse-Werte Speichers
            do #16,HLev                     ; 16-Levelwerte
Rechenl     jsr <Rechne                     ; Damit es nicht kracht, wenn
                                            ; der PC auf sich warten lát.
            jclr   #1,x:HSR,Rechenl         ; warten, bis HTDE gesetzt wird
            movep  y:(r2)+,x:HTX            ; Wert raus
HLev        jmp <HpLoop


;****************************************************************************
;*               SSI Receive Data  Interrupt                                *
;****************************************************************************

Adda     movep  x:SSIRX,x:DataIn         ; SSI-Daten vom AD-Wandler nach x1
         jset    #3,x:SSISR,Frame1       ; jmp Ä> 1. Frame erkannt
         jmp (r5)


Frame2   move  #<Frame3,r5
ADLlo    movep  n5,x:SSITX               ; Bit 17+18 DA = 0
         rti

Frame4
ADRlo    movep  n5,x:SSITX               ; Bit 17+18 DA =0
         rti

Frame1
ADLhi    move  #<Frame2,r5
         move  x,l:xSAVEAD               ; x retten
         movep x:OutL,x:SSITX            ; Linken Ausgang ausgeben
         move  x:DataIn,x1
         move  x1,x:InL                  ; Empfangenen linken Wert in Rechen-Eingang

         move  l:OutR,x                  ; Rechen-Ausgang
         move  x,l:OutRZ                 ; in Zwischenspeicher
         move  l:xSAVEAD,x               ; x wieder herstellen
     IF BA_HOST==1
         movep  x:OutL,x:HTX             ; Linken Kanal zum Host-Interface
         movep  x:HRX,x:InL

     ENDIF

         bset  #FSAMPL,x:FLAGS           ; Flagge zeigen
         rti


Frame3   move #<Frame4,r5
ADRhi    movep x:OutRZ,x:SSITX           ; Zwischenspeicher ausgeben

         move  x,l:xSAVEAD               ; x retten
         move  x:DataIn,x1               ; Eingangswert
         move  x1,y:InRZ                 ; in Zwischenspeicher
         move  l:xSAVEAD,x               ; x wieder herstellen
         rti

;****************************** Hauptschleife *******************************

 opt cc                                  ; Cyklus-Zhlen ein

HpLoop   jsr <Rechne
         jset #FLEVEL,x:FLAGS,HLevel
         jmp <HpLoop

Rechne

 IF BA_CYCL==1                            ; Wenn Cyklus-Zhlen angesagt:
         move n5,r3                       ; r3=0
         nop                              ; Pipeline
Audiol   move (r3)+                       ; r3 incrementieren
         jclr  #FSAMPL,x:FLAGS,Audiol     ; Warten, bis Audiodaten da
         move r3,x:$7000                  ; Ergebnis in x:$7000
 ELSE                                     ; Normalbetrieb:
         jclr  #FSAMPL,x:FLAGS,*          ; Warten, bis Audiodaten da
 ENDIF
         bclr  #FSAMPL,x:FLAGS            ; Audio-Flagge einholen

;****************************** Analyse **************************************
          move y:InR,y1                            ; Rechen Kanal holen
          move x:(r1)+,x0                          ; x0 = à
          move x:Ar4Rett,r4                        ; Ergebniszeiger wieder herstellen
          do #AFilterN,Analyse                     ; Analyse-Filter
           mpy  x0,y1,a  x:(r1)+,x0  y:(r6)+,y0    ; A=à*x(n)     y0=x(n-2)
           mac  x0,y0,a  x:(r1)+,x0  y:(r6)+,y0    ; A=A+åàx(n-2) y0=y(n-1)
           mac  x0,y0,a  x:(r1)+,x0  y:(r6)+,y0    ; A=A+çy(n-1)  y0=x(n-1)
           mac  x0,y0,a  x:(r1)+,x0  y:(r6)+,y0    ; A=A+æàx(n-1) y0=y(n-2)
           mac  x0,y0,a  x:(r1)+,x0  y:(r4),b      ; A=A-áy(n-2)
           asl  a                    y1,y:(r6)-    ; x(n-2)=y1
           cmpm a,b                  a,y:(r6)+n6   ; y(n-2)=a
           tlt  a,b                                ; Wenn Analyse gráer als bisher,
                                                   ; dann Analyse in b
           move b,y0     x:Rueckl,x1               ; Analyse in y0
           mpy  x1,y0,b  x:AGrenz,x1               ; b=x1*y0  = Rcklauf
           abs  b        r6,a                      ; Absolutwert
 IF BA_DURCH
           cmp x1,a                                ; Analyse nicht abspeichern
 ELSE                                              ; oder
           cmp  x1,a    b,y:(r4)+                  ; abspeichern
 ENDIF
           jlt  <r6Ok                              ; Alle A-Filter durchlaufen ?
           bchg #1,x:AToggel                       ; ja, dann Delay-Adresse toggeln
           move x:AToggel,r6                       ; Neuen Delay-Zeiger lesen
r6Ok       move r4,x:Ar4Rett                       ; Ergebniszeiger abspeichern
Analyse

;****************************** Synthese  ************************************

          ori  #$8,MR                 ; Scaling-Mode-UP
          move x:InL,y1               ; Linken Kanal in y1
          clr b      (r1)-            ; Summierschiene lschen
          move #Multipl,r4            ; r4= Anfang auf Analyseergebnisse
          move x:(r0)+,x0             ; ersten Koeffizient in x0

           do #NFilter,Synthes        ; und ab gehts
           mpy  x0,y1,a  x:(r0)+,x0  y:(r7)+,y0    ; A=à*x(n)     y0=x(n-2)
           mac  x0,y0,a  x:(r0)+,x0  y:(r7)+,y0    ; A=A+åàx(n-2) y0=y(n-1)
           mac  x0,y0,a  x:(r0)+,x0  y:(r7)+,y0    ; A=A+çy(n-1)  y0=x(n-1)
           mac  x0,y0,a  x:(r0)+,x0  y:(r7)+,y0    ; A=A+æàx(n-1) y0=y(n-2)
           mac  x0,y0,a  x:(r0)+,x0  y1,y:(r7)-    ; A=A-áy(n-2)  x(n-2)=y1
           move  a,x1    y:(r4)+,y0                ; y0=Analyseergebnis
           mac   x1,y0,b             a,y:(r7)+n7   ; b=x1*y0 (Summierschiene)   y(n-2)=a
Synthes    lua  (r7)-n7,r3                         ; Delay-Zeiger wieder eins zurck
           move x:Volume,y0                        ; Volume in y0
           move y:InR,y1                           ; Orginalsignal in y1
           mac  y0,y1,b         (r0)-              ; Orginalsignal dazumischen
           move b,x:OutL                           ; Ausgabe links
           move b,x:OutR                           ; Ausgabe rechts

           lua (r3)+n3,r7                          ; Delay-Zeiger um zwei verschieben
           andi    #$ff-8,MR                       ; Scaling-Mode  Off
           rts


 opt nocc


Lampe    movep   #$1ef,x:PCC
	 movep   #$fff,x:PCDDR
         bchg    #4,x:PCD                          ; Romeo-Lampe blinken
         rts

;****************************************************************************
;*                   Initialisierung bei Programmstart                      *
;****************************************************************************

START    movep   #$0001,x:BCR           ; 0 Waitstates fr externes X,Y,P,IO

         ori     #$03,MR                ; Maskiere alle Interrupts
         andi    #$FF-4,omr             ; Blendet Sinustabelle aus

	 movep   #$4300,x:CRA           ; SSI WORD LENGTH = 16 4-Frames
	 movep   #$3A00,x:CRB           ; SSI SYNCHRONOUS, ENABLE, RE, TE

	 movep   #$01ef,x:PCC           ; SSI-PORT an, SCI=GENERAL PURPOSE I/O
	 movep   #$fff,x:PCDDR          ; PC0,1,2 CONFIGURED AS AN OutPut

         movep   #$3A00,x:IPR           ; SSI : IPL 2, Host : IPL 1
         movep   #$BA00,x:CRB		; SSI-Interrupts erlauben

         movep   #1,x:PBC               ; Host-Interface an
         movep   #>4,x:HCR              ; HRIE=0,HTIE=0,HF2,HF3 = 0, HCIE=1

         move #KoeffS,r0                ; Zeiger-Initialisierung
         move #NFilter*5-1,m0

         move #KoeffA,r1
         move m0,m1

         move #NFilter*4,m3
         move #4,n3

         move #Multipl,r4
         move r4,x:Ar4Rett
         move #NFilter-1,m4

         move #Frame1,r5

         move #DelayA,r6
         move #3,m6
         move #5,n6

         move #DelayS,r7
         move #3,m7
         move #5,n7

         move #0,n5

         ; movep   #$1F0000,y:PERIPORT   ; eventuell Julia programmieren

         andi    #$fc,MR                 ; alle Interrupts erlauben
         jmp     <HpLoop                 ; in die Hauptprogrammschleife


       end START
