// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0

// ----------------------------------------------------------------------------
// Negate modulo p_521, z := (-x) mod p_521, assuming x reduced
// Input x[9]; output z[9]
//
//    extern void bignum_neg_p521 (uint64_t z[static 9], uint64_t x[static 9]);
//
// Standard x86-64 ABI: RDI = z, RSI = x
// Microsoft x64 ABI:   RCX = z, RDX = x
// ----------------------------------------------------------------------------

#include "_internal_s2n_bignum.h"


        S2N_BN_SYM_VISIBILITY_DIRECTIVE(bignum_neg_p521)
        S2N_BN_SYM_PRIVACY_DIRECTIVE(bignum_neg_p521)
        .text

#define z %rdi
#define x %rsi

#define p %rax
#define d0 %rcx
#define d1 %rdx
#define d2 %r8
#define d3 %r9
#define d4 %r10
#define d5 %r11

S2N_BN_SYMBOL(bignum_neg_p521):

#if WINDOWS_ABI
        pushq   %rdi
        pushq   %rsi
        movq    %rcx, %rdi
        movq    %rdx, %rsi
#endif

// Load most inputs (into the limited registers) and OR all of them to get p

        movq    (x), d0
        movq    d0, p
        movq    8(x), d1
        orq     d1, p
        movq    16(x), d2
        orq     d2, p
        movq    24(x), d3
        orq     d3, p
        movq    32(x), d4
        orq     d4, p
        movq    40(x), d5
        orq     d5, p
        orq     48(x), p
        orq     56(x), p
        orq     64(x), p

// Turn p into a bitmask for "input is nonzero", so that we avoid doing
// -0 = p_521 and hence maintain strict modular reduction

        negq    p
        sbbq    p, p

// Since p_521 is all 1s, the subtraction is just an exclusive-or with p
// to give an optional inversion, with a slight fiddle for the top digit.

        xorq    p, d0
        movq    d0, (z)
        xorq    p, d1
        movq    d1, 8(z)
        xorq    p, d2
        movq    d2, 16(z)
        xorq    p, d3
        movq    d3, 24(z)
        xorq    p, d4
        movq    d4, 32(z)
        xorq    p, d5
        movq    d5, 40(z)
        movq    48(x), d0
        xorq    p, d0
        movq    d0, 48(z)
        movq    56(x), d1
        xorq    p, d1
        movq    d1, 56(z)
        movq    64(x), d2
        andq    $0x1FF, p
        xorq    p, d2
        movq    d2, 64(z)

// Return

#if WINDOWS_ABI
        popq   %rsi
        popq   %rdi
#endif
        ret

#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits
#endif
