/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   Mupen64plus - mips_instructions.def                                   *
 *   Mupen64Plus homepage: https://mupen64plus.org/                        *
 *   Copyright (C) 2002 Hacktarux                                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* Before #including this file the following macros must be defined:
 *
 * instruction operands accessor macros:
 * rrt, rrd, rfs, rrs, rsa,
 * rrt32, rrd32, rrs32, irs32, irt32,
 * irt, irs, ibase, ioffset, iimmediate,
 * jinst_index,
 * lfbase, lfft, lfoffset,
 * cfft, cffs, cffd
 *
 * DECLARE_R4300: An optionnal way of declaring teh r4300 pointer used
 * in instructions definitions
 * PCADDR: Program counter (memory address of the current instruction).
 *
 * ADD_TO_PC(x): Increment the program counter in 'x' instructions.
 * This is only used for small changes to PC, so the new program counter
 * is guaranteed to fall in the current cached interpreter or dynarec block.
 *
 * DECLARE_INSTRUCTION(name)
 * Declares an instruction function which is not a jump.
 * Followed by a block of code.
 *
 * DECLARE_JUMP(name, destination, condition, link, likely, cop1)
 * name is the name of the jump or branch instruction.
 * destination is the destination memory address of the jump.
 * If condition is nonzero, the jump is taken.
 * link is a pointer to a variable where (PC+8) is written unconditionally.
 *     To avoid linking, pass &reg[0]
 * If likely is nonzero, the delay slot is only executed if the jump is taken.
 * If cop1 is nonzero, a COP1 unusable check will be done.
 */

#include "fpu.h"
#include "r4300_core.h"
#include "device/memory/memory.h"
#include "device/rcp/mi/mi_controller.h"
#include "device/rdram/rdram.h"
#include "osal/preproc.h"

#define XXH_INLINE_ALL
#include <xxhash.h>

#include <inttypes.h>
#include <stdint.h>

/* Assists unaligned memory accessors with making masks to preserve or apply
 * bits in registers and memory.
 *
 * BITS_BELOW_MASK32 and BITS_BELOW_MASK64 make masks where bits 0 to (x - 1)
 * are set.
 *
 * BITS_ABOVE_MASK32 makes masks where bits x to 31 are set.
 * BITS_ABOVE_MASK64 makes masks where bits x to 63 are set.
 *
 * e.g. x = 8
 * 0000 0000 0000 0000 0000 0000 1111 1111 <- BITS_BELOW_MASK32(8)
 * 1111 1111 1111 1111 1111 1111 0000 0000 <- BITS_ABOVE_MASK32(8)
 *
 * Giving a negative value or one that is >= the bit count of the mask results
 * in undefined behavior.
 */

#define BITS_BELOW_MASK32(x) ((UINT32_C(1) << (x)) - 1)
#define BITS_ABOVE_MASK32(x) (~(BITS_BELOW_MASK32((x))))

#define BITS_BELOW_MASK64(x) ((UINT64_C(1) << (x)) - 1)
#define BITS_ABOVE_MASK64(x) (~(BITS_BELOW_MASK64((x))))


static unsigned int bshift(uint32_t address)
{
    return ((address & 3) ^ 3) << 3;
}

static unsigned int hshift(uint32_t address)
{
    return ((address & 2) ^ 2) << 3;
}


/* M64P Pseudo instructions */

DECLARE_INSTRUCTION(NI)
{
    DECLARE_R4300
    DebugMessage(M64MSG_ERROR, "NI() @ 0x%" PRIX32, PCADDR);
    DebugMessage(M64MSG_ERROR, "opcode not implemented: %" PRIX32 ":%" PRIX32, PCADDR, *fast_mem_access(r4300, PCADDR));
    *r4300_stop(r4300) = 1;
}

/* Reserved */

DECLARE_INSTRUCTION(RESERVED)
{
    DECLARE_R4300

    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    DebugMessage(M64MSG_ERROR, "reserved opcode: %" PRIX32 ":%" PRIX32, PCADDR, *fast_mem_access(r4300, PCADDR));

    cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_RI;
    exception_general(r4300);
}

DECLARE_INSTRUCTION(RESERVED_COP2)
{
    DECLARE_R4300

    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    if (check_cop2_unusable(r4300)) { return; }

    cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_RI | CP0_CAUSE_CE2;
    exception_general(r4300);
}

/* BREAK */

DECLARE_INSTRUCTION(BREAK)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_BP;
    exception_general(r4300);
}

/* Load instructions */

DECLARE_INSTRUCTION(LB)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    uint32_t value;
    unsigned int shift = bshift(lsaddr);

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = SE8((value >> shift) & 0xff);
    }
}

DECLARE_INSTRUCTION(LBU)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    uint32_t value;
    unsigned int shift = bshift(lsaddr);

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = (value >> shift) & 0xff;
    }
}

DECLARE_INSTRUCTION(LH)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    uint32_t value;
    unsigned int shift = hshift(lsaddr);

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = SE16((value >> shift) & 0xffff);
    }
}

DECLARE_INSTRUCTION(LHU)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    uint32_t value;
    unsigned int shift = hshift(lsaddr);

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = (value >> shift) & 0xffff;
    }
}

DECLARE_INSTRUCTION(LL)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    uint32_t value;

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = SE32(value);
        r4300->llbit = 1;
    }
}

DECLARE_INSTRUCTION(LW)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    uint32_t value;

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = SE32(value);
    }
}

DECLARE_INSTRUCTION(LWU)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    uint32_t value;

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = value;
    }
}

DECLARE_INSTRUCTION(LWL)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 3);
    unsigned int shift = 8 * n;
    uint32_t mask = BITS_BELOW_MASK32(8 * n);
    uint32_t value;

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = SE32(((uint32_t)*lsrtp & mask) | ((uint32_t)value << shift));
    }
}

DECLARE_INSTRUCTION(LWR)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 3);
    unsigned int shift = 8 * (3 - n);
    uint32_t mask = (n == 3)
        ? UINT32_C(0)
        : BITS_ABOVE_MASK32(8 * (n + 1));
    uint32_t value;

    if (r4300_read_aligned_word(r4300, lsaddr, &value)) {
        *lsrtp = SE32(((uint32_t)*lsrtp & mask) | ((uint32_t)value >> shift));
    }
}

DECLARE_INSTRUCTION(LD)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    r4300_read_aligned_dword(r4300, lsaddr, (uint64_t*)lsrtp);
}

DECLARE_INSTRUCTION(LDL)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 7);
    unsigned int shift = 8 * n;
    uint64_t mask = BITS_BELOW_MASK64(8 * n);
    uint64_t value;

    if (r4300_read_aligned_dword(r4300, lsaddr & ~UINT32_C(7), &value)) {
        *lsrtp = ((uint64_t)*lsrtp & mask) | (value << shift);
    }
}

DECLARE_INSTRUCTION(LDR)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 7);
    unsigned int shift = 8 * (7 - n);
    uint64_t mask = (n == 7)
        ? UINT64_C(0)
        : BITS_ABOVE_MASK64(8 * (n + 1));
    uint64_t value;

    if (r4300_read_aligned_dword(r4300, lsaddr & ~UINT32_C(7), &value)) {
        *lsrtp = ((uint64_t)*lsrtp & mask) | (value >> shift);
    }
}

/* Store instructions */

DECLARE_INSTRUCTION(SB)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    unsigned int shift = bshift(lsaddr);

    r4300_write_aligned_word(r4300, lsaddr, (uint32_t)*lsrtp << shift, UINT32_C(0xff) << shift);
}

DECLARE_INSTRUCTION(SH)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);
    unsigned int shift = hshift(lsaddr);

    r4300_write_aligned_word(r4300, lsaddr, (uint32_t)*lsrtp << shift, UINT32_C(0xffff) << shift);
}

DECLARE_INSTRUCTION(SC)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    if (r4300->llbit)
    {
        if (r4300_write_aligned_word(r4300, lsaddr, (uint32_t)*lsrtp, ~UINT32_C(0))) {
            r4300->llbit = 0;
            *lsrtp = 1;
        }
    }
    else
    {
        *lsrtp = 0;
    }
}

DECLARE_INSTRUCTION(SW)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    r4300_write_aligned_word(r4300, lsaddr, (uint32_t)*lsrtp, ~UINT32_C(0));
}

DECLARE_INSTRUCTION(SWL)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 3);
    unsigned int shift = 8 * n;
    uint32_t mask = (n == 0)
        ? ~UINT32_C(0)
        : BITS_BELOW_MASK32(8 * (4 - n));
    uint32_t value = (uint32_t)*lsrtp;

    r4300_write_aligned_word(r4300, lsaddr & ~UINT32_C(0x3), value >> shift, mask);
}

DECLARE_INSTRUCTION(SWR)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 3);
    unsigned int shift = 8 * (3 - n);
    uint32_t mask = BITS_ABOVE_MASK32(8 * (3 - n));
    uint32_t value = (uint32_t)*lsrtp;

    r4300_write_aligned_word(r4300, lsaddr & ~UINT32_C(0x3), value << shift, mask);
}

DECLARE_INSTRUCTION(SD)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    r4300_write_aligned_dword(r4300, lsaddr, (uint64_t)*lsrtp, ~UINT64_C(0));
}

DECLARE_INSTRUCTION(SDL)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 7);
    unsigned int shift = 8 * n;
    uint64_t mask = (n == 0)
        ? ~UINT64_C(0)
        : BITS_BELOW_MASK64(8 * (8 - n));
    uint64_t value = (uint64_t)*lsrtp;

    r4300_write_aligned_dword(r4300, lsaddr & ~UINT32_C(0x7), value >> shift, mask);
}

DECLARE_INSTRUCTION(SDR)
{
    DECLARE_R4300
    const uint32_t lsaddr = (uint32_t) irs32 + (uint32_t) iimmediate;
    int64_t *lsrtp = &irt;
    ADD_TO_PC(1);

    unsigned int n = (lsaddr & 7);
    unsigned int shift = 8 * (7 - n);
    uint64_t mask = BITS_ABOVE_MASK64(8 * (7 - n));
    uint64_t value = (uint64_t)*lsrtp;

    r4300_write_aligned_dword(r4300, lsaddr & ~UINT32_C(0x7), value << shift, mask);
}

/* Computational instructions */

DECLARE_INSTRUCTION(ADD)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrs32 + (uint32_t) rrt32);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ADDU)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrs32 + (uint32_t) rrt32);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ADDI)
{
    DECLARE_R4300
    irt = SE32((uint32_t) irs32 + (uint32_t) iimmediate);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ADDIU)
{
    DECLARE_R4300
    irt = SE32((uint32_t) irs32 + (uint32_t) iimmediate);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DADD)
{
    DECLARE_R4300
    rrd = (uint64_t) rrs + (uint64_t) rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DADDU)
{
    DECLARE_R4300
    rrd = (uint64_t) rrs + (uint64_t) rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DADDI)
{
    DECLARE_R4300
    irt = (uint64_t) irs + (uint64_t) iimmediate;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DADDIU)
{
    DECLARE_R4300
    irt = (uint64_t) irs + (uint64_t) iimmediate;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SUB)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrs32 - (uint32_t) rrt32);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SUBU)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrs32 - (uint32_t) rrt32);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSUB)
{
    DECLARE_R4300
    rrd = (uint64_t) rrs - (uint64_t) rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSUBU)
{
    DECLARE_R4300
    rrd = (uint64_t) rrs - (uint64_t) rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SLT)
{
    DECLARE_R4300
    if (rrs < rrt) { rrd = 1; }
    else { rrd = 0; }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SLTU)
{
    DECLARE_R4300
    if ((uint64_t) rrs < (uint64_t) rrt) { rrd = 1; }
    else { rrd = 0; }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SLTI)
{
    DECLARE_R4300
    if (irs < iimmediate) { irt = 1; }
    else { irt = 0; }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SLTIU)
{
    DECLARE_R4300
    if ((uint64_t) irs < (uint64_t) ((int64_t) iimmediate)) { irt = 1; }
    else { irt = 0; }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(AND)
{
    DECLARE_R4300
    rrd = rrs & rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ANDI)
{
    DECLARE_R4300
    irt = irs & (uint16_t) iimmediate;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(OR)
{
    DECLARE_R4300
    rrd = rrs | rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ORI)
{
    DECLARE_R4300
    irt = irs | (uint16_t) iimmediate;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(XOR)
{
    DECLARE_R4300
    rrd = rrs ^ rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(XORI)
{
    DECLARE_R4300
    irt = irs ^ (uint16_t) iimmediate;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(NOR)
{
    DECLARE_R4300
    rrd = ~(rrs | rrt);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(LUI)
{
    DECLARE_R4300
    irt = SE32((uint32_t) iimmediate << 16);
    ADD_TO_PC(1);
}

/* Shift instructions */

DECLARE_INSTRUCTION(NOP)
{
    DECLARE_R4300
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SLL)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrt32 << rsa);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SLLV)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrt32 << (rrs32 & 0x1F));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSLL)
{
    DECLARE_R4300
    rrd = (uint64_t) rrt << rsa;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSLLV)
{
    DECLARE_R4300
    rrd = (uint64_t) rrt << (rrs32 & 0x3F);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSLL32)
{
    DECLARE_R4300
    rrd = (uint64_t) rrt << (32 + rsa);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SRL)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrt32 >> rsa);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SRLV)
{
    DECLARE_R4300
    rrd = SE32((uint32_t) rrt32 >> (rrs32 & 0x1F));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSRL)
{
    DECLARE_R4300
    rrd = (uint64_t) rrt >> rsa;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSRLV)
{
    DECLARE_R4300
    rrd = (uint64_t) rrt >> (rrs32 & 0x3F);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSRL32)
{
    DECLARE_R4300
    rrd = (uint64_t) rrt >> (32 + rsa);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SRA)
{
    DECLARE_R4300
    rrd = SE32(rrt >> rsa);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SRAV)
{
    DECLARE_R4300
    rrd = SE32(rrt >> (rrs32 & 0x1F));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSRA)
{
    DECLARE_R4300
    rrd = rrt >> rsa;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSRAV)
{
    DECLARE_R4300
    rrd = (int64_t) rrt >> (rrs32 & 0x3F);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DSRA32)
{
    DECLARE_R4300
    rrd = (int64_t) rrt >> (32 + rsa);
    ADD_TO_PC(1);
}

/* Multiply / Divide instructions */

DECLARE_INSTRUCTION(MULT)
{
    DECLARE_R4300
    int64_t temp;
    temp = rrs32 * (int64_t)rrt32;
    *r4300_mult_hi(r4300) = temp >> 32;
    *r4300_mult_lo(r4300) = SE32(temp);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MULTU)
{
    DECLARE_R4300
    uint64_t temp;
    temp = (uint32_t) rrs * (uint64_t) ((uint32_t) rrt);
    *r4300_mult_hi(r4300) = (int64_t) temp >> 32;
    *r4300_mult_lo(r4300) = SE32(temp);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMULT)
{
    DECLARE_R4300
    uint64_t op1, op2, op3, op4;
    uint64_t result1, result2, result3, result4;
    uint64_t temp1, temp2, temp3, temp4;
    int sign = 0;

    if (rrs < 0)
    {
        op2 = -rrs;
        sign = 1 - sign;
    }
    else { op2 = rrs; }
    if (rrt < 0)
    {
        op4 = -rrt;
        sign = 1 - sign;
    }
    else { op4 = rrt; }

    op1 = op2 & UINT64_C(0xFFFFFFFF);
    op2 = (op2 >> 32) & UINT64_C(0xFFFFFFFF);
    op3 = op4 & UINT64_C(0xFFFFFFFF);
    op4 = (op4 >> 32) & UINT64_C(0xFFFFFFFF);

    temp1 = op1 * op3;
    temp2 = (temp1 >> 32) + op1 * op4;
    temp3 = op2 * op3;
    temp4 = (temp3 >> 32) + op2 * op4;

    result1 = temp1 & UINT64_C(0xFFFFFFFF);
    result2 = temp2 + (temp3 & UINT64_C(0xFFFFFFFF));
    result3 = (result2 >> 32) + temp4;
    result4 = (result3 >> 32);

    *r4300_mult_lo(r4300) = result1 | (result2 << 32);
    *r4300_mult_hi(r4300) = (result3 & UINT64_C(0xFFFFFFFF)) | (result4 << 32);
    if (sign)
    {
        *r4300_mult_hi(r4300) = ~*r4300_mult_hi(r4300);
        if (!*r4300_mult_lo(r4300)) { (*r4300_mult_hi(r4300))++; }
        else { *r4300_mult_lo(r4300) = ~*r4300_mult_lo(r4300) + 1; }
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMULTU)
{
    DECLARE_R4300
    uint64_t op1, op2, op3, op4;
    uint64_t result1, result2, result3, result4;
    uint64_t temp1, temp2, temp3, temp4;

    op1 = rrs & UINT64_C(0xFFFFFFFF);
    op2 = (rrs >> 32) & UINT64_C(0xFFFFFFFF);
    op3 = rrt & UINT64_C(0xFFFFFFFF);
    op4 = (rrt >> 32) & UINT64_C(0xFFFFFFFF);

    temp1 = op1 * op3;
    temp2 = (temp1 >> 32) + op1 * op4;
    temp3 = op2 * op3;
    temp4 = (temp3 >> 32) + op2 * op4;

    result1 = temp1 & UINT64_C(0xFFFFFFFF);
    result2 = temp2 + (temp3 & UINT64_C(0xFFFFFFFF));
    result3 = (result2 >> 32) + temp4;
    result4 = (result3 >> 32);

    *r4300_mult_lo(r4300) = result1 | (result2 << 32);
    *r4300_mult_hi(r4300) = (result3 & UINT64_C(0xFFFFFFFF)) | (result4 << 32);

    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DIV)
{
    DECLARE_R4300
    if (rrt32)
    {
        if (rrs32 == INT32_MIN && rrt32 == -1)
        {
            *r4300_mult_lo(r4300) = SE32(rrs32);
            *r4300_mult_hi(r4300) = 0;
        }
        else
        {
            *r4300_mult_lo(r4300) = SE32(rrs32 / rrt32);
            *r4300_mult_hi(r4300) = SE32(rrs32 % rrt32);
        }
    }
    else
    {
        *r4300_mult_lo(r4300) = rrs32 < 0 ? 1 : -1;
        *r4300_mult_hi(r4300) = SE32(rrs32);
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DIVU)
{
    DECLARE_R4300
    if (rrt32)
    {
        *r4300_mult_lo(r4300) = SE32((uint32_t) rrs32 / (uint32_t) rrt32);
        *r4300_mult_hi(r4300) = SE32((uint32_t) rrs32 % (uint32_t) rrt32);
    }
    else
    {
        *r4300_mult_lo(r4300) = -1;
        *r4300_mult_hi(r4300) = SE32(rrs32);
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DDIV)
{
    DECLARE_R4300
    if (rrt)
    {
        if (rrs == INT64_MIN && rrt == -1)
        {
            *r4300_mult_lo(r4300) = rrs;
            *r4300_mult_hi(r4300) = 0;
        }
        else
        {
            *r4300_mult_lo(r4300) = rrs / rrt;
            *r4300_mult_hi(r4300) = rrs % rrt;
        }
    }
    else
    {
        *r4300_mult_lo(r4300) = rrs < 0 ? 1 : -1;
        *r4300_mult_hi(r4300) = rrs;
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DDIVU)
{
    DECLARE_R4300
    if (rrt)
    {
        *r4300_mult_lo(r4300) = (uint64_t) rrs / (uint64_t) rrt;
        *r4300_mult_hi(r4300) = (uint64_t) rrs % (uint64_t) rrt;
    }
    else
    {
        *r4300_mult_lo(r4300) = -1;
        *r4300_mult_hi(r4300) = rrs;
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MFHI)
{
    DECLARE_R4300
    rrd = *r4300_mult_hi(r4300);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MTHI)
{
    DECLARE_R4300
    *r4300_mult_hi(r4300) = rrs;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MFLO)
{
    DECLARE_R4300
    rrd = *r4300_mult_lo(r4300);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MTLO)
{
    DECLARE_R4300
    *r4300_mult_lo(r4300) = rrs;
    ADD_TO_PC(1);
}

/* Jump & Branch instructions */

DECLARE_JUMP(J,   (jinst_index<<2) | ((PCADDR+4) & UINT32_C(0xF0000000)), 1, &r4300_regs(r4300)[0],  0, 0)
DECLARE_JUMP(JAL, (jinst_index<<2) | ((PCADDR+4) & UINT32_C(0xF0000000)), 1, &r4300_regs(r4300)[31], 0, 0)

DECLARE_JUMP(JR,   irs32, 1, &r4300_regs(r4300)[0], 0, 0)
DECLARE_JUMP(JALR, irs32, 1, &rrd,    0, 0)

DECLARE_JUMP(BEQ,     PCADDR + (iimmediate+1)*4, irs == irt, &r4300_regs(r4300)[0], 0, 0)
DECLARE_JUMP(BEQL,    PCADDR + (iimmediate+1)*4, irs == irt, &r4300_regs(r4300)[0], 1, 0)

DECLARE_JUMP(BNE,     PCADDR + (iimmediate+1)*4, irs != irt, &r4300_regs(r4300)[0], 0, 0)
DECLARE_JUMP(BNEL,    PCADDR + (iimmediate+1)*4, irs != irt, &r4300_regs(r4300)[0], 1, 0)

DECLARE_JUMP(BLEZ,    PCADDR + (iimmediate+1)*4, irs <= 0,   &r4300_regs(r4300)[0], 0, 0)
DECLARE_JUMP(BLEZL,   PCADDR + (iimmediate+1)*4, irs <= 0,   &r4300_regs(r4300)[0], 1, 0)

DECLARE_JUMP(BGTZ,    PCADDR + (iimmediate+1)*4, irs > 0,    &r4300_regs(r4300)[0], 0, 0)
DECLARE_JUMP(BGTZL,   PCADDR + (iimmediate+1)*4, irs > 0,    &r4300_regs(r4300)[0], 1, 0)

DECLARE_JUMP(BLTZ,    PCADDR + (iimmediate+1)*4, irs < 0,    &r4300_regs(r4300)[0],  0, 0)
DECLARE_JUMP(BLTZAL,  PCADDR + (iimmediate+1)*4, irs < 0,    &r4300_regs(r4300)[31], 0, 0)
DECLARE_JUMP(BLTZL,   PCADDR + (iimmediate+1)*4, irs < 0,    &r4300_regs(r4300)[0],  1, 0)
DECLARE_JUMP(BLTZALL, PCADDR + (iimmediate+1)*4, irs < 0,    &r4300_regs(r4300)[31], 1, 0)

DECLARE_JUMP(BGEZ,    PCADDR + (iimmediate+1)*4, irs >= 0,   &r4300_regs(r4300)[0],  0, 0)
DECLARE_JUMP(BGEZAL,  PCADDR + (iimmediate+1)*4, irs >= 0,   &r4300_regs(r4300)[31], 0, 0)
DECLARE_JUMP(BGEZL,   PCADDR + (iimmediate+1)*4, irs >= 0,   &r4300_regs(r4300)[0],  1, 0)
DECLARE_JUMP(BGEZALL, PCADDR + (iimmediate+1)*4, irs >= 0,   &r4300_regs(r4300)[31], 1, 0)

DECLARE_JUMP(BC1F,  PCADDR + (iimmediate+1)*4, ((*r4300_cp1_fcr31(&r4300->cp1)) & FCR31_CMP_BIT)==0, &r4300_regs(r4300)[0], 0, 1)
DECLARE_JUMP(BC1FL, PCADDR + (iimmediate+1)*4, ((*r4300_cp1_fcr31(&r4300->cp1)) & FCR31_CMP_BIT)==0, &r4300_regs(r4300)[0], 1, 1)
DECLARE_JUMP(BC1T,  PCADDR + (iimmediate+1)*4, ((*r4300_cp1_fcr31(&r4300->cp1)) & FCR31_CMP_BIT)!=0, &r4300_regs(r4300)[0], 0, 1)
DECLARE_JUMP(BC1TL, PCADDR + (iimmediate+1)*4, ((*r4300_cp1_fcr31(&r4300->cp1)) & FCR31_CMP_BIT)!=0, &r4300_regs(r4300)[0], 1, 1)

/* Special instructions */

DECLARE_INSTRUCTION(CACHE)
{
    DECLARE_R4300
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ERET)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
    int* cp0_cycle_count = r4300_cp0_cycle_count(&r4300->cp0);

    cp0_update_count(r4300);
    if (cp0_regs[CP0_STATUS_REG] & CP0_STATUS_ERL)
    {
        DebugMessage(M64MSG_ERROR, "error in ERET");
        *r4300_stop(r4300)=1;
    }
    else
    {
        cp0_regs[CP0_STATUS_REG] &= ~CP0_STATUS_EXL;
        generic_jump_to(r4300, cp0_regs[CP0_EPC_REG]);
    }
    r4300->llbit = 0;
    r4300_check_interrupt(r4300, CP0_CAUSE_IP2, r4300->mi->regs[MI_INTR_REG] & r4300->mi->regs[MI_INTR_MASK_REG]); // ???
    r4300->cp0.last_addr = PCADDR;
    if (*cp0_cycle_count >= 0) { gen_interrupt(r4300); }
}

DECLARE_INSTRUCTION(SYNC)
{
    DECLARE_R4300
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SYSCALL)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_SYS;
    exception_general(r4300);
}

/* Trap instructions */

#define DECLARE_TRAP(name, cond) \
DECLARE_INSTRUCTION(name) \
{ \
    DECLARE_R4300 \
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); \
    if (cond) \
    { \
        cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_TR; \
        exception_general(r4300); \
    } \
    else \
    { \
        ADD_TO_PC(1); \
    } \
}

DECLARE_TRAP(TGE, rrs >= rrt)
DECLARE_TRAP(TGEU, (uint64_t) rrs >= (uint64_t) rrt)
DECLARE_TRAP(TGEI, rrs >= (int64_t)iimmediate)
DECLARE_TRAP(TGEIU, (uint64_t)rrs >= (uint64_t)(int64_t)iimmediate)
DECLARE_TRAP(TLT, rrs < rrt)
DECLARE_TRAP(TLTU, (uint64_t) rrs < (uint64_t) rrt)
DECLARE_TRAP(TLTI, rrs < (int64_t)iimmediate)
DECLARE_TRAP(TLTIU, (uint64_t)rrs < (uint64_t)(int64_t)iimmediate)
DECLARE_TRAP(TEQ, rrs == rrt)
DECLARE_TRAP(TEQI, rrs == (int64_t)iimmediate)
DECLARE_TRAP(TNE, rrs != rrt)
DECLARE_TRAP(TNEI, rrs != (int64_t)iimmediate)

#undef DECLARE_TRAP

/* TLB instructions */

DECLARE_INSTRUCTION(TLBP)
{
    DECLARE_R4300
    int i;
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    cp0_regs[CP0_INDEX_REG] |= UINT32_C(0x80000000);
    for (i = 0; i < 32; ++i)
    {
        if (((r4300->cp0.tlb.entries[i].vpn2 & (~r4300->cp0.tlb.entries[i].mask)) ==
                    (((cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFFFFE000)) >> 13) & (~r4300->cp0.tlb.entries[i].mask))) &&
                ((r4300->cp0.tlb.entries[i].g) ||
                 (r4300->cp0.tlb.entries[i].asid == (cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFF)))))
        {
            cp0_regs[CP0_INDEX_REG] = i;
            break;
        }
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TLBR)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    int index;
    index = cp0_regs[CP0_INDEX_REG] & UINT32_C(0x1F);
    cp0_regs[CP0_PAGEMASK_REG] = r4300->cp0.tlb.entries[index].mask << 13;
    cp0_regs[CP0_ENTRYHI_REG] = ((r4300->cp0.tlb.entries[index].vpn2 << 13) | r4300->cp0.tlb.entries[index].asid);
    cp0_regs[CP0_ENTRYLO0_REG] = (r4300->cp0.tlb.entries[index].pfn_even << 6) | (r4300->cp0.tlb.entries[index].c_even << 3)
        | (r4300->cp0.tlb.entries[index].d_even << 2) | (r4300->cp0.tlb.entries[index].v_even << 1)
        | r4300->cp0.tlb.entries[index].g;
    cp0_regs[CP0_ENTRYLO1_REG] = (r4300->cp0.tlb.entries[index].pfn_odd << 6) | (r4300->cp0.tlb.entries[index].c_odd << 3)
        | (r4300->cp0.tlb.entries[index].d_odd << 2) | (r4300->cp0.tlb.entries[index].v_odd << 1)
        | r4300->cp0.tlb.entries[index].g;
    ADD_TO_PC(1);
}

static void TLBWrite(struct r4300_core* r4300, unsigned int idx)
{
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
    uint32_t pc_addr = *r4300_pc(r4300);

    if (pc_addr >= r4300->cp0.tlb.entries[idx].start_even && pc_addr < r4300->cp0.tlb.entries[idx].end_even && r4300->cp0.tlb.entries[idx].v_even)
        return;
    if (pc_addr >= r4300->cp0.tlb.entries[idx].start_odd && pc_addr < r4300->cp0.tlb.entries[idx].end_odd && r4300->cp0.tlb.entries[idx].v_odd)
        return;

    if (r4300->emumode != EMUMODE_PURE_INTERPRETER)
    {
        unsigned int i;
        if (r4300->cp0.tlb.entries[idx].v_even)
        {
            for (i=r4300->cp0.tlb.entries[idx].start_even>>12; i<=r4300->cp0.tlb.entries[idx].end_even>>12; i++)
            {
                if(!r4300->cached_interp.invalid_code[i] &&(r4300->cached_interp.invalid_code[r4300->cp0.tlb.LUT_r[i]>>12] ||
                            r4300->cached_interp.invalid_code[(r4300->cp0.tlb.LUT_r[i]>>12)+0x20000])) {
                    r4300->cached_interp.invalid_code[i] = 1;
                }
                if (!r4300->cached_interp.invalid_code[i])
                {
                    r4300->cached_interp.blocks[i]->xxhash = XXH3_64bits(&r4300->rdram->dram[(r4300->cp0.tlb.LUT_r[i]&0x7FF000)/4], 0x1000);
                    r4300->cached_interp.invalid_code[i] = 1;
                }
                else if (r4300->cached_interp.blocks[i])
                {
                    r4300->cached_interp.blocks[i]->xxhash = 0;
                }
            }
        }
        if (r4300->cp0.tlb.entries[idx].v_odd)
        {
            for (i=r4300->cp0.tlb.entries[idx].start_odd>>12; i<=r4300->cp0.tlb.entries[idx].end_odd>>12; i++)
            {
                if(!r4300->cached_interp.invalid_code[i] &&(r4300->cached_interp.invalid_code[r4300->cp0.tlb.LUT_r[i]>>12] ||
                            r4300->cached_interp.invalid_code[(r4300->cp0.tlb.LUT_r[i]>>12)+0x20000])) {
                    r4300->cached_interp.invalid_code[i] = 1;
                }
                if (!r4300->cached_interp.invalid_code[i])
                {
                    r4300->cached_interp.blocks[i]->xxhash = XXH3_64bits(&r4300->rdram->dram[(r4300->cp0.tlb.LUT_r[i]&0x7FF000)/4], 0x1000);
                    r4300->cached_interp.invalid_code[i] = 1;
                }
                else if (r4300->cached_interp.blocks[i])
                {
                    r4300->cached_interp.blocks[i]->xxhash = 0;
                }
            }
        }
    }

    tlb_unmap(&r4300->cp0.tlb, idx);

    r4300->cp0.tlb.entries[idx].g = (cp0_regs[CP0_ENTRYLO0_REG] & cp0_regs[CP0_ENTRYLO1_REG] & 1);
    r4300->cp0.tlb.entries[idx].pfn_even = (cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x3FFFFFC0)) >> 6;
    r4300->cp0.tlb.entries[idx].pfn_odd = (cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x3FFFFFC0)) >> 6;
    r4300->cp0.tlb.entries[idx].c_even = (cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x38)) >> 3;
    r4300->cp0.tlb.entries[idx].c_odd = (cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x38)) >> 3;
    r4300->cp0.tlb.entries[idx].d_even = (cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x4)) >> 2;
    r4300->cp0.tlb.entries[idx].d_odd = (cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x4)) >> 2;
    r4300->cp0.tlb.entries[idx].v_even = (cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x2)) >> 1;
    r4300->cp0.tlb.entries[idx].v_odd = (cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x2)) >> 1;
    r4300->cp0.tlb.entries[idx].asid = (cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFF));
    r4300->cp0.tlb.entries[idx].vpn2 = (cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFFFFE000)) >> 13;
    //r4300->cp0.tlb.entries[idx].r = (cp0_regs[CP0_ENTRYHI_REG] & 0xC000000000000000LL) >> 62;
    r4300->cp0.tlb.entries[idx].mask = (cp0_regs[CP0_PAGEMASK_REG] & UINT32_C(0x1FFE000)) >> 13;

    r4300->cp0.tlb.entries[idx].start_even = r4300->cp0.tlb.entries[idx].vpn2 << 13;
    r4300->cp0.tlb.entries[idx].end_even = r4300->cp0.tlb.entries[idx].start_even+
        (r4300->cp0.tlb.entries[idx].mask << 12) + UINT32_C(0xFFF);
    r4300->cp0.tlb.entries[idx].phys_even = r4300->cp0.tlb.entries[idx].pfn_even << 12;


    r4300->cp0.tlb.entries[idx].start_odd = r4300->cp0.tlb.entries[idx].end_even+1;
    r4300->cp0.tlb.entries[idx].end_odd = r4300->cp0.tlb.entries[idx].start_odd+
        (r4300->cp0.tlb.entries[idx].mask << 12) + UINT32_C(0xFFF);
    r4300->cp0.tlb.entries[idx].phys_odd = r4300->cp0.tlb.entries[idx].pfn_odd << 12;

    tlb_map(&r4300->cp0.tlb, idx);

    if (r4300->emumode != EMUMODE_PURE_INTERPRETER)
    {
        unsigned int i;
        if (r4300->cp0.tlb.entries[idx].v_even)
        {
            for (i=r4300->cp0.tlb.entries[idx].start_even>>12; i<=r4300->cp0.tlb.entries[idx].end_even>>12; i++)
            {
                if(r4300->cached_interp.blocks[i] && r4300->cached_interp.blocks[i]->xxhash)
                {
                    if(r4300->cached_interp.blocks[i]->xxhash == XXH3_64bits(&r4300->rdram->dram[(r4300->cp0.tlb.LUT_r[i]&0x7FF000)/4], 0x1000)) {
                        r4300->cached_interp.invalid_code[i] = 0;
                    }
                }
            }
        }

        if (r4300->cp0.tlb.entries[idx].v_odd)
        {
            for (i=r4300->cp0.tlb.entries[idx].start_odd>>12; i<=r4300->cp0.tlb.entries[idx].end_odd>>12; i++)
            {
                if(r4300->cached_interp.blocks[i] && r4300->cached_interp.blocks[i]->xxhash)
                {
                    if(r4300->cached_interp.blocks[i]->xxhash == XXH3_64bits(&r4300->rdram->dram[(r4300->cp0.tlb.LUT_r[i]&0x7FF000)/4], 0x1000)) {
                        r4300->cached_interp.invalid_code[i] = 0;
                    }
                }
            }
        }
    }
}

DECLARE_INSTRUCTION(TLBWR)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
    cp0_update_count(r4300);
    cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
        + cp0_regs[CP0_WIRED_REG];
    TLBWrite(r4300, cp0_regs[CP0_RANDOM_REG]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TLBWI)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    TLBWrite(r4300, cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F));
    ADD_TO_PC(1);
}

/* CP0 load/store instructions */

DECLARE_INSTRUCTION(MFC0)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
    uint64_t* cp0_latch = r4300_cp0_latch(&r4300->cp0);

    switch(rfs)
    {
    case CP0_RANDOM_REG:
        cp0_update_count(r4300);
        cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
            + cp0_regs[CP0_WIRED_REG];
        rrt = SE32(cp0_regs[rfs]);
        break;
    case CP0_COUNT_REG:
        cp0_update_count(r4300);
        rrt = SE32(cp0_regs[rfs]);
        break;
    case CP0_UNUSED_7:
    case CP0_UNUSED_21:
    case CP0_UNUSED_22:
    case CP0_UNUSED_23:
    case CP0_UNUSED_24:
    case CP0_UNUSED_25:
    case CP0_UNUSED_31:
        rrt = (*cp0_latch);
        break;
    default:
        rrt = SE32(cp0_regs[rfs]);
        break;
    }

    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMFC0)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
    uint64_t* cp0_latch = r4300_cp0_latch(&r4300->cp0);

    switch(rfs)
    {
    case CP0_RANDOM_REG:
        cp0_update_count(r4300);
        cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
            + cp0_regs[CP0_WIRED_REG];
        rrt = cp0_regs[rfs];
        break;
    case CP0_COUNT_REG:
        cp0_update_count(r4300);
        rrt = cp0_regs[rfs];
        break;
    case CP0_EPC_REG:
        rrt = SE32(cp0_regs[rfs]);
        break;
    case CP0_UNUSED_7:
    case CP0_UNUSED_21:
    case CP0_UNUSED_22:
    case CP0_UNUSED_23:
    case CP0_UNUSED_24:
    case CP0_UNUSED_25:
    case CP0_UNUSED_31:
        rrt = (*cp0_latch);
        break;
    default:
        rrt = cp0_regs[rfs];
        break;
    }

    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MTC0)
{
    DECLARE_R4300
    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
    uint64_t* cp0_latch = r4300_cp0_latch(&r4300->cp0);
    int* cp0_cycle_count = r4300_cp0_cycle_count(&r4300->cp0);

    (*cp0_latch) = rrt32;

    switch(rfs)
    {
    case CP0_INDEX_REG:
        cp0_regs[CP0_INDEX_REG] = rrt32 & UINT32_C(0x8000003F);
        if ((cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F)) > UINT32_C(31))
        {
            DebugMessage(M64MSG_ERROR, "MTC0 instruction writing Index register with TLB index > 31");
            *r4300_stop(r4300)=1;
        }
        break;
    case CP0_RANDOM_REG:
        break;
    case CP0_ENTRYLO0_REG:
        cp0_regs[CP0_ENTRYLO0_REG] = rrt32 & UINT32_C(0x3FFFFFFF);
        break;
    case CP0_ENTRYLO1_REG:
        cp0_regs[CP0_ENTRYLO1_REG] = rrt32 & UINT32_C(0x3FFFFFFF);
        break;
    case CP0_CONTEXT_REG:
        cp0_regs[CP0_CONTEXT_REG] = (rrt32 & UINT32_C(0xFF800000))
            | (cp0_regs[CP0_CONTEXT_REG] & UINT32_C(0x007FFFF0));
        break;
    case CP0_PAGEMASK_REG:
        cp0_regs[CP0_PAGEMASK_REG] = rrt32 & UINT32_C(0x01FFE000);
        break;
    case CP0_WIRED_REG:
        cp0_regs[CP0_WIRED_REG] = rrt32 & UINT32_C(0x0000003F);
        cp0_regs[CP0_RANDOM_REG] = UINT32_C(31);
        break;
    case CP0_BADVADDR_REG:
        break;
    case CP0_COUNT_REG:
        cp0_update_count(r4300);
        r4300->cp0.interrupt_unsafe_state |= INTR_UNSAFE_R4300;
        if (*cp0_cycle_count >= 0) { gen_interrupt(r4300); }
        r4300->cp0.interrupt_unsafe_state &= ~INTR_UNSAFE_R4300;
        translate_event_queue(&r4300->cp0, rrt32);
        break;
    case CP0_ENTRYHI_REG:
        cp0_regs[CP0_ENTRYHI_REG] = rrt32 & UINT32_C(0xFFFFE0FF);
        break;
    case CP0_COMPARE_REG:
        cp0_update_count(r4300);
        remove_event(&r4300->cp0.q, COMPARE_INT);

        /* Add count_per_op to avoid wrong event order in case CP0_COUNT_REG == CP0_COMPARE_REG */
        cp0_regs[CP0_COUNT_REG] += r4300->cp0.count_per_op;
        *cp0_cycle_count += r4300->cp0.count_per_op;
        add_interrupt_event_count(&r4300->cp0, COMPARE_INT, rrt32);
        cp0_regs[CP0_COUNT_REG] -= r4300->cp0.count_per_op;

        /* Update next interrupt in case first event is COMPARE_INT */
        *cp0_cycle_count = cp0_regs[CP0_COUNT_REG] - r4300->cp0.q.first->data.count;
        cp0_regs[CP0_COMPARE_REG] = rrt32;
        cp0_regs[CP0_CAUSE_REG] &= ~CP0_CAUSE_IP7;
        break;
    case CP0_STATUS_REG:
        rrt32 &= ~UINT32_C(0x080000); /* 19th bit isn't writable */

        if((rrt32 & CP0_STATUS_FR) != (cp0_regs[CP0_STATUS_REG] & CP0_STATUS_FR))
            set_fpr_pointers(&r4300->cp1, rrt32);

        cp0_regs[CP0_STATUS_REG] = rrt32;
        ADD_TO_PC(1);
        cp0_update_count(r4300);
        r4300_check_interrupt(r4300, CP0_CAUSE_IP2, r4300->mi->regs[MI_INTR_REG] & r4300->mi->regs[MI_INTR_MASK_REG]); // ???
        r4300->cp0.interrupt_unsafe_state |= INTR_UNSAFE_R4300;
        if (*cp0_cycle_count >= 0) { gen_interrupt(r4300); }
        r4300->cp0.interrupt_unsafe_state &= ~INTR_UNSAFE_R4300;
        return;
    case CP0_CAUSE_REG:
        cp0_regs[CP0_CAUSE_REG] &= ~(CP0_CAUSE_IP0 | CP0_CAUSE_IP1);
        cp0_regs[CP0_CAUSE_REG] |= rrt32 & (CP0_CAUSE_IP0 | CP0_CAUSE_IP1);
        break;
    case CP0_EPC_REG:
        cp0_regs[CP0_EPC_REG] = rrt32;
        break;
    case CP0_PREVID_REG:
        break;
    case CP0_CONFIG_REG:
        cp0_regs[CP0_CONFIG_REG] = (rrt32 & UINT32_C(0x0000000F)) 
                | (cp0_regs[CP0_CONFIG_REG] & UINT32_C(0x00008000))
                | (cp0_regs[CP0_CONFIG_REG] & UINT32_C(0x7FFFFFFF));
        break;
    case CP0_LLADDR_REG:
        cp0_regs[CP0_LLADDR_REG] = rrt32;
        break;
    case CP0_WATCHLO_REG:
        cp0_regs[CP0_WATCHLO_REG] = rrt32;
        break;
    case CP0_WATCHHI_REG:
        cp0_regs[CP0_WATCHHI_REG] = rrt32;
        break;
    case CP0_XCONTEXT_REG:
        break;
    case CP0_CACHEERR_REG:
        break;
    case CP0_PARITYERR_REG:
        cp0_regs[CP0_PARITYERR_REG] = rrt32 & UINT32_C(0x000000FF);
        break;
    case CP0_TAGLO_REG:
        cp0_regs[CP0_TAGLO_REG] = rrt32 & UINT32_C(0x0FFFFFC0);
        break;
    case CP0_TAGHI_REG:
        cp0_regs[CP0_TAGHI_REG] = 0;
        break;
    case CP0_ERROREPC_REG:
        cp0_regs[CP0_ERROREPC_REG] = rrt32;
        break;
    default:
        break;
    }

    ADD_TO_PC(1);
}

/* CP1 load/store instructions */

DECLARE_INSTRUCTION(LWC1)
{
    DECLARE_R4300
    const unsigned char lslfft = lfft;
    const uint32_t lslfaddr = (uint32_t) r4300_regs(r4300)[lfbase] + lfoffset;
    if (check_cop1_unusable(r4300)) { return; }
    ADD_TO_PC(1);

    r4300_read_aligned_word(r4300, lslfaddr, (uint32_t*)r4300_cp1_regs_simple(&r4300->cp1)[lslfft]);
}

DECLARE_INSTRUCTION(LDC1)
{
    DECLARE_R4300
    const unsigned char lslfft = lfft;
    const uint32_t lslfaddr = (uint32_t) r4300_regs(r4300)[lfbase] + lfoffset;
    if (check_cop1_unusable(r4300)) { return; }
    ADD_TO_PC(1);

    r4300_read_aligned_dword(r4300, lslfaddr, (uint64_t*)r4300_cp1_regs_double(&r4300->cp1)[lslfft]);
}

DECLARE_INSTRUCTION(SWC1)
{
    DECLARE_R4300
    const unsigned char lslfft = lfft;
    const uint32_t lslfaddr = (uint32_t) r4300_regs(r4300)[lfbase] + lfoffset;
    if (check_cop1_unusable(r4300)) { return; }
    ADD_TO_PC(1);

    r4300_write_aligned_word(r4300, lslfaddr, *((uint32_t*)(r4300_cp1_regs_simple(&r4300->cp1))[lslfft]), ~UINT32_C(0));
}

DECLARE_INSTRUCTION(SDC1)
{
    DECLARE_R4300
    const unsigned char lslfft = lfft;
    const uint32_t lslfaddr = (uint32_t) r4300_regs(r4300)[lfbase] + lfoffset;
    if (check_cop1_unusable(r4300)) { return; }
    ADD_TO_PC(1);

    r4300_write_aligned_dword(r4300, lslfaddr, *((uint64_t*)(r4300_cp1_regs_double(&r4300->cp1))[lslfft]), ~UINT64_C(0));
}

DECLARE_INSTRUCTION(MFC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    rrt = SE32(*((int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[rfs]));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMFC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    rrt = *((int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[rfs]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CFC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    if (rfs==31)
    {
        rrt = SE32((*r4300_cp1_fcr31(&r4300->cp1)));
    }
    if (rfs==0)
    {
        rrt = SE32((*r4300_cp1_fcr0(&r4300->cp1)));
    }
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DCFC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }

    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    fpu_reset_cause(r4300_cp1_fcr31(&r4300->cp1));

    /* Unimplemented Operation */
    (*r4300_cp1_fcr31(&r4300->cp1)) |= FCR31_CAUSE_UNIMPLOP_BIT;

    cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE;
    exception_general(r4300);
}

DECLARE_INSTRUCTION(MTC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    *((int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[rfs]) = rrt32;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMTC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    *((int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[rfs]) = rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CTC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    if (rfs==31)
    {
        (*r4300_cp1_fcr31(&r4300->cp1)) = rrt32;
        update_x86_rounding_mode(&r4300->cp1);
    }
    //if (((*r4300_cp1_fcr31(&r4300->cp1)) >> 7) & 0x1F) printf("FPU Exception enabled : %x\n",
    //                 (int)(((*r4300_cp1_fcr31(&r4300->cp1)) >> 7) & 0x1F));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DCTC1)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }

    uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);

    fpu_reset_cause(r4300_cp1_fcr31(&r4300->cp1));

    /* Unimplemented Operation */
    (*r4300_cp1_fcr31(&r4300->cp1)) |= FCR31_CAUSE_UNIMPLOP_BIT;

    cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE;
    exception_general(r4300);
}

/* CP2 load/store instructions */

DECLARE_INSTRUCTION(MFC2)
{
    DECLARE_R4300
    if (check_cop2_unusable(r4300)) { return; }
    rrt = SE32(*r4300_cp2_latch(&r4300->cp2));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMFC2)
{
    DECLARE_R4300
    if (check_cop2_unusable(r4300)) { return; }
    rrt = (*r4300_cp2_latch(&r4300->cp2));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CFC2)
{
    DECLARE_R4300
    if (check_cop2_unusable(r4300)) { return; }
    rrt = SE32(*r4300_cp2_latch(&r4300->cp2));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MTC2)
{
    DECLARE_R4300
    if (check_cop2_unusable(r4300)) { return; }
    (*r4300_cp2_latch(&r4300->cp2)) = rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMTC2)
{
    DECLARE_R4300
    if (check_cop2_unusable(r4300)) { return; }
    (*r4300_cp2_latch(&r4300->cp2)) = rrt;
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CTC2)
{
    DECLARE_R4300
    if (check_cop2_unusable(r4300)) { return; }
    (*r4300_cp2_latch(&r4300->cp2)) = rrt;
    ADD_TO_PC(1);
}

/* CP1 computational instructions */

DECLARE_INSTRUCTION(ABS_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    abs_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ABS_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    abs_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ADD_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    add_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ADD_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    add_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DIV_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    div_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DIV_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    div_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MOV_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    mov_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MOV_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    mov_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MUL_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    mul_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MUL_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    mul_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(NEG_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    neg_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(NEG_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    neg_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SQRT_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    sqrt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SQRT_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    sqrt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SUB_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    sub_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(SUB_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    sub_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TRUNC_W_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    trunc_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TRUNC_W_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    trunc_w_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TRUNC_L_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    trunc_l_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TRUNC_L_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    trunc_l_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ROUND_W_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    round_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ROUND_W_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    round_w_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ROUND_L_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    round_l_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ROUND_L_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    round_l_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CEIL_W_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    ceil_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CEIL_W_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    ceil_w_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CEIL_L_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    ceil_l_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CEIL_L_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    ceil_l_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(FLOOR_W_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    floor_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(FLOOR_W_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    floor_w_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(FLOOR_L_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    floor_l_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(FLOOR_L_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    floor_l_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_S_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_s_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_S_W)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_s_w(r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_S_L)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_s_l(r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_D_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_d_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_D_W)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_d_w(r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_D_L)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_d_l(r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_W_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_W_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_w_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_L_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_l_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(CVT_L_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    cvt_l_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

/* CP1 relational instructions */

DECLARE_INSTRUCTION(C_F_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_f_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_F_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_f_d(r4300_cp1_fcr31(&r4300->cp1));
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_UN_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_un_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_UN_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_un_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_EQ_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_eq_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_EQ_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_eq_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_UEQ_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ueq_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_UEQ_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ueq_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_OLT_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_olt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_OLT_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_olt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_ULT_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ult_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_ULT_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ult_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_OLE_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ole_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_OLE_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ole_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_ULE_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ule_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_ULE_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ule_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_SF_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_sf_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_SF_D)
{
    DECLARE_R4300
    c_sf_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGLE_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ngle_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGLE_D)
{
    DECLARE_R4300
    c_ngle_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_SEQ_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_seq_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_SEQ_D)
{
    DECLARE_R4300
    c_seq_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGL_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ngl_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGL_D)
{
    DECLARE_R4300
    c_ngl_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_LT_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_lt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_LT_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_lt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGE_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_nge_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGE_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_nge_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_LE_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_le_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_LE_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_le_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGT_S)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ngt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(C_NGT_D)
{
    DECLARE_R4300
    if (check_cop1_unusable(r4300)) { return; }
    c_ngt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]);
    ADD_TO_PC(1);
}
