diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index a92dc352cb..a072d0bb63 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -285,14 +285,20 @@ void hppa_translate_init(void); #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU +static inline uint64_t gva_offset_mask(target_ulong psw) +{ + return (psw & PSW_W + ? MAKE_64BIT_MASK(0, 62) + : MAKE_64BIT_MASK(0, 32)); +} + static inline target_ulong hppa_form_gva_psw(target_ulong psw, uint64_t spc, target_ulong off) { #ifdef CONFIG_USER_ONLY return off; #else - off &= psw & PSW_W ? MAKE_64BIT_MASK(0, 62) : MAKE_64BIT_MASK(0, 32); - return spc | off; + return spc | (off & gva_offset_mask(psw)); #endif } diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 1bdbcd8f98..5900fd70bc 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -86,12 +86,10 @@ DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tl) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(halt, noreturn, env) DEF_HELPER_1(reset, noreturn, env) -DEF_HELPER_1(getshadowregs, void, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(rfi_r, void, env) DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_2(write_eiem, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tl, env, tl) DEF_HELPER_FLAGS_3(itlba_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_3(itlbp_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index f58455dfdb..71074a64c1 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -57,11 +57,16 @@ %neg_to_m 0:1 !function=neg_to_m %a_to_m 2:1 !function=neg_to_m %cmpbid_c 13:2 !function=cmpbid_c +%d_5 5:1 !function=pa20_d +%d_11 11:1 !function=pa20_d +%d_13 13:1 !function=pa20_d #### # Argument set definitions #### +&empty + # All insns that need to form a virtual address should use this set. &ldst t b x disp sp m scale size @@ -84,15 +89,16 @@ # Format definitions #### -@rr_cf_d ...... r:5 ..... cf:4 ...... d:1 t:5 &rr_cf_d +@rr_cf_d ...... r:5 ..... cf:4 ...... . t:5 &rr_cf_d d=%d_5 @rrr ...... r2:5 r1:5 .... ....... t:5 &rrr @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf -@rrr_cf_d ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d +@rrr_cf_d ...... r2:5 r1:5 cf:4 ...... . t:5 &rrr_cf_d d=%d_5 @rrr_sh ...... r2:5 r1:5 ........ sh:2 . t:5 &rrr_sh -@rrr_cf_d_sh ...... r2:5 r1:5 cf:4 .... sh:2 d:1 t:5 &rrr_cf_d_sh -@rrr_cf_d_sh0 ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d_sh sh=0 +@rrr_cf_d_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_d_sh d=%d_5 +@rrr_cf_d_sh0 ...... r2:5 r1:5 cf:4 ...... . t:5 &rrr_cf_d_sh d=%d_5 sh=0 @rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 -@rri_cf_d ...... r:5 t:5 cf:4 d:1 ........... &rri_cf_d i=%lowsign_11 +@rri_cf_d ...... r:5 t:5 cf:4 . ........... \ + &rri_cf_d d=%d_11 i=%lowsign_11 @rrb_cf ...... r2:5 r1:5 c:3 ........... n:1 . \ &rrb_c_f disp=%assemble_12 @@ -368,8 +374,10 @@ fmpysub_d 100110 ..... ..... ..... ..... 1 ..... @mpyadd # Conditional Branches #### -bb_sar 110000 00000 r:5 c:1 1 d:1 ........... n:1 . disp=%assemble_12 -bb_imm 110001 p:5 r:5 c:1 1 d:1 ........... n:1 . disp=%assemble_12 +bb_sar 110000 00000 r:5 c:1 1 . ........... n:1 . \ + disp=%assemble_12 d=%d_13 +bb_imm 110001 p:5 r:5 c:1 1 . ........... n:1 . \ + disp=%assemble_12 d=%d_13 movb 110010 ..... ..... ... ........... . . @rrb_cf f=0 movbi 110011 ..... ..... ... ........... . . @rib_cf f=0 @@ -628,4 +636,18 @@ fdiv_d 001110 ..... ..... 011 ..... ... ..... @f0e_d_3 xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64 # diag -diag 000101 i:26 +{ + [ + diag_btlb 000101 00 0000 0000 0000 0001 0000 0000 + diag_cout 000101 00 0000 0000 0000 0001 0000 0001 + + # For 32-bit PA-7300LC (PCX-L2) + diag_getshadowregs_pa1 000101 00 0000 0000 0001 1010 0000 0000 + diag_putshadowregs_pa1 000101 00 0000 0000 0001 1010 0100 0000 + + # For 64-bit PA8700 (PCX-W2) + diag_getshadowregs_pa2 000101 00 0111 1000 0001 1000 0100 0000 + diag_putshadowregs_pa2 000101 00 0111 0000 0001 1000 0100 0000 + ] + diag_unimp 000101 i:26 +} diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index efe638b36e..90437a92cd 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -28,7 +28,7 @@ static void eval_interrupt(HPPACPU *cpu) { CPUState *cs = CPU(cpu); - if (cpu->env.cr[CR_EIRR] & cpu->env.cr[CR_EIEM]) { + if (cpu->env.cr[CR_EIRR]) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -89,14 +89,6 @@ void HELPER(write_eirr)(CPUHPPAState *env, target_ulong val) bql_unlock(); } -void HELPER(write_eiem)(CPUHPPAState *env, target_ulong val) -{ - env->cr[CR_EIEM] = val; - bql_lock(); - eval_interrupt(env_archcpu(env)); - bql_unlock(); -} - void hppa_cpu_do_interrupt(CPUState *cs) { HPPACPU *cpu = HPPA_CPU(cs); @@ -280,7 +272,9 @@ bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } /* If interrupts are requested and enabled, raise them. */ - if ((env->psw & PSW_I) && (interrupt_request & CPU_INTERRUPT_HARD)) { + if ((interrupt_request & CPU_INTERRUPT_HARD) + && (env->psw & PSW_I) + && (env->cr[CR_EIRR] & env->cr[CR_EIEM])) { cs->exception_index = EXCP_EXT_INTERRUPT; hppa_cpu_do_interrupt(cs); return true; diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 4a31748342..208e51c086 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -95,7 +95,7 @@ void HELPER(rfi)(CPUHPPAState *env) cpu_hppa_put_psw(env, env->cr[CR_IPSW]); } -void HELPER(getshadowregs)(CPUHPPAState *env) +static void getshadowregs(CPUHPPAState *env) { env->gr[1] = env->shadow[0]; env->gr[8] = env->shadow[1]; @@ -108,7 +108,7 @@ void HELPER(getshadowregs)(CPUHPPAState *env) void HELPER(rfi_r)(CPUHPPAState *env) { - helper_getshadowregs(env); + getshadowregs(env); helper_rfi(env); } diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 19594f917e..8a1a8bc3aa 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -200,6 +200,14 @@ static int cmpbid_c(DisasContext *ctx, int val) return val ? val : 4; /* 0 == "*<<" */ } +/* + * In many places pa1.x did not decode the bit that later became + * the pa2.0 D bit. Suppress D unless the cpu is pa2.0. + */ +static int pa20_d(DisasContext *ctx, int val) +{ + return ctx->is_pa20 & val; +} /* Include the auto-generated decoder. */ #include "decode-insns.c.inc" @@ -586,17 +594,10 @@ static bool nullify_end(DisasContext *ctx) return true; } -static uint64_t gva_offset_mask(DisasContext *ctx) -{ - return (ctx->tb_flags & PSW_W - ? MAKE_64BIT_MASK(0, 62) - : MAKE_64BIT_MASK(0, 32)); -} - static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, uint64_t ival, TCGv_i64 vval) { - uint64_t mask = gva_offset_mask(ctx); + uint64_t mask = gva_offset_mask(ctx->tb_flags); if (ival != -1) { tcg_gen_movi_i64(dest, ival & mask); @@ -700,19 +701,13 @@ static bool cond_need_cb(int c) return c == 4 || c == 5; } -/* Need extensions from TCGv_i32 to TCGv_i64. */ -static bool cond_need_ext(DisasContext *ctx, bool d) -{ - return !(ctx->is_pa20 && d); -} - /* * Compute conditional for arithmetic. See Page 5-3, Table 5-1, of * the Parisc 1.1 Architecture Reference Manual for details. */ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, - TCGv_i64 res, TCGv_i64 cb_msb, TCGv_i64 sv) + TCGv_i64 res, TCGv_i64 uv, TCGv_i64 sv) { DisasCond cond; TCGv_i64 tmp; @@ -722,7 +717,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ - if (cond_need_ext(ctx, d)) { + if (!d) { tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(tmp, res); res = tmp; @@ -732,7 +727,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, res, sv); - if (cond_need_ext(ctx, d)) { + if (!d) { tcg_gen_ext32s_i64(tmp, tmp); } cond = cond_make_0_tmp(TCG_COND_LT, tmp); @@ -749,7 +744,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, */ tmp = tcg_temp_new_i64(); tcg_gen_eqv_i64(tmp, res, sv); - if (cond_need_ext(ctx, d)) { + if (!d) { tcg_gen_sextract_i64(tmp, tmp, 31, 1); tcg_gen_and_i64(tmp, tmp, res); tcg_gen_ext32u_i64(tmp, tmp); @@ -759,21 +754,19 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; - case 4: /* NUV / UV (!C / C) */ - /* Only bit 0 of cb_msb is ever set. */ - cond = cond_make_0(TCG_COND_EQ, cb_msb); + case 4: /* NUV / UV (!UV / UV) */ + cond = cond_make_0(TCG_COND_EQ, uv); break; - case 5: /* ZNV / VNZ (!C | Z / C & !Z) */ + case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */ tmp = tcg_temp_new_i64(); - tcg_gen_neg_i64(tmp, cb_msb); - tcg_gen_and_i64(tmp, tmp, res); - if (cond_need_ext(ctx, d)) { + tcg_gen_movcond_i64(TCG_COND_EQ, tmp, uv, ctx->zero, ctx->zero, res); + if (!d) { tcg_gen_ext32u_i64(tmp, tmp); } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 6: /* SV / NSV (V / !V) */ - if (cond_need_ext(ctx, d)) { + if (!d) { tmp = tcg_temp_new_i64(); tcg_gen_ext32s_i64(tmp, sv); sv = tmp; @@ -834,7 +827,7 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, if (cf & 1) { tc = tcg_invert_cond(tc); } - if (cond_need_ext(ctx, d)) { + if (!d) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -911,7 +904,7 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, g_assert_not_reached(); } - if (cond_need_ext(ctx, d)) { + if (!d) { TCGv_i64 tmp = tcg_temp_new_i64(); if (ext_uns) { @@ -943,83 +936,50 @@ static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, bool d, return do_log_cond(ctx, c * 2 + f, d, res); } -/* Similar, but for unit conditions. */ - -static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_i64 res, - TCGv_i64 in1, TCGv_i64 in2) +/* Similar, but for unit zero conditions. */ +static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res) { - DisasCond cond; - TCGv_i64 tmp, cb = NULL; + TCGv_i64 tmp; uint64_t d_repl = d ? 0x0000000100000001ull : 1; - - 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_i64(); - tmp = tcg_temp_new_i64(); - tcg_gen_or_i64(cb, in1, in2); - tcg_gen_and_i64(tmp, in1, in2); - tcg_gen_andc_i64(cb, cb, res); - tcg_gen_or_i64(cb, cb, tmp); - } + uint64_t ones = 0, sgns = 0; switch (cf >> 1) { - case 0: /* never / TR */ - case 1: /* undefined */ - case 5: /* undefined */ - cond = cond_make_f(); + case 1: /* SBW / NBW */ + if (d) { + ones = d_repl; + sgns = d_repl << 31; + } break; - case 2: /* SBZ / NBZ */ - /* See hasless(v,1) from - * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord - */ - tmp = tcg_temp_new_i64(); - tcg_gen_subi_i64(tmp, res, d_repl * 0x01010101u); - tcg_gen_andc_i64(tmp, tmp, res); - tcg_gen_andi_i64(tmp, tmp, d_repl * 0x80808080u); - cond = cond_make_0(TCG_COND_NE, tmp); + ones = d_repl * 0x01010101u; + sgns = ones << 7; break; - case 3: /* SHZ / NHZ */ - tmp = tcg_temp_new_i64(); - tcg_gen_subi_i64(tmp, res, d_repl * 0x00010001u); - tcg_gen_andc_i64(tmp, tmp, res); - tcg_gen_andi_i64(tmp, tmp, d_repl * 0x80008000u); - cond = cond_make_0(TCG_COND_NE, tmp); + ones = d_repl * 0x00010001u; + sgns = ones << 15; break; - - case 4: /* SDC / NDC */ - tcg_gen_andi_i64(cb, cb, d_repl * 0x88888888u); - cond = cond_make_0(TCG_COND_NE, cb); - break; - - case 6: /* SBC / NBC */ - tcg_gen_andi_i64(cb, cb, d_repl * 0x80808080u); - cond = cond_make_0(TCG_COND_NE, cb); - break; - - case 7: /* SHC / NHC */ - tcg_gen_andi_i64(cb, cb, d_repl * 0x80008000u); - cond = cond_make_0(TCG_COND_NE, cb); - break; - - default: - g_assert_not_reached(); } - if (cf & 1) { - cond.c = tcg_invert_cond(cond.c); + if (ones == 0) { + /* Undefined, or 0/1 (never/always). */ + return cf & 1 ? cond_make_t() : cond_make_f(); } - return cond; + /* + * See hasless(v,1) from + * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + */ + tmp = tcg_temp_new_i64(); + tcg_gen_subi_i64(tmp, res, ones); + tcg_gen_andc_i64(tmp, tmp, res); + tcg_gen_andi_i64(tmp, tmp, sgns); + + return cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp); } static TCGv_i64 get_carry(DisasContext *ctx, bool d, TCGv_i64 cb, TCGv_i64 cb_msb) { - if (cond_need_ext(ctx, d)) { + if (!d) { TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_extract_i64(t, cb, 32, 1); return t; @@ -1034,7 +994,8 @@ static TCGv_i64 get_psw_carry(DisasContext *ctx, bool d) /* Compute signed overflow for addition. */ static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res, - TCGv_i64 in1, TCGv_i64 in2) + TCGv_i64 in1, TCGv_i64 in2, + TCGv_i64 orig_in1, int shift, bool d) { TCGv_i64 sv = tcg_temp_new_i64(); TCGv_i64 tmp = tcg_temp_new_i64(); @@ -1043,9 +1004,49 @@ static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res, tcg_gen_xor_i64(tmp, in1, in2); tcg_gen_andc_i64(sv, sv, tmp); + switch (shift) { + case 0: + break; + case 1: + /* Shift left by one and compare the sign. */ + tcg_gen_add_i64(tmp, orig_in1, orig_in1); + tcg_gen_xor_i64(tmp, tmp, orig_in1); + /* Incorporate into the overflow. */ + tcg_gen_or_i64(sv, sv, tmp); + break; + default: + { + int sign_bit = d ? 63 : 31; + + /* Compare the sign against all lower bits. */ + tcg_gen_sextract_i64(tmp, orig_in1, sign_bit, 1); + tcg_gen_xor_i64(tmp, tmp, orig_in1); + /* + * If one of the bits shifting into or through the sign + * differs, then we have overflow. + */ + tcg_gen_extract_i64(tmp, tmp, sign_bit - shift, shift); + tcg_gen_movcond_i64(TCG_COND_NE, sv, tmp, ctx->zero, + tcg_constant_i64(-1), sv); + } + } return sv; } +/* Compute unsigned overflow for addition. */ +static TCGv_i64 do_add_uv(DisasContext *ctx, TCGv_i64 cb, TCGv_i64 cb_msb, + TCGv_i64 in1, int shift, bool d) +{ + if (shift == 0) { + return get_carry(ctx, d, cb, cb_msb); + } else { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_extract_i64(tmp, in1, (d ? 63 : 31) - shift, shift); + tcg_gen_or_i64(tmp, tmp, get_carry(ctx, d, cb, cb_msb)); + return tmp; + } +} + /* Compute signed overflow for subtraction. */ static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, TCGv_i64 in1, TCGv_i64 in2) @@ -1060,19 +1061,19 @@ static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, return sv; } -static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, +static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, TCGv_i64 in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) { - TCGv_i64 dest, cb, cb_msb, cb_cond, sv, tmp; + TCGv_i64 dest, cb, cb_msb, in1, uv, sv, tmp; unsigned c = cf >> 1; DisasCond cond; dest = tcg_temp_new_i64(); cb = NULL; cb_msb = NULL; - cb_cond = NULL; + in1 = orig_in1; if (shift) { tmp = tcg_temp_new_i64(); tcg_gen_shli_i64(tmp, in1, shift); @@ -1090,9 +1091,6 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } tcg_gen_xor_i64(cb, in1, in2); tcg_gen_xor_i64(cb, cb, dest); - if (cond_need_cb(c)) { - cb_cond = get_carry(ctx, d, cb, cb_msb); - } } else { tcg_gen_add_i64(dest, in1, in2); if (is_c) { @@ -1103,15 +1101,23 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, /* Compute signed overflow if required. */ sv = NULL; if (is_tsv || cond_need_sv(c)) { - sv = do_add_sv(ctx, dest, in1, in2); + sv = do_add_sv(ctx, dest, in1, in2, orig_in1, shift, d); if (is_tsv) { - /* ??? Need to include overflow from shift. */ + if (!d) { + tcg_gen_ext32s_i64(sv, sv); + } gen_helper_tsv(tcg_env, sv); } } + /* Compute unsigned overflow if required. */ + uv = NULL; + if (cond_need_cb(c)) { + uv = do_add_uv(ctx, cb, cb_msb, orig_in1, shift, d); + } + /* Emit any conditional trap before any writeback. */ - cond = do_cond(ctx, cf, d, dest, cb_cond, sv); + cond = do_cond(ctx, cf, d, dest, uv, sv); if (is_tc) { tmp = tcg_temp_new_i64(); tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); @@ -1196,6 +1202,9 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, if (is_tsv || cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); if (is_tsv) { + if (!d) { + tcg_gen_ext32s_i64(sv, sv); + } gen_helper_tsv(tcg_env, sv); } } @@ -1310,34 +1319,86 @@ static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, return nullify_end(ctx); } -static void do_unit(DisasContext *ctx, unsigned rt, TCGv_i64 in1, - TCGv_i64 in2, unsigned cf, bool d, bool is_tc, - void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) +static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d, + bool is_tc, bool is_add) { - TCGv_i64 dest; + TCGv_i64 dest = tcg_temp_new_i64(); + uint64_t test_cb = 0; 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_i64(); - fn(dest, in1, in2); - - cond = do_unit_cond(cf, d, dest, in1, in2); - - if (is_tc) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + /* Select which carry-out bits to test. */ + switch (cf >> 1) { + case 4: /* NDC / SDC -- 4-bit carries */ + test_cb = dup_const(MO_8, 0x88); + break; + case 5: /* NWC / SWC -- 32-bit carries */ + if (d) { + test_cb = dup_const(MO_32, INT32_MIN); + } else { + cf &= 1; /* undefined -- map to never/always */ } - save_gpr(ctx, rt, dest); - - cond_free(&ctx->null_cond); - ctx->null_cond = cond; + break; + case 6: /* NBC / SBC -- 8-bit carries */ + test_cb = dup_const(MO_8, INT8_MIN); + break; + case 7: /* NHC / SHC -- 16-bit carries */ + test_cb = dup_const(MO_16, INT16_MIN); + break; } + if (!d) { + test_cb = (uint32_t)test_cb; + } + + if (!test_cb) { + /* No need to compute carries if we don't need to test them. */ + if (is_add) { + tcg_gen_add_i64(dest, in1, in2); + } else { + tcg_gen_sub_i64(dest, in1, in2); + } + cond = do_unit_zero_cond(cf, d, dest); + } else { + TCGv_i64 cb = tcg_temp_new_i64(); + + if (d) { + TCGv_i64 cb_msb = tcg_temp_new_i64(); + if (is_add) { + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); + tcg_gen_xor_i64(cb, in1, in2); + } else { + /* See do_sub, !is_b. */ + TCGv_i64 one = tcg_constant_i64(1); + tcg_gen_sub2_i64(dest, cb_msb, in1, one, in2, ctx->zero); + tcg_gen_eqv_i64(cb, in1, in2); + } + tcg_gen_xor_i64(cb, cb, dest); + tcg_gen_extract2_i64(cb, cb, cb_msb, 1); + } else { + if (is_add) { + tcg_gen_add_i64(dest, in1, in2); + tcg_gen_xor_i64(cb, in1, in2); + } else { + tcg_gen_sub_i64(dest, in1, in2); + tcg_gen_eqv_i64(cb, in1, in2); + } + tcg_gen_xor_i64(cb, cb, dest); + tcg_gen_shri_i64(cb, cb, 1); + } + + tcg_gen_andi_i64(cb, cb, test_cb); + cond = cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb); + } + + if (is_tc) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(tcg_env, tmp); + } + save_gpr(ctx, rt, dest); + + cond_free(&ctx->null_cond); + ctx->null_cond = cond; } #ifndef CONFIG_USER_ONLY @@ -1403,7 +1464,8 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, *pofs = ofs; *pgva = addr = tcg_temp_new_i64(); - tcg_gen_andi_i64(addr, modify <= 0 ? ofs : base, gva_offset_mask(ctx)); + tcg_gen_andi_i64(addr, modify <= 0 ? ofs : base, + gva_offset_mask(ctx->tb_flags)); #ifndef CONFIG_USER_ONLY if (!is_phys) { tcg_gen_or_i64(addr, addr, space_select(ctx, sp, base)); @@ -2055,11 +2117,9 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) nullify_over(ctx); tmp = dest_gpr(ctx, rt); if (translator_io_start(&ctx->base)) { - gen_helper_read_interval_timer(tmp); ctx->base.is_jmp = DISAS_IAQ_N_STALE; - } else { - gen_helper_read_interval_timer(tmp); } + gen_helper_read_interval_timer(tmp); save_gpr(ctx, rt, tmp); return nullify_end(ctx); case 26: @@ -2135,13 +2195,16 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) switch (ctl) { case CR_IT: + if (translator_io_start(&ctx->base)) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } gen_helper_write_interval_timer(tcg_env, reg); break; case CR_EIRR: + /* Helper modifies interrupt lines and is therefore IO. */ + translator_io_start(&ctx->base); gen_helper_write_eirr(tcg_env, reg); - break; - case CR_EIEM: - gen_helper_write_eiem(tcg_env, reg); + /* Exit to re-evaluate interrupts in the main loop. */ ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; break; @@ -2167,6 +2230,10 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) #endif break; + case CR_EIEM: + /* Exit to re-evaluate interrupts in the main loop. */ + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + /* FALLTHRU */ default: tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); break; @@ -2318,14 +2385,37 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a) #endif } -static bool trans_getshadowregs(DisasContext *ctx, arg_getshadowregs *a) +static bool do_getshadowregs(DisasContext *ctx) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); -#ifndef CONFIG_USER_ONLY nullify_over(ctx); - gen_helper_getshadowregs(tcg_env); + tcg_gen_ld_i64(cpu_gr[1], tcg_env, offsetof(CPUHPPAState, shadow[0])); + tcg_gen_ld_i64(cpu_gr[8], tcg_env, offsetof(CPUHPPAState, shadow[1])); + tcg_gen_ld_i64(cpu_gr[9], tcg_env, offsetof(CPUHPPAState, shadow[2])); + tcg_gen_ld_i64(cpu_gr[16], tcg_env, offsetof(CPUHPPAState, shadow[3])); + tcg_gen_ld_i64(cpu_gr[17], tcg_env, offsetof(CPUHPPAState, shadow[4])); + tcg_gen_ld_i64(cpu_gr[24], tcg_env, offsetof(CPUHPPAState, shadow[5])); + tcg_gen_ld_i64(cpu_gr[25], tcg_env, offsetof(CPUHPPAState, shadow[6])); return nullify_end(ctx); -#endif +} + +static bool do_putshadowregs(DisasContext *ctx) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + nullify_over(ctx); + tcg_gen_st_i64(cpu_gr[1], tcg_env, offsetof(CPUHPPAState, shadow[0])); + tcg_gen_st_i64(cpu_gr[8], tcg_env, offsetof(CPUHPPAState, shadow[1])); + tcg_gen_st_i64(cpu_gr[9], tcg_env, offsetof(CPUHPPAState, shadow[2])); + tcg_gen_st_i64(cpu_gr[16], tcg_env, offsetof(CPUHPPAState, shadow[3])); + tcg_gen_st_i64(cpu_gr[17], tcg_env, offsetof(CPUHPPAState, shadow[4])); + tcg_gen_st_i64(cpu_gr[24], tcg_env, offsetof(CPUHPPAState, shadow[5])); + tcg_gen_st_i64(cpu_gr[25], tcg_env, offsetof(CPUHPPAState, shadow[6])); + return nullify_end(ctx); +} + +static bool trans_getshadowregs(DisasContext *ctx, arg_getshadowregs *a) +{ + return do_getshadowregs(ctx); } static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) @@ -2722,14 +2812,24 @@ static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf_d *a) static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a) { - TCGv_i64 tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2, dest; if (a->cf) { nullify_over(ctx); } + tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d, false, tcg_gen_xor_i64); + dest = dest_gpr(ctx, a->t); + + tcg_gen_xor_i64(dest, tcg_r1, tcg_r2); + save_gpr(ctx, a->t, dest); + + cond_free(&ctx->null_cond); + if (a->cf) { + ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); + } + return nullify_end(ctx); } @@ -2737,14 +2837,34 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) { TCGv_i64 tcg_r1, tcg_r2, tmp; - if (a->cf) { - nullify_over(ctx); + if (a->cf == 0) { + tcg_r2 = load_gpr(ctx, a->r2); + tmp = dest_gpr(ctx, a->t); + + if (a->r1 == 0) { + /* UADDCM r0,src,dst is the common idiom for dst = ~src. */ + tcg_gen_not_i64(tmp, tcg_r2); + } else { + /* + * Recall that r1 - r2 == r1 + ~r2 + 1. + * Thus r1 + ~r2 == r1 - r2 - 1, + * which does not require an extra temporary. + */ + tcg_r1 = load_gpr(ctx, a->r1); + tcg_gen_sub_i64(tmp, tcg_r1, tcg_r2); + tcg_gen_subi_i64(tmp, tmp, 1); + } + save_gpr(ctx, a->t, tmp); + cond_free(&ctx->null_cond); + return true; } + + nullify_over(ctx); tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); tmp = tcg_temp_new_i64(); tcg_gen_not_i64(tmp, tcg_r2); - do_unit(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, tcg_gen_add_i64); + do_unit_addsub(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, true); return nullify_end(ctx); } @@ -2765,14 +2885,14 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf_d *a, bool is_i) nullify_over(ctx); tmp = tcg_temp_new_i64(); - tcg_gen_shri_i64(tmp, cpu_psw_cb, 3); + tcg_gen_extract2_i64(tmp, cpu_psw_cb, cpu_psw_cb_msb, 4); if (!is_i) { tcg_gen_not_i64(tmp, tmp); } tcg_gen_andi_i64(tmp, tmp, (uint64_t)0x1111111111111111ull); tcg_gen_muli_i64(tmp, tmp, 6); - do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, a->d, false, - is_i ? tcg_gen_add_i64 : tcg_gen_sub_i64); + do_unit_addsub(ctx, a->t, load_gpr(ctx, a->r), tmp, + a->cf, a->d, false, is_i); return nullify_end(ctx); } @@ -2789,7 +2909,6 @@ static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf_d *a) static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { TCGv_i64 dest, add1, add2, addc, in1, in2; - TCGv_i64 cout; nullify_over(ctx); @@ -2826,19 +2945,23 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) tcg_gen_xor_i64(cpu_psw_cb, add1, add2); tcg_gen_xor_i64(cpu_psw_cb, cpu_psw_cb, dest); - /* Write back PSW[V] for the division step. */ - cout = get_psw_carry(ctx, false); - tcg_gen_neg_i64(cpu_psw_v, cout); + /* + * Write back PSW[V] for the division step. + * Shift cb{8} from where it lives in bit 32 to bit 31, + * so that it overlaps r2{32} in bit 31. + */ + tcg_gen_shri_i64(cpu_psw_v, cpu_psw_cb, 1); tcg_gen_xor_i64(cpu_psw_v, cpu_psw_v, in2); /* Install the new nullification. */ if (a->cf) { - TCGv_i64 sv = NULL; + TCGv_i64 sv = NULL, uv = NULL; if (cond_need_sv(a->cf >> 1)) { - /* ??? The lshift is supposed to contribute to overflow. */ - sv = do_add_sv(ctx, dest, add1, add2); + sv = do_add_sv(ctx, dest, add1, add2, in1, 1, false); + } else if (cond_need_cb(a->cf >> 1)) { + uv = do_add_uv(ctx, cpu_psw_cb, NULL, in1, 1, false); } - ctx->null_cond = do_cond(ctx, a->cf, false, dest, cout, sv); + ctx->null_cond = do_cond(ctx, a->cf, false, dest, uv, sv); } return nullify_end(ctx); @@ -3365,7 +3488,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1, tcg_gen_add_i64(dest, in1, in2); } if (cond_need_sv(c)) { - sv = do_add_sv(ctx, dest, in1, in2); + sv = do_add_sv(ctx, dest, in1, in2, in1, 0, d); } cond = do_cond(ctx, c * 2 + f, d, dest, cb_cond, sv); @@ -3394,12 +3517,12 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) tmp = tcg_temp_new_i64(); tcg_r = load_gpr(ctx, a->r); - if (cond_need_ext(ctx, a->d)) { + if (a->d) { + tcg_gen_shl_i64(tmp, tcg_r, cpu_sar); + } else { /* Force shift into [32,63] */ tcg_gen_ori_i64(tmp, cpu_sar, 32); tcg_gen_shl_i64(tmp, tcg_r, tmp); - } else { - tcg_gen_shl_i64(tmp, tcg_r, cpu_sar); } cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); @@ -3416,7 +3539,7 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) tmp = tcg_temp_new_i64(); tcg_r = load_gpr(ctx, a->r); - p = a->p | (cond_need_ext(ctx, a->d) ? 32 : 0); + p = a->p | (a->d ? 0 : 32); tcg_gen_shli_i64(tmp, tcg_r, p); cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); @@ -3817,7 +3940,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) load_spr(ctx, new_spc, a->sp); if (a->l) { copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f); + tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); } if (a->n && use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); @@ -3825,6 +3948,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); + nullify_set(ctx, 0); } else { copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { @@ -3880,7 +4004,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) } /* No change for non-gateway pages or for priv decrease. */ if (type >= 4 && type - 4 < ctx->privilege) { - dest = deposit32(dest, 0, 2, type - 4); + dest = deposit64(dest, 0, 2, type - 4); } } else { dest &= -4; /* priv = 0 */ @@ -4463,23 +4587,51 @@ static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a) return nullify_end(ctx); } -static bool trans_diag(DisasContext *ctx, arg_diag *a) +/* Emulate PDC BTLB, called by SeaBIOS-hppa */ +static bool trans_diag_btlb(DisasContext *ctx, arg_diag_btlb *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - if (a->i == 0x100) { - /* emulate PDC BTLB, called by SeaBIOS-hppa */ - nullify_over(ctx); - gen_helper_diag_btlb(tcg_env); - return nullify_end(ctx); - } - if (a->i == 0x101) { - /* print char in %r26 to first serial console, used by SeaBIOS-hppa */ - nullify_over(ctx); - gen_helper_diag_console_output(tcg_env); - return nullify_end(ctx); - } + nullify_over(ctx); + gen_helper_diag_btlb(tcg_env); + return nullify_end(ctx); #endif +} + +/* Print char in %r26 to first serial console, used by SeaBIOS-hppa */ +static bool trans_diag_cout(DisasContext *ctx, arg_diag_cout *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + gen_helper_diag_console_output(tcg_env); + return nullify_end(ctx); +#endif +} + +static bool trans_diag_getshadowregs_pa1(DisasContext *ctx, arg_empty *a) +{ + return !ctx->is_pa20 && do_getshadowregs(ctx); +} + +static bool trans_diag_getshadowregs_pa2(DisasContext *ctx, arg_empty *a) +{ + return ctx->is_pa20 && do_getshadowregs(ctx); +} + +static bool trans_diag_putshadowregs_pa1(DisasContext *ctx, arg_empty *a) +{ + return !ctx->is_pa20 && do_putshadowregs(ctx); +} + +static bool trans_diag_putshadowregs_pa2(DisasContext *ctx, arg_empty *a) +{ + return ctx->is_pa20 && do_putshadowregs(ctx); +} + +static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); qemu_log_mask(LOG_UNIMP, "DIAG opcode 0x%04x ignored\n", a->i); return true; }