diff options
| author | Galen Williamson <galen@vector35.com> | 2025-06-29 11:23:58 -0400 |
|---|---|---|
| committer | Galen Williamson <galen@vector35.com> | 2025-06-29 18:28:48 -0400 |
| commit | e8206bc27e1633c7e4c99907f43bf67d2f200cd5 (patch) | |
| tree | 2315bec284e9bb692a0eb8986578fe18e2289d23 /arch/powerpc/decode/vle16.c | |
| parent | 91f1eccb6c41a7ef60fd66387ffae075f88141a4 (diff) | |
[powerpc] Full merge of PowerPC disassembler PRs, including:
* Remove dependency on capstone for PowerPC disassembly #6292
* Add support for PowerPC VLE instruction set #6740
* Add support for paired-single instructions #6821
* Various post-merge fixes and tweaks to disassembly and lifting
* Removal of dependence on capstone for assembler's scoring mechanism (capstone currently disabled, but not removed from codebase yet)
Diffstat (limited to 'arch/powerpc/decode/vle16.c')
| -rw-r--r-- | arch/powerpc/decode/vle16.c | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/arch/powerpc/decode/vle16.c b/arch/powerpc/decode/vle16.c new file mode 100644 index 00000000..7500e6ba --- /dev/null +++ b/arch/powerpc/decode/vle16.c @@ -0,0 +1,790 @@ +#include <string.h> + +#include "decode.h" +#include "priv.h" + +static InstructionId Decode16Vle0x00(uint16_t word16, uint32_t decodeFlags) +{ + switch (word16) { + case 0x0000: + return PPC_ID_VLE_SE_ILLEGAL; + + case 0x0001: + return PPC_ID_VLE_SE_ISYNC; + + case 0x0002: + return PPC_ID_VLE_SE_SC; + + case 0x0004: + case 0x0005: + return PPC_ID_VLE_SE_BLRx; + + case 0x0006: + case 0x0007: + return PPC_ID_VLE_SE_BCTRx; + + case 0x0008: + return PPC_ID_VLE_SE_RFI; + + case 0x0009: + return PPC_ID_VLE_SE_RFCI; + + case 0x000a: + return PPC_ID_VLE_SE_RFDI; + + case 0x000b: + return PPC_ID_VLE_SE_RFMCI; + + default: + ; + } + + uint32_t subop = (word16 >> 4) & 0xfff; + switch (subop) { + case 0x002: + return PPC_ID_VLE_SE_NOT; + + case 0x003: + return PPC_ID_VLE_SE_NEG; + + case 0x008: + return PPC_ID_VLE_SE_MFLR; + + case 0x009: + return PPC_ID_VLE_SE_MTLR; + + case 0x00a: + return PPC_ID_VLE_SE_MFCTR; + + case 0x00b: + return PPC_ID_VLE_SE_MTCTR; + + case 0x00c: + return PPC_ID_VLE_SE_EXTZB; + + case 0x00d: + return PPC_ID_VLE_SE_EXTSB; + + case 0x00e: + return PPC_ID_VLE_SE_EXTZH; + + case 0x00f: + return PPC_ID_VLE_SE_EXTSH; + + default: + return PPC_ID_INVALID;; + } +} +static InstructionId Decode16Vle(uint16_t word16, uint32_t decodeFlags) +{ + uint8_t hi = (word16 >> 8) & 0xff; + switch (hi) { + case 0x00: + return Decode16Vle0x00(word16, decodeFlags); + + case 0x01: + return PPC_ID_VLE_SE_MR; + + case 0x02: + return PPC_ID_VLE_SE_MTAR; + + case 0x03: + return PPC_ID_VLE_SE_MFAR; + + case 0x04: + return PPC_ID_VLE_SE_ADD; + + case 0x05: + return PPC_ID_VLE_SE_MULLW; + + case 0x06: + return PPC_ID_VLE_SE_SUB; + + case 0x07: + return PPC_ID_VLE_SE_SUBF; + + case 0x0c: + return PPC_ID_VLE_SE_CMP; + + case 0x0d: + return PPC_ID_VLE_SE_CMPL; + + case 0x0e: + return PPC_ID_VLE_SE_CMPH; + + case 0x0f: + return PPC_ID_VLE_SE_CMPHL; + + case 0x20: + case 0x21: + return PPC_ID_VLE_SE_ADDI; + + case 0x22: + case 0x23: + return PPC_ID_VLE_SE_CMPLI; + + case 0x24: + case 0x25: + case 0x26: + case 0x27: + return PPC_ID_VLE_SE_SUBIx; + + case 0x2a: + case 0x2b: + return PPC_ID_VLE_SE_CMPI; + + case 0x2c: + case 0x2d: + return PPC_ID_VLE_SE_BMASKI; + + case 0x2e: + case 0x2f: + return PPC_ID_VLE_SE_ANDI; + + case 0x40: + return PPC_ID_VLE_SE_SRW; + + case 0x41: + return PPC_ID_VLE_SE_SRAW; + + case 0x42: + return PPC_ID_VLE_SE_SLW; + + case 0x44: + { + // rY = 0, rX = 0 + // Not technically documented, but compiler + // occasionally emits `or, r0, r0`, which are NOPs in + // 32-bit land + if ((word16 & 0xff) == 0x00) + return PPC_ID_VLE_SE_NOP; + else + return PPC_ID_VLE_SE_OR; + } + + case 0x45: + return PPC_ID_VLE_SE_ANDC; + + case 0x46: + case 0x47: + return PPC_ID_VLE_SE_ANDx; + + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + return PPC_ID_VLE_SE_LI; + + case 0x60: + case 0x61: + return PPC_ID_VLE_SE_BCLRI; + + case 0x62: + case 0x63: + return PPC_ID_VLE_SE_BGENI; + + case 0x64: + case 0x65: + return PPC_ID_VLE_SE_BSETI; + + case 0x66: + case 0x67: + return PPC_ID_VLE_SE_BTSTI; + + case 0x68: + case 0x69: + return PPC_ID_VLE_SE_SRWI; + + case 0x6a: + case 0x6b: + return PPC_ID_VLE_SE_SRAWI; + + case 0x6c: + case 0x6d: + return PPC_ID_VLE_SE_SLWI; + + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + return PPC_ID_VLE_SE_LBZ; + + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + return PPC_ID_VLE_SE_STB; + + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + return PPC_ID_VLE_SE_LHZ; + + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + return PPC_ID_VLE_SE_STH; + + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc8: + case 0xc9: + case 0xca: + case 0xcb: + case 0xcc: + case 0xcd: + case 0xce: + case 0xcf: + return PPC_ID_VLE_SE_LWZ; + + case 0xd0: + case 0xd1: + case 0xd2: + case 0xd3: + case 0xd4: + case 0xd5: + case 0xd6: + case 0xd7: + case 0xd8: + case 0xd9: + case 0xda: + case 0xdb: + case 0xdc: + case 0xdd: + case 0xde: + case 0xdf: + return PPC_ID_VLE_SE_STW; + + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + return PPC_ID_VLE_SE_BC; + + case 0xe8: + case 0xe9: + return PPC_ID_VLE_SE_Bx; + + default: + return PPC_ID_INVALID; + } +} +static uint16_t Get16Rx(uint16_t word16) +{ + return word16 & 0xf; +} + +static uint16_t Get16Ry(uint16_t word16) +{ + return (word16 >> 4) & 0xf; +} + +static uint16_t Get16Rz(uint16_t word16) +{ + return (word16 >> 4) & 0xf; +} + +static void FillOperands16Vle(Instruction* instruction, uint16_t word16, uint64_t address, bool translate) +{ + uint16_t rx = Get16Rx(word16); + uint16_t ry = Get16Ry(word16); + uint16_t rz = Get16Rz(word16); + + switch (instruction->id) + { + // <op> + case PPC_ID_VLE_SE_ILLEGAL: + case PPC_ID_VLE_SE_ISYNC: + case PPC_ID_VLE_SE_NOP: + case PPC_ID_VLE_SE_RFCI: + case PPC_ID_VLE_SE_RFDI: + case PPC_ID_VLE_SE_RFI: + case PPC_ID_VLE_SE_RFMCI: + case PPC_ID_VLE_SE_SC: + return; + + // <op> rX, rY (4-bit) no rc + case PPC_ID_VLE_SE_ADD: + case PPC_ID_VLE_SE_ANDC: + case PPC_ID_VLE_SE_MR: + case PPC_ID_VLE_SE_MULLW: + case PPC_ID_VLE_SE_OR: + case PPC_ID_VLE_SE_SLW: + case PPC_ID_VLE_SE_SRAW: + case PPC_ID_VLE_SE_SRW: + case PPC_ID_VLE_SE_SUB: + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RA, Gpr(ry)); + if (translate) + PushRegister(instruction, PPC_OP_REG_RB, Gpr(rx)); + + break; + + case PPC_ID_VLE_SE_SUBF: + // Subtle difference between this and SE_SUB: + // SUB rX, rY --> SUBF rX, rY, rX (rX := rX - rY) + // SUBF rX, rY --> SUBF rX, rX, rY (rX := rY - rX) + if (translate) + { + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RB, Gpr(ry)); + } + else + { + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RA, Gpr(ry)); + } + + break; + + // <op> rX, rY, comparisons + case PPC_ID_VLE_SE_CMP: + case PPC_ID_VLE_SE_CMPH: + case PPC_ID_VLE_SE_CMPHL: + case PPC_ID_VLE_SE_CMPL: + if (translate) + PushRegister(instruction, PPC_OP_REG_CRFD_IMPLY0, Crf(0)); + + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RB, Gpr(ry)); + break; + + // <op> rX, rY (4-bit) with rc + case PPC_ID_VLE_SE_ANDx: + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RA, Gpr(ry)); + if (translate) + PushRegister(instruction, PPC_OP_REG_RB, Gpr(rx)); + + instruction->flags.rc = (word16 & 0x100) != 0; + break; + + // <op> rX (4-bit) + case PPC_ID_VLE_SE_EXTSB: + case PPC_ID_VLE_SE_EXTSH: + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + if (translate) + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + + break; + + // There aren't any non-VLE instructions that correspond + // directly to these, but they can be mimicked by + // ANDI'ing with 0xff and 0xffff directly + // <op> rX (4-bit) + case PPC_ID_VLE_SE_EXTZB: + case PPC_ID_VLE_SE_EXTZH: + if (translate) + { + // ANDI rX, rX, 0xff + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + if (instruction->id == PPC_ID_VLE_SE_EXTZB) + PushUIMMValue(instruction, 0xff); + else + PushUIMMValue(instruction, 0xffff); + } + else + { + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + } + + break; + + case PPC_ID_VLE_SE_NEG: + case PPC_ID_VLE_SE_NOT: + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + if (translate) + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + + break; + + case PPC_ID_VLE_SE_MFCTR: + case PPC_ID_VLE_SE_MFLR: + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + break; + + case PPC_ID_VLE_SE_MTCTR: + case PPC_ID_VLE_SE_MTLR: + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + break; + + // <op> rX,UI5 + case PPC_ID_VLE_SE_ANDI: + case PPC_ID_VLE_SE_SLWI: + case PPC_ID_VLE_SE_SRAWI: + case PPC_ID_VLE_SE_SRWI: + { + uint16_t ui5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + if (translate) + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + + PushUIMMValue(instruction, ui5); + + break; + } + + case PPC_ID_VLE_SE_CMPI: + { + uint16_t ui5 = (word16 >> 4) & 0x1f; + if (translate) + PushRegister(instruction, PPC_OP_REG_CRFD_IMPLY0, Crf(0)); + + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushUIMMValue(instruction, ui5); + break; + } + + // <op> rX, OIMM (no rc) + case PPC_ID_VLE_SE_ADDI: + { + uint32_t oim5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + + if (translate) + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + + PushSIMMValue(instruction, oim5 + 1); + break; + } + + // <op> rX, OIMM (no rc) + case PPC_ID_VLE_SE_CMPLI: + { + uint32_t oim5 = (word16 >> 4) & 0x1f; + if (translate) + PushRegister(instruction, PPC_OP_REG_CRFD_IMPLY0, Crf(0)); + + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushUIMMValue(instruction, oim5 + 1); + break; + } + + // <op> rX, OIMM (rc) + case PPC_ID_VLE_SE_SUBIx: + { + uint32_t oim5 = (word16 >> 4) & 0x1f; + uint32_t imm5 = oim5 + 1; + bool rc = (word16 >> 9) & 0x1; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + if (translate) + { + // In non-VLE, SUBI is a pseudo-op for ADDI with + // a negative value + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushSIMMValue(instruction, -((int32_t)imm5)); + } + else + { + PushSIMMValue(instruction, imm5); + } + instruction->flags.rc = rc; + break; + } + + // <op>[l] + case PPC_ID_VLE_SE_BCTRx: + case PPC_ID_VLE_SE_BLRx: + instruction->flags.lk = word16 & 0x1; + break; + + // one-off + case PPC_ID_VLE_SE_Bx: + { + bool lk = (word16 >> 8) & 0x1; + uint16_t bd8 = word16 & 0xff; + + instruction->flags.lk = lk; + uint64_t target = address + (int32_t)sign_extend(bd8 << 1, 9); + PushLabel(instruction, target); + break; + } + + case PPC_ID_VLE_SE_BC: + { + uint16_t bo16 = (word16 >> 10) & 0x1; + uint16_t bi16 = (word16 >> 8) & 0x3; + uint16_t bd8 = word16 & 0xff; + + if (translate) + { + // VLEPEM Table 2-6: BO16 Field Encodings + // 0 --> branch if condition false + // 1 --> branch if condition true + // + // PowerPC programming environments 8-7: BO Operand Encodings + // (relevant excerpt) + // 001zy --> branch if condition false + // 011zy --> branch if condition true + // + // VLE doesn't encode hints, so we just map to encodings + // with z and y equal to 0 + uint32_t mapped_bo; + + switch (bo16) { + case 0: mapped_bo = 0x04; break; + case 1: mapped_bo = 0x0c; break; + default: + // should be unreachable + mapped_bo = 0; // only to silence compiler warning + } + + PushUIMMValue(instruction, mapped_bo); + } + else + { + PushUIMMValue(instruction, bo16); + } + PushUIMMValue(instruction, bi16); + + uint64_t target = address + (int32_t)sign_extend(bd8 << 1, 9); + PushLabel(instruction, target); + break; + } + + case PPC_ID_VLE_SE_BCLRI: + { + // --> PPC_ID_ANDI + uint16_t ui5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + if (translate) + { + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + PushUIMMValue(instruction, ~(1 << ui5)); + } + else + { + PushUIMMValue(instruction, ui5); + } + break; + } + + case PPC_ID_VLE_SE_BGENI: + { + // --> PPC_ID_LI + uint16_t ui5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + if (translate) + { + uint32_t value = (1ul << ui5); + PushUIMMValue(instruction, value); + } + else + { + PushUIMMValue(instruction, ui5); + } + + break; + } + + case PPC_ID_VLE_SE_BMASKI: + { + // --> PPC_ID_LI + uint16_t ui5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + if (translate) + { + uint32_t value = ~(1ul << ui5); + PushUIMMValue(instruction, value); + } + else + { + PushUIMMValue(instruction, ui5); + } + break; + } + + case PPC_ID_VLE_SE_BSETI: + { + // --> PPC_ID_ORI + uint16_t ui5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + if (translate) + { + uint32_t value = 1ul << ui5; + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rx)); + PushUIMMValue(instruction, value); + } + else + { + PushUIMMValue(instruction, ui5); + } + break; + } + + case PPC_ID_VLE_SE_BTSTI: + { + // This instruction isn't expressible in standard powerpc, + // so we don't bother trying to convert it to anything + uint16_t ui5 = (word16 >> 4) & 0x1f; + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushUIMMValue(instruction, ui5); + break; + } + + // NOTE: LBZ, LHZ, and LWZ in VLE don't turn rX=0 into 0 + case PPC_ID_VLE_SE_LBZ: + { + uint32_t sd4 = (word16 >> 8) & 0xf; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rz)); + PushMem(instruction, PPC_OP_MEM_RA, Gpr(rx), (int32_t)sd4); + break; + } + + case PPC_ID_VLE_SE_LHZ: + { + uint32_t sd4 = ((word16 >> 8) & 0xf) * 2; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rz)); + PushMem(instruction, PPC_OP_MEM_RA, Gpr(rx), (int32_t)sd4); + break; + } + + case PPC_ID_VLE_SE_LI: + { + uint32_t ui7 = (word16 >> 4) & 0x7f; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rx)); + PushUIMMValue(instruction, ui7); + break; + } + + case PPC_ID_VLE_SE_LWZ: + { + uint32_t sd4 = ((word16 >> 8) & 0xf) * 4; + PushRegister(instruction, PPC_OP_REG_RD, Gpr(rz)); + PushMem(instruction, PPC_OP_MEM_RA, Gpr(rx), (int32_t)sd4); + break; + } + + case PPC_ID_VLE_SE_MFAR: + PushRegister(instruction, PPC_OP_REG_RA, Gpr(rx)); + PushRegister(instruction, PPC_OP_REG_RS, Gpr(8 + ry)); + break; + + case PPC_ID_VLE_SE_MTAR: + PushRegister(instruction, PPC_OP_REG_RA, Gpr(8 + rx)); + PushRegister(instruction, PPC_OP_REG_RS, Gpr(ry)); + break; + + // NOTE: STB, STH, and STW in VLE don't turn rX=0 into 0 + case PPC_ID_VLE_SE_STB: + { + uint32_t sd4 = (word16 >> 8) & 0xf; + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rz)); + PushMem(instruction, PPC_OP_MEM_RA, Gpr(rx), (int32_t)sd4); + break; + } + + case PPC_ID_VLE_SE_STH: + { + uint32_t sd4 = ((word16 >> 8) & 0xf) * 2; + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rz)); + PushMem(instruction, PPC_OP_MEM_RA, Gpr(rx), (int32_t)sd4); + break; + } + + case PPC_ID_VLE_SE_STW: + { + uint32_t sd4 = ((word16 >> 8) & 0xf) * 4; + PushRegister(instruction, PPC_OP_REG_RS, Gpr(rz)); + PushMem(instruction, PPC_OP_MEM_RA, Gpr(rx), (int32_t)sd4); + break; + } + + default: + ; + } +} + +bool Decompose16Vle(Instruction* instruction, uint16_t word16, uint64_t address, uint32_t flags) +{ + memset(instruction, 0, sizeof *instruction); + instruction->id = Decode16Vle(word16, flags); + if (instruction->id == PPC_ID_INVALID) + return false; + + bool translate = (flags & DECODE_FLAGS_VLE_TRANSLATE) != 0; + FillOperands16Vle(instruction, word16, address, translate); + if (translate) + instruction->id = VleTranslateMnemonic(instruction->id); + + return true; +} + +void FillVle16BcOperands(OperandsList *se_bc, const Instruction *instruction) +{ + memset(se_bc, 0, sizeof *se_bc); + + if (instruction->id != PPC_ID_VLE_SE_BC) + return; + + // se_bc only looks at CR0 + se_bc->operands[0].cls = PPC_OP_REG_CRFS_IMPLY0; + se_bc->operands[0].reg = Crf(0); + se_bc->numOperands = 1; +} |
