**
**
** $VER: CalcGroupedValues.asm 1.2 (26.08.2000)
**
**

        OPT P=68030/68882,ALINK


        INCLUDE "Exec/Types.i"
        INCLUDE "Exec.i"


*----------------------------------------------------------------------------------------------------*
*======================================= Fonctions optimisées =======================================*
*----------------------------------------------------------------------------------------------------*

        IFNE CPU_TYPE-68020

******* Misc_funcs/CalcGroupedValues ****************************************
*
*   NAME
*   PsychoC::CalcGroupedValues --
*
*   SYNOPSIS
*   CalcGroupedValues(wNew, GroupedValues, FFTOut, pEnergy, tmp, Part)
*
*   void CalcGroupedValues(UWORD, REAL [][], REAL *, REAL *, REAL *, UBYTE *);
*
*   FUNCTION
*   Calcule des vecteurs complexes à partir des données fournies par la FFT,
*   et les cumule par bandes de fréquences.
*
*   INPUTS
*   wNew          - Indice courant
*   GroupedValues - Tableau des sommes intermédiaires.
*   FFTOut        - Données en sortie de FFT.
*   pEnergy       - Tableau de sauvegarde de l'énergie.
*   tmp           - Tableau de sauvegarde locale
*   Part          - Tableau des indices de partitionnement.
*
*   NOTES
*
*   BUGS
*   Cette fonction n'est pas strictement conforme au source originale : L'
*   énergie du complexe C[512] est ici bornée (borne inférieure = 0.0005).
*
*   SEE ALSO
*
*****************************************************************************
*

        SECTION "_CalcGroupedValues:0",CODE


;void _CalcGroupedValues(UWORD wNew, REAL GroupedValues[][2], REAL *FFTOut, REAL *pEnergy, REAL *tmp, UBYTE *Part)

        XDEF    _CalcGroupedValues

_CalcGroupedValues:

                link    a5,#-8                  ; cos_2phi
                PUSH    d2-d5/a2-a4/a6,CalcGroupedValues
                fmovem.x fp2/fp3/fp4/fp5/fp6/fp7,-(a7)
                move.w  $8(a5),d1               ; UWORD wNew
                movea.l $16(a5),a1              ; REAL *tmp
                movea.l $E(a5),a2               ; float *FFTOut
                movea.l $1A(a5),a4              ; UBYTE *Part

                moveq   #1,d0
                sub.w   d1,d0                   ; wOld = 1 - wNew

                movea.l $A(a5),a0               ; p = &GroupedValues[0][0]
                move.l  a0,d4
                move.l  $12(a5),d5              ; REAL *pEnergy

        ;------ Initialiser la table des sommes intermédiaires.

                moveq   #$3F-1,d2

.ClrLoop        clr.l   (a0)+                   ; *p++ = 0.0
                clr.l   (a0)+
                clr.l   (a0)+                   ; *p++ = 0.0
                clr.l   (a0)+
                dbra    d2,.ClrLoop

        ;------ Initialiser les pointeurs pour la boucle principale.

                lea     (a1,d0.w*4),a6          ; pOld = &tmp[wOld]
                lea     (a1,d1.w*4),a3          ; pNew = &tmp[wNew]

        ;------ Boucle principale.

                moveq   #0,d2

.MasterLoop

        ;------ Calculer cos(phi_prime), sin(phi_prime) et r_prime.
        ; cos(2a) = 2.cos²(a) - 1
        ; sin(2a) = 2.cos(a).sin(a)
        ; cos(2a-b) = cos(2a).cos(b) + sin(2a).sin(b)
        ; sin(2a-b) = sin(2a).cos(b) - cos(2a).sin(b)

                fmove.s (a6),fp3                ; cos_phi  = pOld[COSINE]

                fmove.x fp3,fp7                 ; cos_phi_X2 = cos_phi + cos_phi
                fadd.x  fp3,fp7
                fmove.x fp7,fp2                 ; cos_2phi = cos_phi_X2 * cos_phi - 1
                fmul.x  fp3,fp2
                fsub.s  #1.0,fp2

                fmove.s (a3),fp3                ; cos_teta  = pOldest[COSINE]

                fmul.s  $08(a6),fp7             ; sin_2phi = pOld[SINE] * cos_phi_X2

                fmove.x fp3,fp4                 ; cos_phi' = cos_teta * cos_2phi  (Part #1)
                fmul.x  fp2,fp4

                fmove.s $08(a3),fp5             ; sin_teta  = pOldest[SINE]

                fmove.s $10(a6),fp6             ; r_prime = pOld[ENERGY]

                fmove.x fp5,fp0                 ; cos_phi'  += sin_teta * sin_2phi  (Part #2)
                fmul.x  fp7,fp0
                fadd.x  fp0,fp4

                fmul.x  fp3,fp7                 ; sin_phi' = sin_2phi * cos_teta - cos_2phi * sin_teta
                fmul.x  fp5,fp2
                fsub.x  fp2,fp7

                fadd.x  fp6,fp6                 ; r_prime += r_prime - pOldest[ENERGY]
                fsub.s  $10(a3),fp6
                fmove.d fp4,-$8(a5)             ; = cos_phi'

                ; r_prime  = fp6
                ; cos_phi' = -$8(a5)
                ; sin_phi' = fp7

        ;------ Calculer l'énergie

                fmove.s (a2)+,fp3               ; cos_phi = *p++
                fmove.x fp3,fp4                 ; energy  = cos_phi * cos_phi
                fmul.x  fp3,fp4
                fmove.s #0.0005,fp2
                fmove.s (a2)+,fp5               ; sin_phi = *p++
                fmove.x fp5,fp0                 ; energy += sin_phi * sin_phi
                fmul.x  fp5,fp0
                fadd.x  fp0,fp4

                ; r_prime  = fp6
                ; cos_phi' = -$8(a5)
                ; sin_phi' = fp7
                ; cos_phi  = fp3
                ; sin_phi  = fp5
                ; energy   = fp4
                ; r        = fp2

        ;------ Limiter la valeur minimale de l'énergie.

                fcmp.x  fp2,fp4                 ; if (energy <= r)
                fbogt.w .NoFix

                fmove.x fp2,fp4                 ; energy = r
                fmove.d #$3F96E5B7D1665A3B,fp2  ; r = SQRT(0.0005)
                fmove.x fp2,fp3                 ; cos_phi = SQRT(0.0005)
                fmove.s #0.0,fp5                ; sin_phi = 0
                bra.b   .SaveNRJ

.NoFix          fsqrt.x fp4,fp2                 ; r = SQRT(energy)

                ; Cette partie n'utilise que l'IU pendant que le FPU calcule la racine carrée.
                ;          68882  68040  68060
                ; cycles : 17+89   103     68

.SaveNRJ        moveq   #0,d0
                move.b  0(a4,d2.w),d0           ; part = Part[j]
                asl.l   #4,d0                   ; Grp  = &GroupedValues[part][0]
                add.l   d4,d0
                movea.l d0,a1                   ; == Grp

        IFNE    CPU_TYPE-68030
                tst.l   (a1)                    ; Précharger le cache pendant que le FPU est occupé.
        ENDC

                move.w  d2,d0
                movea.l d5,a0                   ; = pEnergy
                lsr.w   #4,d0                   ; idx = j >> 4
                move.w  d2,d1
                lea     0(a0,d0.w*8),a0
                andi.w  #$F,d1                  ; part = j & 15
                bne.b   .AddEnergy              ; if (part)

                cmpi.w  #$20,d0
                beq.b   .ChkEnds                ; if (idx != 32)

                fmove.d fp4,(a0)                ; pEnergy[idx] = energy

.ChkEnds        subq.w  #1,d0                   ; if (idx--)
                bmi.b   .Continue

                subq.w  #8,a0                   ; Ajuster le pointeur.

.AddEnergy      fmove.d (a0),fp0                ; pEnergy[idx] += energy
                fadd.x  fp4,fp0
                fmove.d fp0,(a0)

        ;------ Mémoriser les données courantes.

.Continue       fmove.s fp2,$10(a3)             ; pNew[ENERGY] = r
                fmove.s #1.0,fp1                ; inv_r = 1.0 / r
                fdiv.x  fp2,fp1
                fmove.x fp3,fp0                 ; pNew[COSINE] = cos_phi * inv_r
                fmul.x  fp1,fp0
                fmove.s fp0,(a3)
                fmul.x  fp5,fp1                 ; pNew[SINE]   = sin_phi * inv_r
                fmove.s fp1,$08(a3)

        ;------ Mettre à jour les sommes intermédiaires.

                ; r_prime  = fp6
                ; cos_phi' = -$8(a5)
                ; sin_phi' = fp7
                ; cos_phi  = fp3
                ; sin_phi  = fp5
                ; energy   = fp4
                ; r        = fp2

                fabs.x  fp6,fp0                 ; r = r + fabs(r_prime)
                fadd.x  fp0,fp2

                fmove.x fp6,fp0                 ; cos_phi -= r_prime * cos_2phi
                fmul.d  -$8(a5),fp0
                fsub.x  fp0,fp3

                fmul.x  fp7,fp6                 ; sin_phi -= r_prime * sin_2phi
                fsub.x  fp6,fp5

                fmul.x  fp3,fp3                 ; cos_phi  = cos_phi * cos_phi + sin_phi * sin_phi
                fmul.x  fp5,fp5
                fadd.x  fp5,fp3

                fmove.d (a1),fp0                ; Grp[0] += energy
                fadd.x  fp4,fp0
                fmove.d fp0,(a1)+

                fsqrt.x fp3,fp0                 ; Grp[1] += energy * sqrt(cos_phi) / r
                ;          68882  68040  68060
                ; cycles : 17+89   103     68
                ; Le FPU est occupé pour un bon moment : Autant en profiter pour
                ; précharger le cache de données.

        IFNE    CPU_TYPE-68030          ; 68040+
                move.l  $18(a6),d0
                move.l  $28(a6),d0
        ENDC

                fmul.x  fp0,fp4
                fdiv.x  fp2,fp4

                ; Profiter du fait que le FPU soit occupé pour faire travailler l'IU
                moveq   #$18,d0
                adda.w  d0,a3                   ; pNew += NEXT_OFFSET
                addq.w  #1,d2
                adda.w  d0,a6                   ; pOld += NEXT_OFFSET

        IFNE    CPU_TYPE-68030
                move.l  $18(a3),d0
        ENDC

                fadd.d  (a1),fp4
                fmove.d fp4,(a1)

                cmpi.w  #$201,d2
                blo .MasterLoop

                fmovem.x (a7)+,fp2/fp3/fp4/fp5/fp6/fp7
                POP     CalcGroupedValues
                unlk    a5
                rts

        ENDC    ; 68030+

        END

