/* * Copyright © 2022 Imagination Technologies Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include "pvr_rogue_pds_defs.h" #include "pvr_rogue_pds_encode.h" #include "pvr_rogue_pds_disasm.h" #include "util/macros.h" static void pvr_error_check(PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error) { if (err_callback) err_callback(error); else fprintf(stderr, "ERROR: %s\n", error.text); } #define X(a) #a, static const char *const instructions[] = { PVR_INSTRUCTIONS }; #undef X static void error_reg_range(uint32_t raw, void *context, PVR_ERR_CALLBACK err_callback, uint32_t parameter, struct pvr_dissassembler_error error) { char param[32]; error.type = PVR_PDS_ERR_PARAM_RANGE; error.instruction = error.instruction; error.parameter = parameter; error.raw = raw; if (parameter == 0) snprintf(param, sizeof(param), "dst"); else snprintf(param, sizeof(param), "src%u", parameter - 1); error.text = malloc(PVR_PDS_MAX_INST_STR_LEN); assert(error.text); snprintf(error.text, PVR_PDS_MAX_INST_STR_LEN, "Register out of range, instruction: %s, operand: %s, value: %u", instructions[error.instruction], param, raw); pvr_error_check(err_callback, error); } static struct pvr_operand * pvr_pds_disassemble_regs32(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS32_MASK; switch (pvr_pds_inst_decode_field_range_regs32(instruction)) { case PVR_ROGUE_PDSINST_REGS32_CONST32: op->type = CONST32; op->address = instruction - PVR_ROGUE_PDSINST_REGS32_CONST32_LOWER; op->absolute_address = op->address; break; case PVR_ROGUE_PDSINST_REGS32_TEMP32: op->type = TEMP32; op->address = instruction - PVR_ROGUE_PDSINST_REGS32_TEMP32_LOWER; op->absolute_address = op->address; break; case PVR_ROGUE_PDSINST_REGS32_PTEMP32: op->type = PTEMP32; op->address = instruction - PVR_ROGUE_PDSINST_REGS32_PTEMP32_LOWER; op->absolute_address = op->address; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } static struct pvr_operand * pvr_pds_disassemble_regs32tp(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS32TP_MASK; switch (pvr_pds_inst_decode_field_range_regs32tp(instruction)) { case PVR_ROGUE_PDSINST_REGS32TP_TEMP32: op->type = TEMP32; op->address = instruction - PVR_ROGUE_PDSINST_REGS32TP_TEMP32_LOWER; op->absolute_address = op->address; break; case PVR_ROGUE_PDSINST_REGS32TP_PTEMP32: op->type = PTEMP32; op->address = instruction - PVR_ROGUE_PDSINST_REGS32TP_PTEMP32_LOWER; op->absolute_address = op->address; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } static struct pvr_operand * pvr_pds_disassemble_regs32t(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS32T_MASK; switch (pvr_pds_inst_decode_field_range_regs32t(instruction)) { case PVR_ROGUE_PDSINST_REGS32T_TEMP32: op->type = TEMP32; op->address = instruction - PVR_ROGUE_PDSINST_REGS32T_TEMP32_LOWER; op->absolute_address = op->address; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } static struct pvr_operand * pvr_pds_disassemble_regs64(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS64_MASK; switch (pvr_pds_inst_decode_field_range_regs64(instruction)) { case PVR_ROGUE_PDSINST_REGS64_CONST64: op->type = CONST64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64_CONST64_LOWER; op->absolute_address = op->address * 2; break; case PVR_ROGUE_PDSINST_REGS64_TEMP64: op->type = TEMP64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64_TEMP64_LOWER; op->absolute_address = op->address * 2; break; case PVR_ROGUE_PDSINST_REGS64_PTEMP64: op->type = PTEMP64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64_PTEMP64_LOWER; op->absolute_address = op->address * 2; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } static struct pvr_operand * pvr_pds_disassemble_regs64t(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS64T_MASK; switch (pvr_pds_inst_decode_field_range_regs64tp(instruction)) { case PVR_ROGUE_PDSINST_REGS64T_TEMP64: op->type = TEMP64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64T_TEMP64_LOWER; op->absolute_address = op->address * 2; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } static struct pvr_operand * pvr_pds_disassemble_regs64C(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS64C_MASK; switch (pvr_rogue_pds_inst_decode_field_range_regs64c(instruction)) { case PVR_ROGUE_PDSINST_REGS64C_CONST64: op->type = CONST64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64C_CONST64_LOWER; op->absolute_address = op->address * 2; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } static struct pvr_operand * pvr_pds_disassemble_regs64tp(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, uint32_t parameter) { struct pvr_operand *op = calloc(1, sizeof(*op)); assert(op); op->type = UNRESOLVED; instruction &= PVR_ROGUE_PDSINST_REGS64TP_MASK; switch (pvr_pds_inst_decode_field_range_regs64tp(instruction)) { case PVR_ROGUE_PDSINST_REGS64TP_TEMP64: op->type = TEMP64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64TP_TEMP64_LOWER; op->absolute_address = op->address * 2; break; case PVR_ROGUE_PDSINST_REGS64TP_PTEMP64: op->type = PTEMP64; op->address = instruction - PVR_ROGUE_PDSINST_REGS64TP_PTEMP64_LOWER; op->absolute_address = op->address * 2; break; default: error_reg_range(instruction, context, err_callback, parameter, error); } return op; } #define PVR_TYPE_OPCODE BITFIELD_BIT(31U) #define PVR_TYPE_OPCODE_SP BITFIELD_BIT(27U) #define PVR_TYPE_OPCODEB BITFIELD_BIT(30U) #define PVR_TYPE_OPCODE_SHIFT 28U #define PVR_TYPE_OPCODE_SP_SHIFT 23U #define PVR_TYPE_OPCODEB_SHIFT 29U static struct pvr_instruction * pvr_pds_disassemble_instruction_add64(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_add *add = malloc(sizeof(*add)); assert(add); add->instruction.type = INS_ADD64; add->instruction.next = NULL; add->cc = instruction & PVR_ROGUE_PDSINST_ADD64_CC_ENABLE; add->alum = instruction & PVR_ROGUE_PDSINST_ADD64_ALUM_SIGNED; add->sna = instruction & PVR_ROGUE_PDSINST_ADD64_SNA_SUB; add->src0 = pvr_pds_disassemble_regs64(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_ADD64_SRC0_SHIFT, 1); add->src0->instruction = &add->instruction; add->src1 = pvr_pds_disassemble_regs64(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_ADD64_SRC1_SHIFT, 2); add->src1->instruction = &add->instruction; add->dst = pvr_pds_disassemble_regs64tp(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_ADD64_DST_SHIFT, 0); add->dst->instruction = &add->instruction; return &add->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_add32(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_add *add = malloc(sizeof(*add)); assert(add); add->instruction.type = INS_ADD32; add->instruction.next = NULL; add->cc = instruction & PVR_ROGUE_PDSINST_ADD32_CC_ENABLE; add->alum = instruction & PVR_ROGUE_PDSINST_ADD32_ALUM_SIGNED; add->sna = instruction & PVR_ROGUE_PDSINST_ADD32_SNA_SUB; add->src0 = pvr_pds_disassemble_regs32(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_ADD32_SRC0_SHIFT, 1); add->src0->instruction = &add->instruction; add->src1 = pvr_pds_disassemble_regs32(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_ADD32_SRC1_SHIFT, 2); add->src1->instruction = &add->instruction; add->dst = pvr_pds_disassemble_regs32tp(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_ADD32_DST_SHIFT, 0); add->dst->instruction = &add->instruction; return &add->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_stm(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_stm *stm = malloc(sizeof(*stm)); assert(stm); stm->instruction.next = NULL; stm->instruction.type = INS_STM; stm->cc = instruction & (1 << PVR_ROGUE_PDSINST_STM_CCS_CCS_CC_SHIFT); stm->ccs_global = instruction & (1 << PVR_ROGUE_PDSINST_STM_CCS_CCS_GLOBAL_SHIFT); stm->ccs_so = instruction & (1 << PVR_ROGUE_PDSINST_STM_CCS_CCS_SO_SHIFT); stm->tst = instruction & (1 << PVR_ROGUE_PDSINST_STM_SO_TST_SHIFT); stm->stream_out = (instruction >> PVR_ROGUE_PDSINST_STM_SO_SHIFT) & PVR_ROGUE_PDSINST_SO_MASK; stm->src0 = pvr_pds_disassemble_regs64tp( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_STM_SO_SRC0_SHIFT, 1); stm->src0->instruction = &stm->instruction; stm->src1 = pvr_pds_disassemble_regs64tp( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_STM_SO_SRC1_SHIFT, 2); stm->src1->instruction = &stm->instruction; stm->src2 = pvr_pds_disassemble_regs32( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_STM_SO_SRC2_SHIFT, 3); stm->src2->instruction = &stm->instruction; stm->src3 = pvr_pds_disassemble_regs64tp( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_STM_SO_SRC3_SHIFT, 4); stm->src3->instruction = &stm->instruction; return &stm->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_sftlp32(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_sftlp *ins = malloc(sizeof(*ins)); assert(ins); ins->instruction.next = NULL; ins->instruction.type = INS_SFTLP32; ins->cc = instruction & PVR_ROGUE_PDSINST_SFTLP32_CC_ENABLE; ins->IM = instruction & PVR_ROGUE_PDSINST_SFTLP32_IM_ENABLE; ins->lop = (instruction >> PVR_ROGUE_PDSINST_SFTLP32_LOP_SHIFT) & PVR_ROGUE_PDSINST_LOP_MASK; ins->src0 = pvr_pds_disassemble_regs32t( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_SFTLP32_SRC0_SHIFT, 1); ins->src0->instruction = &ins->instruction; ins->src1 = pvr_pds_disassemble_regs32( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_SFTLP32_SRC1_SHIFT, 2); ins->src1->instruction = &ins->instruction; ins->dst = pvr_pds_disassemble_regs32t( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_SFTLP32_DST_SHIFT, 0); ins->dst->instruction = &ins->instruction; if (ins->IM) { signed char cImmediate = ((instruction >> PVR_ROGUE_PDSINST_SFTLP32_SRC2_SHIFT) & PVR_ROGUE_PDSINST_REGS32_MASK) << 2; ins->src2 = calloc(1, sizeof(*ins->src2)); assert(ins->src2); ins->src2->literal = abs((cImmediate / 4)); ins->src2->negate = cImmediate < 0; ins->src2->instruction = &ins->instruction; } else { ins->src2 = pvr_pds_disassemble_regs32tp( context, err_callback, error, (instruction >> PVR_ROGUE_PDSINST_SFTLP32_SRC2_SHIFT), 3); ins->src2->instruction = &ins->instruction; } return &ins->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_sftlp64(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_sftlp *ins = malloc(sizeof(*ins)); assert(ins); ins->instruction.next = NULL; ins->instruction.type = INS_SFTLP64; ins->cc = instruction & PVR_ROGUE_PDSINST_SFTLP64_CC_ENABLE; ins->IM = instruction & PVR_ROGUE_PDSINST_SFTLP64_IM_ENABLE; ins->lop = (instruction >> PVR_ROGUE_PDSINST_SFTLP64_LOP_SHIFT) & PVR_ROGUE_PDSINST_LOP_MASK; ins->src0 = pvr_pds_disassemble_regs64tp( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_SFTLP64_SRC0_SHIFT, 1); ins->src0->instruction = &ins->instruction; ins->src1 = pvr_pds_disassemble_regs64tp( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_SFTLP64_SRC1_SHIFT, 2); ins->src1->instruction = &ins->instruction; ins->dst = pvr_pds_disassemble_regs64tp( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_SFTLP64_DST_SHIFT, 0); ins->dst->instruction = &ins->instruction; if (ins->IM) { signed char cImmediate = (instruction >> PVR_ROGUE_PDSINST_SFTLP64_SRC2_SHIFT) & PVR_ROGUE_PDSINST_REGS32_MASK; ins->src2 = calloc(1, sizeof(*ins->src2)); assert(ins->src2); ins->src2->literal = (abs(cImmediate) > 63) ? 63 : abs(cImmediate); ins->src2->negate = (cImmediate < 0); ins->src2->instruction = &ins->instruction; } else { ins->src2 = pvr_pds_disassemble_regs32( context, err_callback, error, (instruction >> PVR_ROGUE_PDSINST_SFTLP64_SRC2_SHIFT), 3); ins->src2->instruction = &ins->instruction; } return &ins->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_cmp(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_cmp *cmp = malloc(sizeof(*cmp)); assert(cmp); cmp->instruction.next = NULL; cmp->instruction.type = INS_CMP; cmp->cc = instruction & PVR_ROGUE_PDSINST_CMP_CC_ENABLE; cmp->IM = instruction & PVR_ROGUE_PDSINST_CMP_IM_ENABLE; cmp->cop = instruction >> PVR_ROGUE_PDSINST_CMP_COP_SHIFT & PVR_ROGUE_PDSINST_COP_MASK; cmp->src0 = pvr_pds_disassemble_regs64tp(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_CMP_SRC0_SHIFT, 1); cmp->src0->instruction = &cmp->instruction; if (cmp->IM) { uint32_t immediate = (instruction >> PVR_ROGUE_PDSINST_CMP_SRC1_SHIFT) & PVR_ROGUE_PDSINST_IMM16_MASK; cmp->src1 = calloc(1, sizeof(*cmp->src1)); assert(cmp->src1); cmp->src1->type = LITERAL_NUM; cmp->src1->literal = immediate; } else { cmp->src1 = pvr_pds_disassemble_regs64( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_CMP_SRC1_SHIFT, 2); } cmp->src1->instruction = &cmp->instruction; return &cmp->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_sp_ld_st(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, bool ld, uint32_t instruction, bool cc) { struct pvr_ldst *ins = malloc(sizeof(*ins)); assert(ins); ins->instruction.next = NULL; ins->instruction.type = ld ? INS_LD : INS_ST; ins->cc = cc; ins->src0 = pvr_pds_disassemble_regs64(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_LD_SRC0_SHIFT, 1); ins->src0->instruction = &ins->instruction; ins->st = !ld; return &ins->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_sp_stmc(uint32_t instruction, bool cc) { struct pvr_stmc *stmc = malloc(sizeof(*stmc)); assert(stmc); stmc->instruction.next = NULL; stmc->instruction.type = INS_STMC; stmc->cc = cc; stmc->src0 = calloc(1, sizeof(*stmc->src0)); assert(stmc->src0); stmc->src0->type = LITERAL_NUM; stmc->src0->literal = (instruction >> PVR_ROGUE_PDSINST_STMC_SOMASK_SHIFT) & PVR_ROGUE_PDSINST_SOMASK_MASK; stmc->src0->instruction = &stmc->instruction; return &stmc->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_sp_limm(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction, bool cc) { struct pvr_limm *limm = malloc(sizeof(*limm)); assert(limm); limm->instruction.next = NULL; limm->instruction.type = INS_LIMM; limm->cc = cc; limm->GR = (instruction & PVR_ROGUE_PDSINST_LIMM_GR_ENABLE) != 0; limm->src0 = calloc(1, sizeof(*limm->src0)); assert(limm->src0); limm->src0->type = LITERAL_NUM; limm->src0->literal = (instruction >> PVR_ROGUE_PDSINST_LIMM_SRC0_SHIFT) & PVR_ROGUE_PDSINST_IMM16_MASK; limm->src0->instruction = &limm->instruction; limm->dst = pvr_pds_disassemble_regs32t(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_LIMM_SRC1_SHIFT, 0); limm->dst->instruction = &limm->instruction; return &limm->instruction; } static struct pvr_instruction * pvr_pds_disassemble_simple(enum pvr_instruction_type type, bool cc) { struct pvr_simple *ins = malloc(sizeof(*ins)); assert(ins); ins->instruction.next = NULL; ins->instruction.type = type; ins->cc = cc; return &ins->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_bra(uint32_t instruction) { uint32_t branch_addr; struct pvr_bra *bra = (struct pvr_bra *)malloc(sizeof(*bra)); assert(bra); bra->instruction.type = INS_BRA; bra->instruction.next = NULL; branch_addr = (instruction >> PVR_ROGUE_PDSINST_BRA_ADDR_SHIFT) & PVR_ROGUE_PDSINST_BRAADDR_MASK; bra->address = (branch_addr & 0x40000U) ? ((int)branch_addr) - 0x80000 : (int)branch_addr; bra->srcc = malloc(sizeof(*bra->srcc)); assert(bra->srcc); bra->srcc->predicate = (instruction >> PVR_ROGUE_PDSINST_BRA_SRCC_SHIFT) & PVR_ROGUE_PDSINST_PREDICATE_MASK; bra->srcc->negate = instruction & PVR_ROGUE_PDSINST_BRA_NEG_ENABLE; bra->setc = malloc(sizeof(*bra->setc)); assert(bra->setc); bra->setc->predicate = (instruction >> PVR_ROGUE_PDSINST_BRA_SETC_SHIFT) & PVR_ROGUE_PDSINST_PREDICATE_MASK; bra->target = NULL; return &bra->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_sp(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { uint32_t op = (instruction >> PVR_TYPE_OPCODE_SP_SHIFT) & PVR_ROGUE_PDSINST_OPCODESP_MASK; bool cc = instruction & PVR_TYPE_OPCODE_SP; switch (op) { case PVR_ROGUE_PDSINST_OPCODESP_LD: error.instruction = INS_LD; return pvr_pds_disassemble_instruction_sp_ld_st( context, err_callback, error, true, instruction, instruction & (1 << PVR_ROGUE_PDSINST_LD_CC_SHIFT)); case PVR_ROGUE_PDSINST_OPCODESP_ST: error.instruction = INS_ST; return pvr_pds_disassemble_instruction_sp_ld_st( context, err_callback, error, false, instruction, instruction & (1 << PVR_ROGUE_PDSINST_ST_CC_SHIFT)); case PVR_ROGUE_PDSINST_OPCODESP_STMC: error.instruction = INS_STMC; return pvr_pds_disassemble_instruction_sp_stmc(instruction, cc); case PVR_ROGUE_PDSINST_OPCODESP_LIMM: error.instruction = INS_LIMM; return pvr_pds_disassemble_instruction_sp_limm(context, err_callback, error, instruction, cc); case PVR_ROGUE_PDSINST_OPCODESP_WDF: error.instruction = INS_WDF; return pvr_pds_disassemble_simple(INS_WDF, cc); case PVR_ROGUE_PDSINST_OPCODESP_LOCK: error.instruction = INS_LOCK; return pvr_pds_disassemble_simple(INS_LOCK, cc); case PVR_ROGUE_PDSINST_OPCODESP_RELEASE: error.instruction = INS_RELEASE; return pvr_pds_disassemble_simple(INS_RELEASE, cc); case PVR_ROGUE_PDSINST_OPCODESP_HALT: error.instruction = INS_HALT; return pvr_pds_disassemble_simple(INS_HALT, cc); case PVR_ROGUE_PDSINST_OPCODESP_NOP: error.instruction = INS_NOP; return pvr_pds_disassemble_simple(INS_NOP, cc); default: error.type = PVR_PDS_ERR_SP_UNKNOWN; error.text = "opcode unknown for special instruction"; pvr_error_check(err_callback, error); return NULL; } } static struct pvr_instruction * pvr_pds_disassemble_instruction_ddmad(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_ddmad *ddmad = malloc(sizeof(*ddmad)); assert(ddmad); ddmad->instruction.next = NULL; ddmad->instruction.type = INS_DDMAD; ddmad->cc = instruction & PVR_ROGUE_PDSINST_DDMAD_CC_ENABLE; ddmad->END = instruction & PVR_ROGUE_PDSINST_DDMAD_END_ENABLE; ddmad->src0 = pvr_pds_disassemble_regs32( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_DDMAD_SRC0_SHIFT, 1); ddmad->src0->instruction = &ddmad->instruction; ddmad->src1 = pvr_pds_disassemble_regs32t( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_DDMAD_SRC1_SHIFT, 2); ddmad->src1->instruction = &ddmad->instruction; ddmad->src2 = pvr_pds_disassemble_regs64( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_DDMAD_SRC2_SHIFT, 3); ddmad->src2->instruction = &ddmad->instruction; ddmad->src3 = pvr_pds_disassemble_regs64C( context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_DDMAD_SRC3_SHIFT, 4); ddmad->src3->instruction = &ddmad->instruction; return &ddmad->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_mad(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_mad *mad = malloc(sizeof(*mad)); assert(mad); mad->instruction.next = NULL; mad->instruction.type = INS_MAD; mad->cc = instruction & PVR_ROGUE_PDSINST_MAD_CC_ENABLE; mad->sna = instruction & PVR_ROGUE_PDSINST_MAD_SNA_SUB; mad->alum = (instruction & PVR_ROGUE_PDSINST_MAD_ALUM_SIGNED); mad->src0 = pvr_pds_disassemble_regs32(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_MAD_SRC0_SHIFT, 1); mad->src0->instruction = &mad->instruction; mad->src1 = pvr_pds_disassemble_regs32(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_MAD_SRC1_SHIFT, 2); mad->src1->instruction = &mad->instruction; mad->src2 = pvr_pds_disassemble_regs64(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_MAD_SRC2_SHIFT, 3); mad->src2->instruction = &mad->instruction; mad->dst = pvr_pds_disassemble_regs64t(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_MAD_DST_SHIFT, 0); mad->dst->instruction = &mad->instruction; return &mad->instruction; } static struct pvr_instruction * pvr_pds_disassemble_instruction_dout(void *context, PVR_ERR_CALLBACK err_callback, struct pvr_dissassembler_error error, uint32_t instruction) { struct pvr_dout *dout = malloc(sizeof(*dout)); assert(dout); dout->instruction.next = NULL; dout->instruction.type = INS_DOUT; dout->END = instruction & PVR_ROGUE_PDSINST_DOUT_END_ENABLE; dout->cc = instruction & PVR_ROGUE_PDSINST_DOUT_CC_ENABLE; dout->dst = (instruction >> PVR_ROGUE_PDSINST_DOUT_DST_SHIFT) & PVR_ROGUE_PDSINST_DSTDOUT_MASK; dout->src0 = pvr_pds_disassemble_regs64(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_DOUT_SRC0_SHIFT, 1); dout->src0->instruction = &dout->instruction; dout->src1 = pvr_pds_disassemble_regs32(context, err_callback, error, instruction >> PVR_ROGUE_PDSINST_DOUT_SRC1_SHIFT, 2); dout->src1->instruction = &dout->instruction; return &dout->instruction; } static void pvr_pds_free_instruction_limm(struct pvr_limm *inst) { free(inst->dst); free(inst->src0); free(inst); } static void pvr_pds_free_instruction_add(struct pvr_add *inst) { free(inst->dst); free(inst->src0); free(inst->src1); free(inst); } static void pvr_pds_free_instruction_cmp(struct pvr_cmp *inst) { free(inst->src0); free(inst->src1); free(inst); } static void pvr_pds_free_instruction_mad(struct pvr_mad *inst) { free(inst->dst); free(inst->src0); free(inst->src1); free(inst->src2); free(inst); } static void pvr_pds_free_instruction_bra(struct pvr_bra *inst) { free(inst->setc); free(inst->srcc); free(inst); } static void pvr_pds_free_instruction_ddmad(struct pvr_ddmad *inst) { free(inst->src0); free(inst->src1); free(inst->src2); free(inst->src3); free(inst); } static void pvr_pds_free_instruction_dout(struct pvr_dout *inst) { free(inst->src0); free(inst->src1); free(inst); } static void pvr_pds_free_instruction_ldst(struct pvr_ldst *inst) { free(inst->src0); free(inst); } static void pvr_pds_free_instruction_simple(struct pvr_simple *inst) { free(inst); } static void pvr_pds_free_instruction_sfltp(struct pvr_sftlp *inst) { free(inst->dst); free(inst->src0); free(inst->src1); free(inst->src2); free(inst); } static void pvr_pds_free_instruction_stm(struct pvr_stm *inst) { free(inst->src0); free(inst->src1); free(inst->src2); free(inst->src3); free(inst); } static void pvr_pds_free_instruction_stmc(struct pvr_stmc *inst) { free(inst->src0); free(inst); } void pvr_pds_free_instruction(struct pvr_instruction *instruction) { if (!instruction) return; switch (instruction->type) { case INS_LIMM: pvr_pds_free_instruction_limm((struct pvr_limm *)instruction); break; case INS_ADD64: case INS_ADD32: pvr_pds_free_instruction_add((struct pvr_add *)instruction); break; case INS_CMP: pvr_pds_free_instruction_cmp((struct pvr_cmp *)instruction); break; case INS_MAD: pvr_pds_free_instruction_mad((struct pvr_mad *)instruction); break; case INS_BRA: pvr_pds_free_instruction_bra((struct pvr_bra *)instruction); break; case INS_DDMAD: pvr_pds_free_instruction_ddmad((struct pvr_ddmad *)instruction); break; case INS_DOUT: pvr_pds_free_instruction_dout((struct pvr_dout *)instruction); break; case INS_LD: case INS_ST: pvr_pds_free_instruction_ldst((struct pvr_ldst *)instruction); break; case INS_WDF: case INS_LOCK: case INS_RELEASE: case INS_HALT: case INS_NOP: pvr_pds_free_instruction_simple((struct pvr_simple *)instruction); break; case INS_SFTLP64: case INS_SFTLP32: pvr_pds_free_instruction_sfltp((struct pvr_sftlp *)instruction); break; case INS_STM: pvr_pds_free_instruction_stm((struct pvr_stm *)instruction); break; case INS_STMC: pvr_pds_free_instruction_stmc((struct pvr_stmc *)instruction); break; } } struct pvr_instruction * pvr_pds_disassemble_instruction2(void *context, PVR_ERR_CALLBACK err_callback, uint32_t instruction) { struct pvr_dissassembler_error error = { .context = context }; /* First we need to find out what type of OPCODE we are dealing with. */ if (instruction & PVR_TYPE_OPCODE) { uint32_t opcode_C = (instruction >> PVR_TYPE_OPCODE_SHIFT) & PVR_ROGUE_PDSINST_OPCODEC_MASK; switch (opcode_C) { case PVR_ROGUE_PDSINST_OPCODEC_ADD64: error.instruction = INS_ADD64; return pvr_pds_disassemble_instruction_add64(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEC_ADD32: error.instruction = INS_ADD32; return pvr_pds_disassemble_instruction_add32(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEC_SFTLP64: error.instruction = INS_SFTLP64; return pvr_pds_disassemble_instruction_sftlp64(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEC_CMP: error.instruction = INS_CMP; return pvr_pds_disassemble_instruction_cmp(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEC_BRA: error.instruction = INS_BRA; return pvr_pds_disassemble_instruction_bra(instruction); case PVR_ROGUE_PDSINST_OPCODEC_SP: return pvr_pds_disassemble_instruction_sp(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEC_DDMAD: error.instruction = INS_DDMAD; return pvr_pds_disassemble_instruction_ddmad(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEC_DOUT: error.instruction = INS_DOUT; return pvr_pds_disassemble_instruction_dout(context, err_callback, error, instruction); } } else if (instruction & PVR_TYPE_OPCODEB) { uint32_t opcode_B = (instruction >> PVR_TYPE_OPCODEB_SHIFT) & PVR_ROGUE_PDSINST_OPCODEB_MASK; switch (opcode_B) { case PVR_ROGUE_PDSINST_OPCODEB_SFTLP32: error.instruction = INS_SFTLP32; return pvr_pds_disassemble_instruction_sftlp32(context, err_callback, error, instruction); case PVR_ROGUE_PDSINST_OPCODEB_STM: error.instruction = INS_STM; return pvr_pds_disassemble_instruction_stm(context, err_callback, error, instruction); } } else { /* Opcode A - MAD instruction. */ error.instruction = INS_MAD; return pvr_pds_disassemble_instruction_mad(context, err_callback, error, instruction); } return NULL; }