📄 translate.c
字号:
/* * m68k translation * * Copyright (c) 2005-2006 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"static inline void qemu_assert(int cond, const char *msg){ if (!cond) { fprintf (stderr, "badness: %s\n", msg); abort(); }}/* internal defines */typedef struct DisasContext { target_ulong pc; int is_jmp; int cc_op; uint32_t fpcr; struct TranslationBlock *tb; int singlestep_enabled;} DisasContext;#define DISAS_JUMP_NEXT 4/* 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"#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)#define M68K_INSN_CF_A (1 << 0)#define M68K_INSN_CF_B (1 << 1)#define M68K_INSN_CF_C (1 << 2)#define M68K_INSN_CF_MAC (1 << 3)#define M68K_INSN_CF_EMAC (1 << 4)#define M68K_INSN_CF_FPU (1 << 5)struct m68k_def_t { const char * name; uint32_t insns;};static m68k_def_t m68k_cpu_defs[] = { {"m5206", M68K_INSN_CF_A}, {"cfv4e", M68K_INSN_CF_A | M68K_INSN_CF_B | M68K_INSN_CF_C | M68K_INSN_CF_MAC | M68K_INSN_CF_EMAC | M68K_INSN_CF_FPU}, {NULL, 0}, };typedef void (*disas_proc)(DisasContext *, uint16_t);#define DISAS_INSN(name) \ static void disas_##name (DisasContext *s, uint16_t insn)/* 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){ int tmp; switch(opsize) { case OS_BYTE: tmp = gen_new_qreg(QMODE_I32); if (sign) gen_op_ld8s32(tmp, addr); else gen_op_ld8u32(tmp, addr); break; case OS_WORD: tmp = gen_new_qreg(QMODE_I32); if (sign) gen_op_ld16s32(tmp, addr); else gen_op_ld16u32(tmp, addr); break; case OS_LONG: tmp = gen_new_qreg(QMODE_I32); gen_op_ld32(tmp, addr); break; case OS_SINGLE: tmp = gen_new_qreg(QMODE_F32); gen_op_ldf32(tmp, addr); break; case OS_DOUBLE: tmp = gen_new_qreg(QMODE_F64); gen_op_ldf64(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(int opsize, int addr, int val){ switch(opsize) { case OS_BYTE: gen_op_st8(addr, val); break; case OS_WORD: gen_op_st16(addr, val); break; case OS_LONG: gen_op_st32(addr, val); break; case OS_SINGLE: gen_op_stf32(addr, val); break; case OS_DOUBLE: gen_op_stf64(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(int opsize, int addr, int val){ if (val > 0) { gen_store(opsize, addr, val); return 0; } else { return gen_load(opsize, addr, val != 0); }}/* 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){ int scale; uint32_t offset; uint16_t ext; int add; int tmp; offset = s->pc; ext = lduw(s->pc); s->pc += 2; tmp = ((ext >> 12) & 7) + ((ext & 0x8000) ? QREG_A0 : QREG_D0); /* ??? Check W/L bit. */ scale = (ext >> 9) & 3; if (scale == 0) { add = tmp; } else { add = gen_new_qreg(QMODE_I32); gen_op_shl32(add, tmp, gen_im32(scale)); } tmp = gen_new_qreg(QMODE_I32); if (base != -1) { gen_op_add32(tmp, base, gen_im32((int8_t)ext)); gen_op_add32(tmp, tmp, add); } else { gen_op_add32(tmp, add, gen_im32(offset + (int8_t)ext)); } return tmp;}/* Read a 32-bit immediate constant. */static inline uint32_t read_im32(DisasContext *s){ uint32_t im; im = ((uint32_t)lduw(s->pc)) << 16; s->pc += 2; im |= lduw(s->pc); s->pc += 2; return im;}/* 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_op_flush_flags(s->cc_op); 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. */ /* ??? generate bad addressing mode fault. */ qemu_assert(0, "invalid addressing mode"); 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(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(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(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: /* ??? generate bad addressing mode fault. */ qemu_assert(0, "invalid addressing mode"); } } /* 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 (addrp) *addrp = tmp; } return gen_ldst(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(opsize, reg, val); case 3: /* Indirect postincrement. */ reg += QREG_A0; result = gen_ldst(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; case 4: /* Indirect predecrememnt. */ { int tmp; if (addrp && val > 0) { tmp = *addrp; } else { tmp = gen_lea(s, insn, opsize); if (addrp) *addrp = tmp; } result = gen_ldst(opsize, tmp, val); /* ??? This is not exception safe. The instruction may still fault after this point. */ if (val > 0 || !addrp) { reg += QREG_A0; gen_op_mov32(reg, tmp); } } return result; case 5: /* Indirect displacement. */ case 6: /* Indirect index + displacement. */ return gen_ea_once(s, insn, opsize, val, addrp); case 7: /* Other */ switch (reg) { case 0: /* Absolute short. */ case 1: /* Absolute long. */ case 2: /* pc displacement */ case 3: /* pc index+displacement. */ return gen_ea_once(s, insn, opsize, val, addrp); case 4: /* Immediate. */ /* Sign extend values for consistency. */ switch (opsize) { case OS_BYTE: if (val) offset = ldsb(s->pc + 1); else offset = ldub(s->pc + 1); s->pc += 2; break; case OS_WORD: if (val) offset = ldsw(s->pc); else offset = lduw(s->pc); s->pc += 2; break; case OS_LONG: offset = read_im32(s); break; default: qemu_assert(0, "Bad immediate operand"); } return gen_im32(offset); default: qemu_assert(0, "invalid addressing mode"); } } /* Should never happen. */ return -1;}static void gen_logic_cc(DisasContext *s, int val){ gen_op_logic_cc(val); s->cc_op = CC_OP_LOGIC;}static void gen_jmpcc(DisasContext *s, int cond, int l1){ int tmp; gen_flush_flags(s); switch (cond) { case 0: /* T */ gen_op_jmp(l1); break; case 1: /* F */ break; case 2: /* HI (!C && !Z) */ tmp = gen_new_qreg(QMODE_I32); gen_op_and32(tmp, QREG_CC_DEST, gen_im32(CCF_C | CCF_Z)); gen_op_jmp_z32(tmp, l1); break; case 3: /* LS (C || Z) */ tmp = gen_new_qreg(QMODE_I32); gen_op_and32(tmp, QREG_CC_DEST, gen_im32(CCF_C | CCF_Z)); gen_op_jmp_nz32(tmp, l1); break; case 4: /* CC (!C) */ tmp = gen_new_qreg(QMODE_I32); gen_op_and32(tmp, QREG_CC_DEST, gen_im32(CCF_C)); gen_op_jmp_z32(tmp, l1); break; case 5: /* CS (C) */ tmp = gen_new_qreg(QMODE_I32); gen_op_and32(tmp, QREG_CC_DEST, gen_im32(CCF_C));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -