/* compare unsigned byte arrays */ uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2) { int i; unsigned char x, y; uint32_t cc; HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n", __func__, l, s1, s2); for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, s1 + i); y = cpu_ldub_data(env, s2 + i); HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y); if (x < y) { cc = 1; goto done; } else if (x > y) { cc = 2; goto done; } } cc = 0; done: HELPER_LOG("\n"); return cc; }
/* compare logical under mask */ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, uint64_t addr) { uint8_t r, d; uint32_t cc; HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1, mask, addr); cc = 0; while (mask) { if (mask & 8) { d = cpu_ldub_data(env, addr); r = (r1 & 0xff000000UL) >> 24; HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d, addr); if (r < d) { cc = 1; break; } else if (r > d) { cc = 2; break; } addr++; } mask = (mask << 1) & 0xf; r1 <<= 8; }
/* xor on array */ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i; unsigned char x; uint32_t cc = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); /* xor with itself is the same as memset(0) */ if (src == dest) { fast_memset(env, dest, 0, l + 1); return 0; } for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i); if (x) { cc = 1; } cpu_stb_data(env, dest + i, x); } return cc; }
/* xor on array */ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i; unsigned char x; uint32_t cc = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); #ifndef CONFIG_USER_ONLY /* xor with itself is the same as memset(0) */ if ((l > 32) && (src == dest) && (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) { mvc_fast_memset(env, l + 1, dest, 0); return 0; } #else if (src == dest) { memset(g2h(dest), 0, l + 1); return 0; } #endif for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i); if (x) { cc = 1; } cpu_stb_data(env, dest + i, x); } return cc; }
/* memmove */ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); /* mvc with source pointing to the byte after the destination is the same as memset with the first source byte */ if (dest == (src + 1)) { fast_memset(env, dest, cpu_ldub_data(env, src), l + 1); return; } /* mvc and memmove do not behave the same when areas overlap! */ if ((dest < src) || (src + l < dest)) { fast_memmove(env, dest, src, l + 1); return; } /* slow version with byte accesses which always work */ for (i = 0; i <= l; i++) { cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); } }
/* 64-bit FP multiply and subtract RR */ void HELPER(msdbr)(CPUS390XState *env, uint32_t f1, uint32_t f3, uint32_t f2) { HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __func__, f1, f2, f3); env->fregs[f1].d = float64_sub(float64_mul(env->fregs[f2].d, env->fregs[f3].d, &env->fpu_status), env->fregs[f1].d, &env->fpu_status); }
/* Raise an exception statically from a TB. */ void HELPER(exception)(CPUS390XState *env, uint32_t excp) { CPUState *cs = CPU(s390_env_get_cpu(env)); HELPER_LOG("%s: exception %d\n", __func__, excp); cs->exception_index = excp; cpu_loop_exit(cs); }
/* 64-bit FP subtraction RR */ uint32_t HELPER(sdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) { env->fregs[f1].d = float64_sub(env->fregs[f1].d, env->fregs[f2].d, &env->fpu_status); HELPER_LOG("%s: subtracting 0x%ld resulting in 0x%ld in f%d\n", __func__, env->fregs[f2].d, env->fregs[f1].d, f1); return set_cc_nz_f64(env->fregs[f1].d); }
/* 32-bit FP compare RR */ uint32_t HELPER(cebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) { float32 v1 = env->fregs[f1].l.upper; float32 v2 = env->fregs[f2].l.upper; HELPER_LOG("%s: comparing 0x%d from f%d and 0x%d\n", __func__, v1, f1, v2); return set_cc_f32(env, v1, v2); }
/* 64-bit FP compare RR */ uint32_t HELPER(cdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) { float64 v1 = env->fregs[f1].d; float64 v2 = env->fregs[f2].d; HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%ld\n", __func__, v1, f1, v2); return set_cc_f64(env, v1, v2); }
/* convert 128-bit float to 32-bit float */ void HELPER(lexbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) { CPU_QuadU x2; x2.ll.upper = env->fregs[f2].ll; x2.ll.lower = env->fregs[f2 + 2].ll; env->fregs[f1].l.upper = float128_to_float32(x2.q, &env->fpu_status); HELPER_LOG("%s: to 0x%d\n", __func__, env->fregs[f1].l.upper); }
/* convert 64-bit int to 128-bit float */ void HELPER(cxgbr)(CPUS390XState *env, uint32_t f1, int64_t v2) { CPU_QuadU x1; x1.q = int64_to_float128(v2, &env->fpu_status); HELPER_LOG("%s: converted %ld to 0x%lx and 0x%lx\n", __func__, v2, x1.ll.upper, x1.ll.lower); env->fregs[f1].ll = x1.ll.upper; env->fregs[f1 + 2].ll = x1.ll.lower; }
/* Set Prefix */ void HELPER(spx)(CPUS390XState *env, uint64_t a1) { CPUState *cs = CPU(s390_env_get_cpu(env)); uint32_t prefix = a1 & 0x7fffe000; env->psa = prefix; HELPER_LOG("prefix: %#x\n", prefix); tlb_flush_page(cs, 0); tlb_flush_page(cs, TARGET_PAGE_SIZE); }
/* 32-bit FP multiplication RM */ void HELPER(meeb)(CPUS390XState *env, uint32_t f1, uint32_t val) { float32 v1 = env->fregs[f1].l.upper; CPU_FloatU v2; v2.l = val; HELPER_LOG("%s: multiplying 0x%d from f%d and 0x%d\n", __func__, v1, f1, v2.f); env->fregs[f1].l.upper = float32_mul(v1, v2.f, &env->fpu_status); }
/* 32-bit FP subtraction RR */ uint32_t HELPER(sebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) { env->fregs[f1].l.upper = float32_sub(env->fregs[f1].l.upper, env->fregs[f2].l.upper, &env->fpu_status); HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __func__, env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1); return set_cc_nz_f32(env->fregs[f1].l.upper); }
/* absolute value 64-bit */ uint64_t HELPER(abs_i64)(int64_t val) { HELPER_LOG("%s: val 0x%" PRIx64 "\n", __func__, val); if (val < 0) { return -val; } else { return val; } }
/* 64-bit FP division RM */ void HELPER(ddb)(CPUS390XState *env, uint32_t f1, uint64_t a2) { float64 v1 = env->fregs[f1].d; CPU_DoubleU v2; v2.ll = cpu_ldq_data(env, a2); HELPER_LOG("%s: dividing 0x%lx from f%d by 0x%ld\n", __func__, v1, f1, v2.d); env->fregs[f1].d = float64_div(v1, v2.d, &env->fpu_status); }
/* 64-bit FP compare RM */ uint32_t HELPER(cdb)(CPUS390XState *env, uint32_t f1, uint64_t a2) { float64 v1 = env->fregs[f1].d; CPU_DoubleU v2; v2.ll = cpu_ldq_data(env, a2); HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%lx\n", __func__, v1, f1, v2.d); return set_cc_f64(env, v1, v2.d); }
/* 32-bit FP division RM */ void HELPER(deb)(CPUS390XState *env, uint32_t f1, uint32_t val) { float32 v1 = env->fregs[f1].l.upper; CPU_FloatU v2; v2.l = val; HELPER_LOG("%s: dividing 0x%d from f%d by 0x%d\n", __func__, v1, f1, v2.f); env->fregs[f1].l.upper = float32_div(v1, v2.f, &env->fpu_status); }
/* 64-bit FP multiply and add RM */ void HELPER(madb)(CPUS390XState *env, uint32_t f1, uint64_t a2, uint32_t f3) { CPU_DoubleU v2; HELPER_LOG("%s: f1 %d a2 0x%lx f3 %d\n", __func__, f1, a2, f3); v2.ll = cpu_ldq_data(env, a2); env->fregs[f1].d = float64_add(env->fregs[f1].d, float64_mul(v2.d, env->fregs[f3].d, &env->fpu_status), &env->fpu_status); }
/* 64-bit FP addition RM */ uint32_t HELPER(adb)(CPUS390XState *env, uint32_t f1, uint64_t a2) { float64 v1 = env->fregs[f1].d; CPU_DoubleU v2; v2.ll = cpu_ldq_data(env, a2); HELPER_LOG("%s: adding 0x%lx from f%d and 0x%lx\n", __func__, v1, f1, v2.d); env->fregs[f1].d = v1 = float64_add(v1, v2.d, &env->fpu_status); return set_cc_nz_f64(v1); }
/* or on array */ uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i; unsigned char x; uint32_t cc = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i); if (x) { cc = 1; } cpu_stb_data(env, dest + i, x); } return cc; }
/* memmove */ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i = 0; int x = 0; uint32_t l_64 = (l + 1) / 8; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); #ifndef CONFIG_USER_ONLY if ((l > 32) && (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) && (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) { if (dest == (src + 1)) { mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src)); return; } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { mvc_fast_memmove(env, l + 1, dest, src); return; } } #else if (dest == (src + 1)) { memset(g2h(dest), cpu_ldub_data(env, src), l + 1); return; } else { memmove(g2h(dest), g2h(src), l + 1); return; } #endif /* handle the parts that fit into 8-byte loads/stores */ if (dest != (src + 1)) { for (i = 0; i < l_64; i++) { cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x)); x += 8; } } /* slow version crossing pages with byte accesses */ for (i = x; i <= l; i++) { cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); } }
/* test data class 64-bit */ uint32_t HELPER(tcdb)(CPUS390XState *env, uint32_t f1, uint64_t m2) { float64 v1 = env->fregs[f1].d; int neg = float64_is_neg(v1); uint32_t cc = 0; HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __func__, v1, m2, neg); if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) || (float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) || (float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || (float64_is_signaling_nan(v1) && (m2 & (1 << (1-neg))))) { cc = 1; } else if (m2 & (1 << (9-neg))) { /* assume normalized number */ cc = 1; } /* FIXME: denormalized? */ return cc; }
uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, uint64_t cpu_addr) { int cc = 0; HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n", __func__, order_code, r1, cpu_addr); /* Remember: Use "R1 or R1 + 1, whichever is the odd-numbered register" as parameter (input). Status (output) is always R1. */ switch (order_code) { case SIGP_SET_ARCH: /* switch arch */ break; case SIGP_SENSE: /* enumerate CPU status */ if (cpu_addr) { /* XXX implement when SMP comes */ return 3; } env->regs[r1] &= 0xffffffff00000000ULL; cc = 1; break; #if !defined(CONFIG_USER_ONLY) case SIGP_RESTART: qemu_system_reset_request(); cpu_loop_exit(env); break; case SIGP_STOP: qemu_system_shutdown_request(); cpu_loop_exit(env); break; #endif default: /* unknown sigp */ fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code); cc = 3; } return cc; }
void HELPER(sacf)(CPUS390XState *env, uint64_t a1) { HELPER_LOG("%s: %16" PRIx64 "\n", __func__, a1); switch (a1 & 0xf00) { case 0x000: env->psw.mask &= ~PSW_MASK_ASC; env->psw.mask |= PSW_ASC_PRIMARY; break; case 0x100: env->psw.mask &= ~PSW_MASK_ASC; env->psw.mask |= PSW_ASC_SECONDARY; break; case 0x300: env->psw.mask &= ~PSW_MASK_ASC; env->psw.mask |= PSW_ASC_HOME; break; default: qemu_log("unknown sacf mode: %" PRIx64 "\n", a1); program_interrupt(env, PGM_SPECIFICATION, 2); break; } }
/* convert 64-bit int to 64-bit float */ void HELPER(cdgbr)(CPUS390XState *env, uint32_t f1, int64_t v2) { HELPER_LOG("%s: converting %ld to f%d\n", __func__, v2, f1); env->fregs[f1].d = int64_to_float64(v2, &env->fpu_status); }
/* convert 32-bit int to 32-bit float */ void HELPER(cefbr)(CPUS390XState *env, uint32_t f1, int32_t v2) { env->fregs[f1].l.upper = int32_to_float32(v2, &env->fpu_status); HELPER_LOG("%s: converting %d to 0x%d in f%d\n", __func__, v2, env->fregs[f1].l.upper, f1); }
/* raise an exception */ void HELPER(exception)(CPUS390XState *env, uint32_t excp) { HELPER_LOG("%s: exception %d\n", __func__, excp); env->exception_index = excp; cpu_loop_exit(env); }
static uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr) { S390CPU *cpu = s390_env_get_cpu(env); uint32_t r = 0; switch (cc_op) { case CC_OP_CONST0: case CC_OP_CONST1: case CC_OP_CONST2: case CC_OP_CONST3: /* cc_op value _is_ cc */ r = cc_op; break; case CC_OP_LTGT0_32: r = cc_calc_ltgt0_32(dst); break; case CC_OP_LTGT0_64: r = cc_calc_ltgt0_64(dst); break; case CC_OP_LTGT_32: r = cc_calc_ltgt_32(src, dst); break; case CC_OP_LTGT_64: r = cc_calc_ltgt_64(src, dst); break; case CC_OP_LTUGTU_32: r = cc_calc_ltugtu_32(src, dst); break; case CC_OP_LTUGTU_64: r = cc_calc_ltugtu_64(src, dst); break; case CC_OP_TM_32: r = cc_calc_tm_32(src, dst); break; case CC_OP_TM_64: r = cc_calc_tm_64(src, dst); break; case CC_OP_NZ: r = cc_calc_nz(dst); break; case CC_OP_ADD_64: r = cc_calc_add_64(src, dst, vr); break; case CC_OP_ADDU_64: r = cc_calc_addu_64(src, dst, vr); break; case CC_OP_ADDC_64: r = cc_calc_addc_64(src, dst, vr); break; case CC_OP_SUB_64: r = cc_calc_sub_64(src, dst, vr); break; case CC_OP_SUBU_64: r = cc_calc_subu_64(src, dst, vr); break; case CC_OP_SUBB_64: r = cc_calc_subb_64(src, dst, vr); break; case CC_OP_ABS_64: r = cc_calc_abs_64(dst); break; case CC_OP_NABS_64: r = cc_calc_nabs_64(dst); break; case CC_OP_COMP_64: r = cc_calc_comp_64(dst); break; case CC_OP_ADD_32: r = cc_calc_add_32(src, dst, vr); break; case CC_OP_ADDU_32: r = cc_calc_addu_32(src, dst, vr); break; case CC_OP_ADDC_32: r = cc_calc_addc_32(src, dst, vr); break; case CC_OP_SUB_32: r = cc_calc_sub_32(src, dst, vr); break; case CC_OP_SUBU_32: r = cc_calc_subu_32(src, dst, vr); break; case CC_OP_SUBB_32: r = cc_calc_subb_32(src, dst, vr); break; case CC_OP_ABS_32: r = cc_calc_abs_32(dst); break; case CC_OP_NABS_32: r = cc_calc_nabs_32(dst); break; case CC_OP_COMP_32: r = cc_calc_comp_32(dst); break; case CC_OP_ICM: r = cc_calc_icm(src, dst); break; case CC_OP_SLA_32: r = cc_calc_sla_32(src, dst); break; case CC_OP_SLA_64: r = cc_calc_sla_64(src, dst); break; case CC_OP_FLOGR: r = cc_calc_flogr(dst); break; case CC_OP_NZ_F32: r = set_cc_nz_f32(dst); break; case CC_OP_NZ_F64: r = set_cc_nz_f64(dst); break; case CC_OP_NZ_F128: r = set_cc_nz_f128(make_float128(src, dst)); break; default: cpu_abort(CPU(cpu), "Unknown CC operation: %s\n", cc_name(cc_op)); } HELPER_LOG("%s: %15s 0x%016lx 0x%016lx 0x%016lx = %d\n", __func__, cc_name(cc_op), src, dst, vr, r); return r; }