ARM emulation support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@232 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2003-06-15 19:42:24 +00:00
parent 394411ac74
commit 5898e81684
6 changed files with 1586 additions and 0 deletions

64
cpu-arm.h Normal file
View File

@ -0,0 +1,64 @@
/*
* 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 "config.h"
#include <setjmp.h>
#define EXCP_UDEF 1 /* undefined instruction */
#define EXCP_SWI 2 /* software interrupt */
#define EXCP_INTERRUPT 256 /* async interruption */
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;
/* user data */
void *opaque;
} CPUARMState;
CPUARMState *cpu_arm_init(void);
int cpu_arm_exec(CPUARMState *s);
void cpu_arm_interrupt(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

32
exec-arm.h Normal file
View File

@ -0,0 +1,32 @@
/*
* 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-arm.h"
#include "exec.h"
void cpu_lock(void);
void cpu_unlock(void);
void cpu_loop_exit(void);

48
op-arm-template.h Normal file
View File

@ -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

665
op-arm.c Normal file
View File

@ -0,0 +1,665 @@
/*
* 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-arm.h"
#define REGNAME r0
#define REG (env->regs[0])
#include "op-arm-template.h"
#define REGNAME r1
#define REG (env->regs[1])
#include "op-arm-template.h"
#define REGNAME r2
#define REG (env->regs[2])
#include "op-arm-template.h"
#define REGNAME r3
#define REG (env->regs[3])
#include "op-arm-template.h"
#define REGNAME r4
#define REG (env->regs[4])
#include "op-arm-template.h"
#define REGNAME r5
#define REG (env->regs[5])
#include "op-arm-template.h"
#define REGNAME r6
#define REG (env->regs[6])
#include "op-arm-template.h"
#define REGNAME r7
#define REG (env->regs[7])
#include "op-arm-template.h"
#define REGNAME r8
#define REG (env->regs[8])
#include "op-arm-template.h"
#define REGNAME r9
#define REG (env->regs[9])
#include "op-arm-template.h"
#define REGNAME r10
#define REG (env->regs[10])
#include "op-arm-template.h"
#define REGNAME r11
#define REG (env->regs[11])
#include "op-arm-template.h"
#define REGNAME r12
#define REG (env->regs[12])
#include "op-arm-template.h"
#define REGNAME r13
#define REG (env->regs[13])
#include "op-arm-template.h"
#define REGNAME r14
#define REG (env->regs[14])
#include "op-arm-template.h"
#define REGNAME r15
#define REG (env->regs[15])
#include "op-arm-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, T0, T1) \
\
void OPPROTO op_ ## sub ## l_T0_T1(void) \
{ \
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); \
} \
\
void OPPROTO op_ ## sbc ## l_T0_T1(void) \
{ \
T0 = 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; \
T0 += T1; \
env->CF = src1 < T1; \
} else { \
T0 = T0 - T1; \
env->CF = src1 <= T1; \
} \
env->VF = (src1 ^ T1) & (src1 ^ T0); \
env->NZF = T0; \
FORCE_RET(); \
}
OPSUB(sub, sbc, T0, T1)
OPSUB(rsb, rsc, 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_cc(void)
{
env->NZF = T0;
}
#define EIP (env->regs[15])
void OPPROTO op_test_eq(void)
{
if (env->NZF == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_ne(void)
{
if (env->NZF != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_cs(void)
{
if (env->CF != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_cc(void)
{
if (env->CF == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_mi(void)
{
if ((env->NZF & 0x80000000) != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_pl(void)
{
if ((env->NZF & 0x80000000) == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_vs(void)
{
if ((env->VF & 0x80000000) != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_vc(void)
{
if ((env->VF & 0x80000000) == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_hi(void)
{
if (env->CF != 0 && env->NZF != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_ls(void)
{
if (env->CF == 0 || env->NZF == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_ge(void)
{
if (((env->VF ^ env->NZF) & 0x80000000) == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_lt(void)
{
if (((env->VF ^ env->NZF) & 0x80000000) != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_gt(void)
{
if (env->NZF != 0 && ((env->VF ^ env->NZF) & 0x80000000) == 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_test_le(void)
{
if (env->NZF == 0 || ((env->VF ^ env->NZF) & 0x80000000) != 0)
JUMP_TB(PARAM1, 0, PARAM2);
FORCE_RET();
}
void OPPROTO op_jmp(void)
{
JUMP_TB(PARAM1, 1, PARAM2);
}
void OPPROTO op_movl_T0_psr(void)
{
int ZF;
ZF = (env->NZF == 0);
T0 = env->cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
(env->CF << 29) | ((env->VF & 0x80000000) >> 3);
}
/* 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);
}

27
syscall-arm.h Normal file
View File

@ -0,0 +1,27 @@
/* this struct defines the way the registers are stored on the
stack during a system call. */
struct target_pt_regs {
target_long uregs[18];
};
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]

750
translate-arm.c Normal file
View File

@ -0,0 +1,750 @@
/*
* 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-arm.h"
#include "exec.h"
#include "disas.h"
/* internal defines */
typedef struct DisasContext {
uint8_t *pc;
int is_jmp;
struct TranslationBlock *tb;
} DisasContext;
/* 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-arm.h"
#undef DEF
NB_OPS,
};
#include "gen-op-arm.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 = 1;
}
if ((insn & 0x0c000000) == 0 &&
(insn & 0x00000090) != 0x90) {
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 >> 16) & 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);
break;
case 0x01:
gen_op_xorl_T0_T1();
gen_movl_reg_T0(s, rd);
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();
}
break;
case 0x09:
if (set_cc) {
gen_op_xorl_T0_T1();
}
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);
break;
case 0x0d:
gen_movl_reg_T1(s, rd);
break;
case 0x0e:
gen_op_bicl_T0_T1();
gen_movl_reg_T0(s, rd);
break;
default:
case 0x0f:
gen_op_notl_T1();
gen_movl_reg_T1(s, rd);
break;
}
if (logic_cc)
gen_op_logic_cc();
} 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_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);
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);
if (insn & (1 << 21))
gen_movl_reg_T1(s, rn);
break;
case 0x08:
case 0x09:
/* load/store multiple words */
if (insn & (1 << 22))
goto illegal_op; /* only usable in supervisor mode */
rn = (insn >> 16) & 0xf;
gen_movl_T1_reg(s, rn);
val = 4;
if (!(insn & (1 << 23)))
val = -val;
for(i=0;i<16;i++) {
if (insn & (1 << i)) {
if (insn & (1 << 24))
gen_op_addl_T1_im(val);
if (insn & (1 << 20)) {
/* load */
gen_op_ldl_T0_T1();
gen_movl_reg_T0(s, i);
} else {
/* store */
gen_movl_T0_reg(s, i);
gen_op_stl_T0_T1();
}
if (!(insn & (1 << 24)))
gen_op_addl_T1_im(val);
}
}
if (insn & (1 << 21))
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;
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. */
int gen_intermediate_code(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));
/* we must store the eflags state if it is not already done */
if (dc->is_jmp != DISAS_TB_JUMP &&
dc->is_jmp != DISAS_JUMP) {
gen_op_movl_T0_im((long)dc->pc - 4);
gen_op_movl_reg_TN[0][15]();
}
if (dc->is_jmp != DISAS_TB_JUMP) {
/* indicate that the hash table must be used to find the next TB */
gen_op_movl_T0_0();
}
*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;
}
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, "CPSR=%08x", env->cpsr);
}