aboutsummaryrefslogtreecommitdiff
path: root/target-s390x
diff options
context:
space:
mode:
Diffstat (limited to 'target-s390x')
-rw-r--r--target-s390x/insn-data.def5
-rw-r--r--target-s390x/translate.c83
2 files changed, 88 insertions, 0 deletions
diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def
index 4478d1c8cf..c80a386c32 100644
--- a/target-s390x/insn-data.def
+++ b/target-s390x/insn-data.def
@@ -500,6 +500,11 @@
C(0xeb1d, RLL, RSY_a, Z, r3_o, sh32, new, r1_32, rll32, 0)
C(0xeb1c, RLLG, RSY_a, Z, r3_o, sh64, r1, 0, rll64, 0)
+/* ROTATE THEN INSERT SELECTED BITS */
+ C(0xec55, RISBG, RIE_f, GIE, 0, r2, r1, 0, risbg, s64)
+ C(0xec5d, RISBHG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0)
+ C(0xec51, RISBLG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0)
+
/* SEARCH STRING */
C(0xb25e, SRST, RRE, Z, r1_o, r2_o, 0, 0, srst, 0)
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index d6f7121a2e..1f8659f4db 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2429,6 +2429,89 @@ static ExitStatus op_ptlb(DisasContext *s, DisasOps *o)
}
#endif
+static ExitStatus op_risbg(DisasContext *s, DisasOps *o)
+{
+ int i3 = get_field(s->fields, i3);
+ int i4 = get_field(s->fields, i4);
+ int i5 = get_field(s->fields, i5);
+ int do_zero = i4 & 0x80;
+ uint64_t mask, imask, pmask;
+ int pos, len, rot;
+
+ /* Adjust the arguments for the specific insn. */
+ switch (s->fields->op2) {
+ case 0x55: /* risbg */
+ i3 &= 63;
+ i4 &= 63;
+ pmask = ~0;
+ break;
+ case 0x5d: /* risbhg */
+ i3 &= 31;
+ i4 &= 31;
+ pmask = 0xffffffff00000000ull;
+ break;
+ case 0x51: /* risblg */
+ i3 &= 31;
+ i4 &= 31;
+ pmask = 0x00000000ffffffffull;
+ break;
+ default:
+ abort();
+ }
+
+ /* MASK is the set of bits to be inserted from R2.
+ Take care for I3/I4 wraparound. */
+ mask = pmask >> i3;
+ if (i3 <= i4) {
+ mask ^= pmask >> i4 >> 1;
+ } else {
+ mask |= ~(pmask >> i4 >> 1);
+ }
+ mask &= pmask;
+
+ /* IMASK is the set of bits to be kept from R1. In the case of the high/low
+ insns, we need to keep the other half of the register. */
+ imask = ~mask | ~pmask;
+ if (do_zero) {
+ if (s->fields->op2 == 0x55) {
+ imask = 0;
+ } else {
+ imask = ~pmask;
+ }
+ }
+
+ /* In some cases we can implement this with deposit, which can be more
+ efficient on some hosts. */
+ if (~mask == imask && i3 <= i4) {
+ if (s->fields->op2 == 0x5d) {
+ i3 += 32, i4 += 32;
+ }
+ /* Note that we rotate the bits to be inserted to the lsb, not to
+ the position as described in the PoO. */
+ len = i4 - i3 + 1;
+ pos = 63 - i4;
+ rot = (i5 - pos) & 63;
+ } else {
+ pos = len = -1;
+ rot = i5 & 63;
+ }
+
+ /* Rotate the input as necessary. */
+ tcg_gen_rotli_i64(o->in2, o->in2, rot);
+
+ /* Insert the selected bits into the output. */
+ if (pos >= 0) {
+ tcg_gen_deposit_i64(o->out, o->out, o->in2, pos, len);
+ } else if (imask == 0) {
+ tcg_gen_andi_i64(o->out, o->in2, mask);
+ } else {
+ tcg_gen_andi_i64(o->in2, o->in2, mask);
+ tcg_gen_andi_i64(o->out, o->out, imask);
+ tcg_gen_or_i64(o->out, o->out, o->in2);
+ }
+ return NO_EXIT;
+}
+
static ExitStatus op_rev16(DisasContext *s, DisasOps *o)
{
tcg_gen_bswap16_i64(o->out, o->in2);