Skip to content

Commit 95c74d3

Browse files
authored
M68K: Fix fmove.x 96-bit extended immediate decode and printing (#3016)
1 parent 7f5f5fe commit 95c74d3

15 files changed

Lines changed: 1419 additions & 32 deletions

File tree

arch/M68K/M68KDisassembler.c

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,39 @@ static unsigned long long read_imm_64(m68k_info *info)
231231
return value & 0xffffffffffffffff;
232232
}
233233

234+
/* Read a 12-byte Motorola extended-precision immediate: sign+exponent word,
235+
* reserved word, and 64-bit significand. */
236+
static bool imm_bytes_available(const m68k_info *info, size_t size)
237+
{
238+
const uint64_t addr = (info->pc - info->baseAddress) &
239+
info->address_mask;
240+
return addr <= info->code_len && info->code_len - addr >= size;
241+
}
242+
243+
static bool read_imm_extended(m68k_info *info, m68k_op_fp_extended *value)
244+
{
245+
if (!imm_bytes_available(info, 12))
246+
return false;
247+
248+
memset(value, 0, sizeof(*value));
249+
value->sign_exp = (uint16_t)read_imm_16(info);
250+
value->reserved = (uint16_t)read_imm_16(info);
251+
value->significand = read_imm_64(info);
252+
return true;
253+
}
254+
255+
/* Read a 12-byte Motorola packed-decimal immediate. */
256+
static bool read_imm_packed(m68k_info *info, m68k_op_fp_packed *value)
257+
{
258+
if (!imm_bytes_available(info, 12))
259+
return false;
260+
261+
memset(value, 0, sizeof(*value));
262+
value->header = read_imm_32(info);
263+
value->fraction = read_imm_64(info);
264+
return true;
265+
}
266+
234267
/* 100% portable signed int generators */
235268
static int make_int_8(int value)
236269
{
@@ -2906,8 +2939,11 @@ static void d68020_cpgen(m68k_info *info)
29062939
cs_m68k_op *op0;
29072940
cs_m68k_op *op1;
29082941
bool supports_single_op;
2942+
bool is_fmove;
2943+
bool packed_destination;
2944+
bool ea_operand;
29092945
uint32_t next;
2910-
int rm, src, dst, opmode;
2946+
int command_type, src, dst, opmode;
29112947

29122948
LIMIT_FEATURE(info, M68020_PLUS | CS_MODE_M68K_CF_FPU);
29132949

@@ -2929,15 +2965,20 @@ static void d68020_cpgen(m68k_info *info)
29292965
* operations (type 0-1); fmove_fpcr/fmovem types are dispatched
29302966
* separately and never reach the SD path. */
29312967
uint32_t peeked = peek_imm_16(info);
2932-
if (M68K_FEXT_TYPE(peeked) <= 1 && M68K_FEXT_SD_FLAG(peeked))
2968+
if (M68K_FEXT_TYPE(peeked) <= M68K_FEXT_TYPE_GENERAL_MAX &&
2969+
M68K_FEXT_SD_FLAG(peeked))
29332970
LIMIT_FEATURE(info, M68040_PLUS | CS_MODE_M68K_CF_FPU);
29342971

29352972
next = read_imm_16(info);
29362973

2937-
rm = M68K_FEXT_RM(next);
2974+
ea_operand = M68K_FEXT_RM(next) != 0;
2975+
command_type = M68K_FEXT_TYPE(next);
29382976
src = M68K_FEXT_SRC(next);
29392977
dst = M68K_FEXT_DST(next);
29402978
opmode = M68K_FEXT_OPMODE(next);
2979+
packed_destination = command_type == M68K_FEXT_TYPE_FMOVE_TO_EA &&
2980+
(src == M68K_FPDST_PACKED_STATIC ||
2981+
src == M68K_FPDST_PACKED_DYNAMIC);
29412982

29422983
if (BITFIELD(info->ir, 5, 0) == 0 && M68K_FEXT_IS_FMOVECR(next)) {
29432984
ext = build_init_op(info, M68K_INS_FMOVECR, 2, 0);
@@ -2954,20 +2995,28 @@ static void d68020_cpgen(m68k_info *info)
29542995
return;
29552996
}
29562997

2957-
switch (M68K_FEXT_TYPE(next)) {
2958-
case 0x4:
2959-
case 0x5:
2998+
switch (command_type) {
2999+
case M68K_FEXT_TYPE_FPCR_FROM_EA:
3000+
case M68K_FEXT_TYPE_FPCR_TO_EA:
29603001
fmove_fpcr(info, next);
29613002
return;
29623003

2963-
case 0x6:
2964-
case 0x7:
3004+
case M68K_FEXT_TYPE_FMOVEM_FROM_EA:
3005+
case M68K_FEXT_TYPE_FMOVEM_TO_EA:
29653006
fmovem(info, next);
29663007
return;
29673008
default:
29683009
break;
29693010
}
29703011

3012+
/* In a packed register-to-memory FMOVE, bits 6:0 encode the static
3013+
* k-factor or dynamic Dn selector rather than an arithmetic opmode. */
3014+
if (packed_destination) {
3015+
MCInst_setOpcode(info->inst, M68K_INS_FMOVE);
3016+
supports_single_op = false;
3017+
goto fpu_operands;
3018+
}
3019+
29713020
if (M68K_FEXT_SD_FLAG(next)) {
29723021
if (opmode == M68K_FPOP_FSSQRT_RAW) {
29733022
MCInst_setOpcode(info->inst, M68K_INS_FSSQRT);
@@ -3112,26 +3161,54 @@ static void d68020_cpgen(m68k_info *info)
31123161

31133162
fpu_operands:
31143163
ext = &info->extension;
3164+
is_fmove = MCInst_getOpcode(info->inst) == M68K_INS_FMOVE;
31153165

31163166
ext->op_count = 2;
31173167
ext->op_size.type = M68K_SIZE_TYPE_CPU;
31183168
ext->op_size.cpu_size = 0;
31193169

3120-
if ((opmode == 0x00) && M68K_FEXT_DIR(next) != 0) {
3170+
if ((opmode == 0x00 || packed_destination) &&
3171+
M68K_FEXT_DIR(next) != 0) {
31213172
op0 = &ext->operands[1];
31223173
op1 = &ext->operands[0];
31233174
} else {
31243175
op0 = &ext->operands[0];
31253176
op1 = &ext->operands[1];
31263177
}
31273178

3128-
if (rm == 0 && supports_single_op && src == dst) {
3179+
if (!ea_operand && supports_single_op && src == dst) {
31293180
ext->op_count = 1;
31303181
op0->reg = M68K_REG_FP0 + dst;
31313182
return;
31323183
}
31333184

3134-
if (rm == 1) {
3185+
if (packed_destination) {
3186+
cs_m68k_op *op_k = &ext->operands[2];
3187+
ext->op_size.type = M68K_SIZE_TYPE_FPU;
3188+
ext->op_size.fpu_size = M68K_FPU_SIZE_PACKED;
3189+
if (!get_ea_mode_op(info, op0, info->ir, 12)) {
3190+
invalid_insn(info);
3191+
return;
3192+
}
3193+
3194+
ext->op_count = 3;
3195+
if (src == M68K_FPDST_PACKED_STATIC) {
3196+
int k_factor = next & 0x7f;
3197+
if (k_factor & 0x40)
3198+
k_factor -= 0x80;
3199+
op_k->address_mode = M68K_AM_IMMEDIATE;
3200+
op_k->type = M68K_OP_IMM;
3201+
op_k->imm = (uint64_t)(int64_t)k_factor;
3202+
} else {
3203+
op_k->address_mode = M68K_AM_NONE;
3204+
op_k->type = M68K_OP_REG;
3205+
op_k->reg = M68K_REG_D0 + ((next >> 4) & 7);
3206+
}
3207+
op1->reg = M68K_REG_FP0 + dst;
3208+
return;
3209+
}
3210+
3211+
if (ea_operand) {
31353212
switch (src) {
31363213
case M68K_FPSRC_LONG:
31373214
ext->op_size.cpu_size = M68K_CPU_SIZE_LONG;
@@ -3184,7 +3261,15 @@ static void d68020_cpgen(m68k_info *info)
31843261
case M68K_FPSRC_EXTENDED:
31853262
ext->op_size.type = M68K_SIZE_TYPE_FPU;
31863263
ext->op_size.fpu_size = M68K_FPU_SIZE_EXTENDED;
3187-
if (!get_ea_mode_op(info, op0, info->ir, 12)) {
3264+
if (is_fmove && m68k_ea_is_immediate(info->ir)) {
3265+
if (!read_imm_extended(info,
3266+
&op0->fp_extended)) {
3267+
invalid_insn(info);
3268+
return;
3269+
}
3270+
op0->address_mode = M68K_AM_IMMEDIATE;
3271+
op0->type = M68K_OP_FP_EXTENDED;
3272+
} else if (!get_ea_mode_op(info, op0, info->ir, 12)) {
31883273
invalid_insn(info);
31893274
return;
31903275
}
@@ -3193,7 +3278,16 @@ static void d68020_cpgen(m68k_info *info)
31933278
case M68K_FPSRC_PACKED:
31943279
ext->op_size.type = M68K_SIZE_TYPE_FPU;
31953280
ext->op_size.fpu_size = M68K_FPU_SIZE_EXTENDED;
3196-
if (!get_ea_mode_op(info, op0, info->ir, 12)) {
3281+
if (is_fmove)
3282+
ext->op_size.fpu_size = M68K_FPU_SIZE_PACKED;
3283+
if (is_fmove && m68k_ea_is_immediate(info->ir)) {
3284+
if (!read_imm_packed(info, &op0->fp_packed)) {
3285+
invalid_insn(info);
3286+
return;
3287+
}
3288+
op0->address_mode = M68K_AM_IMMEDIATE;
3289+
op0->type = M68K_OP_FP_PACKED;
3290+
} else if (!get_ea_mode_op(info, op0, info->ir, 12)) {
31973291
invalid_insn(info);
31983292
return;
31993293
}
@@ -5322,6 +5416,18 @@ static void build_regs_read_write_counts(m68k_info *info)
53225416

53235417
if (!info->extension.op_count)
53245418
return;
5419+
if (MCInst_getOpcode(info->inst) == M68K_INS_FMOVE &&
5420+
info->extension.op_size.type == M68K_SIZE_TYPE_FPU &&
5421+
info->extension.op_size.fpu_size == M68K_FPU_SIZE_PACKED &&
5422+
info->extension.op_count == 3) {
5423+
/* Packed register-to-memory FMOVE reads both the FP source and
5424+
* its static/dynamic k-factor; only the memory operand is a
5425+
* destination. */
5426+
update_op_reg_list(info, &info->extension.operands[0], 0);
5427+
update_op_reg_list(info, &info->extension.operands[1], 1);
5428+
update_op_reg_list(info, &info->extension.operands[2], 0);
5429+
return;
5430+
}
53255431
if (info->inst->Opcode == M68K_INS_FMOVEM &&
53265432
info->extension.op_count == 2 &&
53275433
info->extension.operands[1].type == M68K_OP_REG) {

arch/M68K/M68KDisassembler.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,19 @@ static inline uint32_t m68k_coprocessor_condition(uint32_t word)
147147
/* ── FPU extension-word bit-field helpers ────────────────────────────
148148
* The FPU command word is the 16-bit extension following the F-line. */
149149

150-
/* R/M bit (bit 14): 1 = source from EA, 0 = source from FP register. */
150+
/* R/M bit (bit 14): 1 = effective-address operand, 0 = FP-register operand. */
151151
#define M68K_FEXT_RM(ext) (((ext) >> 14) & 1)
152152

153153
/* Type / command class (bits 15:13). */
154154
#define M68K_FEXT_TYPE(ext) (((ext) >> 13) & 7)
155-
156-
/* Source specifier (bits 12:10) -- data format when R/M=1. */
155+
#define M68K_FEXT_TYPE_GENERAL_MAX 0x1
156+
#define M68K_FEXT_TYPE_FMOVE_TO_EA 0x3
157+
#define M68K_FEXT_TYPE_FPCR_FROM_EA 0x4
158+
#define M68K_FEXT_TYPE_FPCR_TO_EA 0x5
159+
#define M68K_FEXT_TYPE_FMOVEM_FROM_EA 0x6
160+
#define M68K_FEXT_TYPE_FMOVEM_TO_EA 0x7
161+
162+
/* Source/format specifier (bits 12:10). */
157163
#define M68K_FEXT_SRC(ext) (((ext) >> 10) & 7)
158164

159165
/* Destination FP register (bits 9:7). */
@@ -237,6 +243,10 @@ static inline uint32_t m68k_fpu_condition_index(uint32_t word)
237243
#define M68K_FPSRC_DOUBLE 0x05 /* .d -- 64-bit IEEE double */
238244
#define M68K_FPSRC_BYTE 0x06 /* .b -- 8-bit integer */
239245

246+
/* FMOVE register-to-memory packed-decimal destination encodings. */
247+
#define M68K_FPDST_PACKED_STATIC 0x03
248+
#define M68K_FPDST_PACKED_DYNAMIC 0x07
249+
240250
/* ── FPU special raw opmodes (before SD-flag masking) ───────────────
241251
* FSSQRT/FDSQRT have raw 7-bit opmodes 0x41/0x45. After the 6-bit
242252
* truncation (& 0x3f) they become 0x01/0x05 with the SD flag set. */

0 commit comments

Comments
 (0)