Skip to content

Commit 700896c

Browse files
committed
M68K: Fix fmove.x 96-bit extended immediate decode length and printing
1 parent c22422b commit 700896c

3 files changed

Lines changed: 330 additions & 3 deletions

File tree

arch/M68K/M68KDisassembler.c

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

234+
/* Round `value >> shift` to nearest, ties to even. Requires 1 <= shift <= 64. */
235+
static uint64_t round_right_to_even(uint64_t value, unsigned int shift)
236+
{
237+
if (shift < 64) {
238+
uint64_t quotient = value >> shift;
239+
uint64_t remainder = value & ((1ULL << shift) - 1);
240+
uint64_t halfway = 1ULL << (shift - 1);
241+
if (remainder > halfway ||
242+
(remainder == halfway && (quotient & 1)))
243+
return quotient + 1;
244+
return quotient;
245+
}
246+
/* shift == 64: only values above the halfway point round up, and the
247+
* exactly-halfway value rounds to even, i.e. to 0. */
248+
if (value > 0x8000000000000000ULL)
249+
return 1;
250+
return 0;
251+
}
252+
253+
/* Convert a Motorola 96-bit extended-precision real (16-bit sign+exponent
254+
* word and 64-bit significand; the reserved 16-bit word is not part of the
255+
* encoding passed in here) to IEEE-754 binary64.
256+
* Pure integer arithmetic:Out-of-range values map to
257+
* infinity, subnormals, or signed zero; NaNs become quiet NaNs. */
258+
static double extended_to_double(uint16_t sign_exp, uint64_t significand)
259+
{
260+
const uint64_t sign = ((uint64_t)(sign_exp & 0x8000)) << 48;
261+
const unsigned int E = sign_exp & 0x7fff;
262+
263+
if (E == 0x7fff) {
264+
uint64_t fraction = significand & 0x7fffffffffffffffULL;
265+
if (fraction == 0)
266+
return BitsToDouble(sign | 0x7ff0000000000000ULL);
267+
/* quiet NaN: set the quiet bit, keep the top 52 payload bits */
268+
return BitsToDouble(sign | 0x7ff0000000000000ULL |
269+
((fraction >> 11) | 0x0008000000000000ULL));
270+
}
271+
272+
if (E == 0 || significand == 0)
273+
return BitsToDouble(sign); /* signed zero */
274+
275+
{
276+
unsigned int leading = CountLeadingZeros_64(significand);
277+
uint64_t normalized = significand << leading;
278+
int64_t e = (int64_t)E - 16383 - (int64_t)leading;
279+
280+
if (e > 1023)
281+
return BitsToDouble(sign | 0x7ff0000000000000ULL);
282+
283+
if (e >= -1022) {
284+
uint64_t rounded = round_right_to_even(normalized, 11);
285+
if (rounded == (1ULL << 53)) {
286+
rounded >>= 1;
287+
++e;
288+
if (e > 1023)
289+
return BitsToDouble(
290+
sign | 0x7ff0000000000000ULL);
291+
}
292+
return BitsToDouble(sign |
293+
((uint64_t)(e + 1023) << 52) |
294+
(rounded & 0x000fffffffffffffULL));
295+
}
296+
297+
{
298+
uint64_t shift = (uint64_t)(-e - 1011);
299+
uint64_t fraction =
300+
shift > 64 ? 0 :
301+
round_right_to_even(
302+
normalized,
303+
(unsigned int)shift);
304+
/* rounding to 1ULL << 52 naturally encodes the
305+
* smallest normal binary64 value */
306+
return BitsToDouble(sign | fraction);
307+
}
308+
}
309+
}
310+
311+
/* Read a 12-byte Motorola extended-precision immediate: sign+exponent word,
312+
* reserved word (always ignored), 64-bit significand. */
313+
static double read_imm_extended(m68k_info *info)
314+
{
315+
uint16_t sign_exp = (uint16_t)read_imm_16(info);
316+
(void)read_imm_16(info); /* reserved word */
317+
uint64_t significand = read_imm_64(info);
318+
return extended_to_double(sign_exp, significand);
319+
}
320+
234321
/* 100% portable signed int generators */
235322
static int make_int_8(int value)
236323
{
@@ -3184,7 +3271,11 @@ static void d68020_cpgen(m68k_info *info)
31843271
case M68K_FPSRC_EXTENDED:
31853272
ext->op_size.type = M68K_SIZE_TYPE_FPU;
31863273
ext->op_size.fpu_size = M68K_FPU_SIZE_EXTENDED;
3187-
if (!get_ea_mode_op(info, op0, info->ir, 12)) {
3274+
if (m68k_ea_is_immediate(info->ir)) {
3275+
op0->address_mode = M68K_AM_IMMEDIATE;
3276+
op0->type = M68K_OP_FP_DOUBLE;
3277+
op0->dimm = read_imm_extended(info);
3278+
} else if (!get_ea_mode_op(info, op0, info->ir, 12)) {
31883279
invalid_insn(info);
31893280
return;
31903281
}

arch/M68K/M68KInstPrinter.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,13 @@ static void printImmediate(SStream *O, const cs_m68k *inst,
219219
SStream_concat(O, "#<float_point_unsupported>");
220220
return;
221221
#else
222-
if (inst->op_size.fpu_size == M68K_FPU_SIZE_SINGLE)
222+
/* Dispatch on the operand storage type: single- and
223+
* double-precision immediates print their converted value,
224+
* extended-precision `.x` values arrive as binary64 in
225+
* `dimm`, while packed decimal immediates stay unsupported. */
226+
if (op->type == M68K_OP_FP_SINGLE)
223227
SStream_concat(O, "#%f", op->simm);
224-
else if (inst->op_size.fpu_size == M68K_FPU_SIZE_DOUBLE)
228+
else if (op->type == M68K_OP_FP_DOUBLE)
225229
SStream_concat(O, "#%f", op->dimm);
226230
else
227231
SStream_concat(O, "#<unsupported>");

tests/details/m68k.yaml

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,6 +3770,238 @@ test_cases:
37703770
reg: fp1
37713771
regs_write: [fp1]
37723772
regs_impl_write: [fp1]
3773+
# Motorola 96-bit extended-precision immediate (fmove.x):
3774+
# canonical +1.0 is 3fff 0000 8000 0000 0000 0000 (reserved word ignored).
3775+
# The fmove.x must consume all 16 bytes so the two nops follow directly.
3776+
- input:
3777+
bytes:
3778+
[0xf2, 0x3c, 0x48, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
3779+
0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x71, 0x4e, 0x71]
3780+
arch: "m68k"
3781+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3782+
address: 0x0
3783+
expected:
3784+
insns:
3785+
- asm_text: "fmove.x #1.000000, fp0"
3786+
size: 16
3787+
details:
3788+
m68k:
3789+
op_size_type: M68K_SIZE_TYPE_FPU
3790+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3791+
operands:
3792+
- type: M68K_OP_FP_DOUBLE
3793+
address_mode: M68K_AM_IMMEDIATE
3794+
dimm: 1.000000
3795+
- type: M68K_OP_REG
3796+
reg: fp0
3797+
regs_write: [fp0]
3798+
regs_impl_write: [fp0]
3799+
- asm_text: "nop"
3800+
- asm_text: "nop"
3801+
# Signed value with nonzero exponent and fraction: -2.5.
3802+
- input:
3803+
bytes:
3804+
[0xf2, 0x3c, 0x48, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
3805+
0x00, 0x00, 0x00, 0x00, 0x00]
3806+
arch: "m68k"
3807+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3808+
address: 0x0
3809+
expected:
3810+
insns:
3811+
- asm_text: "fmove.x #-2.500000, fp0"
3812+
size: 16
3813+
details:
3814+
m68k:
3815+
op_size_type: M68K_SIZE_TYPE_FPU
3816+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3817+
operands:
3818+
- type: M68K_OP_FP_DOUBLE
3819+
address_mode: M68K_AM_IMMEDIATE
3820+
dimm: -2.500000
3821+
- type: M68K_OP_REG
3822+
reg: fp0
3823+
regs_write: [fp0]
3824+
regs_impl_write: [fp0]
3825+
# Rounding boundary: significand 0x8000000000000c00 is exactly halfway
3826+
# between two binary64 values; the truncated 53-bit significand is odd,
3827+
# so ties-to-even rounds up to 1 + 2^-51 (1.0000000000000004).
3828+
- input:
3829+
bytes:
3830+
[0xf2, 0x3c, 0x48, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
3831+
0x00, 0x00, 0x00, 0x0c, 0x00]
3832+
arch: "m68k"
3833+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3834+
address: 0x0
3835+
expected:
3836+
insns:
3837+
- asm_text: "fmove.x #1.000000, fp0"
3838+
size: 16
3839+
details:
3840+
m68k:
3841+
op_size_type: M68K_SIZE_TYPE_FPU
3842+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3843+
operands:
3844+
- type: M68K_OP_FP_DOUBLE
3845+
address_mode: M68K_AM_IMMEDIATE
3846+
dimm: 1.0000000000000004
3847+
- type: M68K_OP_REG
3848+
reg: fp0
3849+
regs_write: [fp0]
3850+
regs_impl_write: [fp0]
3851+
# The original request encoding put 0x8000 in the ignored reserved word
3852+
# (3fff 8000 0000 0000 0000 0000): the significand is all zero, so the
3853+
# value must decode to 0.0 while still consuming the full 16 bytes.
3854+
- input:
3855+
bytes:
3856+
[0xf2, 0x3c, 0x48, 0x00, 0x3f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
3857+
0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x71, 0x4e, 0x71]
3858+
arch: "m68k"
3859+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3860+
address: 0x0
3861+
expected:
3862+
insns:
3863+
- asm_text: "fmove.x #0.000000, fp0"
3864+
size: 16
3865+
details:
3866+
m68k:
3867+
op_size_type: M68K_SIZE_TYPE_FPU
3868+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3869+
operands:
3870+
- type: M68K_OP_FP_DOUBLE
3871+
address_mode: M68K_AM_IMMEDIATE
3872+
dimm: 0.000000
3873+
- type: M68K_OP_REG
3874+
reg: fp0
3875+
regs_write: [fp0]
3876+
regs_impl_write: [fp0]
3877+
- asm_text: "nop"
3878+
- asm_text: "nop"
3879+
# Rounding boundary with an even truncated significand: 0x8000000000000400
3880+
# is exactly halfway between binary64 neighbors, but the truncated 53-bit
3881+
# significand (0x10000000000000) is even, so ties-to-even must round DOWN
3882+
# to exactly 1.0. This distinguishes ties-to-even from round-half-up.
3883+
- input:
3884+
bytes:
3885+
[0xf2, 0x3c, 0x48, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
3886+
0x00, 0x00, 0x00, 0x04, 0x00]
3887+
arch: "m68k"
3888+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3889+
address: 0x0
3890+
expected:
3891+
insns:
3892+
- asm_text: "fmove.x #1.000000, fp0"
3893+
size: 16
3894+
details:
3895+
m68k:
3896+
op_size_type: M68K_SIZE_TYPE_FPU
3897+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3898+
operands:
3899+
- type: M68K_OP_FP_DOUBLE
3900+
address_mode: M68K_AM_IMMEDIATE
3901+
dimm: 1.000000
3902+
- type: M68K_OP_REG
3903+
reg: fp0
3904+
regs_write: [fp0]
3905+
regs_impl_write: [fp0]
3906+
# Special value: E=0x7fff with a zero 63-bit fraction encodes +infinity.
3907+
- input:
3908+
bytes:
3909+
[0xf2, 0x3c, 0x48, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
3910+
0x00, 0x00, 0x00, 0x00, 0x00]
3911+
arch: "m68k"
3912+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3913+
address: 0x0
3914+
expected:
3915+
insns:
3916+
- asm_text: "fmove.x #inf, fp0"
3917+
size: 16
3918+
details:
3919+
m68k:
3920+
op_size_type: M68K_SIZE_TYPE_FPU
3921+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3922+
operands:
3923+
- type: M68K_OP_FP_DOUBLE
3924+
address_mode: M68K_AM_IMMEDIATE
3925+
dimm: inf
3926+
- type: M68K_OP_REG
3927+
reg: fp0
3928+
regs_write: [fp0]
3929+
regs_impl_write: [fp0]
3930+
# Special value: E=0x7fff with a nonzero fraction becomes a quiet NaN.
3931+
- input:
3932+
bytes:
3933+
[0xf2, 0x3c, 0x48, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
3934+
0x00, 0x00, 0x00, 0x00, 0x01]
3935+
arch: "m68k"
3936+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3937+
address: 0x0
3938+
expected:
3939+
insns:
3940+
- asm_text: "fmove.x #nan, fp0"
3941+
size: 16
3942+
details:
3943+
m68k:
3944+
op_size_type: M68K_SIZE_TYPE_FPU
3945+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3946+
operands:
3947+
- type: M68K_OP_FP_DOUBLE
3948+
address_mode: M68K_AM_IMMEDIATE
3949+
dimm: nan
3950+
- type: M68K_OP_REG
3951+
reg: fp0
3952+
regs_write: [fp0]
3953+
regs_impl_write: [fp0]
3954+
# Subnormal path: E=0x3bcd with significand 0x8000000000000000 is the
3955+
# smallest binary64 subnormal (2^-1074, bits 0x0000000000000001).
3956+
- input:
3957+
bytes:
3958+
[0xf2, 0x3c, 0x48, 0x00, 0x3b, 0xcd, 0x00, 0x00, 0x80, 0x00, 0x00,
3959+
0x00, 0x00, 0x00, 0x00, 0x00]
3960+
arch: "m68k"
3961+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3962+
address: 0x0
3963+
expected:
3964+
insns:
3965+
- asm_text: "fmove.x #0.000000, fp0"
3966+
size: 16
3967+
details:
3968+
m68k:
3969+
op_size_type: M68K_SIZE_TYPE_FPU
3970+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3971+
operands:
3972+
- type: M68K_OP_FP_DOUBLE
3973+
address_mode: M68K_AM_IMMEDIATE
3974+
dimm: 4.9406564584124654e-324
3975+
- type: M68K_OP_REG
3976+
reg: fp0
3977+
regs_write: [fp0]
3978+
regs_impl_write: [fp0]
3979+
# Extended source with a memory effective address: no inline 12-byte
3980+
# immediate is consumed; the operand stays a memory reference.
3981+
- input:
3982+
bytes: [0xf2, 0x10, 0x48, 0x00]
3983+
arch: "m68k"
3984+
options: [CS_OPT_DETAIL, CS_MODE_M68K_040]
3985+
address: 0x0
3986+
expected:
3987+
insns:
3988+
- asm_text: "fmove.x (a0), fp0"
3989+
size: 4
3990+
details:
3991+
m68k:
3992+
op_size_type: M68K_SIZE_TYPE_FPU
3993+
op_size_fpu: M68K_FPU_SIZE_EXTENDED
3994+
operands:
3995+
- type: M68K_OP_MEM
3996+
mem:
3997+
base_reg: a0
3998+
address_mode: M68K_AM_REGI_ADDR
3999+
- type: M68K_OP_REG
4000+
reg: fp0
4001+
regs_read: [a0]
4002+
regs_write: [fp0]
4003+
regs_impl_read: [a0]
4004+
regs_impl_write: [fp0]
37734005
- input:
37744006
bytes: [0xf2, 0x10, 0x50, 0x00]
37754007
arch: "m68k"

0 commit comments

Comments
 (0)