<% function mulS1S2() { %>
        xor    rax, rax
        mov    eax, r8d
        imul   r9d
        jo     mul_manageOverflow   ; rsi already is the 64bits result

        mov    [rdi], rax       ; not necessary to adjust so just save and return

mul_manageOverflow:                 ; Do the operation in 64 bits
        push   rsi
        movsx  rax, r8d
        movsx  rcx, r9d
        imul   rcx
        mov    rsi, rax
        call   rawCopyS2L
        pop    rsi
<% } %>

<% function squareS1() { %>
        xor    rax, rax
        mov    eax, r8d
        imul   eax
        jo     square_manageOverflow   ; rsi already is the 64bits result

        mov    [rdi], rax       ; not necessary to adjust so just save and return

square_manageOverflow:                 ; Do the operation in 64 bits
        push   rsi
        movsx  rax, r8d
        imul   rax
        mov    rsi, rax
        call   rawCopyS2L
        pop    rsi
<% } %>


<% function mulL1S2(t) { %>
        push rsi
        add rsi, 8
        movsx rdx, r9d
        add rdi, 8
        cmp rdx, 0
        <% const rawPositiveLabel = global.tmpLabel() %>
        jns <%= rawPositiveLabel %>
        neg rdx
        call rawMontgomeryMul1
        mov rsi, rdi
        call rawNegL
        sub rdi, 8
        pop rsi
        <% const done = global.tmpLabel() %>
        jmp <%= done %>
<%= rawPositiveLabel %>:
        call rawMontgomeryMul1
        sub rdi, 8
        pop rsi
<%= done %>:

<% } %>

<% function mulS1L2() { %>
        push rsi
        lea rsi, [rdx + 8]
        movsx rdx, r8d
        add rdi, 8
        cmp rdx, 0
        <% const rawPositiveLabel = global.tmpLabel() %>
        jns <%= rawPositiveLabel %>
        neg rdx
        call rawMontgomeryMul1
        mov rsi, rdi
        call rawNegL
        sub rdi, 8
        pop rsi
        <% const done = global.tmpLabel() %>
        jmp <%= done %>
<%= rawPositiveLabel %>:
        call rawMontgomeryMul1
        sub rdi, 8
        pop rsi
<%= done %>:

<% } %>

<% function mulL1L2() { %>
        add rdi, 8
        add rsi, 8
        add rdx, 8
        call rawMontgomeryMul
        sub rdi, 8
        sub rsi, 8
<% } %>


<% function squareL1() { %>
        add rdi, 8
        add rsi, 8
        call rawMontgomerySquare
        sub rdi, 8
        sub rsi, 8
<% } %>

<% function mulR3() { %>
        push rsi
        add rdi, 8
        mov rsi, rdi
        lea rdx, [R3]
        call rawMontgomeryMul
        sub rdi, 8
        pop rsi
<% } %>



;;;;;;;;;;;;;;;;;;;;;;
; square
;;;;;;;;;;;;;;;;;;;;;;
; Squares a field element
; Params:
;   rsi <= Pointer to element 1
;   rdi <= Pointer to result
;   [rdi] = [rsi] * [rsi]
; Modified Registers:
;    r8, r9, 10, r11, rax, rcx
;;;;;;;;;;;;;;;;;;;;;;
<%=name%>_square:
        mov    r8, [rsi]
        bt     r8, 63          ; Check if is short first operand
        jc     square_l1

square_s1:                       ; Both operands are short
<%=     squareS1()       %>
        ret

square_l1:
        bt     r8, 62          ; check if montgomery first
        jc     square_l1m
square_l1n:
<%=     global.setTypeDest("0xC0"); %>
<%=     squareL1()         %>
<%=     mulR3()         %>
        ret

square_l1m:
<%=     global.setTypeDest("0xC0"); %>
<%=     squareL1()         %>
        ret



;;;;;;;;;;;;;;;;;;;;;;
; mul
;;;;;;;;;;;;;;;;;;;;;;
; Multiplies two elements of any kind
; Params:
;   rsi <= Pointer to element 1
;   rdx <= Pointer to element 2
;   rdi <= Pointer to result
;   [rdi] = [rsi] * [rdi]
; Modified Registers:
;    r8, r9, 10, r11, rax, rcx
;;;;;;;;;;;;;;;;;;;;;;
<%=name%>_mul:
        mov    r8, [rsi]
        mov    r9, [rdx]
        bt     r8, 63          ; Check if is short first operand
        jc     mul_l1
        bt     r9, 63          ; Check if is short second operand
        jc     mul_s1l2

mul_s1s2:                       ; Both operands are short
<%=     mulS1S2()       %>
        ret

mul_l1:
        bt     r9, 63          ; Check if is short second operand
        jc     mul_l1l2

;;;;;;;;
mul_l1s2:
        bt     r8, 62          ; check if montgomery first
        jc     mul_l1ms2
mul_l1ns2:
        bt     r9, 62          ; check if montgomery first
        jc     mul_l1ns2m
mul_l1ns2n:
<%=     global.setTypeDest("0xC0"); %>
<%=     mulL1S2()       %>
<%=     mulR3()         %>
        ret


mul_l1ns2m:
<%=     global.setTypeDest("0x80"); %>
<%=     mulL1L2()       %>
        ret


mul_l1ms2:
        bt     r9, 62          ; check if montgomery second
        jc     mul_l1ms2m
mul_l1ms2n:
<%=     global.setTypeDest("0x80"); %>
<%=     mulL1S2()       %>
        ret

mul_l1ms2m:
<%=     global.setTypeDest("0xC0"); %>
<%=     mulL1L2()         %>
        ret


;;;;;;;;
mul_s1l2:
        bt     r8, 62          ; check if montgomery first
        jc     mul_s1ml2
mul_s1nl2:
        bt     r9, 62          ; check if montgomery first
        jc     mul_s1nl2m
mul_s1nl2n:
<%=     global.setTypeDest("0xC0"); %>
<%=     mulS1L2()       %>
<%=     mulR3()         %>
        ret

mul_s1nl2m:
<%=     global.setTypeDest("0x80"); %>
<%=     mulS1L2(); %>
        ret

mul_s1ml2:
        bt     r9, 62          ; check if montgomery first
        jc     mul_s1ml2m
mul_s1ml2n:
<%=     global.setTypeDest("0x80"); %>
<%=     mulL1L2()         %>
        ret

mul_s1ml2m:
<%=     global.setTypeDest("0xC0"); %>
<%=     mulL1L2()         %>
        ret

;;;;
mul_l1l2:
        bt     r8, 62          ; check if montgomery first
        jc     mul_l1ml2
mul_l1nl2:
        bt     r9, 62          ; check if montgomery second
        jc     mul_l1nl2m
mul_l1nl2n:
<%=     global.setTypeDest("0xC0"); %>
<%=     mulL1L2()         %>
<%=     mulR3()         %>
        ret

mul_l1nl2m:
<%=     global.setTypeDest("0x80"); %>
<%=     mulL1L2()         %>
        ret

mul_l1ml2:
        bt     r9, 62          ; check if montgomery seconf
        jc     mul_l1ml2m
mul_l1ml2n:
<%=     global.setTypeDest("0x80"); %>
<%=     mulL1L2()         %>
        ret

mul_l1ml2m:
<%=     global.setTypeDest("0xC0"); %>
<%=     mulL1L2()         %>
        ret


