diff options
Diffstat (limited to 'target-m68k/op.c')
-rw-r--r-- | target-m68k/op.c | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/target-m68k/op.c b/target-m68k/op.c new file mode 100644 index 0000000000..489eafeef6 --- /dev/null +++ b/target-m68k/op.c @@ -0,0 +1,678 @@ +/* + * m68k micro operations + * + * Copyright (c) 2006 CodeSourcery + * Written by Paul Brook + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "exec.h" +#include "m68k-qreg.h" + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +static long qreg_offsets[] = { +#define DEFO32(name, offset) offsetof(CPUState, offset), +#define DEFR(name, reg, mode) -1, +#define DEFF64(name, offset) offsetof(CPUState, offset), + 0, +#include "qregs.def" +}; + +#define CPU_FP_STATUS env->fp_status + +#define RAISE_EXCEPTION(n) do { \ + env->exception_index = n; \ + cpu_loop_exit(); \ + } while(0) + +#define get_op helper_get_op +#define set_op helper_set_op +#define get_opf64 helper_get_opf64 +#define set_opf64 helper_set_opf64 +uint32_t +get_op(int qreg) +{ + if (qreg == QREG_T0) { + return T0; + } else if (qreg < TARGET_NUM_QREGS) { + return *(uint32_t *)(((long)env) + qreg_offsets[qreg]); + } else { + return env->qregs[qreg - TARGET_NUM_QREGS]; + } +} + +void set_op(int qreg, uint32_t val) +{ + if (qreg == QREG_T0) { + T0 = val; + } else if (qreg < TARGET_NUM_QREGS) { + *(uint32_t *)(((long)env) + qreg_offsets[qreg]) = val; + } else { + env->qregs[qreg - TARGET_NUM_QREGS] = val; + } +} + +float64 get_opf64(int qreg) +{ + if (qreg < TARGET_NUM_QREGS) { + return *(float64 *)(((long)env) + qreg_offsets[qreg]); + } else { + return *(float64 *)&env->qregs[qreg - TARGET_NUM_QREGS]; + } +} + +void set_opf64(int qreg, float64 val) +{ + if (qreg < TARGET_NUM_QREGS) { + *(float64 *)(((long)env) + qreg_offsets[qreg]) = val; + } else { + *(float64 *)&env->qregs[qreg - TARGET_NUM_QREGS] = val; + } +} + +#define OP(name) void OPPROTO op_##name (void) + +OP(mov32) +{ + set_op(PARAM1, get_op(PARAM2)); + FORCE_RET(); +} + +OP(mov32_im) +{ + set_op(PARAM1, PARAM2); + FORCE_RET(); +} + +OP(movf64) +{ + set_opf64(PARAM1, get_opf64(PARAM2)); + FORCE_RET(); +} + +OP(zerof64) +{ + set_opf64(PARAM1, 0); + FORCE_RET(); +} + +OP(add32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + set_op(PARAM1, op2 + op3); + FORCE_RET(); +} + +OP(sub32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + set_op(PARAM1, op2 - op3); + FORCE_RET(); +} + +OP(mul32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + set_op(PARAM1, op2 * op3); + FORCE_RET(); +} + +OP(not32) +{ + uint32_t arg = get_op(PARAM2); + set_op(PARAM1, ~arg); + FORCE_RET(); +} + +OP(neg32) +{ + uint32_t arg = get_op(PARAM2); + set_op(PARAM1, -arg); + FORCE_RET(); +} + +OP(bswap32) +{ + uint32_t arg = get_op(PARAM2); + arg = (arg >> 24) | (arg << 24) + | ((arg >> 16) & 0xff00) | ((arg << 16) & 0xff0000); + set_op(PARAM1, arg); + FORCE_RET(); +} + +OP(btest) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + if (op1 & op2) + env->cc_dest &= ~CCF_Z; + else + env->cc_dest |= CCF_Z; + FORCE_RET(); +} + +OP(addx_cc) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + uint32_t res; + if (env->cc_x) { + env->cc_x = (op1 <= op2); + env->cc_op = CC_OP_SUBX; + res = op1 - (op2 + 1); + } else { + env->cc_x = (op1 < op2); + env->cc_op = CC_OP_SUB; + res = op1 - op2; + } + set_op(PARAM1, res); + FORCE_RET(); +} + +OP(subx_cc) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + uint32_t res; + if (env->cc_x) { + res = op1 + op2 + 1; + env->cc_x = (res <= op2); + env->cc_op = CC_OP_ADDX; + } else { + res = op1 + op2; + env->cc_x = (res < op2); + env->cc_op = CC_OP_ADD; + } + set_op(PARAM1, res); + FORCE_RET(); +} + +/* Logic ops. */ + +OP(and32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + set_op(PARAM1, op2 & op3); + FORCE_RET(); +} + +OP(or32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + set_op(PARAM1, op2 | op3); + FORCE_RET(); +} + +OP(xor32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + set_op(PARAM1, op2 ^ op3); + FORCE_RET(); +} + +/* Shifts. */ +OP(shl32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + uint32_t result; + result = op2 << op3; + set_op(PARAM1, result); + FORCE_RET(); +} + +OP(shl_cc) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + uint32_t result; + result = op1 << op2; + set_op(PARAM1, result); + env->cc_x = (op1 << (op2 - 1)) & 1; + FORCE_RET(); +} + +OP(shr32) +{ + uint32_t op2 = get_op(PARAM2); + uint32_t op3 = get_op(PARAM3); + uint32_t result; + result = op2 >> op3; + set_op(PARAM1, result); + FORCE_RET(); +} + +OP(shr_cc) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + uint32_t result; + result = op1 >> op2; + set_op(PARAM1, result); + env->cc_x = (op1 >> (op2 - 1)) & 1; + FORCE_RET(); +} + +OP(sar_cc) +{ + int32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + uint32_t result; + result = op1 >> op2; + set_op(PARAM1, result); + env->cc_x = (op1 >> (op2 - 1)) & 1; + FORCE_RET(); +} + +/* Value extend. */ + +OP(ext8u32) +{ + uint32_t op2 = get_op(PARAM2); + set_op(PARAM1, (uint8_t)op2); + FORCE_RET(); +} + +OP(ext8s32) +{ + uint32_t op2 = get_op(PARAM2); + set_op(PARAM1, (int8_t)op2); + FORCE_RET(); +} + +OP(ext16u32) +{ + uint32_t op2 = get_op(PARAM2); + set_op(PARAM1, (uint16_t)op2); + FORCE_RET(); +} + +OP(ext16s32) +{ + uint32_t op2 = get_op(PARAM2); + set_op(PARAM1, (int16_t)op2); + FORCE_RET(); +} + +/* Load/store ops. */ +OP(ld8u32) +{ + uint32_t addr = get_op(PARAM2); + set_op(PARAM1, ldub(addr)); + FORCE_RET(); +} + +OP(ld8s32) +{ + uint32_t addr = get_op(PARAM2); + set_op(PARAM1, ldsb(addr)); + FORCE_RET(); +} + +OP(ld16u32) +{ + uint32_t addr = get_op(PARAM2); + set_op(PARAM1, lduw(addr)); + FORCE_RET(); +} + +OP(ld16s32) +{ + uint32_t addr = get_op(PARAM2); + set_op(PARAM1, ldsw(addr)); + FORCE_RET(); +} + +OP(ld32) +{ + uint32_t addr = get_op(PARAM2); + set_op(PARAM1, ldl(addr)); + FORCE_RET(); +} + +OP(st8) +{ + uint32_t addr = get_op(PARAM1); + stb(addr, get_op(PARAM2)); + FORCE_RET(); +} + +OP(st16) +{ + uint32_t addr = get_op(PARAM1); + stw(addr, get_op(PARAM2)); + FORCE_RET(); +} + +OP(st32) +{ + uint32_t addr = get_op(PARAM1); + stl(addr, get_op(PARAM2)); + FORCE_RET(); +} + +OP(ldf64) +{ + uint32_t addr = get_op(PARAM2); + set_opf64(PARAM1, ldfq(addr)); + FORCE_RET(); +} + +OP(stf64) +{ + uint32_t addr = get_op(PARAM1); + stfq(addr, get_opf64(PARAM2)); + FORCE_RET(); +} + +OP(flush_flags) +{ + int cc_op = PARAM1; + if (cc_op == CC_OP_DYNAMIC) + cc_op = env->cc_op; + cpu_m68k_flush_flags(env, cc_op); + FORCE_RET(); +} + +OP(divu) +{ + uint32_t num; + uint32_t den; + uint32_t quot; + uint32_t rem; + uint32_t flags; + + num = env->div1; + den = env->div2; + /* ??? This needs to make sure the throwing location is accurate. */ + if (den == 0) + RAISE_EXCEPTION(EXCP_DIV0); + quot = num / den; + rem = num % den; + flags = 0; + if (PARAM1 && quot > 0xffff) + flags |= CCF_V; + if (quot == 0) + flags |= CCF_Z; + else if ((int32_t)quot < 0) + flags |= CCF_N; + env->div1 = quot; + env->div2 = rem; + env->cc_dest = flags; + FORCE_RET(); +} + +OP(divs) +{ + int32_t num; + int32_t den; + int32_t quot; + int32_t rem; + int32_t flags; + + num = env->div1; + den = env->div2; + if (den == 0) + RAISE_EXCEPTION(EXCP_DIV0); + quot = num / den; + rem = num % den; + flags = 0; + if (PARAM1 && quot != (int16_t)quot) + flags |= CCF_V; + if (quot == 0) + flags |= CCF_Z; + else if (quot < 0) + flags |= CCF_N; + env->div1 = quot; + env->div2 = rem; + env->cc_dest = flags; + FORCE_RET(); +} + +OP(raise_exception) +{ + RAISE_EXCEPTION(PARAM1); + FORCE_RET(); +} + +/* Floating point comparison sets flags differently to other instructions. */ + +OP(sub_cmpf64) +{ + float64 src0; + float64 src1; + src0 = get_opf64(PARAM2); + src1 = get_opf64(PARAM3); + set_opf64(PARAM1, helper_sub_cmpf64(env, src0, src1)); + FORCE_RET(); +} + +OP(update_xflag_tst) +{ + uint32_t op1 = get_op(PARAM1); + env->cc_x = op1; + FORCE_RET(); +} + +OP(update_xflag_lt) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + env->cc_x = (op1 < op2); + FORCE_RET(); +} + +OP(get_xflag) +{ + set_op(PARAM1, env->cc_x); + FORCE_RET(); +} + +OP(logic_cc) +{ + uint32_t op1 = get_op(PARAM1); + env->cc_dest = op1; + FORCE_RET(); +} + +OP(update_cc_add) +{ + uint32_t op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + env->cc_dest = op1; + env->cc_src = op2; + FORCE_RET(); +} + +OP(fp_result) +{ + env->fp_result = get_opf64(PARAM1); + FORCE_RET(); +} + +OP(jmp) +{ + GOTO_LABEL_PARAM(1); +} + +/* These ops involve a function call, which probably requires a stack frame + and breaks things on some hosts. */ +OP(jmp_z32) +{ + uint32_t arg = get_op(PARAM1); + if (arg == 0) + GOTO_LABEL_PARAM(2); + FORCE_RET(); +} + +OP(jmp_nz32) +{ + uint32_t arg = get_op(PARAM1); + if (arg != 0) + GOTO_LABEL_PARAM(2); + FORCE_RET(); +} + +OP(jmp_s32) +{ + int32_t arg = get_op(PARAM1); + if (arg < 0) + GOTO_LABEL_PARAM(2); + FORCE_RET(); +} + +OP(jmp_ns32) +{ + int32_t arg = get_op(PARAM1); + if (arg >= 0) + GOTO_LABEL_PARAM(2); + FORCE_RET(); +} + +void OPPROTO op_goto_tb0(void) +{ + GOTO_TB(op_goto_tb0, PARAM1, 0); +} + +void OPPROTO op_goto_tb1(void) +{ + GOTO_TB(op_goto_tb1, PARAM1, 1); +} + +OP(exit_tb) +{ + EXIT_TB(); +} + + +/* Floating point. */ +OP(f64_to_i32) +{ + set_op(PARAM1, float64_to_int32(get_opf64(PARAM2), &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(f64_to_f32) +{ + union { + float32 f; + uint32_t i; + } u; + u.f = float64_to_float32(get_opf64(PARAM2), &CPU_FP_STATUS); + set_op(PARAM1, u.i); + FORCE_RET(); +} + +OP(i32_to_f64) +{ + set_opf64(PARAM1, int32_to_float64(get_op(PARAM2), &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(f32_to_f64) +{ + union { + float32 f; + uint32_t i; + } u; + u.i = get_op(PARAM2); + set_opf64(PARAM1, float32_to_float64(u.f, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(absf64) +{ + float64 op0 = get_opf64(PARAM2); + set_opf64(PARAM1, float64_abs(op0)); + FORCE_RET(); +} + +OP(chsf64) +{ + float64 op0 = get_opf64(PARAM2); + set_opf64(PARAM1, float64_chs(op0)); + FORCE_RET(); +} + +OP(sqrtf64) +{ + float64 op0 = get_opf64(PARAM2); + set_opf64(PARAM1, float64_sqrt(op0, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(addf64) +{ + float64 op0 = get_opf64(PARAM2); + float64 op1 = get_opf64(PARAM3); + set_opf64(PARAM1, float64_add(op0, op1, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(subf64) +{ + float64 op0 = get_opf64(PARAM2); + float64 op1 = get_opf64(PARAM3); + set_opf64(PARAM1, float64_sub(op0, op1, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(mulf64) +{ + float64 op0 = get_opf64(PARAM2); + float64 op1 = get_opf64(PARAM3); + set_opf64(PARAM1, float64_mul(op0, op1, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(divf64) +{ + float64 op0 = get_opf64(PARAM2); + float64 op1 = get_opf64(PARAM3); + set_opf64(PARAM1, float64_div(op0, op1, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(iround_f64) +{ + float64 op0 = get_opf64(PARAM2); + set_opf64(PARAM1, float64_round_to_int(op0, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(itrunc_f64) +{ + float64 op0 = get_opf64(PARAM2); + set_opf64(PARAM1, float64_trunc_to_int(op0, &CPU_FP_STATUS)); + FORCE_RET(); +} + +OP(compare_quietf64) +{ + float64 op0 = get_opf64(PARAM2); + float64 op1 = get_opf64(PARAM3); + set_op(PARAM1, float64_compare_quiet(op0, op1, &CPU_FP_STATUS)); + FORCE_RET(); +} |