+/* Assuming at last one of ARG1 or ARG2 is a fixed point value,
+ perform the binary operation OP on these two operands, and return
+ the resulting value (also as a fixed point). */
+
+static struct value *
+fixed_point_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
+{
+ struct type *type1 = check_typedef (value_type (arg1));
+ struct type *type2 = check_typedef (value_type (arg2));
+ const struct language_defn *language = current_language;
+
+ struct gdbarch *gdbarch = type1->arch ();
+ struct value *val;
+
+ gdb_mpq v1, v2, res;
+
+ gdb_assert (is_fixed_point_type (type1) || is_fixed_point_type (type2));
+ if (op == BINOP_MUL || op == BINOP_DIV)
+ {
+ v1 = value_to_gdb_mpq (arg1);
+ v2 = value_to_gdb_mpq (arg2);
+
+ /* The code below uses TYPE1 for the result type, so make sure
+ it is set properly. */
+ if (!is_fixed_point_type (type1))
+ type1 = type2;
+ }
+ else
+ {
+ if (!is_fixed_point_type (type1))
+ {
+ arg1 = value_cast (type2, arg1);
+ type1 = type2;
+ }
+ if (!is_fixed_point_type (type2))
+ {
+ arg2 = value_cast (type1, arg2);
+ type2 = type1;
+ }
+
+ v1.read_fixed_point (gdb::make_array_view (value_contents (arg1).data (),
+ TYPE_LENGTH (type1)),
+ type_byte_order (type1), type1->is_unsigned (),
+ type1->fixed_point_scaling_factor ());
+ v2.read_fixed_point (gdb::make_array_view (value_contents (arg2).data (),
+ TYPE_LENGTH (type2)),
+ type_byte_order (type2), type2->is_unsigned (),
+ type2->fixed_point_scaling_factor ());
+ }
+
+ auto fixed_point_to_value = [type1] (const gdb_mpq &fp)
+ {
+ value *fp_val = allocate_value (type1);
+
+ fp.write_fixed_point
+ (gdb::make_array_view (value_contents_raw (fp_val).data (),
+ TYPE_LENGTH (type1)),
+ type_byte_order (type1),
+ type1->is_unsigned (),
+ type1->fixed_point_scaling_factor ());
+
+ return fp_val;
+ };
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ mpq_add (res.val, v1.val, v2.val);
+ val = fixed_point_to_value (res);
+ break;
+
+ case BINOP_SUB:
+ mpq_sub (res.val, v1.val, v2.val);
+ val = fixed_point_to_value (res);
+ break;
+
+ case BINOP_MIN:
+ val = fixed_point_to_value (mpq_cmp (v1.val, v2.val) < 0 ? v1 : v2);
+ break;
+
+ case BINOP_MAX:
+ val = fixed_point_to_value (mpq_cmp (v1.val, v2.val) > 0 ? v1 : v2);
+ break;
+
+ case BINOP_MUL:
+ mpq_mul (res.val, v1.val, v2.val);
+ val = fixed_point_to_value (res);
+ break;
+
+ case BINOP_DIV:
+ if (mpq_sgn (v2.val) == 0)
+ error (_("Division by zero"));
+ mpq_div (res.val, v1.val, v2.val);
+ val = fixed_point_to_value (res);
+ break;
+
+ case BINOP_EQUAL:
+ val = value_from_ulongest (language_bool_type (language, gdbarch),
+ mpq_cmp (v1.val, v2.val) == 0 ? 1 : 0);
+ break;
+
+ case BINOP_LESS:
+ val = value_from_ulongest (language_bool_type (language, gdbarch),
+ mpq_cmp (v1.val, v2.val) < 0 ? 1 : 0);
+ break;
+
+ default:
+ error (_("Integer-only operation on fixed point number."));
+ }
+
+ return val;
+}
+