From 172f3709e315e7ddb12748aefb6428167f8bb55b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 6 Mar 2018 08:03:34 -0500 Subject: [PATCH] target/m68k: manage 680x0 stack frames 680x0 manages several stack frame formats: - format 0: four-word stack frame - format 1: four-word throwaway stack frame - format 2: six-word stack frame - format 3: Floating-Point post-instruction stack frame - format 4: eight-word stack frame - format 7: access-error stack frame Backports commit d2f8fb8e7f8e7d082103d705e178c9f72e0bea77 from qemu --- qemu/target/m68k/cpu.h | 1 + qemu/target/m68k/helper.c | 11 ++- qemu/target/m68k/op_helper.c | 176 ++++++++++++++++++++++++++++++++++- 3 files changed, 181 insertions(+), 7 deletions(-) diff --git a/qemu/target/m68k/cpu.h b/qemu/target/m68k/cpu.h index cb45336f..b1a7dd19 100644 --- a/qemu/target/m68k/cpu.h +++ b/qemu/target/m68k/cpu.h @@ -184,6 +184,7 @@ int cpu_m68k_signal_handler(int host_signum, void *pinfo, void *puc); uint32_t cpu_m68k_get_ccr(CPUM68KState *env); void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); +void cpu_m68k_set_sr(CPUM68KState *env, uint32_t); void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val); typedef enum { diff --git a/qemu/target/m68k/helper.c b/qemu/target/m68k/helper.c index 9ecf98cf..6ef93ad7 100644 --- a/qemu/target/m68k/helper.c +++ b/qemu/target/m68k/helper.c @@ -191,11 +191,16 @@ uint32_t HELPER(sats)(uint32_t val, uint32_t v) return val; } +void cpu_m68k_set_sr(CPUM68KState *env, uint32_t sr) +{ + env->sr = sr & 0xffe0; + cpu_m68k_set_ccr(env, sr); + m68k_switch_sp(env); +} + void HELPER(set_sr)(CPUM68KState *env, uint32_t val) { - env->sr = val & 0xffe0; - cpu_m68k_set_ccr(env, val); - m68k_switch_sp(env); + cpu_m68k_set_sr(env, val); } /* MAC unit. */ diff --git a/qemu/target/m68k/op_helper.c b/qemu/target/m68k/op_helper.c index 4b9dad96..8325eff3 100644 --- a/qemu/target/m68k/op_helper.c +++ b/qemu/target/m68k/op_helper.c @@ -52,7 +52,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, } } -static void do_rte(CPUM68KState *env) +static void cf_rte(CPUM68KState *env) { uint32_t sp; uint32_t fmt; @@ -62,9 +62,50 @@ static void do_rte(CPUM68KState *env) env->pc = cpu_ldl_kernel(env, sp + 4); sp |= (fmt >> 28) & 3; env->aregs[7] = sp + 8; - helper_set_sr(env, fmt); + + cpu_m68k_set_sr(env, fmt); } +static void m68k_rte(CPUM68KState *env) +{ + uint32_t sp; + uint16_t fmt; + uint16_t sr; + + sp = env->aregs[7]; +throwaway: + sr = cpu_lduw_kernel(env, sp); + sp += 2; + env->pc = cpu_ldl_kernel(env, sp); + sp += 4; + if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { + /* all except 68000 */ + fmt = cpu_lduw_kernel(env, sp); + sp += 2; + switch (fmt >> 12) { + case 0: + break; + case 1: + env->aregs[7] = sp; + cpu_m68k_set_sr(env, sr); + goto throwaway; + case 2: + case 3: + sp += 4; + break; + case 4: + sp += 8; + break; + case 7: + sp += 52; + break; + } + } + env->aregs[7] = sp; + cpu_m68k_set_sr(env, sr); +} + + static const char *m68k_exception_name(int index) { switch (index) { @@ -173,7 +214,7 @@ static const char *m68k_exception_name(int index) return "Unassigned"; } -static void do_interrupt_all(CPUM68KState *env, int is_hw) +static void cf_interrupt_all(CPUM68KState *env, int is_hw) { CPUState *cs = CPU(m68k_env_get_cpu(env)); uint32_t sp; @@ -189,7 +230,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) switch (cs->exception_index) { case EXCP_RTE: /* Return from an exception. */ - do_rte(env); + cf_rte(env); return; case EXCP_HALT_INSN: cs->halted = 1; @@ -237,6 +278,133 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) env->pc = cpu_ldl_kernel(env, env->vbr + vector); } +static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, + uint16_t format, uint16_t sr, + uint32_t addr, uint32_t retaddr) +{ + CPUState *cs = CPU(m68k_env_get_cpu(env)); + switch (format) { + case 4: + *sp -= 4; + cpu_stl_kernel(env, *sp, env->pc); + *sp -= 4; + cpu_stl_kernel(env, *sp, addr); + break; + case 3: + case 2: + *sp -= 4; + cpu_stl_kernel(env, *sp, addr); + break; + } + *sp -= 2; + cpu_stw_kernel(env, *sp, (format << 12) + (cs->exception_index << 2)); + *sp -= 4; + cpu_stl_kernel(env, *sp, retaddr); + *sp -= 2; + cpu_stw_kernel(env, *sp, sr); +} + +static void m68k_interrupt_all(CPUM68KState *env, int is_hw) +{ + CPUState *cs = CPU(m68k_env_get_cpu(env)); + uint32_t sp; + uint32_t retaddr; + uint32_t vector; + uint16_t sr, oldsr; + + retaddr = env->pc; + + if (!is_hw) { + switch (cs->exception_index) { + case EXCP_RTE: + /* Return from an exception. */ + m68k_rte(env); + return; + case EXCP_TRAP0: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case EXCP_TRAP15: + /* Move the PC after the trap instruction. */ + retaddr += 2; + break; + } + } + + vector = cs->exception_index << 2; + + sr = env->sr | cpu_m68k_get_ccr(env); + if (qemu_loglevel_mask(CPU_LOG_INT)) { + qemu_log("INT: %s(%#x) pc=%08x sp=%08x sr=%04x\n", + m68k_exception_name(cs->exception_index), + vector, env->pc, env->aregs[7], sr); + } + + /* + * MC68040UM/AD, chapter 9.3.10 + */ + + /* "the processor first make an internal copy" */ + oldsr = sr; + /* "set the mode to supervisor" */ + sr |= SR_S; + /* "suppress tracing" */ + sr &= ~SR_T; + /* "sets the processor interrupt mask" */ + if (is_hw) { + sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); + } + cpu_m68k_set_sr(env, sr); + sp = env->aregs[7]; + + sp &= ~1; + if (cs->exception_index == EXCP_ADDRESS) { + do_stack_frame(env, &sp, 2, oldsr, 0, retaddr); + } else if (cs->exception_index == EXCP_ILLEGAL || + cs->exception_index == EXCP_DIV0 || + cs->exception_index == EXCP_CHK || + cs->exception_index == EXCP_TRAPCC || + cs->exception_index == EXCP_TRACE) { + /* FIXME: addr is not only env->pc */ + do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr); + } else if (is_hw && oldsr & SR_M && + cs->exception_index >= EXCP_SPURIOUS && + cs->exception_index <= EXCP_INT_LEVEL_7) { + do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); + oldsr = sr; + env->aregs[7] = sp; + cpu_m68k_set_sr(env, sr &= ~SR_M); + sp = env->aregs[7] & ~1; + do_stack_frame(env, &sp, 1, oldsr, 0, retaddr); + } else { + do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); + } + + env->aregs[7] = sp; + /* Jump to vector. */ + env->pc = cpu_ldl_kernel(env, env->vbr + vector); +} + +static void do_interrupt_all(CPUM68KState *env, int is_hw) +{ + if (m68k_feature(env, M68K_FEATURE_M68000)) { + m68k_interrupt_all(env, is_hw); + return; + } + cf_interrupt_all(env, is_hw); +} + void m68k_cpu_do_interrupt(CPUState *cs) { M68kCPU *cpu = M68K_CPU(cs->uc, cs);