/* * Copyright (C) 2015 Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CISCO SYSTEMS, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if USE(MIPS32_DISASSEMBLER) #include "Mips32Opcode.h" #include #define OPCODE_FMT "%s\t" #define COP1_OPCODE_FMT "%s.%s\t" #define FORMAT_INSTR(_format, ...) \ snprintf(m_formatBuffer, bufferSize - 1, _format, ##__VA_ARGS__) const char *Mips32Opcode::registerName(uint8_t r) { static const char *gpRegisters[] = { "zero", "AT", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "kt0", "kt1", "gp", "sp", "s8", "ra" }; return (r < sizeof(gpRegisters)) ? gpRegisters[r] : "invalid"; } const char *Mips32Opcode::fpRegisterName(uint8_t r) { static const char *fpRegisters[] = { "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" }; return (r < sizeof(fpRegisters)) ? fpRegisters[r] : "invalid"; } void Mips32Opcode::formatSpecialEncodingOpcode(uint8_t op1, uint8_t op2, uint8_t dest, uint8_t shift, uint8_t function) { const char *opcode; OpcodePrintFormat format = Unknown; switch (function) { case 0x00: format = RdRtSa; opcode = "sll"; break; case 0x02: format = RdRtSa; opcode = "srl"; break; case 0x03: format = RdRtSa; opcode = "sra"; break; case 0x04: format = RdRtRs; opcode = "sllv"; break; case 0x06: format = RdRtRs; opcode = "srlv"; break; case 0x07: format = RdRtRs; opcode = "srav"; break; case 0x08: format = Rs; opcode = "jr"; break; case 0x09: format = (dest != 0x1f) ? RdRs : Rs; opcode = "jalr"; break; case 0x10: format = Rd; opcode = "mfhi"; break; case 0x11: format = Rs; opcode = "mthi"; break; case 0x12: format = Rd; opcode = "mflo"; break; case 0x13: format = Rs; opcode = "mtlo"; break; case 0x18: format = RsRt; opcode = "mult"; break; case 0x19: format = RsRt; opcode = "multu"; break; case 0x1a: format = RsRt; opcode = "div"; break; case 0x1b: format = RsRt; opcode = "divu"; break; case 0x20: format = RdRsRt; opcode = "add"; break; case 0x21: if (op2) { format = RdRsRt; opcode = "addu"; } else { format = RdRs; opcode = "move"; } break; case 0x22: format = RdRsRt; opcode = "sub"; break; case 0x23: format = RdRsRt; opcode = "subu"; break; case 0x24: format = RdRsRt; opcode = "and"; break; case 0x25: format = RdRsRt; opcode = "or"; break; case 0x26: format = RdRsRt; opcode = "xor"; break; case 0x27: format = RdRsRt; opcode = "nor"; break; case 0x2a: format = RdRsRt; opcode = "slt"; break; case 0x2b: format = RdRsRt; opcode = "sltu"; break; } switch (format) { case Rs: FORMAT_INSTR(OPCODE_FMT "%s", opcode, registerName(op1)); break; case Rd: FORMAT_INSTR(OPCODE_FMT "%s", opcode, registerName(dest)); break; case RdRs: FORMAT_INSTR(OPCODE_FMT "%s, %s", opcode, registerName(dest), registerName(op1)); break; case RsRt: FORMAT_INSTR(OPCODE_FMT "%s, %s", opcode, registerName(op1), registerName(op2)); break; case RdRtRs: FORMAT_INSTR(OPCODE_FMT "%s, %s, %s", opcode, registerName(dest), registerName(op2), registerName(op1)); break; case RdRsRt: FORMAT_INSTR(OPCODE_FMT "%s, %s, %s", opcode, registerName(dest), registerName(op1), registerName(op2)); break; case RdRtSa: FORMAT_INSTR(OPCODE_FMT "%s, %s, %d", opcode, registerName(dest), registerName(op2), shift); break; default: FORMAT_INSTR("unknown special encoding opcode 0x%x", function); break; } } void Mips32Opcode::formatSpecial2EncodingOpcode(uint8_t op1, uint8_t op2, uint8_t dest, uint8_t function) { if (function == 0x02) { FORMAT_INSTR(OPCODE_FMT "%s, %s, %s", "mul", registerName(dest), registerName(op1), registerName(op2)); return; } FORMAT_INSTR("unknown special2 encoding opcode 0x%x", function); } void Mips32Opcode::formatJumpEncodingOpcode(uint32_t iOp, uint32_t index, uint32_t* opcodePtr) { if ((iOp != 0x02) && (iOp != 0x03)) { FORMAT_INSTR("unknown jump encoding opcode 0x%x", iOp); return; } FORMAT_INSTR(OPCODE_FMT "0x%x", (iOp == 0x02) ? "j" : "jal", (reinterpret_cast(opcodePtr+1) & 0xf0000000) | (index << 2)); } void Mips32Opcode::formatREGIMMEncodingOpcode(uint8_t rs, uint8_t rt, int16_t imm, uint32_t* opcodePtr) { const char *opcodes[] = { "bltz", "bgez", "bltzl", "bgezl" }; if (rt < sizeof(opcodes)) FORMAT_INSTR(OPCODE_FMT "%s, 0x%x", opcodes[rt], registerName(rs), reinterpret_cast(opcodePtr+1) + (imm << 2)); else FORMAT_INSTR("unknown REGIMM encoding opcode 0x%x", rt); } void Mips32Opcode::formatImmediateEncodingOpcode(uint32_t iOp, uint8_t rs, uint8_t rt, int16_t imm, uint32_t* opcodePtr) { const char *opcode; OpcodePrintFormat format = Unknown; switch (iOp) { case 0x04: if (!rs && !rt) { format = Addr; opcode = "b"; } else { format = RsRtAddr; opcode = "beq"; } break; case 0x05: format = RsRtAddr; opcode = "bne"; break; case 0x06: format = RsRtAddr; opcode = "blez"; break; case 0x07: format = RsRtAddr; opcode = "bgtz"; break; case 0x08: format = RtRsImm; opcode = "addi"; break; case 0x09: if (rs) { format = RtRsImm; opcode = "addiu"; } else { format = RtUImm; opcode = "li"; } break; case 0x0a: format = RtRsImm; opcode = "slti"; break; case 0x0b: format = RtRsImm; opcode = "sltiu"; break; case 0x0c: format = RtRsImm; opcode = "andi"; break; case 0x0d: format = RtRsImm; opcode = "ori"; break; case 0x0e: format = RtRsImm; opcode = "xori"; break; case 0x0f: format = RtUImm; opcode = "lui"; break; case 0x20: format = RtOffsetBase; opcode = "lb"; break; case 0x21: format = RtOffsetBase; opcode = "lh"; break; case 0x22: format = RtOffsetBase; opcode = "lwl"; break; case 0x23: format = RtOffsetBase; opcode = "lw"; break; case 0x24: format = RtOffsetBase; opcode = "lbu"; break; case 0x25: format = RtOffsetBase; opcode = "lhu"; break; case 0x26: format = RtOffsetBase; opcode = "lwr"; break; case 0x28: format = RtOffsetBase; opcode = "sb"; break; case 0x29: format = RtOffsetBase; opcode = "sh"; break; case 0x2a: format = RtOffsetBase; opcode = "swl"; break; case 0x2b: format = RtOffsetBase; opcode = "sw"; break; case 0x2e: format = RtOffsetBase; opcode = "swr"; break; case 0x35: format = FtOffsetBase; opcode = "ldc1"; break; case 0x3d: format = FtOffsetBase; opcode = "sdc1"; break; } switch (format) { case Addr: FORMAT_INSTR(OPCODE_FMT "0x%x", opcode, reinterpret_cast(opcodePtr+1) + (imm << 2)); break; case RtUImm: FORMAT_INSTR(OPCODE_FMT "%s, 0x%hx", opcode, registerName(rt), imm); break; case RtRsImm: FORMAT_INSTR(OPCODE_FMT "%s, %s, %d", opcode, registerName(rt), registerName(rs), imm); break; case RsRtAddr: FORMAT_INSTR(OPCODE_FMT "%s, %s, 0x%x", opcode, registerName(rs), registerName(rt), reinterpret_cast(opcodePtr+1) + (imm << 2)); break; case RtOffsetBase: FORMAT_INSTR(OPCODE_FMT "%s, %d(%s)", opcode, registerName(rt), imm, registerName(rs)); break; case FtOffsetBase: FORMAT_INSTR(OPCODE_FMT "%s, %d(%s)", opcode, fpRegisterName(rt), imm, registerName(rs)); break; default: FORMAT_INSTR("unknown immediate encoding opcode 0x%x", iOp); break; } } void Mips32Opcode::formatCOP1Opcode(uint8_t fmt, uint8_t ft, uint8_t fs, uint8_t fd, uint8_t func) { const char *opcode; const char *suffix; OpcodePrintFormat format = Unknown; if (fmt < 0x10) { switch (fmt) { case 0x00: opcode = "mfc1"; break; case 0x04: opcode = "mtc1"; break; default: FORMAT_INSTR("unknown COP1 rs 0x%x", fmt); return; } FORMAT_INSTR(OPCODE_FMT "%s, %s", opcode, registerName(ft), fpRegisterName(fs)); return; } switch (fmt) { case 0x10: suffix = "s"; break; case 0x11: suffix = "d"; break; case 0x14: suffix = "w"; break; case 0x15: suffix = "l"; break; case 0x16: suffix = "ps"; break; default: FORMAT_INSTR("unknown COP1 fmt 0x%x", fmt); return; } switch (func) { case 0x00: format = FdFsFt; opcode = "add"; break; case 0x01: format = FdFsFt; opcode = "sub"; break; case 0x02: format = FdFsFt; opcode = "mul"; break; case 0x03: format = FdFsFt; opcode = "div"; break; case 0x04: format = FdFs; opcode = "sqrt"; break; case 0x05: format = FdFs; opcode = "abs"; break; case 0x06: format = FdFs; opcode = "mov"; break; case 0x07: format = FdFs; opcode = "neg"; break; case 0x08: format = FdFs; opcode = "round.l"; break; case 0x09: format = FdFs; opcode = "trunc.l"; break; case 0x0a: format = FdFs; opcode = "ceil.l"; break; case 0x0b: format = FdFs; opcode = "floor.l"; break; case 0x0c: format = FdFs; opcode = "round.w"; break; case 0x0d: format = FdFs; opcode = "trunc.w"; break; case 0x0e: format = FdFs; opcode = "ceil.w"; break; case 0x0f: format = FdFs; opcode = "floor.w"; break; case 0x20: format = FdFs; opcode = "cvt.s"; break; case 0x21: format = FdFs; opcode = "cvt.d"; break; case 0x24: format = FdFs; opcode = "cvt.w"; break; case 0x25: format = FdFs; opcode = "cvt.l"; break; } switch (format) { case FdFs: FORMAT_INSTR(COP1_OPCODE_FMT "%s, %s", opcode, suffix, fpRegisterName(fd), fpRegisterName(fs)); break; case FdFsFt: FORMAT_INSTR(COP1_OPCODE_FMT "%s, %s, %s", opcode, suffix, fpRegisterName(fd), fpRegisterName(fs), fpRegisterName(ft)); break; default: FORMAT_INSTR("unknown COP1 opcode 0x%x", func); break; } } void Mips32Opcode::formatCOP1FPCompareOpcode(uint8_t fmt, uint8_t ft, uint8_t fs, uint8_t cc, uint8_t cond) { const char *suffix; static const char *opcodes[] = { "c.f", "c.un", "c.eq", "c.ueq", "c.olt", "c.ult", "c.ole", "c.ule", "c.sf", "c.ngle", "c.seq", "c.ngl", "c.lt", "c.nge", "c.le", "c.ngt" }; ASSERT(cond < sizeof(opcdoes)); switch (fmt) { case 0x10: suffix = "s"; break; case 0x11: suffix = "d"; break; case 0x16: suffix = "ps"; break; default: FORMAT_INSTR("unknown COP1 fmt 0x%x", fmt); return; } if (!cc) FORMAT_INSTR(COP1_OPCODE_FMT "%s, %s", opcodes[cond], suffix, fpRegisterName(fs), fpRegisterName(ft)); else FORMAT_INSTR(COP1_OPCODE_FMT "%d, %s, %s", opcodes[cond], suffix, cc, fpRegisterName(fs), fpRegisterName(ft)); } void Mips32Opcode::formatCOP1BCOpcode(uint8_t cc, uint8_t ndtf, int16_t offset, uint32_t* opcodePtr) { static const char *opcodes[] = { "bc1f", "bc1t", "bc1fl", "bc1tl" }; ASSERT(ndtf < sizeof(opcodes)); if (!cc) FORMAT_INSTR(OPCODE_FMT "0x%x", opcodes[ndtf], reinterpret_cast(opcodePtr+1) + (offset << 2)); else FORMAT_INSTR(OPCODE_FMT "%d, 0x%x", opcodes[ndtf], cc, reinterpret_cast(opcodePtr+1) + (offset << 2)); } const char* Mips32Opcode::disassemble(uint32_t* opcodePtr) { uint32_t opcode = *opcodePtr; uint32_t iOp = (opcode >> 26) & 0x3f; if (!opcode) FORMAT_INSTR(OPCODE_FMT, "nop"); else if (!iOp) { uint8_t op1 = (opcode >> 21) & 0x1f; uint8_t op2 = (opcode >> 16) & 0x1f; uint8_t dst = (opcode >> 11) & 0x1f; uint8_t shft = (opcode >> 6) & 0x1f; uint8_t func = opcode & 0x3f; formatSpecialEncodingOpcode(op1, op2, dst, shft, func); } else if ((iOp == 0x02) || (iOp == 0x03)) { uint32_t index = opcode & 0x3ffffff; formatJumpEncodingOpcode(iOp, index, opcodePtr); } else if (iOp == 0x11) { uint8_t fmt = (opcode >> 21) & 0x1f; if (fmt == 0x08) { uint8_t cc = (opcode >> 18) & 0x07; uint8_t ndtf = (opcode >> 16) & 0x03; int16_t offset = opcode & 0xffff; formatCOP1BCOpcode(cc, ndtf, offset, opcodePtr); } else if ((opcode & 0xf0) == 0x30) { uint8_t ft = (opcode >> 16) & 0x1f; uint8_t fs = (opcode >> 11) & 0x1f; uint8_t cc = (opcode >> 8) & 0x07; uint8_t cond = opcode & 0x0f; formatCOP1FPCompareOpcode(fmt, ft, fs, cc, cond); } else { uint8_t ft = (opcode >> 16) & 0x1f; uint8_t fs = (opcode >> 11) & 0x1f; uint8_t fd = (opcode >> 6) & 0x1f; uint8_t func = opcode & 0x3f; formatCOP1Opcode(fmt, ft, fs, fd, func); } } else if (iOp == 0x1c) { uint8_t op1 = (opcode >> 21) & 0x1f; uint8_t op2 = (opcode >> 16) & 0x1f; uint8_t dst = (opcode >> 11) & 0x1f; uint8_t func = opcode & 0x3f; formatSpecial2EncodingOpcode(op1, op2, dst, func); } else { uint8_t rs = (opcode >> 21) & 0x1f; uint8_t rt = (opcode >> 16) & 0x1f; int16_t imm = opcode & 0xffff; if (iOp == 0x01) formatREGIMMEncodingOpcode(rs, rt, imm, opcodePtr); else formatImmediateEncodingOpcode(iOp, rs, rt, imm, opcodePtr); } return m_formatBuffer; } #endif // USE(MIPS32_DISASSEMBLER)