1 /* 2 * S/390 integer helper routines 3 * 4 * Copyright (c) 2009 Ulrich Hecht 5 * Copyright (c) 2009 Alexander Graf 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "s390x-internal.h" 24 #include "tcg_s390x.h" 25 #include "exec/exec-all.h" 26 #include "qemu/host-utils.h" 27 #include "exec/helper-proto.h" 28 29 /* #define DEBUG_HELPER */ 30 #ifdef DEBUG_HELPER 31 #define HELPER_LOG(x...) qemu_log(x) 32 #else 33 #define HELPER_LOG(x...) 34 #endif 35 36 /* 64/32 -> 32 signed division */ 37 int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) 38 { 39 int32_t ret, b = b64; 40 int64_t q; 41 42 if (b == 0) { 43 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 44 } 45 46 ret = q = a / b; 47 env->retxl = a % b; 48 49 /* Catch non-representable quotient. */ 50 if (ret != q) { 51 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 52 } 53 54 return ret; 55 } 56 57 /* 64/32 -> 32 unsigned division */ 58 uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64) 59 { 60 uint32_t ret, b = b64; 61 uint64_t q; 62 63 if (b == 0) { 64 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 65 } 66 67 ret = q = a / b; 68 env->retxl = a % b; 69 70 /* Catch non-representable quotient. */ 71 if (ret != q) { 72 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 73 } 74 75 return ret; 76 } 77 78 /* 64/64 -> 64 signed division */ 79 int64_t HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b) 80 { 81 /* Catch divide by zero, and non-representable quotient (MIN / -1). */ 82 if (b == 0 || (b == -1 && a == (1ll << 63))) { 83 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 84 } 85 env->retxl = a % b; 86 return a / b; 87 } 88 89 /* 128 -> 64/64 unsigned division */ 90 uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, 91 uint64_t b) 92 { 93 uint64_t ret; 94 /* Signal divide by zero. */ 95 if (b == 0) { 96 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 97 } 98 if (ah == 0) { 99 /* 64 -> 64/64 case */ 100 env->retxl = al % b; 101 ret = al / b; 102 } else { 103 /* ??? Move i386 idivq helper to host-utils. */ 104 #ifdef CONFIG_INT128 105 __uint128_t a = ((__uint128_t)ah << 64) | al; 106 __uint128_t q = a / b; 107 env->retxl = a % b; 108 ret = q; 109 if (ret != q) { 110 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); 111 } 112 #else 113 /* 32-bit hosts would need special wrapper functionality - just abort if 114 we encounter such a case; it's very unlikely anyways. */ 115 cpu_abort(env_cpu(env), "128 -> 64/64 division not implemented\n"); 116 #endif 117 } 118 return ret; 119 } 120 121 uint64_t HELPER(cvd)(int32_t reg) 122 { 123 /* positive 0 */ 124 uint64_t dec = 0x0c; 125 int64_t bin = reg; 126 int shift; 127 128 if (bin < 0) { 129 bin = -bin; 130 dec = 0x0d; 131 } 132 133 for (shift = 4; (shift < 64) && bin; shift += 4) { 134 dec |= (bin % 10) << shift; 135 bin /= 10; 136 } 137 138 return dec; 139 } 140 141 uint64_t HELPER(popcnt)(uint64_t val) 142 { 143 /* Note that we don't fold past bytes. */ 144 val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL); 145 val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL); 146 val = (val + (val >> 4)) & 0x0f0f0f0f0f0f0f0fULL; 147 return val; 148 } 149