/*******************************************************************************
Copyright(c) 2000 - 2006 Analog Devices. All Rights Reserved.

By using this module you agree to the terms of the Analog Devices License
Agreement for DSP Software. 
********************************************************************************
Module Name: conv2d5x5_spl.asm
Label name: __conv2d5x5_spl


Description:   For a detailed description, please refer to the file "readme.txt".
               which is included in the main project.

*******************************************************************************/
.section L1_code;
.global __conv2d5x5_spl;
.align 8;
    
__conv2d5x5_spl:
    [--SP] = (R7:6);        // Save R7:6
    
    P0 = 5;                 // Loop counter since there are 5 rows in mask
    P1 = 2;
    P2 = R2;                // P2 = Number of columns
    
    I0 = R0;                // Start address of input matrix.
    B0 = R0;                // Base address of circular buffer
    R7 = R1.L * R2.L (ISS2) || R3 = [SP+20];
                            // Address of Mask
    L0 = R7;                // Circular buffer of length 2 * row * col
    
    R2 = R2 << 1 || R6 = [SP+24];
                            // 2 * col , Address of output matrix
    M3 = R2;                // 2 * col
    L1 = R2;                // Length register is initialized to 2 * col
    
    I2 = R3;                // Starting address of  mask
    B2 = R3;                // Base address of circular buffer
    L2 = 50;                // Length of mask = 50 (25 * 2)
    
    I3 = R6;                // Address of output buffer.
    B3 = R6;                // Base address of circular buffer
    L3 = R7;                // Circular buffer of length 2 * row * col
    
    P2 += -4;               // Col - 4
    
    R0 = R2 << 2;              
    R6 = R0 + R2(S) || NOP;
    M2 = R6;                // 10 * col
    
    R0 = R2;
    R0 += -8;
    M0 = R0;                // 2*COL - 8
    M1 = 8;
    
    R0 = R0 - R0(S) || I0 += M3 || R3.L = W[I2--];
                            // R0 = 0, Modify I0, Make I2 point to end of mask
    
    A1 = A0 = 0 || I0 -= M2 || R3.L = W[I2--];
                            // Accumulator reset, Fetch first element(h0) 
                            // from mask
                            // Modify I0 so that it points to last but second 
                            // row of input
    
LOOP_FS_ROW:
    LSETUP (COL_FS_ST, COL_FS_END) LC0 = P1; 
                            // Loop to find all output elements in one 
                            // row(counter = col)
COL_FS_ST:
        B1 = I0;            // B1 stores the modified address on rows.
        I1 = B1;            // Address is copied to I1
        I1 -= M1;           // Column offset is added
        LSETUP (CONV_FS_ST, CONV_FS_END) LC1 = P0;
                            // Loop for finding one output(convoluting)
CONV_FS_ST: R1 = [I1++] || I0 += M3;
                            // Fetch x0,x1 
            A0 += R1.L * R3.L ,A1 += R1.H * R3.L || R1.L = W[I1++] 
            ||  R3.H = W[I2--]; 
                            // A0 += x0*h0, A1 += x1*h0,  x2 ,  h1
            A0 += R1.H * R3.H, A1 += R1.L * R3.H || R1.H = W[I1++] 
            ||  R3.L = W[I2--]; 
                            // A0 += x1*h1, A1 += x2*h1, x3  ,  h2
            A0 += R1.L * R3.L, A1 += R1.H * R3.L || R1.L = W[I1++] 
            ||  R3.H = W[I2--]; 
                            // A0 += x2*h2, A1 += x3*h2,  x4 ,  h3
            A0 += R1.H * R3.H, A1 += R1.L * R3.H || R1.H = W[I1++]
            ||  R3.L = W[I2--]; 
                            // A0 += x3*h3, A1 += x4*h3,  x4,x5 , h4
            B1 = I0;        // B1 stores the modified address on rows.
            I1 = B1;        // Address is copied to I1
CONV_FS_END:R1.L=(A0+=R1.L * R3.L),R1.H=(A1+=R1.H * R3.L) || I1 -= M1 
            || R3.L = W[I2--]; 
                            // A0 += x4*h4, A1 += x5*h4,  Add column offset, h10
        A1 = A0 = 0 || I0 -= M2 || [I3++] = R1;
                            // Modify I0, Store the output
COL_FS_END:
        M1 = 4;             // Column offset for 3rd and 4th columns
    
    M1 = 8;                 // Column offset for Ist two columns
    R0 = R0 + R2(S) || I3 += M0 || R1 = [I0++M3];
                            // The counter for LOOP_ROW is modified, I0 and I3 
                            // are modified
    CC = R0 < R7;
    If CC JUMP LOOP_FS_ROW (BP);
                            // Jump if all input rows are not over 
    
    MNOP || I3 += M1;       // Modify I3 as the first 4 columns are already 
                            // stored
   
LOOP_ROW:

            LSETUP (COL_ST, COL_END) LC0 = P2 >> 1; 
                            // Loop to find all output elements in one 
                            // row(counter = col)
COL_ST: MNOP || R1 = [I0++];// Fetch x0,x1
        LSETUP (CONV_ST, CONV_END) LC1 = P0;
                            // Loop for finding one output(convoluting)
CONV_ST:    A0 += R1.L * R3.L ,A1 += R1.H * R3.L || R1.L = W[I0++] 
            ||  R3.H = W[I2--]; 
                            // A0 += x0*h0, A1 += x1*h0,  x2  , h1
            A0 += R1.H * R3.H, A1 += R1.L * R3.H || R1.H = W[I0++] 
            ||  R3.L = W[I2--]; 
                            // A0 += x1*h1, A1 += x2*h1, x3  ,  h2
            A0 += R1.L * R3.L, A1 += R1.H * R3.L || R1.L = W[I0] 
            ||  R3.H = W[I2--]; 
                            // A0 += x2*h2, A1 += x3*h2,  x4  , h3
            A0 += R1.H * R3.H, A1 += R1.L * R3.H || R1 = [I0++M0] 
            ||  R3.L = W[I2--]; 
                            // A0 += x3*h3, A1 += x4*h3,  x4,x5 , h4
CONV_END:   R6.L=(A0+=R1.L * R3.L),R6.H=(A1+=R1.H * R3.L) || R1 = [I0++] 
            ||  R3.L = W[I2--]; 
                            // A0 += x4*h4, A1 += x5*h4,  x6,x7  , h10
COL_END:
           A1 = A0 = 0 || I0 -= M2 || [I3++] = R6;

                            // Modify I0, Store the output
         

    R0 = R0 - R2(S) || R6 = [I0++M1] || I3 += M1;        
                            // The counter for LOOP_ROW is modified, I0 and I3 
                            // are modified
    CC = R0 == 0;
    If !CC JUMP LOOP_ROW (BP);
                            // Jump if all input rows are not over 

    (R7:6) = [SP++];        // Restore R7:6
    RTS;                 
    NOP;                    //to avoid one stall if LINK or UNLINK happens to be
                            //the next instruction after RTS in the memory.
__conv2d5x5_spl.end:
