| /* |
| * MIPS emulation micro-operations for qemu. |
| * |
| * Copyright (c) 2004-2005 Jocelyn Mayer |
| * Copyright (c) 2006 Marius Groeger (FPU operations) |
| * Copyright (c) 2007 Thiemo Seufer (64-bit FPU support) |
| * |
| * 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 |
| * Lesser 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 "config.h" |
| #include "exec.h" |
| #include "host-utils.h" |
| |
| #ifndef CALL_FROM_TB0 |
| #define CALL_FROM_TB0(func) func() |
| #endif |
| #ifndef CALL_FROM_TB1 |
| #define CALL_FROM_TB1(func, arg0) func(arg0) |
| #endif |
| #ifndef CALL_FROM_TB1_CONST16 |
| #define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0) |
| #endif |
| #ifndef CALL_FROM_TB2 |
| #define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1) |
| #endif |
| #ifndef CALL_FROM_TB2_CONST16 |
| #define CALL_FROM_TB2_CONST16(func, arg0, arg1) \ |
| CALL_FROM_TB2(func, arg0, arg1) |
| #endif |
| #ifndef CALL_FROM_TB3 |
| #define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2) |
| #endif |
| #ifndef CALL_FROM_TB4 |
| #define CALL_FROM_TB4(func, arg0, arg1, arg2, arg3) \ |
| func(arg0, arg1, arg2, arg3) |
| #endif |
| |
| #define FREG 0 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 1 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 2 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 3 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 4 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 5 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 6 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 7 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 8 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 9 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 10 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 11 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 12 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 13 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 14 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 15 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 16 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 17 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 18 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 19 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 20 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 21 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 22 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 23 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 24 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 25 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 26 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 27 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 28 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 29 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 30 |
| #include "fop_template.c" |
| #undef FREG |
| #define FREG 31 |
| #include "fop_template.c" |
| #undef FREG |
| |
| /* Load and store */ |
| #define MEMSUFFIX _raw |
| #include "op_mem.c" |
| #undef MEMSUFFIX |
| #if !defined(CONFIG_USER_ONLY) |
| #define MEMSUFFIX _user |
| #include "op_mem.c" |
| #undef MEMSUFFIX |
| |
| #define MEMSUFFIX _super |
| #include "op_mem.c" |
| #undef MEMSUFFIX |
| |
| #define MEMSUFFIX _kernel |
| #include "op_mem.c" |
| #undef MEMSUFFIX |
| #endif |
| |
| /* 64 bits arithmetic */ |
| #if TARGET_LONG_BITS > HOST_LONG_BITS |
| void op_mult (void) |
| { |
| CALL_FROM_TB0(do_mult); |
| FORCE_RET(); |
| } |
| |
| void op_multu (void) |
| { |
| CALL_FROM_TB0(do_multu); |
| FORCE_RET(); |
| } |
| |
| void op_madd (void) |
| { |
| CALL_FROM_TB0(do_madd); |
| FORCE_RET(); |
| } |
| |
| void op_maddu (void) |
| { |
| CALL_FROM_TB0(do_maddu); |
| FORCE_RET(); |
| } |
| |
| void op_msub (void) |
| { |
| CALL_FROM_TB0(do_msub); |
| FORCE_RET(); |
| } |
| |
| void op_msubu (void) |
| { |
| CALL_FROM_TB0(do_msubu); |
| FORCE_RET(); |
| } |
| |
| /* Multiplication variants of the vr54xx. */ |
| void op_muls (void) |
| { |
| CALL_FROM_TB0(do_muls); |
| FORCE_RET(); |
| } |
| |
| void op_mulsu (void) |
| { |
| CALL_FROM_TB0(do_mulsu); |
| FORCE_RET(); |
| } |
| |
| void op_macc (void) |
| { |
| CALL_FROM_TB0(do_macc); |
| FORCE_RET(); |
| } |
| |
| void op_macchi (void) |
| { |
| CALL_FROM_TB0(do_macchi); |
| FORCE_RET(); |
| } |
| |
| void op_maccu (void) |
| { |
| CALL_FROM_TB0(do_maccu); |
| FORCE_RET(); |
| } |
| void op_macchiu (void) |
| { |
| CALL_FROM_TB0(do_macchiu); |
| FORCE_RET(); |
| } |
| |
| void op_msac (void) |
| { |
| CALL_FROM_TB0(do_msac); |
| FORCE_RET(); |
| } |
| |
| void op_msachi (void) |
| { |
| CALL_FROM_TB0(do_msachi); |
| FORCE_RET(); |
| } |
| |
| void op_msacu (void) |
| { |
| CALL_FROM_TB0(do_msacu); |
| FORCE_RET(); |
| } |
| |
| void op_msachiu (void) |
| { |
| CALL_FROM_TB0(do_msachiu); |
| FORCE_RET(); |
| } |
| |
| void op_mulhi (void) |
| { |
| CALL_FROM_TB0(do_mulhi); |
| FORCE_RET(); |
| } |
| |
| void op_mulhiu (void) |
| { |
| CALL_FROM_TB0(do_mulhiu); |
| FORCE_RET(); |
| } |
| |
| void op_mulshi (void) |
| { |
| CALL_FROM_TB0(do_mulshi); |
| FORCE_RET(); |
| } |
| |
| void op_mulshiu (void) |
| { |
| CALL_FROM_TB0(do_mulshiu); |
| FORCE_RET(); |
| } |
| |
| #else /* TARGET_LONG_BITS > HOST_LONG_BITS */ |
| |
| static always_inline uint64_t get_HILO (void) |
| { |
| return ((uint64_t)env->HI[env->current_tc][0] << 32) | |
| ((uint64_t)(uint32_t)env->LO[env->current_tc][0]); |
| } |
| |
| static always_inline void set_HILO (uint64_t HILO) |
| { |
| env->LO[env->current_tc][0] = (int32_t)(HILO & 0xFFFFFFFF); |
| env->HI[env->current_tc][0] = (int32_t)(HILO >> 32); |
| } |
| |
| static always_inline void set_HIT0_LO (uint64_t HILO) |
| { |
| env->LO[env->current_tc][0] = (int32_t)(HILO & 0xFFFFFFFF); |
| T0 = env->HI[env->current_tc][0] = (int32_t)(HILO >> 32); |
| } |
| |
| static always_inline void set_HI_LOT0 (uint64_t HILO) |
| { |
| T0 = env->LO[env->current_tc][0] = (int32_t)(HILO & 0xFFFFFFFF); |
| env->HI[env->current_tc][0] = (int32_t)(HILO >> 32); |
| } |
| |
| void op_mult (void) |
| { |
| set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1); |
| FORCE_RET(); |
| } |
| |
| void op_multu (void) |
| { |
| set_HILO((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1); |
| FORCE_RET(); |
| } |
| |
| void op_madd (void) |
| { |
| int64_t tmp; |
| |
| tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1); |
| set_HILO((int64_t)get_HILO() + tmp); |
| FORCE_RET(); |
| } |
| |
| void op_maddu (void) |
| { |
| uint64_t tmp; |
| |
| tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1); |
| set_HILO(get_HILO() + tmp); |
| FORCE_RET(); |
| } |
| |
| void op_msub (void) |
| { |
| int64_t tmp; |
| |
| tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1); |
| set_HILO((int64_t)get_HILO() - tmp); |
| FORCE_RET(); |
| } |
| |
| void op_msubu (void) |
| { |
| uint64_t tmp; |
| |
| tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1); |
| set_HILO(get_HILO() - tmp); |
| FORCE_RET(); |
| } |
| |
| /* Multiplication variants of the vr54xx. */ |
| void op_muls (void) |
| { |
| set_HI_LOT0(0 - ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_mulsu (void) |
| { |
| set_HI_LOT0(0 - ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_macc (void) |
| { |
| set_HI_LOT0(get_HILO() + ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_macchi (void) |
| { |
| set_HIT0_LO(get_HILO() + ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_maccu (void) |
| { |
| set_HI_LOT0(get_HILO() + ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_macchiu (void) |
| { |
| set_HIT0_LO(get_HILO() + ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_msac (void) |
| { |
| set_HI_LOT0(get_HILO() - ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_msachi (void) |
| { |
| set_HIT0_LO(get_HILO() - ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_msacu (void) |
| { |
| set_HI_LOT0(get_HILO() - ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_msachiu (void) |
| { |
| set_HIT0_LO(get_HILO() - ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_mulhi (void) |
| { |
| set_HIT0_LO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1); |
| FORCE_RET(); |
| } |
| |
| void op_mulhiu (void) |
| { |
| set_HIT0_LO((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1); |
| FORCE_RET(); |
| } |
| |
| void op_mulshi (void) |
| { |
| set_HIT0_LO(0 - ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| void op_mulshiu (void) |
| { |
| set_HIT0_LO(0 - ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1)); |
| FORCE_RET(); |
| } |
| |
| #endif /* TARGET_LONG_BITS > HOST_LONG_BITS */ |
| |
| #if defined(TARGET_MIPS64) |
| void op_dmult (void) |
| { |
| CALL_FROM_TB4(muls64, &(env->LO[env->current_tc][0]), &(env->HI[env->current_tc][0]), T0, T1); |
| FORCE_RET(); |
| } |
| |
| void op_dmultu (void) |
| { |
| CALL_FROM_TB4(mulu64, &(env->LO[env->current_tc][0]), &(env->HI[env->current_tc][0]), T0, T1); |
| FORCE_RET(); |
| } |
| #endif |
| |
| /* CP1 functions */ |
| #if 0 |
| # define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env) |
| #else |
| # define DEBUG_FPU_STATE() do { } while(0) |
| #endif |
| |
| void op_mfc1 (void) |
| { |
| T0 = (int32_t)WT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_mtc1 (void) |
| { |
| WT0 = T0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_dmfc1 (void) |
| { |
| T0 = DT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_dmtc1 (void) |
| { |
| DT0 = T0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_mfhc1 (void) |
| { |
| T0 = (int32_t)WTH0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_mthc1 (void) |
| { |
| WTH0 = T0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| /* Float support. |
| Single precition routines have a "s" suffix, double precision a |
| "d" suffix, 32bit integer "w", 64bit integer "l", paired singe "ps", |
| paired single lowwer "pl", paired single upper "pu". */ |
| |
| #define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void) |
| |
| FLOAT_OP(cvtd, s) |
| { |
| CALL_FROM_TB0(do_float_cvtd_s); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtd, w) |
| { |
| CALL_FROM_TB0(do_float_cvtd_w); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtd, l) |
| { |
| CALL_FROM_TB0(do_float_cvtd_l); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtl, d) |
| { |
| CALL_FROM_TB0(do_float_cvtl_d); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtl, s) |
| { |
| CALL_FROM_TB0(do_float_cvtl_s); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtps, s) |
| { |
| WT2 = WT0; |
| WTH2 = WT1; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtps, pw) |
| { |
| CALL_FROM_TB0(do_float_cvtps_pw); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtpw, ps) |
| { |
| CALL_FROM_TB0(do_float_cvtpw_ps); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvts, d) |
| { |
| CALL_FROM_TB0(do_float_cvts_d); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvts, w) |
| { |
| CALL_FROM_TB0(do_float_cvts_w); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvts, l) |
| { |
| CALL_FROM_TB0(do_float_cvts_l); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvts, pl) |
| { |
| CALL_FROM_TB0(do_float_cvts_pl); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvts, pu) |
| { |
| CALL_FROM_TB0(do_float_cvts_pu); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtw, s) |
| { |
| CALL_FROM_TB0(do_float_cvtw_s); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(cvtw, d) |
| { |
| CALL_FROM_TB0(do_float_cvtw_d); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| FLOAT_OP(pll, ps) |
| { |
| DT2 = ((uint64_t)WT0 << 32) | WT1; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(plu, ps) |
| { |
| DT2 = ((uint64_t)WT0 << 32) | WTH1; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(pul, ps) |
| { |
| DT2 = ((uint64_t)WTH0 << 32) | WT1; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(puu, ps) |
| { |
| DT2 = ((uint64_t)WTH0 << 32) | WTH1; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| #define FLOAT_ROUNDOP(op, ttype, stype) \ |
| FLOAT_OP(op ## ttype, stype) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## op ## ttype ## _ ## stype); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| |
| FLOAT_ROUNDOP(round, l, d) |
| FLOAT_ROUNDOP(round, l, s) |
| FLOAT_ROUNDOP(round, w, d) |
| FLOAT_ROUNDOP(round, w, s) |
| |
| FLOAT_ROUNDOP(trunc, l, d) |
| FLOAT_ROUNDOP(trunc, l, s) |
| FLOAT_ROUNDOP(trunc, w, d) |
| FLOAT_ROUNDOP(trunc, w, s) |
| |
| FLOAT_ROUNDOP(ceil, l, d) |
| FLOAT_ROUNDOP(ceil, l, s) |
| FLOAT_ROUNDOP(ceil, w, d) |
| FLOAT_ROUNDOP(ceil, w, s) |
| |
| FLOAT_ROUNDOP(floor, l, d) |
| FLOAT_ROUNDOP(floor, l, s) |
| FLOAT_ROUNDOP(floor, w, d) |
| FLOAT_ROUNDOP(floor, w, s) |
| #undef FLOAR_ROUNDOP |
| |
| FLOAT_OP(movf, d) |
| { |
| if (!(env->fpu->fcr31 & PARAM1)) |
| DT2 = DT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movf, s) |
| { |
| if (!(env->fpu->fcr31 & PARAM1)) |
| WT2 = WT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movf, ps) |
| { |
| unsigned int mask = GET_FP_COND (env->fpu) >> PARAM1; |
| if (!(mask & 1)) |
| WT2 = WT0; |
| if (!(mask & 2)) |
| WTH2 = WTH0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movt, d) |
| { |
| if (env->fpu->fcr31 & PARAM1) |
| DT2 = DT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movt, s) |
| { |
| if (env->fpu->fcr31 & PARAM1) |
| WT2 = WT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movt, ps) |
| { |
| unsigned int mask = GET_FP_COND (env->fpu) >> PARAM1; |
| if (mask & 1) |
| WT2 = WT0; |
| if (mask & 2) |
| WTH2 = WTH0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movz, d) |
| { |
| if (!T0) |
| DT2 = DT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movz, s) |
| { |
| if (!T0) |
| WT2 = WT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movz, ps) |
| { |
| if (!T0) { |
| WT2 = WT0; |
| WTH2 = WTH0; |
| } |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movn, d) |
| { |
| if (T0) |
| DT2 = DT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movn, s) |
| { |
| if (T0) |
| WT2 = WT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(movn, ps) |
| { |
| if (T0) { |
| WT2 = WT0; |
| WTH2 = WTH0; |
| } |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| /* operations calling helpers, for s, d and ps */ |
| #define FLOAT_HOP(name) \ |
| FLOAT_OP(name, d) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## name ## _d); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name, s) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## name ## _s); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name, ps) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## name ## _ps); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_HOP(add) |
| FLOAT_HOP(sub) |
| FLOAT_HOP(mul) |
| FLOAT_HOP(div) |
| FLOAT_HOP(recip2) |
| FLOAT_HOP(rsqrt2) |
| FLOAT_HOP(rsqrt1) |
| FLOAT_HOP(recip1) |
| #undef FLOAT_HOP |
| |
| /* operations calling helpers, for s and d */ |
| #define FLOAT_HOP(name) \ |
| FLOAT_OP(name, d) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## name ## _d); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name, s) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## name ## _s); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_HOP(rsqrt) |
| FLOAT_HOP(recip) |
| #undef FLOAT_HOP |
| |
| /* operations calling helpers, for ps */ |
| #define FLOAT_HOP(name) \ |
| FLOAT_OP(name, ps) \ |
| { \ |
| CALL_FROM_TB0(do_float_ ## name ## _ps); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_HOP(addr) |
| FLOAT_HOP(mulr) |
| #undef FLOAT_HOP |
| |
| /* ternary operations */ |
| #define FLOAT_TERNOP(name1, name2) \ |
| FLOAT_OP(name1 ## name2, d) \ |
| { \ |
| FDT0 = float64_ ## name1 (FDT0, FDT1, &env->fpu->fp_status); \ |
| FDT2 = float64_ ## name2 (FDT0, FDT2, &env->fpu->fp_status); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name1 ## name2, s) \ |
| { \ |
| FST0 = float32_ ## name1 (FST0, FST1, &env->fpu->fp_status); \ |
| FST2 = float32_ ## name2 (FST0, FST2, &env->fpu->fp_status); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name1 ## name2, ps) \ |
| { \ |
| FST0 = float32_ ## name1 (FST0, FST1, &env->fpu->fp_status); \ |
| FSTH0 = float32_ ## name1 (FSTH0, FSTH1, &env->fpu->fp_status); \ |
| FST2 = float32_ ## name2 (FST0, FST2, &env->fpu->fp_status); \ |
| FSTH2 = float32_ ## name2 (FSTH0, FSTH2, &env->fpu->fp_status); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_TERNOP(mul, add) |
| FLOAT_TERNOP(mul, sub) |
| #undef FLOAT_TERNOP |
| |
| /* negated ternary operations */ |
| #define FLOAT_NTERNOP(name1, name2) \ |
| FLOAT_OP(n ## name1 ## name2, d) \ |
| { \ |
| FDT0 = float64_ ## name1 (FDT0, FDT1, &env->fpu->fp_status); \ |
| FDT2 = float64_ ## name2 (FDT0, FDT2, &env->fpu->fp_status); \ |
| FDT2 = float64_chs(FDT2); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(n ## name1 ## name2, s) \ |
| { \ |
| FST0 = float32_ ## name1 (FST0, FST1, &env->fpu->fp_status); \ |
| FST2 = float32_ ## name2 (FST0, FST2, &env->fpu->fp_status); \ |
| FST2 = float32_chs(FST2); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(n ## name1 ## name2, ps) \ |
| { \ |
| FST0 = float32_ ## name1 (FST0, FST1, &env->fpu->fp_status); \ |
| FSTH0 = float32_ ## name1 (FSTH0, FSTH1, &env->fpu->fp_status); \ |
| FST2 = float32_ ## name2 (FST0, FST2, &env->fpu->fp_status); \ |
| FSTH2 = float32_ ## name2 (FSTH0, FSTH2, &env->fpu->fp_status); \ |
| FST2 = float32_chs(FST2); \ |
| FSTH2 = float32_chs(FSTH2); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_NTERNOP(mul, add) |
| FLOAT_NTERNOP(mul, sub) |
| #undef FLOAT_NTERNOP |
| |
| /* unary operations, modifying fp status */ |
| #define FLOAT_UNOP(name) \ |
| FLOAT_OP(name, d) \ |
| { \ |
| FDT2 = float64_ ## name(FDT0, &env->fpu->fp_status); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name, s) \ |
| { \ |
| FST2 = float32_ ## name(FST0, &env->fpu->fp_status); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_UNOP(sqrt) |
| #undef FLOAT_UNOP |
| |
| /* unary operations, not modifying fp status */ |
| #define FLOAT_UNOP(name) \ |
| FLOAT_OP(name, d) \ |
| { \ |
| FDT2 = float64_ ## name(FDT0); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name, s) \ |
| { \ |
| FST2 = float32_ ## name(FST0); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| FLOAT_OP(name, ps) \ |
| { \ |
| FST2 = float32_ ## name(FST0); \ |
| FSTH2 = float32_ ## name(FSTH0); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| FLOAT_UNOP(abs) |
| FLOAT_UNOP(chs) |
| #undef FLOAT_UNOP |
| |
| FLOAT_OP(mov, d) |
| { |
| FDT2 = FDT0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(mov, s) |
| { |
| FST2 = FST0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(mov, ps) |
| { |
| FST2 = FST0; |
| FSTH2 = FSTH0; |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| FLOAT_OP(alnv, ps) |
| { |
| switch (T0 & 0x7) { |
| case 0: |
| FST2 = FST0; |
| FSTH2 = FSTH0; |
| break; |
| case 4: |
| #ifdef TARGET_WORDS_BIGENDIAN |
| FSTH2 = FST0; |
| FST2 = FSTH1; |
| #else |
| FSTH2 = FST1; |
| FST2 = FSTH0; |
| #endif |
| break; |
| default: /* unpredictable */ |
| break; |
| } |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| #ifdef CONFIG_SOFTFLOAT |
| #define clear_invalid() do { \ |
| int flags = get_float_exception_flags(&env->fpu->fp_status); \ |
| flags &= ~float_flag_invalid; \ |
| set_float_exception_flags(flags, &env->fpu->fp_status); \ |
| } while(0) |
| #else |
| #define clear_invalid() do { } while(0) |
| #endif |
| |
| extern void dump_fpu_s(CPUState *env); |
| |
| #define CMP_OP(fmt, op) \ |
| void OPPROTO op_cmp ## _ ## fmt ## _ ## op(void) \ |
| { \ |
| CALL_FROM_TB1(do_cmp ## _ ## fmt ## _ ## op, PARAM1); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } \ |
| void OPPROTO op_cmpabs ## _ ## fmt ## _ ## op(void) \ |
| { \ |
| CALL_FROM_TB1(do_cmpabs ## _ ## fmt ## _ ## op, PARAM1); \ |
| DEBUG_FPU_STATE(); \ |
| FORCE_RET(); \ |
| } |
| #define CMP_OPS(op) \ |
| CMP_OP(d, op) \ |
| CMP_OP(s, op) \ |
| CMP_OP(ps, op) |
| |
| CMP_OPS(f) |
| CMP_OPS(un) |
| CMP_OPS(eq) |
| CMP_OPS(ueq) |
| CMP_OPS(olt) |
| CMP_OPS(ult) |
| CMP_OPS(ole) |
| CMP_OPS(ule) |
| CMP_OPS(sf) |
| CMP_OPS(ngle) |
| CMP_OPS(seq) |
| CMP_OPS(ngl) |
| CMP_OPS(lt) |
| CMP_OPS(nge) |
| CMP_OPS(le) |
| CMP_OPS(ngt) |
| #undef CMP_OPS |
| #undef CMP_OP |
| |
| void op_bc1f (void) |
| { |
| T0 = !!(~GET_FP_COND(env->fpu) & (0x1 << PARAM1)); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| void op_bc1any2f (void) |
| { |
| T0 = !!(~GET_FP_COND(env->fpu) & (0x3 << PARAM1)); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| void op_bc1any4f (void) |
| { |
| T0 = !!(~GET_FP_COND(env->fpu) & (0xf << PARAM1)); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_bc1t (void) |
| { |
| T0 = !!(GET_FP_COND(env->fpu) & (0x1 << PARAM1)); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| void op_bc1any2t (void) |
| { |
| T0 = !!(GET_FP_COND(env->fpu) & (0x3 << PARAM1)); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| void op_bc1any4t (void) |
| { |
| T0 = !!(GET_FP_COND(env->fpu) & (0xf << PARAM1)); |
| DEBUG_FPU_STATE(); |
| FORCE_RET(); |
| } |
| |
| void op_tlbwi (void) |
| { |
| CALL_FROM_TB0(env->tlb->do_tlbwi); |
| FORCE_RET(); |
| } |
| |
| void op_tlbwr (void) |
| { |
| CALL_FROM_TB0(env->tlb->do_tlbwr); |
| FORCE_RET(); |
| } |
| |
| void op_tlbp (void) |
| { |
| CALL_FROM_TB0(env->tlb->do_tlbp); |
| FORCE_RET(); |
| } |
| |
| void op_tlbr (void) |
| { |
| CALL_FROM_TB0(env->tlb->do_tlbr); |
| FORCE_RET(); |
| } |
| |
| /* Specials */ |
| #if defined (CONFIG_USER_ONLY) |
| void op_tls_value (void) |
| { |
| T0 = env->tls_value; |
| } |
| #endif |
| |
| void op_pmon (void) |
| { |
| CALL_FROM_TB1(do_pmon, PARAM1); |
| FORCE_RET(); |
| } |
| |
| void op_di (void) |
| { |
| T0 = env->CP0_Status; |
| env->CP0_Status = T0 & ~(1 << CP0St_IE); |
| CALL_FROM_TB1(cpu_mips_update_irq, env); |
| FORCE_RET(); |
| } |
| |
| void op_ei (void) |
| { |
| T0 = env->CP0_Status; |
| env->CP0_Status = T0 | (1 << CP0St_IE); |
| CALL_FROM_TB1(cpu_mips_update_irq, env); |
| FORCE_RET(); |
| } |
| |
| void op_trap (void) |
| { |
| if (T0) { |
| CALL_FROM_TB1(do_raise_exception, EXCP_TRAP); |
| } |
| FORCE_RET(); |
| } |
| |
| void op_debug (void) |
| { |
| CALL_FROM_TB1(do_raise_exception, EXCP_DEBUG); |
| FORCE_RET(); |
| } |
| |
| void debug_pre_eret (void); |
| void debug_post_eret (void); |
| void op_eret (void) |
| { |
| if (loglevel & CPU_LOG_EXEC) |
| CALL_FROM_TB0(debug_pre_eret); |
| if (env->CP0_Status & (1 << CP0St_ERL)) { |
| env->PC[env->current_tc] = env->CP0_ErrorEPC; |
| env->CP0_Status &= ~(1 << CP0St_ERL); |
| } else { |
| env->PC[env->current_tc] = env->CP0_EPC; |
| env->CP0_Status &= ~(1 << CP0St_EXL); |
| } |
| CALL_FROM_TB1(compute_hflags, env); |
| if (loglevel & CPU_LOG_EXEC) |
| CALL_FROM_TB0(debug_post_eret); |
| env->CP0_LLAddr = 1; |
| FORCE_RET(); |
| } |
| |
| void op_deret (void) |
| { |
| if (loglevel & CPU_LOG_EXEC) |
| CALL_FROM_TB0(debug_pre_eret); |
| env->PC[env->current_tc] = env->CP0_DEPC; |
| env->hflags &= MIPS_HFLAG_DM; |
| CALL_FROM_TB1(compute_hflags, env); |
| if (loglevel & CPU_LOG_EXEC) |
| CALL_FROM_TB0(debug_post_eret); |
| env->CP0_LLAddr = 1; |
| FORCE_RET(); |
| } |
| |
| void op_rdhwr_cpunum(void) |
| { |
| if ((env->hflags & MIPS_HFLAG_CP0) || |
| (env->CP0_HWREna & (1 << 0))) |
| T0 = env->CP0_EBase & 0x3ff; |
| else |
| CALL_FROM_TB1(do_raise_exception, EXCP_RI); |
| FORCE_RET(); |
| } |
| |
| void op_rdhwr_synci_step(void) |
| { |
| if ((env->hflags & MIPS_HFLAG_CP0) || |
| (env->CP0_HWREna & (1 << 1))) |
| T0 = env->SYNCI_Step; |
| else |
| CALL_FROM_TB1(do_raise_exception, EXCP_RI); |
| FORCE_RET(); |
| } |
| |
| void op_rdhwr_cc(void) |
| { |
| if ((env->hflags & MIPS_HFLAG_CP0) || |
| (env->CP0_HWREna & (1 << 2))) |
| T0 = env->CP0_Count; |
| else |
| CALL_FROM_TB1(do_raise_exception, EXCP_RI); |
| FORCE_RET(); |
| } |
| |
| void op_rdhwr_ccres(void) |
| { |
| if ((env->hflags & MIPS_HFLAG_CP0) || |
| (env->CP0_HWREna & (1 << 3))) |
| T0 = env->CCRes; |
| else |
| CALL_FROM_TB1(do_raise_exception, EXCP_RI); |
| FORCE_RET(); |
| } |
| |
| void op_save_state (void) |
| { |
| env->hflags = PARAM1; |
| FORCE_RET(); |
| } |
| |
| void op_wait (void) |
| { |
| env->halted = 1; |
| CALL_FROM_TB1(do_raise_exception, EXCP_HLT); |
| FORCE_RET(); |
| } |
| |
| /* Bitfield operations. */ |
| void op_ext(void) |
| { |
| unsigned int pos = PARAM1; |
| unsigned int size = PARAM2; |
| |
| T0 = (int32_t)((T1 >> pos) & ((size < 32) ? ((1 << size) - 1) : ~0)); |
| FORCE_RET(); |
| } |
| |
| void op_ins(void) |
| { |
| unsigned int pos = PARAM1; |
| unsigned int size = PARAM2; |
| target_ulong mask = ((size < 32) ? ((1 << size) - 1) : ~0) << pos; |
| |
| T0 = (int32_t)((T0 & ~mask) | ((T1 << pos) & mask)); |
| FORCE_RET(); |
| } |
| |
| void op_wsbh(void) |
| { |
| T0 = (int32_t)(((T1 << 8) & ~0x00FF00FF) | ((T1 >> 8) & 0x00FF00FF)); |
| FORCE_RET(); |
| } |
| |
| #if defined(TARGET_MIPS64) |
| void op_dext(void) |
| { |
| unsigned int pos = PARAM1; |
| unsigned int size = PARAM2; |
| |
| T0 = (T1 >> pos) & ((size < 64) ? ((1ULL << size) - 1) : ~0ULL); |
| FORCE_RET(); |
| } |
| |
| void op_dins(void) |
| { |
| unsigned int pos = PARAM1; |
| unsigned int size = PARAM2; |
| target_ulong mask = ((size < 64) ? ((1ULL << size) - 1) : ~0ULL) << pos; |
| |
| T0 = (T0 & ~mask) | ((T1 << pos) & mask); |
| FORCE_RET(); |
| } |
| |
| void op_dsbh(void) |
| { |
| T0 = ((T1 << 8) & ~0x00FF00FF00FF00FFULL) | ((T1 >> 8) & 0x00FF00FF00FF00FFULL); |
| FORCE_RET(); |
| } |
| |
| void op_dshd(void) |
| { |
| T1 = ((T1 << 16) & ~0x0000FFFF0000FFFFULL) | ((T1 >> 16) & 0x0000FFFF0000FFFFULL); |
| T0 = (T1 << 32) | (T1 >> 32); |
| FORCE_RET(); |
| } |
| #endif |