summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2017-02-04 00:41:15 -0800
committerMichael Pavone <pavone@retrodev.com>2017-02-04 00:41:15 -0800
commit9f0504f64332ef5dec262b72a735e6000d4ab457 (patch)
treed3852286cae318c5afb86851c08b4e8e89a18a4a
parent9a092d2574180542ba322480016f3f7a90d7ebdb (diff)
Cycle accurate MULU/MULS emulation
-rw-r--r--gen_x86.c12
-rw-r--r--gen_x86.h1
-rw-r--r--m68k_core_x86.c47
3 files changed, 58 insertions, 2 deletions
diff --git a/gen_x86.c b/gen_x86.c
index 60ad90a..a0cbc19 100644
--- a/gen_x86.c
+++ b/gen_x86.c
@@ -31,6 +31,7 @@
#define OP_POP 0x58
#define OP_MOVSXD 0x63
#define PRE_SIZE 0x66
+#define OP_IMUL 0x69
#define OP_JCC 0x70
#define OP_IMMED_ARITH 0x80
#define OP_TEST 0x84
@@ -530,7 +531,7 @@ void x86_ir(code_info *code, uint8_t opcode, uint8_t op_ex, uint8_t al_opcode, i
if (size == SZ_W) {
*(out++) = PRE_SIZE;
}
- if (dst == RAX && !sign_extend) {
+ if (dst == RAX && !sign_extend && al_opcode) {
if (size != SZ_B) {
al_opcode |= BIT_SIZE;
if (size == SZ_Q) {
@@ -1146,6 +1147,15 @@ void imul_rdispr(code_info *code, uint8_t src_base, int32_t disp, uint8_t dst, u
x86_rrdisp_sizedir(code, OP2_IMUL | (PRE_2BYTE << 8), dst, src_base, disp, size, 0);
}
+void imul_irr(code_info *code, int32_t val, uint8_t src, uint8_t dst, uint8_t size)
+{
+ if (size == SZ_B) {
+ fatal_error("imul immediate only supports 16-bit sizes and up");
+ }
+
+ x86_ir(code, OP_IMUL, dst, 0, val, src, size);
+}
+
void not_r(code_info *code, uint8_t dst, uint8_t size)
{
x86_r_size(code, OP_NOT_NEG, OP_EX_NOT, dst, size);
diff --git a/gen_x86.h b/gen_x86.h
index b836086..29de004 100644
--- a/gen_x86.h
+++ b/gen_x86.h
@@ -148,6 +148,7 @@ void sbb_rrdisp(code_info *code, uint8_t src, uint8_t dst_base, int32_t disp, ui
void sbb_rdispr(code_info *code, uint8_t src_base, int32_t disp, uint8_t dst, uint8_t size);
void cmp_rrdisp(code_info *code, uint8_t src, uint8_t dst_base, int32_t disp, uint8_t size);
void cmp_rdispr(code_info *code, uint8_t src_base, int32_t disp, uint8_t dst, uint8_t size);
+void imul_irr(code_info *code, int32_t val, uint8_t src, uint8_t dst, uint8_t size);
void imul_rr(code_info *code, uint8_t src, uint8_t dst, uint8_t size);
void imul_rrdisp(code_info *code, uint8_t src, uint8_t dst_base, int32_t disp, uint8_t size);
void imul_rdispr(code_info *code, uint8_t src_base, int32_t disp, uint8_t dst, uint8_t size);
diff --git a/m68k_core_x86.c b/m68k_core_x86.c
index 17e8daf..dda8238 100644
--- a/m68k_core_x86.c
+++ b/m68k_core_x86.c
@@ -1807,11 +1807,40 @@ void translate_m68k_exg(m68k_options *opts, m68kinst *inst, host_ea *src_op, hos
}
}
+
+
+static uint32_t mulu_cycles(uint16_t value)
+{
+ //4 for prefetch, 2-cycles per bit x 16, 2 for cleanup
+ uint32_t cycles = 38;
+ uint16_t a = (value & 0b1010101010101010) >> 1;
+ uint16_t b = value & 0b0101010101010101;
+ value = a + b;
+ a = (value & 0b1100110011001100) >> 2;
+ b = value & 0b0011001100110011;
+ value = a + b;
+ a = (value & 0b1111000011110000) >> 4;
+ b = value & 0b0000111100001111;
+ value = a + b;
+ a = (value & 0b1111111100000000) >> 8;
+ b = value & 0b0000000011111111;
+ value = a + b;
+ return cycles + 2*value;
+}
+
+static uint32_t muls_cycles(uint16_t value)
+{
+ //muls timing is essentially the same as muls, but it's based on the number of 0/1
+ //transitions rather than the number of 1 bits. xoring the value with itself shifted
+ //by one effectively sets one bit for every transition
+ return mulu_cycles((value << 1) ^ value);
+}
+
void translate_m68k_mul(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
code_info *code = &opts->gen.code;
- cycles(&opts->gen, 70); //TODO: Calculate the actual value based on the value of the <ea> parameter
if (src_op->mode == MODE_IMMED) {
+ cycles(&opts->gen, inst->op == M68K_MULU ? mulu_cycles(src_op->disp) : muls_cycles(src_op->disp));
mov_ir(code, inst->op == M68K_MULU ? (src_op->disp & 0xFFFF) : ((src_op->disp & 0x8000) ? src_op->disp | 0xFFFF0000 : src_op->disp), opts->gen.scratch1, SZ_D);
} else if (src_op->mode == MODE_REG_DIRECT) {
if (inst->op == M68K_MULS) {
@@ -1826,6 +1855,22 @@ void translate_m68k_mul(m68k_options *opts, m68kinst *inst, host_ea *src_op, hos
movzx_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_W, SZ_D);
}
}
+ if (src_op->mode != MODE_IMMED) {
+ //TODO: Inline cycle calculation so we don't need to save/restore a bunch of registers
+ //save context to memory and call the relevant C function for calculating the cycle count
+ call(code, opts->gen.save_context);
+ push_r(code, opts->gen.scratch1);
+ push_r(code, opts->gen.context_reg);
+ call_args(code, (code_ptr)(inst->op == M68K_MULS ? muls_cycles : mulu_cycles), 1, opts->gen.scratch1);
+ pop_r(code, opts->gen.context_reg);
+ //turn 68K cycles into master clock cycles and add to the current cycle count
+ imul_irr(code, opts->gen.clock_divider, RAX, RAX, SZ_D);
+ add_rrdisp(code, RAX, opts->gen.context_reg, offsetof(m68k_context, current_cycle), SZ_D);
+ //restore context and scratch1
+ call(code, opts->gen.load_context);
+ pop_r(code, opts->gen.scratch1);
+ }
+
uint8_t dst_reg;
if (dst_op->mode == MODE_REG_DIRECT) {
dst_reg = dst_op->base;