/*
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef _PICO_RCP_H
#define _PICO_RCP_H

#include "pico.h"

/** \file hardware/rcp.h
 * \defgroup hardware_rcp hardware_rcp
 * \brief Inline functions and assembly macros for the Redundancy Coprocessor
 */

// ----------------------------------------------------------------------------
// RCP instructions (this header is Arm-only)
#if HAS_REDUNDANCY_COPROCESSOR
#ifdef __riscv
#error "HAS_REDUNDANCY_COPROCESSOR should be false on RISC-V"
#endif

#define rcp_is_true(x) ((x) == RCP_MASK_TRUE)
#define RCP_MASK_TRUE   _u(0xa500a500)
#define RCP_MASK_FALSE  _u(0x00c300c3)
#define RCP_MASK_INTXOR _u(0x96009600)

// ----------------------------------------------------------------------------
// Macros and inline functions for use in C files
#ifndef __ASSEMBLER__

#define __rcpinline __force_inline

#ifdef __cplusplus
extern "C" {
#endif

#ifndef __riscv // should never do an rcp_asm in risc-v code
#if __ARM_ARCH_8M_MAIN__
#define rcp_asm pico_default_asm_volatile
#elif __ARM_ARCH_8M_BASE__
#define rcp_asm(...) ({pico_default_asm_volatile(".cpu cortex-m33\n" __VA_ARGS__); pico_default_asm_volatile(".cpu cortex-m23"); })
#elif __ARM_ARCH_6M__
#define rcp_asm(...) ({pico_default_asm_volatile(".cpu cortex-m33\n" __VA_ARGS__); pico_default_asm_volatile(".cpu cortex-m0plus"); })
#else
#error unknown ARM architecture
#endif

// Each macro has a default version (e.g. rcp_salt_core0) and a no-delay version
// (e.g. rcp_salt_core0_nodelay). The default version adds an up to 127-cycle
// pseudorandom delay on each coprocessor instruction, whereas the no-delay version
// does not have this delay. The difference in the generated code is whether an MCR
// (e.g.) or MCR2 opcode is emitted.

// ----------------------------------------------------------------------------
// RCP Canary instructions

// Initialise 64-bit salt value for core 0
static __rcpinline void rcp_salt_core0(uint64_t salt) {
    rcp_asm (
            "mcrr p7, #8, %0, %1, c0\n"
            : : "r" (salt & 0xffffffffu), "r" (salt >> 32)
            );
}

static __rcpinline void rcp_salt_core0_nodelay(uint64_t salt) {
    rcp_asm (
            "mcrr2 p7, #8, %0, %1, c0\n"
            : : "r" (salt & 0xffffffffu), "r" (salt >> 32)
            );
}

// Initialise 64-bit salt value for core 1
static __rcpinline void rcp_salt_core1(uint64_t salt) {
    rcp_asm (
            "mcrr p7, #8, %0, %1, c1\n"
            : : "r" (salt & 0xffffffffu), "r" (salt >> 32)
            );
}

static __rcpinline void rcp_salt_core1_nodelay(uint64_t salt) {
    rcp_asm (
            "mcrr2 p7, #8, %0, %1, c1\n"
            : : "r" (salt & 0xffffffffu), "r" (salt >> 32)
            );
}

// Get a 32-bit canary value. `tag` must be a constant expression.
#define rcp_canary_get(tag) ({ \
    static_assert(!((tag) & ~0xffu), "Tag out of range"); \
    uint32_t __canary_u32; \
    rcp_asm ( \
        "mrc p7, #0, %0, c%c1, c%c2, #1\n" \
        : "=r" (__canary_u32) \
        : "i" ((tag >> 4) & 0xf), "i" (tag & 0xf) \
    ); \
    __canary_u32; \
})

#define rcp_canary_get_nodelay(tag) ({ \
    static_assert(!((tag) & ~0xffu), "Tag out of range"); \
    uint32_t __canary_u32; \
    rcp_asm ( \
        "mrc2 p7, #0, %0, c%c1, c%c2, #1\n" \
        : "=r" (__canary_u32) \
        : "i" (((tag) >> 4) & 0xf), "i" ((tag) & 0xf) \
    ); \
    __canary_u32; \
})

// Assert that canary matches result of rcp_canary_get with the same tags:
#define rcp_canary_check(tag, canary) ({ \
    static_assert(!((tag) & ~0xffu), "Tag out of range"); \
    rcp_asm ( \
        "mcr p7, #0, %0, c%c1, c%c2, #1\n" \
        : : "r" (canary), \
        "i" (((tag) >> 4) & 0xf), "i" ((tag) & 0xf) \
    ); \
})

#define rcp_canary_check_nodelay(tag, canary) ({ \
    static_assert(!((tag) & ~0xffu), "Tag out of range"); \
    rcp_asm ( \
        "mcr2 p7, #0, %0, c%c1, c%c2, #1\n" \
        : : "r" (canary), \
        "i" (((tag) >> 4) & 0xf), "i" ((tag) & 0xf) \
    ); \
})

// Return true/false bit pattern for whether the salt for this core has been
// initialised. (Invoking with Rt=0xf will set the Arm N flag if initialised.)
// If the salt has not been initialised then any operation other than
// initialising the salt or checking the canary status is a hard error.
static __rcpinline uint32_t rcp_canary_status(void) {
    uint32_t ret;
    rcp_asm ("mrc p7, #1, %0, c0, c0, #0\n" : "=r" (ret));
    return ret;
}

static __rcpinline uint32_t rcp_canary_status_nodelay(void) {
    uint32_t ret;
    rcp_asm ("mrc2 p7, #1, %0, c0, c0, #0\n" : "=r" (ret));
    return ret;
}

// ----------------------------------------------------------------------------
// RCP Boolean instructions

// These instructions assert that all their arguments are valid booleans, in
// addition to any other logical conditions

// Assert b is a valid boolean (0xa500a500u or 0x00c300c3u)
static __rcpinline void rcp_bvalid(uint32_t b) {
    rcp_asm ("mcr p7, #1, %0, c0, c0, #0\n" : : "r" (b));
}

static __rcpinline void rcp_bvalid_nodelay(uint32_t b) {
    rcp_asm ("mcr2 p7, #1, %0, c0, c0, #0\n" : : "r" (b));
}

// Assert b is true (0xa500a500u)
static __rcpinline void rcp_btrue(uint32_t b) {
    rcp_asm ("mcr p7, #2, %0, c0, c0, #0\n" : : "r" (b));
}

static __rcpinline void rcp_btrue_nodelay(uint32_t b) {
    rcp_asm ("mcr2 p7, #2, %0, c0, c0, #0\n" : : "r" (b));
}

// Assert b is false (0x00c300c3u)
static __rcpinline void rcp_bfalse(uint32_t b) {
    rcp_asm ("mcr p7, #3, %0, c0, c0, #1\n" : : "r" (b));
}

static __rcpinline void rcp_bfalse_nodelay(uint32_t b) {
    rcp_asm ("mcr2 p7, #3, %0, c0, c0, #1\n" : : "r" (b));
}

// Assert b0 and b1 are both valid booleans
static __rcpinline void rcp_b2valid(uint32_t b0, uint32_t b1) {
    rcp_asm ("mcrr p7, #0, %0, %1, c8\n" : : "r" (b0), "r" (b1));
}

static __rcpinline void rcp_b2valid_nodelay(uint32_t b0, uint32_t b1) {
    rcp_asm ("mcrr2 p7, #0, %0, %1, c8\n" : : "r" (b0), "r" (b1));
}

// Assert b0 and b1 are both true
static __rcpinline void rcp_b2and(uint32_t b0, uint32_t b1) {
    rcp_asm ("mcrr p7, #1, %0, %1, c0\n" : : "r" (b0), "r" (b1));
}

static __rcpinline void rcp_b2and_nodelay(uint32_t b0, uint32_t b1) {
    rcp_asm ("mcrr2 p7, #1, %0, %1, c0\n" : : "r" (b0), "r" (b1));
}

// Assert b0 and b1 are valid, and at least one is true
static __rcpinline void rcp_b2or(uint32_t b0, uint32_t b1) {
    rcp_asm ("mcrr p7, #2, %0, %1, c0\n" : : "r" (b0), "r" (b1));
}

static __rcpinline void rcp_b2or_nodelay(uint32_t b0, uint32_t b1) {
    rcp_asm ("mcrr2 p7, #2, %0, %1, c0\n" : : "r" (b0), "r" (b1));
}

// Assert (b ^ mask) is a valid boolean
static __rcpinline void rcp_bxorvalid(uint32_t b, uint32_t mask) {
    rcp_asm ("mcrr p7, #3, %0, %1, c8\n" : : "r" (b), "r" (mask));
}

static __rcpinline void rcp_bxorvalid_nodelay(uint32_t b, uint32_t mask) {
    rcp_asm ("mcrr2 p7, #3, %0, %1, c8\n" : : "r" (b), "r" (mask));
}

// Assert (b ^ mask) is true
static __rcpinline void rcp_bxortrue(uint32_t b, uint32_t mask) {
    rcp_asm ("mcrr p7, #4, %0, %1, c0\n" : : "r" (b), "r" (mask));
}

static __rcpinline void rcp_bxortrue_nodelay(uint32_t b, uint32_t mask) {
    rcp_asm ("mcrr2 p7, #4, %0, %1, c0\n" : : "r" (b), "r" (mask));
}

// Assert (b ^ mask) is false
static __rcpinline void rcp_bxorfalse(uint32_t b, uint32_t mask) {
    rcp_asm ("mcrr p7, #5, %0, %1, c8\n" : : "r" (b), "r" (mask));
}

static __rcpinline void rcp_bxorfalse_nodelay(uint32_t b, uint32_t mask) {
    rcp_asm ("mcrr2 p7, #5, %0, %1, c8\n" : : "r" (b), "r" (mask));
}

// ----------------------------------------------------------------------------
// RCP Integer instructions

// Assert (x ^ parity) == 0x96009600u
static __rcpinline void rcp_ivalid(uint32_t x, uint32_t parity) {
    rcp_asm ("mcrr p7, #6, %0, %1, c8\n" : : "r" (x), "r" (parity));
}

static __rcpinline void rcp_ivalid_nodelay(uint32_t x, uint32_t parity) {
    rcp_asm ("mcrr2 p7, #6, %0, %1, c8\n" : : "r" (x), "r" (parity));
}

// Assert x == y
static __rcpinline void rcp_iequal(uint32_t x, uint32_t y) {
    rcp_asm ("mcrr p7, #7, %0, %1, c0\n" : : "r" (x), "r" (y));
}

static __rcpinline void rcp_iequal_nodelay(uint32_t x, uint32_t y) {
    rcp_asm ("mcrr2 p7, #7, %0, %1, c0\n" : : "r" (x), "r" (y));
}

// ----------------------------------------------------------------------------
// RCP Random instructions

// Return a random 8-bit value generated from the upper 24 bits of the 64-bit
// salt value. This is the same PRNG used for random delay values.
static __rcpinline uint8_t rcp_random_byte(void) {
    uint8_t ret;
    rcp_asm ("mrc2 p7, #2, %0, c0, c0, #0\n" : "=r" (ret));
    return ret;
}

// Note neither version of this has delay, since the PRNG for the random delay
// is the same as the one read by this MRC instruction (and it's only stepped
// once).
static __rcpinline uint8_t rcp_random_byte_nodelay(void) {
    uint8_t ret;
    rcp_asm ("mrc2 p7, #2, %0, c0, c0, #0\n" : "=r" (ret));
    return ret;
}

// ----------------------------------------------------------------------------
// RCP Sequence count instructions

// Directly write value cnt to the sequence counter.
// cnt must be a constant expression.
#define rcp_count_set(cnt) rcp_asm ( \
    "mcr p7, #4, r0, c%c0, c%c1, #0\n" \
    : : "i"(((cnt) >> 4) & 0xf), "i"((cnt) & 0xf) \
);

#define rcp_count_set_nodelay(cnt) rcp_asm ( \
    "mcr2 p7, #4, r0, c%c0, c%c1, #0\n" \
    : : "i"(((cnt) >> 4) & 0xf), "i"((cnt) & 0xf) \
);

// Check value cnt against the sequence counter, then increment the counter.
// cnt must be a constant expression.
#define rcp_count_check(cnt) rcp_asm ( \
    "mcr p7, #5, r0, c%c0, c%c1, #1\n" \
    : : "i"(((cnt) >> 4) & 0xf), "i"((cnt) & 0xf) \
);

#define rcp_count_check_nodelay(cnt) rcp_asm ( \
    "mcr2 p7, #5, r0, c%c0, c%c1, #1\n" \
    : : "i"(((cnt) >> 4) & 0xf), "i"((cnt) & 0xf) \
);

// ----------------------------------------------------------------------------
// RCP Panic instructions

// Stall the coprocessor port. If the coprocessor access goes away, assert NMI

static __rcpinline __attribute__((noreturn)) void rcp_panic(void) {
    rcp_asm("cdp p7, #0, c0, c0, c0, #1");
    __builtin_unreachable();
}

// There is no nodelay version.

#endif // !__riscv
// ----------------------------------------------------------------------------
// GAS macros for RCP instructions, for direct use in ASM files
#else // __ASSEMBLER__
#ifndef __riscv

// Meta-macro for evaluating assembler constant expressions and encoding as
// coprocessor register pairs:
.macro rcp_switch_u8_to_ch_cl macro_name, x, args:vararg
.if (\x) == 0
\macro_name  c0,  c0, \args
.elseif (\x) == 1
\macro_name  c0,  c1, \args
.elseif (\x) == 2
\macro_name  c0,  c2, \args
.elseif (\x) == 3
\macro_name  c0,  c3, \args
.elseif (\x) == 4
\macro_name  c0,  c4, \args
.elseif (\x) == 5
\macro_name  c0,  c5, \args
.elseif (\x) == 6
\macro_name  c0,  c6, \args
.elseif (\x) == 7
\macro_name  c0,  c7, \args
.elseif (\x) == 8
\macro_name  c0,  c8, \args
.elseif (\x) == 9
\macro_name  c0,  c9, \args
.elseif (\x) == 10
\macro_name  c0, c10, \args
.elseif (\x) == 11
\macro_name  c0, c11, \args
.elseif (\x) == 12
\macro_name  c0, c12, \args
.elseif (\x) == 13
\macro_name  c0, c13, \args
.elseif (\x) == 14
\macro_name  c0, c14, \args
.elseif (\x) == 15
\macro_name  c0, c15, \args
.elseif (\x) == 16
\macro_name  c1,  c0, \args
.elseif (\x) == 17
\macro_name  c1,  c1, \args
.elseif (\x) == 18
\macro_name  c1,  c2, \args
.elseif (\x) == 19
\macro_name  c1,  c3, \args
.elseif (\x) == 20
\macro_name  c1,  c4, \args
.elseif (\x) == 21
\macro_name  c1,  c5, \args
.elseif (\x) == 22
\macro_name  c1,  c6, \args
.elseif (\x) == 23
\macro_name  c1,  c7, \args
.elseif (\x) == 24
\macro_name  c1,  c8, \args
.elseif (\x) == 25
\macro_name  c1,  c9, \args
.elseif (\x) == 26
\macro_name  c1, c10, \args
.elseif (\x) == 27
\macro_name  c1, c11, \args
.elseif (\x) == 28
\macro_name  c1, c12, \args
.elseif (\x) == 29
\macro_name  c1, c13, \args
.elseif (\x) == 30
\macro_name  c1, c14, \args
.elseif (\x) == 31
\macro_name  c1, c15, \args
.elseif (\x) == 32
\macro_name  c2,  c0, \args
.elseif (\x) == 33
\macro_name  c2,  c1, \args
.elseif (\x) == 34
\macro_name  c2,  c2, \args
.elseif (\x) == 35
\macro_name  c2,  c3, \args
.elseif (\x) == 36
\macro_name  c2,  c4, \args
.elseif (\x) == 37
\macro_name  c2,  c5, \args
.elseif (\x) == 38
\macro_name  c2,  c6, \args
.elseif (\x) == 39
\macro_name  c2,  c7, \args
.elseif (\x) == 40
\macro_name  c2,  c8, \args
.elseif (\x) == 41
\macro_name  c2,  c9, \args
.elseif (\x) == 42
\macro_name  c2, c10, \args
.elseif (\x) == 43
\macro_name  c2, c11, \args
.elseif (\x) == 44
\macro_name  c2, c12, \args
.elseif (\x) == 45
\macro_name  c2, c13, \args
.elseif (\x) == 46
\macro_name  c2, c14, \args
.elseif (\x) == 47
\macro_name  c2, c15, \args
.elseif (\x) == 48
\macro_name  c3,  c0, \args
.elseif (\x) == 49
\macro_name  c3,  c1, \args
.elseif (\x) == 50
\macro_name  c3,  c2, \args
.elseif (\x) == 51
\macro_name  c3,  c3, \args
.elseif (\x) == 52
\macro_name  c3,  c4, \args
.elseif (\x) == 53
\macro_name  c3,  c5, \args
.elseif (\x) == 54
\macro_name  c3,  c6, \args
.elseif (\x) == 55
\macro_name  c3,  c7, \args
.elseif (\x) == 56
\macro_name  c3,  c8, \args
.elseif (\x) == 57
\macro_name  c3,  c9, \args
.elseif (\x) == 58
\macro_name  c3, c10, \args
.elseif (\x) == 59
\macro_name  c3, c11, \args
.elseif (\x) == 60
\macro_name  c3, c12, \args
.elseif (\x) == 61
\macro_name  c3, c13, \args
.elseif (\x) == 62
\macro_name  c3, c14, \args
.elseif (\x) == 63
\macro_name  c3, c15, \args
.elseif (\x) == 64
\macro_name  c4,  c0, \args
.elseif (\x) == 65
\macro_name  c4,  c1, \args
.elseif (\x) == 66
\macro_name  c4,  c2, \args
.elseif (\x) == 67
\macro_name  c4,  c3, \args
.elseif (\x) == 68
\macro_name  c4,  c4, \args
.elseif (\x) == 69
\macro_name  c4,  c5, \args
.elseif (\x) == 70
\macro_name  c4,  c6, \args
.elseif (\x) == 71
\macro_name  c4,  c7, \args
.elseif (\x) == 72
\macro_name  c4,  c8, \args
.elseif (\x) == 73
\macro_name  c4,  c9, \args
.elseif (\x) == 74
\macro_name  c4, c10, \args
.elseif (\x) == 75
\macro_name  c4, c11, \args
.elseif (\x) == 76
\macro_name  c4, c12, \args
.elseif (\x) == 77
\macro_name  c4, c13, \args
.elseif (\x) == 78
\macro_name  c4, c14, \args
.elseif (\x) == 79
\macro_name  c4, c15, \args
.elseif (\x) == 80
\macro_name  c5,  c0, \args
.elseif (\x) == 81
\macro_name  c5,  c1, \args
.elseif (\x) == 82
\macro_name  c5,  c2, \args
.elseif (\x) == 83
\macro_name  c5,  c3, \args
.elseif (\x) == 84
\macro_name  c5,  c4, \args
.elseif (\x) == 85
\macro_name  c5,  c5, \args
.elseif (\x) == 86
\macro_name  c5,  c6, \args
.elseif (\x) == 87
\macro_name  c5,  c7, \args
.elseif (\x) == 88
\macro_name  c5,  c8, \args
.elseif (\x) == 89
\macro_name  c5,  c9, \args
.elseif (\x) == 90
\macro_name  c5, c10, \args
.elseif (\x) == 91
\macro_name  c5, c11, \args
.elseif (\x) == 92
\macro_name  c5, c12, \args
.elseif (\x) == 93
\macro_name  c5, c13, \args
.elseif (\x) == 94
\macro_name  c5, c14, \args
.elseif (\x) == 95
\macro_name  c5, c15, \args
.elseif (\x) == 96
\macro_name  c6,  c0, \args
.elseif (\x) == 97
\macro_name  c6,  c1, \args
.elseif (\x) == 98
\macro_name  c6,  c2, \args
.elseif (\x) == 99
\macro_name  c6,  c3, \args
.elseif (\x) == 100
\macro_name  c6,  c4, \args
.elseif (\x) == 101
\macro_name  c6,  c5, \args
.elseif (\x) == 102
\macro_name  c6,  c6, \args
.elseif (\x) == 103
\macro_name  c6,  c7, \args
.elseif (\x) == 104
\macro_name  c6,  c8, \args
.elseif (\x) == 105
\macro_name  c6,  c9, \args
.elseif (\x) == 106
\macro_name  c6, c10, \args
.elseif (\x) == 107
\macro_name  c6, c11, \args
.elseif (\x) == 108
\macro_name  c6, c12, \args
.elseif (\x) == 109
\macro_name  c6, c13, \args
.elseif (\x) == 110
\macro_name  c6, c14, \args
.elseif (\x) == 111
\macro_name  c6, c15, \args
.elseif (\x) == 112
\macro_name  c7,  c0, \args
.elseif (\x) == 113
\macro_name  c7,  c1, \args
.elseif (\x) == 114
\macro_name  c7,  c2, \args
.elseif (\x) == 115
\macro_name  c7,  c3, \args
.elseif (\x) == 116
\macro_name  c7,  c4, \args
.elseif (\x) == 117
\macro_name  c7,  c5, \args
.elseif (\x) == 118
\macro_name  c7,  c6, \args
.elseif (\x) == 119
\macro_name  c7,  c7, \args
.elseif (\x) == 120
\macro_name  c7,  c8, \args
.elseif (\x) == 121
\macro_name  c7,  c9, \args
.elseif (\x) == 122
\macro_name  c7, c10, \args
.elseif (\x) == 123
\macro_name  c7, c11, \args
.elseif (\x) == 124
\macro_name  c7, c12, \args
.elseif (\x) == 125
\macro_name  c7, c13, \args
.elseif (\x) == 126
\macro_name  c7, c14, \args
.elseif (\x) == 127
\macro_name  c7, c15, \args
.elseif (\x) == 128
\macro_name  c8,  c0, \args
.elseif (\x) == 129
\macro_name  c8,  c1, \args
.elseif (\x) == 130
\macro_name  c8,  c2, \args
.elseif (\x) == 131
\macro_name  c8,  c3, \args
.elseif (\x) == 132
\macro_name  c8,  c4, \args
.elseif (\x) == 133
\macro_name  c8,  c5, \args
.elseif (\x) == 134
\macro_name  c8,  c6, \args
.elseif (\x) == 135
\macro_name  c8,  c7, \args
.elseif (\x) == 136
\macro_name  c8,  c8, \args
.elseif (\x) == 137
\macro_name  c8,  c9, \args
.elseif (\x) == 138
\macro_name  c8, c10, \args
.elseif (\x) == 139
\macro_name  c8, c11, \args
.elseif (\x) == 140
\macro_name  c8, c12, \args
.elseif (\x) == 141
\macro_name  c8, c13, \args
.elseif (\x) == 142
\macro_name  c8, c14, \args
.elseif (\x) == 143
\macro_name  c8, c15, \args
.elseif (\x) == 144
\macro_name  c9,  c0, \args
.elseif (\x) == 145
\macro_name  c9,  c1, \args
.elseif (\x) == 146
\macro_name  c9,  c2, \args
.elseif (\x) == 147
\macro_name  c9,  c3, \args
.elseif (\x) == 148
\macro_name  c9,  c4, \args
.elseif (\x) == 149
\macro_name  c9,  c5, \args
.elseif (\x) == 150
\macro_name  c9,  c6, \args
.elseif (\x) == 151
\macro_name  c9,  c7, \args
.elseif (\x) == 152
\macro_name  c9,  c8, \args
.elseif (\x) == 153
\macro_name  c9,  c9, \args
.elseif (\x) == 154
\macro_name  c9, c10, \args
.elseif (\x) == 155
\macro_name  c9, c11, \args
.elseif (\x) == 156
\macro_name  c9, c12, \args
.elseif (\x) == 157
\macro_name  c9, c13, \args
.elseif (\x) == 158
\macro_name  c9, c14, \args
.elseif (\x) == 159
\macro_name  c9, c15, \args
.elseif (\x) == 160
\macro_name c10,  c0, \args
.elseif (\x) == 161
\macro_name c10,  c1, \args
.elseif (\x) == 162
\macro_name c10,  c2, \args
.elseif (\x) == 163
\macro_name c10,  c3, \args
.elseif (\x) == 164
\macro_name c10,  c4, \args
.elseif (\x) == 165
\macro_name c10,  c5, \args
.elseif (\x) == 166
\macro_name c10,  c6, \args
.elseif (\x) == 167
\macro_name c10,  c7, \args
.elseif (\x) == 168
\macro_name c10,  c8, \args
.elseif (\x) == 169
\macro_name c10,  c9, \args
.elseif (\x) == 170
\macro_name c10, c10, \args
.elseif (\x) == 171
\macro_name c10, c11, \args
.elseif (\x) == 172
\macro_name c10, c12, \args
.elseif (\x) == 173
\macro_name c10, c13, \args
.elseif (\x) == 174
\macro_name c10, c14, \args
.elseif (\x) == 175
\macro_name c10, c15, \args
.elseif (\x) == 176
\macro_name c11,  c0, \args
.elseif (\x) == 177
\macro_name c11,  c1, \args
.elseif (\x) == 178
\macro_name c11,  c2, \args
.elseif (\x) == 179
\macro_name c11,  c3, \args
.elseif (\x) == 180
\macro_name c11,  c4, \args
.elseif (\x) == 181
\macro_name c11,  c5, \args
.elseif (\x) == 182
\macro_name c11,  c6, \args
.elseif (\x) == 183
\macro_name c11,  c7, \args
.elseif (\x) == 184
\macro_name c11,  c8, \args
.elseif (\x) == 185
\macro_name c11,  c9, \args
.elseif (\x) == 186
\macro_name c11, c10, \args
.elseif (\x) == 187
\macro_name c11, c11, \args
.elseif (\x) == 188
\macro_name c11, c12, \args
.elseif (\x) == 189
\macro_name c11, c13, \args
.elseif (\x) == 190
\macro_name c11, c14, \args
.elseif (\x) == 191
\macro_name c11, c15, \args
.elseif (\x) == 192
\macro_name c12,  c0, \args
.elseif (\x) == 193
\macro_name c12,  c1, \args
.elseif (\x) == 194
\macro_name c12,  c2, \args
.elseif (\x) == 195
\macro_name c12,  c3, \args
.elseif (\x) == 196
\macro_name c12,  c4, \args
.elseif (\x) == 197
\macro_name c12,  c5, \args
.elseif (\x) == 198
\macro_name c12,  c6, \args
.elseif (\x) == 199
\macro_name c12,  c7, \args
.elseif (\x) == 200
\macro_name c12,  c8, \args
.elseif (\x) == 201
\macro_name c12,  c9, \args
.elseif (\x) == 202
\macro_name c12, c10, \args
.elseif (\x) == 203
\macro_name c12, c11, \args
.elseif (\x) == 204
\macro_name c12, c12, \args
.elseif (\x) == 205
\macro_name c12, c13, \args
.elseif (\x) == 206
\macro_name c12, c14, \args
.elseif (\x) == 207
\macro_name c12, c15, \args
.elseif (\x) == 208
\macro_name c13,  c0, \args
.elseif (\x) == 209
\macro_name c13,  c1, \args
.elseif (\x) == 210
\macro_name c13,  c2, \args
.elseif (\x) == 211
\macro_name c13,  c3, \args
.elseif (\x) == 212
\macro_name c13,  c4, \args
.elseif (\x) == 213
\macro_name c13,  c5, \args
.elseif (\x) == 214
\macro_name c13,  c6, \args
.elseif (\x) == 215
\macro_name c13,  c7, \args
.elseif (\x) == 216
\macro_name c13,  c8, \args
.elseif (\x) == 217
\macro_name c13,  c9, \args
.elseif (\x) == 218
\macro_name c13, c10, \args
.elseif (\x) == 219
\macro_name c13, c11, \args
.elseif (\x) == 220
\macro_name c13, c12, \args
.elseif (\x) == 221
\macro_name c13, c13, \args
.elseif (\x) == 222
\macro_name c13, c14, \args
.elseif (\x) == 223
\macro_name c13, c15, \args
.elseif (\x) == 224
\macro_name c14,  c0, \args
.elseif (\x) == 225
\macro_name c14,  c1, \args
.elseif (\x) == 226
\macro_name c14,  c2, \args
.elseif (\x) == 227
\macro_name c14,  c3, \args
.elseif (\x) == 228
\macro_name c14,  c4, \args
.elseif (\x) == 229
\macro_name c14,  c5, \args
.elseif (\x) == 230
\macro_name c14,  c6, \args
.elseif (\x) == 231
\macro_name c14,  c7, \args
.elseif (\x) == 232
\macro_name c14,  c8, \args
.elseif (\x) == 233
\macro_name c14,  c9, \args
.elseif (\x) == 234
\macro_name c14, c10, \args
.elseif (\x) == 235
\macro_name c14, c11, \args
.elseif (\x) == 236
\macro_name c14, c12, \args
.elseif (\x) == 237
\macro_name c14, c13, \args
.elseif (\x) == 238
\macro_name c14, c14, \args
.elseif (\x) == 239
\macro_name c14, c15, \args
.elseif (\x) == 240
\macro_name c15,  c0, \args
.elseif (\x) == 241
\macro_name c15,  c1, \args
.elseif (\x) == 242
\macro_name c15,  c2, \args
.elseif (\x) == 243
\macro_name c15,  c3, \args
.elseif (\x) == 244
\macro_name c15,  c4, \args
.elseif (\x) == 245
\macro_name c15,  c5, \args
.elseif (\x) == 246
\macro_name c15,  c6, \args
.elseif (\x) == 247
\macro_name c15,  c7, \args
.elseif (\x) == 248
\macro_name c15,  c8, \args
.elseif (\x) == 249
\macro_name c15,  c9, \args
.elseif (\x) == 250
\macro_name c15, c10, \args
.elseif (\x) == 251
\macro_name c15, c11, \args
.elseif (\x) == 252
\macro_name c15, c12, \args
.elseif (\x) == 253
\macro_name c15, c13, \args
.elseif (\x) == 254
\macro_name c15, c14, \args
.elseif (\x) == 255
\macro_name c15, c15, \args
.else
.error "Value outside of range 0-255"
.endif
.endm

// ----------------------------------------------------------------------------
// RCP Boolean instructions

// Assert b is a valid boolean (0xa500a500u or 0x00c300c3u)
.macro rcp_bvalid r
    mcr p7, #1, \r , c0, c0, #0
.endm

.macro rcp_bvalid_nodelay r
    mcr2 p7, #1, \r , c0, c0, #0
.endm

// Assert b is true (0xa500a500u)
.macro rcp_btrue r
    mcr p7, #2, \r , c0, c0, #0
.endm

.macro rcp_btrue_nodelay r
    mcr2 p7, #2, \r , c0, c0, #0
.endm

// Assert b is false (0x00c300c3u)
.macro rcp_bfalse r
    mcr p7, #3, \r , c0, c0, #1
.endm

.macro rcp_bfalse_nodelay r
    mcr2 p7, #3, \r , c0, c0, #1
.endm

// Assert b0 and b1 are both valid booleans
.macro rcp_b2valid b0, b1
    mcrr p7, #0, \b0 , \b1 , c8
.endm

.macro rcp_b2valid_nodelay b0, b1
    mcrr2 p7, #0, \b0 , \b1 , c8
.endm

// Assert b0 and b1 are both true
.macro rcp_b2and b0, b1
    mcrr p7, #1, \b0 , \b1 , c0
.endm

.macro rcp_b2and_nodelay b0, b1
    mcrr2 p7, #1, \b0 , \b1 , c0
.endm

// Assert b0 and b1 are valid, and at least one is true
.macro rcp_b2or b0, b1
    mcrr p7, #2, \b0 , \b1 , c0
.endm

.macro rcp_b2or_nodelay b0, b1
    mcrr2 p7, #2, \b0 , \b1 , c0
.endm

// Assert (b ^ mask) is a valid boolean
.macro rcp_bxorvalid b, mask
    mcrr p7, #3, \b , \mask , c8
.endm

.macro rcp_bxorvalid_nodelay b, mask
    mcrr2 p7, #3, \b , \mask , c8
.endm

// Assert (b ^ mask) is true
.macro rcp_bxortrue b, mask
    mcrr p7, #4, \b , \mask , c0
.endm

.macro rcp_bxortrue_nodelay b, mask
    mcrr2 p7, #4, \b , \mask , c0
.endm

// Assert (b ^ mask) is false
.macro rcp_bxorfalse b, mask
    mcrr p7, #5, \b , \mask , c8
.endm

.macro rcp_bxorfalse_nodelay b, mask
    mcrr2 p7, #5, \b , \mask , c8
.endm

// ----------------------------------------------------------------------------
// RCP Integer instructions

// Assert (x ^ parity) == 0x96009600u
.macro rcp_ivalid x, parity
    mcrr p7, #6, \x , \parity , c8
.endm

.macro rcp_ivalid_nodelay x, parity
    mcrr2 p7, #6, \x , \parity , c8
.endm

// Assert x == y
.macro rcp_iequal x, y
    mcrr p7, #7, \x , \y , c0
.endm

.macro rcp_iequal_nodelay x, y
    mcrr2 p7, #7, \x , \y , c0
.endm

// ----------------------------------------------------------------------------
// RCP Sequence count instructions

// Directly write 8-bit constant expression cnt to the sequence counter.
.macro rcp_count_set_impl h, l
mcr p7, #4, r0, \h , \l , #0
.endm
.macro rcp_count_set cnt
rcp_switch_u8_to_ch_cl rcp_count_set_impl, \cnt
.endm

.macro rcp_count_set_nodelay_impl h, l
mcr2 p7, #4, r0, \h , \l , #0
.endm
.macro rcp_count_set_nodelay cnt
rcp_switch_u8_to_ch_cl rcp_count_set_nodelay_impl, \cnt
.endm

// Check 8-bit constant expression cnt against the sequence counter, then
// increment the counter.
.macro rcp_count_check_impl h, l
    mcr p7, #5, r0, \h, \l, #1
.endm
.macro rcp_count_check cnt
rcp_switch_u8_to_ch_cl rcp_count_check_impl, \cnt
.endm

.macro rcp_count_check_nodelay_impl h, l
    mcr2 p7, #5, r0, \h, \l, #1
.endm
.macro rcp_count_check_nodelay cnt
rcp_switch_u8_to_ch_cl rcp_count_check_nodelay_impl, \cnt
.endm

// ----------------------------------------------------------------------------
// RCP Canary instructions

// Get a 32-bit canary value. `tag` must be a constant expression.
.macro rcp_canary_get_impl h, l, x
   mrc p7, #0, \x, \h, \l, #1
.endm
.macro rcp_canary_get x, tag
rcp_switch_u8_to_ch_cl rcp_canary_get_impl \tag, \x
.endm

.macro rcp_canary_get_nodelay_impl h, l, x
   mrc2 p7, #0, \x, \h, \l, #1
.endm
.macro rcp_canary_get_nodelay x, tag
rcp_switch_u8_to_ch_cl rcp_canary_get_nodelay_impl \tag, \x
.endm

// Assert that canary matches result of rcp_canary_get with the same tags:
.macro rcp_canary_check_impl h, l, x
   mcr p7, #0, \x, \h, \l, #1
.endm
.macro rcp_canary_check x, tag
rcp_switch_u8_to_ch_cl rcp_canary_check_impl \tag, \x
.endm

.macro rcp_canary_check_nodelay_impl h, l, x
   mcr2 p7, #0, \x, \h, \l, #1
.endm
.macro rcp_canary_check_nodelay x, tag
rcp_switch_u8_to_ch_cl rcp_canary_check_nodelay_impl \tag, \x
.endm

.macro rcp_panic
    cdp p7, #0, c0, c0, c0, #1
.endm

#endif // !__riscv
#endif // __ASSEMBLER__
// ----------------------------------------------------------------------------

#ifdef __cplusplus
}
#endif
#else
#define rcp_is_true(x) ((int32_t)(x) < 0)
#endif
#endif
