diff options
author | Bastian Koppelmann <kbastian@mail.uni-paderborn.de> | 2015-02-03 18:36:53 +0000 |
---|---|---|
committer | Bastian Koppelmann <kbastian@mail.uni-paderborn.de> | 2015-03-03 01:05:44 +0000 |
commit | b00aa8ecbc6fd7468178a0dabc7dfd0b7e1b8cd6 (patch) | |
tree | 121a1d9ea47ec36a2d2b51d92961cd2a4720bb8c /target-tricore/op_helper.c | |
parent | 2e430e1cdcbac8825bc44b42844cbb011b859847 (diff) |
target-tricore: Add instructions of RRR1 opcode format, which have 0x43 as first opcode
Add helpers:
* madd64_q_ssov: multiply two 32 bit q-format number, add them with a
64 bit q-format number and saturate.
* madd32_q_add_ssov: add two 64 bit q-format numbers and return a 32 bit
result.
* maddr_q_ssov: multiplay two 32 bit q-format numbers, add a 32 bit
q-format number and saturate.
* maddr_q: multiplay two 32 bit q-format numbers and add a 32 bit
q-format number.
Note: madd instructions in the q format can behave strange, e.g.
0x1 + (0x80000000 * 0x80000000) << 1 for 32 bit signed values does not cause an
overflow on the guest, because all intermediate results should be handled as if
they are indefinitely precise. We handle this by inverting the overflow bit for
all cases: a + (0x80000000 * 0x80000000) << 1.
Signed-off-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'target-tricore/op_helper.c')
-rw-r--r-- | target-tricore/op_helper.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/target-tricore/op_helper.c b/target-tricore/op_helper.c index 8cd154b6b9..2755d45aa7 100644 --- a/target-tricore/op_helper.c +++ b/target-tricore/op_helper.c @@ -475,6 +475,131 @@ uint64_t helper_madd64_ssov(CPUTriCoreState *env, target_ulong r1, return ret; } +uint32_t +helper_madd32_q_add_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) +{ + int64_t result; + + result = (r1 + r2); + + env->PSW_USB_AV = (result ^ result * 2u); + env->PSW_USB_SAV |= env->PSW_USB_AV; + + /* we do the saturation by hand, since we produce an overflow on the host + if the mul before was (0x80000000 * 0x80000000) << 1). If this is the + case, we flip the saturated value. */ + if (r2 == 0x8000000000000000LL) { + if (result > 0x7fffffffLL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MIN; + } else if (result < -0x80000000LL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MAX; + } else { + env->PSW_USB_V = 0; + } + } else { + if (result > 0x7fffffffLL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MAX; + } else if (result < -0x80000000LL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MIN; + } else { + env->PSW_USB_V = 0; + } + } + return (uint32_t)result; +} + +uint64_t helper_madd64_q_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = (int64_t)r1; + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t result, mul; + int64_t ovf; + + mul = (t2 * t3) << n; + result = mul + t1; + + env->PSW_USB_AV = (result ^ result * 2u) >> 32; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + ovf = (result ^ mul) & ~(mul ^ t1); + /* we do the saturation by hand, since we produce an overflow on the host + if the mul was (0x80000000 * 0x80000000) << 1). If this is the + case, we flip the saturated value. */ + if ((r2 == 0x80000000) && (r3 == 0x80000000) && (n == 1)) { + if (ovf >= 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul < 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + } else { + if (ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul >= 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + } + return (uint64_t)result; +} + +uint32_t helper_maddr_q_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul, ret; + + if ((t2 == -0x8000ll) && (t3 == -0x8000ll) && (n == 1)) { + mul = 0x7fffffff; + } else { + mul = (t2 * t3) << n; + } + + ret = t1 + mul + 0x8000; + + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if (ret > 0x7fffffffll) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + ret = INT32_MAX; + } else if (ret < -0x80000000ll) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + ret = INT32_MIN; + } else { + env->PSW_USB_V = 0; + } + return ret & 0xffff0000ll; +} + uint64_t helper_madd64_suov(CPUTriCoreState *env, target_ulong r1, uint64_t r2, target_ulong r3) { @@ -729,6 +854,34 @@ uint32_t helper_addr_h(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); } +uint32_t helper_maddr_q(CPUTriCoreState *env, uint32_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul, ret; + + if ((t2 == -0x8000ll) && (t3 == -0x8000ll) && (n == 1)) { + mul = 0x7fffffff; + } else { + mul = (t2 * t3) << n; + } + + ret = t1 + mul + 0x8000; + + if ((ret > 0x7fffffffll) || (ret < -0x80000000ll)) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + } else { + env->PSW_USB_V = 0; + } + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret & 0xffff0000ll; +} + uint32_t helper_add_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) { int32_t b, i; |