1e6e5906bSpbrook /* 2e6e5906bSpbrook * m68k op helpers 3e6e5906bSpbrook * 40633879fSpbrook * Copyright (c) 2006-2007 CodeSourcery 5e6e5906bSpbrook * Written by Paul Brook 6e6e5906bSpbrook * 7e6e5906bSpbrook * This library is free software; you can redistribute it and/or 8e6e5906bSpbrook * modify it under the terms of the GNU Lesser General Public 9e6e5906bSpbrook * License as published by the Free Software Foundation; either 10e6e5906bSpbrook * version 2 of the License, or (at your option) any later version. 11e6e5906bSpbrook * 12e6e5906bSpbrook * This library is distributed in the hope that it will be useful, 13e6e5906bSpbrook * but WITHOUT ANY WARRANTY; without even the implied warranty of 14e6e5906bSpbrook * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15e6e5906bSpbrook * General Public License for more details. 16e6e5906bSpbrook * 17e6e5906bSpbrook * You should have received a copy of the GNU Lesser General Public 188167ee88SBlue Swirl * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19e6e5906bSpbrook */ 20e6e5906bSpbrook 21d8416665SPeter Maydell #include "qemu/osdep.h" 22e6e5906bSpbrook #include "cpu.h" 2363c91552SPaolo Bonzini #include "exec/exec-all.h" 24022c62cbSPaolo Bonzini #include "exec/gdbstub.h" 25e6e5906bSpbrook 262ef6175aSRichard Henderson #include "exec/helper-proto.h" 27e1f3808eSpbrook 28e1f3808eSpbrook #define SIGNBIT (1u << 31) 29e1f3808eSpbrook 3011150915SAndreas Färber /* Sort alphabetically, except for "any". */ 3111150915SAndreas Färber static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b) 3211150915SAndreas Färber { 3311150915SAndreas Färber ObjectClass *class_a = (ObjectClass *)a; 3411150915SAndreas Färber ObjectClass *class_b = (ObjectClass *)b; 3511150915SAndreas Färber const char *name_a, *name_b; 36aaed909aSbellard 3711150915SAndreas Färber name_a = object_class_get_name(class_a); 3811150915SAndreas Färber name_b = object_class_get_name(class_b); 397a9f812bSAndreas Färber if (strcmp(name_a, "any-" TYPE_M68K_CPU) == 0) { 4011150915SAndreas Färber return 1; 417a9f812bSAndreas Färber } else if (strcmp(name_b, "any-" TYPE_M68K_CPU) == 0) { 4211150915SAndreas Färber return -1; 4311150915SAndreas Färber } else { 4411150915SAndreas Färber return strcasecmp(name_a, name_b); 4511150915SAndreas Färber } 4611150915SAndreas Färber } 470402f767Spbrook 4811150915SAndreas Färber static void m68k_cpu_list_entry(gpointer data, gpointer user_data) 4911150915SAndreas Färber { 5011150915SAndreas Färber ObjectClass *c = data; 5192a31361SAndreas Färber CPUListState *s = user_data; 527a9f812bSAndreas Färber const char *typename; 537a9f812bSAndreas Färber char *name; 5411150915SAndreas Färber 557a9f812bSAndreas Färber typename = object_class_get_name(c); 567a9f812bSAndreas Färber name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_M68K_CPU)); 5711150915SAndreas Färber (*s->cpu_fprintf)(s->file, "%s\n", 587a9f812bSAndreas Färber name); 597a9f812bSAndreas Färber g_free(name); 6011150915SAndreas Färber } 610402f767Spbrook 629a78eeadSStefan Weil void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) 63009a4356SLaurent Vivier { 6492a31361SAndreas Färber CPUListState s = { 6511150915SAndreas Färber .file = f, 6611150915SAndreas Färber .cpu_fprintf = cpu_fprintf, 6711150915SAndreas Färber }; 6811150915SAndreas Färber GSList *list; 69009a4356SLaurent Vivier 7011150915SAndreas Färber list = object_class_get_list(TYPE_M68K_CPU, false); 7111150915SAndreas Färber list = g_slist_sort(list, m68k_cpu_list_compare); 7211150915SAndreas Färber g_slist_foreach(list, m68k_cpu_list_entry, &s); 7311150915SAndreas Färber g_slist_free(list); 74009a4356SLaurent Vivier } 75009a4356SLaurent Vivier 76f83311e4SLaurent Vivier static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) 7756aebc89Spbrook { 7856aebc89Spbrook if (n < 8) { 79f83311e4SLaurent Vivier float_status s; 80f83311e4SLaurent Vivier stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); 8156aebc89Spbrook return 8; 8256aebc89Spbrook } 83ba624944SLaurent Vivier switch (n) { 84ba624944SLaurent Vivier case 8: /* fpcontrol */ 85ba624944SLaurent Vivier stl_be_p(mem_buf, env->fpcr); 86ba624944SLaurent Vivier return 4; 87ba624944SLaurent Vivier case 9: /* fpstatus */ 88ba624944SLaurent Vivier stl_be_p(mem_buf, env->fpsr); 89ba624944SLaurent Vivier return 4; 90ba624944SLaurent Vivier case 10: /* fpiar, not implemented */ 9156aebc89Spbrook memset(mem_buf, 0, 4); 9256aebc89Spbrook return 4; 9356aebc89Spbrook } 9456aebc89Spbrook return 0; 9556aebc89Spbrook } 9656aebc89Spbrook 97f83311e4SLaurent Vivier static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) 9856aebc89Spbrook { 9956aebc89Spbrook if (n < 8) { 100f83311e4SLaurent Vivier float_status s; 101f83311e4SLaurent Vivier env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s); 10256aebc89Spbrook return 8; 10356aebc89Spbrook } 104ba624944SLaurent Vivier switch (n) { 105ba624944SLaurent Vivier case 8: /* fpcontrol */ 106ba624944SLaurent Vivier cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); 107ba624944SLaurent Vivier return 4; 108ba624944SLaurent Vivier case 9: /* fpstatus */ 109ba624944SLaurent Vivier env->fpsr = ldl_p(mem_buf); 110ba624944SLaurent Vivier return 4; 111ba624944SLaurent Vivier case 10: /* fpiar, not implemented */ 11256aebc89Spbrook return 4; 11356aebc89Spbrook } 11456aebc89Spbrook return 0; 11556aebc89Spbrook } 11656aebc89Spbrook 1175a4526b2SLaurent Vivier static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) 1185a4526b2SLaurent Vivier { 1195a4526b2SLaurent Vivier if (n < 8) { 1205a4526b2SLaurent Vivier stw_be_p(mem_buf, env->fregs[n].l.upper); 1215a4526b2SLaurent Vivier memset(mem_buf + 2, 0, 2); 1225a4526b2SLaurent Vivier stq_be_p(mem_buf + 4, env->fregs[n].l.lower); 1235a4526b2SLaurent Vivier return 12; 1245a4526b2SLaurent Vivier } 1255a4526b2SLaurent Vivier switch (n) { 1265a4526b2SLaurent Vivier case 8: /* fpcontrol */ 1275a4526b2SLaurent Vivier stl_be_p(mem_buf, env->fpcr); 1285a4526b2SLaurent Vivier return 4; 1295a4526b2SLaurent Vivier case 9: /* fpstatus */ 1305a4526b2SLaurent Vivier stl_be_p(mem_buf, env->fpsr); 1315a4526b2SLaurent Vivier return 4; 1325a4526b2SLaurent Vivier case 10: /* fpiar, not implemented */ 1335a4526b2SLaurent Vivier memset(mem_buf, 0, 4); 1345a4526b2SLaurent Vivier return 4; 1355a4526b2SLaurent Vivier } 1365a4526b2SLaurent Vivier return 0; 1375a4526b2SLaurent Vivier } 1385a4526b2SLaurent Vivier 1395a4526b2SLaurent Vivier static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) 1405a4526b2SLaurent Vivier { 1415a4526b2SLaurent Vivier if (n < 8) { 1425a4526b2SLaurent Vivier env->fregs[n].l.upper = lduw_be_p(mem_buf); 1435a4526b2SLaurent Vivier env->fregs[n].l.lower = ldq_be_p(mem_buf + 4); 1445a4526b2SLaurent Vivier return 12; 1455a4526b2SLaurent Vivier } 1465a4526b2SLaurent Vivier switch (n) { 1475a4526b2SLaurent Vivier case 8: /* fpcontrol */ 148ba624944SLaurent Vivier cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); 1495a4526b2SLaurent Vivier return 4; 1505a4526b2SLaurent Vivier case 9: /* fpstatus */ 1515a4526b2SLaurent Vivier env->fpsr = ldl_p(mem_buf); 1525a4526b2SLaurent Vivier return 4; 1535a4526b2SLaurent Vivier case 10: /* fpiar, not implemented */ 1545a4526b2SLaurent Vivier return 4; 1555a4526b2SLaurent Vivier } 1565a4526b2SLaurent Vivier return 0; 1575a4526b2SLaurent Vivier } 1585a4526b2SLaurent Vivier 1596d1bbc62SAndreas Färber void m68k_cpu_init_gdb(M68kCPU *cpu) 1606d1bbc62SAndreas Färber { 16122169d41SAndreas Färber CPUState *cs = CPU(cpu); 1626d1bbc62SAndreas Färber CPUM68KState *env = &cpu->env; 1636d1bbc62SAndreas Färber 16411150915SAndreas Färber if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { 165f83311e4SLaurent Vivier gdb_register_coprocessor(cs, cf_fpu_gdb_get_reg, cf_fpu_gdb_set_reg, 16611150915SAndreas Färber 11, "cf-fp.xml", 18); 1675a4526b2SLaurent Vivier } else if (m68k_feature(env, M68K_FEATURE_FPU)) { 1685a4526b2SLaurent Vivier gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg, 1695a4526b2SLaurent Vivier m68k_fpu_gdb_set_reg, 11, "m68k-fp.xml", 18); 170aaed909aSbellard } 17111150915SAndreas Färber /* TODO: Add [E]MAC registers. */ 172aaed909aSbellard } 173aaed909aSbellard 1746e22b28eSLaurent Vivier void HELPER(cf_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val) 1750633879fSpbrook { 176a47dddd7SAndreas Färber M68kCPU *cpu = m68k_env_get_cpu(env); 177a47dddd7SAndreas Färber 1780633879fSpbrook switch (reg) { 1796e22b28eSLaurent Vivier case M68K_CR_CACR: 18020dcee94Spbrook env->cacr = val; 18120dcee94Spbrook m68k_switch_sp(env); 18220dcee94Spbrook break; 1836e22b28eSLaurent Vivier case M68K_CR_ACR0: 1846e22b28eSLaurent Vivier case M68K_CR_ACR1: 1856e22b28eSLaurent Vivier case M68K_CR_ACR2: 1866e22b28eSLaurent Vivier case M68K_CR_ACR3: 18720dcee94Spbrook /* TODO: Implement Access Control Registers. */ 1880633879fSpbrook break; 1896e22b28eSLaurent Vivier case M68K_CR_VBR: 1900633879fSpbrook env->vbr = val; 1910633879fSpbrook break; 1920633879fSpbrook /* TODO: Implement control registers. */ 1930633879fSpbrook default: 1946e22b28eSLaurent Vivier cpu_abort(CPU(cpu), 1956e22b28eSLaurent Vivier "Unimplemented control register write 0x%x = 0x%x\n", 1966e22b28eSLaurent Vivier reg, val); 1976e22b28eSLaurent Vivier } 1986e22b28eSLaurent Vivier } 1996e22b28eSLaurent Vivier 2006e22b28eSLaurent Vivier void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val) 2016e22b28eSLaurent Vivier { 2026e22b28eSLaurent Vivier M68kCPU *cpu = m68k_env_get_cpu(env); 2036e22b28eSLaurent Vivier 2046e22b28eSLaurent Vivier switch (reg) { 2056e22b28eSLaurent Vivier /* MC680[1234]0 */ 2065fa9f1f2SLaurent Vivier case M68K_CR_SFC: 2075fa9f1f2SLaurent Vivier env->sfc = val & 7; 2085fa9f1f2SLaurent Vivier return; 2095fa9f1f2SLaurent Vivier case M68K_CR_DFC: 2105fa9f1f2SLaurent Vivier env->dfc = val & 7; 2115fa9f1f2SLaurent Vivier return; 2126e22b28eSLaurent Vivier case M68K_CR_VBR: 2136e22b28eSLaurent Vivier env->vbr = val; 2146e22b28eSLaurent Vivier return; 2156e22b28eSLaurent Vivier /* MC680[234]0 */ 2166e22b28eSLaurent Vivier case M68K_CR_CACR: 2176e22b28eSLaurent Vivier env->cacr = val; 2186e22b28eSLaurent Vivier m68k_switch_sp(env); 2196e22b28eSLaurent Vivier return; 2206e22b28eSLaurent Vivier /* MC680[34]0 */ 22188b2fef6SLaurent Vivier case M68K_CR_TC: 22288b2fef6SLaurent Vivier env->mmu.tcr = val; 22388b2fef6SLaurent Vivier return; 224*e55886c3SLaurent Vivier case M68K_CR_MMUSR: 225*e55886c3SLaurent Vivier env->mmu.mmusr = val; 226*e55886c3SLaurent Vivier return; 22788b2fef6SLaurent Vivier case M68K_CR_SRP: 22888b2fef6SLaurent Vivier env->mmu.srp = val; 22988b2fef6SLaurent Vivier return; 23088b2fef6SLaurent Vivier case M68K_CR_URP: 23188b2fef6SLaurent Vivier env->mmu.urp = val; 23288b2fef6SLaurent Vivier return; 2336e22b28eSLaurent Vivier case M68K_CR_USP: 2346e22b28eSLaurent Vivier env->sp[M68K_USP] = val; 2356e22b28eSLaurent Vivier return; 2366e22b28eSLaurent Vivier case M68K_CR_MSP: 2376e22b28eSLaurent Vivier env->sp[M68K_SSP] = val; 2386e22b28eSLaurent Vivier return; 2396e22b28eSLaurent Vivier case M68K_CR_ISP: 2406e22b28eSLaurent Vivier env->sp[M68K_ISP] = val; 2416e22b28eSLaurent Vivier return; 242c05c73b0SLaurent Vivier /* MC68040/MC68LC040 */ 243c05c73b0SLaurent Vivier case M68K_CR_ITT0: 244c05c73b0SLaurent Vivier env->mmu.ttr[M68K_ITTR0] = val; 245c05c73b0SLaurent Vivier return; 246c05c73b0SLaurent Vivier case M68K_CR_ITT1: 247c05c73b0SLaurent Vivier env->mmu.ttr[M68K_ITTR1] = val; 248c05c73b0SLaurent Vivier return; 249c05c73b0SLaurent Vivier case M68K_CR_DTT0: 250c05c73b0SLaurent Vivier env->mmu.ttr[M68K_DTTR0] = val; 251c05c73b0SLaurent Vivier return; 252c05c73b0SLaurent Vivier case M68K_CR_DTT1: 253c05c73b0SLaurent Vivier env->mmu.ttr[M68K_DTTR1] = val; 254c05c73b0SLaurent Vivier return; 2556e22b28eSLaurent Vivier } 256a47dddd7SAndreas Färber cpu_abort(CPU(cpu), "Unimplemented control register write 0x%x = 0x%x\n", 2570633879fSpbrook reg, val); 2580633879fSpbrook } 2596e22b28eSLaurent Vivier 2606e22b28eSLaurent Vivier uint32_t HELPER(m68k_movec_from)(CPUM68KState *env, uint32_t reg) 2616e22b28eSLaurent Vivier { 2626e22b28eSLaurent Vivier M68kCPU *cpu = m68k_env_get_cpu(env); 2636e22b28eSLaurent Vivier 2646e22b28eSLaurent Vivier switch (reg) { 2656e22b28eSLaurent Vivier /* MC680[1234]0 */ 2665fa9f1f2SLaurent Vivier case M68K_CR_SFC: 2675fa9f1f2SLaurent Vivier return env->sfc; 2685fa9f1f2SLaurent Vivier case M68K_CR_DFC: 2695fa9f1f2SLaurent Vivier return env->dfc; 2706e22b28eSLaurent Vivier case M68K_CR_VBR: 2716e22b28eSLaurent Vivier return env->vbr; 2726e22b28eSLaurent Vivier /* MC680[234]0 */ 2736e22b28eSLaurent Vivier case M68K_CR_CACR: 2746e22b28eSLaurent Vivier return env->cacr; 2756e22b28eSLaurent Vivier /* MC680[34]0 */ 27688b2fef6SLaurent Vivier case M68K_CR_TC: 27788b2fef6SLaurent Vivier return env->mmu.tcr; 278*e55886c3SLaurent Vivier case M68K_CR_MMUSR: 279*e55886c3SLaurent Vivier return env->mmu.mmusr; 28088b2fef6SLaurent Vivier case M68K_CR_SRP: 28188b2fef6SLaurent Vivier return env->mmu.srp; 2826e22b28eSLaurent Vivier case M68K_CR_USP: 2836e22b28eSLaurent Vivier return env->sp[M68K_USP]; 2846e22b28eSLaurent Vivier case M68K_CR_MSP: 2856e22b28eSLaurent Vivier return env->sp[M68K_SSP]; 2866e22b28eSLaurent Vivier case M68K_CR_ISP: 2876e22b28eSLaurent Vivier return env->sp[M68K_ISP]; 28888b2fef6SLaurent Vivier /* MC68040/MC68LC040 */ 28988b2fef6SLaurent Vivier case M68K_CR_URP: 29088b2fef6SLaurent Vivier return env->mmu.urp; 291c05c73b0SLaurent Vivier case M68K_CR_ITT0: 292c05c73b0SLaurent Vivier return env->mmu.ttr[M68K_ITTR0]; 293c05c73b0SLaurent Vivier case M68K_CR_ITT1: 294c05c73b0SLaurent Vivier return env->mmu.ttr[M68K_ITTR1]; 295c05c73b0SLaurent Vivier case M68K_CR_DTT0: 296c05c73b0SLaurent Vivier return env->mmu.ttr[M68K_DTTR0]; 297c05c73b0SLaurent Vivier case M68K_CR_DTT1: 298c05c73b0SLaurent Vivier return env->mmu.ttr[M68K_DTTR1]; 2996e22b28eSLaurent Vivier } 3006e22b28eSLaurent Vivier cpu_abort(CPU(cpu), "Unimplemented control register read 0x%x\n", 3016e22b28eSLaurent Vivier reg); 3020633879fSpbrook } 3030633879fSpbrook 304e1f3808eSpbrook void HELPER(set_macsr)(CPUM68KState *env, uint32_t val) 305acf930aaSpbrook { 306acf930aaSpbrook uint32_t acc; 307acf930aaSpbrook int8_t exthigh; 308acf930aaSpbrook uint8_t extlow; 309acf930aaSpbrook uint64_t regval; 310acf930aaSpbrook int i; 311acf930aaSpbrook if ((env->macsr ^ val) & (MACSR_FI | MACSR_SU)) { 312acf930aaSpbrook for (i = 0; i < 4; i++) { 313acf930aaSpbrook regval = env->macc[i]; 314acf930aaSpbrook exthigh = regval >> 40; 315acf930aaSpbrook if (env->macsr & MACSR_FI) { 316acf930aaSpbrook acc = regval >> 8; 317acf930aaSpbrook extlow = regval; 318acf930aaSpbrook } else { 319acf930aaSpbrook acc = regval; 320acf930aaSpbrook extlow = regval >> 32; 321acf930aaSpbrook } 322acf930aaSpbrook if (env->macsr & MACSR_FI) { 323acf930aaSpbrook regval = (((uint64_t)acc) << 8) | extlow; 324acf930aaSpbrook regval |= ((int64_t)exthigh) << 40; 325acf930aaSpbrook } else if (env->macsr & MACSR_SU) { 326acf930aaSpbrook regval = acc | (((int64_t)extlow) << 32); 327acf930aaSpbrook regval |= ((int64_t)exthigh) << 40; 328acf930aaSpbrook } else { 329acf930aaSpbrook regval = acc | (((uint64_t)extlow) << 32); 330acf930aaSpbrook regval |= ((uint64_t)(uint8_t)exthigh) << 40; 331acf930aaSpbrook } 332acf930aaSpbrook env->macc[i] = regval; 333acf930aaSpbrook } 334acf930aaSpbrook } 335acf930aaSpbrook env->macsr = val; 336acf930aaSpbrook } 337acf930aaSpbrook 33820dcee94Spbrook void m68k_switch_sp(CPUM68KState *env) 33920dcee94Spbrook { 34020dcee94Spbrook int new_sp; 34120dcee94Spbrook 34220dcee94Spbrook env->sp[env->current_sp] = env->aregs[7]; 3436e22b28eSLaurent Vivier if (m68k_feature(env, M68K_FEATURE_M68000)) { 3446e22b28eSLaurent Vivier if (env->sr & SR_S) { 3456e22b28eSLaurent Vivier if (env->sr & SR_M) { 3466e22b28eSLaurent Vivier new_sp = M68K_SSP; 3476e22b28eSLaurent Vivier } else { 3486e22b28eSLaurent Vivier new_sp = M68K_ISP; 3496e22b28eSLaurent Vivier } 3506e22b28eSLaurent Vivier } else { 3516e22b28eSLaurent Vivier new_sp = M68K_USP; 3526e22b28eSLaurent Vivier } 3536e22b28eSLaurent Vivier } else { 35420dcee94Spbrook new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP) 35520dcee94Spbrook ? M68K_SSP : M68K_USP; 3566e22b28eSLaurent Vivier } 35720dcee94Spbrook env->aregs[7] = env->sp[new_sp]; 35820dcee94Spbrook env->current_sp = new_sp; 35920dcee94Spbrook } 36020dcee94Spbrook 3610633879fSpbrook #if defined(CONFIG_USER_ONLY) 3620633879fSpbrook 36398670d47SLaurent Vivier int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, 36497b348e7SBlue Swirl int mmu_idx) 3650633879fSpbrook { 3667510454eSAndreas Färber M68kCPU *cpu = M68K_CPU(cs); 3677510454eSAndreas Färber 36827103424SAndreas Färber cs->exception_index = EXCP_ACCESS; 3697510454eSAndreas Färber cpu->env.mmu.ar = address; 3700633879fSpbrook return 1; 3710633879fSpbrook } 3720633879fSpbrook 3730633879fSpbrook #else 3740633879fSpbrook 37588b2fef6SLaurent Vivier /* MMU: 68040 only */ 3764fcc562bSPaul Brook 377c05c73b0SLaurent Vivier static int check_TTR(uint32_t ttr, int *prot, target_ulong addr, 378c05c73b0SLaurent Vivier int access_type) 379c05c73b0SLaurent Vivier { 380c05c73b0SLaurent Vivier uint32_t base, mask; 381c05c73b0SLaurent Vivier 382c05c73b0SLaurent Vivier /* check if transparent translation is enabled */ 383c05c73b0SLaurent Vivier if ((ttr & M68K_TTR_ENABLED) == 0) { 384c05c73b0SLaurent Vivier return 0; 385c05c73b0SLaurent Vivier } 386c05c73b0SLaurent Vivier 387c05c73b0SLaurent Vivier /* check mode access */ 388c05c73b0SLaurent Vivier switch (ttr & M68K_TTR_SFIELD) { 389c05c73b0SLaurent Vivier case M68K_TTR_SFIELD_USER: 390c05c73b0SLaurent Vivier /* match only if user */ 391c05c73b0SLaurent Vivier if ((access_type & ACCESS_SUPER) != 0) { 392c05c73b0SLaurent Vivier return 0; 393c05c73b0SLaurent Vivier } 394c05c73b0SLaurent Vivier break; 395c05c73b0SLaurent Vivier case M68K_TTR_SFIELD_SUPER: 396c05c73b0SLaurent Vivier /* match only if supervisor */ 397c05c73b0SLaurent Vivier if ((access_type & ACCESS_SUPER) == 0) { 398c05c73b0SLaurent Vivier return 0; 399c05c73b0SLaurent Vivier } 400c05c73b0SLaurent Vivier break; 401c05c73b0SLaurent Vivier default: 402c05c73b0SLaurent Vivier /* all other values disable mode matching (FC2) */ 403c05c73b0SLaurent Vivier break; 404c05c73b0SLaurent Vivier } 405c05c73b0SLaurent Vivier 406c05c73b0SLaurent Vivier /* check address matching */ 407c05c73b0SLaurent Vivier 408c05c73b0SLaurent Vivier base = ttr & M68K_TTR_ADDR_BASE; 409c05c73b0SLaurent Vivier mask = (ttr & M68K_TTR_ADDR_MASK) ^ M68K_TTR_ADDR_MASK; 410c05c73b0SLaurent Vivier mask <<= M68K_TTR_ADDR_MASK_SHIFT; 411c05c73b0SLaurent Vivier 412c05c73b0SLaurent Vivier if ((addr & mask) != (base & mask)) { 413c05c73b0SLaurent Vivier return 0; 414c05c73b0SLaurent Vivier } 415c05c73b0SLaurent Vivier 416c05c73b0SLaurent Vivier *prot = PAGE_READ | PAGE_EXEC; 417c05c73b0SLaurent Vivier if ((ttr & M68K_DESC_WRITEPROT) == 0) { 418c05c73b0SLaurent Vivier *prot |= PAGE_WRITE; 419c05c73b0SLaurent Vivier } 420c05c73b0SLaurent Vivier 421c05c73b0SLaurent Vivier return 1; 422c05c73b0SLaurent Vivier } 423c05c73b0SLaurent Vivier 42488b2fef6SLaurent Vivier static int get_physical_address(CPUM68KState *env, hwaddr *physical, 42588b2fef6SLaurent Vivier int *prot, target_ulong address, 42688b2fef6SLaurent Vivier int access_type, target_ulong *page_size) 42788b2fef6SLaurent Vivier { 42888b2fef6SLaurent Vivier M68kCPU *cpu = m68k_env_get_cpu(env); 42988b2fef6SLaurent Vivier CPUState *cs = CPU(cpu); 43088b2fef6SLaurent Vivier uint32_t entry; 43188b2fef6SLaurent Vivier uint32_t next; 43288b2fef6SLaurent Vivier target_ulong page_mask; 43388b2fef6SLaurent Vivier bool debug = access_type & ACCESS_DEBUG; 43488b2fef6SLaurent Vivier int page_bits; 435c05c73b0SLaurent Vivier int i; 436c05c73b0SLaurent Vivier 437c05c73b0SLaurent Vivier /* Transparent Translation (physical = logical) */ 438c05c73b0SLaurent Vivier for (i = 0; i < M68K_MAX_TTR; i++) { 439c05c73b0SLaurent Vivier if (check_TTR(env->mmu.TTR(access_type, i), 440c05c73b0SLaurent Vivier prot, address, access_type)) { 441*e55886c3SLaurent Vivier if (access_type & ACCESS_PTEST) { 442*e55886c3SLaurent Vivier /* Transparent Translation Register bit */ 443*e55886c3SLaurent Vivier env->mmu.mmusr = M68K_MMU_T_040 | M68K_MMU_R_040; 444*e55886c3SLaurent Vivier } 445c05c73b0SLaurent Vivier *physical = address & TARGET_PAGE_MASK; 446c05c73b0SLaurent Vivier *page_size = TARGET_PAGE_SIZE; 447c05c73b0SLaurent Vivier return 0; 448c05c73b0SLaurent Vivier } 449c05c73b0SLaurent Vivier } 45088b2fef6SLaurent Vivier 45188b2fef6SLaurent Vivier /* Page Table Root Pointer */ 45288b2fef6SLaurent Vivier *prot = PAGE_READ | PAGE_WRITE; 45388b2fef6SLaurent Vivier if (access_type & ACCESS_CODE) { 45488b2fef6SLaurent Vivier *prot |= PAGE_EXEC; 45588b2fef6SLaurent Vivier } 45688b2fef6SLaurent Vivier if (access_type & ACCESS_SUPER) { 45788b2fef6SLaurent Vivier next = env->mmu.srp; 45888b2fef6SLaurent Vivier } else { 45988b2fef6SLaurent Vivier next = env->mmu.urp; 46088b2fef6SLaurent Vivier } 46188b2fef6SLaurent Vivier 46288b2fef6SLaurent Vivier /* Root Index */ 46388b2fef6SLaurent Vivier entry = M68K_POINTER_BASE(next) | M68K_ROOT_INDEX(address); 46488b2fef6SLaurent Vivier 46588b2fef6SLaurent Vivier next = ldl_phys(cs->as, entry); 46688b2fef6SLaurent Vivier if (!M68K_UDT_VALID(next)) { 46788b2fef6SLaurent Vivier return -1; 46888b2fef6SLaurent Vivier } 46988b2fef6SLaurent Vivier if (!(next & M68K_DESC_USED) && !debug) { 47088b2fef6SLaurent Vivier stl_phys(cs->as, entry, next | M68K_DESC_USED); 47188b2fef6SLaurent Vivier } 47288b2fef6SLaurent Vivier if (next & M68K_DESC_WRITEPROT) { 473*e55886c3SLaurent Vivier if (access_type & ACCESS_PTEST) { 474*e55886c3SLaurent Vivier env->mmu.mmusr |= M68K_MMU_WP_040; 475*e55886c3SLaurent Vivier } 47688b2fef6SLaurent Vivier *prot &= ~PAGE_WRITE; 47788b2fef6SLaurent Vivier if (access_type & ACCESS_STORE) { 47888b2fef6SLaurent Vivier return -1; 47988b2fef6SLaurent Vivier } 48088b2fef6SLaurent Vivier } 48188b2fef6SLaurent Vivier 48288b2fef6SLaurent Vivier /* Pointer Index */ 48388b2fef6SLaurent Vivier entry = M68K_POINTER_BASE(next) | M68K_POINTER_INDEX(address); 48488b2fef6SLaurent Vivier 48588b2fef6SLaurent Vivier next = ldl_phys(cs->as, entry); 48688b2fef6SLaurent Vivier if (!M68K_UDT_VALID(next)) { 48788b2fef6SLaurent Vivier return -1; 48888b2fef6SLaurent Vivier } 48988b2fef6SLaurent Vivier if (!(next & M68K_DESC_USED) && !debug) { 49088b2fef6SLaurent Vivier stl_phys(cs->as, entry, next | M68K_DESC_USED); 49188b2fef6SLaurent Vivier } 49288b2fef6SLaurent Vivier if (next & M68K_DESC_WRITEPROT) { 493*e55886c3SLaurent Vivier if (access_type & ACCESS_PTEST) { 494*e55886c3SLaurent Vivier env->mmu.mmusr |= M68K_MMU_WP_040; 495*e55886c3SLaurent Vivier } 49688b2fef6SLaurent Vivier *prot &= ~PAGE_WRITE; 49788b2fef6SLaurent Vivier if (access_type & ACCESS_STORE) { 49888b2fef6SLaurent Vivier return -1; 49988b2fef6SLaurent Vivier } 50088b2fef6SLaurent Vivier } 50188b2fef6SLaurent Vivier 50288b2fef6SLaurent Vivier /* Page Index */ 50388b2fef6SLaurent Vivier if (env->mmu.tcr & M68K_TCR_PAGE_8K) { 50488b2fef6SLaurent Vivier entry = M68K_8K_PAGE_BASE(next) | M68K_8K_PAGE_INDEX(address); 50588b2fef6SLaurent Vivier } else { 50688b2fef6SLaurent Vivier entry = M68K_4K_PAGE_BASE(next) | M68K_4K_PAGE_INDEX(address); 50788b2fef6SLaurent Vivier } 50888b2fef6SLaurent Vivier 50988b2fef6SLaurent Vivier next = ldl_phys(cs->as, entry); 51088b2fef6SLaurent Vivier 51188b2fef6SLaurent Vivier if (!M68K_PDT_VALID(next)) { 51288b2fef6SLaurent Vivier return -1; 51388b2fef6SLaurent Vivier } 51488b2fef6SLaurent Vivier if (M68K_PDT_INDIRECT(next)) { 51588b2fef6SLaurent Vivier next = ldl_phys(cs->as, M68K_INDIRECT_POINTER(next)); 51688b2fef6SLaurent Vivier } 51788b2fef6SLaurent Vivier if (access_type & ACCESS_STORE) { 51888b2fef6SLaurent Vivier if (next & M68K_DESC_WRITEPROT) { 51988b2fef6SLaurent Vivier if (!(next & M68K_DESC_USED) && !debug) { 52088b2fef6SLaurent Vivier stl_phys(cs->as, entry, next | M68K_DESC_USED); 52188b2fef6SLaurent Vivier } 52288b2fef6SLaurent Vivier } else if ((next & (M68K_DESC_MODIFIED | M68K_DESC_USED)) != 52388b2fef6SLaurent Vivier (M68K_DESC_MODIFIED | M68K_DESC_USED) && !debug) { 52488b2fef6SLaurent Vivier stl_phys(cs->as, entry, 52588b2fef6SLaurent Vivier next | (M68K_DESC_MODIFIED | M68K_DESC_USED)); 52688b2fef6SLaurent Vivier } 52788b2fef6SLaurent Vivier } else { 52888b2fef6SLaurent Vivier if (!(next & M68K_DESC_USED) && !debug) { 52988b2fef6SLaurent Vivier stl_phys(cs->as, entry, next | M68K_DESC_USED); 53088b2fef6SLaurent Vivier } 53188b2fef6SLaurent Vivier } 53288b2fef6SLaurent Vivier 53388b2fef6SLaurent Vivier if (env->mmu.tcr & M68K_TCR_PAGE_8K) { 53488b2fef6SLaurent Vivier page_bits = 13; 53588b2fef6SLaurent Vivier } else { 53688b2fef6SLaurent Vivier page_bits = 12; 53788b2fef6SLaurent Vivier } 53888b2fef6SLaurent Vivier *page_size = 1 << page_bits; 53988b2fef6SLaurent Vivier page_mask = ~(*page_size - 1); 54088b2fef6SLaurent Vivier *physical = next & page_mask; 54188b2fef6SLaurent Vivier 542*e55886c3SLaurent Vivier if (access_type & ACCESS_PTEST) { 543*e55886c3SLaurent Vivier env->mmu.mmusr |= next & M68K_MMU_SR_MASK_040; 544*e55886c3SLaurent Vivier env->mmu.mmusr |= *physical & 0xfffff000; 545*e55886c3SLaurent Vivier env->mmu.mmusr |= M68K_MMU_R_040; 546*e55886c3SLaurent Vivier } 547*e55886c3SLaurent Vivier 54888b2fef6SLaurent Vivier if (next & M68K_DESC_WRITEPROT) { 54988b2fef6SLaurent Vivier *prot &= ~PAGE_WRITE; 55088b2fef6SLaurent Vivier if (access_type & ACCESS_STORE) { 55188b2fef6SLaurent Vivier return -1; 55288b2fef6SLaurent Vivier } 55388b2fef6SLaurent Vivier } 55488b2fef6SLaurent Vivier if (next & M68K_DESC_SUPERONLY) { 55588b2fef6SLaurent Vivier if ((access_type & ACCESS_SUPER) == 0) { 55688b2fef6SLaurent Vivier return -1; 55788b2fef6SLaurent Vivier } 55888b2fef6SLaurent Vivier } 55988b2fef6SLaurent Vivier 56088b2fef6SLaurent Vivier return 0; 56188b2fef6SLaurent Vivier } 56288b2fef6SLaurent Vivier 56300b941e5SAndreas Färber hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 5644fcc562bSPaul Brook { 56588b2fef6SLaurent Vivier M68kCPU *cpu = M68K_CPU(cs); 56688b2fef6SLaurent Vivier CPUM68KState *env = &cpu->env; 56788b2fef6SLaurent Vivier hwaddr phys_addr; 56888b2fef6SLaurent Vivier int prot; 56988b2fef6SLaurent Vivier int access_type; 57088b2fef6SLaurent Vivier target_ulong page_size; 57188b2fef6SLaurent Vivier 57288b2fef6SLaurent Vivier if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) { 57388b2fef6SLaurent Vivier /* MMU disabled */ 5744fcc562bSPaul Brook return addr; 5754fcc562bSPaul Brook } 5764fcc562bSPaul Brook 57788b2fef6SLaurent Vivier access_type = ACCESS_DATA | ACCESS_DEBUG; 57888b2fef6SLaurent Vivier if (env->sr & SR_S) { 57988b2fef6SLaurent Vivier access_type |= ACCESS_SUPER; 58088b2fef6SLaurent Vivier } 58188b2fef6SLaurent Vivier if (get_physical_address(env, &phys_addr, &prot, 58288b2fef6SLaurent Vivier addr, access_type, &page_size) != 0) { 58388b2fef6SLaurent Vivier return -1; 58488b2fef6SLaurent Vivier } 58588b2fef6SLaurent Vivier return phys_addr; 58688b2fef6SLaurent Vivier } 58788b2fef6SLaurent Vivier 58898670d47SLaurent Vivier int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, 58997b348e7SBlue Swirl int mmu_idx) 5900633879fSpbrook { 59188b2fef6SLaurent Vivier M68kCPU *cpu = M68K_CPU(cs); 59288b2fef6SLaurent Vivier CPUM68KState *env = &cpu->env; 59388b2fef6SLaurent Vivier hwaddr physical; 5940633879fSpbrook int prot; 59588b2fef6SLaurent Vivier int access_type; 59688b2fef6SLaurent Vivier int ret; 59788b2fef6SLaurent Vivier target_ulong page_size; 5980633879fSpbrook 59988b2fef6SLaurent Vivier if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) { 60088b2fef6SLaurent Vivier /* MMU disabled */ 60188b2fef6SLaurent Vivier tlb_set_page(cs, address & TARGET_PAGE_MASK, 60288b2fef6SLaurent Vivier address & TARGET_PAGE_MASK, 60388b2fef6SLaurent Vivier PAGE_READ | PAGE_WRITE | PAGE_EXEC, 60488b2fef6SLaurent Vivier mmu_idx, TARGET_PAGE_SIZE); 605d4c430a8SPaul Brook return 0; 6060633879fSpbrook } 6070633879fSpbrook 60888b2fef6SLaurent Vivier if (rw == 2) { 60988b2fef6SLaurent Vivier access_type = ACCESS_CODE; 61088b2fef6SLaurent Vivier rw = 0; 61188b2fef6SLaurent Vivier } else { 61288b2fef6SLaurent Vivier access_type = ACCESS_DATA; 61388b2fef6SLaurent Vivier if (rw) { 61488b2fef6SLaurent Vivier access_type |= ACCESS_STORE; 61588b2fef6SLaurent Vivier } 61688b2fef6SLaurent Vivier } 61788b2fef6SLaurent Vivier 61888b2fef6SLaurent Vivier if (mmu_idx != MMU_USER_IDX) { 61988b2fef6SLaurent Vivier access_type |= ACCESS_SUPER; 62088b2fef6SLaurent Vivier } 62188b2fef6SLaurent Vivier 62288b2fef6SLaurent Vivier ret = get_physical_address(&cpu->env, &physical, &prot, 62388b2fef6SLaurent Vivier address, access_type, &page_size); 62488b2fef6SLaurent Vivier if (ret == 0) { 62588b2fef6SLaurent Vivier address &= TARGET_PAGE_MASK; 62688b2fef6SLaurent Vivier physical += address & (page_size - 1); 62788b2fef6SLaurent Vivier tlb_set_page(cs, address, physical, 62888b2fef6SLaurent Vivier prot, mmu_idx, TARGET_PAGE_SIZE); 62988b2fef6SLaurent Vivier return 0; 63088b2fef6SLaurent Vivier } 63188b2fef6SLaurent Vivier /* page fault */ 63288b2fef6SLaurent Vivier env->mmu.ssw = M68K_ATC_040; 63388b2fef6SLaurent Vivier switch (size) { 63488b2fef6SLaurent Vivier case 1: 63588b2fef6SLaurent Vivier env->mmu.ssw |= M68K_BA_SIZE_BYTE; 63688b2fef6SLaurent Vivier break; 63788b2fef6SLaurent Vivier case 2: 63888b2fef6SLaurent Vivier env->mmu.ssw |= M68K_BA_SIZE_WORD; 63988b2fef6SLaurent Vivier break; 64088b2fef6SLaurent Vivier case 4: 64188b2fef6SLaurent Vivier env->mmu.ssw |= M68K_BA_SIZE_LONG; 64288b2fef6SLaurent Vivier break; 64388b2fef6SLaurent Vivier } 64488b2fef6SLaurent Vivier if (access_type & ACCESS_SUPER) { 64588b2fef6SLaurent Vivier env->mmu.ssw |= M68K_TM_040_SUPER; 64688b2fef6SLaurent Vivier } 64788b2fef6SLaurent Vivier if (access_type & ACCESS_CODE) { 64888b2fef6SLaurent Vivier env->mmu.ssw |= M68K_TM_040_CODE; 64988b2fef6SLaurent Vivier } else { 65088b2fef6SLaurent Vivier env->mmu.ssw |= M68K_TM_040_DATA; 65188b2fef6SLaurent Vivier } 65288b2fef6SLaurent Vivier if (!(access_type & ACCESS_STORE)) { 65388b2fef6SLaurent Vivier env->mmu.ssw |= M68K_RW_040; 65488b2fef6SLaurent Vivier } 65588b2fef6SLaurent Vivier env->mmu.ar = address; 65688b2fef6SLaurent Vivier cs->exception_index = EXCP_ACCESS; 65788b2fef6SLaurent Vivier return 1; 65888b2fef6SLaurent Vivier } 65988b2fef6SLaurent Vivier 6600633879fSpbrook /* Notify CPU of a pending interrupt. Prioritization and vectoring should 6610633879fSpbrook be handled by the interrupt controller. Real hardware only requests 6620633879fSpbrook the vector when the interrupt is acknowledged by the CPU. For 6630633879fSpbrook simplicitly we calculate it when the interrupt is signalled. */ 664cb3fb38eSAndreas Färber void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector) 6650633879fSpbrook { 666d8ed887bSAndreas Färber CPUState *cs = CPU(cpu); 667cb3fb38eSAndreas Färber CPUM68KState *env = &cpu->env; 668cb3fb38eSAndreas Färber 6690633879fSpbrook env->pending_level = level; 6700633879fSpbrook env->pending_vector = vector; 671d8ed887bSAndreas Färber if (level) { 672c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_HARD); 673d8ed887bSAndreas Färber } else { 674d8ed887bSAndreas Färber cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); 675d8ed887bSAndreas Färber } 6760633879fSpbrook } 6770633879fSpbrook 6780633879fSpbrook #endif 679e1f3808eSpbrook 680e1f3808eSpbrook uint32_t HELPER(bitrev)(uint32_t x) 681e1f3808eSpbrook { 682e1f3808eSpbrook x = ((x >> 1) & 0x55555555u) | ((x << 1) & 0xaaaaaaaau); 683e1f3808eSpbrook x = ((x >> 2) & 0x33333333u) | ((x << 2) & 0xccccccccu); 684e1f3808eSpbrook x = ((x >> 4) & 0x0f0f0f0fu) | ((x << 4) & 0xf0f0f0f0u); 685e1f3808eSpbrook return bswap32(x); 686e1f3808eSpbrook } 687e1f3808eSpbrook 688e1f3808eSpbrook uint32_t HELPER(ff1)(uint32_t x) 689e1f3808eSpbrook { 690e1f3808eSpbrook int n; 691e1f3808eSpbrook for (n = 32; x; n--) 692e1f3808eSpbrook x >>= 1; 693e1f3808eSpbrook return n; 694e1f3808eSpbrook } 695e1f3808eSpbrook 696620c6cf6SRichard Henderson uint32_t HELPER(sats)(uint32_t val, uint32_t v) 697e1f3808eSpbrook { 698e1f3808eSpbrook /* The result has the opposite sign to the original value. */ 699620c6cf6SRichard Henderson if ((int32_t)v < 0) { 700e1f3808eSpbrook val = (((int32_t)val) >> 31) ^ SIGNBIT; 701620c6cf6SRichard Henderson } 702e1f3808eSpbrook return val; 703e1f3808eSpbrook } 704e1f3808eSpbrook 705d2f8fb8eSLaurent Vivier void cpu_m68k_set_sr(CPUM68KState *env, uint32_t sr) 706e1f3808eSpbrook { 707d2f8fb8eSLaurent Vivier env->sr = sr & 0xffe0; 708d2f8fb8eSLaurent Vivier cpu_m68k_set_ccr(env, sr); 709e1f3808eSpbrook m68k_switch_sp(env); 710e1f3808eSpbrook } 711e1f3808eSpbrook 712d2f8fb8eSLaurent Vivier void HELPER(set_sr)(CPUM68KState *env, uint32_t val) 713d2f8fb8eSLaurent Vivier { 714d2f8fb8eSLaurent Vivier cpu_m68k_set_sr(env, val); 715d2f8fb8eSLaurent Vivier } 716e1f3808eSpbrook 717e1f3808eSpbrook /* MAC unit. */ 718e1f3808eSpbrook /* FIXME: The MAC unit implementation is a bit of a mess. Some helpers 719e1f3808eSpbrook take values, others take register numbers and manipulate the contents 720e1f3808eSpbrook in-place. */ 7212b3e3cfeSAndreas Färber void HELPER(mac_move)(CPUM68KState *env, uint32_t dest, uint32_t src) 722e1f3808eSpbrook { 723e1f3808eSpbrook uint32_t mask; 724e1f3808eSpbrook env->macc[dest] = env->macc[src]; 725e1f3808eSpbrook mask = MACSR_PAV0 << dest; 726e1f3808eSpbrook if (env->macsr & (MACSR_PAV0 << src)) 727e1f3808eSpbrook env->macsr |= mask; 728e1f3808eSpbrook else 729e1f3808eSpbrook env->macsr &= ~mask; 730e1f3808eSpbrook } 731e1f3808eSpbrook 7322b3e3cfeSAndreas Färber uint64_t HELPER(macmuls)(CPUM68KState *env, uint32_t op1, uint32_t op2) 733e1f3808eSpbrook { 734e1f3808eSpbrook int64_t product; 735e1f3808eSpbrook int64_t res; 736e1f3808eSpbrook 737e1f3808eSpbrook product = (uint64_t)op1 * op2; 738e1f3808eSpbrook res = (product << 24) >> 24; 739e1f3808eSpbrook if (res != product) { 740e1f3808eSpbrook env->macsr |= MACSR_V; 741e1f3808eSpbrook if (env->macsr & MACSR_OMC) { 742e1f3808eSpbrook /* Make sure the accumulate operation overflows. */ 743e1f3808eSpbrook if (product < 0) 744e1f3808eSpbrook res = ~(1ll << 50); 745e1f3808eSpbrook else 746e1f3808eSpbrook res = 1ll << 50; 747e1f3808eSpbrook } 748e1f3808eSpbrook } 749e1f3808eSpbrook return res; 750e1f3808eSpbrook } 751e1f3808eSpbrook 7522b3e3cfeSAndreas Färber uint64_t HELPER(macmulu)(CPUM68KState *env, uint32_t op1, uint32_t op2) 753e1f3808eSpbrook { 754e1f3808eSpbrook uint64_t product; 755e1f3808eSpbrook 756e1f3808eSpbrook product = (uint64_t)op1 * op2; 757e1f3808eSpbrook if (product & (0xffffffull << 40)) { 758e1f3808eSpbrook env->macsr |= MACSR_V; 759e1f3808eSpbrook if (env->macsr & MACSR_OMC) { 760e1f3808eSpbrook /* Make sure the accumulate operation overflows. */ 761e1f3808eSpbrook product = 1ll << 50; 762e1f3808eSpbrook } else { 763e1f3808eSpbrook product &= ((1ull << 40) - 1); 764e1f3808eSpbrook } 765e1f3808eSpbrook } 766e1f3808eSpbrook return product; 767e1f3808eSpbrook } 768e1f3808eSpbrook 7692b3e3cfeSAndreas Färber uint64_t HELPER(macmulf)(CPUM68KState *env, uint32_t op1, uint32_t op2) 770e1f3808eSpbrook { 771e1f3808eSpbrook uint64_t product; 772e1f3808eSpbrook uint32_t remainder; 773e1f3808eSpbrook 774e1f3808eSpbrook product = (uint64_t)op1 * op2; 775e1f3808eSpbrook if (env->macsr & MACSR_RT) { 776e1f3808eSpbrook remainder = product & 0xffffff; 777e1f3808eSpbrook product >>= 24; 778e1f3808eSpbrook if (remainder > 0x800000) 779e1f3808eSpbrook product++; 780e1f3808eSpbrook else if (remainder == 0x800000) 781e1f3808eSpbrook product += (product & 1); 782e1f3808eSpbrook } else { 783e1f3808eSpbrook product >>= 24; 784e1f3808eSpbrook } 785e1f3808eSpbrook return product; 786e1f3808eSpbrook } 787e1f3808eSpbrook 7882b3e3cfeSAndreas Färber void HELPER(macsats)(CPUM68KState *env, uint32_t acc) 789e1f3808eSpbrook { 790e1f3808eSpbrook int64_t tmp; 791e1f3808eSpbrook int64_t result; 792e1f3808eSpbrook tmp = env->macc[acc]; 793e1f3808eSpbrook result = ((tmp << 16) >> 16); 794e1f3808eSpbrook if (result != tmp) { 795e1f3808eSpbrook env->macsr |= MACSR_V; 796e1f3808eSpbrook } 797e1f3808eSpbrook if (env->macsr & MACSR_V) { 798e1f3808eSpbrook env->macsr |= MACSR_PAV0 << acc; 799e1f3808eSpbrook if (env->macsr & MACSR_OMC) { 800a1c7273bSStefan Weil /* The result is saturated to 32 bits, despite overflow occurring 801e1f3808eSpbrook at 48 bits. Seems weird, but that's what the hardware docs 802e1f3808eSpbrook say. */ 803e1f3808eSpbrook result = (result >> 63) ^ 0x7fffffff; 804e1f3808eSpbrook } 805e1f3808eSpbrook } 806e1f3808eSpbrook env->macc[acc] = result; 807e1f3808eSpbrook } 808e1f3808eSpbrook 8092b3e3cfeSAndreas Färber void HELPER(macsatu)(CPUM68KState *env, uint32_t acc) 810e1f3808eSpbrook { 811e1f3808eSpbrook uint64_t val; 812e1f3808eSpbrook 813e1f3808eSpbrook val = env->macc[acc]; 814e1f3808eSpbrook if (val & (0xffffull << 48)) { 815e1f3808eSpbrook env->macsr |= MACSR_V; 816e1f3808eSpbrook } 817e1f3808eSpbrook if (env->macsr & MACSR_V) { 818e1f3808eSpbrook env->macsr |= MACSR_PAV0 << acc; 819e1f3808eSpbrook if (env->macsr & MACSR_OMC) { 820e1f3808eSpbrook if (val > (1ull << 53)) 821e1f3808eSpbrook val = 0; 822e1f3808eSpbrook else 823e1f3808eSpbrook val = (1ull << 48) - 1; 824e1f3808eSpbrook } else { 825e1f3808eSpbrook val &= ((1ull << 48) - 1); 826e1f3808eSpbrook } 827e1f3808eSpbrook } 828e1f3808eSpbrook env->macc[acc] = val; 829e1f3808eSpbrook } 830e1f3808eSpbrook 8312b3e3cfeSAndreas Färber void HELPER(macsatf)(CPUM68KState *env, uint32_t acc) 832e1f3808eSpbrook { 833e1f3808eSpbrook int64_t sum; 834e1f3808eSpbrook int64_t result; 835e1f3808eSpbrook 836e1f3808eSpbrook sum = env->macc[acc]; 837e1f3808eSpbrook result = (sum << 16) >> 16; 838e1f3808eSpbrook if (result != sum) { 839e1f3808eSpbrook env->macsr |= MACSR_V; 840e1f3808eSpbrook } 841e1f3808eSpbrook if (env->macsr & MACSR_V) { 842e1f3808eSpbrook env->macsr |= MACSR_PAV0 << acc; 843e1f3808eSpbrook if (env->macsr & MACSR_OMC) { 844e1f3808eSpbrook result = (result >> 63) ^ 0x7fffffffffffll; 845e1f3808eSpbrook } 846e1f3808eSpbrook } 847e1f3808eSpbrook env->macc[acc] = result; 848e1f3808eSpbrook } 849e1f3808eSpbrook 8502b3e3cfeSAndreas Färber void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc) 851e1f3808eSpbrook { 852e1f3808eSpbrook uint64_t val; 853e1f3808eSpbrook val = env->macc[acc]; 854c4162574SBlue Swirl if (val == 0) { 855e1f3808eSpbrook env->macsr |= MACSR_Z; 856c4162574SBlue Swirl } else if (val & (1ull << 47)) { 857e1f3808eSpbrook env->macsr |= MACSR_N; 858c4162574SBlue Swirl } 859e1f3808eSpbrook if (env->macsr & (MACSR_PAV0 << acc)) { 860e1f3808eSpbrook env->macsr |= MACSR_V; 861e1f3808eSpbrook } 862e1f3808eSpbrook if (env->macsr & MACSR_FI) { 863e1f3808eSpbrook val = ((int64_t)val) >> 40; 864e1f3808eSpbrook if (val != 0 && val != -1) 865e1f3808eSpbrook env->macsr |= MACSR_EV; 866e1f3808eSpbrook } else if (env->macsr & MACSR_SU) { 867e1f3808eSpbrook val = ((int64_t)val) >> 32; 868e1f3808eSpbrook if (val != 0 && val != -1) 869e1f3808eSpbrook env->macsr |= MACSR_EV; 870e1f3808eSpbrook } else { 871e1f3808eSpbrook if ((val >> 32) != 0) 872e1f3808eSpbrook env->macsr |= MACSR_EV; 873e1f3808eSpbrook } 874e1f3808eSpbrook } 875e1f3808eSpbrook 876db3d7945SLaurent Vivier #define EXTSIGN(val, index) ( \ 877db3d7945SLaurent Vivier (index == 0) ? (int8_t)(val) : ((index == 1) ? (int16_t)(val) : (val)) \ 878db3d7945SLaurent Vivier ) 879620c6cf6SRichard Henderson 880620c6cf6SRichard Henderson #define COMPUTE_CCR(op, x, n, z, v, c) { \ 881620c6cf6SRichard Henderson switch (op) { \ 882620c6cf6SRichard Henderson case CC_OP_FLAGS: \ 883620c6cf6SRichard Henderson /* Everything in place. */ \ 884620c6cf6SRichard Henderson break; \ 885db3d7945SLaurent Vivier case CC_OP_ADDB: \ 886db3d7945SLaurent Vivier case CC_OP_ADDW: \ 887db3d7945SLaurent Vivier case CC_OP_ADDL: \ 888620c6cf6SRichard Henderson res = n; \ 889620c6cf6SRichard Henderson src2 = v; \ 890db3d7945SLaurent Vivier src1 = EXTSIGN(res - src2, op - CC_OP_ADDB); \ 891620c6cf6SRichard Henderson c = x; \ 892620c6cf6SRichard Henderson z = n; \ 893620c6cf6SRichard Henderson v = (res ^ src1) & ~(src1 ^ src2); \ 894620c6cf6SRichard Henderson break; \ 895db3d7945SLaurent Vivier case CC_OP_SUBB: \ 896db3d7945SLaurent Vivier case CC_OP_SUBW: \ 897db3d7945SLaurent Vivier case CC_OP_SUBL: \ 898620c6cf6SRichard Henderson res = n; \ 899620c6cf6SRichard Henderson src2 = v; \ 900db3d7945SLaurent Vivier src1 = EXTSIGN(res + src2, op - CC_OP_SUBB); \ 901620c6cf6SRichard Henderson c = x; \ 902620c6cf6SRichard Henderson z = n; \ 903620c6cf6SRichard Henderson v = (res ^ src1) & (src1 ^ src2); \ 904620c6cf6SRichard Henderson break; \ 905db3d7945SLaurent Vivier case CC_OP_CMPB: \ 906db3d7945SLaurent Vivier case CC_OP_CMPW: \ 907db3d7945SLaurent Vivier case CC_OP_CMPL: \ 908620c6cf6SRichard Henderson src1 = n; \ 909620c6cf6SRichard Henderson src2 = v; \ 910db3d7945SLaurent Vivier res = EXTSIGN(src1 - src2, op - CC_OP_CMPB); \ 911620c6cf6SRichard Henderson n = res; \ 912620c6cf6SRichard Henderson z = res; \ 913620c6cf6SRichard Henderson c = src1 < src2; \ 914620c6cf6SRichard Henderson v = (res ^ src1) & (src1 ^ src2); \ 915620c6cf6SRichard Henderson break; \ 916620c6cf6SRichard Henderson case CC_OP_LOGIC: \ 917620c6cf6SRichard Henderson c = v = 0; \ 918620c6cf6SRichard Henderson z = n; \ 919620c6cf6SRichard Henderson break; \ 920620c6cf6SRichard Henderson default: \ 921620c6cf6SRichard Henderson cpu_abort(CPU(m68k_env_get_cpu(env)), "Bad CC_OP %d", op); \ 922620c6cf6SRichard Henderson } \ 923620c6cf6SRichard Henderson } while (0) 924620c6cf6SRichard Henderson 925620c6cf6SRichard Henderson uint32_t cpu_m68k_get_ccr(CPUM68KState *env) 926e1f3808eSpbrook { 927620c6cf6SRichard Henderson uint32_t x, c, n, z, v; 928620c6cf6SRichard Henderson uint32_t res, src1, src2; 929620c6cf6SRichard Henderson 930620c6cf6SRichard Henderson x = env->cc_x; 931620c6cf6SRichard Henderson n = env->cc_n; 932620c6cf6SRichard Henderson z = env->cc_z; 933620c6cf6SRichard Henderson v = env->cc_v; 934db3d7945SLaurent Vivier c = env->cc_c; 935620c6cf6SRichard Henderson 936620c6cf6SRichard Henderson COMPUTE_CCR(env->cc_op, x, n, z, v, c); 937620c6cf6SRichard Henderson 938620c6cf6SRichard Henderson n = n >> 31; 939620c6cf6SRichard Henderson z = (z == 0); 940db3d7945SLaurent Vivier v = v >> 31; 941620c6cf6SRichard Henderson 942620c6cf6SRichard Henderson return x * CCF_X + n * CCF_N + z * CCF_Z + v * CCF_V + c * CCF_C; 943620c6cf6SRichard Henderson } 944620c6cf6SRichard Henderson 945620c6cf6SRichard Henderson uint32_t HELPER(get_ccr)(CPUM68KState *env) 946620c6cf6SRichard Henderson { 947620c6cf6SRichard Henderson return cpu_m68k_get_ccr(env); 948620c6cf6SRichard Henderson } 949620c6cf6SRichard Henderson 950620c6cf6SRichard Henderson void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t ccr) 951620c6cf6SRichard Henderson { 952620c6cf6SRichard Henderson env->cc_x = (ccr & CCF_X ? 1 : 0); 953620c6cf6SRichard Henderson env->cc_n = (ccr & CCF_N ? -1 : 0); 954620c6cf6SRichard Henderson env->cc_z = (ccr & CCF_Z ? 0 : 1); 955620c6cf6SRichard Henderson env->cc_v = (ccr & CCF_V ? -1 : 0); 956620c6cf6SRichard Henderson env->cc_c = (ccr & CCF_C ? 1 : 0); 957620c6cf6SRichard Henderson env->cc_op = CC_OP_FLAGS; 958620c6cf6SRichard Henderson } 959620c6cf6SRichard Henderson 960620c6cf6SRichard Henderson void HELPER(set_ccr)(CPUM68KState *env, uint32_t ccr) 961620c6cf6SRichard Henderson { 962620c6cf6SRichard Henderson cpu_m68k_set_ccr(env, ccr); 963620c6cf6SRichard Henderson } 964620c6cf6SRichard Henderson 965620c6cf6SRichard Henderson void HELPER(flush_flags)(CPUM68KState *env, uint32_t cc_op) 966620c6cf6SRichard Henderson { 967620c6cf6SRichard Henderson uint32_t res, src1, src2; 968620c6cf6SRichard Henderson 969620c6cf6SRichard Henderson COMPUTE_CCR(cc_op, env->cc_x, env->cc_n, env->cc_z, env->cc_v, env->cc_c); 970620c6cf6SRichard Henderson env->cc_op = CC_OP_FLAGS; 971e1f3808eSpbrook } 972e1f3808eSpbrook 9732b3e3cfeSAndreas Färber uint32_t HELPER(get_macf)(CPUM68KState *env, uint64_t val) 974e1f3808eSpbrook { 975e1f3808eSpbrook int rem; 976e1f3808eSpbrook uint32_t result; 977e1f3808eSpbrook 978e1f3808eSpbrook if (env->macsr & MACSR_SU) { 979e1f3808eSpbrook /* 16-bit rounding. */ 980e1f3808eSpbrook rem = val & 0xffffff; 981e1f3808eSpbrook val = (val >> 24) & 0xffffu; 982e1f3808eSpbrook if (rem > 0x800000) 983e1f3808eSpbrook val++; 984e1f3808eSpbrook else if (rem == 0x800000) 985e1f3808eSpbrook val += (val & 1); 986e1f3808eSpbrook } else if (env->macsr & MACSR_RT) { 987e1f3808eSpbrook /* 32-bit rounding. */ 988e1f3808eSpbrook rem = val & 0xff; 989e1f3808eSpbrook val >>= 8; 990e1f3808eSpbrook if (rem > 0x80) 991e1f3808eSpbrook val++; 992e1f3808eSpbrook else if (rem == 0x80) 993e1f3808eSpbrook val += (val & 1); 994e1f3808eSpbrook } else { 995e1f3808eSpbrook /* No rounding. */ 996e1f3808eSpbrook val >>= 8; 997e1f3808eSpbrook } 998e1f3808eSpbrook if (env->macsr & MACSR_OMC) { 999e1f3808eSpbrook /* Saturate. */ 1000e1f3808eSpbrook if (env->macsr & MACSR_SU) { 1001e1f3808eSpbrook if (val != (uint16_t) val) { 1002e1f3808eSpbrook result = ((val >> 63) ^ 0x7fff) & 0xffff; 1003e1f3808eSpbrook } else { 1004e1f3808eSpbrook result = val & 0xffff; 1005e1f3808eSpbrook } 1006e1f3808eSpbrook } else { 1007e1f3808eSpbrook if (val != (uint32_t)val) { 1008e1f3808eSpbrook result = ((uint32_t)(val >> 63) & 0x7fffffff); 1009e1f3808eSpbrook } else { 1010e1f3808eSpbrook result = (uint32_t)val; 1011e1f3808eSpbrook } 1012e1f3808eSpbrook } 1013e1f3808eSpbrook } else { 1014e1f3808eSpbrook /* No saturation. */ 1015e1f3808eSpbrook if (env->macsr & MACSR_SU) { 1016e1f3808eSpbrook result = val & 0xffff; 1017e1f3808eSpbrook } else { 1018e1f3808eSpbrook result = (uint32_t)val; 1019e1f3808eSpbrook } 1020e1f3808eSpbrook } 1021e1f3808eSpbrook return result; 1022e1f3808eSpbrook } 1023e1f3808eSpbrook 1024e1f3808eSpbrook uint32_t HELPER(get_macs)(uint64_t val) 1025e1f3808eSpbrook { 1026e1f3808eSpbrook if (val == (int32_t)val) { 1027e1f3808eSpbrook return (int32_t)val; 1028e1f3808eSpbrook } else { 1029e1f3808eSpbrook return (val >> 61) ^ ~SIGNBIT; 1030e1f3808eSpbrook } 1031e1f3808eSpbrook } 1032e1f3808eSpbrook 1033e1f3808eSpbrook uint32_t HELPER(get_macu)(uint64_t val) 1034e1f3808eSpbrook { 1035e1f3808eSpbrook if ((val >> 32) == 0) { 1036e1f3808eSpbrook return (uint32_t)val; 1037e1f3808eSpbrook } else { 1038e1f3808eSpbrook return 0xffffffffu; 1039e1f3808eSpbrook } 1040e1f3808eSpbrook } 1041e1f3808eSpbrook 10422b3e3cfeSAndreas Färber uint32_t HELPER(get_mac_extf)(CPUM68KState *env, uint32_t acc) 1043e1f3808eSpbrook { 1044e1f3808eSpbrook uint32_t val; 1045e1f3808eSpbrook val = env->macc[acc] & 0x00ff; 10465ce747cfSPaolo Bonzini val |= (env->macc[acc] >> 32) & 0xff00; 1047e1f3808eSpbrook val |= (env->macc[acc + 1] << 16) & 0x00ff0000; 1048e1f3808eSpbrook val |= (env->macc[acc + 1] >> 16) & 0xff000000; 1049e1f3808eSpbrook return val; 1050e1f3808eSpbrook } 1051e1f3808eSpbrook 10522b3e3cfeSAndreas Färber uint32_t HELPER(get_mac_exti)(CPUM68KState *env, uint32_t acc) 1053e1f3808eSpbrook { 1054e1f3808eSpbrook uint32_t val; 1055e1f3808eSpbrook val = (env->macc[acc] >> 32) & 0xffff; 1056e1f3808eSpbrook val |= (env->macc[acc + 1] >> 16) & 0xffff0000; 1057e1f3808eSpbrook return val; 1058e1f3808eSpbrook } 1059e1f3808eSpbrook 10602b3e3cfeSAndreas Färber void HELPER(set_mac_extf)(CPUM68KState *env, uint32_t val, uint32_t acc) 1061e1f3808eSpbrook { 1062e1f3808eSpbrook int64_t res; 1063e1f3808eSpbrook int32_t tmp; 1064e1f3808eSpbrook res = env->macc[acc] & 0xffffffff00ull; 1065e1f3808eSpbrook tmp = (int16_t)(val & 0xff00); 1066e1f3808eSpbrook res |= ((int64_t)tmp) << 32; 1067e1f3808eSpbrook res |= val & 0xff; 1068e1f3808eSpbrook env->macc[acc] = res; 1069e1f3808eSpbrook res = env->macc[acc + 1] & 0xffffffff00ull; 1070e1f3808eSpbrook tmp = (val & 0xff000000); 1071e1f3808eSpbrook res |= ((int64_t)tmp) << 16; 1072e1f3808eSpbrook res |= (val >> 16) & 0xff; 1073e1f3808eSpbrook env->macc[acc + 1] = res; 1074e1f3808eSpbrook } 1075e1f3808eSpbrook 10762b3e3cfeSAndreas Färber void HELPER(set_mac_exts)(CPUM68KState *env, uint32_t val, uint32_t acc) 1077e1f3808eSpbrook { 1078e1f3808eSpbrook int64_t res; 1079e1f3808eSpbrook int32_t tmp; 1080e1f3808eSpbrook res = (uint32_t)env->macc[acc]; 1081e1f3808eSpbrook tmp = (int16_t)val; 1082e1f3808eSpbrook res |= ((int64_t)tmp) << 32; 1083e1f3808eSpbrook env->macc[acc] = res; 1084e1f3808eSpbrook res = (uint32_t)env->macc[acc + 1]; 1085e1f3808eSpbrook tmp = val & 0xffff0000; 1086e1f3808eSpbrook res |= (int64_t)tmp << 16; 1087e1f3808eSpbrook env->macc[acc + 1] = res; 1088e1f3808eSpbrook } 1089e1f3808eSpbrook 10902b3e3cfeSAndreas Färber void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc) 1091e1f3808eSpbrook { 1092e1f3808eSpbrook uint64_t res; 1093e1f3808eSpbrook res = (uint32_t)env->macc[acc]; 1094e1f3808eSpbrook res |= ((uint64_t)(val & 0xffff)) << 32; 1095e1f3808eSpbrook env->macc[acc] = res; 1096e1f3808eSpbrook res = (uint32_t)env->macc[acc + 1]; 1097e1f3808eSpbrook res |= (uint64_t)(val & 0xffff0000) << 16; 1098e1f3808eSpbrook env->macc[acc + 1] = res; 1099e1f3808eSpbrook } 11000bdb2b3bSLaurent Vivier 11010bdb2b3bSLaurent Vivier #if defined(CONFIG_SOFTMMU) 1102*e55886c3SLaurent Vivier void HELPER(ptest)(CPUM68KState *env, uint32_t addr, uint32_t is_read) 1103*e55886c3SLaurent Vivier { 1104*e55886c3SLaurent Vivier M68kCPU *cpu = m68k_env_get_cpu(env); 1105*e55886c3SLaurent Vivier CPUState *cs = CPU(cpu); 1106*e55886c3SLaurent Vivier hwaddr physical; 1107*e55886c3SLaurent Vivier int access_type; 1108*e55886c3SLaurent Vivier int prot; 1109*e55886c3SLaurent Vivier int ret; 1110*e55886c3SLaurent Vivier target_ulong page_size; 1111*e55886c3SLaurent Vivier 1112*e55886c3SLaurent Vivier access_type = ACCESS_PTEST; 1113*e55886c3SLaurent Vivier if (env->dfc & 4) { 1114*e55886c3SLaurent Vivier access_type |= ACCESS_SUPER; 1115*e55886c3SLaurent Vivier } 1116*e55886c3SLaurent Vivier if ((env->dfc & 3) == 2) { 1117*e55886c3SLaurent Vivier access_type |= ACCESS_CODE; 1118*e55886c3SLaurent Vivier } 1119*e55886c3SLaurent Vivier if (!is_read) { 1120*e55886c3SLaurent Vivier access_type |= ACCESS_STORE; 1121*e55886c3SLaurent Vivier } 1122*e55886c3SLaurent Vivier 1123*e55886c3SLaurent Vivier env->mmu.mmusr = 0; 1124*e55886c3SLaurent Vivier env->mmu.ssw = 0; 1125*e55886c3SLaurent Vivier ret = get_physical_address(env, &physical, &prot, addr, 1126*e55886c3SLaurent Vivier access_type, &page_size); 1127*e55886c3SLaurent Vivier if (ret == 0) { 1128*e55886c3SLaurent Vivier addr &= TARGET_PAGE_MASK; 1129*e55886c3SLaurent Vivier physical += addr & (page_size - 1); 1130*e55886c3SLaurent Vivier tlb_set_page(cs, addr, physical, 1131*e55886c3SLaurent Vivier prot, access_type & ACCESS_SUPER ? 1132*e55886c3SLaurent Vivier MMU_KERNEL_IDX : MMU_USER_IDX, page_size); 1133*e55886c3SLaurent Vivier } 1134*e55886c3SLaurent Vivier } 1135*e55886c3SLaurent Vivier 1136*e55886c3SLaurent Vivier void HELPER(pflush)(CPUM68KState *env, uint32_t addr, uint32_t opmode) 1137*e55886c3SLaurent Vivier { 1138*e55886c3SLaurent Vivier M68kCPU *cpu = m68k_env_get_cpu(env); 1139*e55886c3SLaurent Vivier 1140*e55886c3SLaurent Vivier switch (opmode) { 1141*e55886c3SLaurent Vivier case 0: /* Flush page entry if not global */ 1142*e55886c3SLaurent Vivier case 1: /* Flush page entry */ 1143*e55886c3SLaurent Vivier tlb_flush_page(CPU(cpu), addr); 1144*e55886c3SLaurent Vivier break; 1145*e55886c3SLaurent Vivier case 2: /* Flush all except global entries */ 1146*e55886c3SLaurent Vivier tlb_flush(CPU(cpu)); 1147*e55886c3SLaurent Vivier break; 1148*e55886c3SLaurent Vivier case 3: /* Flush all entries */ 1149*e55886c3SLaurent Vivier tlb_flush(CPU(cpu)); 1150*e55886c3SLaurent Vivier break; 1151*e55886c3SLaurent Vivier } 1152*e55886c3SLaurent Vivier } 1153*e55886c3SLaurent Vivier 11540bdb2b3bSLaurent Vivier void HELPER(reset)(CPUM68KState *env) 11550bdb2b3bSLaurent Vivier { 11560bdb2b3bSLaurent Vivier /* FIXME: reset all except CPU */ 11570bdb2b3bSLaurent Vivier } 11580bdb2b3bSLaurent Vivier #endif 1159