blob: 38f3c3e8138fc866b9417f480870f5ed3517a0a8 [file] [log] [blame]
//
// ARM32 Application-level simulator.
//
//
// Copyright (c) 2007 FirmWorks
// Copyright 2010 Apple, Inc. All rights reserved.
// See license at end.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
typedef char s8;
typedef short s16;
typedef int s32;
typedef long long s64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
static u32 trace = 0;
//#if TRACE
#define INSTR(a) if (trace) printf("%s -- %0x N%d Z%d C%d V%d %s\n", \
a, COND, N, Z, C, V, cond ? "true" : "false"); \
if (cond == 0) break
//#else
//#define INSTR(a) if (cond == 0) break
//#endif
#define MAXMEM 0x80000
#define MEM(type, adr) *(type *)(&mem[(adr)])
u32 r[16];
#define SP r[13]
#define LR r[14]
#define PC r[15]
void regdump(u32 instruction, u32 last_pc, u8 cr)
{
printf(" r0 %08x r1 %08x r2 %08x r3 %08x\n", r[0], r[1], r[2], r[3]);
printf(" r4 %08x r5 %08x r6 %08x base %08x\n", r[4], r[5], r[6], r[7]);
printf(" r8 %08x up %08x tos %08x rp %08x\n", r[8], r[9], r[10], r[11]);
printf(" ip %08x sp %08x lr %08x pc %08x\n", r[12], r[13], r[14], r[15]);
printf("pc %08x lpc %08x i %08x ", PC - 8, last_pc, instruction);
if (cr)
putchar('\n');
}
#define UFIELD(lbit,nbits) ( (instruction << (31 - lbit)) >> (32 - nbits))
#define SFIELD(lbit,nbits) ((long)(instruction << (31 - lbit)) >> (32 - nbits))
#define COND UFIELD(31, 4)
#define OP UFIELD(27, 7)
#define S UFIELD(20, 1)
#define L UFIELD(20, 1)
#define RD r[UFIELD(15, 4)]
#define RN r[UFIELD(19, 4)]
#define RM r[UFIELD( 3, 4)]
#define RS r[UFIELD(11, 4)]
#define TYPE UFIELD( 6, 2)
#define IMM5 UFIELD(11, 5)
#define OP1 UFIELD( 4, 1)
#define OP2 UFIELD( 7, 1)
#define ROT UFIELD(11, 4)
#define IMM8 UFIELD( 7, 8)
#define IMM24 UFIELD(23,24)
#define LINK UFIELD( 5, 1)
#define MSB UFIELD(20, 5)
#define LSB UFIELD(11, 5)
#define BXTYPE UFIELD( 7, 4)
#define IMM12 UFIELD(11,12)
#define IMM16 ((UFIELD(19, 4) << 12) | IMM12)
#define IMMHL ((UFIELD(11, 4) << 4) | UFIELD( 3, 4))
#define P UFIELD(24, 1)
#define U UFIELD(23, 1)
#define W UFIELD(21, 1)
// In BTGT we want to force a PC update. Since we don't implement Thumb
// the value "1" must always be invalid for last_pc.
struct { signed int imm24:24; } sext24;
#define BTGT { PC += sext24.imm24 = (IMM24 << 2); last_pc = 1; }
int scout; // Hold the last bit shifted out
#define ROTATE(imm, rot) (((imm) >> (rot)) | ((imm) << (32-(rot))))
#define IMM32 ROTATE(IMM8, (ROT<<1))
#define TEST_SHIFT 0
#if defined(TEST_SHIFT) && TEST_SHIFT
#define EXIT(r) goto exit
#else
#define EXIT(r) return res
#endif
u32 shifter(u32 rm, u32 rs, u32 type, u32 imm, u32 imm5, u32 cin, u32 pc, u32 ir)
{
u32 res, res2;
int cnt;
#if defined(TEST_SHIFT) && TEST_SHIFT
u32 res2;
cnt = imm ? imm5 : rs;
switch (type) {
case 0: res = rm << cnt; break;
case 1: res = rm >> cnt; break;
case 2: if (cnt == 0) { res = ((s32)(rm) < 0) ? -1 : 0; } else { res = (s32)(rm) >> cnt; } break;
case 3: res = ROTATE(rm, cnt);
}
res2 = res;
#endif
if (imm && (imm5 == 0)) {
if (type == 0) {
res = rm;
scout = cin;
EXIT(res);
return res;
}
if (type == 2) {
if ((s32)rm < 0) res = -1;
else res = 0;
scout = res & 1;
EXIT(res);
return res;
}
if (type == 3) {
res = (cin << 31) | (rm >> 1);
scout = rm & 1;
EXIT(res);
return res;
}
}
if (imm) {
if (imm5 == 0) cnt = 32;
else cnt = imm5;
} else cnt = rs & 31;
if (cnt == 0) {
res = rm;
scout = cin;
EXIT(res);
return res;
}
switch (type) {
case 0: res = rm << cnt; scout = rm >> (32 - cnt); break;
case 1: res = rm >> cnt; scout = rm >> (cnt - 1); break;
case 2: res = (s32)rm >> cnt; scout = rm >> (cnt - 1); break;
case 3: res = ROTATE(rm, cnt); scout = res >> 31;
} /* switch */
scout &= 1;
EXIT(res);
#if defined(TEST_SHIFT) && TEST_SHIFT
exit:
if (res != res2) {
char *shifts[] = {"lsl", "lsr", "asr", "ror"};
printf("PC: %x: %x ", pc, ir);
if (imm) {
printf("imm = #%d", imm5);
} else {
printf("RS = %d", rs & 31);
}
printf(", RM = %x, %s #%d; res = %x, res2 = %x\n", rm, shifts[type], cnt, res, res2);
// while (1);
}
return res;
#endif
}
#define SHFT(res) res = shifter(RM, RS, TYPE, !OP1, IMM5, C, PC, instruction)
#define BF(sb, eb) ((u32)(((s32)0x80000000) >> (sb - eb))) >> (31 - sb);
union {
u32 all;
struct {
u32 res :28;
u32 Vbit:1;
u32 Zbit:1;
u32 Cbit:1;
u32 Nbit:1;
} bits;
} APSR;
#define N (APSR.bits.Nbit)
#define Z (APSR.bits.Zbit)
#define C (APSR.bits.Cbit)
#define V (APSR.bits.Vbit)
// FIXME: We're skipping the V bit for now.
/* Leave C alone. */
#define UPCC(res) \
{ \
if (S) { \
N = (res) >> 31; \
Z = (res == 0); \
} \
}
/* Factor in the shifted-out Carry. */
#define SHFT_UPCC(res) \
{ \
if (S) { \
N = (res) >> 31; \
Z = (res == 0); \
C = scout ? 1 : 0; \
} \
}
#define ADC(dest, a, b, c) \
{ \
temp = (a) + (b) + (c); \
if (S) { \
N = temp >> 31; \
Z = temp == 0; \
C = (temp < (a)) || ((c) && (temp == (a))); \
V = ((((s32)(temp)^(s32)(a)) < 0) && (((s32)(a)^(s32)(b)) >= 0)); \
} \
dest = temp; \
}
#define SBB(dest, a, b, c) \
{ \
temp = (a) - (b) - (!(c)); \
if (S) { \
N = temp >> 31; \
Z = temp == 0; \
C = !(((a) < temp) || ((!(c)) && ((a) == temp))); \
V = ((((s32)temp^(s32)(a)) < 0) && (((s32)(a)^(s32)(b)) < 0)); \
} \
dest = temp; \
}
#define UNIMP(s) \
{ \
printf("UNIMPLEMENTED '%s' op %02x s %d bxtype %02x; Source line: %d\n", s, OP, S, BXTYPE, __LINE__); \
regdump(instruction, last_pc, 1); \
return; \
}
#define EVAL_COND(cc) \
{ \
switch (cc) { \
case 0x0: cond = (Z == 1); break; \
case 0x1: cond = (Z == 0); break; \
case 0x2: cond = (C == 1); break; \
case 0x3: cond = (C == 0); break; \
case 0x4: cond = (N == 1); break; \
case 0x5: cond = (N == 0); break; \
case 0x6: cond = (V == 1); break; \
case 0x7: cond = (V == 0); break; \
case 0x8: cond = (C == 1 && Z == 0); break; \
case 0x9: cond = (C == 0 || Z == 1); break; \
case 0xa: cond = (N == V); break; \
case 0xb: cond = (N != V); break; \
case 0xc: cond = (Z == 0 && N == V); break; \
case 0xd: cond = (Z == 1 || N != V); break; \
case 0xe: cond = (1); break; \
case 0xf: cond = (0xf); break; \
} \
}
u32 instruction;
u32 last_pc;
void simhandler(int sig)
{
extern void restoremode();
psignal(sig, "forth");
regdump(instruction, last_pc, 1);
restoremode();
exit(1);
}
// alf = find(adr, len, link, origin);
u32 *
find(u8 *adr, u32 len, u32 *link, void *origin)
{
u8 *wp, *np;
u32 namelen;
while (link != origin) {
link -= 1; // Move from code field to link field
np = (u8 *)link - 1;
namelen = (*np) & 0x1f;
if (namelen == len) {
np -= namelen;
wp = adr;
while (namelen--) {
if (*np++ != *wp++) {
break;
}
}
if (namelen == -1) {
return (link);
}
}
link = (u32 *)*link;
}
return ((u32 *)0);
}
void
simulate(u8 *mem, u32 start, u32 header, u32 syscall_vec,
u32 memtop, u32 argc, u32 argv)
{
// register u32 instruction;
register u32 res;
register u32 cond;
register u32 temp;
s32 indent = 0;
u32 name;
u32 namelen;
// u32 last_pc;
signal(SIGBUS, simhandler);
signal(SIGSEGV, simhandler);
APSR.all = 0;
PC = start + 8; // Stupid ARM.
r[0] = header;
r[1] = 0; // Tell Forth it is using the simulator
r[2] = memtop;
r[3] = argc;
SP = memtop;
*((u32 *)SP) = argv;
while (1) {
instruction = MEM(u32, PC - 8);
last_pc = PC;
//#if TRACE
if (trace)
regdump(instruction, last_pc, 0);
//#endif
EVAL_COND(COND);
if (cond == 0) {
goto annul;
}
if (cond == 0xf)
UNIMP("unconditional");
switch (OP) {
case 0x00: if (OP1 == 0 || OP2 == 0) {
INSTR("and"); SHFT(res); RD = RN & res; SHFT_UPCC(RD); break;
}
switch (BXTYPE) {
case 0x9: INSTR("mul"); RN = RS * RM; UPCC(RN); break;
// P=0, U=0, bit22=0, W=0 - post-index, add offset, register, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN);
RN = RN + RM;
} else {
INSTR("strh");
MEM(u16, RN) = RD;
RN = RN + RM;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN);
RN = RN + RM;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN);
RN = RN + RM;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x01: if (OP1 == 0 || OP2 == 0) {
INSTR("eor"); SHFT(res); RD = RN ^ res; SHFT_UPCC(RD); break;
}
switch (BXTYPE) {
case 0x9: UNIMP("mla"); break;
// P=0, U=0, bit22=0, W=1 - post-index, add offset, register, writeback - UNPREDICTABLE (P=0, W=1)
case 0xb: UNIMP("ldrh"); break; // UNPREDICTABLE
case 0xd: UNIMP("ldrd"); break; // UNPREDICTABLE
case 0xf: UNIMP("ldrsh"); break; // UNPREDICTABLE
} break;
case 0x02: if (OP1 == 0 || OP2 == 0) {
INSTR("sub"); SHFT(res); SBB(RD, RN, res, 1); break;
}
switch (BXTYPE) {
// P=0, U=0, bit22=1, W=0 - post-index, add offset, immediate, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN);
RN = RN + IMMHL;
} else {
INSTR("strh");
MEM(u16, RN) = RD;
RN = RN + IMMHL;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN);
RN = RN + IMMHL;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN);
RN = RN + IMMHL;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x03: if (OP1 == 0 || OP2 == 0) {
INSTR("rsb"); SHFT(res); SBB(RD, res, RN, 1); break;
} else {
switch (BXTYPE) {
case 0x1:
case 0x3:
case 0x5:
case 0x7: INSTR("rsb"); SHFT(res); SBB(RD, res, RN, 1); break;
case 0x9: UNIMP("mls"); break;
// P=0, U=0, bit22=1, W=1 - post-index, add offset, immediate, writeback - UNPREDICTABLE (P=0, W=1)
case 0xb: UNIMP("ldrh"); break; // UNPREDICTABLE
case 0xd: UNIMP("ldrd"); break; // UNPREDICTABLE
case 0xf: UNIMP("ldrsh"); break; // UNPREDICTABLE
} break;
} break;
case 0x04: if (OP1 == 0 || OP2 == 0) {
INSTR("add"); SHFT(res); ADC(RD, RN, res, 0); break;
}
switch (BXTYPE) {
// P=0, U=1, bit22=0, W=0 - post-index, subtract offset, register, no writeback
case 0x9: INSTR("umull");
{ u64 result;
result = (u64)RM * (u64)RS;
RN = (u32)((result >> 32) & 0xffffffffLL);
RD = (u32)(result & 0xffffffffLL);
if (S) {
N = (RN & 0x80000000LL) ? 1 : 0;
Z = (result == 0);
}
}
break;
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN);
RN = RN - RM;
} else {
INSTR("strh");
MEM(u16, RN) = RD;
RN = RN - RM;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN);
RN = RN - RM;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN);
RN = RN - RM;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x05: if (OP1 == 0 || OP2 == 0) {
INSTR("adc"); SHFT(res); ADC(RD, RN, res, C); break;
}
switch (BXTYPE) {
// P=0, U=1, bit22=0, W=1 - post-index, subtract offset, register, writeback - UNPREDICTABLE (P=0, W=1)
case 0xb: UNIMP("ldrh"); break; // UNPREDICTABLE
case 0xd: UNIMP("ldrd"); break; // UNPREDICTABLE
case 0xf: UNIMP("ldrsh"); break; // UNPREDICTABLE
default: UNIMP("BXTYPE"); break;
} break;
case 0x06:
if (OP1 == 0 || OP2 == 0) {
INSTR("sbc"); SHFT(res);
// printf("sbc RN %x res %x ~res %x C %x -- ", RN, res, ~(res), C);
// ADC(RD, RN, ~(res), C);
// printf("res %x\n", RD);
// printf("sbc RN %x res %x ~res %x C %x -- ", RN, res, ~(res), C);
SBB(RD, RN, res, C);
// printf("res %x\n", RD);
break;
} else {
switch (BXTYPE) {
case 0x1:
case 0x3:
case 0x5:
case 0x7: INSTR("sbc"); SHFT(res); SBB(RD, res, RN, C); break;
case 0x9: INSTR("smull");
{ s64 result, a, b;
a = (int)RM;
b = (int)RS;
result = a * b;
RN = (u32)((result >> 32) & 0xffffffffLL);
RD = (u32)(result & 0xffffffffLL);
if (S) {
N = (result < 0);
Z = (result == 0);
}
}
break;
// P=0, U=1, bit22=1, W=0 - post-index, subtract offset, immediate, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN);
RN = RN - IMMHL;
} else {
INSTR("strh");
MEM(u16, RN) = RD;
RN = RN - IMMHL;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN);
RN = RN - IMMHL;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN);
RN = RN - IMMHL;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
} break;
case 0x07: if (OP1 == 0 || OP2 == 0) {
INSTR("rsc"); SHFT(res); SBB(RD, res, RN, C); break;
} else {
switch (BXTYPE) {
case 0x1:
case 0x3:
case 0x5:
case 0x7: INSTR("rsc"); SHFT(res); SBB(RD, res, RN, C); break;
case 0x9: UNIMP("smlal"); break;
// P=0, U=1, bit22=1, W=1 - post-index, subtract offset, immediate, writeback - UNPREDICTABLE (P=0, W=1)
case 0xb: UNIMP("ldrh"); break; // UNPREDICTABLE
case 0xd: UNIMP("ldrd"); break; // UNPREDICTABLE
case 0xf: UNIMP("ldrsh"); break; // UNPREDICTABLE
default: UNIMP("BXTYPE"); break;
} break;
} break;
case 0x08: switch (BXTYPE) {
case 0x0: INSTR("mrs"); RD = APSR.all; break;
case 0x5: UNIMP("qadd"); break;
case 0x8:
case 0xa:
case 0xc:
case 0xe: UNIMP("smlabb"); break;
// P=1, U=0, bit22=0, W=0 - offset/pre-index, subtract offset, register, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN - RM);
} else {
INSTR("strh");
MEM(u16, RN - RM) = RD;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN - RM);
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN - RM);
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x09: switch (BXTYPE) {
case 0x0: INSTR("msr"); APSR.all = RM; break;
case 0x1: INSTR("bx"); PC = RM; break;
case 0x2: INSTR("bxj"); PC = RM; break;
case 0x3: INSTR("blx"); if (LINK) LR = PC - 4; PC = RM; break;
case 0x5: UNIMP("qsub"); break;
case 0x7: UNIMP("bkpt"); break;
case 0x8:
case 0xa:
case 0xc:
case 0xe: UNIMP("smlawb"); break;
// P=1, U=0, bit22=0, W=1 - offset/pre-index, subtract offset, register, writeback
case 0xb:
if (L) {
INSTR("ldrh");
temp = RN - RM;
RD = MEM(u16, temp);
RN = temp;
} else {
INSTR("strh");
temp = RN - RM;
MEM(u16, temp) = RD;
RN = temp;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
temp = RN - RM;
RD = MEM(s8, temp);
RN = temp;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
temp = RN - RM;
RD = MEM(s16, temp);
RN = temp;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x0a: if (OP1 == 0 || OP2 == 0) {
INSTR("cmp"); SHFT(res); SBB(res, RN, res, 1); break;
}
switch (BXTYPE) {
case 0x5: UNIMP("qdadd"); break;
case 0x8:
case 0xa:
case 0xc:
case 0xe: UNIMP("smlalbb"); break;
// P=1, U=0, bit22=1, W=0 - offset/pre-index, subtract offset, immediate, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN - IMMHL);
} else {
INSTR("strh");
MEM(u16, RN - IMMHL) = RD;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN - IMMHL);
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN - IMMHL);
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x0b: if (OP1 == 0 || OP2 == 0) {
if (S) {
INSTR("cmn"); SHFT(res); ADC(res, RN, res, 0); break;
} else {
// "You don't need this instruction anyway."
// --mjterave@gmail.com
UNIMP("clz"); break;
}
}
switch (BXTYPE) {
case 0x5: UNIMP("qdsub"); break;
case 0x8:
case 0xa:
case 0xc:
case 0xe: UNIMP("smulbb"); break;
// P=1, U=0, bit22=1, W=1 - offset/pre-index, subtract offset, immediate, writeback
case 0xb:
if (L) {
INSTR("ldrh");
temp = RN - IMMHL;
RD = MEM(u16, temp);
RN = temp;
} else {
INSTR("strh");
temp = RN - IMMHL;
MEM(u16, temp) = RD;
RN = temp;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
temp = RN - IMMHL;
RD = MEM(s8, temp);
RN = temp;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
temp = RN - IMMHL;
RD = MEM(s16, temp);
RN = temp;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
case 0x0c: if (OP1 == 0 || OP2 == 0) {
INSTR("orr"); SHFT(res); RD = RN | res; SHFT_UPCC(RD);
} else {
switch (BXTYPE) {
case 0x1:
case 0x3:
case 0x5:
case 0x7: INSTR("orr"); SHFT(res); RD = RN | res; SHFT_UPCC(RD);
break;
case 0x9: UNIMP("ldrex"); break;
// P=1, U=0, bit22=0, W=0 - offset/pre-index, subtract offset, register, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN + RM);
} else {
INSTR("strh");
MEM(u16, RN + RM) = RD;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN + RM);
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN + RM);
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
} break;
case 0x0d: switch (BXTYPE) {
case 0x0:
if (instruction == 0xe1a00000) {
INSTR("nop"); break;
} else if (IMM5 == 0) {
if (instruction == 0xe1a0f009) { // mov pc,up i.e. the part of NEXT at the end of each code word
INSTR("NEXT");
} else {
INSTR("mov");
}
RD = RM; UPCC(RD); break;
} /* else fall through */
case 0x8:
case 0x1: INSTR("lsl"); SHFT(RD); SHFT_UPCC(RD); break;
case 0x2:
case 0xa:
case 0x3: INSTR("lsr"); SHFT(RD); SHFT_UPCC(RD); break;
case 0x4:
case 0xc:
case 0x5: INSTR("asr"); SHFT(RD); SHFT_UPCC(RD); break;
case 0x6:
case 0xe:
case 0x7: INSTR("ror"); SHFT(RD); SHFT_UPCC(RD); break;
case 0x9: UNIMP("ldrexd"); break;
// P=1, U=1, bit22=0, W=1 - offset/pre-index, add offset, register, writeback
case 0xb:
if (L) {
INSTR("ldrh");
temp = RN + RM;
RD = MEM(u16, temp);
RN = temp;
} else {
INSTR("strh");
temp = RN + RM;
MEM(u16, temp) = RD;
RN = temp;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
temp = RN + RM;
RD = MEM(s8, temp);
RN = temp;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
temp = RN + RM;
RD = MEM(s16, temp);
RN = temp;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
}; break;
case 0x0e: if (OP1 == 0 || OP2 == 0) {
INSTR("bic"); SHFT(res); RD = RN & ~res; SHFT_UPCC(RD); break;
}
switch (BXTYPE) {
case 0x9: UNIMP("ldrexb"); break;
// P=1, U=1, bit22=1, W=0 - offset/pre-index, add offset, immediate, no writeback
case 0xb:
if (L) {
INSTR("ldrh");
RD = MEM(u16, RN + IMMHL);
} else {
INSTR("strh");
MEM(u16, RN + IMMHL) = RD;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
RD = MEM(s8, RN + IMMHL);
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
RD = MEM(s16, RN + IMMHL);
} else {
UNIMP("strd");
}
break;
} break;
case 0x0f: if (OP1 == 0 || OP2 == 0) {
INSTR("mvn"); SHFT(res); RD = ~res; SHFT_UPCC(RD); break;
} else {
switch (BXTYPE) {
case 0x9: UNIMP("ldrexh"); break;
// P=1, U=1, bit22=1, W=1 - offset/pre-index, add offset, immediate, writeback
case 0xb:
if (L) {
INSTR("ldrh");
temp = RN + IMMHL;
RD = MEM(u16, temp);
RN = temp;
} else {
INSTR("strh");
temp = RN + IMMHL;
MEM(u16, temp) = RD;
RN = temp;
}
break;
case 0xd:
if (L) {
INSTR("ldrsb");
temp = RN + IMMHL;
RD = MEM(s8, temp);
RN = temp;
} else {
UNIMP("ldrd");
}
break;
case 0xf: if (L) {
INSTR("ldrsh");
temp = RN + IMMHL;
RD = MEM(s16, temp);
RN = temp;
} else {
UNIMP("strd");
}
break;
default: UNIMP("BXTYPE"); break;
} break;
} break;
case 0x10: INSTR("and"); RD = RN & IMM32; UPCC(RD); break;
case 0x11: INSTR("eor"); RD = RN ^ IMM32; UPCC(RD); break;
case 0x12: INSTR("sub"); SBB(RD, RN, IMM32, 1); break;
case 0x13: INSTR("rsb"); SBB(RD, IMM32, RN, 1); break;
case 0x14: INSTR("add"); ADC(RD, RN, IMM32, 0); break;
case 0x15: INSTR("adc"); ADC(RD, RN, IMM32, C); break;
case 0x16: INSTR("sbc"); SBB(RD, RN, IMM32, C); break;
case 0x17: INSTR("rsc"); SBB(RD, IMM32, RN, C); break;
case 0x18: INSTR("movw"); RD = IMM16; break;
case 0x19: switch (BXTYPE) {
case 0x0: INSTR("nop"); break;
case 0x1:
INSTR("wrc");
if (UFIELD(19, 4) == 0xf) { // "wrc pc"
printf("Tracing on\n");
trace = 1;
} else if (UFIELD(19, 4) == 0xe) { // "wrc lr"
printf("Tracing off\n");
trace = 0;
} else if (RN == -1) {
// trace = 1;
// printf("find %x %x %x %x\n",r[2], r[1], r[0], r[3]);
// alf = find(u8 *adr, u32 len, u32 *link, void *origin);
r[0] = (u32)find((u8 *)r[2], r[1], (u32 *)r[0], (u8 *)r[3]);
// printf("returns %x\n", r[0]);
} else {
/* Handle Forth wrapper calls - the call# is in RN */
r[0] = (*(long (*) ())(*(long *)(syscall_vec + RN)))
(r[0],r[1],r[2],r[3],r[4],r[5]);
}
break;
case 0xf: UNIMP("dbg"); break;
default: UNIMP("msr"); break;
} break;
case 0x1a: if (S) {
INSTR("cmp"); SBB(res, RN, IMM32, 1); break;
} else {
INSTR("movt"); RD = (IMM16 << 16) | (RD & 0xffff); break;
}
case 0x1b: INSTR("cmn"); ADC(res, RN, IMM32, 0); break;
case 0x1c: INSTR("orr"); RD = RN | IMM32; UPCC(RD); break;
case 0x1d: INSTR("mov"); RD = IMM32; UPCC(RD); break;
case 0x1e: INSTR("bic"); RD = RN & (~IMM32); UPCC(RD); break;
case 0x1f: INSTR("mvn"); RD = ~IMM32; UPCC(RD); break;
case 0x20:
case 0x21: if (L) {
INSTR("ldr"); RD = MEM(u32, RN); RN -= IMM12;
} else {
INSTR("str"); MEM(u32, RN) = RD; RN -= IMM12;
} break;
case 0x22:
case 0x23: if (L) {
INSTR("ldrb"); RD = MEM(u8, RN); RN -= IMM12;
} else {
INSTR("strb"); MEM(u8, RN) = RD; RN -= IMM12;
} break;
case 0x24: if (L) {
if (UFIELD(19, 4) == 0xd) {
INSTR("pop"); RD = MEM(u32, RN); RN += IMM12;
} else {
if (instruction == 0xe49cf004) { // ldr pc,[ip],#4 i.e. the guts of NEXT
INSTR("DONEXT");
} else if (instruction == 0xe49ca004) { // ldr tos,[ip],#4 i.e. the guts of (lit) and (')
INSTR("DOLIT");
} else {
if (instruction == 0xe49bc004) { indent--; if (indent < 0) indent = 0; }
INSTR("ldr");
}
RD = MEM(u32, RN); RN += IMM12;
if (trace) {
// If NEXT or ('), show the name of the word
if ((instruction == 0xe49cf004) || (instruction == 0xe49ca004)) {
name = RD - 5;
// If name is not in the dictionary area, it is probably a numeric literal
if (name >= r[9] && ((name - r[9]) < 0x100000)) {
namelen = *(char *)name & 0x3f;
name = name - namelen;
// Indent with * for EMACS outline mode - handy for hiding subordinate calls
// for (temp = 0; temp < indent; temp++)
// putchar('*');
while (namelen--)
putchar(*(char *)name++);
printf(" Stack: %x %x %x %x indent %x\n" ,
((u32 *)r[13])[2], ((u32 *)r[13])[1], ((u32 *)r[13])[0], r[10], indent);
}
}
}
}
} else {
INSTR("str"); MEM(u32, RN) = RD; RN += IMM12;
} break;
case 0x25: if (L) {
INSTR("ldr"); RD = MEM(u32, RN); RN += IMM12;
} else {
INSTR("str"); MEM(u32, RN) = RD; RN += IMM12;
} break;
case 0x26:
case 0x27: if (L) {
INSTR("ldrb"); RD = MEM(u8, RN); RN += IMM12;
} else {
INSTR("strb"); MEM(u8, RN) = RD; RN += IMM12;
} break;
case 0x28: if (L) {
INSTR("ldr"); RD = MEM(u32, RN - IMM12); break;
} else {
INSTR("str"); MEM(u32, RN - IMM12) = RD; break;
} break;
case 0x29: if (L) {
INSTR("ldr"); RN -= IMM12; RD = MEM(u32, RN); break;
} else {
if (instruction == 0xe52bc004) indent++; // DOCOLON
INSTR("str"); RN -= IMM12; MEM(u32, RN) = RD;
break;
} break;
case 0x2a: if (L) {
INSTR("ldrb"); RD = MEM(u8, RN - IMM12); break;
} else {
INSTR("strb"); MEM(u8, RN - IMM12) = RD; break;
} break;
case 0x2b: if (L) {
INSTR("ldrb"); RN -= IMM12; RD = MEM(u8, RN); break;
} else {
INSTR("strb"); RN -= IMM12; MEM(u8, RN) = RD; break;
} break;
case 0x2c: if (L) {
INSTR("ldr"); RD = MEM(u32, RN + IMM12); break;
} else {
INSTR("str"); MEM(u32, RN + IMM12) = RD; break;
} break;
case 0x2d: if (L) {
INSTR("ldr"); RN += IMM12; RD = MEM(u32, RN); break;
} else {
INSTR("str"); RN += IMM12; MEM(u32, RN) = RD; break;
} break;
case 0x2e: if (L) {
INSTR("ldrb"); RD = MEM(u8, RN + IMM12); break;
} else {
INSTR("strb"); MEM(u8, RN + IMM12) = RD; break;
} break;
case 0x2f: if (L) {
INSTR("ldrb"); RN += IMM12; RD = MEM(u8, RN); break;
} else {
INSTR("strb"); RN += IMM12; MEM(u8, RN) = RD; break;
} break;
case 0x30:
case 0x31: if (OP1) {
UNIMP("mcr"); break;
} else if (L) {
INSTR("ldr"); SHFT(res); RD = MEM(u32, RN); RN -= res; break;
} else {
INSTR("str"); SHFT(res); MEM(u32, RN) = RD; RN -= res; break;
} break;
case 0x32:
case 0x33: if (OP1) {
UNIMP("mcr"); break;
} else if (L) {
INSTR("ldrb"); SHFT(res); RD = MEM(u8, RN); RN -= res; break;
} else {
INSTR("strb"); SHFT(res); MEM(u8, RN) = RD; RN -= res; break;
} break;
case 0x34:
case 0x35: if (OP1) {
UNIMP("mcr"); break;
} else if (L) {
INSTR("ldr"); SHFT(res); RD = MEM(u32, RN); RN += res; break;
} else {
INSTR("str"); SHFT(res); MEM(u32, RN) = RD; RN += res; break;
} break;
case 0x36:
case 0x37: if (OP1) {
UNIMP("mcr"); break;
} else if (L) {
INSTR("ldrb"); SHFT(res); RD = MEM(u8, RN); RN += res; break;
} else {
INSTR("strb"); SHFT(res); MEM(u8, RN) = RD; RN += res; break;
} break;
case 0x38: if (OP1) {
UNIMP("smlad"); break;
} else if (L) {
INSTR("ldr"); SHFT(res); RD = MEM(u32, RN - res); break;
} else {
INSTR("str"); SHFT(res); MEM(u32, RN - res) = RD; break;
} break;
case 0x39: if (L) {
INSTR("ldr"); SHFT(res); RN -= res; RD = MEM(u32, RN); break;
} else {
INSTR("str"); SHFT(res); RN -= res; MEM(u32, RN) = RD; break;
} break;
case 0x3a: if (OP1) {
UNIMP("smlald"); break;
} else if (L) {
INSTR("ldrb"); SHFT(res); RD = MEM(u8, RN - res); break;
} else {
INSTR("strb"); SHFT(res); MEM(u8, RN - res) = RD; break;
} break;
case 0x3b: if (L) {
INSTR("ldrb"); SHFT(res); RN -= res; RD = MEM(u8, RN); break;
} else {
INSTR("strb"); SHFT(res); RN -= res; MEM(u8, RN) = RD; break;
}
case 0x3c: if (L) {
INSTR("ldr"); SHFT(res); RD = MEM(u32, RN + res); break;
} else {
INSTR("str"); SHFT(res); MEM(u32, RN + res) = RD; break;
}
case 0x3d: if (OP1) {
UNIMP("sbfx"); break;
} else if (L) {
INSTR("ldr"); SHFT(res); RN += res; RD = MEM(u32, RN); break;
} else {
INSTR("str"); SHFT(res); RN += res; MEM(u32, RN) = RD; break;
} break;
case 0x3e: if (OP1) {
if (UFIELD(3, 4) == 0xf) {
INSTR("bfc"); RD &= ~BF(MSB, LSB);
} else {
INSTR("bfi");
RD &= ~BF(MSB, LSB);
RD |= RN & ~BF(MSB, LSB);
}
} else if (L) {
INSTR("ldrb"); SHFT(res); RD = MEM(u8, RN + res);
} else {
INSTR("strb"); SHFT(res); MEM(u8, RN + res) = RD;
} break;
case 0x3f: if (L) {
INSTR("ldrb"); SHFT(res); RN += res; RD = MEM(u8, RN); break;
} else {
INSTR("strb"); SHFT(res); RN += res; MEM(u8, RN) = RD; break;
} break;
case 0x40:
case 0x41: { u32 base = RN;
u32 reglist = UFIELD(15, 16);
s32 reg;
if (L) {
INSTR("ldmda");
} else {
INSTR("stmda");
}
for (reg = 15; reg >= 0; reg--) {
if ((1 << reg) & reglist) {
if (L) {
r[reg] = MEM(u32, base);
} else {
MEM(u32, base) = r[reg];
}
base -= 4;
}
}
if (W) RN = base;
} break;
case 0x42: UNIMP("xxx");
case 0x43: UNIMP("xxx");
case 0x44:
case 0x45: { u32 base = RN;
u32 reglist = UFIELD(15, 16);
s32 reg;
if (L) {
INSTR("ldm");
} else {
INSTR("stm");
}
for (reg = 0; reg < 16; reg++) {
if ((1 << reg) & reglist) {
if (L) {
r[reg] = MEM(u32, base);
} else {
MEM(u32, base) = r[reg];
}
base += 4;
}
}
if (W) RN = base;
} break;
case 0x46: UNIMP("xxx");
case 0x47: UNIMP("xxx");
case 0x48:
case 0x49: { u32 base = RN;
u32 reglist = UFIELD(15, 16);
s32 reg;
if (L) {
INSTR("ldmdb");
} else {
INSTR("stmdb");
}
for (reg = 15; reg >= 0; reg--) {
if ((1 << reg) & reglist) {
base -= 4;
if (L) {
r[reg] = MEM(u32, base);
} else {
MEM(u32, base) = r[reg];
}
}
}
if (W) RN = base;
} break;
case 0x4a: UNIMP("xxx"); break;
case 0x4b: UNIMP("xxx"); break;
case 0x4c:
case 0x4d: { u32 base = RN + 4;
u32 reglist = UFIELD(15, 16);
s32 reg;
if (L) {
INSTR("ldmib");
} else {
INSTR("stmib");
}
for (reg = 0; reg < 16; reg++) {
if ((1 << reg) & reglist) {
if (L) {
r[reg] = MEM(u32, base);
} else {
MEM(u32, base) = r[reg];
}
base += 4;
}
}
if (W) RN = base;
} break;
case 0x4e: UNIMP("xxx"); break;
case 0x4f: UNIMP("xxx"); break;
case 0x50:
case 0x51:
case 0x52:
case 0x53:
case 0x54:
case 0x55:
case 0x56:
case 0x57: INSTR("b"); BTGT; break;
case 0x58:
case 0x59:
case 0x5a:
case 0x5b:
case 0x5c:
case 0x5d:
case 0x5e:
case 0x5f: INSTR("bl"); LR = PC - 4; BTGT; break;
case 0x60:
case 0x61: UNIMP("stc"); break;
case 0x62: UNIMP("mcrr"); break;
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e: UNIMP("stc"); break;
case 0x6f: UNIMP("ldc"); break;
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77: UNIMP("cdp"); break;
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f: UNIMP("svc"); break;
} // switch (OP)
annul:
if (PC == last_pc)
PC += 4;
else // branch or move
PC += 8;
} // while (1)
}
// LICENSE_BEGIN
// Copyright (c) 2007 FirmWorks
// Copyright 2010 Apple, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// LICENSE_END