/* * z64 * * This program is free software; you can redistribute it and/ * or modify it under the terms of the GNU General Public Li- * cence as published by the Free Software Foundation; either * version 2 of the Licence, or any later version. * * This program is distributed in the hope that it will be use- * ful, but WITHOUT ANY WARRANTY; without even the implied war- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public Licence for more details. * * You should have received a copy of the GNU General Public * Licence along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, * 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 base = (op >> 21) & 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; iset, dest, (i>>1)&7); break; case RSP_LLV: for (i=index; iset, dest, (i>>1)&7); break; case RSP_LDV: for (i=index; iset, 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) fatalerror("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; iused, dest, (i>>1)&7); break; case RSP_SLV: for (i=index; iused, dest, (i>>1)&7); break; case RSP_SDV: for (i=index; iused, 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, eaoffset; int vs = dest; int ve = dest + 8; if (ve > 32) ve = 32; element = 8 - (index >> 1); if (index & 0x1) fatalerror("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: fatalerror("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; } } }