/* Try to fill the TLB and return an exception if error. If retaddr is
NULL, it means that the function was called in C code (i.e. not
from generated code or from helper.c) */
-void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
- int mmu_idx, uintptr_t retaddr)
+void tlb_fill(CPUState *cs, target_ulong addr, int size,
+ MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
int ret;
- ret = m68k_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
+ ret = m68k_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx);
if (unlikely(ret)) {
- if (retaddr) {
- /* now we have a real cpu fault */
- cpu_restore_state(cs, retaddr);
- }
- cpu_loop_exit(cs);
+ /* now we have a real cpu fault */
+ cpu_loop_exit_restore(cs, retaddr);
}
}
-static void do_rte(CPUM68KState *env)
+static void cf_rte(CPUM68KState *env)
{
uint32_t sp;
uint32_t fmt;
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;
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()
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) {
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)
+{
+ if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) {
+ /* all except 68000 */
+ 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_ACCESS) {
+ if (env->mmu.fault) {
+ cpu_abort(cs, "DOUBLE MMU FAULT\n");
+ }
+ env->mmu.fault = true;
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* push data 3 */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* push data 2 */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* push data 1 */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* write back 1 / push data 0 */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* write back 1 address */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* write back 2 data */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* write back 2 address */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, 0); /* write back 3 data */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, env->mmu.ar); /* write back 3 address */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, env->mmu.ar); /* fault address */
+ sp -= 2;
+ cpu_stw_kernel(env, sp, 0); /* write back 1 status */
+ sp -= 2;
+ cpu_stw_kernel(env, sp, 0); /* write back 2 status */
+ sp -= 2;
+ cpu_stw_kernel(env, sp, 0); /* write back 3 status */
+ sp -= 2;
+ cpu_stw_kernel(env, sp, env->mmu.ssw); /* special status word */
+ sp -= 4;
+ cpu_stl_kernel(env, sp, env->mmu.ar); /* effective address */
+ do_stack_frame(env, &sp, 7, oldsr, 0, retaddr);
+ env->mmu.fault = false;
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ qemu_log(" "
+ "ssw: %08x ea: %08x sfc: %d dfc: %d\n",
+ env->mmu.ssw, env->mmu.ar, env->sfc, env->dfc);
+ }
+ } else 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);
{
do_interrupt_all(env, 1);
}
+
+void m68k_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write,
+ bool is_exec, int is_asi, unsigned size)
+{
+ M68kCPU *cpu = M68K_CPU(cs);
+ CPUM68KState *env = &cpu->env;
+#ifdef DEBUG_UNASSIGNED
+ qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
+ addr, is_write, is_exec);
+#endif
+ if (env == NULL) {
+ /* when called from gdb, env is NULL */
+ return;
+ }
+
+ if (m68k_feature(env, M68K_FEATURE_M68040)) {
+ env->mmu.mmusr = 0;
+ env->mmu.ssw |= M68K_ATC_040;
+ /* FIXME: manage MMU table access error */
+ env->mmu.ssw &= ~M68K_TM_040;
+ if (env->sr & SR_S) { /* SUPERVISOR */
+ env->mmu.ssw |= M68K_TM_040_SUPER;
+ }
+ if (is_exec) { /* instruction or data */
+ env->mmu.ssw |= M68K_TM_040_CODE;
+ } else {
+ env->mmu.ssw |= M68K_TM_040_DATA;
+ }
+ env->mmu.ssw &= ~M68K_BA_SIZE_MASK;
+ switch (size) {
+ case 1:
+ env->mmu.ssw |= M68K_BA_SIZE_BYTE;
+ break;
+ case 2:
+ env->mmu.ssw |= M68K_BA_SIZE_WORD;
+ break;
+ case 4:
+ env->mmu.ssw |= M68K_BA_SIZE_LONG;
+ break;
+ }
+
+ if (!is_write) {
+ env->mmu.ssw |= M68K_RW_040;
+ }
+
+ env->mmu.ar = addr;
+
+ cs->exception_index = EXCP_ACCESS;
+ cpu_loop_exit(cs);
+ }
+}
#endif
bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
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(), true);
+
+ /* 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(), true);
+
+ /* 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);
+ }
+}