RSP LLE plugin. Compile and run (slowly, eat 50% CPU) on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-rsp-z64 / src / rsp_opinfo.cpp
diff --git a/source/mupen64plus-rsp-z64/src/rsp_opinfo.cpp b/source/mupen64plus-rsp-z64/src/rsp_opinfo.cpp
new file mode 100644 (file)
index 0000000..d02b561
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ * z64
+ *
+ * Copyright (C) 2007  ziggy
+ *
+ * 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.
+ *
+**/
+
+#include "rsp_opinfo.h"
+
+static const int vector_elements_2[16][8] =
+{
+    { 0, 1, 2, 3, 4, 5, 6, 7 },     // none
+    { 0, 1, 2, 3, 4, 5, 6, 7 },     // ???
+    { 0, 0, 2, 2, 4, 4, 6, 6 },     // 0q
+    { 1, 1, 3, 3, 5, 5, 7, 7 },     // 1q
+    { 0, 0, 0, 0, 4, 4, 4, 4 },     // 0h
+    { 1, 1, 1, 1, 5, 5, 5, 5 },     // 1h
+    { 2, 2, 2, 2, 6, 6, 6, 6 },     // 2h
+    { 3, 3, 3, 3, 7, 7, 7, 7 },     // 3h
+    { 0, 0, 0, 0, 0, 0, 0, 0 },     // 0
+    { 1, 1, 1, 1, 1, 1, 1, 1 },     // 1
+    { 2, 2, 2, 2, 2, 2, 2, 2 },     // 2
+    { 3, 3, 3, 3, 3, 3, 3, 3 },     // 3
+    { 4, 4, 4, 4, 4, 4, 4, 4 },     // 4
+    { 5, 5, 5, 5, 5, 5, 5, 5 },     // 5
+    { 6, 6, 6, 6, 6, 6, 6, 6 },     // 6
+    { 7, 7, 7, 7, 7, 7, 7, 7 },     // 7
+};
+
+void rsp_get_opinfo(UINT32 op, rsp_opinfo_t * info)
+{
+    int op2;
+    int i;
+    info->op = op;
+    switch (op>>26) {
+    case 0:     /* SPECIAL */
+        op2 = RSP_SPECIAL_OFFS + (op&0x3f);
+        break;
+    case 0x12:  /* COP2 */
+        if (((op>>21)&0x1f) >= 0x10)
+            op2 = RSP_COP2_2_OFFS + (op & 0x3f);
+        else
+            op2 = RSP_COP2_1_OFFS + ((op >> 21) & 0x1f);
+        break;
+    case 0x32:  /* LWC2 */
+        op2 = RSP_LWC2_OFFS + ((op>>11)&0x1f);
+        break;
+    case 0x3a:  /* SWC2 */
+        op2 = RSP_SWC2_OFFS + ((op>>11)&0x1f);
+        break;
+    default:
+        op2 = RSP_BASIC_OFFS + (op>>26);
+        if (op2 == RSP_REGIMM) {
+            switch (RTREG)
+            {
+            case 0x00: /* BLTZ */
+                op2 = RSP_BLTZ;
+                break;
+            case 0x01: /* BGEZ */
+                op2 = RSP_BGEZ;
+                break;
+            case 0x11: /* BGEZAL */
+                op2 = RSP_BGEZAL;
+                break;
+            }
+        }
+    }
+    info->op2 = op2;
+
+    memset(&info->used, 0, sizeof(info->used));
+    memset(&info->set, 0, sizeof(info->set));
+    info->used.accu = info->used.flag = 0;
+    info->set.accu = info->set.flag = 0;
+    info->flags = 0;
+
+    int dest = (op >> 16) & 0x1f;
+    int index = (op >> 7) & 0xf;
+    int offset = (op & 0x7f);
+    if (offset & 0x40)
+        offset |= 0xffffffc0;
+
+    switch(op2) {
+    case RSP_SPECIAL:
+    case RSP_BLTZ:
+        info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC;
+        break;
+    case RSP_BGEZ:
+        info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC;
+        break;
+    case RSP_BGEZAL:
+        info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_LINK | RSP_OPINFO_USEPC;
+        break;
+    case RSP_J:
+        info->flags = RSP_OPINFO_JUMP;
+        break;
+    case RSP_JAL:
+        info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_LINK | RSP_OPINFO_USEPC;
+        break;
+    case RSP_BEQ:
+    case RSP_BNE:
+    case RSP_BLEZ:
+    case RSP_BGTZ:
+        info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC;
+        break;
+    case RSP_ADDI:
+    case RSP_ADDIU:
+    case RSP_SLTI:
+    case RSP_SLTIU:
+    case RSP_ANDI:
+    case RSP_ORI:
+    case RSP_XORI:
+    case RSP_LUI:
+    case RSP_COP0:
+    case RSP_LB:
+    case RSP_LH:
+    case RSP_LW:
+    case RSP_LBU:
+    case RSP_LHU:
+    case RSP_SB:
+    case RSP_SH:
+    case RSP_SW:
+        break;
+
+    case RSP_SLL:
+    case RSP_SRL:
+    case RSP_SRA:
+    case RSP_SLLV:
+    case RSP_SRLV:
+    case RSP_SRAV:
+        break;
+
+    case RSP_JR:
+        info->flags = RSP_OPINFO_JUMP;
+        break;
+    case RSP_JALR:
+        info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_LINK | RSP_OPINFO_USEPC;
+        break;
+    case RSP_BREAK:
+        info->flags = RSP_OPINFO_BREAK;
+        break;
+
+    case RSP_ADD:
+    case RSP_ADDU:
+    case RSP_SUB:
+    case RSP_SUBU:
+    case RSP_AND:
+    case RSP_OR:
+    case RSP_XOR:
+    case RSP_NOR:
+    case RSP_SLT:
+    case RSP_SLTU:
+        break;
+
+    case RSP_MFC2:
+        {
+            int el = op >> 7;
+            RSP_SET_VEC_I(info->used, VS1REG, ((el+0)&0xf)>>1);
+            RSP_SET_VEC_I(info->used, VS1REG, ((el+1)&0xf)>>1);
+            break;
+        }
+    case RSP_CFC2:
+        RSP_SET_FLAG_I(info->used, RDREG & 3);
+        break;
+    case RSP_MTC2:
+        {
+            int el = op >> 7;
+            RSP_SET_VEC_I(info->set, VS1REG, ((el+0)&0xf)>>1);
+            RSP_SET_VEC_I(info->set, VS1REG, ((el+1)&0xf)>>1);
+            break;
+        }
+    case RSP_CTC2:
+        RSP_SET_FLAG_I(info->set, RDREG & 3);
+        break;
+
+
+    case RSP_LBV:
+        RSP_SET_VEC_I(info->set, dest, index>>1);
+        break;
+    case RSP_LSV:
+        for (i=index; i<index+2; i++)
+            RSP_SET_VEC_I(info->set, dest, (i>>1)&7);
+        break;
+    case RSP_LLV:
+        for (i=index; i<index+4; i++)
+            RSP_SET_VEC_I(info->set, dest, (i>>1)&7);
+        break;
+    case RSP_LDV:
+        for (i=index; i<index+8; i++)
+            RSP_SET_VEC_I(info->set, dest, (i>>1)&7);
+        break;
+    case RSP_LQV:
+    case RSP_LRV:
+        // WARNING WARNING WARNING
+        // we assume this instruction always used to load the full vector
+        // i.e. the address is always 16 bytes aligned
+        //       for (i=0; i<8; i++)
+        //         RSP_SET_VEC_I(info->set, dest, i);
+        break;
+    case RSP_LPV:
+    case RSP_LUV:
+    case RSP_LHV:
+    case RSP_LWV:
+        for (i=0; i<8; i++)
+            RSP_SET_VEC_I(info->set, dest, i);
+        break;
+    case RSP_LFV:
+        for (i=(index>>1); i<(index>>1)+4; i++)
+            RSP_SET_VEC_I(info->set, dest, i);
+        break;
+    case RSP_LTV:
+        {
+            // 31       25      20      15      10     6        0
+            // --------------------------------------------------
+            // | 110010 | BBBBB | TTTTT | 01011 | IIII | Offset |
+            // --------------------------------------------------
+            //
+            // Loads one element to maximum of 8 vectors, while incrementing element index
+
+            // FIXME: has a small problem with odd indices
+
+            int element;
+            int vs = dest;
+            int ve = dest + 8;
+            if (ve > 32)
+                ve = 32;
+
+            element = 7 - (index >> 1);
+
+            if (index & 1)
+                log(M64MSG_ERROR, "RSP: LTV: index = %d\n", index);
+
+            for (i=vs; i < ve; i++)
+            {
+                element = ((8 - (index >> 1) + (i-vs)) << 1);
+                RSP_SET_VEC_I(info->set, i, (element & 0xf)>>1);
+                RSP_SET_VEC_I(info->set, i, ((element+1) & 0xf)>>1);
+            }
+            break;
+        }
+
+    case RSP_SBV:
+        RSP_SET_VEC_I(info->used, dest, index>>1);
+        break;
+    case RSP_SSV:
+        for (i=index; i<index+2; i++)
+            RSP_SET_VEC_I(info->used, dest, (i>>1)&7);
+        break;
+    case RSP_SLV:
+        for (i=index; i<index+4; i++)
+            RSP_SET_VEC_I(info->used, dest, (i>>1)&7);
+        break;
+    case RSP_SDV:
+        for (i=index; i<index+8; i++)
+            RSP_SET_VEC_I(info->used, dest, (i>>1)&7);
+        break;
+    case RSP_SQV:
+    case RSP_SRV:
+        // WARNING WARNING WARNING
+        // we assume this instruction always used to store the full vector
+        // i.e. the address is always 16 bytes aligned
+        for (i=0; i<8; i++)
+            RSP_SET_VEC_I(info->used, dest, i);
+        break;
+    case RSP_SPV:
+    case RSP_SUV:
+    case RSP_SHV:
+    case RSP_SWV:
+        for (i=0; i<8; i++)
+            RSP_SET_VEC_I(info->used, dest, i);
+        break;
+    case RSP_SFV:
+        for (i=(index>>1); i<(index>>1)+4; i++)
+            RSP_SET_VEC_I(info->used, dest, i);
+        break;
+    case RSP_STV:
+        {
+            // 31       25      20      15      10     6        0
+            // --------------------------------------------------
+            // | 111010 | BBBBB | TTTTT | 01011 | IIII | Offset |
+            // --------------------------------------------------
+            //
+            // Stores one element from maximum of 8 vectors, while incrementing element index
+
+            int element;
+            int vs = dest;
+            int ve = dest + 8;
+            if (ve > 32)
+                ve = 32;
+
+            element = 8 - (index >> 1);
+            if (index & 0x1)
+                log(M64MSG_ERROR, "RSP: STV: index = %d at %08X\n", index, rsp.ppc);
+
+            for (i=vs; i < ve; i++)
+            {
+                RSP_SET_VEC_I(info->used, i, element & 0x7);
+                element++;
+            }
+            break;
+        }
+
+    case RSP_VMULF:
+    case RSP_VMULU:
+    case RSP_VMUDL:
+    case RSP_VMUDM:
+    case RSP_VMUDN:
+    case RSP_VMUDH:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 14);
+            }
+            break;
+        }
+    case RSP_VMACF:
+    case RSP_VMACU:
+    case RSP_VMADL:
+    case RSP_VMADM:
+    case RSP_VMADN:
+    case RSP_VMADH:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->used, i, 14);
+                RSP_SET_ACCU_I(info->set, i, 14);
+            }
+            break;
+        }
+    case RSP_VADD:
+    case RSP_VSUB:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->used, 0);
+            RSP_SET_FLAG_I(info->set, 0);
+            break;
+        }
+    case RSP_VABS:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            break;
+        }
+    case RSP_VADDC:
+    case RSP_VSUBC:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->set, 0);
+            break;
+        }
+    case RSP_VSAW:
+        switch (EL)
+        {
+        case 0x08:             // VSAWH
+            {
+                for (i=0; i < 8; i++)
+                {
+                    RSP_SET_VEC_I(info->set, VDREG, i);
+                    RSP_SET_ACCU_I(info->used, i, 8);
+                }
+                break;
+            }
+        case 0x09:             // VSAWM
+            {
+                for (i=0; i < 8; i++)
+                {
+                    RSP_SET_VEC_I(info->set, VDREG, i);
+                    RSP_SET_ACCU_I(info->used, i, 4);
+                }
+                break;
+            }
+        case 0x0a:             // VSAWL
+            {
+                for (i=0; i < 8; i++)
+                {
+                    RSP_SET_VEC_I(info->set, VDREG, i);
+                    RSP_SET_ACCU_I(info->used, i, 2);
+                }
+                break;
+            }
+        default:
+            log(M64MSG_ERROR, "RSP: VSAW: el = %d\n", EL);
+        }
+        break;
+    case RSP_VLT:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->set, 0);
+            RSP_SET_FLAG_I(info->set, 1);
+            break;
+        }
+    case RSP_VEQ:
+    case RSP_VNE:
+    case RSP_VGE:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->used, 0);
+            RSP_SET_FLAG_I(info->set, 0);
+            RSP_SET_FLAG_I(info->set, 1);
+            break;
+        }
+    case RSP_VCL:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->used, 0);
+            RSP_SET_FLAG_I(info->used, 1);
+            RSP_SET_FLAG_I(info->set, 0);
+            RSP_SET_FLAG_I(info->set, 1);
+            RSP_SET_FLAG_I(info->set, 2);
+            break;
+        }
+    case RSP_VCH:
+    case RSP_VCR:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->set, 0);
+            RSP_SET_FLAG_I(info->set, 1);
+            RSP_SET_FLAG_I(info->set, 2);
+            break;
+        }
+    case RSP_VMRG:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            RSP_SET_FLAG_I(info->used, 1);
+            break;
+        }
+    case RSP_VAND:
+    case RSP_VNAND:
+    case RSP_VOR:
+    case RSP_VNOR:
+    case RSP_VXOR:
+    case RSP_VNXOR:
+        {
+            for (i=0; i < 8; i++)
+            {
+                int sel = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS1REG, i);
+                RSP_SET_VEC_I(info->used, VS2REG, sel);
+                RSP_SET_VEC_I(info->set, VDREG, i);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+            break;
+        }
+    case RSP_VRCP:
+    case RSP_VRCPL:
+    case RSP_VRCPH:
+    case RSP_VRSQL:
+    case RSP_VRSQH:
+        {
+            int del = (VS1REG & 7);
+            int sel = VEC_EL_2(EL, del);
+
+            RSP_SET_VEC_I(info->used, VS2REG, sel);
+
+            for (i=0; i < 8; i++)
+            {
+                int element = VEC_EL_2(EL, i);
+                RSP_SET_VEC_I(info->used, VS2REG, element);
+                RSP_SET_ACCU_I(info->set, i, 2);
+            }
+
+            RSP_SET_VEC_I(info->set, VDREG, del);
+            break;
+        }
+    case RSP_VMOV:
+        {
+            int element = VS1REG & 7;
+            RSP_SET_VEC_I(info->used, VS2REG, VEC_EL_2(EL, 7-element));
+            RSP_SET_VEC_I(info->set, VDREG, element);
+            break;
+        }
+
+    default:
+        {
+            //       char string[200];
+            //       rsp_dasm_one(string, 0x800, op);
+            //       if (strcmp(string, "???")) {
+            //         printf("%s\n", string);
+            //         printf("unimplemented opcode\n");
+            //       }
+            break;
+        }
+    }
+}