+// XXX: stupid mess
+#define emith_mul_(op, dlo, dhi, s1, s2) { \
+ int rmr; \
+ if (dlo != xAX && dhi != xAX) \
+ emith_push(xAX); \
+ if (dlo != xDX && dhi != xDX) \
+ emith_push(xDX); \
+ if ((s1) == xAX) \
+ rmr = s2; \
+ else if ((s2) == xAX) \
+ rmr = s1; \
+ else { \
+ emith_move_r_r(xAX, s1); \
+ rmr = s2; \
+ } \
+ EMIT_OP_MODRM(0xf7, 3, op, rmr); /* xMUL rmr */ \
+ /* XXX: using push/pop for the case of edx->eax; eax->edx */ \
+ if (dhi != xDX && dhi != -1) \
+ emith_push(xDX); \
+ if (dlo != xAX) \
+ emith_move_r_r(dlo, xAX); \
+ if (dhi != xDX && dhi != -1) \
+ emith_pop(dhi); \
+ if (dlo != xDX && dhi != xDX) \
+ emith_pop(xDX); \
+ if (dlo != xAX && dhi != xAX) \
+ emith_pop(xAX); \
+}
+
+#define emith_mul_u64(dlo, dhi, s1, s2) \
+ emith_mul_(4, dlo, dhi, s1, s2) /* MUL */
+
+#define emith_mul_s64(dlo, dhi, s1, s2) \
+ emith_mul_(5, dlo, dhi, s1, s2) /* IMUL */
+
+#define emith_mul(d, s1, s2) \
+ emith_mul_(4, d, -1, s1, s2)
+
+// (dlo,dhi) += signed(s1) * signed(s2)
+#define emith_mula_s64(dlo, dhi, s1, s2) { \
+ emith_push(dhi); \
+ emith_push(dlo); \
+ emith_mul_(5, dlo, dhi, s1, s2); \
+ EMIT_OP_MODRM(0x03, 0, dlo, 4); \
+ EMIT_SIB(0, 4, 4); /* add dlo, [esp] */ \
+ EMIT_OP_MODRM(0x13, 1, dhi, 4); \
+ EMIT_SIB(0, 4, 4); \
+ EMIT(4, u8); /* adc dhi, [esp+4] */ \
+ emith_add_r_imm(xSP, 4*2); \
+}
+
+// "flag" instructions are the same
+#define emith_subf_r_imm emith_sub_r_imm
+#define emith_addf_r_r emith_add_r_r
+#define emith_subf_r_r emith_sub_r_r
+#define emith_adcf_r_r emith_adc_r_r
+#define emith_sbcf_r_r emith_sbc_r_r
+#define emith_eorf_r_r emith_eor_r_r
+#define emith_negcf_r_r emith_negc_r_r
+
+#define emith_lslf emith_lsl
+#define emith_lsrf emith_lsr
+#define emith_asrf emith_asr
+#define emith_rolf emith_rol
+#define emith_rorf emith_ror
+#define emith_rolcf emith_rolc
+#define emith_rorcf emith_rorc
+
+#define emith_deref_op(op, r, rs, offs) do { \
+ /* mov r <-> [ebp+#offs] */ \
+ if ((offs) >= 0x80) { \
+ EMIT_OP_MODRM(op, 2, r, rs); \
+ EMIT(offs, u32); \
+ } else { \
+ EMIT_OP_MODRM(op, 1, r, rs); \
+ EMIT(offs, u8); \
+ } \
+} while (0)
+
+#define is_abcdx(r) (xAX <= (r) && (r) <= xDX)
+
+#define emith_read_r_r_offs(r, rs, offs) \
+ emith_deref_op(0x8b, r, rs, offs)
+
+#define emith_write_r_r_offs(r, rs, offs) \
+ emith_deref_op(0x89, r, rs, offs)
+
+// note: don't use prefixes on this
+#define emith_read8_r_r_offs(r, rs, offs) do { \
+ int r_ = r; \
+ if (!is_abcdx(r)) \
+ r_ = rcache_get_tmp(); \
+ emith_deref_op(0x8a, r_, rs, offs); \
+ if ((r) != r_) { \
+ emith_move_r_r(r, r_); \
+ rcache_free_tmp(r_); \
+ } \
+} while (0)
+
+#define emith_write8_r_r_offs(r, rs, offs) do {\
+ int r_ = r; \
+ if (!is_abcdx(r)) { \
+ r_ = rcache_get_tmp(); \
+ emith_move_r_r(r_, r); \
+ } \
+ emith_deref_op(0x88, r_, rs, offs); \
+ if ((r) != r_) \
+ rcache_free_tmp(r_); \
+} while (0)
+
+#define emith_read16_r_r_offs(r, rs, offs) { \
+ EMIT(0x66, u8); /* operand override */ \
+ emith_read_r_r_offs(r, rs, offs); \
+}
+
+#define emith_write16_r_r_offs(r, rs, offs) { \
+ EMIT(0x66, u8); \
+ emith_write_r_r_offs(r, rs, offs); \
+}
+
+#define emith_ctx_read(r, offs) \
+ emith_read_r_r_offs(r, CONTEXT_REG, offs)
+
+#define emith_ctx_write(r, offs) \
+ emith_write_r_r_offs(r, CONTEXT_REG, offs)
+
+#define emith_ctx_read_multiple(r, offs, cnt, tmpr) do { \
+ int r_ = r, offs_ = offs, cnt_ = cnt; \
+ for (; cnt_ > 0; r_++, offs_ += 4, cnt_--) \
+ emith_ctx_read(r_, offs_); \
+} while (0)
+
+#define emith_ctx_write_multiple(r, offs, cnt, tmpr) do { \
+ int r_ = r, offs_ = offs, cnt_ = cnt; \
+ for (; cnt_ > 0; r_++, offs_ += 4, cnt_--) \
+ emith_ctx_write(r_, offs_); \
+} while (0)
+
+// assumes EBX is free
+#define emith_ret_to_ctx(offs) { \
+ emith_pop(xBX); \
+ emith_ctx_write(xBX, offs); \