summaryrefslogtreecommitdiffstats
path: root/target/m68k/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/m68k/translate.c')
-rw-r--r--target/m68k/translate.c192
1 files changed, 140 insertions, 52 deletions
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 4026572ed8..8f3c298ad0 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -114,6 +114,7 @@ typedef struct DisasContext {
DisasContextBase base;
CPUM68KState *env;
target_ulong pc;
+ target_ulong pc_prev;
CCOp cc_op; /* Current CC operation */
int cc_op_synced;
TCGv_i64 mactmp;
@@ -298,6 +299,21 @@ static void gen_raise_exception(int nr)
tcg_temp_free_i32(tmp);
}
+static void gen_raise_exception_format2(DisasContext *s, int nr,
+ target_ulong this_pc)
+{
+ /*
+ * Pass the address of the insn to the exception handler,
+ * for recording in the Format $2 (6-word) stack frame.
+ * Re-use mmu.ar for the purpose, since that's only valid
+ * after tlb_fill.
+ */
+ tcg_gen_st_i32(tcg_constant_i32(this_pc), cpu_env,
+ offsetof(CPUM68KState, mmu.ar));
+ gen_raise_exception(nr);
+ s->base.is_jmp = DISAS_NORETURN;
+}
+
static void gen_exception(DisasContext *s, uint32_t dest, int nr)
{
update_cc_op(s);
@@ -1494,12 +1510,13 @@ static void gen_exit_tb(DisasContext *s)
} while (0)
/* Generate a jump to an immediate address. */
-static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
+static void gen_jmp_tb(DisasContext *s, int n, target_ulong dest,
+ target_ulong src)
{
if (unlikely(s->ss_active)) {
update_cc_op(s);
tcg_gen_movi_i32(QREG_PC, dest);
- gen_raise_exception(EXCP_TRACE);
+ gen_raise_exception_format2(s, EXCP_TRACE, src);
} else if (translator_use_goto_tb(&s->base, dest)) {
tcg_gen_goto_tb(n);
tcg_gen_movi_i32(QREG_PC, dest);
@@ -1548,9 +1565,9 @@ DISAS_INSN(dbcc)
tcg_gen_addi_i32(tmp, tmp, -1);
gen_partset_reg(OS_WORD, reg, tmp);
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, -1, l1);
- gen_jmp_tb(s, 1, base + offset);
+ gen_jmp_tb(s, 1, base + offset, s->base.pc_next);
gen_set_label(l1);
- gen_jmp_tb(s, 0, s->pc);
+ gen_jmp_tb(s, 0, s->pc, s->base.pc_next);
}
DISAS_INSN(undef_mac)
@@ -1601,6 +1618,7 @@ DISAS_INSN(divw)
int sign;
TCGv src;
TCGv destr;
+ TCGv ilen;
/* divX.w <EA>,Dn 32/16 -> 16r:16q */
@@ -1609,20 +1627,20 @@ DISAS_INSN(divw)
/* dest.l / src.w */
SRC_EA(env, src, OS_WORD, sign, NULL);
- destr = tcg_const_i32(REG(insn, 9));
+ destr = tcg_constant_i32(REG(insn, 9));
+ ilen = tcg_constant_i32(s->pc - s->base.pc_next);
if (sign) {
- gen_helper_divsw(cpu_env, destr, src);
+ gen_helper_divsw(cpu_env, destr, src, ilen);
} else {
- gen_helper_divuw(cpu_env, destr, src);
+ gen_helper_divuw(cpu_env, destr, src, ilen);
}
- tcg_temp_free(destr);
set_cc_op(s, CC_OP_FLAGS);
}
DISAS_INSN(divl)
{
- TCGv num, reg, den;
+ TCGv num, reg, den, ilen;
int sign;
uint16_t ext;
@@ -1639,15 +1657,14 @@ DISAS_INSN(divl)
/* divX.l <EA>, Dr:Dq 64/32 -> 32r:32q */
SRC_EA(env, den, OS_LONG, 0, NULL);
- num = tcg_const_i32(REG(ext, 12));
- reg = tcg_const_i32(REG(ext, 0));
+ num = tcg_constant_i32(REG(ext, 12));
+ reg = tcg_constant_i32(REG(ext, 0));
+ ilen = tcg_constant_i32(s->pc - s->base.pc_next);
if (sign) {
- gen_helper_divsll(cpu_env, num, reg, den);
+ gen_helper_divsll(cpu_env, num, reg, den, ilen);
} else {
- gen_helper_divull(cpu_env, num, reg, den);
+ gen_helper_divull(cpu_env, num, reg, den, ilen);
}
- tcg_temp_free(reg);
- tcg_temp_free(num);
set_cc_op(s, CC_OP_FLAGS);
return;
}
@@ -1656,15 +1673,14 @@ DISAS_INSN(divl)
/* divXl.l <EA>, Dr:Dq 32/32 -> 32r:32q */
SRC_EA(env, den, OS_LONG, 0, NULL);
- num = tcg_const_i32(REG(ext, 12));
- reg = tcg_const_i32(REG(ext, 0));
+ num = tcg_constant_i32(REG(ext, 12));
+ reg = tcg_constant_i32(REG(ext, 0));
+ ilen = tcg_constant_i32(s->pc - s->base.pc_next);
if (sign) {
- gen_helper_divsl(cpu_env, num, reg, den);
+ gen_helper_divsl(cpu_env, num, reg, den, ilen);
} else {
- gen_helper_divul(cpu_env, num, reg, den);
+ gen_helper_divul(cpu_env, num, reg, den, ilen);
}
- tcg_temp_free(reg);
- tcg_temp_free(num);
set_cc_op(s, CC_OP_FLAGS);
}
@@ -3059,22 +3075,6 @@ DISAS_INSN(addsubq)
tcg_temp_free(dest);
}
-DISAS_INSN(tpf)
-{
- switch (insn & 7) {
- case 2: /* One extension word. */
- s->pc += 2;
- break;
- case 3: /* Two extension words. */
- s->pc += 4;
- break;
- case 4: /* No extension words. */
- break;
- default:
- disas_undef(env, s, insn);
- }
-}
-
DISAS_INSN(branch)
{
int32_t offset;
@@ -3097,13 +3097,13 @@ DISAS_INSN(branch)
/* Bcc */
TCGLabel *l1 = gen_new_label();
gen_jmpcc(s, ((insn >> 8) & 0xf) ^ 1, l1);
- gen_jmp_tb(s, 1, base + offset);
+ gen_jmp_tb(s, 1, base + offset, s->base.pc_next);
gen_set_label(l1);
- gen_jmp_tb(s, 0, s->pc);
+ gen_jmp_tb(s, 0, s->pc, s->base.pc_next);
} else {
/* Unconditional branch. */
update_cc_op(s);
- gen_jmp_tb(s, 0, base + offset);
+ gen_jmp_tb(s, 0, base + offset, s->base.pc_next);
}
}
@@ -4860,7 +4860,62 @@ DISAS_INSN(wdebug)
DISAS_INSN(trap)
{
- gen_exception(s, s->base.pc_next, EXCP_TRAP0 + (insn & 0xf));
+ gen_exception(s, s->pc, EXCP_TRAP0 + (insn & 0xf));
+}
+
+static void do_trapcc(DisasContext *s, DisasCompare *c)
+{
+ if (c->tcond != TCG_COND_NEVER) {
+ TCGLabel *over = NULL;
+
+ update_cc_op(s);
+
+ if (c->tcond != TCG_COND_ALWAYS) {
+ /* Jump over if !c. */
+ over = gen_new_label();
+ tcg_gen_brcond_i32(tcg_invert_cond(c->tcond), c->v1, c->v2, over);
+ }
+
+ tcg_gen_movi_i32(QREG_PC, s->pc);
+ gen_raise_exception_format2(s, EXCP_TRAPCC, s->base.pc_next);
+
+ if (over != NULL) {
+ gen_set_label(over);
+ s->base.is_jmp = DISAS_NEXT;
+ }
+ }
+ free_cond(c);
+}
+
+DISAS_INSN(trapcc)
+{
+ DisasCompare c;
+
+ /* Consume and discard the immediate operand. */
+ switch (extract32(insn, 0, 3)) {
+ case 2: /* trapcc.w */
+ (void)read_im16(env, s);
+ break;
+ case 3: /* trapcc.l */
+ (void)read_im32(env, s);
+ break;
+ case 4: /* trapcc (no operand) */
+ break;
+ default:
+ /* trapcc registered with only valid opmodes */
+ g_assert_not_reached();
+ }
+
+ gen_cc_cond(&c, s, extract32(insn, 8, 4));
+ do_trapcc(s, &c);
+}
+
+DISAS_INSN(trapv)
+{
+ DisasCompare c;
+
+ gen_cc_cond(&c, s, 9); /* V set */
+ do_trapcc(s, &c);
}
static void gen_load_fcr(DisasContext *s, TCGv res, int reg)
@@ -5486,9 +5541,9 @@ DISAS_INSN(fbcc)
l1 = gen_new_label();
update_cc_op(s);
gen_fjmpcc(s, insn & 0x3f, l1);
- gen_jmp_tb(s, 0, s->pc);
+ gen_jmp_tb(s, 0, s->pc, s->base.pc_next);
gen_set_label(l1);
- gen_jmp_tb(s, 1, base + offset);
+ gen_jmp_tb(s, 1, base + offset, s->base.pc_next);
}
DISAS_INSN(fscc)
@@ -5511,6 +5566,34 @@ DISAS_INSN(fscc)
tcg_temp_free(tmp);
}
+DISAS_INSN(ftrapcc)
+{
+ DisasCompare c;
+ uint16_t ext;
+ int cond;
+
+ ext = read_im16(env, s);
+ cond = ext & 0x3f;
+
+ /* Consume and discard the immediate operand. */
+ switch (extract32(insn, 0, 3)) {
+ case 2: /* ftrapcc.w */
+ (void)read_im16(env, s);
+ break;
+ case 3: /* ftrapcc.l */
+ (void)read_im32(env, s);
+ break;
+ case 4: /* ftrapcc (no operand) */
+ break;
+ default:
+ /* ftrapcc registered with only valid opmodes */
+ g_assert_not_reached();
+ }
+
+ gen_fcc_cond(&c, s, cond);
+ do_trapcc(s, &c);
+}
+
#if defined(CONFIG_SOFTMMU)
DISAS_INSN(frestore)
{
@@ -6003,6 +6086,7 @@ void register_m68k_insns (CPUM68KState *env)
INSN(tas, 4ac0, ffc0, M68000);
#if defined(CONFIG_SOFTMMU)
INSN(halt, 4ac8, ffff, CF_ISA_A);
+ INSN(halt, 4ac8, ffff, M68060);
#endif
INSN(pulse, 4acc, ffff, CF_ISA_A);
BASE(illegal, 4afc, ffff);
@@ -6026,6 +6110,7 @@ void register_m68k_insns (CPUM68KState *env)
BASE(nop, 4e71, ffff);
INSN(rtd, 4e74, ffff, RTD);
BASE(rts, 4e75, ffff);
+ INSN(trapv, 4e76, ffff, M68000);
INSN(rtr, 4e77, ffff, M68000);
BASE(jump, 4e80, ffc0);
BASE(jump, 4ec0, ffc0);
@@ -6034,7 +6119,10 @@ void register_m68k_insns (CPUM68KState *env)
INSN(scc, 50c0, f0f8, CF_ISA_A); /* Scc.B Dx */
INSN(scc, 50c0, f0c0, M68000); /* Scc.B <EA> */
INSN(dbcc, 50c8, f0f8, M68000);
- INSN(tpf, 51f8, fff8, CF_ISA_A);
+ INSN(trapcc, 50fa, f0fe, TRAPCC); /* opmode 010, 011 */
+ INSN(trapcc, 50fc, f0ff, TRAPCC); /* opmode 100 */
+ INSN(trapcc, 51fa, fffe, CF_ISA_A); /* TPF (trapf) opmode 010, 011 */
+ INSN(trapcc, 51fc, ffff, CF_ISA_A); /* TPF (trapf) opmode 100 */
/* Branch instructions. */
BASE(branch, 6000, f000);
@@ -6132,6 +6220,8 @@ void register_m68k_insns (CPUM68KState *env)
INSN(fbcc, f280, ffc0, CF_FPU);
INSN(fpu, f200, ffc0, FPU);
INSN(fscc, f240, ffc0, FPU);
+ INSN(ftrapcc, f27a, fffe, FPU); /* opmode 010, 011 */
+ INSN(ftrapcc, f27c, ffff, FPU); /* opmode 100 */
INSN(fbcc, f280, ff80, FPU);
#if defined(CONFIG_SOFTMMU)
INSN(frestore, f340, ffc0, CF_FPU);
@@ -6159,6 +6249,8 @@ static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
dc->env = env;
dc->pc = dc->base.pc_first;
+ /* This value will always be filled in properly before m68k_tr_tb_stop. */
+ dc->pc_prev = 0xdeadbeef;
dc->cc_op = CC_OP_DYNAMIC;
dc->cc_op_synced = 1;
dc->done_mac = 0;
@@ -6192,6 +6284,7 @@ static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
do_writebacks(dc);
do_release(dc);
+ dc->pc_prev = dc->base.pc_next;
dc->base.pc_next = dc->pc;
if (dc->base.is_jmp == DISAS_NEXT) {
@@ -6226,17 +6319,12 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
break;
case DISAS_TOO_MANY:
update_cc_op(dc);
- if (dc->ss_active) {
- tcg_gen_movi_i32(QREG_PC, dc->pc);
- gen_raise_exception(EXCP_TRACE);
- } else {
- gen_jmp_tb(dc, 0, dc->pc);
- }
+ gen_jmp_tb(dc, 0, dc->pc, dc->pc_prev);
break;
case DISAS_JUMP:
/* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */
if (dc->ss_active) {
- gen_raise_exception(EXCP_TRACE);
+ gen_raise_exception_format2(dc, EXCP_TRACE, dc->pc_prev);
} else {
tcg_gen_lookup_and_goto_ptr();
}
@@ -6247,7 +6335,7 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
* other state that may require returning to the main loop.
*/
if (dc->ss_active) {
- gen_raise_exception(EXCP_TRACE);
+ gen_raise_exception_format2(dc, EXCP_TRACE, dc->pc_prev);
} else {
tcg_gen_exit_tb(NULL, 0);
}