blob: 76f398b3d7172ac492c23536ed4102ebf77ccde0 [file] [log] [blame]
/* armemu.c -- Main instruction emulation: ARM7 Instruction Emulator.
Copyright (C) 1994 Advanced RISC Machines Ltd.
Modifications to add arch. v4 support by <jsmith@cygnus.com>.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>. */
#include "armdefs.h"
#include "armemu.h"
#include "armos.h"
#include "iwmmxt.h"
static ARMword GetDPRegRHS (ARMul_State *, ARMword);
static ARMword GetDPSRegRHS (ARMul_State *, ARMword);
static void WriteR15 (ARMul_State *, ARMword);
static void WriteSR15 (ARMul_State *, ARMword);
static void WriteR15Branch (ARMul_State *, ARMword);
static void WriteR15Load (ARMul_State *, ARMword);
static ARMword GetLSRegRHS (ARMul_State *, ARMword);
static ARMword GetLS7RHS (ARMul_State *, ARMword);
static unsigned LoadWord (ARMul_State *, ARMword, ARMword);
static unsigned LoadHalfWord (ARMul_State *, ARMword, ARMword, int);
static unsigned LoadByte (ARMul_State *, ARMword, ARMword, int);
static unsigned StoreWord (ARMul_State *, ARMword, ARMword);
static unsigned StoreHalfWord (ARMul_State *, ARMword, ARMword);
static unsigned StoreByte (ARMul_State *, ARMword, ARMword);
static void LoadMult (ARMul_State *, ARMword, ARMword, ARMword);
static void StoreMult (ARMul_State *, ARMword, ARMword, ARMword);
static void LoadSMult (ARMul_State *, ARMword, ARMword, ARMword);
static void StoreSMult (ARMul_State *, ARMword, ARMword, ARMword);
static unsigned Multiply64 (ARMul_State *, ARMword, int, int);
static unsigned MultiplyAdd64 (ARMul_State *, ARMword, int, int);
static void Handle_Load_Double (ARMul_State *, ARMword);
static void Handle_Store_Double (ARMul_State *, ARMword);
#define LUNSIGNED (0) /* unsigned operation */
#define LSIGNED (1) /* signed operation */
#define LDEFAULT (0) /* default : do nothing */
#define LSCC (1) /* set condition codes on result */
extern int stop_simulator;
/* Short-hand macros for LDR/STR. */
/* Store post decrement writeback. */
#define SHDOWNWB() \
lhs = LHS ; \
if (StoreHalfWord (state, instr, lhs)) \
LSBase = lhs - GetLS7RHS (state, instr);
/* Store post increment writeback. */
#define SHUPWB() \
lhs = LHS ; \
if (StoreHalfWord (state, instr, lhs)) \
LSBase = lhs + GetLS7RHS (state, instr);
/* Store pre decrement. */
#define SHPREDOWN() \
(void)StoreHalfWord (state, instr, LHS - GetLS7RHS (state, instr));
/* Store pre decrement writeback. */
#define SHPREDOWNWB() \
temp = LHS - GetLS7RHS (state, instr); \
if (StoreHalfWord (state, instr, temp)) \
LSBase = temp;
/* Store pre increment. */
#define SHPREUP() \
(void)StoreHalfWord (state, instr, LHS + GetLS7RHS (state, instr));
/* Store pre increment writeback. */
#define SHPREUPWB() \
temp = LHS + GetLS7RHS (state, instr); \
if (StoreHalfWord (state, instr, temp)) \
LSBase = temp;
/* Load post decrement writeback. */
#define LHPOSTDOWN() \
{ \
int done = 1; \
lhs = LHS; \
temp = lhs - GetLS7RHS (state, instr); \
\
switch (BITS (5, 6)) \
{ \
case 1: /* H */ \
if (LoadHalfWord (state, instr, lhs, LUNSIGNED)) \
LSBase = temp; \
break; \
case 2: /* SB */ \
if (LoadByte (state, instr, lhs, LSIGNED)) \
LSBase = temp; \
break; \
case 3: /* SH */ \
if (LoadHalfWord (state, instr, lhs, LSIGNED)) \
LSBase = temp; \
break; \
case 0: /* SWP handled elsewhere. */ \
default: \
done = 0; \
break; \
} \
if (done) \
break; \
}
/* Load post increment writeback. */
#define LHPOSTUP() \
{ \
int done = 1; \
lhs = LHS; \
temp = lhs + GetLS7RHS (state, instr); \
\
switch (BITS (5, 6)) \
{ \
case 1: /* H */ \
if (LoadHalfWord (state, instr, lhs, LUNSIGNED)) \
LSBase = temp; \
break; \
case 2: /* SB */ \
if (LoadByte (state, instr, lhs, LSIGNED)) \
LSBase = temp; \
break; \
case 3: /* SH */ \
if (LoadHalfWord (state, instr, lhs, LSIGNED)) \
LSBase = temp; \
break; \
case 0: /* SWP handled elsewhere. */ \
default: \
done = 0; \
break; \
} \
if (done) \
break; \
}
/* Load pre decrement. */
#define LHPREDOWN() \
{ \
int done = 1; \
\
temp = LHS - GetLS7RHS (state, instr); \
switch (BITS (5, 6)) \
{ \
case 1: /* H */ \
(void) LoadHalfWord (state, instr, temp, LUNSIGNED); \
break; \
case 2: /* SB */ \
(void) LoadByte (state, instr, temp, LSIGNED); \
break; \
case 3: /* SH */ \
(void) LoadHalfWord (state, instr, temp, LSIGNED); \
break; \
case 0: \
/* SWP handled elsewhere. */ \
default: \
done = 0; \
break; \
} \
if (done) \
break; \
}
/* Load pre decrement writeback. */
#define LHPREDOWNWB() \
{ \
int done = 1; \
\
temp = LHS - GetLS7RHS (state, instr); \
switch (BITS (5, 6)) \
{ \
case 1: /* H */ \
if (LoadHalfWord (state, instr, temp, LUNSIGNED)) \
LSBase = temp; \
break; \
case 2: /* SB */ \
if (LoadByte (state, instr, temp, LSIGNED)) \
LSBase = temp; \
break; \
case 3: /* SH */ \
if (LoadHalfWord (state, instr, temp, LSIGNED)) \
LSBase = temp; \
break; \
case 0: \
/* SWP handled elsewhere. */ \
default: \
done = 0; \
break; \
} \
if (done) \
break; \
}
/* Load pre increment. */
#define LHPREUP() \
{ \
int done = 1; \
\
temp = LHS + GetLS7RHS (state, instr); \
switch (BITS (5, 6)) \
{ \
case 1: /* H */ \
(void) LoadHalfWord (state, instr, temp, LUNSIGNED); \
break; \
case 2: /* SB */ \
(void) LoadByte (state, instr, temp, LSIGNED); \
break; \
case 3: /* SH */ \
(void) LoadHalfWord (state, instr, temp, LSIGNED); \
break; \
case 0: \
/* SWP handled elsewhere. */ \
default: \
done = 0; \
break; \
} \
if (done) \
break; \
}
/* Load pre increment writeback. */
#define LHPREUPWB() \
{ \
int done = 1; \
\
temp = LHS + GetLS7RHS (state, instr); \
switch (BITS (5, 6)) \
{ \
case 1: /* H */ \
if (LoadHalfWord (state, instr, temp, LUNSIGNED)) \
LSBase = temp; \
break; \
case 2: /* SB */ \
if (LoadByte (state, instr, temp, LSIGNED)) \
LSBase = temp; \
break; \
case 3: /* SH */ \
if (LoadHalfWord (state, instr, temp, LSIGNED)) \
LSBase = temp; \
break; \
case 0: \
/* SWP handled elsewhere. */ \
default: \
done = 0; \
break; \
} \
if (done) \
break; \
}
/* Attempt to emulate an ARMv6 instruction.
Returns non-zero upon success. */
#ifdef MODE32
static int
handle_v6_insn (ARMul_State * state, ARMword instr)
{
ARMword val;
ARMword Rd;
ARMword Rm;
ARMword Rn;
switch (BITS (20, 27))
{
#if 0
case 0x03: printf ("Unhandled v6 insn: ldr\n"); break;
case 0x04: printf ("Unhandled v6 insn: umaal\n"); break;
case 0x06: printf ("Unhandled v6 insn: mls/str\n"); break;
case 0x16: printf ("Unhandled v6 insn: smi\n"); break;
case 0x18: printf ("Unhandled v6 insn: strex\n"); break;
case 0x19: printf ("Unhandled v6 insn: ldrex\n"); break;
case 0x1a: printf ("Unhandled v6 insn: strexd\n"); break;
case 0x1b: printf ("Unhandled v6 insn: ldrexd\n"); break;
case 0x1c: printf ("Unhandled v6 insn: strexb\n"); break;
case 0x1d: printf ("Unhandled v6 insn: ldrexb\n"); break;
case 0x1e: printf ("Unhandled v6 insn: strexh\n"); break;
case 0x1f: printf ("Unhandled v6 insn: ldrexh\n"); break;
case 0x32: printf ("Unhandled v6 insn: nop/sev/wfe/wfi/yield\n"); break;
case 0x3f: printf ("Unhandled v6 insn: rbit\n"); break;
#endif
case 0x61: printf ("Unhandled v6 insn: sadd/ssub\n"); break;
case 0x63: printf ("Unhandled v6 insn: shadd/shsub\n"); break;
case 0x6c: printf ("Unhandled v6 insn: uxtb16/uxtab16\n"); break;
case 0x70: printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n"); break;
case 0x74: printf ("Unhandled v6 insn: smlald/smlsld\n"); break;
case 0x75: printf ("Unhandled v6 insn: smmla/smmls/smmul\n"); break;
case 0x78: printf ("Unhandled v6 insn: usad/usada8\n"); break;
case 0x30:
{
/* MOVW<c> <Rd>,#<imm16>
instr[31,28] = cond
instr[27,20] = 0011 0000
instr[19,16] = imm4
instr[15,12] = Rd
instr[11, 0] = imm12. */
Rd = BITS (12, 15);
val = (BITS (16, 19) << 12) | BITS (0, 11);
state->Reg[Rd] = val;
return 1;
}
case 0x34:
{
/* MOVT<c> <Rd>,#<imm16>
instr[31,28] = cond
instr[27,20] = 0011 0100
instr[19,16] = imm4
instr[15,12] = Rd
instr[11, 0] = imm12. */
Rd = BITS (12, 15);
val = (BITS (16, 19) << 12) | BITS (0, 11);
state->Reg[Rd] &= 0xFFFF;
state->Reg[Rd] |= val << 16;
return 1;
}
case 0x62:
{
ARMword val1;
ARMword val2;
ARMsword n, m, r;
int i;
Rd = BITS (12, 15);
Rn = BITS (16, 19);
Rm = BITS (0, 3);
if (Rd == 15 || Rn == 15 || Rm == 15)
break;
val1 = state->Reg[Rn];
val2 = state->Reg[Rm];
switch (BITS (4, 11))
{
case 0xF1: /* QADD16<c> <Rd>,<Rn>,<Rm>. */
state->Reg[Rd] = 0;
for (i = 0; i < 32; i+= 16)
{
n = (val1 >> i) & 0xFFFF;
if (n & 0x8000)
n |= -(1 << 16);
m = (val2 >> i) & 0xFFFF;
if (m & 0x8000)
m |= -(1 << 16);
r = n + m;
if (r > 0x7FFF)
r = 0x7FFF;
else if (r < -(0x8000))
r = - 0x8000;
state->Reg[Rd] |= (r & 0xFFFF) << i;
}
return 1;
case 0xF3: /* QASX<c> <Rd>,<Rn>,<Rm>. */
n = val1 & 0xFFFF;
if (n & 0x8000)
n |= -(1 << 16);
m = (val2 >> 16) & 0xFFFF;
if (m & 0x8000)
m |= -(1 << 16);
r = n - m;
if (r > 0x7FFF)
r = 0x7FFF;
else if (r < -(0x8000))
r = - 0x8000;
state->Reg[Rd] = (r & 0xFFFF);
n = (val1 >> 16) & 0xFFFF;
if (n & 0x8000)
n |= -(1 << 16);
m = val2 & 0xFFFF;
if (m & 0x8000)
m |= -(1 << 16);
r = n + m;
if (r > 0x7FFF)
r = 0x7FFF;
else if (r < -(0x8000))
r = - 0x8000;
state->Reg[Rd] |= (r & 0xFFFF) << 16;
return 1;
case 0xF5: /* QSAX<c> <Rd>,<Rn>,<Rm>. */
n = val1 & 0xFFFF;
if (n & 0x8000)
n |= -(1 << 16);
m = (val2 >> 16) & 0xFFFF;
if (m & 0x8000)
m |= -(1 << 16);
r = n + m;
if (r > 0x7FFF)
r = 0x7FFF;
else if (r < -(0x8000))
r = - 0x8000;
state->Reg[Rd] = (r & 0xFFFF);
n = (val1 >> 16) & 0xFFFF;
if (n & 0x8000)
n |= -(1 << 16);
m = val2 & 0xFFFF;
if (m & 0x8000)
m |= -(1 << 16);
r = n - m;
if (r > 0x7FFF)
r = 0x7FFF;
else if (r < -(0x8000))
r = - 0x8000;
state->Reg[Rd] |= (r & 0xFFFF) << 16;
return 1;
case 0xF7: /* QSUB16<c> <Rd>,<Rn>,<Rm>. */
state->Reg[Rd] = 0;
for (i = 0; i < 32; i+= 16)
{
n = (val1 >> i) & 0xFFFF;
if (n & 0x8000)
n |= -(1 << 16);
m = (val2 >> i) & 0xFFFF;
if (m & 0x8000)
m |= -(1 << 16);
r = n - m;
if (r > 0x7FFF)
r = 0x7FFF;
else if (r < -(0x8000))
r = - 0x8000;
state->Reg[Rd] |= (r & 0xFFFF) << i;
}
return 1;
case 0xF9: /* QADD8<c> <Rd>,<Rn>,<Rm>. */
state->Reg[Rd] = 0;
for (i = 0; i < 32; i+= 8)
{
n = (val1 >> i) & 0xFF;
if (n & 0x80)
n |= - (1 << 8);
m = (val2 >> i) & 0xFF;
if (m & 0x80)
m |= - (1 << 8);
r = n + m;
if (r > 127)
r = 127;
else if (r < -128)
r = -128;
state->Reg[Rd] |= (r & 0xFF) << i;
}
return 1;
case 0xFF: /* QSUB8<c> <Rd>,<Rn>,<Rm>. */
state->Reg[Rd] = 0;
for (i = 0; i < 32; i+= 8)
{
n = (val1 >> i) & 0xFF;
if (n & 0x80)
n |= - (1 << 8);
m = (val2 >> i) & 0xFF;
if (m & 0x80)
m |= - (1 << 8);
r = n - m;
if (r > 127)
r = 127;
else if (r < -128)
r = -128;
state->Reg[Rd] |= (r & 0xFF) << i;
}
return 1;
default:
break;
}
break;
}
case 0x65:
{
ARMword valn;
ARMword valm;
ARMword res1, res2, res3, res4;
/* U{ADD|SUB}{8|16}<c> <Rd>, <Rn>, <Rm>
instr[31,28] = cond
instr[27,20] = 0110 0101
instr[19,16] = Rn
instr[15,12] = Rd
instr[11, 8] = 1111
instr[ 7, 4] = opcode: UADD8 (1001), UADD16 (0001), USUB8 (1111), USUB16 (0111)
instr[ 3, 0] = Rm. */
if (BITS (8, 11) != 0xF)
break;
Rn = BITS (16, 19);
Rd = BITS (12, 15);
Rm = BITS (0, 3);
if (Rn == 15 || Rd == 15 || Rm == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
break;
}
valn = state->Reg[Rn];
valm = state->Reg[Rm];
switch (BITS (4, 7))
{
case 1: /* UADD16. */
res1 = (valn & 0xFFFF) + (valm & 0xFFFF);
if (res1 > 0xFFFF)
state->Cpsr |= (GE0 | GE1);
else
state->Cpsr &= ~ (GE0 | GE1);
res2 = (valn >> 16) + (valm >> 16);
if (res2 > 0xFFFF)
state->Cpsr |= (GE2 | GE3);
else
state->Cpsr &= ~ (GE2 | GE3);
state->Reg[Rd] = (res1 & 0xFFFF) | (res2 << 16);
return 1;
case 7: /* USUB16. */
res1 = (valn & 0xFFFF) - (valm & 0xFFFF);
if (res1 & 0x800000)
state->Cpsr |= (GE0 | GE1);
else
state->Cpsr &= ~ (GE0 | GE1);
res2 = (valn >> 16) - (valm >> 16);
if (res2 & 0x800000)
state->Cpsr |= (GE2 | GE3);
else
state->Cpsr &= ~ (GE2 | GE3);
state->Reg[Rd] = (res1 & 0xFFFF) | (res2 << 16);
return 1;
case 9: /* UADD8. */
res1 = (valn & 0xFF) + (valm & 0xFF);
if (res1 > 0xFF)
state->Cpsr |= GE0;
else
state->Cpsr &= ~ GE0;
res2 = ((valn >> 8) & 0xFF) + ((valm >> 8) & 0xFF);
if (res2 > 0xFF)
state->Cpsr |= GE1;
else
state->Cpsr &= ~ GE1;
res3 = ((valn >> 16) & 0xFF) + ((valm >> 16) & 0xFF);
if (res3 > 0xFF)
state->Cpsr |= GE2;
else
state->Cpsr &= ~ GE2;
res4 = (valn >> 24) + (valm >> 24);
if (res4 > 0xFF)
state->Cpsr |= GE3;
else
state->Cpsr &= ~ GE3;
state->Reg[Rd] = (res1 & 0xFF) | ((res2 << 8) & 0xFF00)
| ((res3 << 16) & 0xFF0000) | (res4 << 24);
return 1;
case 15: /* USUB8. */
res1 = (valn & 0xFF) - (valm & 0xFF);
if (res1 & 0x800000)
state->Cpsr |= GE0;
else
state->Cpsr &= ~ GE0;
res2 = ((valn >> 8) & 0XFF) - ((valm >> 8) & 0xFF);
if (res2 & 0x800000)
state->Cpsr |= GE1;
else
state->Cpsr &= ~ GE1;
res3 = ((valn >> 16) & 0XFF) - ((valm >> 16) & 0xFF);
if (res3 & 0x800000)
state->Cpsr |= GE2;
else
state->Cpsr &= ~ GE2;
res4 = (valn >> 24) - (valm >> 24) ;
if (res4 & 0x800000)
state->Cpsr |= GE3;
else
state->Cpsr &= ~ GE3;
state->Reg[Rd] = (res1 & 0xFF) | ((res2 << 8) & 0xFF00)
| ((res3 << 16) & 0xFF0000) | (res4 << 24);
return 1;
default:
break;
}
break;
}
case 0x68:
{
ARMword res;
/* PKHBT<c> <Rd>,<Rn>,<Rm>{,LSL #<imm>}
PKHTB<c> <Rd>,<Rn>,<Rm>{,ASR #<imm>}
SXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>}
SXTB16<c> <Rd>,<Rm>{,<rotation>}
SEL<c> <Rd>,<Rn>,<Rm>
instr[31,28] = cond
instr[27,20] = 0110 1000
instr[19,16] = Rn
instr[15,12] = Rd
instr[11, 7] = imm5 (PKH), 11111 (SEL), rr000 (SXTAB16 & SXTB16),
instr[6] = tb (PKH), 0 (SEL), 1 (SXT)
instr[5] = opcode: PKH (0), SEL/SXT (1)
instr[4] = 1
instr[ 3, 0] = Rm. */
if (BIT (4) != 1)
break;
if (BIT (5) == 0)
{
/* FIXME: Add implementation of PKH. */
fprintf (stderr, "PKH: NOT YET IMPLEMENTED\n");
ARMul_UndefInstr (state, instr);
break;
}
if (BIT (6) == 1)
{
/* FIXME: Add implementation of SXT. */
fprintf (stderr, "SXT: NOT YET IMPLEMENTED\n");
ARMul_UndefInstr (state, instr);
break;
}
Rn = BITS (16, 19);
Rd = BITS (12, 15);
Rm = BITS (0, 3);
if (Rn == 15 || Rm == 15 || Rd == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
break;
}
res = (state->Reg[(state->Cpsr & GE0) ? Rn : Rm]) & 0xFF;
res |= (state->Reg[(state->Cpsr & GE1) ? Rn : Rm]) & 0xFF00;
res |= (state->Reg[(state->Cpsr & GE2) ? Rn : Rm]) & 0xFF0000;
res |= (state->Reg[(state->Cpsr & GE3) ? Rn : Rm]) & 0xFF000000;
state->Reg[Rd] = res;
return 1;
}
case 0x6a:
{
int ror = -1;
switch (BITS (4, 11))
{
case 0x07: ror = 0; break;
case 0x47: ror = 8; break;
case 0x87: ror = 16; break;
case 0xc7: ror = 24; break;
case 0x01:
case 0xf3:
printf ("Unhandled v6 insn: ssat\n");
return 0;
default:
break;
}
if (ror == -1)
{
if (BITS (4, 6) == 0x7)
{
printf ("Unhandled v6 insn: ssat\n");
return 0;
}
break;
}
Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
if (Rm & 0x80)
Rm |= 0xffffff00;
if (BITS (16, 19) == 0xf)
/* SXTB */
state->Reg[BITS (12, 15)] = Rm;
else
/* SXTAB */
state->Reg[BITS (12, 15)] += Rm;
}
return 1;
case 0x6b:
{
int ror = -1;
switch (BITS (4, 11))
{
case 0x07: ror = 0; break;
case 0x47: ror = 8; break;
case 0x87: ror = 16; break;
case 0xc7: ror = 24; break;
case 0xf3:
{
/* REV<c> <Rd>,<Rm>
instr[31,28] = cond
instr[27,20] = 0110 1011
instr[19,16] = 1111
instr[15,12] = Rd
instr[11, 4] = 1111 0011
instr[ 3, 0] = Rm. */
if (BITS (16, 19) != 0xF)
break;
Rd = BITS (12, 15);
Rm = BITS (0, 3);
if (Rd == 15 || Rm == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
break;
}
val = state->Reg[Rm] << 24;
val |= ((state->Reg[Rm] << 8) & 0xFF0000);
val |= ((state->Reg[Rm] >> 8) & 0xFF00);
val |= ((state->Reg[Rm] >> 24));
state->Reg[Rd] = val;
return 1;
}
case 0xfb:
{
/* REV16<c> <Rd>,<Rm>. */
if (BITS (16, 19) != 0xF)
break;
Rd = BITS (12, 15);
Rm = BITS (0, 3);
if (Rd == 15 || Rm == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
break;
}
val = 0;
val |= ((state->Reg[Rm] >> 8) & 0x00FF00FF);
val |= ((state->Reg[Rm] << 8) & 0xFF00FF00);
state->Reg[Rd] = val;
return 1;
}
default:
break;
}
if (ror == -1)
break;
Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
if (Rm & 0x8000)
Rm |= 0xffff0000;
if (BITS (16, 19) == 0xf)
/* SXTH */
state->Reg[BITS (12, 15)] = Rm;
else
/* SXTAH */
state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
}
return 1;
case 0x6e:
{
int ror = -1;
switch (BITS (4, 11))
{
case 0x07: ror = 0; break;
case 0x47: ror = 8; break;
case 0x87: ror = 16; break;
case 0xc7: ror = 24; break;
case 0x01:
case 0xf3:
printf ("Unhandled v6 insn: usat\n");
return 0;
default:
break;
}
if (ror == -1)
{
if (BITS (4, 6) == 0x7)
{
printf ("Unhandled v6 insn: usat\n");
return 0;
}
break;
}
Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
if (BITS (16, 19) == 0xf)
/* UXTB */
state->Reg[BITS (12, 15)] = Rm;
else
/* UXTAB */
state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
}
return 1;
case 0x6f:
{
int i;
int ror = -1;
switch (BITS (4, 11))
{
case 0x07: ror = 0; break;
case 0x47: ror = 8; break;
case 0x87: ror = 16; break;
case 0xc7: ror = 24; break;
case 0xf3: /* RBIT */
if (BITS (16, 19) != 0xF)
break;
Rd = BITS (12, 15);
state->Reg[Rd] = 0;
Rm = state->Reg[BITS (0, 3)];
for (i = 0; i < 32; i++)
if (Rm & (1 << i))
state->Reg[Rd] |= (1 << (31 - i));
return 1;
case 0xfb:
printf ("Unhandled v6 insn: revsh\n");
return 0;
default:
break;
}
if (ror == -1)
break;
Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
if (BITS (16, 19) == 0xf)
/* UXT */
state->Reg[BITS (12, 15)] = Rm;
else
/* UXTAH */
state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm;
}
return 1;
case 0x7c:
case 0x7d:
{
int lsb;
int msb;
ARMword mask;
/* BFC<c> <Rd>,#<lsb>,#<width>
BFI<c> <Rd>,<Rn>,#<lsb>,#<width>
instr[31,28] = cond
instr[27,21] = 0111 110
instr[20,16] = msb
instr[15,12] = Rd
instr[11, 7] = lsb
instr[ 6, 4] = 001 1111
instr[ 3, 0] = Rn (BFI) / 1111 (BFC). */
if (BITS (4, 6) != 0x1)
break;
Rd = BITS (12, 15);
if (Rd == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
}
lsb = BITS (7, 11);
msb = BITS (16, 20);
if (lsb > msb)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
}
mask = -(1 << lsb);
mask &= ~(-(1 << (msb + 1)));
state->Reg[Rd] &= ~ mask;
Rn = BITS (0, 3);
if (Rn != 0xF)
{
ARMword val = state->Reg[Rn] & ~(-(1 << ((msb + 1) - lsb)));
state->Reg[Rd] |= val << lsb;
}
return 1;
}
case 0x7b:
case 0x7a: /* SBFX<c> <Rd>,<Rn>,#<lsb>,#<width>. */
{
int lsb;
int widthm1;
ARMsword sval;
if (BITS (4, 6) != 0x5)
break;
Rd = BITS (12, 15);
if (Rd == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
}
Rn = BITS (0, 3);
if (Rn == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
}
lsb = BITS (7, 11);
widthm1 = BITS (16, 20);
sval = state->Reg[Rn];
sval <<= (31 - (lsb + widthm1));
sval >>= (31 - widthm1);
state->Reg[Rd] = sval;
return 1;
}
case 0x7f:
case 0x7e:
{
int lsb;
int widthm1;
/* UBFX<c> <Rd>,<Rn>,#<lsb>,#<width>
instr[31,28] = cond
instr[27,21] = 0111 111
instr[20,16] = widthm1
instr[15,12] = Rd
instr[11, 7] = lsb
instr[ 6, 4] = 101
instr[ 3, 0] = Rn. */
if (BITS (4, 6) != 0x5)
break;
Rd = BITS (12, 15);
if (Rd == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
}
Rn = BITS (0, 3);
if (Rn == 15)
{
ARMul_UndefInstr (state, instr);
state->Emulate = FALSE;
}
lsb = BITS (7, 11);
widthm1 = BITS (16, 20);
val = state->Reg[Rn];
val >>= lsb;
val &= ~(-(1 << (widthm1 + 1)));
state->Reg[Rd] = val;
return 1;
}
#if 0
case 0x84: printf ("Unhandled v6 insn: srs\n"); break;
#endif
default:
break;
}
printf ("Unhandled v6 insn: UNKNOWN: %08x\n", instr);
return 0;
}
#endif
static void
handle_VFP_move (ARMul_State * state, ARMword instr)
{
switch (BITS (20, 27))
{
case 0xC4:
case 0xC5:
switch (BITS (4, 11))
{
case 0xA1:
case 0xA3:
{
/* VMOV two core <-> two VFP single precision. */
int sreg = (BITS (0, 3) << 1) | BIT (5);
if (BIT (20))
{
state->Reg[BITS (12, 15)] = VFP_uword (sreg);
state->Reg[BITS (16, 19)] = VFP_uword (sreg + 1);
}
else
{
VFP_uword (sreg) = state->Reg[BITS (12, 15)];
VFP_uword (sreg + 1) = state->Reg[BITS (16, 19)];
}
}
break;
case 0xB1:
case 0xB3:
{
/* VMOV two core <-> VFP double precision. */
int dreg = BITS (0, 3) | (BIT (5) << 4);
if (BIT (20))
{
if (trace)
fprintf (stderr, " VFP: VMOV: r%d r%d <= d%d\n",
BITS (12, 15), BITS (16, 19), dreg);
state->Reg[BITS (12, 15)] = VFP_dword (dreg);
state->Reg[BITS (16, 19)] = VFP_dword (dreg) >> 32;
}
else
{
VFP_dword (dreg) = state->Reg[BITS (16, 19)];
VFP_dword (dreg) <<= 32;
VFP_dword (dreg) |= state->Reg[BITS (12, 15)];
if (trace)
fprintf (stderr, " VFP: VMOV: d%d <= r%d r%d : %g\n",
dreg, BITS (16, 19), BITS (12, 15),
VFP_dval (dreg));
}
}
break;
default:
fprintf (stderr, "SIM: VFP: Unimplemented move insn %x\n", BITS (20, 27));
break;
}
break;
case 0xe0:
case 0xe1:
/* VMOV single core <-> VFP single precision. */
if (BITS (0, 6) != 0x10 || BITS (8, 11) != 0xA)
fprintf (stderr, "SIM: VFP: Unimplemented move insn %x\n", BITS (20, 27));
else
{
int sreg = (BITS (16, 19) << 1) | BIT (7);
if (BIT (20))
state->Reg[DESTReg] = VFP_uword (sreg);
else
VFP_uword (sreg) = state->Reg[DESTReg];
}
break;
default:
fprintf (stderr, "SIM: VFP: Unimplemented move insn %x\n", BITS (20, 27));
return;
}
}
/* EMULATION of ARM6. */
/* The PC pipeline value depends on whether ARM
or Thumb instructions are being executed. */
ARMword isize;
ARMword
#ifdef MODE32
ARMul_Emulate32 (ARMul_State * state)
#else
ARMul_Emulate26 (ARMul_State * state)
#endif
{
ARMword instr; /* The current instruction. */
ARMword dest = 0; /* Almost the DestBus. */
ARMword temp; /* Ubiquitous third hand. */
ARMword pc = 0; /* The address of the current instruction. */
ARMword lhs; /* Almost the ABus and BBus. */
ARMword rhs;
ARMword decoded = 0; /* Instruction pipeline. */
ARMword loaded = 0;
/* Execute the next instruction. */
if (state->NextInstr < PRIMEPIPE)
{
decoded = state->decoded;
loaded = state->loaded;
pc = state->pc;
}
do
{
/* Just keep going. */
isize = INSN_SIZE;
switch (state->NextInstr)
{
case SEQ:
/* Advance the pipeline, and an S cycle. */
state->Reg[15] += isize;
pc += isize;
instr = decoded;
decoded = loaded;
loaded = ARMul_LoadInstrS (state, pc + (isize * 2), isize);
break;
case NONSEQ:
/* Advance the pipeline, and an N cycle. */
state->Reg[15] += isize;
pc += isize;
instr = decoded;
decoded = loaded;
loaded = ARMul_LoadInstrN (state, pc + (isize * 2), isize);
NORMALCYCLE;
break;
case PCINCEDSEQ:
/* Program counter advanced, and an S cycle. */
pc += isize;
instr = decoded;
decoded = loaded;
loaded = ARMul_LoadInstrS (state, pc + (isize * 2), isize);
NORMALCYCLE;
break;
case PCINCEDNONSEQ:
/* Program counter advanced, and an N cycle. */
pc += isize;
instr = decoded;
decoded = loaded;
loaded = ARMul_LoadInstrN (state, pc + (isize * 2), isize);
NORMALCYCLE;
break;
case RESUME:
/* The program counter has been changed. */
pc = state->Reg[15];
#ifndef MODE32
pc = pc & R15PCBITS;
#endif
state->Reg[15] = pc + (isize * 2);
state->Aborted = 0;
instr = ARMul_ReLoadInstr (state, pc, isize);
decoded = ARMul_ReLoadInstr (state, pc + isize, isize);
loaded = ARMul_ReLoadInstr (state, pc + isize * 2, isize);
NORMALCYCLE;
break;
default:
/* The program counter has been changed. */
pc = state->Reg[15];
#ifndef MODE32
pc = pc & R15PCBITS;
#endif
state->Reg[15] = pc + (isize * 2);
state->Aborted = 0;
instr = ARMul_LoadInstrN (state, pc, isize);
decoded = ARMul_LoadInstrS (state, pc + (isize), isize);
loaded = ARMul_LoadInstrS (state, pc + (isize * 2), isize);
NORMALCYCLE;
break;
}
if (state->EventSet)
ARMul_EnvokeEvent (state);
if (! TFLAG && trace)
{
fprintf (stderr, "pc: %x, ", pc & ~1);
if (! disas)
fprintf (stderr, "instr: %x\n", instr);
}
if (instr == 0 || pc < 0x10)
{
ARMul_Abort (state, ARMUndefinedInstrV);
state->Emulate = FALSE;
}
#if 0 /* Enable this code to help track down stack alignment bugs. */
{
static ARMword old_sp = -1;
if (old_sp != state->Reg[13])
{
old_sp = state->Reg[13];
fprintf (stderr, "pc: %08x: SP set to %08x%s\n",
pc & ~1, old_sp, (old_sp % 8) ? " [UNALIGNED!]" : "");
}
}
#endif
if (state->Exception)
{
/* Any exceptions ? */
if (state->NresetSig == LOW)
{
ARMul_Abort (state, ARMul_ResetV);
break;
}
else if (!state->NfiqSig && !FFLAG)
{
ARMul_Abort (state, ARMul_FIQV);
break;
}
else if (!state->NirqSig && !IFLAG)
{
ARMul_Abort (state, ARMul_IRQV);
break;
}
}
if (state->CallDebug > 0)
{
if (state->Emulate < ONCE)
{
state->NextInstr = RESUME;
break;
}
if (state->Debug)
{
fprintf (stderr, "sim: At %08lx Instr %08lx Mode %02lx\n",
(long) pc, (long) instr, (long) state->Mode);
(void) fgetc (stdin);
}
}
else if (state->Emulate < ONCE)
{
state->NextInstr = RESUME;
break;
}
state->NumInstrs++;
#ifdef MODET
/* Provide Thumb instruction decoding. If the processor is in Thumb
mode, then we can simply decode the Thumb instruction, and map it
to the corresponding ARM instruction (by directly loading the
instr variable, and letting the normal ARM simulator
execute). There are some caveats to ensure that the correct
pipelined PC value is used when executing Thumb code, and also for
dealing with the BL instruction. */
if (TFLAG)
{
ARMword new;
/* Check if in Thumb mode. */
switch (ARMul_ThumbDecode (state, pc, instr, &new))
{
case t_undefined:
/* This is a Thumb instruction. */
ARMul_UndefInstr (state, instr);
goto donext;
case t_branch:
/* Already processed. */
goto donext;
case t_decoded:
/* ARM instruction available. */
if (disas || trace)
{
fprintf (stderr, " emulate as: ");
if (trace)
fprintf (stderr, "%08x ", new);
if (! disas)
fprintf (stderr, "\n");
}
instr = new;
/* So continue instruction decoding. */
break;
default:
break;
}
}
#endif
if (disas)
print_insn (instr);
/* Check the condition codes. */
if ((temp = TOPBITS (28)) == AL)
/* Vile deed in the need for speed. */
goto mainswitch;
/* Check the condition code. */
switch ((int) TOPBITS (28))
{
case AL:
temp = TRUE;
break;
case NV:
if (state->is_v5)
{
if (BITS (25, 27) == 5) /* BLX(1) */
{
ARMword dest;
state->Reg[14] = pc + 4;
/* Force entry into Thumb mode. */
dest = pc + 8 + 1;
if (BIT (23))
dest += (NEGBRANCH + (BIT (24) << 1));
else
dest += POSBRANCH + (BIT (24) << 1);
WriteR15Branch (state, dest);
goto donext;
}
else if ((instr & 0xFC70F000) == 0xF450F000)
/* The PLD instruction. Ignored. */
goto donext;
else if ( ((instr & 0xfe500f00) == 0xfc100100)
|| ((instr & 0xfe500f00) == 0xfc000100))
/* wldrw and wstrw are unconditional. */
goto mainswitch;
else
/* UNDEFINED in v5, UNPREDICTABLE in v3, v4, non executed in v1, v2. */
ARMul_UndefInstr (state, instr);
}
temp = FALSE;
break;
case EQ:
temp = ZFLAG;
break;
case NE:
temp = !ZFLAG;
break;
case VS:
temp = VFLAG;
break;
case VC:
temp = !VFLAG;
break;
case MI:
temp = NFLAG;
break;
case PL:
temp = !NFLAG;
break;
case CS:
temp = CFLAG;
break;
case CC:
temp = !CFLAG;
break;
case HI:
temp = (CFLAG && !ZFLAG);
break;
case LS:
temp = (!CFLAG || ZFLAG);
break;
case GE:
temp = ((!NFLAG && !VFLAG) || (NFLAG && VFLAG));
break;
case LT:
temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG));
break;
case GT:
temp = ((!NFLAG && !VFLAG && !ZFLAG) || (NFLAG && VFLAG && !ZFLAG));
break;
case LE:
temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG)) || ZFLAG;
break;
} /* cc check */
/* Handle the Clock counter here. */
if (state->is_XScale)
{
ARMword cp14r0;
int ok;
ok = state->CPRead[14] (state, 0, & cp14r0);
if (ok && (cp14r0 & ARMul_CP14_R0_ENABLE))
{
unsigned long newcycles, nowtime = ARMul_Time (state);
newcycles = nowtime - state->LastTime;
state->LastTime = nowtime;
if (cp14r0 & ARMul_CP14_R0_CCD)
{
if (state->CP14R0_CCD == -1)
state->CP14R0_CCD = newcycles;
else
state->CP14R0_CCD += newcycles;
if (state->CP14R0_CCD >= 64)
{
newcycles = 0;
while (state->CP14R0_CCD >= 64)
state->CP14R0_CCD -= 64, newcycles++;
goto check_PMUintr;
}
}
else
{
ARMword cp14r1;
int do_int;
state->CP14R0_CCD = -1;
check_PMUintr:
do_int = 0;
cp14r0 |= ARMul_CP14_R0_FLAG2;
(void) state->CPWrite[14] (state, 0, cp14r0);
ok = state->CPRead[14] (state, 1, & cp14r1);
/* Coded like this for portability. */
while (ok && newcycles)
{
if (cp14r1 == 0xffffffff)
{
cp14r1 = 0;
do_int = 1;
}
else
cp14r1 ++;
newcycles --;
}
(void) state->CPWrite[14] (state, 1, cp14r1);
if (do_int && (cp14r0 & ARMul_CP14_R0_INTEN2))
{
ARMword temp;
if (state->CPRead[13] (state, 8, & temp)
&& (temp & ARMul_CP13_R8_PMUS))
ARMul_Abort (state, ARMul_FIQV);
else
ARMul_Abort (state, ARMul_IRQV);
}
}
}
}
/* Handle hardware instructions breakpoints here. */
if (state->is_XScale)
{
if ( (pc | 3) == (read_cp15_reg (14, 0, 8) | 2)
|| (pc | 3) == (read_cp15_reg (14, 0, 9) | 2))
{
if (XScale_debug_moe (state, ARMul_CP14_R10_MOE_IB))
ARMul_OSHandleSWI (state, SWI_Breakpoint);
}
}
/* Actual execution of instructions begins here. */
/* If the condition codes don't match, stop here. */
if (temp)
{
mainswitch:
if (state->is_XScale)
{
if (BIT (20) == 0 && BITS (25, 27) == 0)
{
if (BITS (4, 7) == 0xD)
{
/* XScale Load Consecutive insn. */
ARMword temp = GetLS7RHS (state, instr);
ARMword temp2 = BIT (23) ? LHS + temp : LHS - temp;
ARMword addr = BIT (24) ? temp2 : LHS;
if (BIT (12))
ARMul_UndefInstr (state, instr);
else if (addr & 7)
/* Alignment violation. */
ARMul_Abort (state, ARMul_DataAbortV);
else
{
int wb = BIT (21) || (! BIT (24));
state->Reg[BITS (12, 15)] =
ARMul_LoadWordN (state, addr);
state->Reg[BITS (12, 15) + 1] =
ARMul_LoadWordN (state, addr + 4);
if (wb)
LSBase = temp2;
}
goto donext;
}
else if (BITS (4, 7) == 0xF)
{
/* XScale Store Consecutive insn. */
ARMword temp = GetLS7RHS (state, instr);
ARMword temp2 = BIT (23) ? LHS + temp : LHS - temp;
ARMword addr = BIT (24) ? temp2 : LHS;
if (BIT (12))
ARMul_UndefInstr (state, instr);
else if (addr & 7)
/* Alignment violation. */
ARMul_Abort (state, ARMul_DataAbortV);
else
{
ARMul_StoreWordN (state, addr,
state->Reg[BITS (12, 15)]);
ARMul_StoreWordN (state, addr + 4,
state->Reg[BITS (12, 15) + 1]);
if (BIT (21)|| ! BIT (24))
LSBase = temp2;
}
goto donext;
}
}
if (ARMul_HandleIwmmxt (state, instr))
goto donext;
}
switch ((int) BITS (20, 27))
{
/* Data Processing Register RHS Instructions. */
case 0x00: /* AND reg and MUL */
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, no write-back, down, post indexed. */
SHDOWNWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
if (BITS (4, 7) == 9)
{
/* MUL */
rhs = state->Reg[MULRHSReg];
if (MULLHSReg == MULDESTReg)
{
UNDEF_MULDestEQOp1;
state->Reg[MULDESTReg] = 0;
}
else if (MULDESTReg != 15)
state->Reg[MULDESTReg] = state->Reg[MULLHSReg] * rhs;
else
UNDEF_MULPCDest;
for (dest = 0, temp = 0; dest < 32; dest ++)
if (rhs & (1L << dest))
temp = dest;
/* Mult takes this many/2 I cycles. */
ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
}
else
{
/* AND reg. */
rhs = DPRegRHS;
dest = LHS & rhs;
WRITEDEST (dest);
}
break;
case 0x01: /* ANDS reg and MULS */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, no write-back, down, post indexed. */
LHPOSTDOWN ();
/* Fall through to rest of decoding. */
#endif
if (BITS (4, 7) == 9)
{
/* MULS */
rhs = state->Reg[MULRHSReg];
if (MULLHSReg == MULDESTReg)
{
UNDEF_MULDestEQOp1;
state->Reg[MULDESTReg] = 0;
CLEARN;
SETZ;
}
else if (MULDESTReg != 15)
{
dest = state->Reg[MULLHSReg] * rhs;
ARMul_NegZero (state, dest);
state->Reg[MULDESTReg] = dest;
}
else
UNDEF_MULPCDest;
for (dest = 0, temp = 0; dest < 32; dest ++)
if (rhs & (1L << dest))
temp = dest;
/* Mult takes this many/2 I cycles. */
ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
}
else
{
/* ANDS reg. */
rhs = DPSRegRHS;
dest = LHS & rhs;
WRITESDEST (dest);
}
break;
case 0x02: /* EOR reg and MLA */
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, write-back, down, post indexed. */
SHDOWNWB ();
break;
}
#endif
if (BITS (4, 7) == 9)
{ /* MLA */
rhs = state->Reg[MULRHSReg];
if (MULLHSReg == MULDESTReg)
{
UNDEF_MULDestEQOp1;
state->Reg[MULDESTReg] = state->Reg[MULACCReg];
}
else if (MULDESTReg != 15)
state->Reg[MULDESTReg] =
state->Reg[MULLHSReg] * rhs + state->Reg[MULACCReg];
else
UNDEF_MULPCDest;
for (dest = 0, temp = 0; dest < 32; dest ++)
if (rhs & (1L << dest))
temp = dest;
/* Mult takes this many/2 I cycles. */
ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
}
else
{
rhs = DPRegRHS;
dest = LHS ^ rhs;
WRITEDEST (dest);
}
break;
case 0x03: /* EORS reg and MLAS */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, write-back, down, post-indexed. */
LHPOSTDOWN ();
/* Fall through to rest of the decoding. */
#endif
if (BITS (4, 7) == 9)
{
/* MLAS */
rhs = state->Reg[MULRHSReg];
if (MULLHSReg == MULDESTReg)
{
UNDEF_MULDestEQOp1;
dest = state->Reg[MULACCReg];
ARMul_NegZero (state, dest);
state->Reg[MULDESTReg] = dest;
}
else if (MULDESTReg != 15)
{
dest =
state->Reg[MULLHSReg] * rhs + state->Reg[MULACCReg];
ARMul_NegZero (state, dest);
state->Reg[MULDESTReg] = dest;
}
else
UNDEF_MULPCDest;
for (dest = 0, temp = 0; dest < 32; dest ++)
if (rhs & (1L << dest))
temp = dest;
/* Mult takes this many/2 I cycles. */
ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
}
else
{
/* EORS Reg. */
rhs = DPSRegRHS;
dest = LHS ^ rhs;
WRITESDEST (dest);
}
break;
case 0x04: /* SUB reg */
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, no write-back, down, post indexed. */
SHDOWNWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
rhs = DPRegRHS;
dest = LHS - rhs;
WRITEDEST (dest);
break;
case 0x05: /* SUBS reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, no write-back, down, post indexed. */
LHPOSTDOWN ();
/* Fall through to the rest of the instruction decoding. */
#endif
lhs = LHS;
rhs = DPRegRHS;
dest = lhs - rhs;
if ((lhs >= rhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, lhs, rhs, dest);
ARMul_SubOverflow (state, lhs, rhs, dest);
}
else
{
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x06: /* RSB reg */
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, write-back, down, post indexed. */
SHDOWNWB ();
break;
}
#endif
rhs = DPRegRHS;
dest = rhs - LHS;
WRITEDEST (dest);
break;
case 0x07: /* RSBS reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, write-back, down, post indexed. */
LHPOSTDOWN ();
/* Fall through to remainder of instruction decoding. */
#endif
lhs = LHS;
rhs = DPRegRHS;
dest = rhs - lhs;
if ((rhs >= lhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, rhs, lhs, dest);
ARMul_SubOverflow (state, rhs, lhs, dest);
}
else
{
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x08: /* ADD reg */
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, no write-back, up, post indexed. */
SHUPWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
#ifdef MODET
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32 = 64 */
ARMul_Icycles (state,
Multiply64 (state, instr, LUNSIGNED,
LDEFAULT), 0L);
break;
}
#endif
rhs = DPRegRHS;
dest = LHS + rhs;
WRITEDEST (dest);
break;
case 0x09: /* ADDS reg */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, no write-back, up, post indexed. */
LHPOSTUP ();
/* Fall through to remaining instruction decoding. */
#endif
#ifdef MODET
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
Multiply64 (state, instr, LUNSIGNED, LSCC),
0L);
break;
}
#endif
lhs = LHS;
rhs = DPRegRHS;
dest = lhs + rhs;
ASSIGNZ (dest == 0);
if ((lhs | rhs) >> 30)
{
/* Possible C,V,N to set. */
ASSIGNN (NEG (dest));
ARMul_AddCarry (state, lhs, rhs, dest);
ARMul_AddOverflow (state, lhs, rhs, dest);
}
else
{
CLEARN;
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x0a: /* ADC reg */
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, write-back, up, post-indexed. */
SHUPWB ();
break;
}
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
MultiplyAdd64 (state, instr, LUNSIGNED,
LDEFAULT), 0L);
break;
}
#endif
rhs = DPRegRHS;
dest = LHS + rhs + CFLAG;
WRITEDEST (dest);
break;
case 0x0b: /* ADCS reg */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, write-back, up, post indexed. */
LHPOSTUP ();
/* Fall through to remaining instruction decoding. */
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
MultiplyAdd64 (state, instr, LUNSIGNED,
LSCC), 0L);
break;
}
#endif
lhs = LHS;
rhs = DPRegRHS;
dest = lhs + rhs + CFLAG;
ASSIGNZ (dest == 0);
if ((lhs | rhs) >> 30)
{
/* Possible C,V,N to set. */
ASSIGNN (NEG (dest));
ARMul_AddCarry (state, lhs, rhs, dest);
ARMul_AddOverflow (state, lhs, rhs, dest);
}
else
{
CLEARN;
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x0c: /* SBC reg */
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, no write-back, up post indexed. */
SHUPWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
Multiply64 (state, instr, LSIGNED, LDEFAULT),
0L);
break;
}
#endif
rhs = DPRegRHS;
dest = LHS - rhs - !CFLAG;
WRITEDEST (dest);
break;
case 0x0d: /* SBCS reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, no write-back, up, post indexed. */
LHPOSTUP ();
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
Multiply64 (state, instr, LSIGNED, LSCC),
0L);
break;
}
#endif
lhs = LHS;
rhs = DPRegRHS;
dest = lhs - rhs - !CFLAG;
if ((lhs >= rhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, lhs, rhs, dest);
ARMul_SubOverflow (state, lhs, rhs, dest);
}
else
{
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x0e: /* RSC reg */
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, write-back, up, post indexed. */
SHUPWB ();
break;
}
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
MultiplyAdd64 (state, instr, LSIGNED,
LDEFAULT), 0L);
break;
}
#endif
rhs = DPRegRHS;
dest = rhs - LHS - !CFLAG;
WRITEDEST (dest);
break;
case 0x0f: /* RSCS reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, write-back, up, post indexed. */
LHPOSTUP ();
/* Fall through to remaining instruction decoding. */
if (BITS (4, 7) == 0x9)
{
/* MULL */
/* 32x32=64 */
ARMul_Icycles (state,
MultiplyAdd64 (state, instr, LSIGNED, LSCC),
0L);
break;
}
#endif
lhs = LHS;
rhs = DPRegRHS;
dest = rhs - lhs - !CFLAG;
if ((rhs >= lhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, rhs, lhs, dest);
ARMul_SubOverflow (state, rhs, lhs, dest);
}
else
{
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x10: /* TST reg and MRS CPSR and SWP word. */
if (state->is_v5e)
{
if (BIT (4) == 0 && BIT (7) == 1)
{
/* ElSegundo SMLAxy insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
ARMword op2 = state->Reg[BITS (8, 11)];
ARMword Rn = state->Reg[BITS (12, 15)];
if (BIT (5))
op1 >>= 16;
if (BIT (6))
op2 >>= 16;
op1 &= 0xFFFF;
op2 &= 0xFFFF;
if (op1 & 0x8000)
op1 -= 65536;
if (op2 & 0x8000)
op2 -= 65536;
op1 *= op2;
if (AddOverflow (op1, Rn, op1 + Rn))
SETS;
state->Reg[BITS (16, 19)] = op1 + Rn;
break;
}
if (BITS (4, 11) == 5)
{
/* ElSegundo QADD insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
ARMword op2 = state->Reg[BITS (16, 19)];
ARMword result = op1 + op2;
if (AddOverflow (op1, op2, result))
{
result = POS (result) ? 0x80000000 : 0x7fffffff;
SETS;
}
state->Reg[BITS (12, 15)] = result;
break;
}
}
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, no write-back, down, pre indexed. */
SHPREDOWN ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
if (BITS (4, 11) == 9)
{
/* SWP */
UNDEF_SWPPC;
temp = LHS;
BUSUSEDINCPCS;
#ifndef MODE32
if (VECTORACCESS (temp) || ADDREXCEPT (temp))
{
INTERNALABORT (temp);
(void) ARMul_LoadWordN (state, temp);
(void) ARMul_LoadWordN (state, temp);
}
else
#endif
dest = ARMul_SwapWord (state, temp, state->Reg[RHSReg]);
if (temp & 3)
DEST = ARMul_Align (state, temp, dest);
else
DEST = dest;
if (state->abortSig || state->Aborted)
TAKEABORT;
}
else if ((BITS (0, 11) == 0) && (LHSReg == 15))
{ /* MRS CPSR */
UNDEF_MRSPC;
DEST = ECC | EINT | EMODE;
}
else
{
#ifdef MODE32
if (state->is_v6
&& handle_v6_insn (state, instr))
break;
#endif
UNDEF_Test;
}
break;
case 0x11: /* TSTP reg */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, no write-back, down, pre indexed. */
LHPREDOWN ();
/* Continue with remaining instruction decode. */
#endif
if (DESTReg == 15)
{
/* TSTP reg */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS & rhs;
SETR15PSR (temp);
#endif
}
else
{
/* TST reg */
rhs = DPSRegRHS;
dest = LHS & rhs;
ARMul_NegZero (state, dest);
}
break;
case 0x12: /* TEQ reg and MSR reg to CPSR (ARM6). */
if (state->is_v5)
{
if (BITS (4, 7) == 3)
{
/* BLX(2) */
ARMword temp;
if (TFLAG)
temp = (pc + 2) | 1;
else
temp = pc + 4;
WriteR15Branch (state, state->Reg[RHSReg]);
state->Reg[14] = temp;
break;
}
}
if (state->is_v5e)
{
if (BIT (4) == 0 && BIT (7) == 1
&& (BIT (5) == 0 || BITS (12, 15) == 0))
{
/* ElSegundo SMLAWy/SMULWy insn. */
ARMdword op1 = state->Reg[BITS (0, 3)];
ARMdword op2 = state->Reg[BITS (8, 11)];
ARMdword result;
if (BIT (6))
op2 >>= 16;
if (op1 & 0x80000000)
op1 -= 1ULL << 32;
op2 &= 0xFFFF;
if (op2 & 0x8000)
op2 -= 65536;
result = (op1 * op2) >> 16;
if (BIT (5) == 0)
{
ARMword Rn = state->Reg[BITS (12, 15)];
if (AddOverflow (result, Rn, result + Rn))
SETS;
result += Rn;
}
state->Reg[BITS (16, 19)] = result;
break;
}
if (BITS (4, 11) == 5)
{
/* ElSegundo QSUB insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
ARMword op2 = state->Reg[BITS (16, 19)];
ARMword result = op1 - op2;
if (SubOverflow (op1, op2, result))
{
result = POS (result) ? 0x80000000 : 0x7fffffff;
SETS;
}
state->Reg[BITS (12, 15)] = result;
break;
}
}
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, write-back, down, pre indexed. */
SHPREDOWNWB ();
break;
}
if (BITS (4, 27) == 0x12FFF1)
{
/* BX */
WriteR15Branch (state, state->Reg[RHSReg]);
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
if (state->is_v5)
{
if (BITS (4, 7) == 0x7)
{
extern int SWI_vector_installed;
/* Hardware is allowed to optionally override this
instruction and treat it as a breakpoint. Since
this is a simulator not hardware, we take the position
that if a SWI vector was not installed, then an Abort
vector was probably not installed either, and so
normally this instruction would be ignored, even if an
Abort is generated. This is a bad thing, since GDB
uses this instruction for its breakpoints (at least in
Thumb mode it does). So intercept the instruction here
and generate a breakpoint SWI instead. */
if (! SWI_vector_installed)
ARMul_OSHandleSWI (state, SWI_Breakpoint);
else
{
/* BKPT - normally this will cause an abort, but on the
XScale we must check the DCSR. */
XScale_set_fsr_far (state, ARMul_CP15_R5_MMU_EXCPT, pc);
if (!XScale_debug_moe (state, ARMul_CP14_R10_MOE_BT))
break;
}
/* Force the next instruction to be refetched. */
state->NextInstr = RESUME;
break;
}
}
if (DESTReg == 15)
{
/* MSR reg to CPSR. */
UNDEF_MSRPC;
temp = DPRegRHS;
#ifdef MODET
/* Don't allow TBIT to be set by MSR. */
temp &= ~ TBIT;
#endif
ARMul_FixCPSR (state, instr, temp);
}
#ifdef MODE32
else if (state->is_v6
&& handle_v6_insn (state, instr))
break;
#endif
else
UNDEF_Test;
break;
case 0x13: /* TEQP reg */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, write-back, down, pre indexed. */
LHPREDOWNWB ();
/* Continue with remaining instruction decode. */
#endif
if (DESTReg == 15)
{
/* TEQP reg */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS ^ rhs;
SETR15PSR (temp);
#endif
}
else
{
/* TEQ Reg. */
rhs = DPSRegRHS;
dest = LHS ^ rhs;
ARMul_NegZero (state, dest);
}
break;
case 0x14: /* CMP reg and MRS SPSR and SWP byte. */
if (state->is_v5e)
{
if (BIT (4) == 0 && BIT (7) == 1)
{
/* ElSegundo SMLALxy insn. */
ARMdword op1 = state->Reg[BITS (0, 3)];
ARMdword op2 = state->Reg[BITS (8, 11)];
ARMdword dest;
if (BIT (5))
op1 >>= 16;
if (BIT (6))
op2 >>= 16;
op1 &= 0xFFFF;
if (op1 & 0x8000)
op1 -= 65536;
op2 &= 0xFFFF;
if (op2 & 0x8000)
op2 -= 65536;
dest = (ARMdword) state->Reg[BITS (16, 19)] << 32;
dest |= state->Reg[BITS (12, 15)];
dest += op1 * op2;
state->Reg[BITS (12, 15)] = dest;
state->Reg[BITS (16, 19)] = dest >> 32;
break;
}
if (BITS (4, 11) == 5)
{
/* ElSegundo QDADD insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
ARMword op2 = state->Reg[BITS (16, 19)];
ARMword op2d = op2 + op2;
ARMword result;
if (AddOverflow (op2, op2, op2d))
{
SETS;
op2d = POS (op2d) ? 0x80000000 : 0x7fffffff;
}
result = op1 + op2d;
if (AddOverflow (op1, op2d, result))
{
SETS;
result = POS (result) ? 0x80000000 : 0x7fffffff;
}
state->Reg[BITS (12, 15)] = result;
break;
}
}
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, no write-back, down, pre indexed. */
SHPREDOWN ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
if (BITS (4, 11) == 9)
{
/* SWP */
UNDEF_SWPPC;
temp = LHS;
BUSUSEDINCPCS;
#ifndef MODE32
if (VECTORACCESS (temp) || ADDREXCEPT (temp))
{
INTERNALABORT (temp);
(void) ARMul_LoadByte (state, temp);
(void) ARMul_LoadByte (state, temp);
}
else
#endif
DEST = ARMul_SwapByte (state, temp, state->Reg[RHSReg]);
if (state->abortSig || state->Aborted)
TAKEABORT;
}
else if ((BITS (0, 11) == 0) && (LHSReg == 15))
{
/* MRS SPSR */
UNDEF_MRSPC;
DEST = GETSPSR (state->Bank);
}
#ifdef MODE32
else if (state->is_v6
&& handle_v6_insn (state, instr))
break;
#endif
else
UNDEF_Test;
break;
case 0x15: /* CMPP reg. */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, no write-back, down, pre indexed. */
LHPREDOWN ();
/* Continue with remaining instruction decode. */
#endif
if (DESTReg == 15)
{
/* CMPP reg. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS - rhs;
SETR15PSR (temp);
#endif
}
else
{
/* CMP reg. */
lhs = LHS;
rhs = DPRegRHS;
dest = lhs - rhs;
ARMul_NegZero (state, dest);
if ((lhs >= rhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, lhs, rhs, dest);
ARMul_SubOverflow (state, lhs, rhs, dest);
}
else
{
CLEARC;
CLEARV;
}
}
break;
case 0x16: /* CMN reg and MSR reg to SPSR */
if (state->is_v5e)
{
if (BIT (4) == 0 && BIT (7) == 1 && BITS (12, 15) == 0)
{
/* ElSegundo SMULxy insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
ARMword op2 = state->Reg[BITS (8, 11)];
if (BIT (5))
op1 >>= 16;
if (BIT (6))
op2 >>= 16;
op1 &= 0xFFFF;
op2 &= 0xFFFF;
if (op1 & 0x8000)
op1 -= 65536;
if (op2 & 0x8000)
op2 -= 65536;
state->Reg[BITS (16, 19)] = op1 * op2;
break;
}
if (BITS (4, 11) == 5)
{
/* ElSegundo QDSUB insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
ARMword op2 = state->Reg[BITS (16, 19)];
ARMword op2d = op2 + op2;
ARMword result;
if (AddOverflow (op2, op2, op2d))
{
SETS;
op2d = POS (op2d) ? 0x80000000 : 0x7fffffff;
}
result = op1 - op2d;
if (SubOverflow (op1, op2d, result))
{
SETS;
result = POS (result) ? 0x80000000 : 0x7fffffff;
}
state->Reg[BITS (12, 15)] = result;
break;
}
}
if (state->is_v5)
{
if (BITS (4, 11) == 0xF1 && BITS (16, 19) == 0xF)
{
/* ARM5 CLZ insn. */
ARMword op1 = state->Reg[BITS (0, 3)];
int result = 32;
if (op1)
for (result = 0; (op1 & 0x80000000) == 0; op1 <<= 1)
result++;
state->Reg[BITS (12, 15)] = result;
break;
}
}
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, write-back, down, pre indexed. */
SHPREDOWNWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
if (DESTReg == 15)
{
/* MSR */
UNDEF_MSRPC;
ARMul_FixSPSR (state, instr, DPRegRHS);
}
else
{
#ifdef MODE32
if (state->is_v6
&& handle_v6_insn (state, instr))
break;
#endif
UNDEF_Test;
}
break;
case 0x17: /* CMNP reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, write-back, down, pre indexed. */
LHPREDOWNWB ();
/* Continue with remaining instruction decoding. */
#endif
if (DESTReg == 15)
{
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS + rhs;
SETR15PSR (temp);
#endif
break;
}
else
{
/* CMN reg. */
lhs = LHS;
rhs = DPRegRHS;
dest = lhs + rhs;
ASSIGNZ (dest == 0);
if ((lhs | rhs) >> 30)
{
/* Possible C,V,N to set. */
ASSIGNN (NEG (dest));
ARMul_AddCarry (state, lhs, rhs, dest);
ARMul_AddOverflow (state, lhs, rhs, dest);
}
else
{
CLEARN;
CLEARC;
CLEARV;
}
}
break;
case 0x18: /* ORR reg */
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, no write-back, up, pre indexed. */
SHPREUP ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
rhs = DPRegRHS;
dest = LHS | rhs;
WRITEDEST (dest);
break;
case 0x19: /* ORRS reg */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, no write-back, up, pre indexed. */
LHPREUP ();
/* Continue with remaining instruction decoding. */
#endif
rhs = DPSRegRHS;
dest = LHS | rhs;
WRITESDEST (dest);
break;
case 0x1a: /* MOV reg */
#ifdef MODET
if (BITS (4, 11) == 0xB)
{
/* STRH register offset, write-back, up, pre indexed. */
SHPREUPWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
dest = DPRegRHS;
WRITEDEST (dest);
break;
case 0x1b: /* MOVS reg */
#ifdef MODET
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, write-back, up, pre indexed. */
LHPREUPWB ();
/* Continue with remaining instruction decoding. */
#endif
dest = DPSRegRHS;
WRITESDEST (dest);
break;
case 0x1c: /* BIC reg */
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, no write-back, up, pre indexed. */
SHPREUP ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
else if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
rhs = DPRegRHS;
dest = LHS & ~rhs;
WRITEDEST (dest);
break;
case 0x1d: /* BICS reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, no write-back, up, pre indexed. */
LHPREUP ();
/* Continue with instruction decoding. */
#endif
rhs = DPSRegRHS;
dest = LHS & ~rhs;
WRITESDEST (dest);
break;
case 0x1e: /* MVN reg */
#ifdef MODET
if (BITS (4, 7) == 0xB)
{
/* STRH immediate offset, write-back, up, pre indexed. */
SHPREUPWB ();
break;
}
if (BITS (4, 7) == 0xD)
{
Handle_Load_Double (state, instr);
break;
}
if (BITS (4, 7) == 0xF)
{
Handle_Store_Double (state, instr);
break;
}
#endif
dest = ~DPRegRHS;
WRITEDEST (dest);
break;
case 0x1f: /* MVNS reg */
#ifdef MODET
if ((BITS (4, 7) & 0x9) == 0x9)
/* LDR immediate offset, write-back, up, pre indexed. */
LHPREUPWB ();
/* Continue instruction decoding. */
#endif
dest = ~DPSRegRHS;
WRITESDEST (dest);
break;
/* Data Processing Immediate RHS Instructions. */
case 0x20: /* AND immed */
dest = LHS & DPImmRHS;
WRITEDEST (dest);
break;
case 0x21: /* ANDS immed */
DPSImmRHS;
dest = LHS & rhs;
WRITESDEST (dest);
break;
case 0x22: /* EOR immed */
dest = LHS ^ DPImmRHS;
WRITEDEST (dest);
break;
case 0x23: /* EORS immed */
DPSImmRHS;
dest = LHS ^ rhs;
WRITESDEST (dest);
break;
case 0x24: /* SUB immed */
dest = LHS - DPImmRHS;
WRITEDEST (dest);
break;
case 0x25: /* SUBS immed */
lhs = LHS;
rhs = DPImmRHS;
dest = lhs - rhs;
if ((lhs >= rhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, lhs, rhs, dest);
ARMul_SubOverflow (state, lhs, rhs, dest);
}
else
{
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x26: /* RSB immed */
dest = DPImmRHS - LHS;
WRITEDEST (dest);
break;
case 0x27: /* RSBS immed */
lhs = LHS;
rhs = DPImmRHS;
dest = rhs - lhs;
if ((rhs >= lhs) || ((rhs | lhs) >> 31))
{
ARMul_SubCarry (state, rhs, lhs, dest);
ARMul_SubOverflow (state, rhs, lhs, dest);
}
else
{
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x28: /* ADD immed */
dest = LHS + DPImmRHS;
WRITEDEST (dest);
break;
case 0x29: /* ADDS immed */
lhs = LHS;
rhs = DPImmRHS;
dest = lhs + rhs;
ASSIGNZ (dest == 0);
if ((lhs | rhs) >> 30)
{
/* Possible C,V,N to set. */
ASSIGNN (NEG (dest));
ARMul_AddCarry (state, lhs, rhs, dest);
ARMul_AddOverflow (state, lhs, rhs, dest);
}
else
{
CLEARN;
CLEARC;
CLEARV;
}
WRITESDEST (dest);
break;
case 0x2a</