10633879fSpbrook /* 20633879fSpbrook * M68K helper routines 30633879fSpbrook * 40633879fSpbrook * Copyright (c) 2007 CodeSourcery 50633879fSpbrook * 60633879fSpbrook * This library is free software; you can redistribute it and/or 70633879fSpbrook * modify it under the terms of the GNU Lesser General Public 80633879fSpbrook * License as published by the Free Software Foundation; either 90633879fSpbrook * version 2 of the License, or (at your option) any later version. 100633879fSpbrook * 110633879fSpbrook * This library is distributed in the hope that it will be useful, 120633879fSpbrook * but WITHOUT ANY WARRANTY; without even the implied warranty of 130633879fSpbrook * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 140633879fSpbrook * Lesser General Public License for more details. 150633879fSpbrook * 160633879fSpbrook * You should have received a copy of the GNU Lesser General Public 178167ee88SBlue Swirl * License along with this library; if not, see <http://www.gnu.org/licenses/>. 180633879fSpbrook */ 19d8416665SPeter Maydell #include "qemu/osdep.h" 203e457172SBlue Swirl #include "cpu.h" 212ef6175aSRichard Henderson #include "exec/helper-proto.h" 2263c91552SPaolo Bonzini #include "exec/exec-all.h" 23f08b6170SPaolo Bonzini #include "exec/cpu_ldst.h" 24cfe67cefSLeon Alrae #include "exec/semihost.h" 250633879fSpbrook 260633879fSpbrook #if defined(CONFIG_USER_ONLY) 270633879fSpbrook 2897a8ea5aSAndreas Färber void m68k_cpu_do_interrupt(CPUState *cs) 290633879fSpbrook { 3027103424SAndreas Färber cs->exception_index = -1; 313c688828SBlue Swirl } 323c688828SBlue Swirl 33ab409bb3SRichard Henderson static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 343c688828SBlue Swirl { 350633879fSpbrook } 360633879fSpbrook 370633879fSpbrook #else 380633879fSpbrook 390633879fSpbrook /* Try to fill the TLB and return an exception if error. If retaddr is 400633879fSpbrook NULL, it means that the function was called in C code (i.e. not 410633879fSpbrook from generated code or from helper.c) */ 42b35399bbSSergey Sorokin void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, 43b35399bbSSergey Sorokin int mmu_idx, uintptr_t retaddr) 440633879fSpbrook { 450633879fSpbrook int ret; 460633879fSpbrook 47b35399bbSSergey Sorokin ret = m68k_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx); 48551bd27fSths if (unlikely(ret)) { 490633879fSpbrook if (retaddr) { 500633879fSpbrook /* now we have a real cpu fault */ 513f38f309SAndreas Färber cpu_restore_state(cs, retaddr); 520633879fSpbrook } 535638d180SAndreas Färber cpu_loop_exit(cs); 540633879fSpbrook } 550633879fSpbrook } 560633879fSpbrook 5731871141SBlue Swirl static void do_rte(CPUM68KState *env) 580633879fSpbrook { 590633879fSpbrook uint32_t sp; 600633879fSpbrook uint32_t fmt; 610633879fSpbrook 620633879fSpbrook sp = env->aregs[7]; 6331871141SBlue Swirl fmt = cpu_ldl_kernel(env, sp); 6431871141SBlue Swirl env->pc = cpu_ldl_kernel(env, sp + 4); 650633879fSpbrook sp |= (fmt >> 28) & 3; 660633879fSpbrook env->aregs[7] = sp + 8; 6799c51448SRichard Henderson 6899c51448SRichard Henderson helper_set_sr(env, fmt); 690633879fSpbrook } 700633879fSpbrook 7131871141SBlue Swirl static void do_interrupt_all(CPUM68KState *env, int is_hw) 720633879fSpbrook { 7327103424SAndreas Färber CPUState *cs = CPU(m68k_env_get_cpu(env)); 740633879fSpbrook uint32_t sp; 750633879fSpbrook uint32_t fmt; 760633879fSpbrook uint32_t retaddr; 770633879fSpbrook uint32_t vector; 780633879fSpbrook 790633879fSpbrook fmt = 0; 800633879fSpbrook retaddr = env->pc; 810633879fSpbrook 820633879fSpbrook if (!is_hw) { 8327103424SAndreas Färber switch (cs->exception_index) { 840633879fSpbrook case EXCP_RTE: 850633879fSpbrook /* Return from an exception. */ 8631871141SBlue Swirl do_rte(env); 870633879fSpbrook return; 88a87295e8Spbrook case EXCP_HALT_INSN: 89cfe67cefSLeon Alrae if (semihosting_enabled() 90a87295e8Spbrook && (env->sr & SR_S) != 0 91a87295e8Spbrook && (env->pc & 3) == 0 9231871141SBlue Swirl && cpu_lduw_code(env, env->pc - 4) == 0x4e71 9331871141SBlue Swirl && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { 94a87295e8Spbrook env->pc += 4; 95a87295e8Spbrook do_m68k_semihosting(env, env->dregs[0]); 96a87295e8Spbrook return; 97a87295e8Spbrook } 98259186a7SAndreas Färber cs->halted = 1; 9927103424SAndreas Färber cs->exception_index = EXCP_HLT; 1005638d180SAndreas Färber cpu_loop_exit(cs); 101a87295e8Spbrook return; 1020633879fSpbrook } 10327103424SAndreas Färber if (cs->exception_index >= EXCP_TRAP0 10427103424SAndreas Färber && cs->exception_index <= EXCP_TRAP15) { 1050633879fSpbrook /* Move the PC after the trap instruction. */ 1060633879fSpbrook retaddr += 2; 1070633879fSpbrook } 1080633879fSpbrook } 1090633879fSpbrook 11027103424SAndreas Färber vector = cs->exception_index << 2; 1110633879fSpbrook 1120633879fSpbrook fmt |= 0x40000000; 1130633879fSpbrook fmt |= vector << 16; 1140633879fSpbrook fmt |= env->sr; 11599c51448SRichard Henderson fmt |= cpu_m68k_get_ccr(env); 1160633879fSpbrook 11720dcee94Spbrook env->sr |= SR_S; 11820dcee94Spbrook if (is_hw) { 11920dcee94Spbrook env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 12020dcee94Spbrook env->sr &= ~SR_M; 12120dcee94Spbrook } 12220dcee94Spbrook m68k_switch_sp(env); 1230c8ff723SGreg Ungerer sp = env->aregs[7]; 1240c8ff723SGreg Ungerer fmt |= (sp & 3) << 28; 12520dcee94Spbrook 1260633879fSpbrook /* ??? This could cause MMU faults. */ 1270633879fSpbrook sp &= ~3; 1280633879fSpbrook sp -= 4; 12931871141SBlue Swirl cpu_stl_kernel(env, sp, retaddr); 1300633879fSpbrook sp -= 4; 13131871141SBlue Swirl cpu_stl_kernel(env, sp, fmt); 1320633879fSpbrook env->aregs[7] = sp; 1330633879fSpbrook /* Jump to vector. */ 13431871141SBlue Swirl env->pc = cpu_ldl_kernel(env, env->vbr + vector); 1350633879fSpbrook } 1360633879fSpbrook 13797a8ea5aSAndreas Färber void m68k_cpu_do_interrupt(CPUState *cs) 1383c688828SBlue Swirl { 13997a8ea5aSAndreas Färber M68kCPU *cpu = M68K_CPU(cs); 14097a8ea5aSAndreas Färber CPUM68KState *env = &cpu->env; 14197a8ea5aSAndreas Färber 14231871141SBlue Swirl do_interrupt_all(env, 0); 1433c688828SBlue Swirl } 1443c688828SBlue Swirl 145ab409bb3SRichard Henderson static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 1463c688828SBlue Swirl { 14731871141SBlue Swirl do_interrupt_all(env, 1); 1483c688828SBlue Swirl } 1490633879fSpbrook #endif 150e1f3808eSpbrook 151ab409bb3SRichard Henderson bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 152ab409bb3SRichard Henderson { 153ab409bb3SRichard Henderson M68kCPU *cpu = M68K_CPU(cs); 154ab409bb3SRichard Henderson CPUM68KState *env = &cpu->env; 155ab409bb3SRichard Henderson 156ab409bb3SRichard Henderson if (interrupt_request & CPU_INTERRUPT_HARD 157ab409bb3SRichard Henderson && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { 158ab409bb3SRichard Henderson /* Real hardware gets the interrupt vector via an IACK cycle 159ab409bb3SRichard Henderson at this point. Current emulated hardware doesn't rely on 160ab409bb3SRichard Henderson this, so we provide/save the vector when the interrupt is 161ab409bb3SRichard Henderson first signalled. */ 162ab409bb3SRichard Henderson cs->exception_index = env->pending_vector; 163ab409bb3SRichard Henderson do_interrupt_m68k_hardirq(env); 164ab409bb3SRichard Henderson return true; 165ab409bb3SRichard Henderson } 166ab409bb3SRichard Henderson return false; 167ab409bb3SRichard Henderson } 168ab409bb3SRichard Henderson 16931871141SBlue Swirl static void raise_exception(CPUM68KState *env, int tt) 170e1f3808eSpbrook { 17127103424SAndreas Färber CPUState *cs = CPU(m68k_env_get_cpu(env)); 17227103424SAndreas Färber 17327103424SAndreas Färber cs->exception_index = tt; 1745638d180SAndreas Färber cpu_loop_exit(cs); 175e1f3808eSpbrook } 176e1f3808eSpbrook 17731871141SBlue Swirl void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) 178e1f3808eSpbrook { 17931871141SBlue Swirl raise_exception(env, tt); 180e1f3808eSpbrook } 181e1f3808eSpbrook 1822b3e3cfeSAndreas Färber void HELPER(divu)(CPUM68KState *env, uint32_t word) 183e1f3808eSpbrook { 184e1f3808eSpbrook uint32_t num; 185e1f3808eSpbrook uint32_t den; 186e1f3808eSpbrook uint32_t quot; 187e1f3808eSpbrook uint32_t rem; 188e1f3808eSpbrook 189e1f3808eSpbrook num = env->div1; 190e1f3808eSpbrook den = env->div2; 191e1f3808eSpbrook /* ??? This needs to make sure the throwing location is accurate. */ 19231871141SBlue Swirl if (den == 0) { 19331871141SBlue Swirl raise_exception(env, EXCP_DIV0); 19431871141SBlue Swirl } 195e1f3808eSpbrook quot = num / den; 196e1f3808eSpbrook rem = num % den; 197620c6cf6SRichard Henderson 198620c6cf6SRichard Henderson env->cc_v = (word && quot > 0xffff ? -1 : 0); 199620c6cf6SRichard Henderson env->cc_z = quot; 200620c6cf6SRichard Henderson env->cc_n = quot; 201620c6cf6SRichard Henderson env->cc_c = 0; 202620c6cf6SRichard Henderson 203e1f3808eSpbrook env->div1 = quot; 204e1f3808eSpbrook env->div2 = rem; 205e1f3808eSpbrook } 206e1f3808eSpbrook 2072b3e3cfeSAndreas Färber void HELPER(divs)(CPUM68KState *env, uint32_t word) 208e1f3808eSpbrook { 209e1f3808eSpbrook int32_t num; 210e1f3808eSpbrook int32_t den; 211e1f3808eSpbrook int32_t quot; 212e1f3808eSpbrook int32_t rem; 213e1f3808eSpbrook 214e1f3808eSpbrook num = env->div1; 215e1f3808eSpbrook den = env->div2; 21631871141SBlue Swirl if (den == 0) { 21731871141SBlue Swirl raise_exception(env, EXCP_DIV0); 21831871141SBlue Swirl } 219e1f3808eSpbrook quot = num / den; 220e1f3808eSpbrook rem = num % den; 221620c6cf6SRichard Henderson 222620c6cf6SRichard Henderson env->cc_v = (word && quot != (int16_t)quot ? -1 : 0); 223620c6cf6SRichard Henderson env->cc_z = quot; 224620c6cf6SRichard Henderson env->cc_n = quot; 225620c6cf6SRichard Henderson env->cc_c = 0; 226620c6cf6SRichard Henderson 227e1f3808eSpbrook env->div1 = quot; 228e1f3808eSpbrook env->div2 = rem; 229e1f3808eSpbrook } 230