diff options
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/cpu.h | 63 | ||||
-rw-r--r-- | target-arm/exec.h | 40 | ||||
-rw-r--r-- | target-arm/op.c | 673 | ||||
-rw-r--r-- | target-arm/op_template.h | 48 | ||||
-rw-r--r-- | target-arm/translate.c | 903 |
5 files changed, 1727 insertions, 0 deletions
diff --git a/target-arm/cpu.h b/target-arm/cpu.h new file mode 100644 index 0000000000..7f755a7541 --- /dev/null +++ b/target-arm/cpu.h @@ -0,0 +1,63 @@ +/* + * ARM virtual CPU header + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 + */ +#ifndef CPU_ARM_H +#define CPU_ARM_H + +#include "cpu-defs.h" + +#define EXCP_UDEF 1 /* undefined instruction */ +#define EXCP_SWI 2 /* software interrupt */ + +typedef struct CPUARMState { + uint32_t regs[16]; + uint32_t cpsr; + + /* cpsr flag cache for faster execution */ + uint32_t CF; /* 0 or 1 */ + uint32_t VF; /* V is the bit 31. All other bits are undefined */ + uint32_t NZF; /* N is bit 31. Z is computed from NZF */ + + /* exception/interrupt handling */ + jmp_buf jmp_env; + int exception_index; + int interrupt_request; + struct TranslationBlock *current_tb; + int user_mode_only; + + /* user data */ + void *opaque; +} CPUARMState; + +CPUARMState *cpu_arm_init(void); +int cpu_arm_exec(CPUARMState *s); +void cpu_arm_close(CPUARMState *s); +/* 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. */ +struct siginfo; +int cpu_arm_signal_handler(int host_signum, struct siginfo *info, + void *puc); + +void cpu_arm_dump_state(CPUARMState *env, FILE *f, int flags); + +#define TARGET_PAGE_BITS 12 +#include "cpu-all.h" + +#endif diff --git a/target-arm/exec.h b/target-arm/exec.h new file mode 100644 index 0000000000..70afdc5d95 --- /dev/null +++ b/target-arm/exec.h @@ -0,0 +1,40 @@ +/* + * ARM execution defines + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 "dyngen-exec.h" + +register struct CPUARMState *env asm(AREG0); +register uint32_t T0 asm(AREG1); +register uint32_t T1 asm(AREG2); +register uint32_t T2 asm(AREG3); + +#include "cpu.h" +#include "exec-all.h" + +void cpu_lock(void); +void cpu_unlock(void); +void cpu_loop_exit(void); + +static inline int compute_cpsr(void) +{ + int ZF; + ZF = (env->NZF == 0); + return env->cpsr | (env->NZF & 0x80000000) | (ZF << 30) | + (env->CF << 29) | ((env->VF & 0x80000000) >> 3); +} diff --git a/target-arm/op.c b/target-arm/op.c new file mode 100644 index 0000000000..5879eba9fd --- /dev/null +++ b/target-arm/op.c @@ -0,0 +1,673 @@ +/* + * ARM micro operations + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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" + +#define REGNAME r0 +#define REG (env->regs[0]) +#include "op_template.h" + +#define REGNAME r1 +#define REG (env->regs[1]) +#include "op_template.h" + +#define REGNAME r2 +#define REG (env->regs[2]) +#include "op_template.h" + +#define REGNAME r3 +#define REG (env->regs[3]) +#include "op_template.h" + +#define REGNAME r4 +#define REG (env->regs[4]) +#include "op_template.h" + +#define REGNAME r5 +#define REG (env->regs[5]) +#include "op_template.h" + +#define REGNAME r6 +#define REG (env->regs[6]) +#include "op_template.h" + +#define REGNAME r7 +#define REG (env->regs[7]) +#include "op_template.h" + +#define REGNAME r8 +#define REG (env->regs[8]) +#include "op_template.h" + +#define REGNAME r9 +#define REG (env->regs[9]) +#include "op_template.h" + +#define REGNAME r10 +#define REG (env->regs[10]) +#include "op_template.h" + +#define REGNAME r11 +#define REG (env->regs[11]) +#include "op_template.h" + +#define REGNAME r12 +#define REG (env->regs[12]) +#include "op_template.h" + +#define REGNAME r13 +#define REG (env->regs[13]) +#include "op_template.h" + +#define REGNAME r14 +#define REG (env->regs[14]) +#include "op_template.h" + +#define REGNAME r15 +#define REG (env->regs[15]) +#include "op_template.h" + +void OPPROTO op_movl_T0_0(void) +{ + T0 = 0; +} + +void OPPROTO op_movl_T0_im(void) +{ + T0 = PARAM1; +} + +void OPPROTO op_movl_T1_im(void) +{ + T1 = PARAM1; +} + +void OPPROTO op_movl_T2_im(void) +{ + T2 = PARAM1; +} + +void OPPROTO op_addl_T1_im(void) +{ + T1 += PARAM1; +} + +void OPPROTO op_addl_T1_T2(void) +{ + T1 += T2; +} + +void OPPROTO op_subl_T1_T2(void) +{ + T1 -= T2; +} + +void OPPROTO op_addl_T0_T1(void) +{ + T0 += T1; +} + +void OPPROTO op_addl_T0_T1_cc(void) +{ + unsigned int src1; + src1 = T0; + T0 += T1; + env->NZF = T0; + env->CF = T0 < src1; + env->VF = (src1 ^ T1 ^ -1) & (src1 ^ T0); +} + +void OPPROTO op_adcl_T0_T1(void) +{ + T0 += T1 + env->CF; +} + +void OPPROTO op_adcl_T0_T1_cc(void) +{ + unsigned int src1; + src1 = T0; + if (!env->CF) { + T0 += T1; + env->CF = T0 < src1; + } else { + T0 += T1 + 1; + env->CF = T0 <= src1; + } + env->VF = (src1 ^ T1 ^ -1) & (src1 ^ T0); + env->NZF = T0; + FORCE_RET(); +} + +#define OPSUB(sub, sbc, res, T0, T1) \ + \ +void OPPROTO op_ ## sub ## l_T0_T1(void) \ +{ \ + res = T0 - T1; \ +} \ + \ +void OPPROTO op_ ## sub ## l_T0_T1_cc(void) \ +{ \ + unsigned int src1; \ + src1 = T0; \ + T0 -= T1; \ + env->NZF = T0; \ + env->CF = src1 >= T1; \ + env->VF = (src1 ^ T1) & (src1 ^ T0); \ + res = T0; \ +} \ + \ +void OPPROTO op_ ## sbc ## l_T0_T1(void) \ +{ \ + res = T0 - T1 + env->CF - 1; \ +} \ + \ +void OPPROTO op_ ## sbc ## l_T0_T1_cc(void) \ +{ \ + unsigned int src1; \ + src1 = T0; \ + if (!env->CF) { \ + T0 = T0 - T1 - 1; \ + env->CF = src1 >= T1; \ + } else { \ + T0 = T0 - T1; \ + env->CF = src1 > T1; \ + } \ + env->VF = (src1 ^ T1) & (src1 ^ T0); \ + env->NZF = T0; \ + res = T0; \ + FORCE_RET(); \ +} + +OPSUB(sub, sbc, T0, T0, T1) + +OPSUB(rsb, rsc, T0, T1, T0) + +void OPPROTO op_andl_T0_T1(void) +{ + T0 &= T1; +} + +void OPPROTO op_xorl_T0_T1(void) +{ + T0 ^= T1; +} + +void OPPROTO op_orl_T0_T1(void) +{ + T0 |= T1; +} + +void OPPROTO op_bicl_T0_T1(void) +{ + T0 &= ~T1; +} + +void OPPROTO op_notl_T1(void) +{ + T1 = ~T1; +} + +void OPPROTO op_logic_T0_cc(void) +{ + env->NZF = T0; +} + +void OPPROTO op_logic_T1_cc(void) +{ + env->NZF = T1; +} + +#define EIP (env->regs[15]) + +void OPPROTO op_test_eq(void) +{ + if (env->NZF == 0) + JUMP_TB(op_test_eq, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_ne(void) +{ + if (env->NZF != 0) + JUMP_TB(op_test_ne, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_cs(void) +{ + if (env->CF != 0) + JUMP_TB(op_test_cs, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_cc(void) +{ + if (env->CF == 0) + JUMP_TB(op_test_cc, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_mi(void) +{ + if ((env->NZF & 0x80000000) != 0) + JUMP_TB(op_test_mi, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_pl(void) +{ + if ((env->NZF & 0x80000000) == 0) + JUMP_TB(op_test_pl, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_vs(void) +{ + if ((env->VF & 0x80000000) != 0) + JUMP_TB(op_test_vs, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_vc(void) +{ + if ((env->VF & 0x80000000) == 0) + JUMP_TB(op_test_vc, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_hi(void) +{ + if (env->CF != 0 && env->NZF != 0) + JUMP_TB(op_test_hi, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_ls(void) +{ + if (env->CF == 0 || env->NZF == 0) + JUMP_TB(op_test_ls, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_ge(void) +{ + if (((env->VF ^ env->NZF) & 0x80000000) == 0) + JUMP_TB(op_test_ge, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_lt(void) +{ + if (((env->VF ^ env->NZF) & 0x80000000) != 0) + JUMP_TB(op_test_lt, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_gt(void) +{ + if (env->NZF != 0 && ((env->VF ^ env->NZF) & 0x80000000) == 0) + JUMP_TB(op_test_gt, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_test_le(void) +{ + if (env->NZF == 0 || ((env->VF ^ env->NZF) & 0x80000000) != 0) + JUMP_TB(op_test_le, PARAM1, 0, PARAM2); + FORCE_RET(); +} + +void OPPROTO op_jmp(void) +{ + JUMP_TB(op_jmp, PARAM1, 1, PARAM2); +} + +void OPPROTO op_exit_tb(void) +{ + EXIT_TB(); +} + +void OPPROTO op_movl_T0_psr(void) +{ + T0 = compute_cpsr(); +} + +/* NOTE: N = 1 and Z = 1 cannot be stored currently */ +void OPPROTO op_movl_psr_T0(void) +{ + unsigned int psr; + psr = T0; + env->CF = (psr >> 29) & 1; + env->NZF = (psr & 0xc0000000) ^ 0x40000000; + env->VF = (psr << 3) & 0x80000000; + /* for user mode we do not update other state info */ +} + +void OPPROTO op_mul_T0_T1(void) +{ + T0 = T0 * T1; +} + +/* 64 bit unsigned mul */ +void OPPROTO op_mull_T0_T1(void) +{ + uint64_t res; + res = T0 * T1; + T1 = res >> 32; + T0 = res; +} + +/* 64 bit signed mul */ +void OPPROTO op_imull_T0_T1(void) +{ + uint64_t res; + res = (int32_t)T0 * (int32_t)T1; + T1 = res >> 32; + T0 = res; +} + +void OPPROTO op_addq_T0_T1(void) +{ + uint64_t res; + res = ((uint64_t)T1 << 32) | T0; + res += ((uint64_t)(env->regs[PARAM2]) << 32) | (env->regs[PARAM1]); + T1 = res >> 32; + T0 = res; +} + +void OPPROTO op_logicq_cc(void) +{ + env->NZF = (T1 & 0x80000000) | ((T0 | T1) != 0); +} + +/* memory access */ + +void OPPROTO op_ldub_T0_T1(void) +{ + T0 = ldub((void *)T1); +} + +void OPPROTO op_ldsb_T0_T1(void) +{ + T0 = ldsb((void *)T1); +} + +void OPPROTO op_lduw_T0_T1(void) +{ + T0 = lduw((void *)T1); +} + +void OPPROTO op_ldsw_T0_T1(void) +{ + T0 = ldsw((void *)T1); +} + +void OPPROTO op_ldl_T0_T1(void) +{ + T0 = ldl((void *)T1); +} + +void OPPROTO op_stb_T0_T1(void) +{ + stb((void *)T1, T0); +} + +void OPPROTO op_stw_T0_T1(void) +{ + stw((void *)T1, T0); +} + +void OPPROTO op_stl_T0_T1(void) +{ + stl((void *)T1, T0); +} + +void OPPROTO op_swpb_T0_T1(void) +{ + int tmp; + + cpu_lock(); + tmp = ldub((void *)T1); + stb((void *)T1, T0); + T0 = tmp; + cpu_unlock(); +} + +void OPPROTO op_swpl_T0_T1(void) +{ + int tmp; + + cpu_lock(); + tmp = ldl((void *)T1); + stl((void *)T1, T0); + T0 = tmp; + cpu_unlock(); +} + +/* shifts */ + +/* T1 based */ +void OPPROTO op_shll_T1_im(void) +{ + T1 = T1 << PARAM1; +} + +void OPPROTO op_shrl_T1_im(void) +{ + T1 = (uint32_t)T1 >> PARAM1; +} + +void OPPROTO op_sarl_T1_im(void) +{ + T1 = (int32_t)T1 >> PARAM1; +} + +void OPPROTO op_rorl_T1_im(void) +{ + int shift; + shift = PARAM1; + T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift)); +} + +/* T1 based, set C flag */ +void OPPROTO op_shll_T1_im_cc(void) +{ + env->CF = (T1 >> (32 - PARAM1)) & 1; + T1 = T1 << PARAM1; +} + +void OPPROTO op_shrl_T1_im_cc(void) +{ + env->CF = (T1 >> (PARAM1 - 1)) & 1; + T1 = (uint32_t)T1 >> PARAM1; +} + +void OPPROTO op_sarl_T1_im_cc(void) +{ + env->CF = (T1 >> (PARAM1 - 1)) & 1; + T1 = (int32_t)T1 >> PARAM1; +} + +void OPPROTO op_rorl_T1_im_cc(void) +{ + int shift; + shift = PARAM1; + env->CF = (T1 >> (shift - 1)) & 1; + T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift)); +} + +/* T2 based */ +void OPPROTO op_shll_T2_im(void) +{ + T2 = T2 << PARAM1; +} + +void OPPROTO op_shrl_T2_im(void) +{ + T2 = (uint32_t)T2 >> PARAM1; +} + +void OPPROTO op_sarl_T2_im(void) +{ + T2 = (int32_t)T2 >> PARAM1; +} + +void OPPROTO op_rorl_T2_im(void) +{ + int shift; + shift = PARAM1; + T2 = ((uint32_t)T2 >> shift) | (T2 << (32 - shift)); +} + +/* T1 based, use T0 as shift count */ + +void OPPROTO op_shll_T1_T0(void) +{ + int shift; + shift = T0 & 0xff; + if (shift >= 32) + T1 = 0; + else + T1 = T1 << shift; + FORCE_RET(); +} + +void OPPROTO op_shrl_T1_T0(void) +{ + int shift; + shift = T0 & 0xff; + if (shift >= 32) + T1 = 0; + else + T1 = (uint32_t)T1 >> shift; + FORCE_RET(); +} + +void OPPROTO op_sarl_T1_T0(void) +{ + int shift; + shift = T0 & 0xff; + if (shift >= 32) + shift = 31; + T1 = (int32_t)T1 >> shift; +} + +void OPPROTO op_rorl_T1_T0(void) +{ + int shift; + shift = T0 & 0x1f; + if (shift) { + T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift)); + } + FORCE_RET(); +} + +/* T1 based, use T0 as shift count and compute CF */ + +void OPPROTO op_shll_T1_T0_cc(void) +{ + int shift; + shift = T0 & 0xff; + if (shift >= 32) { + if (shift == 32) + env->CF = T1 & 1; + else + env->CF = 0; + T1 = 0; + } else if (shift != 0) { + env->CF = (T1 >> (32 - shift)) & 1; + T1 = T1 << shift; + } + FORCE_RET(); +} + +void OPPROTO op_shrl_T1_T0_cc(void) +{ + int shift; + shift = T0 & 0xff; + if (shift >= 32) { + if (shift == 32) + env->CF = (T1 >> 31) & 1; + else + env->CF = 0; + T1 = 0; + } else if (shift != 0) { + env->CF = (T1 >> (shift - 1)) & 1; + T1 = (uint32_t)T1 >> shift; + } + FORCE_RET(); +} + +void OPPROTO op_sarl_T1_T0_cc(void) +{ + int shift; + shift = T0 & 0xff; + if (shift >= 32) { + env->CF = (T1 >> 31) & 1; + T1 = (int32_t)T1 >> 31; + } else { + env->CF = (T1 >> (shift - 1)) & 1; + T1 = (int32_t)T1 >> shift; + } + FORCE_RET(); +} + +void OPPROTO op_rorl_T1_T0_cc(void) +{ + int shift1, shift; + shift1 = T0 & 0xff; + shift = shift1 & 0x1f; + if (shift == 0) { + if (shift1 != 0) + env->CF = (T1 >> 31) & 1; + } else { + env->CF = (T1 >> (shift - 1)) & 1; + T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift)); + } + FORCE_RET(); +} + +/* exceptions */ + +void OPPROTO op_swi(void) +{ + env->exception_index = EXCP_SWI; + cpu_loop_exit(); +} + +void OPPROTO op_undef_insn(void) +{ + env->exception_index = EXCP_UDEF; + cpu_loop_exit(); +} + +/* thread support */ + +spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; + +void cpu_lock(void) +{ + spin_lock(&global_cpu_lock); +} + +void cpu_unlock(void) +{ + spin_unlock(&global_cpu_lock); +} + diff --git a/target-arm/op_template.h b/target-arm/op_template.h new file mode 100644 index 0000000000..00e9d51fef --- /dev/null +++ b/target-arm/op_template.h @@ -0,0 +1,48 @@ +/* + * ARM micro operations (templates for various register related + * operations) + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 + */ + +void OPPROTO glue(op_movl_T0_, REGNAME)(void) +{ + T0 = REG; +} + +void OPPROTO glue(op_movl_T1_, REGNAME)(void) +{ + T1 = REG; +} + +void OPPROTO glue(op_movl_T2_, REGNAME)(void) +{ + T2 = REG; +} + +void OPPROTO glue(glue(op_movl_, REGNAME), _T0)(void) +{ + REG = T0; +} + +void OPPROTO glue(glue(op_movl_, REGNAME), _T1)(void) +{ + REG = T1; +} + +#undef REG +#undef REGNAME diff --git a/target-arm/translate.c b/target-arm/translate.c new file mode 100644 index 0000000000..bffeefa4fb --- /dev/null +++ b/target-arm/translate.c @@ -0,0 +1,903 @@ +/* + * ARM translation + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" + +/* internal defines */ +typedef struct DisasContext { + uint8_t *pc; + int is_jmp; + struct TranslationBlock *tb; +} DisasContext; + +#define DISAS_JUMP_NEXT 4 + +/* XXX: move that elsewhere */ +static 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" + +typedef void (GenOpFunc)(void); +typedef void (GenOpFunc1)(long); +typedef void (GenOpFunc2)(long, long); +typedef void (GenOpFunc3)(long, long, long); + +static GenOpFunc2 *gen_test_cc[14] = { + gen_op_test_eq, + gen_op_test_ne, + gen_op_test_cs, + gen_op_test_cc, + gen_op_test_mi, + gen_op_test_pl, + gen_op_test_vs, + gen_op_test_vc, + gen_op_test_hi, + gen_op_test_ls, + gen_op_test_ge, + gen_op_test_lt, + gen_op_test_gt, + gen_op_test_le, +}; + +const uint8_t table_logic_cc[16] = { + 1, /* and */ + 1, /* xor */ + 0, /* sub */ + 0, /* rsb */ + 0, /* add */ + 0, /* adc */ + 0, /* sbc */ + 0, /* rsc */ + 1, /* andl */ + 1, /* xorl */ + 0, /* cmp */ + 0, /* cmn */ + 1, /* orr */ + 1, /* mov */ + 1, /* bic */ + 1, /* mvn */ +}; + +static GenOpFunc1 *gen_shift_T1_im[4] = { + gen_op_shll_T1_im, + gen_op_shrl_T1_im, + gen_op_sarl_T1_im, + gen_op_rorl_T1_im, +}; + +static GenOpFunc1 *gen_shift_T2_im[4] = { + gen_op_shll_T2_im, + gen_op_shrl_T2_im, + gen_op_sarl_T2_im, + gen_op_rorl_T2_im, +}; + +static GenOpFunc1 *gen_shift_T1_im_cc[4] = { + gen_op_shll_T1_im_cc, + gen_op_shrl_T1_im_cc, + gen_op_sarl_T1_im_cc, + gen_op_rorl_T1_im_cc, +}; + +static GenOpFunc *gen_shift_T1_T0[4] = { + gen_op_shll_T1_T0, + gen_op_shrl_T1_T0, + gen_op_sarl_T1_T0, + gen_op_rorl_T1_T0, +}; + +static GenOpFunc *gen_shift_T1_T0_cc[4] = { + gen_op_shll_T1_T0_cc, + gen_op_shrl_T1_T0_cc, + gen_op_sarl_T1_T0_cc, + gen_op_rorl_T1_T0_cc, +}; + +static GenOpFunc *gen_op_movl_TN_reg[3][16] = { + { + gen_op_movl_T0_r0, + gen_op_movl_T0_r1, + gen_op_movl_T0_r2, + gen_op_movl_T0_r3, + gen_op_movl_T0_r4, + gen_op_movl_T0_r5, + gen_op_movl_T0_r6, + gen_op_movl_T0_r7, + gen_op_movl_T0_r8, + gen_op_movl_T0_r9, + gen_op_movl_T0_r10, + gen_op_movl_T0_r11, + gen_op_movl_T0_r12, + gen_op_movl_T0_r13, + gen_op_movl_T0_r14, + gen_op_movl_T0_r15, + }, + { + gen_op_movl_T1_r0, + gen_op_movl_T1_r1, + gen_op_movl_T1_r2, + gen_op_movl_T1_r3, + gen_op_movl_T1_r4, + gen_op_movl_T1_r5, + gen_op_movl_T1_r6, + gen_op_movl_T1_r7, + gen_op_movl_T1_r8, + gen_op_movl_T1_r9, + gen_op_movl_T1_r10, + gen_op_movl_T1_r11, + gen_op_movl_T1_r12, + gen_op_movl_T1_r13, + gen_op_movl_T1_r14, + gen_op_movl_T1_r15, + }, + { + gen_op_movl_T2_r0, + gen_op_movl_T2_r1, + gen_op_movl_T2_r2, + gen_op_movl_T2_r3, + gen_op_movl_T2_r4, + gen_op_movl_T2_r5, + gen_op_movl_T2_r6, + gen_op_movl_T2_r7, + gen_op_movl_T2_r8, + gen_op_movl_T2_r9, + gen_op_movl_T2_r10, + gen_op_movl_T2_r11, + gen_op_movl_T2_r12, + gen_op_movl_T2_r13, + gen_op_movl_T2_r14, + gen_op_movl_T2_r15, + }, +}; + +static GenOpFunc *gen_op_movl_reg_TN[2][16] = { + { + gen_op_movl_r0_T0, + gen_op_movl_r1_T0, + gen_op_movl_r2_T0, + gen_op_movl_r3_T0, + gen_op_movl_r4_T0, + gen_op_movl_r5_T0, + gen_op_movl_r6_T0, + gen_op_movl_r7_T0, + gen_op_movl_r8_T0, + gen_op_movl_r9_T0, + gen_op_movl_r10_T0, + gen_op_movl_r11_T0, + gen_op_movl_r12_T0, + gen_op_movl_r13_T0, + gen_op_movl_r14_T0, + gen_op_movl_r15_T0, + }, + { + gen_op_movl_r0_T1, + gen_op_movl_r1_T1, + gen_op_movl_r2_T1, + gen_op_movl_r3_T1, + gen_op_movl_r4_T1, + gen_op_movl_r5_T1, + gen_op_movl_r6_T1, + gen_op_movl_r7_T1, + gen_op_movl_r8_T1, + gen_op_movl_r9_T1, + gen_op_movl_r10_T1, + gen_op_movl_r11_T1, + gen_op_movl_r12_T1, + gen_op_movl_r13_T1, + gen_op_movl_r14_T1, + gen_op_movl_r15_T1, + }, +}; + +static GenOpFunc1 *gen_op_movl_TN_im[3] = { + gen_op_movl_T0_im, + gen_op_movl_T1_im, + gen_op_movl_T2_im, +}; + +static inline void gen_movl_TN_reg(DisasContext *s, int reg, int t) +{ + int val; + + if (reg == 15) { + /* normaly, since we updated PC, we need only to add 4 */ + val = (long)s->pc + 4; + gen_op_movl_TN_im[t](val); + } else { + gen_op_movl_TN_reg[t][reg](); + } +} + +static inline void gen_movl_T0_reg(DisasContext *s, int reg) +{ + gen_movl_TN_reg(s, reg, 0); +} + +static inline void gen_movl_T1_reg(DisasContext *s, int reg) +{ + gen_movl_TN_reg(s, reg, 1); +} + +static inline void gen_movl_T2_reg(DisasContext *s, int reg) +{ + gen_movl_TN_reg(s, reg, 2); +} + +static inline void gen_movl_reg_TN(DisasContext *s, int reg, int t) +{ + gen_op_movl_reg_TN[t][reg](); + if (reg == 15) { + s->is_jmp = DISAS_JUMP; + } +} + +static inline void gen_movl_reg_T0(DisasContext *s, int reg) +{ + gen_movl_reg_TN(s, reg, 0); +} + +static inline void gen_movl_reg_T1(DisasContext *s, int reg) +{ + gen_movl_reg_TN(s, reg, 1); +} + +static inline void gen_add_data_offset(DisasContext *s, unsigned int insn) +{ + int val, rm, shift; + + if (!(insn & (1 << 25))) { + /* immediate */ + val = insn & 0xfff; + if (!(insn & (1 << 23))) + val = -val; + gen_op_addl_T1_im(val); + } else { + /* shift/register */ + rm = (insn) & 0xf; + shift = (insn >> 7) & 0x1f; + gen_movl_T2_reg(s, rm); + if (shift != 0) { + gen_shift_T2_im[(insn >> 5) & 3](shift); + } + if (!(insn & (1 << 23))) + gen_op_subl_T1_T2(); + else + gen_op_addl_T1_T2(); + } +} + +static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn) +{ + int val, rm; + + if (insn & (1 << 22)) { + /* immediate */ + val = (insn & 0xf) | ((insn >> 4) & 0xf0); + if (!(insn & (1 << 23))) + val = -val; + gen_op_addl_T1_im(val); + } else { + /* register */ + rm = (insn) & 0xf; + gen_movl_T2_reg(s, rm); + if (!(insn & (1 << 23))) + gen_op_subl_T1_T2(); + else + gen_op_addl_T1_T2(); + } +} + +static void disas_arm_insn(DisasContext *s) +{ + unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh; + + insn = ldl(s->pc); + s->pc += 4; + + cond = insn >> 28; + if (cond == 0xf) + goto illegal_op; + if (cond != 0xe) { + /* if not always execute, we generate a conditional jump to + next instruction */ + gen_test_cc[cond ^ 1]((long)s->tb, (long)s->pc); + s->is_jmp = DISAS_JUMP_NEXT; + } + if (((insn & 0x0e000000) == 0 && + (insn & 0x00000090) != 0x90) || + ((insn & 0x0e000000) == (1 << 25))) { + int set_cc, logic_cc, shiftop; + + op1 = (insn >> 21) & 0xf; + set_cc = (insn >> 20) & 1; + logic_cc = table_logic_cc[op1] & set_cc; + + /* data processing instruction */ + if (insn & (1 << 25)) { + /* immediate operand */ + val = insn & 0xff; + shift = ((insn >> 8) & 0xf) * 2; + if (shift) + val = (val >> shift) | (val << (32 - shift)); + gen_op_movl_T1_im(val); + /* XXX: is CF modified ? */ + } else { + /* register */ + rm = (insn) & 0xf; + gen_movl_T1_reg(s, rm); + shiftop = (insn >> 5) & 3; + if (!(insn & (1 << 4))) { + shift = (insn >> 7) & 0x1f; + if (shift != 0) { + if (logic_cc) { + gen_shift_T1_im_cc[shiftop](shift); + } else { + gen_shift_T1_im[shiftop](shift); + } + } + } else { + rs = (insn >> 8) & 0xf; + gen_movl_T0_reg(s, rs); + if (logic_cc) { + gen_shift_T1_T0_cc[shiftop](); + } else { + gen_shift_T1_T0[shiftop](); + } + } + } + if (op1 != 0x0f && op1 != 0x0d) { + rn = (insn >> 16) & 0xf; + gen_movl_T0_reg(s, rn); + } + rd = (insn >> 12) & 0xf; + switch(op1) { + case 0x00: + gen_op_andl_T0_T1(); + gen_movl_reg_T0(s, rd); + if (logic_cc) + gen_op_logic_T0_cc(); + break; + case 0x01: + gen_op_xorl_T0_T1(); + gen_movl_reg_T0(s, rd); + if (logic_cc) + gen_op_logic_T0_cc(); + break; + case 0x02: + if (set_cc) + gen_op_subl_T0_T1_cc(); + else + gen_op_subl_T0_T1(); + gen_movl_reg_T0(s, rd); + break; + case 0x03: + if (set_cc) + gen_op_rsbl_T0_T1_cc(); + else + gen_op_rsbl_T0_T1(); + gen_movl_reg_T0(s, rd); + break; + case 0x04: + if (set_cc) + gen_op_addl_T0_T1_cc(); + else + gen_op_addl_T0_T1(); + gen_movl_reg_T0(s, rd); + break; + case 0x05: + if (set_cc) + gen_op_adcl_T0_T1_cc(); + else + gen_op_adcl_T0_T1(); + gen_movl_reg_T0(s, rd); + break; + case 0x06: + if (set_cc) + gen_op_sbcl_T0_T1_cc(); + else + gen_op_sbcl_T0_T1(); + gen_movl_reg_T0(s, rd); + break; + case 0x07: + if (set_cc) + gen_op_rscl_T0_T1_cc(); + else + gen_op_rscl_T0_T1(); + gen_movl_reg_T0(s, rd); + break; + case 0x08: + if (set_cc) { + gen_op_andl_T0_T1(); + gen_op_logic_T0_cc(); + } + break; + case 0x09: + if (set_cc) { + gen_op_xorl_T0_T1(); + gen_op_logic_T0_cc(); + } + break; + case 0x0a: + if (set_cc) { + gen_op_subl_T0_T1_cc(); + } + break; + case 0x0b: + if (set_cc) { + gen_op_addl_T0_T1_cc(); + } + break; + case 0x0c: + gen_op_orl_T0_T1(); + gen_movl_reg_T0(s, rd); + if (logic_cc) + gen_op_logic_T0_cc(); + break; + case 0x0d: + gen_movl_reg_T1(s, rd); + if (logic_cc) + gen_op_logic_T1_cc(); + break; + case 0x0e: + gen_op_bicl_T0_T1(); + gen_movl_reg_T0(s, rd); + if (logic_cc) + gen_op_logic_T0_cc(); + break; + default: + case 0x0f: + gen_op_notl_T1(); + gen_movl_reg_T1(s, rd); + if (logic_cc) + gen_op_logic_T1_cc(); + break; + } + } else { + /* other instructions */ + op1 = (insn >> 24) & 0xf; + switch(op1) { + case 0x0: + case 0x1: + sh = (insn >> 5) & 3; + if (sh == 0) { + if (op1 == 0x0) { + rd = (insn >> 16) & 0xf; + rn = (insn >> 12) & 0xf; + rs = (insn >> 8) & 0xf; + rm = (insn) & 0xf; + if (!(insn & (1 << 23))) { + /* 32 bit mul */ + gen_movl_T0_reg(s, rs); + gen_movl_T1_reg(s, rm); + gen_op_mul_T0_T1(); + if (insn & (1 << 21)) { + gen_movl_T1_reg(s, rn); + gen_op_addl_T0_T1(); + } + if (insn & (1 << 20)) + gen_op_logic_T0_cc(); + gen_movl_reg_T0(s, rd); + } else { + /* 64 bit mul */ + gen_movl_T0_reg(s, rs); + gen_movl_T1_reg(s, rm); + if (insn & (1 << 22)) + gen_op_mull_T0_T1(); + else + gen_op_imull_T0_T1(); + if (insn & (1 << 21)) + gen_op_addq_T0_T1(rn, rd); + if (insn & (1 << 20)) + gen_op_logicq_cc(); + gen_movl_reg_T0(s, rn); + gen_movl_reg_T1(s, rd); + } + } else { + /* SWP instruction */ + rn = (insn >> 16) & 0xf; + rd = (insn >> 12) & 0xf; + rm = (insn) & 0xf; + + gen_movl_T0_reg(s, rm); + gen_movl_T1_reg(s, rn); + if (insn & (1 << 22)) { + gen_op_swpb_T0_T1(); + } else { + gen_op_swpl_T0_T1(); + } + gen_movl_reg_T0(s, rd); + } + } else { + /* load/store half word */ + rn = (insn >> 16) & 0xf; + rd = (insn >> 12) & 0xf; + gen_movl_T1_reg(s, rn); + if (insn & (1 << 25)) + gen_add_datah_offset(s, insn); + if (insn & (1 << 20)) { + /* load */ + switch(sh) { + case 1: + gen_op_lduw_T0_T1(); + break; + case 2: + gen_op_ldsb_T0_T1(); + break; + default: + case 3: + gen_op_ldsw_T0_T1(); + break; + } + } else { + /* store */ + gen_op_stw_T0_T1(); + } + if (!(insn & (1 << 24))) { + gen_add_datah_offset(s, insn); + gen_movl_reg_T1(s, rn); + } else if (insn & (1 << 21)) { + gen_movl_reg_T1(s, rn); + } + } + break; + case 0x4: + case 0x5: + case 0x6: + case 0x7: + /* load/store byte/word */ + rn = (insn >> 16) & 0xf; + rd = (insn >> 12) & 0xf; + gen_movl_T1_reg(s, rn); + if (insn & (1 << 24)) + gen_add_data_offset(s, insn); + if (insn & (1 << 20)) { + /* load */ + if (insn & (1 << 22)) + gen_op_ldub_T0_T1(); + else + gen_op_ldl_T0_T1(); + gen_movl_reg_T0(s, rd); + } else { + /* store */ + gen_movl_T0_reg(s, rd); + if (insn & (1 << 22)) + gen_op_stb_T0_T1(); + else + gen_op_stl_T0_T1(); + } + if (!(insn & (1 << 24))) { + gen_add_data_offset(s, insn); + gen_movl_reg_T1(s, rn); + } else if (insn & (1 << 21)) + gen_movl_reg_T1(s, rn); { + } + break; + case 0x08: + case 0x09: + { + int j, n; + /* load/store multiple words */ + /* XXX: store correct base if write back */ + if (insn & (1 << 22)) + goto illegal_op; /* only usable in supervisor mode */ + rn = (insn >> 16) & 0xf; + gen_movl_T1_reg(s, rn); + + /* compute total size */ + n = 0; + for(i=0;i<16;i++) { + if (insn & (1 << i)) + n++; + } + /* XXX: test invalid n == 0 case ? */ + if (insn & (1 << 23)) { + if (insn & (1 << 24)) { + /* pre increment */ + gen_op_addl_T1_im(4); + } else { + /* post increment */ + } + } else { + if (insn & (1 << 24)) { + /* pre decrement */ + gen_op_addl_T1_im(-(n * 4)); + } else { + /* post decrement */ + if (n != 1) + gen_op_addl_T1_im(-((n - 1) * 4)); + } + } + j = 0; + for(i=0;i<16;i++) { + if (insn & (1 << i)) { + if (insn & (1 << 20)) { + /* load */ + gen_op_ldl_T0_T1(); + gen_movl_reg_T0(s, i); + } else { + /* store */ + if (i == 15) { + /* special case: r15 = PC + 12 */ + val = (long)s->pc + 8; + gen_op_movl_TN_im[0](val); + } else { + gen_movl_T0_reg(s, i); + } + gen_op_stl_T0_T1(); + } + j++; + /* no need to add after the last transfer */ + if (j != n) + gen_op_addl_T1_im(4); + } + } + if (insn & (1 << 21)) { + /* write back */ + if (insn & (1 << 23)) { + if (insn & (1 << 24)) { + /* pre increment */ + } else { + /* post increment */ + gen_op_addl_T1_im(4); + } + } else { + if (insn & (1 << 24)) { + /* pre decrement */ + if (n != 1) + gen_op_addl_T1_im(-((n - 1) * 4)); + } else { + /* post decrement */ + gen_op_addl_T1_im(-(n * 4)); + } + } + gen_movl_reg_T1(s, rn); + } + } + break; + case 0xa: + case 0xb: + { + int offset; + + /* branch (and link) */ + val = (int)s->pc; + if (insn & (1 << 24)) { + gen_op_movl_T0_im(val); + gen_op_movl_reg_TN[0][14](); + } + offset = (((int)insn << 8) >> 8); + val += (offset << 2) + 4; + gen_op_jmp((long)s->tb, val); + s->is_jmp = DISAS_TB_JUMP; + } + break; + case 0xf: + /* swi */ + gen_op_movl_T0_im((long)s->pc); + gen_op_movl_reg_TN[0][15](); + gen_op_swi(); + s->is_jmp = DISAS_JUMP; + break; + case 0xc: + case 0xd: + rd = (insn >> 12) & 0x7; + rn = (insn >> 16) & 0xf; + gen_movl_T1_reg(s, rn); + val = (insn) & 0xff; + if (!(insn & (1 << 23))) + val = -val; + switch((insn >> 8) & 0xf) { + case 0x1: + /* load/store */ + if ((insn & (1 << 24))) + gen_op_addl_T1_im(val); + /* XXX: do it */ + if (!(insn & (1 << 24))) + gen_op_addl_T1_im(val); + if (insn & (1 << 21)) + gen_movl_reg_T1(s, rn); + break; + case 0x2: + { + int n, i; + /* load store multiple */ + if ((insn & (1 << 24))) + gen_op_addl_T1_im(val); + switch(insn & 0x00408000) { + case 0x00008000: n = 1; break; + case 0x00400000: n = 2; break; + case 0x00408000: n = 3; break; + default: n = 4; break; + } + for(i = 0;i < n; i++) { + /* XXX: do it */ + } + if (!(insn & (1 << 24))) + gen_op_addl_T1_im(val); + if (insn & (1 << 21)) + gen_movl_reg_T1(s, rn); + } + break; + default: + goto illegal_op; + } + break; + case 0x0e: + /* float ops */ + /* XXX: do it */ + switch((insn >> 20) & 0xf) { + case 0x2: /* wfs */ + break; + case 0x3: /* rfs */ + break; + case 0x4: /* wfc */ + break; + case 0x5: /* rfc */ + break; + default: + goto illegal_op; + } + break; + default: + illegal_op: + gen_op_movl_T0_im((long)s->pc - 4); + gen_op_movl_reg_TN[0][15](); + gen_op_undef_insn(); + s->is_jmp = DISAS_JUMP; + break; + } + } +} + +/* generate intermediate code in gen_opc_buf and gen_opparam_buf for + basic block 'tb'. If search_pc is TRUE, also generate PC + information for each intermediate instruction. */ +static inline int gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, + int search_pc) +{ + DisasContext dc1, *dc = &dc1; + uint16_t *gen_opc_end; + int j, lj; + uint8_t *pc_start; + + /* generate intermediate code */ + pc_start = (uint8_t *)tb->pc; + + dc->tb = tb; + + gen_opc_ptr = gen_opc_buf; + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + gen_opparam_ptr = gen_opparam_buf; + + dc->is_jmp = DISAS_NEXT; + dc->pc = pc_start; + lj = -1; + do { + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) + gen_opc_instr_start[lj++] = 0; + } + gen_opc_pc[lj] = (uint32_t)dc->pc; + gen_opc_instr_start[lj] = 1; + } + disas_arm_insn(dc); + } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end && + (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32)); + switch(dc->is_jmp) { + case DISAS_JUMP_NEXT: + case DISAS_NEXT: + gen_op_jmp((long)dc->tb, (long)dc->pc); + break; + default: + case DISAS_JUMP: + /* indicate that the hash table must be used to find the next TB */ + gen_op_movl_T0_0(); + gen_op_exit_tb(); + break; + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + } + *gen_opc_ptr = INDEX_op_end; + +#ifdef DEBUG_DISAS + if (loglevel) { + fprintf(logfile, "----------------\n"); + fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start)); + disas(logfile, pc_start, dc->pc - pc_start, 0, 0); + fprintf(logfile, "\n"); + + fprintf(logfile, "OP:\n"); + dump_ops(gen_opc_buf, gen_opparam_buf); + fprintf(logfile, "\n"); + } +#endif + if (!search_pc) + tb->size = dc->pc - pc_start; + return 0; +} + +int gen_intermediate_code(CPUState *env, TranslationBlock *tb) +{ + return gen_intermediate_code_internal(env, tb, 0); +} + +int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) +{ + return gen_intermediate_code_internal(env, tb, 1); +} + +CPUARMState *cpu_arm_init(void) +{ + CPUARMState *env; + + cpu_exec_init(); + + env = malloc(sizeof(CPUARMState)); + if (!env) + return NULL; + memset(env, 0, sizeof(CPUARMState)); + return env; +} + +void cpu_arm_close(CPUARMState *env) +{ + free(env); +} + +void cpu_arm_dump_state(CPUARMState *env, FILE *f, int flags) +{ + int i; + + for(i=0;i<16;i++) { + fprintf(f, "R%02d=%08x", i, env->regs[i]); + if ((i % 4) == 3) + fprintf(f, "\n"); + else + fprintf(f, " "); + } + fprintf(f, "PSR=%08x %c%c%c%c\n", + env->cpsr, + env->cpsr & (1 << 31) ? 'N' : '-', + env->cpsr & (1 << 30) ? 'Z' : '-', + env->cpsr & (1 << 29) ? 'C' : '-', + env->cpsr & (1 << 28) ? 'V' : '-'); +} |