diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-01-08 21:39:44 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-01-08 21:39:44 +0000 |
commit | 232e5537e476c463f5e6e2e9ae31f6e2da9ebfe9 (patch) | |
tree | fe9fc4a6bf46d1d2a6fce3287b7e92f2f37f5665 /target/m68k/op_helper.c | |
parent | 4bd797af6f5bc9f7114030695716d199dceafa6e (diff) | |
parent | cc5230267678c26b7f96157086f45fd8a347eb21 (diff) |
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging
# gpg: Signature made Thu 04 Jan 2018 16:37:32 GMT
# gpg: using RSA key 0xF30C38BD3F2FBE3C
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>"
# gpg: aka "Laurent Vivier <laurent@vivier.eu>"
# gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>"
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C
* remotes/vivier/tags/m68k-for-2.12-pull-request:
target/m68k: fix m68k_cpu_dump_state()
target/m68k: add the Interrupt Stack Pointer
target/m68k: add andi/ori/eori to SR/CCR
target/m68k: add 680x0 "move to SR" instruction
target/m68k: move CCR/SR functions
target/m68k: implement fsave/frestore
target/m68k: add reset
target/m68k: add cpush/cinv
target/m68k: softmmu cleanup
target/m68k: add move16
target/m68k: add chk and chk2
target/m68k: manage 680x0 stack frames
target/m68k: add CPU_LOG_INT trace
target/m68k: use insn_pc to generate instruction fault address
linux-user, m68k: correctly manage SR in context
target/m68k: fix gen_get_ccr()
target-m68k: sync CC_OP before gen_jmp_tb()
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target/m68k/op_helper.c')
-rw-r--r-- | target/m68k/op_helper.c | 338 |
1 files changed, 332 insertions, 6 deletions
diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 78bfb9f0cc..c61ca9392f 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -51,7 +51,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,13 +62,158 @@ static void do_rte(CPUM68KState *env) sp |= (fmt >> 28) & 3; env->aregs[7] = sp + 8; - helper_set_sr(env, fmt); + cpu_m68k_set_sr(env, fmt); } -static void do_interrupt_all(CPUM68KState *env, int is_hw) +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) { + case EXCP_ACCESS: + return "Access Fault"; + case EXCP_ADDRESS: + return "Address Error"; + case EXCP_ILLEGAL: + return "Illegal Instruction"; + case EXCP_DIV0: + return "Divide by Zero"; + case EXCP_CHK: + return "CHK/CHK2"; + case EXCP_TRAPCC: + return "FTRAPcc, TRAPcc, TRAPV"; + case EXCP_PRIVILEGE: + return "Privilege Violation"; + case EXCP_TRACE: + return "Trace"; + case EXCP_LINEA: + return "A-Line"; + case EXCP_LINEF: + return "F-Line"; + case EXCP_DEBEGBP: /* 68020/030 only */ + return "Copro Protocol Violation"; + case EXCP_FORMAT: + return "Format Error"; + case EXCP_UNINITIALIZED: + return "Unitialized Interruot"; + case EXCP_SPURIOUS: + return "Spurious Interrupt"; + case EXCP_INT_LEVEL_1: + return "Level 1 Interrupt"; + case EXCP_INT_LEVEL_1 + 1: + return "Level 2 Interrupt"; + case EXCP_INT_LEVEL_1 + 2: + return "Level 3 Interrupt"; + case EXCP_INT_LEVEL_1 + 3: + return "Level 4 Interrupt"; + case EXCP_INT_LEVEL_1 + 4: + return "Level 5 Interrupt"; + case EXCP_INT_LEVEL_1 + 5: + return "Level 6 Interrupt"; + case EXCP_INT_LEVEL_1 + 6: + return "Level 7 Interrupt"; + case EXCP_TRAP0: + return "TRAP #0"; + case EXCP_TRAP0 + 1: + return "TRAP #1"; + case EXCP_TRAP0 + 2: + return "TRAP #2"; + case EXCP_TRAP0 + 3: + return "TRAP #3"; + case EXCP_TRAP0 + 4: + return "TRAP #4"; + case EXCP_TRAP0 + 5: + return "TRAP #5"; + case EXCP_TRAP0 + 6: + return "TRAP #6"; + case EXCP_TRAP0 + 7: + return "TRAP #7"; + case EXCP_TRAP0 + 8: + return "TRAP #8"; + case EXCP_TRAP0 + 9: + return "TRAP #9"; + case EXCP_TRAP0 + 10: + return "TRAP #10"; + case EXCP_TRAP0 + 11: + return "TRAP #11"; + case EXCP_TRAP0 + 12: + return "TRAP #12"; + case EXCP_TRAP0 + 13: + return "TRAP #13"; + case EXCP_TRAP0 + 14: + return "TRAP #14"; + case EXCP_TRAP0 + 15: + return "TRAP #15"; + case EXCP_FP_BSUN: + return "FP Branch/Set on unordered condition"; + case EXCP_FP_INEX: + return "FP Inexact Result"; + case EXCP_FP_DZ: + return "FP Divide by Zero"; + case EXCP_FP_UNFL: + return "FP Underflow"; + case EXCP_FP_OPERR: + return "FP Operand Error"; + case EXCP_FP_OVFL: + return "FP Overflow"; + case EXCP_FP_SNAN: + return "FP Signaling NAN"; + case EXCP_FP_UNIMP: + return "FP Unimplemented Data Type"; + case EXCP_MMU_CONF: /* 68030/68851 only */ + return "MMU Configuration Error"; + case EXCP_MMU_ILLEGAL: /* 68851 only */ + return "MMU Illegal Operation"; + case EXCP_MMU_ACCESS: /* 68851 only */ + return "MMU Access Level Violation"; + case 64 ... 255: + return "User Defined Vector"; + } + return "Unassigned"; +} + +static void cf_interrupt_all(CPUM68KState *env, int is_hw) { CPUState *cs = CPU(m68k_env_get_cpu(env)); uint32_t sp; + uint32_t sr; uint32_t fmt; uint32_t retaddr; uint32_t vector; @@ -80,7 +225,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: if (semihosting_enabled() @@ -106,10 +251,17 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) vector = cs->exception_index << 2; + sr = env->sr | cpu_m68k_get_ccr(env); + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n", + ++count, m68k_exception_name(cs->exception_index), + vector, env->pc, env->aregs[7], sr); + } + fmt |= 0x40000000; fmt |= vector << 16; - fmt |= env->sr; - fmt |= cpu_m68k_get_ccr(env); + fmt |= sr; env->sr |= SR_S; if (is_hw) { @@ -131,6 +283,119 @@ 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 ... 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)) { + static int count; + qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n", + ++count, 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); @@ -679,3 +944,64 @@ uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, is already zero. */ return n | ffo; } + +void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub) +{ + /* From the specs: + * X: Not affected, C,V,Z: Undefined, + * N: Set if val < 0; cleared if val > ub, undefined otherwise + * We implement here values found from a real MC68040: + * X,V,Z: Not affected + * N: Set if val < 0; cleared if val >= 0 + * C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise + * if 0 > ub: set if val > ub and val < 0, cleared otherwise + */ + env->cc_n = val; + env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0; + + if (val < 0 || val > ub) { + CPUState *cs = CPU(m68k_env_get_cpu(env)); + + /* Recover PC and CC_OP for the beginning of the insn. */ + cpu_restore_state(cs, GETPC()); + + /* flags have been modified by gen_flush_flags() */ + env->cc_op = CC_OP_FLAGS; + /* Adjust PC to end of the insn. */ + env->pc += 2; + + cs->exception_index = EXCP_CHK; + cpu_loop_exit(cs); + } +} + +void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub) +{ + /* From the specs: + * X: Not affected, N,V: Undefined, + * Z: Set if val is equal to lb or ub + * C: Set if val < lb or val > ub, cleared otherwise + * We implement here values found from a real MC68040: + * X,N,V: Not affected + * Z: Set if val is equal to lb or ub + * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise + * if lb > ub: set if val > ub and val < lb, cleared otherwise + */ + env->cc_z = val != lb && val != ub; + env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb; + + if (env->cc_c) { + CPUState *cs = CPU(m68k_env_get_cpu(env)); + + /* Recover PC and CC_OP for the beginning of the insn. */ + cpu_restore_state(cs, GETPC()); + + /* flags have been modified by gen_flush_flags() */ + env->cc_op = CC_OP_FLAGS; + /* Adjust PC to end of the insn. */ + env->pc += 4; + + cs->exception_index = EXCP_CHK; + cpu_loop_exit(cs); + } +} |