diff options
author | Richard Henderson <rth@twiddle.net> | 2012-08-17 18:52:33 -0700 |
---|---|---|
committer | Richard Henderson <rth@twiddle.net> | 2013-01-05 12:00:29 -0800 |
commit | 4e4bb43899c4c97e14b59fbd7cd5cb44eea850a4 (patch) | |
tree | 3930052d5131df78f04713ce7230fc995477057b /target-s390x/cc_helper.c | |
parent | 2b280b97085ae90e804c1b31557a79d1da2789a4 (diff) | |
download | qemu-4e4bb43899c4c97e14b59fbd7cd5cb44eea850a4.zip qemu-4e4bb43899c4c97e14b59fbd7cd5cb44eea850a4.tar.gz qemu-4e4bb43899c4c97e14b59fbd7cd5cb44eea850a4.tar.bz2 |
target-s390: Convert ADD LOGICAL CARRY and SUBTRACT LOGICAL BORROW
I'm resonably certain that the carry/borrow-out condition for both
helpers was incorrect, failing to take into account the carry-in.
Adding the new CC_OP codes also allows removing the awkward interface
we used for the slb helpers.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'target-s390x/cc_helper.c')
-rw-r--r-- | target-s390x/cc_helper.c | 108 |
1 files changed, 78 insertions, 30 deletions
diff --git a/target-s390x/cc_helper.c b/target-s390x/cc_helper.c index 19ef145..880e3b2 100644 --- a/target-s390x/cc_helper.c +++ b/target-s390x/cc_helper.c @@ -146,22 +146,21 @@ static inline uint32_t cc_calc_add_64(CPUS390XState *env, int64_t a1, } } -static inline uint32_t cc_calc_addu_64(CPUS390XState *env, uint64_t a1, - uint64_t a2, uint64_t ar) +static uint32_t cc_calc_addu_64(CPUS390XState *env, uint64_t a1, + uint64_t a2, uint64_t ar) { - if (ar == 0) { - if (a1) { - return 2; - } else { - return 0; - } - } else { - if (ar < a1 || ar < a2) { - return 3; - } else { - return 1; - } - } + return (ar != 0) + 2 * (ar < a1); +} + +static uint32_t cc_calc_addc_64(CPUS390XState *env, uint64_t a1, + uint64_t a2, uint64_t ar) +{ + /* Recover a2 + carry_in. */ + uint64_t a2c = ar - a1; + /* Check for a2+carry_in overflow, then a1+a2c overflow. */ + int carry_out = (a2c < a2) || (ar < a1); + + return (ar != 0) + 2 * carry_out; } static inline uint32_t cc_calc_sub_64(CPUS390XState *env, int64_t a1, @@ -194,6 +193,25 @@ static inline uint32_t cc_calc_subu_64(CPUS390XState *env, uint64_t a1, } } +static uint32_t cc_calc_subb_64(CPUS390XState *env, uint64_t a1, + uint64_t a2, uint64_t ar) +{ + /* We had borrow-in if normal subtraction isn't equal. */ + int borrow_in = ar - (a1 - a2); + int borrow_out; + + /* If a2 was ULONG_MAX, and borrow_in, then a2 is logically 65 bits, + and we must have had borrow out. */ + if (borrow_in && a2 == (uint64_t)-1) { + borrow_out = 1; + } else { + a2 += borrow_in; + borrow_out = (a2 > a1); + } + + return (ar != 0) + 2 * !borrow_out; +} + static inline uint32_t cc_calc_abs_64(CPUS390XState *env, int64_t dst) { if ((uint64_t)dst == 0x8000000000000000ULL) { @@ -240,22 +258,21 @@ static inline uint32_t cc_calc_add_32(CPUS390XState *env, int32_t a1, } } -static inline uint32_t cc_calc_addu_32(CPUS390XState *env, uint32_t a1, - uint32_t a2, uint32_t ar) +static uint32_t cc_calc_addu_32(CPUS390XState *env, uint32_t a1, + uint32_t a2, uint32_t ar) { - if (ar == 0) { - if (a1) { - return 2; - } else { - return 0; - } - } else { - if (ar < a1 || ar < a2) { - return 3; - } else { - return 1; - } - } + return (ar != 0) + 2 * (ar < a1); +} + +static uint32_t cc_calc_addc_32(CPUS390XState *env, uint32_t a1, + uint32_t a2, uint32_t ar) +{ + /* Recover a2 + carry_in. */ + uint32_t a2c = ar - a1; + /* Check for a2+carry_in overflow, then a1+a2c overflow. */ + int carry_out = (a2c < a2) || (ar < a1); + + return (ar != 0) + 2 * carry_out; } static inline uint32_t cc_calc_sub_32(CPUS390XState *env, int32_t a1, @@ -288,6 +305,25 @@ static inline uint32_t cc_calc_subu_32(CPUS390XState *env, uint32_t a1, } } +static uint32_t cc_calc_subb_32(CPUS390XState *env, uint32_t a1, + uint32_t a2, uint32_t ar) +{ + /* We had borrow-in if normal subtraction isn't equal. */ + int borrow_in = ar - (a1 - a2); + int borrow_out; + + /* If a2 was UINT_MAX, and borrow_in, then a2 is logically 65 bits, + and we must have had borrow out. */ + if (borrow_in && a2 == (uint32_t)-1) { + borrow_out = 1; + } else { + a2 += borrow_in; + borrow_out = (a2 > a1); + } + + return (ar != 0) + 2 * !borrow_out; +} + static inline uint32_t cc_calc_abs_32(CPUS390XState *env, int32_t dst) { if ((uint32_t)dst == 0x80000000UL) { @@ -426,12 +462,18 @@ static inline uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, case CC_OP_ADDU_64: r = cc_calc_addu_64(env, src, dst, vr); break; + case CC_OP_ADDC_64: + r = cc_calc_addc_64(env, src, dst, vr); + break; case CC_OP_SUB_64: r = cc_calc_sub_64(env, src, dst, vr); break; case CC_OP_SUBU_64: r = cc_calc_subu_64(env, src, dst, vr); break; + case CC_OP_SUBB_64: + r = cc_calc_subb_64(env, src, dst, vr); + break; case CC_OP_ABS_64: r = cc_calc_abs_64(env, dst); break; @@ -448,12 +490,18 @@ static inline uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, case CC_OP_ADDU_32: r = cc_calc_addu_32(env, src, dst, vr); break; + case CC_OP_ADDC_32: + r = cc_calc_addc_32(env, src, dst, vr); + break; case CC_OP_SUB_32: r = cc_calc_sub_32(env, src, dst, vr); break; case CC_OP_SUBU_32: r = cc_calc_subu_32(env, src, dst, vr); break; + case CC_OP_SUBB_32: + r = cc_calc_subb_32(env, src, dst, vr); + break; case CC_OP_ABS_32: r = cc_calc_abs_64(env, dst); break; |