mirror of
https://github.com/Ed94/perfaware.git
synced 2026-09-14 03:39:23 +00:00
515 lines
20 KiB
C
515 lines
20 KiB
C
#ifdef INTELLISENSE_DIRECTIVES
|
|
# include "encoder.h"
|
|
# include "info.h"
|
|
# include "decoder.h"
|
|
# include "gen/decoder_table.h"
|
|
#endif
|
|
|
|
RO_ global U1 x8616_decode_disp_bytes[32] = {
|
|
0,0,0,0,0,0,2,0,
|
|
1,1,1,1,1,1,1,1,
|
|
2,2,2,2,2,2,2,2,
|
|
0,0,0,0,0,0,0,0,
|
|
};
|
|
|
|
RO_ global U1 x8616_decode_payload_bytes[] = {
|
|
0, // none
|
|
0, // imm: dynamic
|
|
1, // imm8
|
|
2, // imm16
|
|
2, // mem-direct
|
|
1, // rel8
|
|
2, // rel16
|
|
4, // far-ptr
|
|
};
|
|
|
|
typedef Struct_(X8616_DecodePlex) {
|
|
U1* source;
|
|
U4 source_size;
|
|
U4 source_offset;
|
|
|
|
U1 body[X8616_DECODE_BODY_CAP + X8616_DECODE_BODY_PAD];
|
|
U1 body_available;
|
|
|
|
U2 dispatch;
|
|
U1 plan_idx;
|
|
X8616_DecodePlan* plan;
|
|
|
|
U1 header;
|
|
U1 post_opcode;
|
|
U1 mod_rm;
|
|
|
|
X8616_Direction d;
|
|
X8616_Width w;
|
|
X8616_Sign s;
|
|
X8616_VariableShift v;
|
|
X8616_Repeat z;
|
|
X8616_ALU alu;
|
|
X8616_Condition cc;
|
|
X8616_DecodedReg reg_opcode;
|
|
X8616_Segment sr_opcode;
|
|
|
|
X8616_Mod mod;
|
|
X8616_DecodedReg reg;
|
|
X8616_EA rm;
|
|
X8616_Segment sr_modrm;
|
|
|
|
X8616_WidthMode width;
|
|
|
|
U1 body_at;
|
|
U1 displacement_at;
|
|
U1 displacement_bytes;
|
|
U1 payload_at;
|
|
U1 payload_bytes;
|
|
U1 body_required;
|
|
|
|
S2 displacement;
|
|
U2 direct_address;
|
|
U2 payload_u16;
|
|
U2 far_segment;
|
|
|
|
B1 direct_memory;
|
|
B1 classification_truncated;
|
|
B1 encoding_invalid;
|
|
|
|
X8616_DecodedPrefixes prefixes;
|
|
X8616_DecodedOperand operand_source[X8616_OPERAND_SOURCE_COUNT];
|
|
X8616_DecodedInstruction instruction;
|
|
|
|
FArena* info_arena;
|
|
X8616_InfoList* msgs;
|
|
};
|
|
|
|
FI_ U2 x8616_decode_u2(U1_R bytes) { return C_(U2, bytes[0] | u2_(bytes[1] << 8)); }
|
|
|
|
internal X8616_Opcode
|
|
x8616_decode_opcode(X8616_DecodePlan const* plan, U1 header)
|
|
{
|
|
U1 field_mask = 0;
|
|
if (plan->flags & x8616_plan_has_d) field_mask |= x8616_field_mask(plan->d_shift, X8616_OPCODE_BIT_WIDTH);
|
|
if (plan->flags & x8616_plan_has_w) field_mask |= x8616_field_mask(plan->w_shift, X8616_OPCODE_BIT_WIDTH);
|
|
if (plan->flags & x8616_plan_has_s) field_mask |= x8616_field_mask(plan->s_shift, X8616_OPCODE_BIT_WIDTH);
|
|
if (plan->flags & x8616_plan_has_v) field_mask |= x8616_field_mask(plan->v_shift, X8616_OPCODE_BIT_WIDTH);
|
|
if (plan->flags & x8616_plan_has_z) field_mask |= x8616_field_mask(plan->z_shift, X8616_OPCODE_BIT_WIDTH);
|
|
if (plan->flags & x8616_plan_has_reg) field_mask |= x8616_field_mask(plan->reg_shift, X8616_OPCODE_REG_REG_WIDTH);
|
|
if (plan->flags & x8616_plan_has_sr) field_mask |= x8616_field_mask(plan->sr_shift, X8616_OPCODE_SR_WIDTH);
|
|
if (plan->flags & x8616_plan_has_alu) field_mask |= x8616_field_mask(plan->alu_shift, X8616_OPCODE_ALU_TTT_WIDTH);
|
|
if (plan->flags & x8616_plan_has_cc) field_mask |= x8616_field_mask(plan->cc_shift, X8616_OPCODE_CC_WIDTH);
|
|
if (plan->flags & x8616_plan_has_pair) field_mask |= x8616_field_mask(plan->pair_shift, X8616_OPCODE_PAIR_WIDTH);
|
|
if (field_mask == 0) return 0;
|
|
U1 opcode_mask = u1_(~field_mask);
|
|
if (opcode_mask == 0) return 0;
|
|
return C_(X8616_Opcode, (header & opcode_mask) >> count_trailing_zeros_u4(opcode_mask));
|
|
}
|
|
|
|
internal void
|
|
x8616_decode_apply_prefix(X8616_DecodePlex_R plex, X8616_DecodePlan_R plan, U1 opcode) {
|
|
plex->prefixes.count += 1;
|
|
plex->prefixes.lock |= plan->prefix_kind == x8616_prefix_lock;
|
|
if (plan->prefix_kind == x8616_prefix_repeat) {
|
|
plex->prefixes.has_repeat = 1;
|
|
plex->prefixes.repeat = C_(X8616_Repeat, x8616_bit_field_extract(opcode, (X8616_BitField){ plan->z_shift, X8616_OPCODE_BIT_WIDTH }));
|
|
}
|
|
if (plan->prefix_kind == x8616_prefix_segment) {
|
|
plex->prefixes.has_segment = 1;
|
|
plex->prefixes.segment = C_(X8616_Segment, x8616_bit_field_extract(opcode, (X8616_BitField){ plan->sr_shift, X8616_OPCODE_SR_WIDTH }));
|
|
}
|
|
}
|
|
|
|
internal U4
|
|
x8616_decode_one_plex(X8616_DecodePlex* plex)
|
|
{
|
|
U4 prefix_at = 0;
|
|
|
|
// Prefix pass. Ambiguous/group opcodes are never prefixes, so only direct
|
|
// dispatch entries participate in this loop.
|
|
while (prefix_at < plex->source_size)
|
|
{
|
|
U1 prefix_opcode = plex->source[prefix_at];
|
|
U2 prefix_dispatch = x8616_decode_dispatch[prefix_opcode];
|
|
if (prefix_dispatch == false || (prefix_dispatch & X8616_DECODE_AUX_BIT)) break;
|
|
|
|
X8616_DecodePlan_R prefix_plan = x8616_decode_plans + prefix_dispatch;
|
|
if ((prefix_plan->flags & x8616_plan_is_prefix) == false) break;
|
|
|
|
x8616_decode_apply_prefix(plex, prefix_plan, prefix_opcode);
|
|
++ prefix_at;
|
|
}
|
|
|
|
U4 body_source_size = plex->source_size - prefix_at;
|
|
plex->body_available = u1_(body_source_size < X8616_DECODE_BODY_CAP ? body_source_size : X8616_DECODE_BODY_CAP);
|
|
|
|
for (U1 idx = 0; idx < plex->body_available; ++ idx) plex->body[idx] = plex->source[prefix_at + idx];
|
|
|
|
plex->header = plex->body[0];
|
|
plex->dispatch = x8616_decode_dispatch[plex->header];
|
|
|
|
if (body_source_size == 0) {
|
|
plex->classification_truncated = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_truncated_instruction
|
|
, plex->source_offset + prefix_at
|
|
, 0
|
|
, 1
|
|
, 0
|
|
);
|
|
}
|
|
else if (plex->dispatch & X8616_DECODE_AUX_BIT)
|
|
{
|
|
if (body_source_size < 2) {
|
|
plex->classification_truncated = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_truncated_instruction
|
|
, plex->source_offset + prefix_at
|
|
, u2_(body_source_size)
|
|
, 2
|
|
, body_source_size
|
|
);
|
|
}
|
|
else
|
|
{
|
|
U2 aux_base = plex->dispatch & X8616_DECODE_AUX_MASK;
|
|
plex->plan_idx = x8616_decode_aux[aux_base + plex->body[1]];
|
|
if (plex->plan_idx == false) {
|
|
plex->encoding_invalid = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_invalid_opcode_extension
|
|
, plex->source_offset + prefix_at
|
|
, 2
|
|
, 0
|
|
, plex->body[1]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
plex->plan_idx = u1_(plex->dispatch);
|
|
if (plex->plan_idx == 0) {
|
|
plex->encoding_invalid = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_invalid_opcode
|
|
, plex->source_offset + prefix_at
|
|
, 1
|
|
, 0
|
|
, plex->header
|
|
);
|
|
}
|
|
}
|
|
|
|
plex->plan = x8616_decode_plans + plex->plan_idx;
|
|
|
|
// A direct-dispatch plan may still have a constrained second byte.
|
|
// Group opcodes with multiple candidates were already resolved through aux.
|
|
if (plex->plan_idx && (plex->plan->flags & (x8616_plan_has_modrm | x8616_plan_has_post_opcode)))
|
|
{
|
|
if (body_source_size < 2)
|
|
{
|
|
if (plex->classification_truncated == 0) {
|
|
plex->classification_truncated = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_truncated_instruction
|
|
, plex->source_offset + prefix_at
|
|
, u2_(body_source_size)
|
|
, 2
|
|
, body_source_size
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
U1 second = plex->body[1];
|
|
if (plex->plan->mod_rm.mask && (second & plex->plan->mod_rm.mask) != plex->plan->mod_rm.bits) {
|
|
plex->encoding_invalid = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_invalid_opcode_extension
|
|
, plex->source_offset + prefix_at + 1
|
|
, 1
|
|
, plex->plan->mod_rm.bits
|
|
, second & plex->plan->mod_rm.mask
|
|
);
|
|
}
|
|
|
|
if (plex->plan->post_opcode.mask && (second & plex->plan->post_opcode.mask) != plex->plan->post_opcode.bits) {
|
|
plex->encoding_invalid = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_invalid_post_opcode
|
|
, plex->source_offset + prefix_at + 1
|
|
, 1
|
|
, plex->plan->post_opcode.bits
|
|
, second & plex->plan->post_opcode.mask
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nil plan: still return a normal zero-valued instruction record and make
|
|
// progress through the stream. Aux classification inspected two bytes.
|
|
if (plex->plan_idx == 0) {
|
|
U1 invalid_body_size = (plex->dispatch & X8616_DECODE_AUX_BIT) && body_source_size >= 2 ? 2 : u1_(body_source_size != 0);
|
|
plex->instruction.prefixes = plex->prefixes;
|
|
plex->instruction.op = x8616_op_invalid;
|
|
plex->instruction.decode_flags = (plex->encoding_invalid ? x8616_decode_invalid : 0) | (plex->classification_truncated ? x8616_decode_truncated : 0);
|
|
plex->instruction.size = u1_(prefix_at + invalid_body_size);
|
|
plex->instruction.size_required = plex->instruction.size;
|
|
return plex->instruction.size;
|
|
}
|
|
|
|
X8616_DecodePlan_R plan = plex->plan;
|
|
plex->d = C_(X8616_Direction, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->d_shift, X8616_OPCODE_BIT_WIDTH }));
|
|
plex->w = C_(X8616_Width, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->w_shift, X8616_OPCODE_BIT_WIDTH }));
|
|
plex->s = C_(X8616_Sign, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->s_shift, X8616_OPCODE_BIT_WIDTH }));
|
|
plex->v = C_(X8616_VariableShift, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->v_shift, X8616_OPCODE_BIT_WIDTH }));
|
|
plex->z = C_(X8616_Repeat, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->z_shift, X8616_OPCODE_BIT_WIDTH }));
|
|
plex->reg_opcode.r16 = C_(X8616_Reg16, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->reg_shift, X8616_OPCODE_REG_REG_WIDTH }));
|
|
plex->sr_opcode = C_(X8616_Segment, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->sr_shift, X8616_OPCODE_SR_WIDTH }));
|
|
|
|
plex->width = plan->width;
|
|
if (plex->width == x8616_width_dynamic && (plan->flags & x8616_plan_has_w))
|
|
plex->width = plex->w ? x8616_width_word : x8616_width_byte;
|
|
|
|
plex->body_at = 1;
|
|
|
|
if (plan->flags & x8616_plan_has_post_opcode) {
|
|
plex->post_opcode = plex->body[plex->body_at];
|
|
plex->body_at += 1;
|
|
}
|
|
|
|
if (plan->flags & x8616_plan_has_modrm) {
|
|
plex->mod_rm = plex->body[plex->body_at];
|
|
plex->mod = C_(X8616_Mod, x8616_modrm_mod(plex->mod_rm));
|
|
plex->reg.r16 = C_(X8616_Reg16, x8616_modrm_reg(plex->mod_rm));
|
|
plex->rm = C_(X8616_EA, x8616_modrm_rm(plex->mod_rm));
|
|
plex->sr_modrm = C_(X8616_Segment, x8616_modrm_sr(plex->mod_rm));
|
|
plex->body_at += 1;
|
|
}
|
|
|
|
if (plan->flags & x8616_plan_has_alu) {
|
|
plex->alu = C_(X8616_ALU, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->alu_shift, X8616_OPCODE_ALU_TTT_WIDTH }));
|
|
}
|
|
if (plan->flags & x8616_plan_has_cc) {
|
|
plex->cc = C_(X8616_Condition, x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->cc_shift, X8616_OPCODE_CC_WIDTH }));
|
|
}
|
|
|
|
plex->displacement_at = plex->body_at;
|
|
|
|
if (plan->flags & x8616_plan_uses_rm) plex->displacement_bytes = x8616_decode_disp_bytes[(u1_(plex->mod) << 3) | u1_(plex->rm)];
|
|
|
|
plex->direct_memory = (plan->flags & x8616_plan_uses_rm) && plex->mod == x8616_mod_mem && plex->rm == x8616_ea_direct;
|
|
|
|
if (plex->displacement_bytes == 1) plex->displacement = s2_(s1_(plex->body[plex->displacement_at]));
|
|
else if (plex->displacement_bytes == 2) plex->displacement = s2_(x8616_decode_u2(plex->body + plex->displacement_at));
|
|
|
|
if (plex->direct_memory) plex->direct_address = x8616_decode_u2(plex->body + plex->displacement_at);
|
|
|
|
plex->payload_at = plex->displacement_at + plex->displacement_bytes;
|
|
plex->payload_bytes = x8616_decode_payload_bytes[plan->payload];
|
|
|
|
if (plan->payload == x8616_payload_imm) {
|
|
plex->payload_bytes = (plex->width == x8616_width_word && ((plan->flags & x8616_plan_has_s) && plex->s) == false) ? 2 : 1;
|
|
}
|
|
|
|
plex->body_required = plex->payload_at + plex->payload_bytes;
|
|
plex->payload_u16 = x8616_decode_u2(plex->body + plex->payload_at);
|
|
plex->far_segment = x8616_decode_u2(plex->body + plex->payload_at + 2);
|
|
|
|
if (body_source_size < plex->body_required && plex->classification_truncated == 0) {
|
|
plex->classification_truncated = 1;
|
|
x8616_info_push(plex->info_arena, plex->msgs, x8616_info_error
|
|
, x8616_info_truncated_instruction
|
|
, plex->source_offset + prefix_at
|
|
, u2_(body_source_size)
|
|
, plex->body_required, body_source_size);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Operand-source plex
|
|
//
|
|
// Every possible operand source is populated once from the shared decoded
|
|
// fields. The plan projects two slots out of this array; there is no
|
|
// operand-kind switch on the hot path.
|
|
|
|
X8616_DecodedOperand* source = plex->operand_source;
|
|
|
|
source[x8616_operand_reg_modrm].flags = x8616_decoded_operand_register;
|
|
source[x8616_operand_reg_modrm].width = plex->width;
|
|
source[x8616_operand_reg_modrm].reg = plex->reg;
|
|
|
|
source[x8616_operand_reg_opcode].flags = x8616_decoded_operand_register;
|
|
source[x8616_operand_reg_opcode].width = plex->width;
|
|
source[x8616_operand_reg_opcode].reg = plex->reg_opcode;
|
|
|
|
source[x8616_operand_segment_modrm].flags = x8616_decoded_operand_segment;
|
|
source[x8616_operand_segment_modrm].width = x8616_width_word;
|
|
source[x8616_operand_segment_modrm].segment = plex->sr_modrm;
|
|
|
|
source[x8616_operand_segment_opcode].flags = x8616_decoded_operand_segment;
|
|
source[x8616_operand_segment_opcode].width = x8616_width_word;
|
|
source[x8616_operand_segment_opcode].segment = plex->sr_opcode;
|
|
|
|
source[x8616_operand_acc].flags = x8616_decoded_operand_register | x8616_decoded_operand_implicit;
|
|
source[x8616_operand_acc].width = plex->width;
|
|
source[x8616_operand_acc].reg.r16 = x8616_ax;
|
|
|
|
source[x8616_operand_dx].flags = x8616_decoded_operand_register | x8616_decoded_operand_implicit;
|
|
source[x8616_operand_dx].width = x8616_width_word;
|
|
source[x8616_operand_dx].reg.r16 = x8616_dx;
|
|
|
|
source[x8616_operand_rm].width = plex->width;
|
|
if (plex->mod == x8616_mod_reg) {
|
|
source[x8616_operand_rm].flags = x8616_decoded_operand_register;
|
|
source[x8616_operand_rm].reg.r16 = C_(X8616_Reg16, plex->rm);
|
|
}
|
|
else {
|
|
source[x8616_operand_rm].flags = x8616_decoded_operand_memory;
|
|
source[x8616_operand_rm].mod = plex->mod;
|
|
source[x8616_operand_rm].ea = plex->rm;
|
|
source[x8616_operand_rm].displacement = plex->displacement;
|
|
source[x8616_operand_rm].displacement_bytes = plex->displacement_bytes;
|
|
if (plex->direct_memory) {
|
|
source[x8616_operand_rm].flags |= x8616_decoded_operand_direct;
|
|
source[x8616_operand_rm].address = plex->direct_address;
|
|
}
|
|
}
|
|
|
|
source[x8616_operand_imm].flags = x8616_decoded_operand_immediate
|
|
| (((plan->flags & x8616_plan_has_s) && plex->s) ? x8616_decoded_operand_sign_extended : 0);
|
|
source[x8616_operand_imm].width = plex->width;
|
|
source[x8616_operand_imm].immediate = plex->payload_u16;
|
|
source[x8616_operand_imm].immediate_bytes = plex->payload_bytes;
|
|
|
|
source[x8616_operand_imm8].flags = x8616_decoded_operand_immediate;
|
|
source[x8616_operand_imm8].width = x8616_width_byte;
|
|
source[x8616_operand_imm8].immediate = u1_(plex->payload_u16);
|
|
source[x8616_operand_imm8].immediate_bytes = 1;
|
|
|
|
source[x8616_operand_imm16].flags = x8616_decoded_operand_immediate;
|
|
source[x8616_operand_imm16].width = x8616_width_word;
|
|
source[x8616_operand_imm16].immediate = plex->payload_u16;
|
|
source[x8616_operand_imm16].immediate_bytes = 2;
|
|
|
|
source[x8616_operand_mem_direct].flags = x8616_decoded_operand_memory | x8616_decoded_operand_direct;
|
|
source[x8616_operand_mem_direct].width = plex->width;
|
|
source[x8616_operand_mem_direct].address = plex->payload_u16;
|
|
|
|
source[x8616_operand_rel8].flags = x8616_decoded_operand_relative;
|
|
source[x8616_operand_rel8].width = x8616_width_byte;
|
|
source[x8616_operand_rel8].displacement = C_(S2, s1_(plex->payload_u16));
|
|
source[x8616_operand_rel8].displacement_bytes = 1;
|
|
|
|
source[x8616_operand_rel16].flags = x8616_decoded_operand_relative;
|
|
source[x8616_operand_rel16].width = x8616_width_word;
|
|
source[x8616_operand_rel16].displacement = s2_(plex->payload_u16);
|
|
source[x8616_operand_rel16].displacement_bytes = 2;
|
|
|
|
source[x8616_operand_far_ptr].flags = x8616_decoded_operand_far_ptr;
|
|
source[x8616_operand_far_ptr].far_offset = plex->payload_u16;
|
|
source[x8616_operand_far_ptr].far_segment = plex->far_segment;
|
|
|
|
source[x8616_operand_shift_count].flags = x8616_decoded_operand_implicit;
|
|
source[x8616_operand_shift_count].width = x8616_width_byte;
|
|
if (plex->v == x8616_v_cl) {
|
|
source[x8616_operand_shift_count].flags |= x8616_decoded_operand_register;
|
|
source[x8616_operand_shift_count].reg.r8 = x8616_cl;
|
|
}
|
|
else {
|
|
source[x8616_operand_shift_count].flags |= x8616_decoded_operand_immediate;
|
|
source[x8616_operand_shift_count].immediate = 1;
|
|
source[x8616_operand_shift_count].immediate_bytes = 0;
|
|
}
|
|
|
|
// Final projection. Authored operand order is d=0; d=1 exchanges the two source slots without introducing a separate decoder path.
|
|
U1 swap = C_(U1, ((plan->flags & x8616_plan_has_d) != 0) & u1_(plex->d));
|
|
plex->instruction.operands[swap] = source[plan->operands[0]];
|
|
plex->instruction.operands[swap ^ 1] = source[plan->operands[1]];
|
|
|
|
plex->instruction.op = plan->op;
|
|
if (plan->flags & x8616_plan_has_alu) {
|
|
plex->instruction.alu = plex->alu;
|
|
plex->instruction.op = x8616_op_from_alu[plex->alu];
|
|
}
|
|
if (plan->flags & x8616_plan_has_cc) {
|
|
plex->instruction.cc = plex->cc;
|
|
plex->instruction.op = x8616_op_from_cc[plex->cc];
|
|
}
|
|
if (plan->digit_kind != x8616_digit_none) {
|
|
U1 digit = u1_(plex->reg.r16);
|
|
if (plan->digit_kind == x8616_digit_alu) {
|
|
plex->alu = C_(X8616_ALU, digit);
|
|
plex->instruction.alu = plex->alu;
|
|
plex->instruction.op = x8616_op_from_alu[digit];
|
|
}
|
|
else if (plan->digit_kind == x8616_digit_shift) plex->instruction.op = x8616_op_from_shift[digit];
|
|
else if (plan->digit_kind == x8616_digit_g3) plex->instruction.op = x8616_op_from_g3[digit];
|
|
else if (plan->digit_kind == x8616_digit_ff) plex->instruction.op = x8616_op_from_ff[digit];
|
|
else if (plan->digit_kind == x8616_digit_incdec) plex->instruction.op = x8616_op_from_incdec[digit];
|
|
}
|
|
if (plan->flags & x8616_plan_has_pair) {
|
|
U1 bit = x8616_bit_field_extract(plex->header, (X8616_BitField){ plan->pair_shift, X8616_OPCODE_PAIR_WIDTH });
|
|
if (plan->pair_kind == x8616_pair_incdec) plex->instruction.op = x8616_op_from_incdec[bit];
|
|
if (plan->pair_kind == x8616_pair_pushpop) plex->instruction.op = x8616_op_from_pushpop[bit];
|
|
}
|
|
plex->instruction.flags = plan->encoding_flags;
|
|
plex->instruction.decode_flags = (plex->encoding_invalid ? x8616_decode_invalid : 0) | (plex->classification_truncated ? x8616_decode_truncated : 0);
|
|
plex->instruction.width = plex->width;
|
|
plex->instruction.prefixes = plex->prefixes;
|
|
plex->instruction.operand_count = plan->operand_count;
|
|
plex->instruction.opcode = x8616_decode_opcode(plan, plex->header);
|
|
plex->instruction.d = (plan->flags & x8616_plan_has_d) ? plex->d : x8616_d_rm_dst;
|
|
plex->instruction.w = (plan->flags & x8616_plan_has_w) ? plex->w : x8616_w_byte;
|
|
plex->instruction.has_mod_rm = (plan->flags & x8616_plan_has_modrm) != 0;
|
|
|
|
U4 total_required = prefix_at + plex->body_required;
|
|
U4 total_available = plex->source_size;
|
|
U4 total_consumed = total_required < total_available ? total_required : total_available;
|
|
|
|
plex->instruction.size = u1_(total_consumed);
|
|
plex->instruction.size_required = u1_(total_required);
|
|
return total_consumed;
|
|
}
|
|
|
|
#define x8616_decode_(...) x8616_decode((X8616_DecodeRequest){__VA_ARGS__})
|
|
X8616_DecodeInfo x8616_decode(X8616_DecodeRequest request)
|
|
{
|
|
X8616_DecodeInfo result = {0};
|
|
result.instruction_cap = request.instruction_cap;
|
|
|
|
while (result.source_consumed < request.source_len && result.instruction_count < request.instruction_cap) {
|
|
X8616_DecodePlex plex = {0};
|
|
plex.source = request.source + result.source_consumed;
|
|
plex.source_size = request.source_len - result.source_consumed;
|
|
plex.source_offset = result.source_consumed;
|
|
plex.info_arena = request.info_arena;
|
|
plex.msgs = & result.msgs;
|
|
U4 consumed = x8616_decode_one_plex(& plex);
|
|
request.out_instructions[result.instruction_count] = plex.instruction;
|
|
result.instruction_count += 1;
|
|
if (consumed == false) consumed = 1;
|
|
result.source_consumed += consumed;
|
|
}
|
|
|
|
if (result.source_consumed < request.source_len && result.instruction_count == request.instruction_cap) {
|
|
x8616_info_push(request.info_arena, & result.msgs, x8616_info_warning
|
|
, x8616_info_output_full
|
|
, result.source_consumed
|
|
, 0
|
|
, request.instruction_cap
|
|
, result.instruction_count
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
X8616_DecodeOneInfo x8616_decode_one(U1_R source, U4 source_size, FArena_R info_arena) {
|
|
X8616_DecodeOneInfo result = {0};
|
|
X8616_DecodePlex plex = {0};
|
|
plex.source = source;
|
|
plex.source_size = source_size;
|
|
plex.source_offset = 0;
|
|
plex.info_arena = info_arena;
|
|
plex.msgs = & result.msgs;
|
|
result.source_consumed = x8616_decode_one_plex(& plex);
|
|
result.instruction = plex.instruction;
|
|
return result;
|
|
}
|