target-hppa: Implement basic arithmetic

Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2016-12-15 13:37:23 -08:00
parent 129e9cc3a1
commit b2167459ae
3 changed files with 907 additions and 0 deletions

View File

@ -1,3 +1,5 @@
DEF_HELPER_2(excp, noreturn, env, int)
DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl)
DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl)
DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env)

View File

@ -31,6 +31,29 @@ void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp)
cpu_loop_exit(cs);
}
static void QEMU_NORETURN dynexcp(CPUHPPAState *env, int excp, uintptr_t ra)
{
HPPACPU *cpu = hppa_env_get_cpu(env);
CPUState *cs = CPU(cpu);
cs->exception_index = excp;
cpu_loop_exit_restore(cs, ra);
}
void HELPER(tsv)(CPUHPPAState *env, target_ulong cond)
{
if (unlikely((target_long)cond < 0)) {
dynexcp(env, EXCP_SIGFPE, GETPC());
}
}
void HELPER(tcond)(CPUHPPAState *env, target_ulong cond)
{
if (unlikely(cond)) {
dynexcp(env, EXCP_SIGFPE, GETPC());
}
}
void HELPER(loaded_fr0)(CPUHPPAState *env)
{
uint32_t shadow = env->fr[0] >> 32;

View File

@ -83,6 +83,9 @@ typedef struct DisasInsn {
uint32_t insn, mask;
ExitStatus (*trans)(DisasContext *ctx, uint32_t insn,
const struct DisasInsn *f);
union {
void (*f_ttt)(TCGv, TCGv, TCGv);
};
} DisasInsn;
/* global register indexes */
@ -443,6 +446,870 @@ static void gen_goto_tb(DisasContext *ctx, int which,
}
}
/* PA has a habit of taking the LSB of a field and using that as the sign,
with the rest of the field becoming the least significant bits. */
static target_long low_sextract(uint32_t val, int pos, int len)
{
target_ulong x = -(target_ulong)extract32(val, pos, 1);
x = (x << (len - 1)) | extract32(val, pos + 1, len - 1);
return x;
}
static target_long assemble_16(uint32_t insn)
{
/* Take the name from PA2.0, which produces a 16-bit number
only with wide mode; otherwise a 14-bit number. Since we don't
implement wide mode, this is always the 14-bit number. */
return low_sextract(insn, 0, 14);
}
static target_long assemble_21(uint32_t insn)
{
target_ulong x = -(target_ulong)(insn & 1);
x = (x << 11) | extract32(insn, 1, 11);
x = (x << 2) | extract32(insn, 14, 2);
x = (x << 5) | extract32(insn, 16, 5);
x = (x << 2) | extract32(insn, 12, 2);
return x << 11;
}
/* The parisc documentation describes only the general interpretation of
the conditions, without describing their exact implementation. The
interpretations do not stand up well when considering ADD,C and SUB,B.
However, considering the Addition, Subtraction and Logical conditions
as a whole it would appear that these relations are similar to what
a traditional NZCV set of flags would produce. */
static DisasCond do_cond(unsigned cf, TCGv res, TCGv cb_msb, TCGv sv)
{
DisasCond cond;
TCGv tmp;
switch (cf >> 1) {
case 0: /* Never / TR */
cond = cond_make_f();
break;
case 1: /* = / <> (Z / !Z) */
cond = cond_make_0(TCG_COND_EQ, res);
break;
case 2: /* < / >= (N / !N) */
cond = cond_make_0(TCG_COND_LT, res);
break;
case 3: /* <= / > (N | Z / !N & !Z) */
cond = cond_make_0(TCG_COND_LE, res);
break;
case 4: /* NUV / UV (!C / C) */
cond = cond_make_0(TCG_COND_EQ, cb_msb);
break;
case 5: /* ZNV / VNZ (!C | Z / C & !Z) */
tmp = tcg_temp_new();
tcg_gen_neg_tl(tmp, cb_msb);
tcg_gen_and_tl(tmp, tmp, res);
cond = cond_make_0(TCG_COND_EQ, tmp);
tcg_temp_free(tmp);
break;
case 6: /* SV / NSV (V / !V) */
cond = cond_make_0(TCG_COND_LT, sv);
break;
case 7: /* OD / EV */
tmp = tcg_temp_new();
tcg_gen_andi_tl(tmp, res, 1);
cond = cond_make_0(TCG_COND_NE, tmp);
tcg_temp_free(tmp);
break;
default:
g_assert_not_reached();
}
if (cf & 1) {
cond.c = tcg_invert_cond(cond.c);
}
return cond;
}
/* Similar, but for the special case of subtraction without borrow, we
can use the inputs directly. This can allow other computation to be
deleted as unused. */
static DisasCond do_sub_cond(unsigned cf, TCGv res, TCGv in1, TCGv in2, TCGv sv)
{
DisasCond cond;
switch (cf >> 1) {
case 1: /* = / <> */
cond = cond_make(TCG_COND_EQ, in1, in2);
break;
case 2: /* < / >= */
cond = cond_make(TCG_COND_LT, in1, in2);
break;
case 3: /* <= / > */
cond = cond_make(TCG_COND_LE, in1, in2);
break;
case 4: /* << / >>= */
cond = cond_make(TCG_COND_LTU, in1, in2);
break;
case 5: /* <<= / >> */
cond = cond_make(TCG_COND_LEU, in1, in2);
break;
default:
return do_cond(cf, res, sv, sv);
}
if (cf & 1) {
cond.c = tcg_invert_cond(cond.c);
}
return cond;
}
/* Similar, but for logicals, where the carry and overflow bits are not
computed, and use of them is undefined. */
static DisasCond do_log_cond(unsigned cf, TCGv res)
{
switch (cf >> 1) {
case 4: case 5: case 6:
cf &= 1;
break;
}
return do_cond(cf, res, res, res);
}
/* Similar, but for unit conditions. */
static DisasCond do_unit_cond(unsigned cf, TCGv res, TCGv in1, TCGv in2)
{
DisasCond cond;
TCGv tmp, cb;
TCGV_UNUSED(cb);
if (cf & 8) {
/* Since we want to test lots of carry-out bits all at once, do not
* do our normal thing and compute carry-in of bit B+1 since that
* leaves us with carry bits spread across two words.
*/
cb = tcg_temp_new();
tmp = tcg_temp_new();
tcg_gen_or_tl(cb, in1, in2);
tcg_gen_and_tl(tmp, in1, in2);
tcg_gen_andc_tl(cb, cb, res);
tcg_gen_or_tl(cb, cb, tmp);
tcg_temp_free(tmp);
}
switch (cf >> 1) {
case 0: /* never / TR */
case 1: /* undefined */
case 5: /* undefined */
cond = cond_make_f();
break;
case 2: /* SBZ / NBZ */
/* See hasless(v,1) from
* https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
*/
tmp = tcg_temp_new();
tcg_gen_subi_tl(tmp, res, 0x01010101u);
tcg_gen_andc_tl(tmp, tmp, res);
tcg_gen_andi_tl(tmp, tmp, 0x80808080u);
cond = cond_make_0(TCG_COND_NE, tmp);
tcg_temp_free(tmp);
break;
case 3: /* SHZ / NHZ */
tmp = tcg_temp_new();
tcg_gen_subi_tl(tmp, res, 0x00010001u);
tcg_gen_andc_tl(tmp, tmp, res);
tcg_gen_andi_tl(tmp, tmp, 0x80008000u);
cond = cond_make_0(TCG_COND_NE, tmp);
tcg_temp_free(tmp);
break;
case 4: /* SDC / NDC */
tcg_gen_andi_tl(cb, cb, 0x88888888u);
cond = cond_make_0(TCG_COND_NE, cb);
break;
case 6: /* SBC / NBC */
tcg_gen_andi_tl(cb, cb, 0x80808080u);
cond = cond_make_0(TCG_COND_NE, cb);
break;
case 7: /* SHC / NHC */
tcg_gen_andi_tl(cb, cb, 0x80008000u);
cond = cond_make_0(TCG_COND_NE, cb);
break;
default:
g_assert_not_reached();
}
if (cf & 8) {
tcg_temp_free(cb);
}
if (cf & 1) {
cond.c = tcg_invert_cond(cond.c);
}
return cond;
}
/* Compute signed overflow for addition. */
static TCGv do_add_sv(DisasContext *ctx, TCGv res, TCGv in1, TCGv in2)
{
TCGv sv = get_temp(ctx);
TCGv tmp = tcg_temp_new();
tcg_gen_xor_tl(sv, res, in1);
tcg_gen_xor_tl(tmp, in1, in2);
tcg_gen_andc_tl(sv, sv, tmp);
tcg_temp_free(tmp);
return sv;
}
/* Compute signed overflow for subtraction. */
static TCGv do_sub_sv(DisasContext *ctx, TCGv res, TCGv in1, TCGv in2)
{
TCGv sv = get_temp(ctx);
TCGv tmp = tcg_temp_new();
tcg_gen_xor_tl(sv, res, in1);
tcg_gen_xor_tl(tmp, in1, in2);
tcg_gen_and_tl(sv, sv, tmp);
tcg_temp_free(tmp);
return sv;
}
static ExitStatus do_add(DisasContext *ctx, unsigned rt, TCGv in1, TCGv in2,
unsigned shift, bool is_l, bool is_tsv, bool is_tc,
bool is_c, unsigned cf)
{
TCGv dest, cb, cb_msb, sv, tmp;
unsigned c = cf >> 1;
DisasCond cond;
dest = tcg_temp_new();
TCGV_UNUSED(cb);
TCGV_UNUSED(cb_msb);
if (shift) {
tmp = get_temp(ctx);
tcg_gen_shli_tl(tmp, in1, shift);
in1 = tmp;
}
if (!is_l || c == 4 || c == 5) {
TCGv zero = tcg_const_tl(0);
cb_msb = get_temp(ctx);
tcg_gen_add2_tl(dest, cb_msb, in1, zero, in2, zero);
if (is_c) {
tcg_gen_add2_tl(dest, cb_msb, dest, cb_msb, cpu_psw_cb_msb, zero);
}
tcg_temp_free(zero);
if (!is_l) {
cb = get_temp(ctx);
tcg_gen_xor_tl(cb, in1, in2);
tcg_gen_xor_tl(cb, cb, dest);
}
} else {
tcg_gen_add_tl(dest, in1, in2);
if (is_c) {
tcg_gen_add_tl(dest, dest, cpu_psw_cb_msb);
}
}
/* Compute signed overflow if required. */
TCGV_UNUSED(sv);
if (is_tsv || c == 6) {
sv = do_add_sv(ctx, dest, in1, in2);
if (is_tsv) {
/* ??? Need to include overflow from shift. */
gen_helper_tsv(cpu_env, sv);
}
}
/* Emit any conditional trap before any writeback. */
cond = do_cond(cf, dest, cb_msb, sv);
if (is_tc) {
cond_prep(&cond);
tmp = tcg_temp_new();
tcg_gen_setcond_tl(cond.c, tmp, cond.a0, cond.a1);
gen_helper_tcond(cpu_env, tmp);
tcg_temp_free(tmp);
}
/* Write back the result. */
if (!is_l) {
save_or_nullify(ctx, cpu_psw_cb, cb);
save_or_nullify(ctx, cpu_psw_cb_msb, cb_msb);
}
save_gpr(ctx, rt, dest);
tcg_temp_free(dest);
/* Install the new nullification. */
cond_free(&ctx->null_cond);
ctx->null_cond = cond;
return NO_EXIT;
}
static ExitStatus do_sub(DisasContext *ctx, unsigned rt, TCGv in1, TCGv in2,
bool is_tsv, bool is_b, bool is_tc, unsigned cf)
{
TCGv dest, sv, cb, cb_msb, zero, tmp;
unsigned c = cf >> 1;
DisasCond cond;
dest = tcg_temp_new();
cb = tcg_temp_new();
cb_msb = tcg_temp_new();
zero = tcg_const_tl(0);
if (is_b) {
/* DEST,C = IN1 + ~IN2 + C. */
tcg_gen_not_tl(cb, in2);
tcg_gen_add2_tl(dest, cb_msb, in1, zero, cpu_psw_cb_msb, zero);
tcg_gen_add2_tl(dest, cb_msb, dest, cb_msb, cb, zero);
tcg_gen_xor_tl(cb, cb, in1);
tcg_gen_xor_tl(cb, cb, dest);
} else {
/* DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer
operations by seeding the high word with 1 and subtracting. */
tcg_gen_movi_tl(cb_msb, 1);
tcg_gen_sub2_tl(dest, cb_msb, in1, cb_msb, in2, zero);
tcg_gen_eqv_tl(cb, in1, in2);
tcg_gen_xor_tl(cb, cb, dest);
}
tcg_temp_free(zero);
/* Compute signed overflow if required. */
TCGV_UNUSED(sv);
if (is_tsv || c == 6) {
sv = do_sub_sv(ctx, dest, in1, in2);
if (is_tsv) {
gen_helper_tsv(cpu_env, sv);
}
}
/* Compute the condition. We cannot use the special case for borrow. */
if (!is_b) {
cond = do_sub_cond(cf, dest, in1, in2, sv);
} else {
cond = do_cond(cf, dest, cb_msb, sv);
}
/* Emit any conditional trap before any writeback. */
if (is_tc) {
cond_prep(&cond);
tmp = tcg_temp_new();
tcg_gen_setcond_tl(cond.c, tmp, cond.a0, cond.a1);
gen_helper_tcond(cpu_env, tmp);
tcg_temp_free(tmp);
}
/* Write back the result. */
save_or_nullify(ctx, cpu_psw_cb, cb);
save_or_nullify(ctx, cpu_psw_cb_msb, cb_msb);
save_gpr(ctx, rt, dest);
tcg_temp_free(dest);
/* Install the new nullification. */
cond_free(&ctx->null_cond);
ctx->null_cond = cond;
return NO_EXIT;
}
static ExitStatus do_cmpclr(DisasContext *ctx, unsigned rt, TCGv in1,
TCGv in2, unsigned cf)
{
TCGv dest, sv;
DisasCond cond;
dest = tcg_temp_new();
tcg_gen_sub_tl(dest, in1, in2);
/* Compute signed overflow if required. */
TCGV_UNUSED(sv);
if ((cf >> 1) == 6) {
sv = do_sub_sv(ctx, dest, in1, in2);
}
/* Form the condition for the compare. */
cond = do_sub_cond(cf, dest, in1, in2, sv);
/* Clear. */
tcg_gen_movi_tl(dest, 0);
save_gpr(ctx, rt, dest);
tcg_temp_free(dest);
/* Install the new nullification. */
cond_free(&ctx->null_cond);
ctx->null_cond = cond;
return NO_EXIT;
}
static ExitStatus do_log(DisasContext *ctx, unsigned rt, TCGv in1, TCGv in2,
unsigned cf, void (*fn)(TCGv, TCGv, TCGv))
{
TCGv dest = dest_gpr(ctx, rt);
/* Perform the operation, and writeback. */
fn(dest, in1, in2);
save_gpr(ctx, rt, dest);
/* Install the new nullification. */
cond_free(&ctx->null_cond);
if (cf) {
ctx->null_cond = do_log_cond(cf, dest);
}
return NO_EXIT;
}
static ExitStatus do_unit(DisasContext *ctx, unsigned rt, TCGv in1,
TCGv in2, unsigned cf, bool is_tc,
void (*fn)(TCGv, TCGv, TCGv))
{
TCGv dest;
DisasCond cond;
if (cf == 0) {
dest = dest_gpr(ctx, rt);
fn(dest, in1, in2);
save_gpr(ctx, rt, dest);
cond_free(&ctx->null_cond);
} else {
dest = tcg_temp_new();
fn(dest, in1, in2);
cond = do_unit_cond(cf, dest, in1, in2);
if (is_tc) {
TCGv tmp = tcg_temp_new();
cond_prep(&cond);
tcg_gen_setcond_tl(cond.c, tmp, cond.a0, cond.a1);
gen_helper_tcond(cpu_env, tmp);
tcg_temp_free(tmp);
}
save_gpr(ctx, rt, dest);
cond_free(&ctx->null_cond);
ctx->null_cond = cond;
}
return NO_EXIT;
}
static ExitStatus trans_nop(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
cond_free(&ctx->null_cond);
return NO_EXIT;
}
static ExitStatus trans_add(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned ext = extract32(insn, 8, 4);
unsigned shift = extract32(insn, 6, 2);
unsigned rt = extract32(insn, 0, 5);
TCGv tcg_r1, tcg_r2;
bool is_c = false;
bool is_l = false;
bool is_tc = false;
bool is_tsv = false;
ExitStatus ret;
switch (ext) {
case 0x6: /* ADD, SHLADD */
break;
case 0xa: /* ADD,L, SHLADD,L */
is_l = true;
break;
case 0xe: /* ADD,TSV, SHLADD,TSV (1) */
is_tsv = true;
break;
case 0x7: /* ADD,C */
is_c = true;
break;
case 0xf: /* ADD,C,TSV */
is_c = is_tsv = true;
break;
default:
return gen_illegal(ctx);
}
if (cf) {
nullify_over(ctx);
}
tcg_r1 = load_gpr(ctx, r1);
tcg_r2 = load_gpr(ctx, r2);
ret = do_add(ctx, rt, tcg_r1, tcg_r2, shift, is_l, is_tsv, is_tc, is_c, cf);
return nullify_end(ctx, ret);
}
static ExitStatus trans_sub(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned ext = extract32(insn, 6, 6);
unsigned rt = extract32(insn, 0, 5);
TCGv tcg_r1, tcg_r2;
bool is_b = false;
bool is_tc = false;
bool is_tsv = false;
ExitStatus ret;
switch (ext) {
case 0x10: /* SUB */
break;
case 0x30: /* SUB,TSV */
is_tsv = true;
break;
case 0x14: /* SUB,B */
is_b = true;
break;
case 0x34: /* SUB,B,TSV */
is_b = is_tsv = true;
break;
case 0x13: /* SUB,TC */
is_tc = true;
break;
case 0x33: /* SUB,TSV,TC */
is_tc = is_tsv = true;
break;
default:
return gen_illegal(ctx);
}
if (cf) {
nullify_over(ctx);
}
tcg_r1 = load_gpr(ctx, r1);
tcg_r2 = load_gpr(ctx, r2);
ret = do_sub(ctx, rt, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, cf);
return nullify_end(ctx, ret);
}
static ExitStatus trans_log(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 0, 5);
TCGv tcg_r1, tcg_r2;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_r1 = load_gpr(ctx, r1);
tcg_r2 = load_gpr(ctx, r2);
ret = do_log(ctx, rt, tcg_r1, tcg_r2, cf, di->f_ttt);
return nullify_end(ctx, ret);
}
/* OR r,0,t -> COPY (according to gas) */
static ExitStatus trans_copy(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r1 = extract32(insn, 16, 5);
unsigned rt = extract32(insn, 0, 5);
if (r1 == 0) {
TCGv dest = dest_gpr(ctx, rt);
tcg_gen_movi_tl(dest, 0);
save_gpr(ctx, rt, dest);
} else {
save_gpr(ctx, rt, cpu_gr[r1]);
}
cond_free(&ctx->null_cond);
return NO_EXIT;
}
static ExitStatus trans_cmpclr(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 0, 5);
TCGv tcg_r1, tcg_r2;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_r1 = load_gpr(ctx, r1);
tcg_r2 = load_gpr(ctx, r2);
ret = do_cmpclr(ctx, rt, tcg_r1, tcg_r2, cf);
return nullify_end(ctx, ret);
}
static ExitStatus trans_uxor(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 0, 5);
TCGv tcg_r1, tcg_r2;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_r1 = load_gpr(ctx, r1);
tcg_r2 = load_gpr(ctx, r2);
ret = do_unit(ctx, rt, tcg_r1, tcg_r2, cf, false, tcg_gen_xor_tl);
return nullify_end(ctx, ret);
}
static ExitStatus trans_uaddcm(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned is_tc = extract32(insn, 6, 1);
unsigned rt = extract32(insn, 0, 5);
TCGv tcg_r1, tcg_r2, tmp;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_r1 = load_gpr(ctx, r1);
tcg_r2 = load_gpr(ctx, r2);
tmp = get_temp(ctx);
tcg_gen_not_tl(tmp, tcg_r2);
ret = do_unit(ctx, rt, tcg_r1, tmp, cf, is_tc, tcg_gen_add_tl);
return nullify_end(ctx, ret);
}
static ExitStatus trans_dcor(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned is_i = extract32(insn, 6, 1);
unsigned rt = extract32(insn, 0, 5);
TCGv tmp;
ExitStatus ret;
nullify_over(ctx);
tmp = get_temp(ctx);
tcg_gen_shri_tl(tmp, cpu_psw_cb, 3);
if (!is_i) {
tcg_gen_not_tl(tmp, tmp);
}
tcg_gen_andi_tl(tmp, tmp, 0x11111111);
tcg_gen_muli_tl(tmp, tmp, 6);
ret = do_unit(ctx, rt, tmp, load_gpr(ctx, r2), cf, false,
is_i ? tcg_gen_add_tl : tcg_gen_sub_tl);
return nullify_end(ctx, ret);
}
static ExitStatus trans_ds(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
unsigned r2 = extract32(insn, 21, 5);
unsigned r1 = extract32(insn, 16, 5);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 0, 5);
TCGv dest, add1, add2, addc, zero, in1, in2;
nullify_over(ctx);
in1 = load_gpr(ctx, r1);
in2 = load_gpr(ctx, r2);
add1 = tcg_temp_new();
add2 = tcg_temp_new();
addc = tcg_temp_new();
dest = tcg_temp_new();
zero = tcg_const_tl(0);
/* Form R1 << 1 | PSW[CB]{8}. */
tcg_gen_add_tl(add1, in1, in1);
tcg_gen_add_tl(add1, add1, cpu_psw_cb_msb);
/* Add or subtract R2, depending on PSW[V]. Proper computation of
carry{8} requires that we subtract via + ~R2 + 1, as described in
the manual. By extracting and masking V, we can produce the
proper inputs to the addition without movcond. */
tcg_gen_sari_tl(addc, cpu_psw_v, TARGET_LONG_BITS - 1);
tcg_gen_xor_tl(add2, in2, addc);
tcg_gen_andi_tl(addc, addc, 1);
/* ??? This is only correct for 32-bit. */
tcg_gen_add2_i32(dest, cpu_psw_cb_msb, add1, zero, add2, zero);
tcg_gen_add2_i32(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero);
tcg_temp_free(addc);
tcg_temp_free(zero);
/* Write back the result register. */
save_gpr(ctx, rt, dest);
/* Write back PSW[CB]. */
tcg_gen_xor_tl(cpu_psw_cb, add1, add2);
tcg_gen_xor_tl(cpu_psw_cb, cpu_psw_cb, dest);
/* Write back PSW[V] for the division step. */
tcg_gen_neg_tl(cpu_psw_v, cpu_psw_cb_msb);
tcg_gen_xor_tl(cpu_psw_v, cpu_psw_v, in2);
/* Install the new nullification. */
if (cf) {
TCGv sv;
TCGV_UNUSED(sv);
if (cf >> 1 == 6) {
/* ??? The lshift is supposed to contribute to overflow. */
sv = do_add_sv(ctx, dest, add1, add2);
}
ctx->null_cond = do_cond(cf, dest, cpu_psw_cb_msb, sv);
}
tcg_temp_free(add1);
tcg_temp_free(add2);
tcg_temp_free(dest);
return nullify_end(ctx, NO_EXIT);
}
static const DisasInsn table_arith_log[] = {
{ 0x08000240u, 0xfc00ffffu, trans_nop }, /* or x,y,0 */
{ 0x08000240u, 0xffe0ffe0u, trans_copy }, /* or x,0,t */
{ 0x08000000u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_andc_tl },
{ 0x08000200u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_and_tl },
{ 0x08000240u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_or_tl },
{ 0x08000280u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_xor_tl },
{ 0x08000880u, 0xfc000fe0u, trans_cmpclr },
{ 0x08000380u, 0xfc000fe0u, trans_uxor },
{ 0x08000980u, 0xfc000fa0u, trans_uaddcm },
{ 0x08000b80u, 0xfc1f0fa0u, trans_dcor },
{ 0x08000440u, 0xfc000fe0u, trans_ds },
{ 0x08000700u, 0xfc0007e0u, trans_add }, /* add */
{ 0x08000400u, 0xfc0006e0u, trans_sub }, /* sub; sub,b; sub,tsv */
{ 0x080004c0u, 0xfc0007e0u, trans_sub }, /* sub,tc; sub,tsv,tc */
{ 0x08000200u, 0xfc000320u, trans_add }, /* shladd */
};
static ExitStatus trans_addi(DisasContext *ctx, uint32_t insn)
{
target_long im = low_sextract(insn, 0, 11);
unsigned e1 = extract32(insn, 11, 1);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 16, 5);
unsigned r2 = extract32(insn, 21, 5);
unsigned o1 = extract32(insn, 26, 1);
TCGv tcg_im, tcg_r2;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_im = load_const(ctx, im);
tcg_r2 = load_gpr(ctx, r2);
ret = do_add(ctx, rt, tcg_im, tcg_r2, 0, false, e1, !o1, false, cf);
return nullify_end(ctx, ret);
}
static ExitStatus trans_subi(DisasContext *ctx, uint32_t insn)
{
target_long im = low_sextract(insn, 0, 11);
unsigned e1 = extract32(insn, 11, 1);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 16, 5);
unsigned r2 = extract32(insn, 21, 5);
TCGv tcg_im, tcg_r2;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_im = load_const(ctx, im);
tcg_r2 = load_gpr(ctx, r2);
ret = do_sub(ctx, rt, tcg_im, tcg_r2, e1, false, false, cf);
return nullify_end(ctx, ret);
}
static ExitStatus trans_cmpiclr(DisasContext *ctx, uint32_t insn)
{
target_long im = low_sextract(insn, 0, 11);
unsigned cf = extract32(insn, 12, 4);
unsigned rt = extract32(insn, 16, 5);
unsigned r2 = extract32(insn, 21, 5);
TCGv tcg_im, tcg_r2;
ExitStatus ret;
if (cf) {
nullify_over(ctx);
}
tcg_im = load_const(ctx, im);
tcg_r2 = load_gpr(ctx, r2);
ret = do_cmpclr(ctx, rt, tcg_im, tcg_r2, cf);
return nullify_end(ctx, ret);
}
static ExitStatus trans_ldil(DisasContext *ctx, uint32_t insn)
{
unsigned rt = extract32(insn, 21, 5);
target_long i = assemble_21(insn);
TCGv tcg_rt = dest_gpr(ctx, rt);
tcg_gen_movi_tl(tcg_rt, i);
save_gpr(ctx, rt, tcg_rt);
cond_free(&ctx->null_cond);
return NO_EXIT;
}
static ExitStatus trans_addil(DisasContext *ctx, uint32_t insn)
{
unsigned rt = extract32(insn, 21, 5);
target_long i = assemble_21(insn);
TCGv tcg_rt = load_gpr(ctx, rt);
TCGv tcg_r1 = dest_gpr(ctx, 1);
tcg_gen_addi_tl(tcg_r1, tcg_rt, i);
save_gpr(ctx, 1, tcg_r1);
cond_free(&ctx->null_cond);
return NO_EXIT;
}
static ExitStatus trans_ldo(DisasContext *ctx, uint32_t insn)
{
unsigned rb = extract32(insn, 21, 5);
unsigned rt = extract32(insn, 16, 5);
target_long i = assemble_16(insn);
TCGv tcg_rt = dest_gpr(ctx, rt);
/* Special case rb == 0, for the LDI pseudo-op.
The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */
if (rb == 0) {
tcg_gen_movi_tl(tcg_rt, i);
} else {
tcg_gen_addi_tl(tcg_rt, cpu_gr[rb], i);
}
save_gpr(ctx, rt, tcg_rt);
cond_free(&ctx->null_cond);
return NO_EXIT;
}
static ExitStatus translate_table_int(DisasContext *ctx, uint32_t insn,
const DisasInsn table[], size_t n)
{
@ -463,6 +1330,21 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
uint32_t opc = extract32(insn, 26, 6);
switch (opc) {
case 0x02:
return translate_table(ctx, insn, table_arith_log);
case 0x08:
return trans_ldil(ctx, insn);
case 0x0A:
return trans_addil(ctx, insn);
case 0x0D:
return trans_ldo(ctx, insn);
case 0x24:
return trans_cmpiclr(ctx, insn);
case 0x25:
return trans_subi(ctx, insn);
case 0x2C:
case 0x2D:
return trans_addi(ctx, insn);
default:
break;
}