/*============================================================================*/
/* Private definitions                                                        */
/*============================================================================*/

//#define KARATSUBA

/*
 * Pointer register which points to the parameter a.
 */
#define A_PTR       r14

/*
 * Pointer register which points to the parameter b.
 */
#define B_PTR       r13

/*
 * Pointer register which points to the parameter c.
 */
#define C_PTR       r15

/*
 * Temporary register.
 */
#define RT          r4

/*
 * Pointer to the table.
 */
#define T_PTR       r12

/*
 * Size in bits of the multiplication of a binary field element by a polynomial
 * with degree lower than 4.
 */
#define POLY_M4     (FB_POLYN + 4)

/*
 * Size in digits of a precomputation table line.
 */
#define T_LINE 16

/*
 * Size of the multiplication precomputation table in digits.
 */
#define T_DIGS  (16 * T_LINE)

.macro PROLOGUE
    push r4
    push r5
    push r6
    push r7
    push r8
    push r9
    push r10
    push r11
.endm

.macro RLC_MULN_TABLE
    mov     r1, T_PTR
    RLC_FILL_TABLE 0, 10 /* fill 11 columns */
.endm

.macro RLC_MULN_TABLE_KARATSUBA
    mov     r1, T_PTR
    RLC_FILL_TABLE 0, 8 /* fill 9 columns */
    PREP_LAST   r6, r7, r8, r9 /* fill last column */
    FILL_COLUMN r6, r7, r8, r9
    sub #2*T_LINE, T_PTR /* restore T_PTR */
    sub #2*T_LINE, B_PTR /* restore B_PTR */
.endm

.macro RLC_FILL_TABLE i, j
    .if \i == 0
        PREP_FIRST  r6, r7, r8, r9
        FILL_COLUMN r6, r7, r8, r9
    .else
        PREP_COLUMN \i + \i, r6, r7, r8, r9
        FILL_COLUMN r6, r7, r8, r9
    .endif
    incd T_PTR
    .if \i < \j
        RLC_FILL_TABLE \i + 1, \j
    .endif
.endm

/*
 * Prepares the first column values.
 */
.macro PREP_FIRST r1, r2, r4, r8
    mov     @B_PTR+, \r1    ; r1 = b[0]
    mov     \r1, \r2
    rla     \r2             ; r2 = r1 << 1
    mov     \r2, \r4
    rla     \r4             ; r4 = r1 << 2
    mov     \r4, \r8
    rla     \r8             ; r8 = r1 << 3
.endm

/*
 * Prepares the values for column i.
 */
.macro PREP_COLUMN i, r1, r2, r4, r8
    mov     \r1, RT

    mov     @B_PTR+, \r1
    mov     \r1, \r2
    rla     RT
    rlc     \r2
    mov     \r2, \r4
    rla     RT
    rlc     \r4
    mov     \r4, \r8
    rla     RT
    rlc     \r8
.endm

/*
 * Prepares the values for the last column.
 */
.macro PREP_LAST r1, r2, r4, r8
    mov     \r1, RT

    clr     \r1
    mov     \r1, \r2
    rla     RT
    rlc     \r2
    mov     \r2, \r4
    rla     RT
    rlc     \r4
    mov     \r4, \r8
    rla     RT
    rlc     \r8
.endm

/*
 * Fills a column of the precomputation table.
 */
.macro FILL_COLUMN r1, r2, r4, r8
    clr     0(T_PTR)
    mov     \r1, 1*2*T_LINE(T_PTR)  ; tab[1][i] = r1
    mov     \r2, 2*2*T_LINE(T_PTR)  ; tab[2][i] = r2
    mov     \r1, RT
    xor     \r2, RT
    mov     RT, 3*2*T_LINE(T_PTR)   ; tab[3][i] = r1^r2
    mov     \r4, 4*2*T_LINE(T_PTR)  ; tab[4][i] = r4
    xor     \r4, RT
    mov     RT, 7*2*T_LINE(T_PTR)   ; tab[7][i] = r1^r2^r4
    xor     \r1, RT
    mov     RT, 6*2*T_LINE(T_PTR)   ; tab[6][i] = r2^r4
    mov     \r1, RT
    xor     \r4, RT
    mov     RT, 5*2*T_LINE(T_PTR)   ; tab[5][i] = r1^r4
    mov     \r8, 8*2*T_LINE(T_PTR)  ; tab[8][i] = r8
    mov     \r1, RT
    xor     \r8, RT
    mov     RT, 9*2*T_LINE(T_PTR)   ; tab[9][i] = r1^r8
    xor     \r2, RT
    mov     RT, 11*2*T_LINE(T_PTR)  ; tab[11][i] = r1^r2^r8
    xor     \r1, RT
    mov     RT, 10*2*T_LINE(T_PTR)  ; tab[10][i] = r2^r8
    xor     \r8, \r4
    mov     \r4, 12*2*T_LINE(T_PTR); tab[12][i] = r4^r8
    xor     \r1, \r4
    mov     \r4, 13*2*T_LINE(T_PTR); tab[13][i] = r1^r4^r8
    xor     \r2, \r4
    mov     \r4, 15*2*T_LINE(T_PTR); tab[15][i] = r1^r2^r4^r8
    xor     \r1, \r4
    mov     \r4, 14*2*T_LINE(T_PTR); tab[14][i] = r2^r4^r8
.endm

.macro FILL_LAST r1, r2, r4, r8
    /*
     * Since the last bit of the 17th digit of a number is 0, then
     * r1 and r2 for the 18th digit are zero.
     */
    clr     @T_PTR
    clr     1*2*T_LINE(T_PTR)  ; tab[1][i] = r1
    clr     2*2*T_LINE(T_PTR)  ; tab[2][i] = r2
    clr     3*2*T_LINE(T_PTR)   ; tab[3][i] = r1^r2
    mov     \r4, 4*2*T_LINE(T_PTR)  ; tab[4][i] = r4
    mov     \r4, 7*2*T_LINE(T_PTR)   ; tab[7][i] = r1^r2^r4
    mov     \r4, 6*2*T_LINE(T_PTR)   ; tab[6][i] = r2^r4
    mov     \r4, 5*2*T_LINE(T_PTR)   ; tab[5][i] = r1^r4
    mov     \r8, 8*2*T_LINE(T_PTR)  ; tab[8][i] = r8
    mov     \r8, 9*2*T_LINE(T_PTR)   ; tab[9][i] = r1^r8
    mov     \r8, 11*2*T_LINE(T_PTR)  ; tab[11][i] = r1^r2^r8
    mov     \r8, 10*2*T_LINE(T_PTR)  ; tab[10][i] = r2^r8
    xor     \r8, \r4
    mov     \r4, 12*2*T_LINE(T_PTR); tab[12][i] = r4^r8
    mov     \r4, 13*2*T_LINE(T_PTR); tab[13][i] = r1^r4^r8
    mov     \r4, 15*2*T_LINE(T_PTR); tab[15][i] = r1^r2^r4^r8
    mov     \r4, 14*2*T_LINE(T_PTR); tab[14][i] = r2^r4^r8
.endm

.macro EPILOGUE
    pop r11
    pop r10
    pop r9
    pop r8
    pop r7
    pop r6
    pop r5
    pop r4
    ret
.endm

.text
.align 2

.global fb_muln_low

#ifndef KARATSUBA

fb_muln_low:
    /* r15: c
       r14: a
       r13: b
    */
    /* pushes */
    PROLOGUE

    sub #512,r1

    RLC_MULN_TABLE

#include "fb_mul_163_ld.inc"

    add #512,r1

    /* pops */
    EPILOGUE

#else

#define TEMP_SIZE 72
#define RLC_TABLE_SIZE 512

fb_muln_low:
    /* r15: c
       r14: a
       r13: b
    */
    //pushes
    PROLOGUE

    clr 2*17(r14)
    clr 2*17(r13)

    //allocate temp area
    sub #TEMP_SIZE,r1


    //compute t0 = a0 + a1
    mov r14,r11
    add #2*9,r11
    .irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8
        mov @r14+,r12
        xor @r11+,r12
        mov r12,(2 * \i)(r1)
    .endr

    //compute t1 = b0 + b1
    mov r13,r11
    add #2*9,r11
    .irp i, 9, 10, 11, 12, 13, 14, 15, 16, 17
        mov @r13+,r12
        xor @r11+,r12
        mov r12,(2 * \i)(r1)
    .endr

    //restore pointers
    sub #2*9,r14
    sub #2*9,r13

    //compute c01 = a0b0
    push r13
    call #fb_muln_low_karatsuba

    //compute c23 = a1b1
    add #2*9,r14
    mov @r1,r13
    add #2*9,r13
    add #2*18,r15
    call #fb_muln_low_karatsuba
    //"pop r13"
    add #2,r1
    //restore and push c
    sub #2*18,r15
    push r15
    //compute t23 = t0t1
    mov r1,r15
    mov r1,r14
    mov r1,r13
    add #2*18+2,r15
    add #2,r14
    add #2*9+2,r13
    call #fb_muln_low_karatsuba

    //pop c
    pop r15

    //r14 = &t23
    mov r1,r14
    add #2*18,r14

    //compute t23 += c01 (a0b0)
    .irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
        xor @r15+,(2 * \i)(r14)
    .endr

    //compute t23 += c23 (a1b1)
    .irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
        xor @r15+,(2 * \i)(r14)
    .endr

    //set r15 from &c4 to &c1
    sub #2*3*9,r15

    //compute c12 += t23
    .irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
        xor @r14+,(2 * \i)(r15)
    .endr

    add #TEMP_SIZE,r1
    //pops
    EPILOGUE

fb_muln_low_karatsuba:

    sub #RLC_TABLE_SIZE,r1

    RLC_MULN_TABLE_KARATSUBA

#include "ld_mult_271_karatsuba.inc"

    add #RLC_TABLE_SIZE,r1

    ret

#endif
