/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   Mupen64plus - interpreter_special.def                                 *
 *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
 *   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.          *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

DECLARE_INSTRUCTION(NOP)
{
   ADD_TO_PC(1);
}

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

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

DECLARE_INSTRUCTION(SRA)
{
   rrd = SE32((int32_t) rrt32 >> rsa);
   ADD_TO_PC(1);
}

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

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

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

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

DECLARE_INSTRUCTION(SYSCALL)
{
   g_cp0_regs[CP0_CAUSE_REG] = UINT32_C(8) << 2;
   exception_general();
}

DECLARE_INSTRUCTION(SYNC)
{
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MFHI)
{
   rrd = hi;
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MTHI)
{
   hi = rrs;
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MFLO)
{
   rrd = lo;
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MTLO)
{
   lo = rrs;
   ADD_TO_PC(1);
}

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

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

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

DECLARE_INSTRUCTION(MULT)
{
   int64_t temp;
   temp = rrs * rrt;
   hi = temp >> 32;
   lo = SE32(temp);
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(MULTU)
{
   uint64_t temp;
   temp = (uint32_t) rrs * (uint64_t) ((uint32_t) rrt);
   hi = (int64_t) temp >> 32;
   lo = SE32(temp);
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DIV)
{
   if (rrt32)
   {
     lo = SE32(rrs32 / rrt32);
     hi = SE32(rrs32 % rrt32);
   }
   else DebugMessage(M64MSG_ERROR, "DIV: divide by 0");
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DIVU)
{
   if (rrt32)
   {
     lo = SE32((uint32_t) rrs32 / (uint32_t) rrt32);
     hi = SE32((uint32_t) rrs32 % (uint32_t) rrt32);
   }
   else DebugMessage(M64MSG_ERROR, "DIVU: divide by 0");
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMULT)
{
   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);
   
   lo = result1 | (result2 << 32);
   hi = (result3 & UINT64_C(0xFFFFFFFF)) | (result4 << 32);
   if (sign)
     {
    hi = ~hi;
    if (!lo) hi++;
    else lo = ~lo + 1;
     }
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DMULTU)
{
   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);
   
   lo = result1 | (result2 << 32);
   hi = (result3 & UINT64_C(0xFFFFFFFF)) | (result4 << 32);
   
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DDIV)
{
   if (rrt)
   {
     lo = rrs / rrt;
     hi = rrs % rrt;
   }
   else DebugMessage(M64MSG_ERROR, "DDIV: divide by 0");
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(DDIVU)
{
   if (rrt)
   {
     lo = (uint64_t) rrs / (uint64_t) rrt;
     hi = (uint64_t) rrs % (uint64_t) rrt;
   }
   else DebugMessage(M64MSG_ERROR, "DDIVU: divide by 0");
   ADD_TO_PC(1);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

DECLARE_INSTRUCTION(TEQ)
{
   if (rrs == rrt)
   {
     DebugMessage(M64MSG_ERROR, "trap exception in TEQ");
     stop=1;
   }
   ADD_TO_PC(1);
}

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

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

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

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

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

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