;********************************************************************
;
; (c) Freescale Semiconductor
; (c) Copyright 2000, 2001 Freescale, Inc.
;
;
;********************************************************************
;
; File Name:    dfr16cfft.asm
;
; Description:  56852 Assembly module for Complex FFT
;
; Modules
;    Included:  Fdfr16CFFT
;
; Author(s):    Prasad N R (Optimized by Meera S P)
;               Further optimized by Anuj Pachoree as per 56852
;               specifications
;
; Date:         22 Feb 2000
;
;********************************************************************

    include "portasm.h"

    SECTION rtlib

    GLOBAL  Fdfr16CFFT

;********************************************************************
;
; Module Name:  Fdfr16CFFT
;
; Description:  Computes complex FFT for the given input block.
;
; Functions
;      Called:  Fdfr16Cbitrev - Complex bit reverse function.
;
; Calling
; Requirements: 1. r2 -> Pointer to CFFT Handle.
;               2. r3 -> Pointer to input buffer whose CFFT is to be
;                        computed.
;               3. r4 -> Pointer to output buffer.
;
; C Callable:   Yes.
;
; Reentrant:    Yes.
;
; Globals:      None.
;
; Statics:      None.
;
; Registers
;      Changed: All.
;
; DO loops:     17; max. nesting depth = 2.
;
; REP loops:    None.
;
; Environment:  MetroWerks on PC.
;
; Special
;     Issues:   FFT with No Scale option gives correct results only for
;               low energy signals.
;
;******************************Change History************************
;
;    DD/MM/YY     Code Ver     Description      Author(s)
;    --------     --------     -----------      ------
;    22/02/2000   0.1          Module created   Prasad N R,
;    09/03/2000   1.0          Reviewed &
;                              Baselined        Prasad N R
;    29/01/2001   1.1          Optimized,
;                              Reviewed &
;                              Baselined        Meera S P
;    10/12/2001   1.2          Optimized,       Anuj Pachoree
;                              Reviewed &
;                              Baselined
;
;********************************************************************


Fdfr16CFFT

Offset_options        equ    0
Offset_n              equ    Offset_options+1
Offset_Twiddle        equ    Offset_n+1
Offset_No_of_Stages   equ    Offset_Twiddle+PTR_SIZE
Scratch_Size          equ    6
START_BFLY_SZ         equ    2            ;To start with, butterfly size = 2.
THRESHOLD_BFP         equ    0.25         ;Minimum absolute value used in
                                          ;  BFP for comparison.
FFT_DEFAULT_OPTIONS               equ   0 ;Default all options bits
                                          ;  to 0
FFT_SCALE_RESULTS_BY_N            equ   1 ;Unconditionally scale by N

FFT_SCALE_RESULTS_BY_DATA_SIZE    equ   2 ;Scale according to data
                                          ;  sizes
FFT_INPUT_IS_BITREVERSED          equ   4 ;Default to normal (linear)
                                          ;  input
FFT_OUTPUT_IS_BITREVERSED         equ   8 ;Default to normal (linear)


    DEFINE    old_omr        'X:(SP-1)'   ;Scratch to store old omr reg.
    DEFINE    ScaleBF        'X:(SP-2)'   ;Counter to count the number of
                                          ;stages wherein scaling is done
                                          ;in BFP method.
    DEFINE    pZ             'X:(SP-4)'   ;For storing pointer to the output array

;--------------------------------------
;Save c and d
;-------------------------------------

    adda    #2,sp
    move.l  c2,x:(sp)+
    move.l  c10,x:(sp)+
    move.l  d2,x:(sp)+
    move.l  d10,x:(sp)+
    move.l  r5,x:(sp)

;---------------------------------
; Accommodate for scratch in stack
;---------------------------------

    adda #Scratch_Size,sp                 ;Update stack pointer by adding
                                          ;Scratch locations needed for FFT.

;---------------------------------------------------------
; Store the existing omr and set the saturation bit to 0
;----------------------------------------------------------

    move.w  omr,old_omr                   ;Store old omr.
    tfra    r2,r5
    bfclr   #$30,omr                      ;S-bit = 0.
                                          ;Rounding mode enabled
                                          ;(2's complement).

;---------------------------------------------------------------
; Rearrange the input array in bit-reversed order, if required
;---------------------------------------------------------------


;    move.w  r4,pZ                         ;Storing pointer to the output array
    StoreRx r4,pZ                         ;Storing pointer to the output array
    tfra    r3,r1                         ;storing r3
    nop
    nop
    nop
    nop
    nop
    brclr   #FFT_INPUT_IS_BITREVERSED,x:(r2+Offset_options),FFT_Computation
                                          ;Test if input is bit-reversed.
    nop
    nop
    nop
    nop
    nop
;--------------------------------------------------------
; Set up the parameters for calling bit-reverse function
;--------------------------------------------------------

    move.w  x:(r2+Offset_n),y0            ;y0 = N, the size of FFT.
    tfra    r3,r2                         ;r2 -> pX
    tfra    r4,r3
    tfra    r3,r1                         ;Storing r3
    jsr     Fdfr16Cbitrev_                ;Call bit reverse function.
                                          ;Result is returned in y0.
    cmp.w   #FAIL,y0
    bne     FFT_Computation               ;Bit reverse passed, go to FFT_Computation.
    moveu.w old_omr,omr                   ;Restore previous OMR value.
    moveu.w #-Scratch_Size,n              ;Restore Stack pointer.
    adda    n,sp
    move.l  x:(sp)-,r5
    move.l  x:(sp)-,d
    move.l  x:(sp)-,d2
    move.l  x:(sp)-,c
    move.l  x:(sp)-,c2
    rts                                   ;FAIL is returned in y0.

FFT_Computation

;-----------------------------------------------
; Decide whether scaling is AS or BFP or NS
;-----------------------------------------------

    tfra    r5,r2
    brset   #FFT_SCALE_RESULTS_BY_N,x:(r5+Offset_options),AutoScaling
                                          ;Check the type of scaling
                                          ;Autoscaling...
    move.w  x:(r5+Offset_options),x0      ;Check if scaling type is BFP
    andc    #FFT_SCALE_RESULTS_BY_DATA_SIZE,x0
    tst.w   x0
    jne     BlockFloatingPoint            ;Block floating point...
    jmp     NoScaling                     ;If not, it implies no scaling.

AutoScaling

;----------------------------
; CASE 1: Code for AS.
;----------------------------


;    moveu.w pZ,r4                         ;getting the pointer to the output array in r4
    LoadRx  pZ,r4                         ;getting the pointer to the output array in r4
    moveu.w x:(r5+Offset_n),n             ;n = No. of points in FFT
    tfra    r4,r2                         ;r2 -> output buffer
    tfra    r1,r3                         ;r3 -> 1st Ar of first pass
    adda    n,r3                          ;r3 -> 1st Br of 1st pass
    tfra    r2,r0                         ;r0 -> 1st Br of first pass
    move.w  x:(r1)+,a                     ;Get 1st Ar of 1st pass
    adda    n,r0
    deca    r0                            ;Adjust r0 for first pass
    asra    n                             ;half the no. of DFT points
    move.w  x:(r0),b                      ;Save the memory cotents so
                                          ;that first parallel move
                                          ;doesn't corrupt the data
    move.w  x:(r3)+,y0                    ;Get 1st Br of 1st pass

    if CODEWARRIOR_WORKAROUND==1
    do      n,>>_first_pass               ;The first pass has to be
                                          ;repeated length/2 times
    else
    do      n,_first_pass                 ;The first pass has to be
                                          ;repeated length/2 times
    endif

    add     y0,a         b,x:(r0)+        ;Find Cr,save Di in previous
                                          ;Bi. r0 -> Br
    asr     a            x:(r1)+,b        ;Find Cr/2,get Ai,r1 -> next Ar
    rnd     a            x:(r3)+,x0       ;round, get Bi, r3 -> next Br
    sub     y0,a         a,x:(r2)+        ;Find Dr/2 ,save Cr/2,r2 -> Ci
    add     x0,b         a,x:(r0)+        ;Find Ci,save Dr/2 ,r0 ->Di
    asr     b            x:(r1)+,a        ;Find Ci/2, get next Ar,
                                          ;r1  -> next Ai
    rnd     b            x:(r3)+,y0       ;round Ci/2, get next Br,
                                          ;r3 -> next Bi
    sub     x0,b         b,x:(r2)+        ;Find Di/2, save Ci/2 ,
                                          ;r2 -> next Cr. Di/2 saved
                                          ;in the next loop
_first_pass

    move.w  b,x:(r0)                      ;Save last Di/2 of the 1st pass
    tfra    r4,r1                         ;r1 -> 1st Ar of 2nd pass
    tfra    r1,r3                         ;r3 -> 1st Ar of 2nd pass
                                          ;n set to N, used for
                                          ;controlling the no.of bflies
                                          ;per group in the pass.
    moveu.w #2,n3                         ;n3 set to 2,used for controlling
                                          ;the no. of groups in the pass
    adda    n,r3                          ;r3 -> first Br of second pass
                                          ;Set counter for no. of passes
    move.w  x:(r5+Offset_No_of_Stages),c
    sub.w   #2,c

_second_pass
    LoadRx  x:(r5+Offset_Twiddle),r0      ;twiddle fac. ,twiddle fac.
                                          ;stored in bit reversed fashion
    asra    n                             ;Halving n =n/2,butterflies per group is n
    move.w  n3,b                          ;Save the no. of groups/passin b
    tfra    r3,r2                         ;r2 -> first Br of the first pass

    if CODEWARRIOR_WORKAROUND==1
    do      b,>>_end_group                ;Middle loop is done b times
                                          ;b=2**(pass number-1)
                                          ;b = no. of groups/pass
    else
    do      b,_end_group                  ;Middle loop is done b times
                                          ;b=2**(pass number-1)
                                          ;b = no. of groups/pass
    endif

    move.w  x:(r0)+,y0                    ;y0=Wr,x0=Br,r0 ->Wi, r3 ->Bi
    move.w  x:(r3)+,x0
    move.w  x:(r0)+,y1                    ;y1=Wi, r0 -> next Wr
                                          ;(in bit reversed order)
;    move.w  r0,d                          ;Save twiddle factor pointer in d register
    StoreRx r0,d                          ;Save twiddle factor pointer in d register
    tfra    r1,r0                         ;Move r1 to r0
    deca    r2
    move.w  x:(r2),b                      ;Save the contents so that the
                                          ;mem. contents aren't corrupted
                                          ;in the first middle loop

    if CODEWARRIOR_WORKAROUND==1
    do      n,>>_end_bfly                 ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
                                          ;n=no. of butterflies/group
    else
    do      n,_end_bfly                   ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
                                          ;n=no. of butterflies/group
    endif

    mpy     y0,x0,b      b,x:(r2)+        ;b=WrBr,store the previous
                                          ;butterfly's Di,r1->current Ar
    mpy     y1,x0,a      x:(r3)+,x0       ;a=+WiBr,get Bi
    mac     -y0,x0,a                      ;a=-WrBi+WiBr
    mac     y1,x0,b      x:(r1)+,x0       ;b=WrBr+WiBi,x0=Ar,r1 -> Ai
    add     x0,b                          ;Find Cr in b
    asr     b                             ;b=Cr/2,Scale down for storage
    rnd     b                             ;Round.The rounding done here
                                          ;closely matches with the
                                          ;autoscale mode rounding
    sub     b,x0                          ;Find Dr/2
    neg     a            b,x:(r0)+        ;a= WrBi-WiBr
                                          ;Store Cr/2
    move.w  x:(r1)+,b                     ;fetch Ai into b,r1 -> next Ar
    add     b,a          x0,x:(r2)+       ;Find Ci ,store Dr/2,r2 -> Di
    asr     a            x:(r3)+,x0       ;a=Ci/2,x0 = Next Br,
                                          ;r3 -> Next Bi
    rnd     a                             ;Round
    sub     a,b          a,x:(r0)+        ;b = Di/2, a = Ci/2
                                          ;store Ci/2
                                          ;Di/2 stored in next loop
_end_bfly

    move.w  b,x:(r2)+                     ;Store last butterfly's Ci
    moveu.w  d,r0                         ;Restore the pointer pointing
                                          ;to the twiddle factors
    tfra    r2,r1                         ;r1 -> next group's first Ar
    adda.l  n,r2                          ;r2 -> next group's first Br
    tfra    r2,r3                         ;r3 -> next group's first Br

_end_group

    tfra    r4,r1                         ;r1 ->1st Ar,at start of each pass
    tfra    r1,r3                         ;r3 ->1st Ar,at start of each pass
    move.w  n3,b                          ;double the no of groups for next
    asl     b            x:(r3)+n,x0      ;pass,Dummy read  to adjust r3
                                          ;r3 -> first Br of the next pass
    moveu.w b,n3
    dec.w   c
    tst.w   c                             ;Test the pass counter for Zero
    bne     _second_pass                  ;If less than zero then go to

_end_second_pass                          ;last pass .

    LoadRx  x:(r5+Offset_Twiddle),r0      ;Get address of twiddle factors
    move.w  x:(r0)+,y0

    if CODEWARRIOR_WORKAROUND==1
    do      b,>>_last_pass                ;N/2 groups in last pass
    else
    do      b,_last_pass                  ;N/2 groups in last pass
    endif

    tfra    r3,r2
    move.w  x:(r3)+,x0                    ;x0=Br,r3 -> Bi
    mpy     x0,y0,b      x:(r0)+,y1       ;b=BrWr, y1=Wi, r0 ->Next Wr
    mpy     y1,x0,a      x:(r3)+,x0       ;a=WiBr
    mac     -y0,x0,a                      ;a=-WrBi+WiBr
    mac     y1,x0,b      x:(r1)+,x0       ;b=WiBi+WrBr, x0=Ar, r1 -> Ai
    add     x0,b                          ;Find Cr
    asr     b                             ;Find Cr/2
    rnd     b                             ;Round
    sub     b,x0
                                          ;Find Dr/2
                                          ;Store Cr/2 , r1 -> Ai
    move.w  b,x:(r1-1)                    ;Store Cr/2 , r1 -> Ai
    move.w  x:(r1)+,b                     ;b = Ai, r1 -> Ai
    sub     b,a          x0,x:(r2)+       ;Find -Ci , store Dr/2,r2 ->Di
    asr     a            x:(r3)+n,x0      ;Find -Ci/2, Dummy read to
                                          ;adjust r3 to next Br
    rnd     a            x:(r0)+,y0       ;y0=Wr,r0=Wi
    add     a,b          x:(r1)+n,x0      ;Find Di/2,Dummy read to
                                          ;adjust r1
    neg     a            b,x:(r2)+        ;Find Ci/2 ,store Di/2
    move.w  a,x:(r1-3)                    ;Store Ci/2

_last_pass
_End_fft

    jmp     PostProcessing

;---------------------------------------------------------------
; CASE 2: Code for BFP
;---------------------------------------------------------------

BlockFloatingPoint

;---------------------------------------------------------------
; See whether the magnitude of any of the data values is > 0.25
;---------------------------------------------------------------

;    moveu.w pZ,r4                         ;Copy pointer to output buffer in r4
    LoadRx  pZ,r4                         ;Copy pointer to output buffer in r4
    moveu.w x:(r5+Offset_n),n             ;n = No.of points in FFT
    tfra    r4,r2                         ;r2 output buffer
    asla    n
    cmpa    r1,r2
    beq     _avoid_copy_bfp

    if CODEWARRIOR_WORKAROUND==1
    do      n,>>_endcpy
    else
    do      n,_endcpy
    endif

    move.w x:(r1)+,y0
    move.w y0,x:(r2)+
_endcpy
_avoid_copy_bfp
    asra    n
    tfra    r4,r1
    tfra    r1,r3                         ;r3 -> 1st Ar of 1st pass
    dec.w   x:(r5+Offset_No_of_Stages)
    move.w  #1,c                          ;c set to 1, for controlling
                                          ;the no. of groups in the pass
    adda    n,r3                          ;r3 -> first Br of first pass
    move.w  #0,ScaleBF                    ;Memory location used as a flag
                                          ;0 indicates last pass is not
                                          ;to be scaled down
    moveu.w n,n3                          ;Store no. of butterflies in n3
_first_passBF
    move.w  n3,b
    tfra    r1,r0                         ;r0 -> first Br
    move.w  #0,x0                         ;x0= 0; smallest positive number
    move.w  x:(r0)+,a                     ;a= first Ar, r0 -> First Ai

    if CODEWARRIOR_WORKAROUND==1
    do      b,>>_chkp                     ;Repeat for all samples
    else
    do      b,_chkp                       ;Repeat for all samples
    endif

    abs     a            x:(r0)+,b        ;Get the magnitude in acc a
                                          ;get the next data in acc b
    or.w    a,x0                          ;Get the most significant bit of
                                          ;the largest number in x0
    abs     b            x:(r0)+,a        ;Get the magnitude of next data
                                          ;in b and get next data in a
    or.w    b,x0                          ;Find the max value in x0
_chkp

    LoadRx  x:(r5+Offset_Twiddle),r0      ;r0 ->memory location of the
                                          ;first twiddle factor Wr
    asra    n                             ;Halving n=n/2
    tfra    r3,r2                         ;r2 -> first Br of the pass
    cmp.w   #$2000,x0                     ;Compare the max value with 0.25
    ble     _nscdown                      ;If greater, perform the sc_down
                                          ;pass, else no scale down pass
    inc.w   ScaleBF                       ;Increment the scale_fac by one

    if CODEWARRIOR_WORKAROUND==1
    do      c1,>>_end_groupBF             ;Middle loop is done b times
                                          ;b=2**(pass number-1)
                                          ;b = no. of groups/pass
    else
    do      c1,_end_groupBF               ;Middle loop is done b times
                                          ;b=2**(pass number-1)
                                          ;b = no. of groups/pass
    endif

    move.w  x:(r0)+,y0                    ;y0=Wr, x0=Br, r0 -> Wi, r3 -> Bi
    move.w  x:(r3)+,x0
    move.w  x:(r0)+,y1                    ;y1=Wi, r0 -> next Wr
;    move.w  r0,d
    StoreRx r0,d
    tfra    r1,r0                         ;Move r1 to r0
    deca    r2
    move.w  x:(r2),b                      ;Save the contents so that they
                                          ;are not corrupted in the first
                                          ;middle loop

    if CODEWARRIOR_WORKAROUND==1
    do      n,>>_end_bflyBF               ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
                                          ;n=no. of butterflies/group
    else
    do      n,_end_bflyBF                 ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
                                          ;n=no. of butterflies/group
    endif

    mpy     y0,x0,b      b,x:(r2)+        ;b=WrBr, store previous butter-
                                          ;fly's Di , r1 -> current Ar
    mpy     y1,x0,a      x:(r3)+,x0       ;a=+WiBr, get Bi
    mac     -y0,x0,a                      ;a=-WrBi+WiBr
    mac     y1,x0,b      x:(r1)+,x0       ;b=WrBr+WiBi,x0=Ar,r1 -> Ai
    add     x0,b                          ;Find Cr in b
    asr     b                             ;b=Cr/2, Scale down for storage
    rnd     b                             ;Round. The rounding done here
                                          ;closely matches with the
                                          ;autoscale down mode rounding
    sub     b,x0                          ;Find Dr/2
    neg     a            b,x:(r0)+        ;a = WrBi - WiBr
                                          ;store Cr/2
    move.w  x:(r1)+,b                     ;Fetch Ai into b, r1-> next Ar
    add     b,a          x0,x:(r2)+       ;Find Ci, store Dr/2, r2-> Di
    asr     a            x:(r3)+,x0       ;a=Ci/2, x0= Next Br,
                                          ;r3-> Next Bi
    rnd     a                             ;Round
    sub     a,b          a,x:(r0)+        ;b=Di/2, a=Ci/2, store Ci/2
                                          ;Di/2 is stored in next loop
_end_bflyBF

    move.w  b,x:(r2)+                     ;Store last butterfly's Ci
    moveu.w  d,r0                         ;Restore the pointer pointing
                                          ;to the twiddle factors
    tfra    r2,r1                         ;r1 -> next group's first Ar
    adda.l  n,r2                          ;r2 -> next group's first Br
    tfra    r2,r3                         ;r3 -> next group's first Br

_end_groupBF

    tfra    r4,r3                         ;r3 -> 1st Ar, for start of each
    tfra    r3,r1                         ;r1 -> 1st Ar,for start of each
                                          ;pass
    asl     c                             ;Double the no of groups for next
                                          ;pass
    adda    n,r3                          ;adjust r3-> first Br of the next pass
    dec.w   x:(r5+Offset_No_of_Stages)    ;Test for pass counter
                                          ;r2=2**(pass number-1)
    bne     _first_passBF                 ;perform the loop till counter= 0
    bra     _lpass

_nscdown                                  ;No scale down mode

    move.w  n,d
    moveu.w #0,n

    if CODEWARRIOR_WORKAROUND==1
    do      c1,>>_end_group2              ;Middle loop is done c1 times
                                          ;c1=2**(pass number-1)
    else
    do      c1,_end_group2                ;Middle loop is done c1 times
                                          ;c1=2**(pass number-1)
    endif

    move.w  x:(r0)+,y0                    ;y0=Wr,x0=first Br of the
    move.w  x:(r3)+,x0                    ;group. r0->Next Wr. r3->first
                                          ;Bi of the group.

    if CODEWARRIOR_WORKAROUND==1
    do      d1,>>_end_bfly2               ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
    else
    do      d1,_end_bfly2                 ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
    endif

    mpy     x0,y0,b      x:(r0)+n,y1
    move.w  x:(r3)-,x0                    ;y1=Wi, b=WrBr, x0=Bi, r3->Br
                                          ;r0->Wi
    macr    x0,y1,b      x:(r1)+n,a       ;b=WrBr+WiBi, a=Ar, r1->Ar
    add     a,b                           ;Find Cr in b, b=Ar+WrBr+WiBi,
    asl     a            b,x:(r1)+        ;a=2Ar, store Cr. r1->Ai
    sub     b,a          x:(r3)+,b        ;Find Dr in a, b=Br, r3->Bi
    neg     b            a,x:(r2)+        ;b=-Br, store Dr, r2->Bi
    mpy     b1,y1,b      x:(r3)+,x0       ;b=-Brwi, x0=Bi, r3->Next Br
    macr    y0,x0,b      x:(r1)+n,a       ;b=WrBi-WiBr, a=Ai, r1->Ai
    add     a,b          x:(r3)+,x0       ;Find Ci in b, x0=Next Br,
                                          ;r3->Next Bi
    asl     a            b,x:(r1)+        ;a=2Ai, store Ci, r1->Next Ar
    sub     b,a                           ;Find Di
    move.w  a,x:(r2)+                     ;Store Di, r2->Next Br

_end_bfly2

    adda    #1,r0                         ;r0->First Wr of next group
                                          ;Restore the no. of butterflies
    moveu.w  d,r3                         ;Restore the pointer pointing
    tfra    r2,r1                         ;r1->next group,s first Ar
    adda.l  r3,r2                         ;r2 -> next group's first Br
    tfra    r2,r3                         ;r3-> Next groups first Br

_end_group2

    moveu.w d1,n
    tfra    r4,r3                         ;r3 -> 1st Ar, for start of each pass
    tfra    r3,r1                         ;r1 -> 1st Ar of next pass
    asl     c                             ;Double the no of groups for next pass
    adda    n,r3                          ;adjust r0, r0 -> first Br of the next pass
    dec.w   x:(r5+Offset_No_of_Stages)    ;Test the pass counter for zero
                                          ;r2=2**(pass number-1)
    bne     >_first_passBF                ;If not equal to zero then go to
                                          ;beginning of the pass

_lpass                                    ;data scanning for the last pass

    tfra    r4,r0                         ;r0->first Ar
    move.w  x:(r5+Offset_n),y0            ;y0= number of points in fft
    move.w  #0,x0                         ;x0= 0; smallest positive number
    move.w  x:(r0)+,a                     ;a= first Ar, r3 -> First Ai

    if CODEWARRIOR_WORKAROUND==1
    do      y0,>>_chklp                   ;Repeat for all samples
    else
    do      y0,_chklp                     ;Repeat for all samples
    endif

    abs     a            x:(r0)+,b        ;Get the magnitude in acc a
                                          ;get the next data in acc b
    or.w    a,x0                          ;Get the most significant bit of
                                          ;the largest number in x0
    abs     b            x:(r0)+,a        ;Get the magnitude of next data
                                          ;in b and get next data in a
    or.w    b,x0
                                          ;Find the max value in x0
_chklp

    LoadRx  x:(r5+Offset_Twiddle),r0      ;r0->first Wr
    cmp.w   #0.25,x0                      ;if the largest value is > 0.25
    ble     _lnscdown                     ;perform scale down pass
    inc.w   ScaleBF                       ;Increment the scale factor
    move.w  x:(r0)+,y0                    ;y0=Wr,r0->Wi

    if CODEWARRIOR_WORKAROUND==1
    do      c1,>>_last_pass1              ;N/2 groups in last pass
    else
    do      c1,_last_pass1                ;N/2 groups in last pass
    endif

    tfra    r3,r2
    move.w  x:(r3)+,x0                    ;x0=Br,r3 -> Bi
    mpy     x0,y0,b      x:(r0)+,y1       ;b=BrWr, y1=Wi, r0 -> Next Wr
    mpy     y1,x0,a      x:(r3)+,x0       ;a=WiBr
    mac     -y0,x0,a                      ;a=-WrBi+WiBr
    mac     y1,x0,b      x:(r1)+,x0       ;b=WiBi+WrBr, x0=Ar, r1 -> Ai
    add     x0,b                          ;Find Cr
    asr     b                             ;Find Cr/2
    rnd     b                             ;Round
    sub     b,x0                          ;Find Dr/2
    move.w  b,x:(r1-1)                    ;Store Cr/2 , r1 -> Ai
    move.w  x:(r1)+,b                     ;b = Ai, r1 -> Ai
    sub     b,a          x0,x:(r2)+       ;Find -Ci , store Dr/2,r2 ->Di
    asr     a            x:(r3)+n,x0      ;Find -Ci/2, Dummy read to adjust
                                          ;r0 to next Br
    rnd     a            x:(r0)+,y0       ;Round,Dummy read to adjust r1
                                          ;r1 -> Next Ar
    add     a,b          x:(r1)+n,x0      ;Find Di/2
    neg     a            b,x:(r2)+        ;Find Ci/2 ,store Di/2
    move.w  a,x:(r1-3)                    ;Store Ci/2

_last_pass1

    jmp     PostProcessing                ;Perform bitreversal operation

_lnscdown                                 ;Last pass no scale down
                                          ;Offset for the last pass
    move.w  x:(r0)+,y0
    moveu.w #0,n

    if CODEWARRIOR_WORKAROUND==1
    do      c1,>>_last_pass2              ;N/2 number of butterflies
    else
    do      c1,_last_pass2                ;N/2 number of butterflies
    endif

    move.w  x:(r3)+,x0                    ;x0=Br,r3->Bi
    mpy     x0,y0,b      x:(r0)+,y1       ;b=WrBr, y1=Wi,r0->Wi
                                          ;r3->Br
    move.w  x:(r3)-,x0                    ;x0=Bi
    macr    x0,y1,b      x:(r1)+n,a       ;b=WrBr+WiBi, a=Ar, r1->Ar
    add     a,b                           ;Find Cr in b, b=Ar+WrBr+WiBi,
    asl     a            b,x:(r1)+        ;a=2Ar, store Cr, r1->Ai
    sub     b,a          x:(r3)+n,b       ;Find Dr in a, a=2Ar-Cr, b=Br
                                          ;r3->Br
    neg     b            a,x:(r3)+        ;b=-Br, store Dr, r3->Bi
    mpy     b1,y1,b      x:(r3)+n,x0      ;b=-BrWi, x0=Bi, r3->Bi
    macr    y0,x0,b      x:(r1)+n,a       ;b=WrBi-BrWi, a=Ai, r1->Ai
    add     a,b          x:(r0)+,y0       ;find Ci in acc b
    asl     a            b,x:(r1)+        ;a=2Ai, store Ci, r1->Br
    sub     b,a          x:(r1)+,x0       ;Find Di in acc a, dummy read
                                          ;in x0, r1->Bi
    move.w  a,x:(r3)+                     ;Store Di, r3-> Ar of next
                                          ;butterfly
    adda    #1,r1
    adda    #2,r3

_last_pass2
_rev

    jmp     PostProcessing

NoScaling

;---------------------------------------------------------------
; CASE 3: Code for no-scaling.
;---------------------------------------------------------------

;    moveu.w pZ,r4                         ;Copy pointer to the output buffer in r4
    LoadRx  pZ,r4                         ;Copy pointer to the output buffer in r4
    moveu.w x:(r5+Offset_n),n             ;n=number of points in FFT
    tfra    r4,r2                         ;r2 output buffer
    tfra    r1,r3                         ;r3 -> 1st Ar of first pass
    adda    n,r3                          ;r3 -> 1st Br of 1st pass
    tfra    r2,r0                         ;r0 -> 1st Br of first pass
    move.w  x:(r1)+,a                     ;Get 1st Ar of 1st pass
    adda    n,r0
    deca    r0                            ;Adjust r0 so that in first
    asra    n                             ;first pass , which is half
                                          ;half the no. of DFT points
    move.w  x:(r0),b                      ;Save the memory cotents so
                                          ;that first parallel move
                                          ;doesn't corrupt the data
    move.w  x:(r3)+,y0                    ;Get 1st Br of 1st pass

    if CODEWARRIOR_WORKAROUND==1
    do      n,>>_first_pass_NS            ;The first pass has to be
                                          ;repeated length/2 times
    else
    do      n,_first_pass_NS              ;The first pass has to be
                                          ;repeated length/2 times
    endif

    add     y0,a         b,x:(r0)+        ;Find Cr,save Di in previous
    rnd     a            x:(r3)+,x0       ;round, get Bi, r3 -> next Br
    sub     y0,a         a,x:(r2)+        ;save Cr,r2 -> Ci
    sub     y0,a         x:(r1)+,b        ;Find Dr,get Ai,r1 -> next Ar
    add     x0,b         a,x:(r0)+        ;Find Ci,save Dr ,r0 ->Di
    rnd     b            x:(r3)+,y0       ;round Ci, get next Br,
                                          ;r3 -> next Bi
    sub     x0,b         b,x:(r2)+        ;Find Di, save Ci,
                                          ;r2 -> next Cr.
    sub     x0,b         x:(r1)+,a        ;Find Di, get next Ar, Di saved
                                          ;in the next loop
_first_pass_NS

    move.w  b,x:(r0)                      ;Save last Di/2 of the 1st pass
    tfra    r4,r1                         ;r1 -> 1st Ar of 2nd pass
;    moveu.w r1,r3                         ;r3 -> 1st Ar of 2nd pass
    tfra    r1,r3                         ;r3 -> 1st Ar of 2nd pass
    moveu.w  #2,n3                        ;n3 set to 2,used for controlling
    adda    n,r3                          ;r3 -> first Br of second pass
    move.w x:(r5+Offset_No_of_Stages),c   ;Set counter for no. of passes
    sub.w     #2,c                        ;last pass is also separate

_second_pass_NS

    LoadRx  x:(r5+Offset_Twiddle),r0     ;r0->mem. location of the first
                                          ;twiddle fac. ,twiddle fac.
                                          ;stored in bit reversed fashio
    asra    n                             ;Halving n =n/2 , butterflies per group is n
    move.w  n3,b                          ;Save the no. of groups/passin b
    tfra    r3,r2                         ;r2 -> first Br of the first pass

    if CODEWARRIOR_WORKAROUND==1
    do      b,>>_end_group_NS             ;Middle loop is done b times
                                          ;b=2**(pass number-1)
                                          ;b = no. of groups/pass
    else
    do      b,_end_group_NS               ;Middle loop is done b times
                                          ;b=2**(pass number-1)
                                          ;b = no. of groups/pass
    endif

    move.w  x:(r0)+,y0                    ;y0=Wr,x0=Br,r0 ->Wi,r3 ->Bi
    move.w  x:(r3)+,x0
    move.w  x:(r0)+,y1                    ;y1=Wi, r0 -> next Wr
                                          ;(in bit reversed order)
;    move.w  r0,d                          ;Save twiddle factor pointer
    StoreRx r0,d                          ;Save twiddle factor pointer
    tfra    r1,r0                         ;Move r1 to r0
    deca    r2
    move.w  x:(r2),b                      ;Save the contents so that the
                                          ;mem. contents aren't corrupted
                                          ;in the first middle loop

    if CODEWARRIOR_WORKAROUND==1
    do      n,>>_end_bfly_NS              ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
                                          ;n=no. of butterflies/group
    else
    do      n,_end_bfly_NS                ;Inner loop is done n times
                                          ;n=2**(L-passnumber)
                                          ;n=no. of butterflies/group
    endif

    mpy     y0,x0,b      b,x:(r2)+        ;b=WrBr,store the previous
                                          ;butterfly's Di,r1->current Ar
    mpy     y1,x0,a      x:(r3)+,x0       ;a=+WiBr,get Bi
    mac     -y0,x0,a                      ;a=-WrBi+WiBr
    mac     y1,x0,b      x:(r1)+,x0       ;b=WrBr+WiBi,x0=Ar,r1 -> Ai
    add     x0,b                          ;Find Cr in b
    rnd     b                             ;Round.The rounding done here
                                          ;closely matches with the
                                          ;autoscale mode rounding
    asl     x0                            ;Get 2*Ar
    sub     b,x0                          ;Find Dr [ Dr = 2*Ar - Cr]
    neg     a            b,x:(r0)+        ;a= WrBi-WiBr
                                          ;Store Cr
    move.w  x:(r1)+,b                     ;fetch Ai into b,r1 -> next Ar
    add     b,a          x0,x:(r2)+       ;Find Ci ,store Dr,r2 -> Di
                                          ;r3 -> Next Bi
    rnd     a            x:(r3)+,x0       ;Round
    asl     b                             ;Get 2*Ai
    sub     a,b          a,x:(r0)+        ;Find Di,[Di = 2*Ai - Ci]
                                          ;store Ci
                                          ;Di stored in next loop
_end_bfly_NS

    move.w  b,x:(r2)+                     ;Store last butterfly's Ci
    moveu.w  d,r0                         ;Restore the pointer pointing
                                          ;to the twiddle factors
    tfra    r2,r1                         ;r1 -> next group's first Ar
    adda.l  n,r2                          ;r2 -> next group's first Br
    tfra    r2,r3                         ;r3 -> next group's first Br

_end_group_NS

    tfra    r4,r1
    tfra    r1,r3                         ;r3 ->1st Ar,at start of each pass
    move.w  n3,b                          ;double the no of groups for next
    asl     b            x:(r3)+n,x0
                                          ;pass,Dummy read  to adjust r3
                                          ;r3 -> first Br of the next pass
    moveu.w b,n3

    dec.w   c                             ;Test the pass counter for Zero
    tst.w   c
    bne     _second_pass_NS               ;If less than zero then go to
                                          ;last pass .
    LoadRx  x:(r5+Offset_Twiddle),r0      ;Get address of twiddle factors
    move.w  x:(r0)+,y0

    if CODEWARRIOR_WORKAROUND==1
    do      b,>>_last_pass_NS             ;N/2 groups in last pass
    else
    do      b,_last_pass_NS               ;N/2 groups in last pass
    endif

    tfra    r3,r2
    move.w  x:(r3)+,x0                    ;x0=Br,r3 -> Bi

    mpy     x0,y0,b      x:(r0)+,y1       ;b=BrWr, y1=Wi, r0 ->Next Wr
    mpy     y1,x0,a      x:(r3)+,x0       ;a=WiBr
    mac     -y0,x0,a                      ;a=-WrBi+WiBr
    mac     y1,x0,b      x:(r1)+,x0       ;b=WiBi+WrBr, x0=Ar, r1 -> Ai
    add     x0,b                          ;Find Cr
    rnd     b                             ;Round
    asl     x0                            ;Get 2*Ar
    sub     b,x0                          ;Find Dr [Dr = 2*Ar - Cr]
    move.w  b,x:(r1-1)                    ;Store Cr , r1 -> Ai
    move.w  x:(r1)+,b                     ;b = Ai, r1 -> Ai
    sub     b,a          x0,x:(r2)+       ;Find -Ci , store Dr,r2 ->Di
    rnd     a            x:(r3)+n,x0      ;Find -round(Ci), Dummy read to
                                          ;adjust r0 to next Br
    asl     b            x:(r0)+,y0       ;Get 2*Ai
    add     a,b          x:(r1)+n,x0      ;Find Di,Dummy read to
                                          ;adjust r1
    neg     a            b,x:(r2)+        ;Find Ci ,store Di
    move.w  a,x:(r1-3)                    ;Store Ci

_last_pass_NS

_End_fft_NS

PostProcessing

;--------------------------------------------------------------
; Rearrange the output array in bit-reversed order, if required
;--------------------------------------------------------------

    tfra    r5,r2                         ;r2 -> pCFFT.
    brset   #FFT_OUTPUT_IS_BITREVERSED,x:(r2+Offset_options),no_bitrev_output
                                          ;Test if output is bit-reversed.
                                          ;No bit reverse required.

    ;--------------------------------------------------------
    ; Set up the parameters for calling bit-reverse function
    ;--------------------------------------------------------

    move.w  x:(r2+Offset_n),y0            ;y0 = N, the size of FFT.
    tfra    r4,r2                         ;Input pointer
    tfra    r2,r3                         ;Output pointer
    jsr     Fdfr16Cbitrev_                ;Call bit reverse function.
                                          ;Result is returned in y0.
    cmp.w   #FAIL,y0
    bne     no_bitrev_output              ;Bit reverse passed, go to FFT.
    moveu.w old_omr,omr                   ;Restore previous OMR value.
    adda    #-Scratch_Size,sp             ;Restore Stack pointer.
    move.l  x:(sp)-,r5
    move.l  x:(sp)-,d
    move.l  x:(sp)-,d2
    move.l  x:(sp)-,c
    move.l  x:(sp)-,c2
    rts                                   ;FAIL is returned in y0.

no_bitrev_output

;----------------------------------------------------------------
; Return the amount of scaling done in terms of number of shifts
;----------------------------------------------------------------

    tfra    r5,r2
    move.w  ScaleBF,y1
    moveu.w old_omr,omr                   ;Restore previous OMR value.
    adda    #-Scratch_Size,sp             ;Restore Stack pointer.
    move.w  x:(r2+Offset_options),x0
    brclr   #FFT_SCALE_RESULTS_BY_N,x0,_return_BFP
                                          ;Return amount of scaling done
                                          ;in y0, for AS.
    move.w  x:(r2+Offset_No_of_Stages),y0
    move.l  x:(sp)-,r5
    move.l  x:(sp)-,d
    move.l  x:(sp)-,d2
    move.l  x:(sp)-,c
    move.l  x:(sp)-,c2
    rts

_return_BFP

    brclr   #FFT_SCALE_RESULTS_BY_DATA_SIZE,x0,return_NS
    move.w  y1,y0                         ;Return amount of scaling done in
                                          ;BFP method.
    move.l  x:(sp)-,r5
    move.l  x:(sp)-,d
    move.l  x:(sp)-,d2
    move.l  x:(sp)-,c
    move.l  x:(sp)-,c2
    rts

return_NS

    move.w  #0,y0                         ;No scaling is done, return 0.
    move.l  x:(sp)-,r5
    move.l  x:(sp)-,d
    move.l  x:(sp)-,d2
    move.l  x:(sp)-,c
    move.l  x:(sp)-,c2
    rts

    ENDSEC
