📄 translate.c.svn-base
字号:
/* * m68k translation * * Copyright (c) 2005-2007 CodeSourcery * Written by Paul Brook * * 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 * 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 <stdarg.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <inttypes.h>#include "config.h"#include "cpu.h"#include "exec-all.h"#include "disas.h"#include "m68k-qreg.h"//#define DEBUG_DISPATCH 1static inline void qemu_assert(int cond, const char *msg){ if (!cond) { fprintf (stderr, "badness: %s\n", msg); abort(); }}/* internal defines */typedef struct DisasContext { CPUM68KState *env; target_ulong insn_pc; /* Start of the current instruction. */ target_ulong pc; int is_jmp; int cc_op; int user; uint32_t fpcr; struct TranslationBlock *tb; int singlestep_enabled; int is_mem;} 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;#define gen_last_qop NULLstatic uint16_t *gen_opc_ptr;static uint32_t *gen_opparam_ptr;extern FILE *logfile;extern int loglevel;enum {#define DEF(s, n, copy_size) INDEX_op_ ## s,#include "opc.h"#undef DEF NB_OPS,};#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#define OS_WORD 1#define OS_LONG 2#define OS_SINGLE 4#define OS_DOUBLE 5#define DREG(insn, pos) (((insn >> pos) & 7) + QREG_D0)#define AREG(insn, pos) (((insn >> pos) & 7) + QREG_A0)#define FREG(insn, pos) (((insn >> pos) & 7) + QREG_F0)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(DisasContext * s, int opsize, int addr, int sign){ int tmp; s->is_mem = 1; switch(opsize) { case OS_BYTE: tmp = gen_new_qreg(QMODE_I32); if (sign) gen_ld(s, 8s32, tmp, addr); else gen_ld(s, 8u32, tmp, addr); break; case OS_WORD: tmp = gen_new_qreg(QMODE_I32); if (sign) gen_ld(s, 16s32, tmp, addr); else gen_ld(s, 16u32, tmp, addr); break; case OS_LONG: tmp = gen_new_qreg(QMODE_I32); gen_ld(s, 32, tmp, addr); break; case OS_SINGLE: tmp = gen_new_qreg(QMODE_F32); gen_ld(s, f32, tmp, addr); break; case OS_DOUBLE: tmp = gen_new_qreg(QMODE_F64); gen_ld(s, f64, tmp, addr); break; default: qemu_assert(0, "bad load size"); } gen_throws_exception = gen_last_qop; return tmp;}/* Generate a store. */static inline void gen_store(DisasContext *s, int opsize, int addr, int val){ s->is_mem = 1; switch(opsize) { case OS_BYTE: gen_st(s, 8, addr, val); break; case OS_WORD: gen_st(s, 16, addr, val); break; case OS_LONG: gen_st(s, 32, addr, val); break; case OS_SINGLE: gen_st(s, f32, addr, val); break; case OS_DOUBLE: gen_st(s, f64, addr, val); break; default: qemu_assert(0, "bad store size"); } gen_throws_exception = gen_last_qop;}/* Generate an unsigned load if VAL is 0 a signed load if val is -1, otherwise generate a store. */static int gen_ldst(DisasContext *s, int opsize, int addr, int val){ if (val > 0) { gen_store(s, opsize, addr, val); return 0; } else { return gen_load(s, opsize, addr, val != 0); }}/* Read a 32-bit immediate constant. */static inline uint32_t read_im32(DisasContext *s){ uint32_t im; im = ((uint32_t)lduw_code(s->pc)) << 16; s->pc += 2; im |= lduw_code(s->pc); s->pc += 2; return im;}/* Calculate and address index. */static int gen_addr_index(uint16_t ext, int tmp){ int add; int scale; add = (ext & 0x8000) ? AREG(ext, 12) : DREG(ext, 12); if ((ext & 0x800) == 0) { gen_op_ext16s32(tmp, add); add = tmp; } scale = (ext >> 9) & 3; if (scale != 0) { gen_op_shl32(tmp, add, gen_im32(scale)); add = tmp; } return add;}/* Handle a base + index + displacement effective addresss. A base of -1 means pc-relative. */static int gen_lea_indexed(DisasContext *s, int opsize, int base){ uint32_t offset; uint16_t ext; int add; int tmp; uint32_t bd, od; offset = s->pc; ext = lduw_code(s->pc); s->pc += 2; if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX)) return -1; if (ext & 0x100) { /* full extension word format */ if (!m68k_feature(s->env, M68K_FEATURE_EXT_FULL)) return -1; if ((ext & 0x30) > 0x10) { /* base displacement */ if ((ext & 0x30) == 0x20) { bd = (int16_t)lduw_code(s->pc); s->pc += 2; } else { bd = read_im32(s); } } else { bd = 0; } tmp = gen_new_qreg(QMODE_I32); if ((ext & 0x44) == 0) { /* pre-index */ add = gen_addr_index(ext, tmp); } else { add = QREG_NULL; } if ((ext & 0x80) == 0) { /* base not suppressed */ if (base == -1) { base = gen_im32(offset + bd); bd = 0; } if (add) { gen_op_add32(tmp, add, base); add = tmp; } else { add = base; } } if (add) { if (bd != 0) { gen_op_add32(tmp, add, gen_im32(bd)); add = tmp; } } else { add = gen_im32(bd); } if ((ext & 3) != 0) { /* memory indirect */ base = gen_load(s, OS_LONG, add, 0); if ((ext & 0x44) == 4) { add = gen_addr_index(ext, tmp); gen_op_add32(tmp, add, base); add = tmp; } else { add = base; } if ((ext & 3) > 1) { /* outer displacement */ if ((ext & 3) == 2) { od = (int16_t)lduw_code(s->pc); s->pc += 2; } else { od = read_im32(s); } } else { od = 0; } if (od != 0) { gen_op_add32(tmp, add, gen_im32(od)); add = tmp; } } } else { /* brief extension word format */ tmp = gen_new_qreg(QMODE_I32); add = gen_addr_index(ext, tmp); if (base != -1) { gen_op_add32(tmp, add, base); if ((int8_t)ext) gen_op_add32(tmp, tmp, gen_im32((int8_t)ext)); } else { gen_op_add32(tmp, add, gen_im32(offset + (int8_t)ext)); } add = tmp; } return add;}/* Update the CPU env CC_OP state. */static inline void gen_flush_cc_op(DisasContext *s){ if (s->cc_op != CC_OP_DYNAMIC) gen_op_mov32(QREG_CC_OP, gen_im32(s->cc_op));}/* Evaluate all the CC flags. */static inline void gen_flush_flags(DisasContext *s){ if (s->cc_op == CC_OP_FLAGS) return; gen_flush_cc_op(s); gen_op_flush_flags(); s->cc_op = CC_OP_FLAGS;}static inline int opsize_bytes(int opsize){ switch (opsize) { case OS_BYTE: return 1; case OS_WORD: return 2; case OS_LONG: return 4; case OS_SINGLE: return 4; case OS_DOUBLE: return 8; default: qemu_assert(0, "bad operand size"); }}/* Assign value to a register. If the width is less than the register width only the low part of the register is set. */static void gen_partset_reg(int opsize, int reg, int val){ int tmp; switch (opsize) { case OS_BYTE: gen_op_and32(reg, reg, gen_im32(0xffffff00)); tmp = gen_new_qreg(QMODE_I32); gen_op_and32(tmp, val, gen_im32(0xff)); gen_op_or32(reg, reg, tmp); break; case OS_WORD: gen_op_and32(reg, reg, gen_im32(0xffff0000)); tmp = gen_new_qreg(QMODE_I32); gen_op_and32(tmp, val, gen_im32(0xffff)); gen_op_or32(reg, reg, tmp); break; case OS_LONG: gen_op_mov32(reg, val); break; case OS_SINGLE: gen_op_pack_32_f32(reg, val); break; default: qemu_assert(0, "Bad operand size"); break; }}/* Sign or zero extend a value. */static inline int gen_extend(int val, int opsize, int sign){ int tmp; switch (opsize) { case OS_BYTE: tmp = gen_new_qreg(QMODE_I32); if (sign) gen_op_ext8s32(tmp, val); else gen_op_ext8u32(tmp, val); break; case OS_WORD: tmp = gen_new_qreg(QMODE_I32); if (sign) gen_op_ext16s32(tmp, val); else gen_op_ext16u32(tmp, val); break; case OS_LONG: tmp = val; break; case OS_SINGLE: tmp = gen_new_qreg(QMODE_F32); gen_op_pack_f32_32(tmp, val); break; default: qemu_assert(0, "Bad operand size"); } return tmp;}/* Generate code for an "effective address". Does not adjust the base register for autoincrememnt addressing modes. */static int gen_lea(DisasContext *s, uint16_t insn, int opsize){ int reg; int tmp; uint16_t ext; uint32_t offset; reg = insn & 7; switch ((insn >> 3) & 7) { case 0: /* Data register direct. */ case 1: /* Address register direct. */ return -1; case 2: /* Indirect register */ case 3: /* Indirect postincrement. */ reg += QREG_A0; return reg; case 4: /* Indirect predecrememnt. */ reg += QREG_A0; tmp = gen_new_qreg(QMODE_I32); gen_op_sub32(tmp, reg, gen_im32(opsize_bytes(opsize))); return tmp; case 5: /* Indirect displacement. */ reg += QREG_A0; tmp = gen_new_qreg(QMODE_I32); ext = lduw_code(s->pc); s->pc += 2; gen_op_add32(tmp, reg, gen_im32((int16_t)ext)); return tmp; case 6: /* Indirect index + displacement. */ reg += QREG_A0; return gen_lea_indexed(s, opsize, reg); case 7: /* Other */ switch (reg) { case 0: /* Absolute short. */ offset = ldsw_code(s->pc); s->pc += 2; return gen_im32(offset); case 1: /* Absolute long. */ offset = read_im32(s); return gen_im32(offset); case 2: /* pc displacement */ tmp = gen_new_qreg(QMODE_I32); offset = s->pc; offset += ldsw_code(s->pc); s->pc += 2; return gen_im32(offset); case 3: /* pc index+displacement. */ return gen_lea_indexed(s, opsize, -1); case 4: /* Immediate. */ default: return -1; } } /* Should never happen. */ return -1;}/* Helper function for gen_ea. Reuse the computed address between the for read/write operands. */static inline int gen_ea_once(DisasContext *s, uint16_t insn, int opsize, int val, int *addrp){ int tmp; if (addrp && val > 0) { tmp = *addrp; } else { tmp = gen_lea(s, insn, opsize); if (tmp == -1) return -1; if (addrp) *addrp = tmp; } return gen_ldst(s, opsize, tmp, val);}/* Generate code to load/store a value ito/from an EA. If VAL > 0 this is a write otherwise it is a read (0 == sign extend, -1 == zero extend). ADDRP is non-null for readwrite operands. */static int gen_ea(DisasContext *s, uint16_t insn, int opsize, int val, int *addrp){ int reg; int result; uint32_t offset; reg = insn & 7; switch ((insn >> 3) & 7) { case 0: /* Data register direct. */ reg += QREG_D0; if (val > 0) { gen_partset_reg(opsize, reg, val); return 0; } else { return gen_extend(reg, opsize, val); } case 1: /* Address register direct. */ reg += QREG_A0; if (val > 0) { gen_op_mov32(reg, val); return 0; } else { return gen_extend(reg, opsize, val); } case 2: /* Indirect register */ reg += QREG_A0; return gen_ldst(s, opsize, reg, val); case 3: /* Indirect postincrement. */ reg += QREG_A0; result = gen_ldst(s, opsize, reg, val); /* ??? This is not exception safe. The instruction may still fault after this point. */ if (val > 0 || !addrp) gen_op_add32(reg, reg, gen_im32(opsize_bytes(opsize))); return result;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -