1 /* 2 * M68K helper routines 3 * 4 * Copyright (c) 2007 CodeSourcery 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 #include "qemu/osdep.h" 20 #include "cpu.h" 21 #include "exec/helper-proto.h" 22 #include "exec/exec-all.h" 23 #include "exec/cpu_ldst.h" 24 #include "exec/semihost.h" 25 26 #if defined(CONFIG_USER_ONLY) 27 28 void m68k_cpu_do_interrupt(CPUState *cs) 29 { 30 cs->exception_index = -1; 31 } 32 33 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 34 { 35 } 36 37 #else 38 39 /* Try to fill the TLB and return an exception if error. If retaddr is 40 NULL, it means that the function was called in C code (i.e. not 41 from generated code or from helper.c) */ 42 void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, 43 int mmu_idx, uintptr_t retaddr) 44 { 45 int ret; 46 47 ret = m68k_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx); 48 if (unlikely(ret)) { 49 if (retaddr) { 50 /* now we have a real cpu fault */ 51 cpu_restore_state(cs, retaddr); 52 } 53 cpu_loop_exit(cs); 54 } 55 } 56 57 static void do_rte(CPUM68KState *env) 58 { 59 uint32_t sp; 60 uint32_t fmt; 61 62 sp = env->aregs[7]; 63 fmt = cpu_ldl_kernel(env, sp); 64 env->pc = cpu_ldl_kernel(env, sp + 4); 65 sp |= (fmt >> 28) & 3; 66 env->aregs[7] = sp + 8; 67 68 helper_set_sr(env, fmt); 69 } 70 71 static void do_interrupt_all(CPUM68KState *env, int is_hw) 72 { 73 CPUState *cs = CPU(m68k_env_get_cpu(env)); 74 uint32_t sp; 75 uint32_t fmt; 76 uint32_t retaddr; 77 uint32_t vector; 78 79 fmt = 0; 80 retaddr = env->pc; 81 82 if (!is_hw) { 83 switch (cs->exception_index) { 84 case EXCP_RTE: 85 /* Return from an exception. */ 86 do_rte(env); 87 return; 88 case EXCP_HALT_INSN: 89 if (semihosting_enabled() 90 && (env->sr & SR_S) != 0 91 && (env->pc & 3) == 0 92 && cpu_lduw_code(env, env->pc - 4) == 0x4e71 93 && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { 94 env->pc += 4; 95 do_m68k_semihosting(env, env->dregs[0]); 96 return; 97 } 98 cs->halted = 1; 99 cs->exception_index = EXCP_HLT; 100 cpu_loop_exit(cs); 101 return; 102 } 103 if (cs->exception_index >= EXCP_TRAP0 104 && cs->exception_index <= EXCP_TRAP15) { 105 /* Move the PC after the trap instruction. */ 106 retaddr += 2; 107 } 108 } 109 110 vector = cs->exception_index << 2; 111 112 fmt |= 0x40000000; 113 fmt |= vector << 16; 114 fmt |= env->sr; 115 fmt |= cpu_m68k_get_ccr(env); 116 117 env->sr |= SR_S; 118 if (is_hw) { 119 env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 120 env->sr &= ~SR_M; 121 } 122 m68k_switch_sp(env); 123 sp = env->aregs[7]; 124 fmt |= (sp & 3) << 28; 125 126 /* ??? This could cause MMU faults. */ 127 sp &= ~3; 128 sp -= 4; 129 cpu_stl_kernel(env, sp, retaddr); 130 sp -= 4; 131 cpu_stl_kernel(env, sp, fmt); 132 env->aregs[7] = sp; 133 /* Jump to vector. */ 134 env->pc = cpu_ldl_kernel(env, env->vbr + vector); 135 } 136 137 void m68k_cpu_do_interrupt(CPUState *cs) 138 { 139 M68kCPU *cpu = M68K_CPU(cs); 140 CPUM68KState *env = &cpu->env; 141 142 do_interrupt_all(env, 0); 143 } 144 145 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 146 { 147 do_interrupt_all(env, 1); 148 } 149 #endif 150 151 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 152 { 153 M68kCPU *cpu = M68K_CPU(cs); 154 CPUM68KState *env = &cpu->env; 155 156 if (interrupt_request & CPU_INTERRUPT_HARD 157 && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { 158 /* Real hardware gets the interrupt vector via an IACK cycle 159 at this point. Current emulated hardware doesn't rely on 160 this, so we provide/save the vector when the interrupt is 161 first signalled. */ 162 cs->exception_index = env->pending_vector; 163 do_interrupt_m68k_hardirq(env); 164 return true; 165 } 166 return false; 167 } 168 169 static void raise_exception(CPUM68KState *env, int tt) 170 { 171 CPUState *cs = CPU(m68k_env_get_cpu(env)); 172 173 cs->exception_index = tt; 174 cpu_loop_exit(cs); 175 } 176 177 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) 178 { 179 raise_exception(env, tt); 180 } 181 182 void HELPER(divu)(CPUM68KState *env, uint32_t word) 183 { 184 uint32_t num; 185 uint32_t den; 186 uint32_t quot; 187 uint32_t rem; 188 189 num = env->div1; 190 den = env->div2; 191 /* ??? This needs to make sure the throwing location is accurate. */ 192 if (den == 0) { 193 raise_exception(env, EXCP_DIV0); 194 } 195 quot = num / den; 196 rem = num % den; 197 198 env->cc_v = (word && quot > 0xffff ? -1 : 0); 199 env->cc_z = quot; 200 env->cc_n = quot; 201 env->cc_c = 0; 202 203 env->div1 = quot; 204 env->div2 = rem; 205 } 206 207 void HELPER(divs)(CPUM68KState *env, uint32_t word) 208 { 209 int32_t num; 210 int32_t den; 211 int32_t quot; 212 int32_t rem; 213 214 num = env->div1; 215 den = env->div2; 216 if (den == 0) { 217 raise_exception(env, EXCP_DIV0); 218 } 219 quot = num / den; 220 rem = num % den; 221 222 env->cc_v = (word && quot != (int16_t)quot ? -1 : 0); 223 env->cc_z = quot; 224 env->cc_n = quot; 225 env->cc_c = 0; 226 227 env->div1 = quot; 228 env->div2 = rem; 229 } 230