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 */ 190633879fSpbrook #include "exec.h" 20e1f3808eSpbrook #include "helpers.h" 210633879fSpbrook 220633879fSpbrook #if defined(CONFIG_USER_ONLY) 230633879fSpbrook 243c688828SBlue Swirl void do_interrupt(CPUState *env1) 250633879fSpbrook { 263c688828SBlue Swirl env1->exception_index = -1; 273c688828SBlue Swirl } 283c688828SBlue Swirl 293c688828SBlue Swirl void do_interrupt_m68k_hardirq(CPUState *env1) 303c688828SBlue Swirl { 310633879fSpbrook } 320633879fSpbrook 330633879fSpbrook #else 340633879fSpbrook 35a87295e8Spbrook extern int semihosting_enabled; 36a87295e8Spbrook 370633879fSpbrook #define MMUSUFFIX _mmu 380633879fSpbrook 390633879fSpbrook #define SHIFT 0 400633879fSpbrook #include "softmmu_template.h" 410633879fSpbrook 420633879fSpbrook #define SHIFT 1 430633879fSpbrook #include "softmmu_template.h" 440633879fSpbrook 450633879fSpbrook #define SHIFT 2 460633879fSpbrook #include "softmmu_template.h" 470633879fSpbrook 480633879fSpbrook #define SHIFT 3 490633879fSpbrook #include "softmmu_template.h" 500633879fSpbrook 510633879fSpbrook /* Try to fill the TLB and return an exception if error. If retaddr is 520633879fSpbrook NULL, it means that the function was called in C code (i.e. not 530633879fSpbrook from generated code or from helper.c) */ 540633879fSpbrook /* XXX: fix it to restore all registers */ 556ebbf390Sj_mayer void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) 560633879fSpbrook { 570633879fSpbrook TranslationBlock *tb; 580633879fSpbrook CPUState *saved_env; 5944f8625dSbellard unsigned long pc; 600633879fSpbrook int ret; 610633879fSpbrook 620633879fSpbrook /* XXX: hack to restore env in all cases, even if not called from 630633879fSpbrook generated code */ 640633879fSpbrook saved_env = env; 650633879fSpbrook env = cpu_single_env; 666ebbf390Sj_mayer ret = cpu_m68k_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); 67551bd27fSths if (unlikely(ret)) { 680633879fSpbrook if (retaddr) { 690633879fSpbrook /* now we have a real cpu fault */ 7044f8625dSbellard pc = (unsigned long)retaddr; 710633879fSpbrook tb = tb_find_pc(pc); 720633879fSpbrook if (tb) { 730633879fSpbrook /* the PC is inside the translated code. It means that we have 740633879fSpbrook a virtual CPU fault */ 75618ba8e6SStefan Weil cpu_restore_state(tb, env, pc); 760633879fSpbrook } 770633879fSpbrook } 781162c041SBlue Swirl cpu_loop_exit(env); 790633879fSpbrook } 800633879fSpbrook env = saved_env; 810633879fSpbrook } 820633879fSpbrook 830633879fSpbrook static void do_rte(void) 840633879fSpbrook { 850633879fSpbrook uint32_t sp; 860633879fSpbrook uint32_t fmt; 870633879fSpbrook 880633879fSpbrook sp = env->aregs[7]; 890633879fSpbrook fmt = ldl_kernel(sp); 900633879fSpbrook env->pc = ldl_kernel(sp + 4); 910633879fSpbrook sp |= (fmt >> 28) & 3; 920633879fSpbrook env->sr = fmt & 0xffff; 9320dcee94Spbrook m68k_switch_sp(env); 940633879fSpbrook env->aregs[7] = sp + 8; 950633879fSpbrook } 960633879fSpbrook 973c688828SBlue Swirl static void do_interrupt_all(int is_hw) 980633879fSpbrook { 990633879fSpbrook uint32_t sp; 1000633879fSpbrook uint32_t fmt; 1010633879fSpbrook uint32_t retaddr; 1020633879fSpbrook uint32_t vector; 1030633879fSpbrook 1040633879fSpbrook fmt = 0; 1050633879fSpbrook retaddr = env->pc; 1060633879fSpbrook 1070633879fSpbrook if (!is_hw) { 1080633879fSpbrook switch (env->exception_index) { 1090633879fSpbrook case EXCP_RTE: 1100633879fSpbrook /* Return from an exception. */ 1110633879fSpbrook do_rte(); 1120633879fSpbrook return; 113a87295e8Spbrook case EXCP_HALT_INSN: 114a87295e8Spbrook if (semihosting_enabled 115a87295e8Spbrook && (env->sr & SR_S) != 0 116a87295e8Spbrook && (env->pc & 3) == 0 117a87295e8Spbrook && lduw_code(env->pc - 4) == 0x4e71 118a87295e8Spbrook && ldl_code(env->pc) == 0x4e7bf000) { 119a87295e8Spbrook env->pc += 4; 120a87295e8Spbrook do_m68k_semihosting(env, env->dregs[0]); 121a87295e8Spbrook return; 122a87295e8Spbrook } 123a87295e8Spbrook env->halted = 1; 124a87295e8Spbrook env->exception_index = EXCP_HLT; 1251162c041SBlue Swirl cpu_loop_exit(env); 126a87295e8Spbrook return; 1270633879fSpbrook } 1280633879fSpbrook if (env->exception_index >= EXCP_TRAP0 1290633879fSpbrook && env->exception_index <= EXCP_TRAP15) { 1300633879fSpbrook /* Move the PC after the trap instruction. */ 1310633879fSpbrook retaddr += 2; 1320633879fSpbrook } 1330633879fSpbrook } 1340633879fSpbrook 1350633879fSpbrook vector = env->exception_index << 2; 1360633879fSpbrook 1370cf5c677Spbrook sp = env->aregs[7]; 1380cf5c677Spbrook 1390633879fSpbrook fmt |= 0x40000000; 1400633879fSpbrook fmt |= (sp & 3) << 28; 1410633879fSpbrook fmt |= vector << 16; 1420633879fSpbrook fmt |= env->sr; 1430633879fSpbrook 14420dcee94Spbrook env->sr |= SR_S; 14520dcee94Spbrook if (is_hw) { 14620dcee94Spbrook env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 14720dcee94Spbrook env->sr &= ~SR_M; 14820dcee94Spbrook } 14920dcee94Spbrook m68k_switch_sp(env); 15020dcee94Spbrook 1510633879fSpbrook /* ??? This could cause MMU faults. */ 1520633879fSpbrook sp &= ~3; 1530633879fSpbrook sp -= 4; 1540633879fSpbrook stl_kernel(sp, retaddr); 1550633879fSpbrook sp -= 4; 1560633879fSpbrook stl_kernel(sp, fmt); 1570633879fSpbrook env->aregs[7] = sp; 1580633879fSpbrook /* Jump to vector. */ 1590633879fSpbrook env->pc = ldl_kernel(env->vbr + vector); 1600633879fSpbrook } 1610633879fSpbrook 1623c688828SBlue Swirl void do_interrupt(CPUState *env1) 1633c688828SBlue Swirl { 1643c688828SBlue Swirl CPUState *saved_env; 1653c688828SBlue Swirl 1663c688828SBlue Swirl saved_env = env; 1673c688828SBlue Swirl env = env1; 1683c688828SBlue Swirl do_interrupt_all(0); 1693c688828SBlue Swirl env = saved_env; 1703c688828SBlue Swirl } 1713c688828SBlue Swirl 1723c688828SBlue Swirl void do_interrupt_m68k_hardirq(CPUState *env1) 1733c688828SBlue Swirl { 1743c688828SBlue Swirl CPUState *saved_env; 1753c688828SBlue Swirl 1763c688828SBlue Swirl saved_env = env; 1773c688828SBlue Swirl env = env1; 1783c688828SBlue Swirl do_interrupt_all(1); 1793c688828SBlue Swirl env = saved_env; 1803c688828SBlue Swirl } 1810633879fSpbrook #endif 182e1f3808eSpbrook 183e1f3808eSpbrook static void raise_exception(int tt) 184e1f3808eSpbrook { 185e1f3808eSpbrook env->exception_index = tt; 1861162c041SBlue Swirl cpu_loop_exit(env); 187e1f3808eSpbrook } 188e1f3808eSpbrook 189e1f3808eSpbrook void HELPER(raise_exception)(uint32_t tt) 190e1f3808eSpbrook { 191e1f3808eSpbrook raise_exception(tt); 192e1f3808eSpbrook } 193e1f3808eSpbrook 194e1f3808eSpbrook void HELPER(divu)(CPUState *env, uint32_t word) 195e1f3808eSpbrook { 196e1f3808eSpbrook uint32_t num; 197e1f3808eSpbrook uint32_t den; 198e1f3808eSpbrook uint32_t quot; 199e1f3808eSpbrook uint32_t rem; 200e1f3808eSpbrook uint32_t flags; 201e1f3808eSpbrook 202e1f3808eSpbrook num = env->div1; 203e1f3808eSpbrook den = env->div2; 204e1f3808eSpbrook /* ??? This needs to make sure the throwing location is accurate. */ 205e1f3808eSpbrook if (den == 0) 206e1f3808eSpbrook raise_exception(EXCP_DIV0); 207e1f3808eSpbrook quot = num / den; 208e1f3808eSpbrook rem = num % den; 209e1f3808eSpbrook flags = 0; 210e1f3808eSpbrook /* Avoid using a PARAM1 of zero. This breaks dyngen because it uses 211e1f3808eSpbrook the address of a symbol, and gcc knows symbols can't have address 212e1f3808eSpbrook zero. */ 213e1f3808eSpbrook if (word && quot > 0xffff) 214e1f3808eSpbrook flags |= CCF_V; 215e1f3808eSpbrook if (quot == 0) 216e1f3808eSpbrook flags |= CCF_Z; 217e1f3808eSpbrook else if ((int32_t)quot < 0) 218e1f3808eSpbrook flags |= CCF_N; 219e1f3808eSpbrook env->div1 = quot; 220e1f3808eSpbrook env->div2 = rem; 221e1f3808eSpbrook env->cc_dest = flags; 222e1f3808eSpbrook } 223e1f3808eSpbrook 224e1f3808eSpbrook void HELPER(divs)(CPUState *env, uint32_t word) 225e1f3808eSpbrook { 226e1f3808eSpbrook int32_t num; 227e1f3808eSpbrook int32_t den; 228e1f3808eSpbrook int32_t quot; 229e1f3808eSpbrook int32_t rem; 230e1f3808eSpbrook int32_t flags; 231e1f3808eSpbrook 232e1f3808eSpbrook num = env->div1; 233e1f3808eSpbrook den = env->div2; 234e1f3808eSpbrook if (den == 0) 235e1f3808eSpbrook raise_exception(EXCP_DIV0); 236e1f3808eSpbrook quot = num / den; 237e1f3808eSpbrook rem = num % den; 238e1f3808eSpbrook flags = 0; 239e1f3808eSpbrook if (word && quot != (int16_t)quot) 240e1f3808eSpbrook flags |= CCF_V; 241e1f3808eSpbrook if (quot == 0) 242e1f3808eSpbrook flags |= CCF_Z; 243e1f3808eSpbrook else if (quot < 0) 244e1f3808eSpbrook flags |= CCF_N; 245e1f3808eSpbrook env->div1 = quot; 246e1f3808eSpbrook env->div2 = rem; 247e1f3808eSpbrook env->cc_dest = flags; 248e1f3808eSpbrook } 249