diff options
author | pbrook | 2007-05-23 21:58:11 +0200 |
---|---|---|
committer | pbrook | 2007-05-23 21:58:11 +0200 |
commit | 0633879f1ac38b18d84c46dda506300cc8329723 (patch) | |
tree | 8df64e0861c32132c7a08af16451629f1ec69112 /target-m68k | |
parent | Add more cpu.h dependencies. (diff) | |
download | qemu-0633879f1ac38b18d84c46dda506300cc8329723.tar.gz qemu-0633879f1ac38b18d84c46dda506300cc8329723.tar.xz qemu-0633879f1ac38b18d84c46dda506300cc8329723.zip |
m68k/ColdFire system emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2851 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-m68k')
-rw-r--r-- | target-m68k/cpu.h | 29 | ||||
-rw-r--r-- | target-m68k/exec.h | 4 | ||||
-rw-r--r-- | target-m68k/helper.c | 64 | ||||
-rw-r--r-- | target-m68k/op-hacks.h | 30 | ||||
-rw-r--r-- | target-m68k/op.c | 101 | ||||
-rw-r--r-- | target-m68k/op_helper.c | 140 | ||||
-rw-r--r-- | target-m68k/op_mem.h | 46 | ||||
-rw-r--r-- | target-m68k/qregs.def | 1 | ||||
-rw-r--r-- | target-m68k/translate.c | 392 |
9 files changed, 608 insertions, 199 deletions
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index de37baf9a9..c916ad7cd5 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -1,7 +1,7 @@ /* * m68k virtual CPU header * - * Copyright (c) 2005-2006 CodeSourcery + * Copyright (c) 2005-2007 CodeSourcery * Written by Paul Brook * * This library is free software; you can redistribute it and/or @@ -50,6 +50,8 @@ #define EXCP_UNSUPPORTED 61 #define EXCP_ICE 13 +#define EXCP_RTE 0x100 + typedef struct CPUM68KState { uint32_t dregs[8]; uint32_t aregs[8]; @@ -76,6 +78,12 @@ typedef struct CPUM68KState { struct { uint32_t ar; } mmu; + + /* Control registers. */ + uint32_t vbr; + uint32_t mbar; + uint32_t rambar0; + /* ??? remove this. */ uint32_t t1; @@ -84,7 +92,10 @@ typedef struct CPUM68KState { int exception_index; int interrupt_request; int user_mode_only; - uint32_t address; + int halted; + + int pending_vector; + int pending_level; uint32_t qregs[MAX_QREGS]; @@ -94,6 +105,7 @@ typedef struct CPUM68KState { CPUM68KState *cpu_m68k_init(void); int cpu_m68k_exec(CPUM68KState *s); void cpu_m68k_close(CPUM68KState *s); +void do_interrupt(int is_hw); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ @@ -120,12 +132,19 @@ enum { #define CCF_V 0x02 #define CCF_Z 0x04 #define CCF_N 0x08 -#define CCF_X 0x01 +#define CCF_X 0x10 + +#define SR_I_SHIFT 8 +#define SR_I 0x0700 +#define SR_M 0x1000 +#define SR_S 0x2000 +#define SR_T 0x8000 typedef struct m68k_def_t m68k_def_t; -m68k_def_t *m68k_find_by_name(const char *); -void cpu_m68k_register(CPUM68KState *, m68k_def_t *); +int cpu_m68k_set_model(CPUM68KState *env, const char * name); + +void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector); #define M68K_FPCR_PREC (1 << 6) diff --git a/target-m68k/exec.h b/target-m68k/exec.h index ef4adeb391..254f5581ff 100644 --- a/target-m68k/exec.h +++ b/target-m68k/exec.h @@ -40,8 +40,12 @@ static inline void regs_to_env(void) int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int is_user, int is_softmmu); +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op); float64 helper_sub_cmpf64(CPUM68KState *env, float64 src0, float64 src1); +void helper_movec(CPUM68KState *env, int reg, uint32_t val); void cpu_loop_exit(void); diff --git a/target-m68k/helper.c b/target-m68k/helper.c index 6b8f18d15d..640fe4fb8f 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -1,7 +1,7 @@ /* * m68k op helpers * - * Copyright (c) 2006 CodeSourcery + * Copyright (c) 2006-2007 CodeSourcery * Written by Paul Brook * * This library is free software; you can redistribute it and/or @@ -147,3 +147,65 @@ float64 helper_sub_cmpf64(CPUM68KState *env, float64 src0, float64 src1) } return res; } + +void helper_movec(CPUM68KState *env, int reg, uint32_t val) +{ + switch (reg) { + case 0x02: /* CACR */ + /* Ignored. */ + break; + case 0x801: /* VBR */ + env->vbr = val; + break; + /* TODO: Implement control registers. */ + default: + cpu_abort(env, "Unimplemented control register write 0x%x = 0x%x\n", + reg, val); + } +} + +/* MMU */ + +/* TODO: This will need fixing once the MMU is implemented. */ +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr; +} + +#if defined(CONFIG_USER_ONLY) + +int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int is_user, int is_softmmu) +{ + env->exception_index = EXCP_ACCESS; + env->mmu.ar = address; + return 1; +} + +#else + +int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int is_user, int is_softmmu) +{ + int prot; + + address &= TARGET_PAGE_MASK; + prot = PAGE_READ | PAGE_WRITE; + return tlb_set_page(env, address, address, prot, is_user, is_softmmu); +} + +/* Notify CPU of a pending interrupt. Prioritization and vectoring should + be handled by the interrupt controller. Real hardware only requests + the vector when the interrupt is acknowledged by the CPU. For + simplicitly we calculate it when the interrupt is signalled. */ +void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector) +{ + env->pending_level = level; + env->pending_vector = vector; + if (level) + cpu_interrupt(env, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); +} + +#endif diff --git a/target-m68k/op-hacks.h b/target-m68k/op-hacks.h index c7865638ee..01a158d83e 100644 --- a/target-m68k/op-hacks.h +++ b/target-m68k/op-hacks.h @@ -27,16 +27,38 @@ static inline int gen_im32(uint32_t i) return qreg; } -static inline void gen_op_ldf32(int dest, int addr) +static inline void gen_op_ldf32_raw(int dest, int addr) { - gen_op_ld32(dest, addr); + gen_op_ld32_raw(dest, addr); } -static inline void gen_op_stf32(int addr, int dest) +static inline void gen_op_stf32_raw(int addr, int dest) { - gen_op_st32(addr, dest); + gen_op_st32_raw(addr, dest); } +#if !defined(CONFIG_USER_ONLY) +static inline void gen_op_ldf32_user(int dest, int addr) +{ + gen_op_ld32_user(dest, addr); +} + +static inline void gen_op_stf32_user(int addr, int dest) +{ + gen_op_st32_user(addr, dest); +} + +static inline void gen_op_ldf32_kernel(int dest, int addr) +{ + gen_op_ld32_kernel(dest, addr); +} + +static inline void gen_op_stf32_kernel(int addr, int dest) +{ + gen_op_st32_kernel(addr, dest); +} +#endif + static inline void gen_op_pack_32_f32(int dest, int src) { gen_op_mov32(dest, src); diff --git a/target-m68k/op.c b/target-m68k/op.c index 34878c0215..ad6f403f5c 100644 --- a/target-m68k/op.c +++ b/target-m68k/op.c @@ -1,7 +1,7 @@ /* * m68k micro operations * - * Copyright (c) 2006 CodeSourcery + * Copyright (c) 2006-2007 CodeSourcery * Written by Paul Brook * * This library is free software; you can redistribute it and/or @@ -86,7 +86,7 @@ void set_opf64(int qreg, float64 val) } } -#define OP(name) void OPPROTO op_##name (void) +#define OP(name) void OPPROTO glue(op_,name) (void) OP(mov32) { @@ -316,77 +316,6 @@ OP(ext16s32) FORCE_RET(); } -/* Load/store ops. */ -OP(ld8u32) -{ - uint32_t addr = get_op(PARAM2); - set_op(PARAM1, ldub(addr)); - FORCE_RET(); -} - -OP(ld8s32) -{ - uint32_t addr = get_op(PARAM2); - set_op(PARAM1, ldsb(addr)); - FORCE_RET(); -} - -OP(ld16u32) -{ - uint32_t addr = get_op(PARAM2); - set_op(PARAM1, lduw(addr)); - FORCE_RET(); -} - -OP(ld16s32) -{ - uint32_t addr = get_op(PARAM2); - set_op(PARAM1, ldsw(addr)); - FORCE_RET(); -} - -OP(ld32) -{ - uint32_t addr = get_op(PARAM2); - set_op(PARAM1, ldl(addr)); - FORCE_RET(); -} - -OP(st8) -{ - uint32_t addr = get_op(PARAM1); - stb(addr, get_op(PARAM2)); - FORCE_RET(); -} - -OP(st16) -{ - uint32_t addr = get_op(PARAM1); - stw(addr, get_op(PARAM2)); - FORCE_RET(); -} - -OP(st32) -{ - uint32_t addr = get_op(PARAM1); - stl(addr, get_op(PARAM2)); - FORCE_RET(); -} - -OP(ldf64) -{ - uint32_t addr = get_op(PARAM2); - set_opf64(PARAM1, ldfq(addr)); - FORCE_RET(); -} - -OP(stf64) -{ - uint32_t addr = get_op(PARAM1); - stfq(addr, get_opf64(PARAM2)); - FORCE_RET(); -} - OP(flush_flags) { int cc_op = PARAM1; @@ -454,6 +383,13 @@ OP(divs) FORCE_RET(); } +OP(halt) +{ + env->halted = 1; + RAISE_EXCEPTION(EXCP_HLT); + FORCE_RET(); +} + OP(raise_exception) { RAISE_EXCEPTION(PARAM1); @@ -679,3 +615,22 @@ OP(compare_quietf64) set_op(PARAM1, float64_compare_quiet(op0, op1, &CPU_FP_STATUS)); FORCE_RET(); } + +OP(movec) +{ + int op1 = get_op(PARAM1); + uint32_t op2 = get_op(PARAM2); + helper_movec(env, op1, op2); +} + +/* Memory access. */ + +#define MEMSUFFIX _raw +#include "op_mem.h" + +#if !defined(CONFIG_USER_ONLY) +#define MEMSUFFIX _user +#include "op_mem.h" +#define MEMSUFFIX _kernel +#include "op_mem.h" +#endif diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c new file mode 100644 index 0000000000..7455e31659 --- /dev/null +++ b/target-m68k/op_helper.c @@ -0,0 +1,140 @@ +/* + * M68K helper routines + * + * Copyright (c) 2007 CodeSourcery + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "exec.h" + +#if defined(CONFIG_USER_ONLY) + +void do_interrupt(int is_hw) +{ + env->exception_index = -1; +} + +#else + +#define MMUSUFFIX _mmu +#define GETPC() (__builtin_return_address(0)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +/* 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) */ +/* XXX: fix it to restore all registers */ +void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) +{ + TranslationBlock *tb; + CPUState *saved_env; + target_phys_addr_t pc; + int ret; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + ret = cpu_m68k_handle_mmu_fault(env, addr, is_write, is_user, 1); + if (__builtin_expect(ret, 0)) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (target_phys_addr_t)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } + cpu_loop_exit(); + } + env = saved_env; +} + +static void do_rte(void) +{ + uint32_t sp; + uint32_t fmt; + + sp = env->aregs[7]; + fmt = ldl_kernel(sp); + env->pc = ldl_kernel(sp + 4); + sp |= (fmt >> 28) & 3; + env->sr = fmt & 0xffff; + env->aregs[7] = sp + 8; +} + +void do_interrupt(int is_hw) +{ + uint32_t sp; + uint32_t fmt; + uint32_t retaddr; + uint32_t vector; + + fmt = 0; + retaddr = env->pc; + + if (!is_hw) { + switch (env->exception_index) { + case EXCP_RTE: + /* Return from an exception. */ + do_rte(); + return; + } + if (env->exception_index >= EXCP_TRAP0 + && env->exception_index <= EXCP_TRAP15) { + /* Move the PC after the trap instruction. */ + retaddr += 2; + } + } + + /* TODO: Implement USP. */ + sp = env->aregs[7]; + + vector = env->exception_index << 2; + + fmt |= 0x40000000; + fmt |= (sp & 3) << 28; + fmt |= vector << 16; + fmt |= env->sr; + + /* ??? This could cause MMU faults. */ + sp &= ~3; + sp -= 4; + stl_kernel(sp, retaddr); + sp -= 4; + stl_kernel(sp, fmt); + env->aregs[7] = sp; + env->sr |= SR_S; + if (is_hw) { + env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); + } + /* Jump to vector. */ + env->pc = ldl_kernel(env->vbr + vector); +} + +#endif diff --git a/target-m68k/op_mem.h b/target-m68k/op_mem.h new file mode 100644 index 0000000000..556829f691 --- /dev/null +++ b/target-m68k/op_mem.h @@ -0,0 +1,46 @@ +/* Load/store ops. */ +#define MEM_LD_OP(name,suffix) \ +OP(glue(glue(ld,name),MEMSUFFIX)) \ +{ \ + uint32_t addr = get_op(PARAM2); \ + set_op(PARAM1, glue(glue(ld,suffix),MEMSUFFIX)(addr)); \ + FORCE_RET(); \ +} + +MEM_LD_OP(8u32,ub) +MEM_LD_OP(8s32,sb) +MEM_LD_OP(16u32,uw) +MEM_LD_OP(16s32,sw) +MEM_LD_OP(32,l) + +#undef MEM_LD_OP + +#define MEM_ST_OP(name,suffix) \ +OP(glue(glue(st,name),MEMSUFFIX)) \ +{ \ + uint32_t addr = get_op(PARAM1); \ + glue(glue(st,suffix),MEMSUFFIX)(addr, get_op(PARAM2)); \ + FORCE_RET(); \ +} + +MEM_ST_OP(8,b) +MEM_ST_OP(16,w) +MEM_ST_OP(32,l) + +#undef MEM_ST_OP + +OP(glue(ldf64,MEMSUFFIX)) +{ + uint32_t addr = get_op(PARAM2); + set_opf64(PARAM1, glue(ldfq,MEMSUFFIX)(addr)); + FORCE_RET(); +} + +OP(glue(stf64,MEMSUFFIX)) +{ + uint32_t addr = get_op(PARAM1); + glue(stfq,MEMSUFFIX)(addr, get_opf64(PARAM2)); + FORCE_RET(); +} + +#undef MEMSUFFIX diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def index d8c1944082..08e7e85156 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -24,6 +24,7 @@ DEFF64(F6, fregs[6]) DEFF64(F7, fregs[7]) DEFF64(FP_RESULT, fp_result) DEFO32(PC, pc) +DEFO32(SR, sr) DEFO32(CC_OP, cc_op) DEFR(T0, AREG1, QMODE_I32) DEFO32(CC_DEST, cc_dest) diff --git a/target-m68k/translate.c b/target-m68k/translate.c index 3b25635820..eff32867b8 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -1,7 +1,7 @@ /* * m68k translation * - * Copyright (c) 2005-2006 CodeSourcery + * Copyright (c) 2005-2007 CodeSourcery * Written by Paul Brook * * This library is free software; you can redistribute it and/or @@ -30,6 +30,8 @@ #include "disas.h" #include "m68k-qreg.h" +//#define DEBUG_DISPATCH 1 + static inline void qemu_assert(int cond, const char *msg) { if (!cond) { @@ -43,6 +45,7 @@ typedef struct DisasContext { target_ulong pc; int is_jmp; int cc_op; + int user; uint32_t fpcr; struct TranslationBlock *tb; int singlestep_enabled; @@ -50,6 +53,12 @@ typedef struct DisasContext { #define DISAS_JUMP_NEXT 4 +#if defined(CONFIG_USER_ONLY) +#define IS_USER(s) 1 +#else +#define IS_USER(s) s->user +#endif + /* XXX: move that elsewhere */ /* ??? Fix exceptions. */ static void *gen_throws_exception; @@ -68,6 +77,25 @@ enum { }; #include "gen-op.h" + +#if defined(CONFIG_USER_ONLY) +#define gen_st(s, name, addr, val) gen_op_st##name##_raw(addr, val) +#define gen_ld(s, name, val, addr) gen_op_ld##name##_raw(val, addr) +#else +#define gen_st(s, name, addr, val) do { \ + if (IS_USER(s)) \ + gen_op_st##name##_user(addr, val); \ + else \ + gen_op_st##name##_kernel(addr, val); \ + } while (0) +#define gen_ld(s, name, val, addr) do { \ + if (IS_USER(s)) \ + gen_op_ld##name##_user(val, addr); \ + else \ + gen_op_ld##name##_kernel(val, addr); \ + } while (0) +#endif + #include "op-hacks.h" #define OS_BYTE 0 @@ -101,40 +129,49 @@ static m68k_def_t m68k_cpu_defs[] = { typedef void (*disas_proc)(DisasContext *, uint16_t); +#ifdef DEBUG_DISPATCH +#define DISAS_INSN(name) \ + static void real_disas_##name (DisasContext *s, uint16_t insn); \ + static void disas_##name (DisasContext *s, uint16_t insn) { \ + if (logfile) fprintf(logfile, "Dispatch " #name "\n"); \ + real_disas_##name(s, insn); } \ + static void real_disas_##name (DisasContext *s, uint16_t insn) +#else #define DISAS_INSN(name) \ static void disas_##name (DisasContext *s, uint16_t insn) +#endif /* Generate a load from the specified address. Narrow values are sign extended to full register width. */ -static inline int gen_load(int opsize, int addr, int sign) +static inline int gen_load(DisasContext * s, int opsize, int addr, int sign) { int tmp; switch(opsize) { case OS_BYTE: tmp = gen_new_qreg(QMODE_I32); if (sign) - gen_op_ld8s32(tmp, addr); + gen_ld(s, 8s32, tmp, addr); else - gen_op_ld8u32(tmp, addr); + gen_ld(s, 8u32, tmp, addr); break; case OS_WORD: tmp = gen_new_qreg(QMODE_I32); if (sign) - gen_op_ld16s32(tmp, addr); + gen_ld(s, 16s32, tmp, addr); else - gen_op_ld16u32(tmp, addr); + gen_ld(s, 16u32, tmp, addr); break; case OS_LONG: tmp = gen_new_qreg(QMODE_I32); - gen_op_ld32(tmp, addr); + gen_ld(s, 32, tmp, addr); break; case OS_SINGLE: tmp = gen_new_qreg(QMODE_F32); - gen_op_ldf32(tmp, addr); + gen_ld(s, f32, tmp, addr); break; case OS_DOUBLE: tmp = gen_new_qreg(QMODE_F64); - gen_op_ldf64(tmp, addr); + gen_ld(s, f64, tmp, addr); break; default: qemu_assert(0, "bad load size"); @@ -144,23 +181,23 @@ static inline int gen_load(int opsize, int addr, int sign) } /* Generate a store. */ -static inline void gen_store(int opsize, int addr, int val) +static inline void gen_store(DisasContext *s, int opsize, int addr, int val) { switch(opsize) { case OS_BYTE: - gen_op_st8(addr, val); + gen_st(s, 8, addr, val); break; case OS_WORD: - gen_op_st16(addr, val); + gen_st(s, 16, addr, val); break; case OS_LONG: - gen_op_st32(addr, val); + gen_st(s, 32, addr, val); break; case OS_SINGLE: - gen_op_stf32(addr, val); + gen_st(s, f32, addr, val); break; case OS_DOUBLE: - gen_op_stf64(addr, val); + gen_st(s, f64, addr, val); break; default: qemu_assert(0, "bad store size"); @@ -170,13 +207,13 @@ static inline void gen_store(int opsize, int addr, int val) /* Generate an unsigned load if VAL is 0 a signed load if val is -1, otherwise generate a store. */ -static int gen_ldst(int opsize, int addr, int val) +static int gen_ldst(DisasContext *s, int opsize, int addr, int val) { if (val > 0) { - gen_store(opsize, addr, val); + gen_store(s, opsize, addr, val); return 0; } else { - return gen_load(opsize, addr, val != 0); + return gen_load(s, opsize, addr, val != 0); } } @@ -191,7 +228,7 @@ static int gen_lea_indexed(DisasContext *s, int opsize, int base) int tmp; offset = s->pc; - ext = lduw(s->pc); + ext = lduw_code(s->pc); s->pc += 2; tmp = ((ext >> 12) & 7) + ((ext & 0x8000) ? QREG_A0 : QREG_D0); /* ??? Check W/L bit. */ @@ -216,9 +253,9 @@ static int gen_lea_indexed(DisasContext *s, int opsize, int base) static inline uint32_t read_im32(DisasContext *s) { uint32_t im; - im = ((uint32_t)lduw(s->pc)) << 16; + im = ((uint32_t)lduw_code(s->pc)) << 16; s->pc += 2; - im |= lduw(s->pc); + im |= lduw_code(s->pc); s->pc += 2; return im; } @@ -343,7 +380,7 @@ static int gen_lea(DisasContext *s, uint16_t insn, int opsize) case 5: /* Indirect displacement. */ reg += QREG_A0; tmp = gen_new_qreg(QMODE_I32); - ext = lduw(s->pc); + ext = lduw_code(s->pc); s->pc += 2; gen_op_add32(tmp, reg, gen_im32((int16_t)ext)); return tmp; @@ -353,7 +390,7 @@ static int gen_lea(DisasContext *s, uint16_t insn, int opsize) case 7: /* Other */ switch (reg) { case 0: /* Absolute short. */ - offset = ldsw(s->pc); + offset = ldsw_code(s->pc); s->pc += 2; return gen_im32(offset); case 1: /* Absolute long. */ @@ -362,7 +399,7 @@ static int gen_lea(DisasContext *s, uint16_t insn, int opsize) case 2: /* pc displacement */ tmp = gen_new_qreg(QMODE_I32); offset = s->pc; - offset += ldsw(s->pc); + offset += ldsw_code(s->pc); s->pc += 2; return gen_im32(offset); case 3: /* pc index+displacement. */ @@ -391,7 +428,7 @@ static inline int gen_ea_once(DisasContext *s, uint16_t insn, int opsize, if (addrp) *addrp = tmp; } - return gen_ldst(opsize, tmp, val); + return gen_ldst(s, opsize, tmp, val); } /* Generate code to load/store a value ito/from an EA. If VAL > 0 this is @@ -424,10 +461,10 @@ static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val, } case 2: /* Indirect register */ reg += QREG_A0; - return gen_ldst(opsize, reg, val); + return gen_ldst(s, opsize, reg, val); case 3: /* Indirect postincrement. */ reg += QREG_A0; - result = gen_ldst(opsize, reg, val); + result = gen_ldst(s, opsize, reg, val); /* ??? This is not exception safe. The instruction may still fault after this point. */ if (val > 0 || !addrp) @@ -443,7 +480,7 @@ static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val, if (addrp) *addrp = tmp; } - result = gen_ldst(opsize, tmp, val); + result = gen_ldst(s, opsize, tmp, val); /* ??? This is not exception safe. The instruction may still fault after this point. */ if (val > 0 || !addrp) { @@ -467,16 +504,16 @@ static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val, switch (opsize) { case OS_BYTE: if (val) - offset = ldsb(s->pc + 1); + offset = ldsb_code(s->pc + 1); else - offset = ldub(s->pc + 1); + offset = ldub_code(s->pc + 1); s->pc += 2; break; case OS_WORD: if (val) - offset = ldsw(s->pc); + offset = ldsw_code(s->pc); else - offset = lduw(s->pc); + offset = lduw_code(s->pc); s->pc += 2; break; case OS_LONG: @@ -622,6 +659,14 @@ DISAS_INSN(scc) gen_set_label(l1); } +/* Force a TB lookup after an instruction that changes the CPU state. */ +static void gen_lookup_tb(DisasContext *s) +{ + gen_flush_cc_op(s); + gen_op_mov32(QREG_PC, gen_im32(s->pc)); + s->is_jmp = DISAS_UPDATE; +} + /* Generate a jump to to the address in qreg DEST. */ static void gen_jmp(DisasContext *s, int dest) { @@ -735,7 +780,7 @@ DISAS_INSN(divl) int reg; uint16_t ext; - ext = lduw(s->pc); + ext = lduw_code(s->pc); s->pc += 2; if (ext & 0x87f8) { gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED); @@ -903,13 +948,13 @@ DISAS_INSN(sats) gen_logic_cc(s, tmp); } -static void gen_push(int val) +static void gen_push(DisasContext *s, int val) { int tmp; tmp = gen_new_qreg(QMODE_I32); gen_op_sub32(tmp, QREG_SP, gen_im32(4)); - gen_store(OS_LONG, tmp, val); + gen_store(s, OS_LONG, tmp, val); gen_op_mov32(QREG_SP, tmp); } @@ -922,7 +967,7 @@ DISAS_INSN(movem) int tmp; int is_load; - mask = lduw(s->pc); + mask = lduw_code(s->pc); s->pc += 2; tmp = gen_lea(s, insn, OS_LONG); addr = gen_new_qreg(QMODE_I32); @@ -935,10 +980,10 @@ DISAS_INSN(movem) else reg = AREG(i, 0); if (is_load) { - tmp = gen_load(OS_LONG, addr, 0); + tmp = gen_load(s, OS_LONG, addr, 0); gen_op_mov32(reg, tmp); } else { - gen_store(OS_LONG, addr, reg); + gen_store(s, OS_LONG, addr, reg); } if (mask != 1) gen_op_add32(addr, addr, gen_im32(4)); @@ -963,7 +1008,7 @@ DISAS_INSN(bitop_im) opsize = OS_LONG; op = (insn >> 6) & 3; - bitnum = lduw(s->pc); + bitnum = lduw_code(s->pc); s->pc += 2; if (bitnum & 0xff00) { disas_undef(s, insn); @@ -1155,9 +1200,8 @@ DISAS_INSN(clr) gen_logic_cc(s, gen_im32(0)); } -DISAS_INSN(move_from_ccr) +static int gen_get_ccr(DisasContext *s) { - int reg; int dest; gen_flush_flags(s); @@ -1165,8 +1209,17 @@ DISAS_INSN(move_from_ccr) gen_op_get_xflag(dest); gen_op_shl32(dest, dest, gen_im32(4)); gen_op_or32(dest, dest, QREG_CC_DEST); + return dest; +} + +DISAS_INSN(move_from_ccr) +{ + int reg; + int ccr; + + ccr = gen_get_ccr(s); reg = DREG(insn, 0); - gen_partset_reg(OS_WORD, reg, dest); + gen_partset_reg(OS_WORD, reg, ccr); } DISAS_INSN(neg) @@ -1184,7 +1237,16 @@ DISAS_INSN(neg) s->cc_op = CC_OP_SUB; } -DISAS_INSN(move_to_ccr) +static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) +{ + gen_op_logic_cc(gen_im32(val & 0xf)); + gen_op_update_xflag_tst(gen_im32((val & 0x10) >> 4)); + if (!ccr_only) { + gen_op_mov32(QREG_SR, gen_im32(val & 0xff00)); + } +} + +static void gen_set_sr(DisasContext *s, uint16_t insn, int ccr_only) { int src1; int reg; @@ -1199,19 +1261,26 @@ DISAS_INSN(move_to_ccr) gen_op_shr32(src1, reg, gen_im32(4)); gen_op_and32(src1, src1, gen_im32(1)); gen_op_update_xflag_tst(src1); + if (!ccr_only) { + gen_op_and32(QREG_SR, reg, gen_im32(0xff00)); + } } - else if ((insn & 0x3f) != 0x3c) + else if ((insn & 0x3f) == 0x3c) { - uint8_t val; - val = ldsb(s->pc); + uint16_t val; + val = lduw_code(s->pc); s->pc += 2; - gen_op_logic_cc(gen_im32(val & 0xf)); - gen_op_update_xflag_tst(gen_im32((val & 0x10) >> 4)); + gen_set_sr_im(s, val, ccr_only); } else disas_undef(s, insn); } +DISAS_INSN(move_to_ccr) +{ + gen_set_sr(s, insn, 1); +} + DISAS_INSN(not) { int reg; @@ -1244,7 +1313,7 @@ DISAS_INSN(pea) int tmp; tmp = gen_lea(s, insn, OS_LONG); - gen_push(tmp); + gen_push(s, tmp); } DISAS_INSN(ext) @@ -1322,7 +1391,7 @@ DISAS_INSN(mull) /* The upper 32 bits of the product are discarded, so muls.l and mulu.l are functionally equivalent. */ - ext = lduw(s->pc); + ext = lduw_code(s->pc); s->pc += 2; if (ext & 0x87ff) { gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED); @@ -1343,12 +1412,12 @@ DISAS_INSN(link) int reg; int tmp; - offset = ldsw(s->pc); + offset = ldsw_code(s->pc); s->pc += 2; reg = AREG(insn, 0); tmp = gen_new_qreg(QMODE_I32); gen_op_sub32(tmp, QREG_SP, gen_im32(4)); - gen_store(OS_LONG, tmp, reg); + gen_store(s, OS_LONG, tmp, reg); if (reg != QREG_SP) gen_op_mov32(reg, tmp); gen_op_add32(QREG_SP, tmp, gen_im32(offset)); @@ -1363,7 +1432,7 @@ DISAS_INSN(unlk) src = gen_new_qreg(QMODE_I32); reg = AREG(insn, 0); gen_op_mov32(src, reg); - tmp = gen_load(OS_LONG, src, 0); + tmp = gen_load(s, OS_LONG, src, 0); gen_op_mov32(reg, tmp); gen_op_add32(QREG_SP, src, gen_im32(4)); } @@ -1376,7 +1445,7 @@ DISAS_INSN(rts) { int tmp; - tmp = gen_load(OS_LONG, QREG_SP, 0); + tmp = gen_load(s, OS_LONG, QREG_SP, 0); gen_op_add32(QREG_SP, QREG_SP, gen_im32(4)); gen_jmp(s, tmp); } @@ -1390,7 +1459,7 @@ DISAS_INSN(jump) tmp = gen_lea(s, insn, OS_LONG); if ((insn & 0x40) == 0) { /* jsr */ - gen_push(gen_im32(s->pc)); + gen_push(s, gen_im32(s->pc)); } gen_jmp(s, tmp); } @@ -1460,14 +1529,14 @@ DISAS_INSN(branch) op = (insn >> 8) & 0xf; offset = (int8_t)insn; if (offset == 0) { - offset = ldsw(s->pc); + offset = ldsw_code(s->pc); s->pc += 2; } else if (offset == -1) { offset = read_im32(s); } if (op == 1) { /* bsr */ - gen_push(gen_im32(s->pc)); + gen_push(s, gen_im32(s->pc)); } gen_flush_cc_op(s); if (op > 1) { @@ -1752,68 +1821,154 @@ DISAS_INSN(ff1) cpu_abort(NULL, "Unimplemented insn: ff1"); } +static int gen_get_sr(DisasContext *s) +{ + int ccr; + int sr; + + ccr = gen_get_ccr(s); + sr = gen_new_qreg(QMODE_I32); + gen_op_and32(sr, QREG_SR, gen_im32(0xffe0)); + gen_op_or32(sr, sr, ccr); + return sr; +} + DISAS_INSN(strldsr) { uint16_t ext; uint32_t addr; addr = s->pc - 2; - ext = lduw(s->pc); + ext = lduw_code(s->pc); s->pc += 2; - if (ext != 0x46FC) + if (ext != 0x46FC) { gen_exception(s, addr, EXCP_UNSUPPORTED); - else + return; + } + ext = lduw_code(s->pc); + s->pc += 2; + if (IS_USER(s) || (ext & SR_S) == 0) { gen_exception(s, addr, EXCP_PRIVILEGE); + return; + } + gen_push(s, gen_get_sr(s)); + gen_set_sr_im(s, ext, 0); } DISAS_INSN(move_from_sr) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + int reg; + int sr; + + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + sr = gen_get_sr(s); + reg = DREG(insn, 0); + gen_partset_reg(OS_WORD, reg, sr); } DISAS_INSN(move_to_sr) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + gen_set_sr(s, insn, 0); + gen_lookup_tb(s); } DISAS_INSN(move_from_usp) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + /* TODO: Implement USP. */ + gen_exception(s, s->pc - 2, EXCP_ILLEGAL); } DISAS_INSN(move_to_usp) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + /* TODO: Implement USP. */ + gen_exception(s, s->pc - 2, EXCP_ILLEGAL); } DISAS_INSN(halt) { - gen_exception(s, s->pc, EXCP_HLT); + gen_flush_cc_op(s); + gen_jmp(s, gen_im32(s->pc)); + gen_op_halt(); } DISAS_INSN(stop) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + uint16_t ext; + + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + + ext = lduw_code(s->pc); + s->pc += 2; + + gen_set_sr_im(s, ext, 0); + disas_halt(s, insn); } DISAS_INSN(rte) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + gen_exception(s, s->pc - 2, EXCP_RTE); } DISAS_INSN(movec) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + uint16_t ext; + int reg; + + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + + ext = lduw_code(s->pc); + s->pc += 2; + + if (ext & 0x8000) { + reg = AREG(ext, 12); + } else { + reg = DREG(ext, 12); + } + gen_op_movec(gen_im32(ext & 0xfff), reg); + gen_lookup_tb(s); } DISAS_INSN(intouch) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + /* ICache fetch. Implement as no-op. */ } DISAS_INSN(cpushl) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + /* Cache push/invalidate. Implement as no-op. */ } DISAS_INSN(wddata) @@ -1823,7 +1978,12 @@ DISAS_INSN(wddata) DISAS_INSN(wdebug) { - gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + if (IS_USER(s)) { + gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); + return; + } + /* TODO: Implement wdebug. */ + qemu_assert(0, "WDEBUG not implemented"); } DISAS_INSN(trap) @@ -1843,7 +2003,7 @@ DISAS_INSN(fpu) int round; int opsize; - ext = lduw(s->pc); + ext = lduw_code(s->pc); s->pc += 2; opmode = ext & 0x7f; switch ((ext >> 13) & 7) { @@ -1928,10 +2088,10 @@ DISAS_INSN(fpu) if (ext & mask) { if (ext & (1 << 13)) { /* store */ - gen_op_stf64(addr, dest); + gen_st(s, f64, addr, dest); } else { /* load */ - gen_op_ldf64(dest, addr); + gen_ld(s, f64, dest, addr); } if (ext & (mask - 1)) gen_op_add32(addr, addr, gen_im32(8)); @@ -2060,10 +2220,10 @@ DISAS_INSN(fbcc) int l1; addr = s->pc; - offset = ldsw(s->pc); + offset = ldsw_code(s->pc); s->pc += 2; if (insn & (1 << 6)) { - offset = (offset << 16) | lduw(s->pc); + offset = (offset << 16) | lduw_code(s->pc); s->pc += 2; } @@ -2143,6 +2303,18 @@ DISAS_INSN(fbcc) gen_jmp_tb(s, 1, addr + offset); } +DISAS_INSN(frestore) +{ + /* TODO: Implement frestore. */ + qemu_assert(0, "FRESTORE not implemented"); +} + +DISAS_INSN(fsave) +{ + /* TODO: Implement fsave. */ + qemu_assert(0, "FSAVE not implemented"); +} + static disas_proc opcode_table[65536]; static void @@ -2168,11 +2340,10 @@ register_opcode (disas_proc proc, uint16_t opcode, uint16_t mask) i <<= 1; from = opcode & ~(i - 1); to = from + i; - for (i = from; i < to; i++) - { + for (i = from; i < to; i++) { if ((i & mask) == opcode) opcode_table[i] = proc; - } + } } /* Register m68k opcode handlers. Order is important. @@ -2274,6 +2445,8 @@ register_m68k_insns (m68k_def_t *def) INSN(undef_fpu, f000, f000, CF_A); INSN(fpu, f200, ffc0, CF_FPU); INSN(fbcc, f280, ffc0, CF_FPU); + INSN(frestore, f340, ffc0, CF_FPU); + INSN(fsave, f340, ffc0, CF_FPU); INSN(intouch, f340, ffc0, CF_A); INSN(cpushl, f428, ff38, CF_A); INSN(wddata, fb00, ff00, CF_A); @@ -2287,7 +2460,7 @@ static void disas_m68k_insn(CPUState * env, DisasContext *s) { uint16_t insn; - insn = lduw(s->pc); + insn = lduw_code(s->pc); s->pc += 2; opcode_table[insn](s, insn); @@ -2576,6 +2749,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb, dc->cc_op = CC_OP_DYNAMIC; dc->singlestep_enabled = env->singlestep_enabled; dc->fpcr = env->fpcr; + dc->user = (env->sr & SR_S) == 0; nb_gen_labels = 0; lj = -1; do { @@ -2675,6 +2849,19 @@ int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) return gen_intermediate_code_internal(env, tb, 1); } +void cpu_reset(CPUM68KState *env) +{ + memset(env, 0, offsetof(CPUM68KState, breakpoints)); +#if !defined (CONFIG_USER_ONLY) + env->sr = 0x2700; +#endif + /* ??? FP regs should be initialized to NaN. */ + env->cc_op = CC_OP_FLAGS; + /* TODO: We should set PC from the interrupt vector. */ + env->pc = 0; + tlb_flush(env, 1); +} + CPUM68KState *cpu_m68k_init(void) { CPUM68KState *env; @@ -2684,10 +2871,7 @@ CPUM68KState *cpu_m68k_init(void) return NULL; cpu_exec_init(env); - memset(env, 0, sizeof(CPUM68KState)); - /* ??? FP regs should be initialized to NaN. */ - cpu_single_env = env; - env->cc_op = CC_OP_FLAGS; + cpu_reset(env); return env; } @@ -2696,23 +2880,20 @@ void cpu_m68k_close(CPUM68KState *env) free(env); } -m68k_def_t *m68k_find_by_name(const char *name) +int cpu_m68k_set_model(CPUM68KState *env, const char * name) { m68k_def_t *def; - def = m68k_cpu_defs; - while (def->name) - { + for (def = m68k_cpu_defs; def->name; def++) { if (strcmp(def->name, name) == 0) - return def; - def++; - } - return NULL; -} + break; + } + if (!def->name) + return 1; -void cpu_m68k_register(CPUM68KState *env, m68k_def_t *def) -{ register_m68k_insns(def); + + return 0; } void cpu_dump_state(CPUState *env, FILE *f, @@ -2737,24 +2918,3 @@ void cpu_dump_state(CPUState *env, FILE *f, cpu_fprintf (f, "FPRESULT = %12g\n", env->fp_result); } -/* ??? */ -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) -{ - return addr; -} - -#if defined(CONFIG_USER_ONLY) - -int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, - int is_user, int is_softmmu) -{ - env->exception_index = EXCP_ACCESS; - env->mmu.ar = address; - return 1; -} - -#else - -#error not implemented - -#endif |