From c4179a94b0052032d799b7ed0dd6ef946fd01ad5 Mon Sep 17 00:00:00 2001 From: billow Date: Thu, 6 Aug 2026 21:14:13 +0800 Subject: [PATCH] M68K: Fix fmove.x 96-bit extended immediate decode length and printing --- arch/M68K/M68KDisassembler.c | 26 +- arch/M68K/M68KInstPrinter.c | 83 ++- bindings/java/capstone/M68k_const.java | 3 +- bindings/ocaml/m68k_const.ml | 1 + bindings/python/capstone/m68k.py | 13 + bindings/python/capstone/m68k_const.py | 38 +- .../python/cstest_py/src/cstest_py/details.py | 21 + cstool/cstool_m68k.c | 12 + include/capstone/m68k.h | 14 + suite/cstest/include/test_detail_m68k.h | 21 + suite/cstest/include/test_mapping.h | 1 + suite/cstest/src/test_detail_m68k.c | 17 + tests/details/m68k.yaml | 499 ++++++++++++++++++ 13 files changed, 733 insertions(+), 16 deletions(-) diff --git a/arch/M68K/M68KDisassembler.c b/arch/M68K/M68KDisassembler.c index 9f3e45e25d..09b5e767af 100644 --- a/arch/M68K/M68KDisassembler.c +++ b/arch/M68K/M68KDisassembler.c @@ -231,6 +231,22 @@ static unsigned long long read_imm_64(m68k_info *info) return value & 0xffffffffffffffff; } +/* Read a 12-byte Motorola extended-precision immediate: sign+exponent word, + * reserved word, and 64-bit significand. */ +static bool read_imm_extended(m68k_info *info, m68k_op_fp_extended *value) +{ + const uint64_t addr = (info->pc - info->baseAddress) & + info->address_mask; + if (addr > info->code_len || info->code_len - addr < 12) + return false; + + memset(value, 0, sizeof(*value)); + value->sign_exp = (uint16_t)read_imm_16(info); + value->reserved = (uint16_t)read_imm_16(info); + value->significand = read_imm_64(info); + return true; +} + /* 100% portable signed int generators */ static int make_int_8(int value) { @@ -3184,7 +3200,15 @@ static void d68020_cpgen(m68k_info *info) case M68K_FPSRC_EXTENDED: ext->op_size.type = M68K_SIZE_TYPE_FPU; ext->op_size.fpu_size = M68K_FPU_SIZE_EXTENDED; - if (!get_ea_mode_op(info, op0, info->ir, 12)) { + if (m68k_ea_is_immediate(info->ir)) { + if (!read_imm_extended(info, + &op0->fp_extended)) { + invalid_insn(info); + return; + } + op0->address_mode = M68K_AM_IMMEDIATE; + op0->type = M68K_OP_FP_EXTENDED; + } else if (!get_ea_mode_op(info, op0, info->ir, 12)) { invalid_insn(info); return; } diff --git a/arch/M68K/M68KInstPrinter.c b/arch/M68K/M68KInstPrinter.c index e402279780..f1ba231a7a 100644 --- a/arch/M68K/M68KInstPrinter.c +++ b/arch/M68K/M68KInstPrinter.c @@ -16,6 +16,7 @@ #include "../../MCInst.h" #include "../../MCInstrDesc.h" #include "../../MCRegisterInfo.h" +#include "../../MathExtras.h" #ifndef CAPSTONE_DIET static const char s_spacing[] = " "; @@ -211,6 +212,79 @@ static void printBitfield(SStream *O, const cs_m68k_op *op) SStream_concat0(O, "}"); } +#if !defined(_KERNEL_MODE) +/* Round `value >> shift` to nearest, ties to even. Requires 1 <= shift <= 64. */ +static uint64_t round_right_to_even(uint64_t value, unsigned int shift) +{ + if (shift < 64) { + uint64_t quotient = value >> shift; + uint64_t remainder = value & ((1ULL << shift) - 1); + uint64_t halfway = 1ULL << (shift - 1); + if (remainder > halfway || + (remainder == halfway && (quotient & 1))) + return quotient + 1; + return quotient; + } + if (value > 0x8000000000000000ULL) + return 1; + return 0; +} + +/* Convert only for assembly-text rendering. The detail operand retains the + * complete external representation in fp_extended. */ +static double extended_to_double(const m68k_op_fp_extended *value) +{ + const uint16_t sign_exp = value->sign_exp; + const uint64_t significand = value->significand; + const uint64_t sign = ((uint64_t)(sign_exp & 0x8000)) << 48; + const unsigned int E = sign_exp & 0x7fff; + + if (E == 0x7fff) { + uint64_t fraction = significand & 0x7fffffffffffffffULL; + if (fraction == 0) + return BitsToDouble(sign | 0x7ff0000000000000ULL); + return BitsToDouble(sign | 0x7ff0000000000000ULL | + ((fraction >> 11) | 0x0008000000000000ULL)); + } + + if (E == 0 || significand == 0) + return BitsToDouble(sign); + + { + unsigned int leading = CountLeadingZeros_64(significand); + uint64_t normalized = significand << leading; + int64_t e = (int64_t)E - 16383 - (int64_t)leading; + + if (e > 1023) + return BitsToDouble(sign | 0x7ff0000000000000ULL); + + if (e >= -1022) { + uint64_t rounded = round_right_to_even(normalized, 11); + if (rounded == (1ULL << 53)) { + rounded >>= 1; + ++e; + if (e > 1023) + return BitsToDouble( + sign | 0x7ff0000000000000ULL); + } + return BitsToDouble(sign | + ((uint64_t)(e + 1023) << 52) | + (rounded & 0x000fffffffffffffULL)); + } + + { + uint64_t shift = (uint64_t)(-e - 1011); + uint64_t fraction = + shift > 64 ? 0 : + round_right_to_even( + normalized, + (unsigned int)shift); + return BitsToDouble(sign | fraction); + } + } +} +#endif + static void printImmediate(SStream *O, const cs_m68k *inst, const cs_m68k_op *op) { @@ -219,10 +293,15 @@ static void printImmediate(SStream *O, const cs_m68k *inst, SStream_concat(O, "#"); return; #else - if (inst->op_size.fpu_size == M68K_FPU_SIZE_SINGLE) + /* Dispatch on the operand storage type. Extended immediates + * are converted only for GNU-compatible assembly text. */ + if (op->type == M68K_OP_FP_SINGLE) SStream_concat(O, "#%f", op->simm); - else if (inst->op_size.fpu_size == M68K_FPU_SIZE_DOUBLE) + else if (op->type == M68K_OP_FP_DOUBLE) SStream_concat(O, "#%f", op->dimm); + else if (op->type == M68K_OP_FP_EXTENDED) + SStream_concat(O, "#0e%g", + extended_to_double(&op->fp_extended)); else SStream_concat(O, "#"); return; diff --git a/bindings/java/capstone/M68k_const.java b/bindings/java/capstone/M68k_const.java index 68287e78c5..1b4ce3e367 100644 --- a/bindings/java/capstone/M68k_const.java +++ b/bindings/java/capstone/M68k_const.java @@ -94,6 +94,7 @@ public class M68k_const { public static final int M68K_OP_REG_PAIR = CS_OP_SPECIAL+3; public static final int M68K_OP_BR_DISP = CS_OP_SPECIAL+4; public static final int M68K_OP_SHIFT = CS_OP_SPECIAL+5; + public static final int M68K_OP_FP_EXTENDED = CS_OP_SPECIAL+6; public static final int M68K_OP_MEM = CS_OP_MEM; public static final int M68K_OP_FLAG_NONE = 0; @@ -534,4 +535,4 @@ public class M68k_const { public static final int M68K_GRP_IRET = 5; public static final int M68K_GRP_BRANCH_RELATIVE = 7; public static final int M68K_GRP_ENDING = 8; -} \ No newline at end of file +} diff --git a/bindings/ocaml/m68k_const.ml b/bindings/ocaml/m68k_const.ml index d9ff737b3c..b71905f9a9 100644 --- a/bindings/ocaml/m68k_const.ml +++ b/bindings/ocaml/m68k_const.ml @@ -78,6 +78,7 @@ let _M68K_OP_FP_DOUBLE = _CS_OP_SPECIAL+1;; let _M68K_OP_REG_BITS = _CS_OP_SPECIAL+2;; let _M68K_OP_REG_PAIR = _CS_OP_SPECIAL+3;; let _M68K_OP_BR_DISP = _CS_OP_SPECIAL+4;; +let _M68K_OP_FP_EXTENDED = _CS_OP_SPECIAL+6;; let _M68K_OP_MEM = _CS_OP_MEM;; let _M68K_OP_BR_DISP_SIZE_INVALID = 0;; diff --git a/bindings/python/capstone/m68k.py b/bindings/python/capstone/m68k.py index 86f4e6501a..05377eba2b 100644 --- a/bindings/python/capstone/m68k.py +++ b/bindings/python/capstone/m68k.py @@ -33,11 +33,20 @@ class M68KOpRegPair(ctypes.Structure): ) +class M68KOpFpExtended(ctypes.Structure): + _fields_ = ( + ("significand", ctypes.c_uint64), + ("sign_exp", ctypes.c_uint16), + ("reserved", ctypes.c_uint16), + ) + + class M68KOpValue(ctypes.Union): _fields_ = ( ("imm", ctypes.c_int64), ("dimm", ctypes.c_double), ("simm", ctypes.c_float), + ("fp_extended", M68KOpFpExtended), ("reg", ctypes.c_uint), ("reg_pair", M68KOpRegPair), ) @@ -73,6 +82,10 @@ def dimm(self): def simm(self): return self.value.simm + @property + def fp_extended(self): + return self.value.fp_extended + @property def reg(self): return self.value.reg diff --git a/bindings/python/capstone/m68k_const.py b/bindings/python/capstone/m68k_const.py index 4f283a8fb5..af22cbe783 100644 --- a/bindings/python/capstone/m68k_const.py +++ b/bindings/python/capstone/m68k_const.py @@ -1,4 +1,17 @@ -from . import CS_OP_INVALID, CS_OP_REG, CS_OP_IMM, CS_OP_FP, CS_OP_PRED, CS_OP_SPECIAL, CS_OP_MEM, CS_OP_MEM_REG, CS_OP_MEM_IMM, UINT16_MAX, UINT8_MAX +from . import ( + CS_OP_INVALID, + CS_OP_REG, + CS_OP_IMM, + CS_OP_FP, + CS_OP_PRED, + CS_OP_SPECIAL, + CS_OP_MEM, + CS_OP_MEM_REG, + CS_OP_MEM_IMM, + UINT16_MAX, + UINT8_MAX, +) + # For Capstone Engine. AUTO-GENERATED FILE, DO NOT EDIT [m68k_const.py] M68K_OPERAND_COUNT = 6 @@ -86,20 +99,21 @@ M68K_OP_INVALID = CS_OP_INVALID M68K_OP_REG = CS_OP_REG M68K_OP_IMM = CS_OP_IMM -M68K_OP_FP_SINGLE = CS_OP_SPECIAL+0 -M68K_OP_FP_DOUBLE = CS_OP_SPECIAL+1 -M68K_OP_REG_BITS = CS_OP_SPECIAL+2 -M68K_OP_REG_PAIR = CS_OP_SPECIAL+3 -M68K_OP_BR_DISP = CS_OP_SPECIAL+4 -M68K_OP_SHIFT = CS_OP_SPECIAL+5 +M68K_OP_FP_SINGLE = CS_OP_SPECIAL + 0 +M68K_OP_FP_DOUBLE = CS_OP_SPECIAL + 1 +M68K_OP_REG_BITS = CS_OP_SPECIAL + 2 +M68K_OP_REG_PAIR = CS_OP_SPECIAL + 3 +M68K_OP_BR_DISP = CS_OP_SPECIAL + 4 +M68K_OP_SHIFT = CS_OP_SPECIAL + 5 +M68K_OP_FP_EXTENDED = CS_OP_SPECIAL + 6 M68K_OP_MEM = CS_OP_MEM M68K_OP_FLAG_NONE = 0 -M68K_OP_FLAG_REG_LOWER = 1<<0 -M68K_OP_FLAG_REG_UPPER = 1<<1 -M68K_OP_FLAG_SHIFT_LEFT = 1<<2 -M68K_OP_FLAG_SHIFT_RIGHT = 1<<3 -M68K_OP_FLAG_MEM_UPDATE = 1<<4 +M68K_OP_FLAG_REG_LOWER = 1 << 0 +M68K_OP_FLAG_REG_UPPER = 1 << 1 +M68K_OP_FLAG_SHIFT_LEFT = 1 << 2 +M68K_OP_FLAG_SHIFT_RIGHT = 1 << 3 +M68K_OP_FLAG_MEM_UPDATE = 1 << 4 M68K_BF_REG_FLAG = 0x80 M68K_OP_BR_DISP_SIZE_INVALID = 0 diff --git a/bindings/python/cstest_py/src/cstest_py/details.py b/bindings/python/cstest_py/src/cstest_py/details.py index f6adf60309..2ed0aa030f 100644 --- a/bindings/python/cstest_py/src/cstest_py/details.py +++ b/bindings/python/cstest_py/src/cstest_py/details.py @@ -67,6 +67,7 @@ M68K_OP_BR_DISP, M68K_OP_REG_BITS, M68K_OP_FP_DOUBLE, + M68K_OP_FP_EXTENDED, M68K_OP_FP_SINGLE, M68K_OP_MEM, M68K_OP_SHIFT, @@ -1146,6 +1147,26 @@ def test_expected_m68k(actual: CsInsn, expected: dict) -> bool: elif aop.type == M68K_OP_FP_DOUBLE: if not compare_dp(aop.dimm, eop.get("dimm"), "dimm"): return False + elif aop.type == M68K_OP_FP_EXTENDED: + fp_extended = eop.get("fp_extended", {}) + if not compare_uint64( + aop.fp_extended.significand, + fp_extended.get("significand"), + "fp_extended.significand", + ): + return False + if not compare_uint16( + aop.fp_extended.sign_exp, + fp_extended.get("sign_exp"), + "fp_extended.sign_exp", + ): + return False + if not compare_uint16( + aop.fp_extended.reserved, + fp_extended.get("reserved"), + "fp_extended.reserved", + ): + return False elif aop.type == M68K_OP_FP_SINGLE: if not compare_fp(aop.simm, eop.get("simm"), "simm"): return False diff --git a/cstool/cstool_m68k.c b/cstool/cstool_m68k.c index 49588ce661..1e6adc8f32 100644 --- a/cstool/cstool_m68k.c +++ b/cstool/cstool_m68k.c @@ -157,6 +157,18 @@ void print_insn_detail_m68k(csh handle, cs_insn *ins) printf("\t\toperands[%u].type: FP_DOUBLE\n", i); printf("\t\t\toperands[%u].dimm: %lf\n", i, op->dimm); break; + case M68K_OP_FP_EXTENDED: + printf("\t\toperands[%u].type: FP_EXTENDED\n", i); + printf("\t\t\toperands[%u].fp_extended.sign_exp: " + "0x%04" PRIx16 "\n", + i, op->fp_extended.sign_exp); + printf("\t\t\toperands[%u].fp_extended.reserved: " + "0x%04" PRIx16 "\n", + i, op->fp_extended.reserved); + printf("\t\t\toperands[%u].fp_extended.significand: " + "0x%016" PRIx64 "\n", + i, op->fp_extended.significand); + break; case M68K_OP_SHIFT: printf("\t\toperands[%u].type: SHIFT\n", i); break; diff --git a/include/capstone/m68k.h b/include/capstone/m68k.h index a510a82ff2..7ac3af4a8d 100644 --- a/include/capstone/m68k.h +++ b/include/capstone/m68k.h @@ -138,6 +138,8 @@ typedef enum m68k_op_type { /// Shift-direction pseudo operand. /// Shift direction is set in cs_m68k_op.flags M68K_OP_SHIFT = CS_OP_SPECIAL + 5, + /// Extended precision Floating-Point operand. + M68K_OP_FP_EXTENDED = CS_OP_SPECIAL + 6, M68K_OP_MEM = CS_OP_MEM, ///< = CS_OP_MEM (Memory operand). } m68k_op_type; @@ -199,12 +201,24 @@ typedef struct cs_m68k_op_reg_pair { m68k_reg reg_1; } cs_m68k_op_reg_pair; +/// Motorola extended-precision real in its 96-bit external representation. +/// The fields are host-endian components, not a packed 12-byte buffer. +typedef struct m68k_op_fp_extended { + /// Explicit integer bit followed by the 63-bit fractional part. + uint64_t significand; + /// Sign in bit 15 and the 15-bit biased exponent in bits 14-0. + uint16_t sign_exp; + /// Raw reserved word from the external representation. + uint16_t reserved; +} m68k_op_fp_extended; + /// Instruction operand typedef struct cs_m68k_op { union { uint64_t imm; ///< immediate value for IMM operand double dimm; ///< double imm float simm; ///< float imm + m68k_op_fp_extended fp_extended; ///< extended-precision immediate m68k_reg reg; ///< register value for REG operand cs_m68k_op_reg_pair reg_pair; ///< register pair in one operand }; diff --git a/suite/cstest/include/test_detail_m68k.h b/suite/cstest/include/test_detail_m68k.h index 1f6640c964..fa09e33c83 100644 --- a/suite/cstest/include/test_detail_m68k.h +++ b/suite/cstest/include/test_detail_m68k.h @@ -65,6 +65,23 @@ static const cyaml_schema_field_t test_detail_m68k_op_mem_mapping_schema[] = { CYAML_FIELD_END }; +typedef struct { + uint64_t significand; + uint16_t sign_exp; + uint16_t reserved; +} TestDetailM68KOpFpExtended; + +static const cyaml_schema_field_t + test_detail_m68k_op_fp_extended_mapping_schema[] = { + CYAML_FIELD_UINT("significand", CYAML_FLAG_OPTIONAL, + TestDetailM68KOpFpExtended, significand), + CYAML_FIELD_UINT("sign_exp", CYAML_FLAG_OPTIONAL, + TestDetailM68KOpFpExtended, sign_exp), + CYAML_FIELD_UINT("reserved", CYAML_FLAG_OPTIONAL, + TestDetailM68KOpFpExtended, reserved), + CYAML_FIELD_END + }; + typedef struct { char *type; char *address_mode; @@ -82,6 +99,7 @@ typedef struct { double dimm; float simm; + TestDetailM68KOpFpExtended *fp_extended; TestDetailM68KOpMem *mem; char **flags; size_t flags_count; @@ -114,6 +132,9 @@ static const cyaml_schema_field_t test_detail_m68k_op_mapping_schema[] = { register_bits), CYAML_FIELD_FLOAT("dimm", CYAML_FLAG_OPTIONAL, TestDetailM68KOp, dimm), CYAML_FIELD_FLOAT("simm", CYAML_FLAG_OPTIONAL, TestDetailM68KOp, simm), + CYAML_FIELD_MAPPING_PTR("fp_extended", CYAML_FLAG_OPTIONAL, + TestDetailM68KOp, fp_extended, + test_detail_m68k_op_fp_extended_mapping_schema), CYAML_FIELD_MAPPING_PTR("mem", CYAML_FLAG_OPTIONAL, TestDetailM68KOp, mem, test_detail_m68k_op_mem_mapping_schema), CYAML_FIELD_SEQUENCE("flags", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, diff --git a/suite/cstest/include/test_mapping.h b/suite/cstest/include/test_mapping.h index 6664515260..077c34cc1d 100644 --- a/suite/cstest/include/test_mapping.h +++ b/suite/cstest/include/test_mapping.h @@ -948,6 +948,7 @@ static const cs_enum_id_map cs_enum_map[] = { { .str = "M68K_OP_FLAG_SHIFT_LEFT", .val = M68K_OP_FLAG_SHIFT_LEFT }, { .str = "M68K_OP_FLAG_SHIFT_RIGHT", .val = M68K_OP_FLAG_SHIFT_RIGHT }, { .str = "M68K_OP_FP_DOUBLE", .val = M68K_OP_FP_DOUBLE }, + { .str = "M68K_OP_FP_EXTENDED", .val = M68K_OP_FP_EXTENDED }, { .str = "M68K_OP_FP_SINGLE", .val = M68K_OP_FP_SINGLE }, { .str = "M68K_OP_IMM", .val = M68K_OP_IMM }, { .str = "M68K_OP_MEM", .val = M68K_OP_MEM }, diff --git a/suite/cstest/src/test_detail_m68k.c b/suite/cstest/src/test_detail_m68k.c index 79bad50003..c480d65fdc 100644 --- a/suite/cstest/src/test_detail_m68k.c +++ b/suite/cstest/src/test_detail_m68k.c @@ -113,6 +113,11 @@ TestDetailM68KOp *test_detail_m68k_op_clone(TestDetailM68KOp *op) clone->br_disp = op->br_disp; clone->br_disp_size = op->br_disp_size; clone->register_bits = op->register_bits; + if (op->fp_extended) { + clone->fp_extended = + cs_mem_calloc(sizeof(TestDetailM68KOpFpExtended), 1); + *clone->fp_extended = *op->fp_extended; + } clone->flags_count = op->flags_count; if (op->flags_count > 0) clone->flags = cs_mem_calloc(sizeof(char *), op->flags_count); @@ -136,6 +141,7 @@ void test_detail_m68k_op_free(TestDetailM68KOp *op) for (size_t i = 0; i < op->flags_count; ++i) cs_mem_free(op->flags[i]); cs_mem_free(op->flags); + cs_mem_free(op->fp_extended); test_detail_m68k_op_mem_free(op->mem); cs_mem_free(op); } @@ -182,6 +188,17 @@ bool test_expected_m68k(csh *handle, cs_m68k *actual, TestDetailM68K *expected) case M68K_OP_FP_DOUBLE: compare_fp_ret(op->dimm, eop->dimm, false); break; + case M68K_OP_FP_EXTENDED: + if (!eop->fp_extended) + return false; + compare_uint64_ret(op->fp_extended.significand, + eop->fp_extended->significand, + false); + compare_uint16_ret(op->fp_extended.sign_exp, + eop->fp_extended->sign_exp, false); + compare_uint16_ret(op->fp_extended.reserved, + eop->fp_extended->reserved, false); + break; case M68K_OP_REG_BITS: compare_uint32_ret(op->register_bits, eop->register_bits, false); diff --git a/tests/details/m68k.yaml b/tests/details/m68k.yaml index 861eb1c471..648c8765d1 100644 --- a/tests/details/m68k.yaml +++ b/tests/details/m68k.yaml @@ -3770,6 +3770,505 @@ test_cases: reg: fp1 regs_write: [fp1] regs_impl_write: [fp1] + # Motorola 96-bit extended-precision immediate (fmove.x): + # canonical +1.0 is 3fff 0000 8000 0000 0000 0000. + # The fmove.x must consume all 16 bytes so the two nops follow directly. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x3f, + 0xff, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x4e, + 0x71, + 0x4e, + 0x71, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e1, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x3fff + reserved: 0x0000 + significand: 0x8000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + - asm_text: "nop" + - asm_text: "nop" + # A complete extended immediate needs 12 operand bytes after the opcode and + # command word. Reject truncated input instead of decoding safe-read values. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x3f, + 0xff, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: [] + # Signed value with nonzero exponent and fraction: -2.5. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0xc0, + 0x00, + 0x00, + 0x00, + 0xa0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e-2.5, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0xc000 + reserved: 0x0000 + significand: 0xa000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Preserve significand bits that cannot be represented by binary64. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x3f, + 0xff, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0c, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e1, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x3fff + reserved: 0x0000 + significand: 0x8000000000000c00 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # The original request encoding put 0x8000 in the reserved word + # (3fff 8000 0000 0000 0000 0000): the significand is all zero, so the + # value must decode to 0.0 while still consuming the full 16 bytes. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x3f, + 0xff, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x4e, + 0x71, + 0x4e, + 0x71, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e0, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x3fff + reserved: 0x8000 + significand: 0x0000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + - asm_text: "nop" + - asm_text: "nop" + # Preserve a second low-bit significand pattern without rounding it. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x3f, + 0xff, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e1, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x3fff + reserved: 0x0000 + significand: 0x8000000000000400 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Finite extended value outside the binary64 exponent range remains exact + # in detail even though the assembly-text approximation overflows. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x7f, + 0xfe, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0einf, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x7ffe + reserved: 0x0000 + significand: 0x8000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Preserve the sign of an extended negative zero. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e-0, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x8000 + reserved: 0x0000 + significand: 0x0000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Special value: E=0x7fff with a zero 63-bit fraction encodes +infinity. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x7f, + 0xff, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0einf, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x7fff + reserved: 0x0000 + significand: 0x8000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Special value: E=0x7fff with a nonzero fraction becomes a quiet NaN. + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x7f, + 0xff, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0enan, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x7fff + reserved: 0x0000 + significand: 0x8000000000000001 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Subnormal path: E=0x3bcd with significand 0x8000000000000000 is the + # smallest binary64 subnormal (2^-1074, bits 0x0000000000000001). + - input: + bytes: + [ + 0xf2, + 0x3c, + 0x48, + 0x00, + 0x3b, + 0xcd, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x #0e4.94066e-324, fp0" + size: 16 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_FP_EXTENDED + address_mode: M68K_AM_IMMEDIATE + fp_extended: + sign_exp: 0x3bcd + reserved: 0x0000 + significand: 0x8000000000000000 + - type: M68K_OP_REG + reg: fp0 + regs_write: [fp0] + regs_impl_write: [fp0] + # Extended source with a memory effective address: no inline 12-byte + # immediate is consumed; the operand stays a memory reference. + - input: + bytes: [0xf2, 0x10, 0x48, 0x00] + arch: "m68k" + options: [CS_OPT_DETAIL, CS_MODE_M68K_040] + address: 0x0 + expected: + insns: + - asm_text: "fmove.x (a0), fp0" + size: 4 + details: + m68k: + op_size_type: M68K_SIZE_TYPE_FPU + op_size_fpu: M68K_FPU_SIZE_EXTENDED + operands: + - type: M68K_OP_MEM + mem: + base_reg: a0 + address_mode: M68K_AM_REGI_ADDR + - type: M68K_OP_REG + reg: fp0 + regs_read: [a0] + regs_write: [fp0] + regs_impl_read: [a0] + regs_impl_write: [fp0] - input: bytes: [0xf2, 0x10, 0x50, 0x00] arch: "m68k"