diff options
Diffstat (limited to 'sim/arm/iwmmxt.c')
-rw-r--r-- | sim/arm/iwmmxt.c | 3730 |
1 files changed, 3730 insertions, 0 deletions
diff --git a/sim/arm/iwmmxt.c b/sim/arm/iwmmxt.c new file mode 100644 index 0000000..72444f6 --- /dev/null +++ b/sim/arm/iwmmxt.c @@ -0,0 +1,3730 @@ +/* iwmmxt.c -- Intel(r) Wireless MMX(tm) technology co-processor interface. + Copyright (C) 2002 Free Software Foundation, Inc. + Contributed by matthew green (mrg@redhat.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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "armdefs.h" +#include "armos.h" +#include "armemu.h" +#include "ansidecl.h" +#include "iwmmxt.h" + +/* #define DEBUG 1 */ + +/* Intel(r) Wireless MMX(tm) technology co-processor. + It uses co-processor numbers (0 and 1). There are 16 vector registers wRx + and 16 control registers wCx. Co-processors 0 and 1 are used in MCR/MRC + to access wRx and wCx respectively. */ + +static ARMdword wR[16]; +static ARMword wC[16] = { 0x69051010 }; + +#define SUBSTR(w,t,m,n) ((t)(w << ((sizeof (t) * 8 - 1) - (n))) \ + >> (((sizeof (t) * 8 - 1) - (n)) + (m))) +#define wCBITS(w,x,y) SUBSTR (wC[w], ARMword, x, y) +#define wRBITS(w,x,y) SUBSTR (wR[w], ARMdword, x, y) +#define wCID 0 +#define wCon 1 +#define wCSSF 2 +#define wCASF 3 +#define wCGR0 8 +#define wCGR1 9 +#define wCGR2 10 +#define wCGR3 11 + +/* Bits in the wCon register. */ +#define WCON_CUP (1 << 0) +#define WCON_MUP (1 << 1) + +/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ +#define SIMD8_SET(x, v, n, b) (x) |= ((v != 0) << ((((b) + 1) * 4) + (n))) +#define SIMD16_SET(x, v, n, h) (x) |= ((v != 0) << ((((h) + 1) * 8) + (n))) +#define SIMD32_SET(x, v, n, w) (x) |= ((v != 0) << ((((w) + 1) * 16) + (n))) +#define SIMD64_SET(x, v, n) (x) |= ((v != 0) << (32 + (n))) + +/* Flags to pass as "n" above. */ +#define SIMD_NBIT -1 +#define SIMD_ZBIT -2 +#define SIMD_CBIT -3 +#define SIMD_VBIT -4 + +/* Various status bit macros. */ +#define NBIT8(x) ((x) & 0x80) +#define NBIT16(x) ((x) & 0x8000) +#define NBIT32(x) ((x) & 0x80000000) +#define NBIT64(x) ((x) & 0x8000000000000000ULL) +#define ZBIT8(x) (((x) & 0xff) == 0) +#define ZBIT16(x) (((x) & 0xffff) == 0) +#define ZBIT32(x) (((x) & 0xffffffff) == 0) +#define ZBIT64(x) (x == 0) + +/* Access byte/half/word "n" of register "x". */ +#define wRBYTE(x,n) wRBITS ((x), (n) * 8, (n) * 8 + 7) +#define wRHALF(x,n) wRBITS ((x), (n) * 16, (n) * 16 + 15) +#define wRWORD(x,n) wRBITS ((x), (n) * 32, (n) * 32 + 31) + +/* Macro to handle how the G bit selects wCGR registers. */ +#define DECODE_G_BIT(state, instr, shift) \ +{ \ + unsigned int reg; \ + \ + reg = BITS (0, 3); \ + \ + if (BIT (8)) /* G */ \ + { \ + if (reg < wCGR0 || reg > wCGR3) \ + { \ + ARMul_UndefInstr (state, instr); \ + return ARMul_DONE; \ + } \ + shift = wC [reg]; \ + } \ + else \ + shift = wR [reg]; \ + \ + shift &= 0xff; \ +} + +/* Index calculations for the satrv[] array. */ +#define BITIDX8(x) (x) +#define BITIDX16(x) (((x) + 1) * 2 - 1) +#define BITIDX32(x) (((x) + 1) * 4 - 1) + +/* Sign extension macros. */ +#define EXTEND8(a) ((a) & 0x80 ? ((a) | 0xffffff00) : (a)) +#define EXTEND16(a) ((a) & 0x8000 ? ((a) | 0xffff0000) : (a)) +#define EXTEND32(a) ((a) & 0x80000000ULL ? ((a) | 0xffffffff00000000ULL) : (a)) + +/* Set the wCSSF from 8 values. */ +#define SET_wCSSF(a,b,c,d,e,f,g,h) \ + wC[wCSSF] = (((h) != 0) << 7) | (((g) != 0) << 6) \ + | (((f) != 0) << 5) | (((e) != 0) << 4) \ + | (((d) != 0) << 3) | (((c) != 0) << 2) \ + | (((b) != 0) << 1) | (((a) != 0) << 0); + +/* Set the wCSSR from an array with 8 values. */ +#define SET_wCSSFvec(v) \ + SET_wCSSF((v)[0],(v)[1],(v)[2],(v)[3],(v)[4],(v)[5],(v)[6],(v)[7]) + +/* Size qualifiers for vector operations. */ +#define Bqual 0 +#define Hqual 1 +#define Wqual 2 +#define Dqual 3 + +/* Saturation qualifiers for vector operations. */ +#define NoSaturation 0 +#define UnsignedSaturation 1 +#define SignedSaturation 3 + + +/* Prototypes. */ +static ARMword Add32 (ARMword, ARMword, int *, int *, ARMword); +static ARMdword AddS32 (ARMdword, ARMdword, int *, int *); +static ARMdword AddU32 (ARMdword, ARMdword, int *, int *); +static ARMword AddS16 (ARMword, ARMword, int *, int *); +static ARMword AddU16 (ARMword, ARMword, int *, int *); +static ARMword AddS8 (ARMword, ARMword, int *, int *); +static ARMword AddU8 (ARMword, ARMword, int *, int *); +static ARMword Sub32 (ARMword, ARMword, int *, int *, ARMword); +static ARMdword SubS32 (ARMdword, ARMdword, int *, int *); +static ARMdword SubU32 (ARMdword, ARMdword, int *, int *); +static ARMword SubS16 (ARMword, ARMword, int *, int *); +static ARMword SubS8 (ARMword, ARMword, int *, int *); +static ARMword SubU16 (ARMword, ARMword, int *, int *); +static ARMword SubU8 (ARMword, ARMword, int *, int *); +static unsigned char IwmmxtSaturateU8 (signed short, int *); +static signed char IwmmxtSaturateS8 (signed short, int *); +static unsigned short IwmmxtSaturateU16 (signed int, int *); +static signed short IwmmxtSaturateS16 (signed int, int *); +static unsigned long IwmmxtSaturateU32 (signed long long, int *); +static signed long IwmmxtSaturateS32 (signed long long, int *); +static ARMword Compute_Iwmmxt_Address (ARMul_State *, ARMword, int *); +static ARMdword Iwmmxt_Load_Double_Word (ARMul_State *, ARMword); +static ARMword Iwmmxt_Load_Word (ARMul_State *, ARMword); +static ARMword Iwmmxt_Load_Half_Word (ARMul_State *, ARMword); +static ARMword Iwmmxt_Load_Byte (ARMul_State *, ARMword); +static void Iwmmxt_Store_Double_Word (ARMul_State *, ARMword, ARMdword); +static void Iwmmxt_Store_Word (ARMul_State *, ARMword, ARMword); +static void Iwmmxt_Store_Half_Word (ARMul_State *, ARMword, ARMword); +static void Iwmmxt_Store_Byte (ARMul_State *, ARMword, ARMword); +static int Process_Instruction (ARMul_State *, ARMword); + +static int TANDC (ARMul_State *, ARMword); +static int TBCST (ARMul_State *, ARMword); +static int TEXTRC (ARMul_State *, ARMword); +static int TEXTRM (ARMul_State *, ARMword); +static int TINSR (ARMul_State *, ARMword); +static int TMCR (ARMul_State *, ARMword); +static int TMCRR (ARMul_State *, ARMword); +static int TMIA (ARMul_State *, ARMword); +static int TMIAPH (ARMul_State *, ARMword); +static int TMIAxy (ARMul_State *, ARMword); +static int TMOVMSK (ARMul_State *, ARMword); +static int TMRC (ARMul_State *, ARMword); +static int TMRRC (ARMul_State *, ARMword); +static int TORC (ARMul_State *, ARMword); +static int WACC (ARMul_State *, ARMword); +static int WADD (ARMul_State *, ARMword); +static int WALIGNI (ARMword); +static int WALIGNR (ARMul_State *, ARMword); +static int WAND (ARMword); +static int WANDN (ARMword); +static int WAVG2 (ARMword); +static int WCMPEQ (ARMul_State *, ARMword); +static int WCMPGT (ARMul_State *, ARMword); +static int WLDR (ARMul_State *, ARMword); +static int WMAC (ARMword); +static int WMADD (ARMword); +static int WMAX (ARMul_State *, ARMword); +static int WMIN (ARMul_State *, ARMword); +static int WMUL (ARMword); +static int WOR (ARMword); +static int WPACK (ARMul_State *, ARMword); +static int WROR (ARMul_State *, ARMword); +static int WSAD (ARMword); +static int WSHUFH (ARMword); +static int WSLL (ARMul_State *, ARMword); +static int WSRA (ARMul_State *, ARMword); +static int WSRL (ARMul_State *, ARMword); +static int WSTR (ARMul_State *, ARMword); +static int WSUB (ARMul_State *, ARMword); +static int WUNPCKEH (ARMul_State *, ARMword); +static int WUNPCKEL (ARMul_State *, ARMword); +static int WUNPCKIH (ARMul_State *, ARMword); +static int WUNPCKIL (ARMul_State *, ARMword); +static int WXOR (ARMword); + +/* This function does the work of adding two 32bit values + together, and calculating if a carry has occurred. */ + +static ARMword +Add32 (ARMword a1, + ARMword a2, + int * carry_ptr, + int * overflow_ptr, + ARMword sign_mask) +{ + ARMword result = (a1 + a2); + unsigned int uresult = (unsigned int) result; + unsigned int ua1 = (unsigned int) a1; + + /* If (result == a1) and (a2 == 0), + or (result > a2) then we have no carry. */ + * carry_ptr = ((uresult == ua1) ? (a2 != 0) : (uresult < ua1)); + + /* Overflow occurs when both arguments are the + same sign, but the result is a different sign. */ + * overflow_ptr = ( ( (result & sign_mask) && !(a1 & sign_mask) && !(a2 & sign_mask)) + || (!(result & sign_mask) && (a1 & sign_mask) && (a2 & sign_mask))); + + return result; +} + +static ARMdword +AddS32 (ARMdword a1, ARMdword a2, int * carry_ptr, int * overflow_ptr) +{ + ARMdword result; + unsigned int uresult; + unsigned int ua1; + + a1 = EXTEND32 (a1); + a2 = EXTEND32 (a2); + + result = a1 + a2; + uresult = (unsigned int) result; + ua1 = (unsigned int) a1; + + * carry_ptr = ((uresult == a1) ? (a2 != 0) : (uresult < ua1)); + + * overflow_ptr = ( ( (result & 0x80000000ULL) && !(a1 & 0x80000000ULL) && !(a2 & 0x80000000ULL)) + || (!(result & 0x80000000ULL) && (a1 & 0x80000000ULL) && (a2 & 0x80000000ULL))); + + return result; +} + +static ARMdword +AddU32 (ARMdword a1, ARMdword a2, int * carry_ptr, int * overflow_ptr) +{ + ARMdword result; + unsigned int uresult; + unsigned int ua1; + + a1 &= 0xffffffff; + a2 &= 0xffffffff; + + result = a1 + a2; + uresult = (unsigned int) result; + ua1 = (unsigned int) a1; + + * carry_ptr = ((uresult == a1) ? (a2 != 0) : (uresult < ua1)); + + * overflow_ptr = ( ( (result & 0x80000000ULL) && !(a1 & 0x80000000ULL) && !(a2 & 0x80000000ULL)) + || (!(result & 0x80000000ULL) && (a1 & 0x80000000ULL) && (a2 & 0x80000000ULL))); + + return result; +} + +static ARMword +AddS16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 = EXTEND16 (a1); + a2 = EXTEND16 (a2); + + return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x8000); +} + +static ARMword +AddU16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 &= 0xffff; + a2 &= 0xffff; + + return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x8000); +} + +static ARMword +AddS8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 = EXTEND8 (a1); + a2 = EXTEND8 (a2); + + return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x80); +} + +static ARMword +AddU8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 &= 0xff; + a2 &= 0xff; + + return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x80); +} + +static ARMword +Sub32 (ARMword a1, + ARMword a2, + int * borrow_ptr, + int * overflow_ptr, + ARMword sign_mask) +{ + ARMword result = (a1 - a2); + unsigned int ua1 = (unsigned int) a1; + unsigned int ua2 = (unsigned int) a2; + + /* A borrow occurs if a2 is (unsigned) larger than a1. + However the carry flag is *cleared* if a borrow occurs. */ + * borrow_ptr = ! (ua2 > ua1); + + /* Overflow occurs when a negative number is subtracted from a + positive number and the result is negative or a positive + number is subtracted from a negative number and the result is + positive. */ + * overflow_ptr = ( (! (a1 & sign_mask) && (a2 & sign_mask) && (result & sign_mask)) + || ((a1 & sign_mask) && ! (a2 & sign_mask) && ! (result & sign_mask))); + + return result; +} + +static ARMdword +SubS32 (ARMdword a1, ARMdword a2, int * borrow_ptr, int * overflow_ptr) +{ + ARMdword result; + unsigned int ua1; + unsigned int ua2; + + a1 = EXTEND32 (a1); + a2 = EXTEND32 (a2); + + result = a1 - a2; + ua1 = (unsigned int) a1; + ua2 = (unsigned int) a2; + + * borrow_ptr = ! (ua2 > ua1); + + * overflow_ptr = ( (! (a1 & 0x80000000ULL) && (a2 & 0x80000000ULL) && (result & 0x80000000ULL)) + || ((a1 & 0x80000000ULL) && ! (a2 & 0x80000000ULL) && ! (result & 0x80000000ULL))); + + return result; +} + +static ARMword +SubS16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 = EXTEND16 (a1); + a2 = EXTEND16 (a2); + + return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x8000); +} + +static ARMword +SubS8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 = EXTEND8 (a1); + a2 = EXTEND8 (a2); + + return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x80); +} + +static ARMword +SubU16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 &= 0xffff; + a2 &= 0xffff; + + return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x8000); +} + +static ARMword +SubU8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr) +{ + a1 &= 0xff; + a2 &= 0xff; + + return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x80); +} + +static ARMdword +SubU32 (ARMdword a1, ARMdword a2, int * borrow_ptr, int * overflow_ptr) +{ + ARMdword result; + unsigned int ua1; + unsigned int ua2; + + a1 &= 0xffffffff; + a2 &= 0xffffffff; + + result = a1 - a2; + ua1 = (unsigned int) a1; + ua2 = (unsigned int) a2; + + * borrow_ptr = ! (ua2 > ua1); + + * overflow_ptr = ( (! (a1 & 0x80000000ULL) && (a2 & 0x80000000ULL) && (result & 0x80000000ULL)) + || ((a1 & 0x80000000ULL) && ! (a2 & 0x80000000ULL) && ! (result & 0x80000000ULL))); + + return result; +} + +/* For the saturation. */ + +static unsigned char +IwmmxtSaturateU8 (signed short val, int * sat) +{ + unsigned char rv; + + if (val < 0) + { + rv = 0; + *sat = 1; + } + else if (val > 0xff) + { + rv = 0xff; + *sat = 1; + } + else + { + rv = val & 0xff; + *sat = 0; + } + return rv; +} + +static signed char +IwmmxtSaturateS8 (signed short val, int * sat) +{ + signed char rv; + + if (val < -0x80) + { + rv = -0x80; + *sat = 1; + } + else if (val > 0x7f) + { + rv = 0x7f; + *sat = 1; + } + else + { + rv = val & 0xff; + *sat = 0; + } + return rv; +} + +static unsigned short +IwmmxtSaturateU16 (signed int val, int * sat) +{ + unsigned short rv; + + if (val < 0) + { + rv = 0; + *sat = 1; + } + else if (val > 0xffff) + { + rv = 0xffff; + *sat = 1; + } + else + { + rv = val & 0xffff; + *sat = 0; + } + return rv; +} + +static signed short +IwmmxtSaturateS16 (signed int val, int * sat) +{ + signed short rv; + + if (val < -0x8000) + { + rv = - 0x8000; + *sat = 1; + } + else if (val > 0x7fff) + { + rv = 0x7fff; + *sat = 1; + } + else + { + rv = val & 0xffff; + *sat = 0; + } + return rv; +} + +static unsigned long +IwmmxtSaturateU32 (signed long long val, int * sat) +{ + unsigned long rv; + + if (val < 0) + { + rv = 0; + *sat = 1; + } + else if (val > 0xffffffff) + { + rv = 0xffffffff; + *sat = 1; + } + else + { + rv = val & 0xffffffff; + *sat = 0; + } + return rv; +} + +static signed long +IwmmxtSaturateS32 (signed long long val, int * sat) +{ + signed long rv; + + if (val < -0x80000000LL) + { + rv = -0x80000000; + *sat = 1; + } + else if (val > 0x7fffffff) + { + rv = 0x7fffffff; + *sat = 1; + } + else + { + rv = val & 0xffffffff; + *sat = 0; + } + return rv; +} + +/* Intel(r) Wireless MMX(tm) technology Acessor functions. */ + +unsigned +IwmmxtLDC (ARMul_State * state ATTRIBUTE_UNUSED, + unsigned type ATTRIBUTE_UNUSED, + ARMword instr, + ARMword data) +{ + return ARMul_CANT; +} + +unsigned +IwmmxtSTC (ARMul_State * state ATTRIBUTE_UNUSED, + unsigned type ATTRIBUTE_UNUSED, + ARMword instr, + ARMword * data) +{ + return ARMul_CANT; +} + +unsigned +IwmmxtMRC (ARMul_State * state ATTRIBUTE_UNUSED, + unsigned type ATTRIBUTE_UNUSED, + ARMword instr, + ARMword * value) +{ + return ARMul_CANT; +} + +unsigned +IwmmxtMCR (ARMul_State * state ATTRIBUTE_UNUSED, + unsigned type ATTRIBUTE_UNUSED, + ARMword instr, + ARMword value) +{ + return ARMul_CANT; +} + +unsigned +IwmmxtCDP (ARMul_State * state, unsigned type, ARMword instr) +{ + return ARMul_CANT; +} + +/* Intel(r) Wireless MMX(tm) technology instruction implementations. */ + +static int +TANDC (ARMul_State * state, ARMword instr) +{ + ARMword cpsr; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tandc\n"); +#endif + + /* The Rd field must be r15. */ + if (BITS (12, 15) != 15) + return ARMul_CANT; + + /* The CRn field must be r3. */ + if (BITS (16, 19) != 3) + return ARMul_CANT; + + /* The CRm field must be r0. */ + if (BITS (0, 3) != 0) + return ARMul_CANT; + + cpsr = ARMul_GetCPSR (state) & 0x0fffffff; + + switch (BITS (22, 23)) + { + case Bqual: + cpsr |= ( (wCBITS (wCASF, 28, 31) & wCBITS (wCASF, 24, 27) + & wCBITS (wCASF, 20, 23) & wCBITS (wCASF, 16, 19) + & wCBITS (wCASF, 12, 15) & wCBITS (wCASF, 8, 11) + & wCBITS (wCASF, 4, 7) & wCBITS (wCASF, 0, 3)) << 28); + break; + + case Hqual: + cpsr |= ( (wCBITS (wCASF, 28, 31) & wCBITS (wCASF, 20, 23) + & wCBITS (wCASF, 12, 15) & wCBITS (wCASF, 4, 7)) << 28); + break; + + case Wqual: + cpsr |= ((wCBITS (wCASF, 28, 31) & wCBITS (wCASF, 12, 15)) << 28); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + ARMul_SetCPSR (state, cpsr); + + return ARMul_DONE; +} + +static int +TBCST (ARMul_State * state, ARMword instr) +{ + ARMdword Rn; + int wRd; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tbcst\n"); +#endif + + Rn = state->Reg [BITS (12, 15)]; + if (BITS (12, 15) == 15) + Rn &= 0xfffffffc; + + wRd = BITS (16, 19); + + switch (BITS (6, 7)) + { + case Bqual: + Rn &= 0xff; + wR [wRd] = (Rn << 56) | (Rn << 48) | (Rn << 40) | (Rn << 32) + | (Rn << 24) | (Rn << 16) | (Rn << 8) | Rn; + break; + + case Hqual: + Rn &= 0xffff; + wR [wRd] = (Rn << 48) | (Rn << 32) | (Rn << 16) | Rn; + break; + + case Wqual: + Rn &= 0xffffffff; + wR [wRd] = (Rn << 32) | Rn; + break; + + default: + ARMul_UndefInstr (state, instr); + break; + } + + wC [wCon] |= WCON_MUP; + return ARMul_DONE; +} + +static int +TEXTRC (ARMul_State * state, ARMword instr) +{ + ARMword cpsr; + ARMword selector; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "textrc\n"); +#endif + + /* The Rd field must be r15. */ + if (BITS (12, 15) != 15) + return ARMul_CANT; + + /* The CRn field must be r3. */ + if (BITS (16, 19) != 3) + return ARMul_CANT; + + /* The CRm field must be 0xxx. */ + if (BIT (3) != 0) + return ARMul_CANT; + + selector = BITS (0, 2); + cpsr = ARMul_GetCPSR (state) & 0x0fffffff; + + switch (BITS (22, 23)) + { + case Bqual: selector *= 4; break; + case Hqual: selector = ((selector & 3) * 8) + 4; break; + case Wqual: selector = ((selector & 1) * 16) + 12; break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + cpsr |= wCBITS (wCASF, selector, selector + 3) << 28; + ARMul_SetCPSR (state, cpsr); + + return ARMul_DONE; +} + +static int +TEXTRM (ARMul_State * state, ARMword instr) +{ + ARMword Rd; + int offset; + int wRn; + int sign; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "textrm\n"); +#endif + + wRn = BITS (16, 19); + sign = BIT (3); + offset = BITS (0, 2); + + switch (BITS (22, 23)) + { + case Bqual: + offset *= 8; + Rd = wRBITS (wRn, offset, offset + 7); + if (sign) + Rd = EXTEND8 (Rd); + break; + + case Hqual: + offset = (offset & 3) * 16; + Rd = wRBITS (wRn, offset, offset + 15); + if (sign) + Rd = EXTEND16 (Rd); + break; + + case Wqual: + offset = (offset & 1) * 32; + Rd = wRBITS (wRn, offset, offset + 31); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + if (BITS (12, 15) == 15) + ARMul_UndefInstr (state, instr); + else + state->Reg [BITS (12, 15)] = Rd; + + return ARMul_DONE; +} + +static int +TINSR (ARMul_State * state, ARMword instr) +{ + ARMdword data; + ARMword offset; + int wRd; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tinsr\n"); +#endif + + wRd = BITS (16, 19); + data = state->Reg [BITS (12, 15)]; + offset = BITS (0, 2); + + switch (BITS (6, 7)) + { + case Bqual: + data &= 0xff; + switch (offset) + { + case 0: wR [wRd] = data | (wRBITS (wRd, 8, 63) << 8); break; + case 1: wR [wRd] = wRBITS (wRd, 0, 7) | (data << 8) | (wRBITS (wRd, 16, 63) << 16); break; + case 2: wR [wRd] = wRBITS (wRd, 0, 15) | (data << 16) | (wRBITS (wRd, 24, 63) << 24); break; + case 3: wR [wRd] = wRBITS (wRd, 0, 23) | (data << 24) | (wRBITS (wRd, 32, 63) << 32); break; + case 4: wR [wRd] = wRBITS (wRd, 0, 31) | (data << 32) | (wRBITS (wRd, 40, 63) << 40); break; + case 5: wR [wRd] = wRBITS (wRd, 0, 39) | (data << 40) | (wRBITS (wRd, 48, 63) << 48); break; + case 6: wR [wRd] = wRBITS (wRd, 0, 47) | (data << 48) | (wRBITS (wRd, 56, 63) << 56); break; + case 7: wR [wRd] = wRBITS (wRd, 0, 55) | (data << 56); break; + } + break; + + case Hqual: + data &= 0xffff; + + switch (offset & 3) + { + case 0: wR [wRd] = data | (wRBITS (wRd, 16, 63) << 16); break; + case 1: wR [wRd] = wRBITS (wRd, 0, 15) | (data << 16) | (wRBITS (wRd, 32, 63) << 32); break; + case 2: wR [wRd] = wRBITS (wRd, 0, 31) | (data << 32) | (wRBITS (wRd, 48, 63) << 48); break; + case 3: wR [wRd] = wRBITS (wRd, 0, 47) | (data << 48); break; + } + break; + + case Wqual: + if (offset & 1) + wR [wRd] = wRBITS (wRd, 0, 31) | (data << 32); + else + wR [wRd] = (wRBITS (wRd, 32, 63) << 32) | data; + break; + + default: + ARMul_UndefInstr (state, instr); + break; + } + + wC [wCon] |= WCON_MUP; + return ARMul_DONE; +} + +static int +TMCR (ARMul_State * state, ARMword instr) +{ + ARMword val; + int wCreg; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmcr\n"); +#endif + + if (BITS (0, 3) != 0) + return ARMul_CANT; + + val = state->Reg [BITS (12, 15)]; + if (BITS (12, 15) == 15) + val &= 0xfffffffc; + + wCreg = BITS (16, 19); + + switch (wCreg) + { + case wCID: + /* The wCID register is read only. */ + break; + + case wCon: + /* Writing to the MUP or CUP bits clears them. */ + wC [wCon] &= ~ (val & 0x3); + break; + + case wCSSF: + /* Only the bottom 8 bits can be written to. + The higher bits write as zero. */ + wC [wCSSF] = (val & 0xff); + wC [wCon] |= WCON_CUP; + break; + + default: + wC [wCreg] = val; + wC [wCon] |= WCON_CUP; + break; + } + + return ARMul_DONE; +} + +static int +TMCRR (ARMul_State * state, ARMword instr) +{ + ARMdword RdHi = state->Reg [BITS (16, 19)]; + ARMword RdLo = state->Reg [BITS (12, 15)]; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmcrr\n"); +#endif + + if ((BITS (16, 19) == 15) || (BITS (12, 15) == 15)) + return ARMul_CANT; + + wR [BITS (0, 3)] = (RdHi << 32) | RdLo; + + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +TMIA (ARMul_State * state, ARMword instr) +{ + signed long long a, b; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmia\n"); +#endif + + if ((BITS (0, 3) == 15) || (BITS (12, 15) == 15)) + { + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + a = state->Reg [BITS (0, 3)]; + b = state->Reg [BITS (12, 15)]; + + a = EXTEND32 (a); + b = EXTEND32 (b); + + wR [BITS (5, 8)] += a * b; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +TMIAPH (ARMul_State * state, ARMword instr) +{ + signed long a, b, result; + signed long long r; + ARMword Rm = state->Reg [BITS (0, 3)]; + ARMword Rs = state->Reg [BITS (12, 15)]; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmiaph\n"); +#endif + + if (BITS (0, 3) == 15 || BITS (12, 15) == 15) + { + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + a = SUBSTR (Rs, ARMword, 16, 31); + b = SUBSTR (Rm, ARMword, 16, 31); + + a = EXTEND16 (a); + b = EXTEND16 (b); + + result = a * b; + + r = result; + r = EXTEND32 (r); + + wR [BITS (5, 8)] += r; + + a = SUBSTR (Rs, ARMword, 0, 15); + b = SUBSTR (Rm, ARMword, 0, 15); + + a = EXTEND16 (a); + b = EXTEND16 (b); + + result = a * b; + + r = result; + r = EXTEND32 (r); + + wR [BITS (5, 8)] += r; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +TMIAxy (ARMul_State * state, ARMword instr) +{ + ARMword Rm; + ARMword Rs; + long long temp; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmiaxy\n"); +#endif + + if (BITS (0, 3) == 15 || BITS (12, 15) == 15) + { + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + Rm = state->Reg [BITS (0, 3)]; + if (BIT (17)) + Rm >>= 16; + else + Rm &= 0xffff; + + Rs = state->Reg [BITS (12, 15)]; + if (BIT (16)) + Rs >>= 16; + else + Rs &= 0xffff; + + if (Rm & (1 << 15)) + Rm -= 1 << 16; + + if (Rs & (1 << 15)) + Rs -= 1 << 16; + + Rm *= Rs; + temp = Rm; + + if (temp & (1 << 31)) + temp -= 1ULL << 32; + + wR [BITS (5, 8)] += temp; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +TMOVMSK (ARMul_State * state, ARMword instr) +{ + ARMdword result; + int wRn; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmovmsk\n"); +#endif + + /* The CRm field must be r0. */ + if (BITS (0, 3) != 0) + return ARMul_CANT; + + wRn = BITS (16, 19); + + switch (BITS (22, 23)) + { + case Bqual: + result = ( (wRBITS (wRn, 63, 63) << 7) + | (wRBITS (wRn, 55, 55) << 6) + | (wRBITS (wRn, 47, 47) << 5) + | (wRBITS (wRn, 39, 39) << 4) + | (wRBITS (wRn, 31, 31) << 3) + | (wRBITS (wRn, 23, 23) << 2) + | (wRBITS (wRn, 15, 15) << 1) + | (wRBITS (wRn, 7, 7) << 0)); + break; + + case Hqual: + result = ( (wRBITS (wRn, 63, 63) << 3) + | (wRBITS (wRn, 47, 47) << 2) + | (wRBITS (wRn, 31, 31) << 1) + | (wRBITS (wRn, 15, 15) << 0)); + break; + + case Wqual: + result = (wRBITS (wRn, 63, 63) << 1) | wRBITS (wRn, 31, 31); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + state->Reg [BITS (12, 15)] = result; + + return ARMul_DONE; +} + +static int +TMRC (ARMul_State * state, ARMword instr) +{ + int reg = BITS (12, 15); + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmrc\n"); +#endif + + if (BITS (0, 3) != 0) + return ARMul_CANT; + + if (reg == 15) + ARMul_UndefInstr (state, instr); + else + state->Reg [reg] = wC [BITS (16, 19)]; + + return ARMul_DONE; +} + +static int +TMRRC (ARMul_State * state, ARMword instr) +{ + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "tmrrc\n"); +#endif + + if ((BITS (16, 19) == 15) || (BITS (12, 15) == 15) || (BITS (4, 11) != 0)) + ARMul_UndefInstr (state, instr); + else + { + state->Reg [BITS (16, 19)] = wRBITS (BITS (0, 3), 32, 63); + state->Reg [BITS (12, 15)] = wRBITS (BITS (0, 3), 0, 31); + } + + return ARMul_DONE; +} + +static int +TORC (ARMul_State * state, ARMword instr) +{ + ARMword cpsr = ARMul_GetCPSR (state); + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "torc\n"); +#endif + + /* The Rd field must be r15. */ + if (BITS (12, 15) != 15) + return ARMul_CANT; + + /* The CRn field must be r3. */ + if (BITS (16, 19) != 3) + return ARMul_CANT; + + /* The CRm field must be r0. */ + if (BITS (0, 3) != 0) + return ARMul_CANT; + + cpsr &= 0x0fffffff; + + switch (BITS (22, 23)) + { + case Bqual: + cpsr |= ( (wCBITS (wCASF, 28, 31) | wCBITS (wCASF, 24, 27) + | wCBITS (wCASF, 20, 23) | wCBITS (wCASF, 16, 19) + | wCBITS (wCASF, 12, 15) | wCBITS (wCASF, 8, 11) + | wCBITS (wCASF, 4, 7) | wCBITS (wCASF, 0, 3)) << 28); + break; + + case Hqual: + cpsr |= ( (wCBITS (wCASF, 28, 31) | wCBITS (wCASF, 20, 23) + | wCBITS (wCASF, 12, 15) | wCBITS (wCASF, 4, 7)) << 28); + break; + + case Wqual: + cpsr |= ((wCBITS (wCASF, 28, 31) | wCBITS (wCASF, 12, 15)) << 28); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + ARMul_SetCPSR (state, cpsr); + + return ARMul_DONE; +} + +static int +WACC (ARMul_State * state, ARMword instr) +{ + int wRn; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wacc\n"); +#endif + + wRn = BITS (16, 19); + + switch (BITS (22, 23)) + { + case Bqual: + wR [BITS (12, 15)] = + wRBITS (wRn, 56, 63) + wRBITS (wRn, 48, 55) + + wRBITS (wRn, 40, 47) + wRBITS (wRn, 32, 39) + + wRBITS (wRn, 24, 31) + wRBITS (wRn, 16, 23) + + wRBITS (wRn, 8, 15) + wRBITS (wRn, 0, 7); + break; + + case Hqual: + wR [BITS (12, 15)] = + wRBITS (wRn, 48, 63) + wRBITS (wRn, 32, 47) + + wRBITS (wRn, 16, 31) + wRBITS (wRn, 0, 15); + break; + + case Wqual: + wR [BITS (12, 15)] = wRBITS (wRn, 32, 63) + wRBITS (wRn, 0, 31); + break; + + default: + ARMul_UndefInstr (state, instr); + break; + } + + wC [wCon] |= WCON_MUP; + return ARMul_DONE; +} + +static int +WADD (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword x; + ARMdword s; + ARMword psr = 0; + int i; + int carry; + int overflow; + int satrv[8]; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wadd\n"); +#endif + + /* Add two numbers using the specified function, + leaving setting the carry bit as required. */ +#define ADDx(x, y, m, f) \ + (*f) (wRBITS (BITS (16, 19), (x), (y)) & (m), \ + wRBITS (BITS ( 0, 3), (x), (y)) & (m), \ + & carry, & overflow) + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 8; i++) + { + switch (BITS (20, 21)) + { + case NoSaturation: + s = ADDx ((i * 8), (i * 8) + 7, 0xff, AddS8); + satrv [BITIDX8 (i)] = 0; + r |= (s & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + SIMD8_SET (psr, carry, SIMD_CBIT, i); + SIMD8_SET (psr, overflow, SIMD_VBIT, i); + break; + + case UnsignedSaturation: + s = ADDx ((i * 8), (i * 8) + 7, 0xff, AddU8); + x = IwmmxtSaturateU8 (s, satrv + BITIDX8 (i)); + r |= (x & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX8 (i)]) + { + SIMD8_SET (psr, carry, SIMD_CBIT, i); + SIMD8_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + case SignedSaturation: + s = ADDx ((i * 8), (i * 8) + 7, 0xff, AddS8); + x = IwmmxtSaturateS8 (s, satrv + BITIDX8 (i)); + r |= (x & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX8 (i)]) + { + SIMD8_SET (psr, carry, SIMD_CBIT, i); + SIMD8_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + } + break; + + case Hqual: + satrv[0] = satrv[2] = satrv[4] = satrv[6] = 0; + + for (i = 0; i < 4; i++) + { + switch (BITS (20, 21)) + { + case NoSaturation: + s = ADDx ((i * 16), (i * 16) + 15, 0xffff, AddS16); + satrv [BITIDX16 (i)] = 0; + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + SIMD16_SET (psr, carry, SIMD_CBIT, i); + SIMD16_SET (psr, overflow, SIMD_VBIT, i); + break; + + case UnsignedSaturation: + s = ADDx ((i * 16), (i * 16) + 15, 0xffff, AddU16); + x = IwmmxtSaturateU16 (s, satrv + BITIDX16 (i)); + r |= (x & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (x), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX16 (i)]) + { + SIMD16_SET (psr, carry, SIMD_CBIT, i); + SIMD16_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + case SignedSaturation: + s = ADDx ((i * 16), (i * 16) + 15, 0xffff, AddS16); + x = IwmmxtSaturateS16 (s, satrv + BITIDX16 (i)); + r |= (x & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (x), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX16 (i)]) + { + SIMD16_SET (psr, carry, SIMD_CBIT, i); + SIMD16_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + } + break; + + case Wqual: + satrv[0] = satrv[1] = satrv[2] = satrv[4] = satrv[5] = satrv[6] = 0; + + for (i = 0; i < 2; i++) + { + switch (BITS (20, 21)) + { + case NoSaturation: + s = ADDx ((i * 32), (i * 32) + 31, 0xffffffff, AddS32); + satrv [BITIDX32 (i)] = 0; + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + SIMD32_SET (psr, carry, SIMD_CBIT, i); + SIMD32_SET (psr, overflow, SIMD_VBIT, i); + break; + + case UnsignedSaturation: + s = ADDx ((i * 32), (i * 32) + 31, 0xffffffff, AddU32); + x = IwmmxtSaturateU32 (s, satrv + BITIDX32 (i)); + r |= (x & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX32 (i)]) + { + SIMD32_SET (psr, carry, SIMD_CBIT, i); + SIMD32_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + case SignedSaturation: + s = ADDx ((i * 32), (i * 32) + 31, 0xffffffff, AddS32); + x = IwmmxtSaturateS32 (s, satrv + BITIDX32 (i)); + r |= (x & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX32 (i)]) + { + SIMD32_SET (psr, carry, SIMD_CBIT, i); + SIMD32_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_MUP | WCON_CUP); + + SET_wCSSFvec (satrv); + +#undef ADDx + + return ARMul_DONE; +} + +static int +WALIGNI (ARMword instr) +{ + int shift = BITS (20, 22) * 8; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "waligni\n"); +#endif + + if (shift) + wR [BITS (12, 15)] = + wRBITS (BITS (16, 19), shift, 63) + | (wRBITS (BITS (0, 3), 0, shift) << ((64 - shift))); + else + wR [BITS (12, 15)] = wR [BITS (16, 19)]; + + wC [wCon] |= WCON_MUP; + return ARMul_DONE; +} + +static int +WALIGNR (ARMul_State * state, ARMword instr) +{ + int shift = (wC [BITS (20, 21) + 8] & 0x7) * 8; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "walignr\n"); +#endif + + if (shift) + wR [BITS (12, 15)] = + wRBITS (BITS (16, 19), shift, 63) + | (wRBITS (BITS (0, 3), 0, shift) << ((64 - shift))); + else + wR [BITS (12, 15)] = wR [BITS (16, 19)]; + + wC [wCon] |= WCON_MUP; + return ARMul_DONE; +} + +static int +WAND (ARMword instr) +{ + ARMdword result; + ARMword psr = 0; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wand\n"); +#endif + + result = wR [BITS (16, 19)] & wR [BITS (0, 3)]; + wR [BITS (12, 15)] = result; + + SIMD64_SET (psr, (result == 0), SIMD_ZBIT); + SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT); + + wC [wCASF] = psr; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WANDN (ARMword instr) +{ + ARMdword result; + ARMword psr = 0; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wandn\n"); +#endif + + result = wR [BITS (16, 19)] & ~ wR [BITS (0, 3)]; + wR [BITS (12, 15)] = result; + + SIMD64_SET (psr, (result == 0), SIMD_ZBIT); + SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT); + + wC [wCASF] = psr; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WAVG2 (ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + int round = BIT (20) ? 1 : 0; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wavg2\n"); +#endif + +#define AVG2x(x, y, m) (((wRBITS (BITS (16, 19), (x), (y)) & (m)) \ + + (wRBITS (BITS ( 0, 3), (x), (y)) & (m)) \ + + round) / 2) + + if (BIT (22)) + { + for (i = 0; i < 4; i++) + { + s = AVG2x ((i * 16), (i * 16) + 15, 0xffff) & 0xffff; + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + r |= s << (i * 16); + } + } + else + { + for (i = 0; i < 8; i++) + { + s = AVG2x ((i * 8), (i * 8) + 7, 0xff) & 0xff; + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + r |= s << (i * 8); + } + } + + wR [BITS (12, 15)] = r; + wC [wCASF] = psr; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WCMPEQ (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wcmpeq\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 8; i++) + { + s = wRBYTE (BITS (16, 19), i) == wRBYTE (BITS (0, 3), i) ? 0xff : 0; + r |= s << (i * 8); + SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + } + break; + + case Hqual: + for (i = 0; i < 4; i++) + { + s = wRHALF (BITS (16, 19), i) == wRHALF (BITS (0, 3), i) ? 0xffff : 0; + r |= s << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + for (i = 0; i < 2; i++) + { + s = wRWORD (BITS (16, 19), i) == wRWORD (BITS (0, 3), i) ? 0xffffffff : 0; + r |= s << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WCMPGT (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wcmpgt\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + if (BIT (21)) + { + /* Use a signed comparison. */ + for (i = 0; i < 8; i++) + { + signed char a, b; + + a = wRBYTE (BITS (16, 19), i); + b = wRBYTE (BITS (0, 3), i); + + s = (a > b) ? 0xff : 0; + r |= s << (i * 8); + SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + } + } + else + { + for (i = 0; i < 8; i++) + { + s = (wRBYTE (BITS (16, 19), i) > wRBYTE (BITS (0, 3), i)) + ? 0xff : 0; + r |= s << (i * 8); + SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + } + } + break; + + case Hqual: + if (BIT (21)) + { + for (i = 0; i < 4; i++) + { + signed int a, b; + + a = wRHALF (BITS (16, 19), i); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i); + b = EXTEND16 (b); + + s = (a > b) ? 0xffff : 0; + r |= s << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + } + else + { + for (i = 0; i < 4; i++) + { + s = (wRHALF (BITS (16, 19), i) > wRHALF (BITS (0, 3), i)) + ? 0xffff : 0; + r |= s << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + } + break; + + case Wqual: + if (BIT (21)) + { + for (i = 0; i < 2; i++) + { + signed long a, b; + + a = wRWORD (BITS (16, 19), i); + b = wRWORD (BITS (0, 3), i); + + s = (a > b) ? 0xffffffff : 0; + r |= s << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + } + else + { + for (i = 0; i < 2; i++) + { + s = (wRWORD (BITS (16, 19), i) > wRWORD (BITS (0, 3), i)) + ? 0xffffffff : 0; + r |= s << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static ARMword +Compute_Iwmmxt_Address (ARMul_State * state, ARMword instr, int * pFailed) +{ + ARMword Rn; + ARMword addr; + ARMword offset; + ARMword multiplier; + + * pFailed = 0; + Rn = BITS (16, 19); + addr = state->Reg [Rn]; + offset = BITS (0, 7); + multiplier = BIT (8) ? 4 : 1; + + if (BIT (24)) /* P */ + { + /* Pre Indexed Addressing. */ + if (BIT (23)) + addr += offset * multiplier; + else + addr -= offset * multiplier; + + /* Immediate Pre-Indexed. */ + if (BIT (21)) /* W */ + { + if (Rn == 15) + { + /* Writeback into R15 is UNPREDICTABLE. */ +#ifdef DEBUG + fprintf (stderr, "iWMMXt: writeback into r15\n"); +#endif + * pFailed = 1; + } + else + state->Reg [Rn] = addr; + } + } + else + { + /* Post Indexed Addressing. */ + if (BIT (21)) /* W */ + { + /* Handle the write back of the final address. */ + if (Rn == 15) + { + /* Writeback into R15 is UNPREDICTABLE. */ +#ifdef DEBUG + fprintf (stderr, "iWMMXt: writeback into r15\n"); +#endif + * pFailed = 1; + } + else + { + ARMword increment; + + if (BIT (23)) + increment = offset * multiplier; + else + increment = - (offset * multiplier); + + state->Reg [Rn] = addr + increment; + } + } + else + { + /* P == 0, W == 0, U == 0 is UNPREDICTABLE. */ + if (BIT (23) == 0) + { +#ifdef DEBUG + fprintf (stderr, "iWMMXt: undefined addressing mode\n"); +#endif + * pFailed = 1; + } + } + } + + return addr; +} + +static ARMdword +Iwmmxt_Load_Double_Word (ARMul_State * state, ARMword address) +{ + ARMdword value; + + /* The address must be aligned on a 8 byte boundary. */ + if (address & 0x7) + { + fprintf (stderr, "iWMMXt: At addr 0x%x: Unaligned double word load from 0x%x\n", + (state->Reg[15] - 8) & ~0x3, address); +#ifdef DEBUG +#endif + /* No need to check for alignment traps. An unaligned + double word load with alignment trapping disabled is + UNPREDICTABLE. */ + ARMul_Abort (state, ARMul_DataAbortV); + } + + /* Load the words. */ + if (! state->bigendSig) + { + value = ARMul_LoadWordN (state, address + 4); + value <<= 32; + value |= ARMul_LoadWordN (state, address); + } + else + { + value = ARMul_LoadWordN (state, address); + value <<= 32; + value |= ARMul_LoadWordN (state, address + 4); + } + + /* Check for data aborts. */ + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); + else + ARMul_Icycles (state, 2, 0L); + + return value; +} + +static ARMword +Iwmmxt_Load_Word (ARMul_State * state, ARMword address) +{ + ARMword value; + + /* Check for a misaligned address. */ + if (address & 3) + { + if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN)) + ARMul_Abort (state, ARMul_DataAbortV); + else + address &= ~ 3; + } + + value = ARMul_LoadWordN (state, address); + + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); + else + ARMul_Icycles (state, 1, 0L); + + return value; +} + +static ARMword +Iwmmxt_Load_Half_Word (ARMul_State * state, ARMword address) +{ + ARMword value; + + /* Check for a misaligned address. */ + if (address & 1) + { + if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN)) + ARMul_Abort (state, ARMul_DataAbortV); + else + address &= ~ 1; + } + + value = ARMul_LoadHalfWord (state, address); + + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); + else + ARMul_Icycles (state, 1, 0L); + + return value; +} + +static ARMword +Iwmmxt_Load_Byte (ARMul_State * state, ARMword address) +{ + ARMword value; + + value = ARMul_LoadByte (state, address); + + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); + else + ARMul_Icycles (state, 1, 0L); + + return value; +} + +static void +Iwmmxt_Store_Double_Word (ARMul_State * state, ARMword address, ARMdword value) +{ + /* The address must be aligned on a 8 byte boundary. */ + if (address & 0x7) + { + fprintf (stderr, "iWMMXt: At addr 0x%x: Unaligned double word store to 0x%x\n", + (state->Reg[15] - 8) & ~0x3, address); +#ifdef DEBUG +#endif + /* No need to check for alignment traps. An unaligned + double word store with alignment trapping disabled is + UNPREDICTABLE. */ + ARMul_Abort (state, ARMul_DataAbortV); + } + + /* Store the words. */ + if (! state->bigendSig) + { + ARMul_StoreWordN (state, address, value); + ARMul_StoreWordN (state, address + 4, value >> 32); + } + else + { + ARMul_StoreWordN (state, address + 4, value); + ARMul_StoreWordN (state, address, value >> 32); + } + + /* Check for data aborts. */ + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); + else + ARMul_Icycles (state, 2, 0L); +} + +static void +Iwmmxt_Store_Word (ARMul_State * state, ARMword address, ARMword value) +{ + /* Check for a misaligned address. */ + if (address & 3) + { + if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN)) + ARMul_Abort (state, ARMul_DataAbortV); + else + address &= ~ 3; + } + + ARMul_StoreWordN (state, address, value); + + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); +} + +static void +Iwmmxt_Store_Half_Word (ARMul_State * state, ARMword address, ARMword value) +{ + /* Check for a misaligned address. */ + if (address & 1) + { + if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN)) + ARMul_Abort (state, ARMul_DataAbortV); + else + address &= ~ 1; + } + + ARMul_StoreHalfWord (state, address, value); + + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); +} + +static void +Iwmmxt_Store_Byte (ARMul_State * state, ARMword address, ARMword value) +{ + ARMul_StoreByte (state, address, value); + + if (state->Aborted) + ARMul_Abort (state, ARMul_DataAbortV); +} + +static int +WLDR (ARMul_State * state, ARMword instr) +{ + ARMword address; + int failed; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wldr\n"); +#endif + + address = Compute_Iwmmxt_Address (state, instr, & failed); + if (failed) + return ARMul_CANT; + + if (BITS (28, 31) == 0xf) + { + /* WLDRW wCx */ + wC [BITS (12, 15)] = Iwmmxt_Load_Word (state, address); + } + else if (BIT (8) == 0) + { + if (BIT (22) == 0) + /* WLDRB */ + wR [BITS (12, 15)] = Iwmmxt_Load_Byte (state, address); + else + /* WLDRH */ + wR [BITS (12, 15)] = Iwmmxt_Load_Half_Word (state, address); + } + else + { + if (BIT (22) == 0) + /* WLDRW wRd */ + wR [BITS (12, 15)] = Iwmmxt_Load_Word (state, address); + else + /* WLDRD */ + wR [BITS (12, 15)] = Iwmmxt_Load_Double_Word (state, address); + } + + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WMAC (ARMword instr) +{ + int i; + ARMdword t = 0; + ARMword a, b; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wmac\n"); +#endif + + for (i = 0; i < 4; i++) + { + if (BIT (21)) + { + /* Signed. */ + signed long s; + + a = wRHALF (BITS (16, 19), i); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i); + b = EXTEND16 (b); + + s = (signed long) a * (signed long) b; + + (signed long long) t += s; + } + else + { + /* Unsigned. */ + a = wRHALF (BITS (16, 19), i); + b = wRHALF (BITS ( 0, 3), i); + + t += a * b; + } + } + + if (BIT (20)) + wR [BITS (12, 15)] = 0; + + if (BIT (21)) /* Signed. */ + (signed long long) wR[BITS (12, 15)] += (signed long long) t; + else + wR [BITS (12, 15)] += t; + + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WMADD (ARMword instr) +{ + ARMdword r = 0; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wmadd\n"); +#endif + + for (i = 0; i < 2; i++) + { + ARMdword s1, s2; + + if (BIT (21)) /* Signed. */ + { + signed long a, b; + + a = wRHALF (BITS (16, 19), i * 2); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i * 2); + b = EXTEND16 (b); + + (signed long) s1 = a * b; + + a = wRHALF (BITS (16, 19), i * 2 + 1); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i * 2 + 1); + b = EXTEND16 (b); + + (signed long) s2 = a * b; + } + else /* Unsigned. */ + { + unsigned long a, b; + + a = wRHALF (BITS (16, 19), i * 2); + b = wRHALF (BITS ( 0, 3), i * 2); + + (unsigned long) s1 = a * b; + + a = wRHALF (BITS (16, 19), i * 2 + 1); + b = wRHALF (BITS ( 0, 3), i * 2 + 1); + + (signed long) s2 = a * b; + } + + r |= (ARMdword) ((s1 + s2) & 0xffffffff) << (i ? 32 : 0); + } + + wR [BITS (12, 15)] = r; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WMAX (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wmax\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 8; i++) + if (BIT (21)) /* Signed. */ + { + int a, b; + + a = wRBYTE (BITS (16, 19), i); + a = EXTEND8 (a); + + b = wRBYTE (BITS (0, 3), i); + b = EXTEND8 (b); + + if (a > b) + s = a; + else + s = b; + + r |= (s & 0xff) << (i * 8); + } + else /* Unsigned. */ + { + unsigned int a, b; + + a = wRBYTE (BITS (16, 19), i); + b = wRBYTE (BITS (0, 3), i); + + if (a > b) + s = a; + else + s = b; + + r |= (s & 0xff) << (i * 8); + } + break; + + case Hqual: + for (i = 0; i < 4; i++) + if (BIT (21)) /* Signed. */ + { + int a, b; + + a = wRHALF (BITS (16, 19), i); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i); + b = EXTEND16 (b); + + if (a > b) + s = a; + else + s = b; + + r |= (s & 0xffff) << (i * 16); + } + else /* Unsigned. */ + { + unsigned int a, b; + + a = wRHALF (BITS (16, 19), i); + b = wRHALF (BITS (0, 3), i); + + if (a > b) + s = a; + else + s = b; + + r |= (s & 0xffff) << (i * 16); + } + break; + + case Wqual: + for (i = 0; i < 2; i++) + if (BIT (21)) /* Signed. */ + { + int a, b; + + a = wRWORD (BITS (16, 19), i); + b = wRWORD (BITS (0, 3), i); + + if (a > b) + s = a; + else + s = b; + + r |= (s & 0xffffffff) << (i * 32); + } + else + { + unsigned int a, b; + + a = wRWORD (BITS (16, 19), i); + b = wRWORD (BITS (0, 3), i); + + if (a > b) + s = a; + else + s = b; + + r |= (s & 0xffffffff) << (i * 32); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wR [BITS (12, 15)] = r; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WMIN (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wmin\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 8; i++) + if (BIT (21)) /* Signed. */ + { + int a, b; + + a = wRBYTE (BITS (16, 19), i); + a = EXTEND8 (a); + + b = wRBYTE (BITS (0, 3), i); + b = EXTEND8 (b); + + if (a < b) + s = a; + else + s = b; + + r |= (s & 0xff) << (i * 8); + } + else /* Unsigned. */ + { + unsigned int a, b; + + a = wRBYTE (BITS (16, 19), i); + b = wRBYTE (BITS (0, 3), i); + + if (a < b) + s = a; + else + s = b; + + r |= (s & 0xff) << (i * 8); + } + break; + + case Hqual: + for (i = 0; i < 4; i++) + if (BIT (21)) /* Signed. */ + { + int a, b; + + a = wRHALF (BITS (16, 19), i); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i); + b = EXTEND16 (b); + + if (a < b) + s = a; + else + s = b; + + r |= (s & 0xffff) << (i * 16); + } + else + { + /* Unsigned. */ + unsigned int a, b; + + a = wRHALF (BITS (16, 19), i); + b = wRHALF (BITS ( 0, 3), i); + + if (a < b) + s = a; + else + s = b; + + r |= (s & 0xffff) << (i * 16); + } + break; + + case Wqual: + for (i = 0; i < 2; i++) + if (BIT (21)) /* Signed. */ + { + int a, b; + + a = wRWORD (BITS (16, 19), i); + b = wRWORD (BITS ( 0, 3), i); + + if (a < b) + s = a; + else + s = b; + + r |= (s & 0xffffffff) << (i * 32); + } + else + { + unsigned int a, b; + + a = wRWORD (BITS (16, 19), i); + b = wRWORD (BITS (0, 3), i); + + if (a < b) + s = a; + else + s = b; + + r |= (s & 0xffffffff) << (i * 32); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wR [BITS (12, 15)] = r; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WMUL (ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wmul\n"); +#endif + + for (i = 0; i < 4; i++) + if (BIT (21)) /* Signed. */ + { + long a, b; + + a = wRHALF (BITS (16, 19), i); + a = EXTEND16 (a); + + b = wRHALF (BITS (0, 3), i); + b = EXTEND16 (b); + + s = a * b; + + if (BIT (20)) + r |= ((s >> 16) & 0xffff) << (i * 16); + else + r |= (s & 0xffff) << (i * 16); + } + else /* Unsigned. */ + { + unsigned long a, b; + + a = wRHALF (BITS (16, 19), i); + b = wRHALF (BITS (0, 3), i); + + s = a * b; + + if (BIT (20)) + r |= ((s >> 16) & 0xffff) << (i * 16); + else + r |= (s & 0xffff) << (i * 16); + } + + wR [BITS (12, 15)] = r; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WOR (ARMword instr) +{ + ARMword psr = 0; + ARMdword result; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wor\n"); +#endif + + result = wR [BITS (16, 19)] | wR [BITS (0, 3)]; + wR [BITS (12, 15)] = result; + + SIMD64_SET (psr, (result == 0), SIMD_ZBIT); + SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT); + + wC [wCASF] = psr; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WPACK (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword x; + ARMdword s; + int i; + int satrv[8]; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wpack\n"); +#endif + + switch (BITS (22, 23)) + { + case Hqual: + for (i = 0; i < 8; i++) + { + x = wRHALF (i < 4 ? BITS (16, 19) : BITS (0, 3), i & 3); + + switch (BITS (20, 21)) + { + case UnsignedSaturation: + s = IwmmxtSaturateU8 (x, satrv + BITIDX8 (i)); + break; + + case SignedSaturation: + s = IwmmxtSaturateS8 (x, satrv + BITIDX8 (i)); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + r |= (s & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + satrv[0] = satrv[2] = satrv[4] = satrv[6] = 0; + + for (i = 0; i < 4; i++) + { + x = wRWORD (i < 2 ? BITS (16, 19) : BITS (0, 3), i & 1); + + switch (BITS (20, 21)) + { + case UnsignedSaturation: + s = IwmmxtSaturateU16 (x, satrv + BITIDX16 (i)); + break; + + case SignedSaturation: + s = IwmmxtSaturateS16 (x, satrv + BITIDX16 (i)); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Dqual: + satrv[0] = satrv[1] = satrv[2] = satrv[4] = satrv[5] = satrv[6] = 0; + + for (i = 0; i < 2; i++) + { + x = wR [i ? BITS (0, 3) : BITS (16, 19)]; + + switch (BITS (20, 21)) + { + case UnsignedSaturation: + s = IwmmxtSaturateU32 (x, satrv + BITIDX32 (i)); + break; + + case SignedSaturation: + s = IwmmxtSaturateS32 (x, satrv + BITIDX32 (i)); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + SET_wCSSFvec (satrv); + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WROR (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + ARMword psr = 0; + int i; + int shift; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wror\n"); +#endif + + DECODE_G_BIT (state, instr, shift); + + switch (BITS (22, 23)) + { + case Hqual: + shift &= 0xf; + for (i = 0; i < 4; i++) + { + s = ((wRHALF (BITS (16, 19), i) & 0xffff) << (16 - shift)) + | ((wRHALF (BITS (16, 19), i) & 0xffff) >> shift); + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + shift &= 0x1f; + for (i = 0; i < 2; i++) + { + s = ((wRWORD (BITS (16, 19), i) & 0xffffffff) << (32 - shift)) + | ((wRWORD (BITS (16, 19), i) & 0xffffffff) >> shift); + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + case Dqual: + shift &= 0x3f; + r = (wR [BITS (16, 19)] >> shift) + | (wR [BITS (16, 19)] << (64 - shift)); + + SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT); + SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WSAD (ARMword instr) +{ + ARMdword r; + int s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wsad\n"); +#endif + + /* Z bit. */ + r = BIT (20) ? 0 : (wR [BITS (12, 15)] & 0xffffffff); + + if (BIT (22)) + /* Half. */ + for (i = 0; i < 4; i++) + { + s = (wRHALF (BITS (16, 19), i) - wRHALF (BITS (0, 3), i)); + r += abs (s); + } + else + /* Byte. */ + for (i = 0; i < 8; i++) + { + s = (wRBYTE (BITS (16, 19), i) - wRBYTE (BITS (0, 3), i)); + r += abs (s); + } + + wR [BITS (12, 15)] = r; + wC [wCon] |= WCON_MUP; + + return ARMul_DONE; +} + +static int +WSHUFH (ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + int imm8; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wshufh\n"); +#endif + + imm8 = (BITS (20, 23) << 4) | BITS (0, 3); + + for (i = 0; i < 4; i++) + { + s = wRHALF (BITS (16, 19), ((imm8 >> (i * 2) & 3)) & 0xff); + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WSLL (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + ARMword psr = 0; + int i; + unsigned shift; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wsll\n"); +#endif + + DECODE_G_BIT (state, instr, shift); + + switch (BITS (22, 23)) + { + case Hqual: + for (i = 0; i < 4; i++) + { + if (shift > 15) + s = 0; + else + s = ((wRHALF (BITS (16, 19), i) & 0xffff) << shift); + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + for (i = 0; i < 2; i++) + { + if (shift > 31) + s = 0; + else + s = ((wRWORD (BITS (16, 19), i) & 0xffffffff) << shift); + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + case Dqual: + if (shift > 63) + r = 0; + else + r = ((wR[BITS (16, 19)] & 0xffffffffffffffff) << shift); + + SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT); + SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WSRA (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + ARMword psr = 0; + int i; + unsigned shift; + signed long t; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wsra\n"); +#endif + + DECODE_G_BIT (state, instr, shift); + + switch (BITS (22, 23)) + { + case Hqual: + for (i = 0; i < 4; i++) + { + if (shift > 15) + t = (wRHALF (BITS (16, 19), i) & 0x8000) ? 0xffff : 0; + else + { + t = wRHALF (BITS (16, 19), i); + t = EXTEND16 (t); + t >>= shift; + } + + s = t; + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + for (i = 0; i < 2; i++) + { + if (shift > 31) + t = (wRWORD (BITS (16, 19), i) & 0x80000000) ? 0xffffffff : 0; + else + { + t = wRWORD (BITS (16, 19), i); + t >>= shift; + } + s = t; + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + case Dqual: + if (shift > 63) + r = (wR [BITS (16, 19)] & 0x8000000000000000) ? 0xffffffffffffffff : 0; + else + r = ((signed long long) (wR[BITS (16, 19)] & 0xffffffffffffffff) >> shift); + SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT); + SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WSRL (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMdword s; + ARMword psr = 0; + int i; + unsigned int shift; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wsrl\n"); +#endif + + DECODE_G_BIT (state, instr, shift); + + switch (BITS (22, 23)) + { + case Hqual: + for (i = 0; i < 4; i++) + { + if (shift > 15) + s = 0; + else + s = ((unsigned) (wRHALF (BITS (16, 19), i) & 0xffff) >> shift); + + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + for (i = 0; i < 2; i++) + { + if (shift > 31) + s = 0; + else + s = ((unsigned long) (wRWORD (BITS (16, 19), i) & 0xffffffff) >> shift); + + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + case Dqual: + if (shift > 63) + r = 0; + else + r = (wR [BITS (16, 19)] & 0xffffffffffffffff) >> shift; + + SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT); + SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WSTR (ARMul_State * state, ARMword instr) +{ + ARMword address; + int failed; + + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wstr\n"); +#endif + + address = Compute_Iwmmxt_Address (state, instr, & failed); + if (failed) + return ARMul_CANT; + + if (BITS (28, 31) == 0xf) + { + /* WSTRW wCx */ + Iwmmxt_Store_Word (state, address, wC [BITS (12, 15)]); + } + else if (BIT (8) == 0) + { + if (BIT (22) == 0) + /* WSTRB */ + Iwmmxt_Store_Byte (state, address, wR [BITS (12, 15)]); + else + /* WSTRH */ + Iwmmxt_Store_Half_Word (state, address, wR [BITS (12, 15)]); + } + else + { + if (BIT (22) == 0) + /* WSTRW wRd */ + Iwmmxt_Store_Word (state, address, wR [BITS (12, 15)]); + else + /* WSTRD */ + Iwmmxt_Store_Double_Word (state, address, wR [BITS (12, 15)]); + } + + return ARMul_DONE; +} + +static int +WSUB (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword x; + ARMdword s; + int i; + int carry; + int overflow; + int satrv[8]; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wsub\n"); +#endif + +/* Subtract two numbers using the specified function, + leaving setting the carry bit as required. */ +#define SUBx(x, y, m, f) \ + (*f) (wRBITS (BITS (16, 19), (x), (y)) & (m), \ + wRBITS (BITS ( 0, 3), (x), (y)) & (m), & carry, & overflow) + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 8; i++) + { + switch (BITS (20, 21)) + { + case NoSaturation: + s = SUBx ((i * 8), (i * 8) + 7, 0xff, SubS8); + satrv [BITIDX8 (i)] = 0; + r |= (s & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i); + SIMD8_SET (psr, carry, SIMD_CBIT, i); + SIMD8_SET (psr, overflow, SIMD_VBIT, i); + break; + + case UnsignedSaturation: + s = SUBx ((i * 8), (i * 8) + 7, 0xff, SubU8); + x = IwmmxtSaturateU8 (s, satrv + BITIDX8 (i)); + r |= (x & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX8 (i)]) + { + SIMD8_SET (psr, carry, SIMD_CBIT, i); + SIMD8_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + case SignedSaturation: + s = SUBx ((i * 8), (i * 8) + 7, 0xff, SubS8); + x = IwmmxtSaturateS8 (s, satrv + BITIDX8 (i)); + r |= (x & 0xff) << (i * 8); + SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i); + SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX8 (i)]) + { + SIMD8_SET (psr, carry, SIMD_CBIT, i); + SIMD8_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + } + break; + + case Hqual: + satrv[0] = satrv[2] = satrv[4] = satrv[6] = 0; + + for (i = 0; i < 4; i++) + { + switch (BITS (20, 21)) + { + case NoSaturation: + s = SUBx ((i * 16), (i * 16) + 15, 0xffff, SubU16); + satrv [BITIDX16 (i)] = 0; + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + SIMD16_SET (psr, carry, SIMD_CBIT, i); + SIMD16_SET (psr, overflow, SIMD_VBIT, i); + break; + + case UnsignedSaturation: + s = SUBx ((i * 16), (i * 16) + 15, 0xffff, SubU16); + x = IwmmxtSaturateU16 (s, satrv + BITIDX16 (i)); + r |= (x & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (x & 0xffff), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX16 (i)]) + { + SIMD16_SET (psr, carry, SIMD_CBIT, i); + SIMD16_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + case SignedSaturation: + s = SUBx ((i * 16), (i * 16) + 15, 0xffff, SubS16); + x = IwmmxtSaturateS16 (s, satrv + BITIDX16 (i)); + r |= (x & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (x), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX16 (i)]) + { + SIMD16_SET (psr, carry, SIMD_CBIT, i); + SIMD16_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + } + break; + + case Wqual: + satrv[0] = satrv[1] = satrv[2] = satrv[4] = satrv[5] = satrv[6] = 0; + + for (i = 0; i < 2; i++) + { + switch (BITS (20, 21)) + { + case NoSaturation: + s = SUBx ((i * 32), (i * 32) + 31, 0xffffffff, SubU32); + satrv[BITIDX32 (i)] = 0; + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + SIMD32_SET (psr, carry, SIMD_CBIT, i); + SIMD32_SET (psr, overflow, SIMD_VBIT, i); + break; + + case UnsignedSaturation: + s = SUBx ((i * 32), (i * 32) + 31, 0xffffffff, SubU32); + x = IwmmxtSaturateU32 (s, satrv + BITIDX32 (i)); + r |= (x & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX32 (i)]) + { + SIMD32_SET (psr, carry, SIMD_CBIT, i); + SIMD32_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + case SignedSaturation: + s = SUBx ((i * 32), (i * 32) + 31, 0xffffffff, SubS32); + x = IwmmxtSaturateS32 (s, satrv + BITIDX32 (i)); + r |= (x & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i); + if (! satrv [BITIDX32 (i)]) + { + SIMD32_SET (psr, carry, SIMD_CBIT, i); + SIMD32_SET (psr, overflow, SIMD_VBIT, i); + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + } + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wR [BITS (12, 15)] = r; + wC [wCASF] = psr; + SET_wCSSFvec (satrv); + wC [wCon] |= (WCON_CUP | WCON_MUP); + +#undef SUBx + + return ARMul_DONE; +} + +static int +WUNPCKEH (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wunpckeh\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 4; i++) + { + s = wRBYTE (BITS (16, 19), i + 4); + + if (BIT (21) && NBIT8 (s)) + s |= 0xff00; + + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Hqual: + for (i = 0; i < 2; i++) + { + s = wRHALF (BITS (16, 19), i + 2); + + if (BIT (21) && NBIT16 (s)) + s |= 0xffff0000; + + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + r = wRWORD (BITS (16, 19), 1); + + if (BIT (21) && NBIT32 (r)) + r |= 0xffffffff00000000; + + SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT); + SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WUNPCKEL (ARMul_State * state, ARMword instr) +{ + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wunpckel\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 4; i++) + { + s = wRBYTE (BITS (16, 19), i); + + if (BIT (21) && NBIT8 (s)) + s |= 0xff00; + + r |= (s & 0xffff) << (i * 16); + SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i); + SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i); + } + break; + + case Hqual: + for (i = 0; i < 2; i++) + { + s = wRHALF (BITS (16, 19), i); + + if (BIT (21) && NBIT16 (s)) + s |= 0xffff0000; + + r |= (s & 0xffffffff) << (i * 32); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i); + } + break; + + case Wqual: + r = wRWORD (BITS (16, 19), 0); + + if (BIT (21) && NBIT32 (r)) + r |= 0xffffffff00000000; + + SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT); + SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WUNPCKIH (ARMul_State * state, ARMword instr) +{ + ARMword a, b; + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wunpckih\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 4; i++) + { + a = wRBYTE (BITS (16, 19), i + 4); + b = wRBYTE (BITS ( 0, 3), i + 4); + s = a | (b << 8); + r |= (s & 0xffff) << (i * 16); + SIMD8_SET (psr, NBIT8 (a), SIMD_NBIT, i * 2); + SIMD8_SET (psr, ZBIT8 (a), SIMD_ZBIT, i * 2); + SIMD8_SET (psr, NBIT8 (b), SIMD_NBIT, (i * 2) + 1); + SIMD8_SET (psr, ZBIT8 (b), SIMD_ZBIT, (i * 2) + 1); + } + break; + + case Hqual: + for (i = 0; i < 2; i++) + { + a = wRHALF (BITS (16, 19), i + 2); + b = wRHALF (BITS ( 0, 3), i + 2); + s = a | (b << 16); + r |= (s & 0xffffffff) << (i * 32); + SIMD16_SET (psr, NBIT16 (a), SIMD_NBIT, (i * 2)); + SIMD16_SET (psr, ZBIT16 (a), SIMD_ZBIT, (i * 2)); + SIMD16_SET (psr, NBIT16 (b), SIMD_NBIT, (i * 2) + 1); + SIMD16_SET (psr, ZBIT16 (b), SIMD_ZBIT, (i * 2) + 1); + } + break; + + case Wqual: + a = wRWORD (BITS (16, 19), 1); + s = wRWORD (BITS ( 0, 3), 1); + r = a | (s << 32); + + SIMD32_SET (psr, NBIT32 (a), SIMD_NBIT, 0); + SIMD32_SET (psr, ZBIT32 (a), SIMD_ZBIT, 0); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, 1); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, 1); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WUNPCKIL (ARMul_State * state, ARMword instr) +{ + ARMword a, b; + ARMdword r = 0; + ARMword psr = 0; + ARMdword s; + int i; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wunpckil\n"); +#endif + + switch (BITS (22, 23)) + { + case Bqual: + for (i = 0; i < 4; i++) + { + a = wRBYTE (BITS (16, 19), i); + b = wRBYTE (BITS ( 0, 3), i); + s = a | (b << 8); + r |= (s & 0xffff) << (i * 16); + SIMD8_SET (psr, NBIT8 (a), SIMD_NBIT, i * 2); + SIMD8_SET (psr, ZBIT8 (a), SIMD_ZBIT, i * 2); + SIMD8_SET (psr, NBIT8 (b), SIMD_NBIT, (i * 2) + 1); + SIMD8_SET (psr, ZBIT8 (b), SIMD_ZBIT, (i * 2) + 1); + } + break; + + case Hqual: + for (i = 0; i < 2; i++) + { + a = wRHALF (BITS (16, 19), i); + b = wRHALF (BITS ( 0, 3), i); + s = a | (b << 16); + r |= (s & 0xffffffff) << (i * 32); + SIMD16_SET (psr, NBIT16 (a), SIMD_NBIT, (i * 2)); + SIMD16_SET (psr, ZBIT16 (a), SIMD_ZBIT, (i * 2)); + SIMD16_SET (psr, NBIT16 (b), SIMD_NBIT, (i * 2) + 1); + SIMD16_SET (psr, ZBIT16 (b), SIMD_ZBIT, (i * 2) + 1); + } + break; + + case Wqual: + a = wRWORD (BITS (16, 19), 0); + s = wRWORD (BITS ( 0, 3), 0); + r = a | (s << 32); + + SIMD32_SET (psr, NBIT32 (a), SIMD_NBIT, 0); + SIMD32_SET (psr, ZBIT32 (a), SIMD_ZBIT, 0); + SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, 1); + SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, 1); + break; + + default: + ARMul_UndefInstr (state, instr); + return ARMul_DONE; + } + + wC [wCASF] = psr; + wR [BITS (12, 15)] = r; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +static int +WXOR (ARMword instr) +{ + ARMword psr = 0; + ARMdword result; + + if ((read_cp15_reg (15, 0, 1) & 3) != 3) + return ARMul_CANT; + +#ifdef DEBUG + fprintf (stderr, "wxor\n"); +#endif + + result = wR [BITS (16, 19)] ^ wR [BITS (0, 3)]; + wR [BITS (12, 15)] = result; + + SIMD64_SET (psr, (result == 0), SIMD_ZBIT); + SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT); + + wC [wCASF] = psr; + wC [wCon] |= (WCON_CUP | WCON_MUP); + + return ARMul_DONE; +} + +/* This switch table is moved to a seperate function in order + to work around a compiler bug in the host compiler... */ + +static int +Process_Instruction (ARMul_State * state, ARMword instr) +{ + int status = ARMul_BUSY; + + switch ((BITS (20, 23) << 8) | BITS (4, 11)) + { + case 0x000: status = WOR (instr); break; + case 0x011: status = TMCR (state, instr); break; + case 0x100: status = WXOR (instr); break; + case 0x111: status = TMRC (state, instr); break; + case 0x300: status = WANDN (instr); break; + case 0x200: status = WAND (instr); break; + + case 0x810: case 0xa10: + status = WMADD (instr); break; + + case 0x10e: case 0x50e: case 0x90e: case 0xd0e: + status = WUNPCKIL (state, instr); break; + case 0x10c: case 0x50c: case 0x90c: case 0xd0c: + status = WUNPCKIH (state, instr); break; + case 0x012: case 0x112: case 0x412: case 0x512: + status = WSAD (instr); break; + case 0x010: case 0x110: case 0x210: case 0x310: + status = WMUL (instr); break; + case 0x410: case 0x510: case 0x610: case 0x710: + status = WMAC (instr); break; + case 0x006: case 0x406: case 0x806: case 0xc06: + status = WCMPEQ (state, instr); break; + case 0x800: case 0x900: case 0xc00: case 0xd00: + status = WAVG2 (instr); break; + case 0x802: case 0x902: case 0xa02: case 0xb02: + status = WALIGNR (state, instr); break; + case 0x601: case 0x605: case 0x609: case 0x60d: + status = TINSR (state, instr); break; + case 0x107: case 0x507: case 0x907: case 0xd07: + status = TEXTRM (state, instr); break; + case 0x117: case 0x517: case 0x917: case 0xd17: + status = TEXTRC (state, instr); break; + case 0x401: case 0x405: case 0x409: case 0x40d: + status = TBCST (state, instr); break; + case 0x113: case 0x513: case 0x913: case 0xd13: + status = TANDC (state, instr); break; + case 0x01c: case 0x41c: case 0x81c: case 0xc1c: + status = WACC (state, instr); break; + case 0x115: case 0x515: case 0x915: case 0xd15: + status = TORC (state, instr); break; + case 0x103: case 0x503: case 0x903: case 0xd03: + status = TMOVMSK (state, instr); break; + case 0x106: case 0x306: case 0x506: case 0x706: + case 0x906: case 0xb06: case 0xd06: case 0xf06: + status = WCMPGT (state, instr); break; + case 0x00e: case 0x20e: case 0x40e: case 0x60e: + case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: + status = WUNPCKEL (state, instr); break; + case 0x00c: case 0x20c: case 0x40c: case 0x60c: + case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: + status = WUNPCKEH (state, instr); break; + case 0x204: case 0x604: case 0xa04: case 0xe04: + case 0x214: case 0x614: case 0xa14: case 0xe14: + status = WSRL (state, instr); break; + case 0x004: case 0x404: case 0x804: case 0xc04: + case 0x014: case 0x414: case 0x814: case 0xc14: + status = WSRA (state, instr); break; + case 0x104: case 0x504: case 0x904: case 0xd04: + case 0x114: case 0x514: case 0x914: case 0xd14: + status = WSLL (state, instr); break; + case 0x304: case 0x704: case 0xb04: case 0xf04: + case 0x314: case 0x714: case 0xb14: case 0xf14: + status = WROR (state, instr); break; + case 0x116: case 0x316: case 0x516: case 0x716: + case 0x916: case 0xb16: case 0xd16: case 0xf16: + status = WMIN (state, instr); break; + case 0x016: case 0x216: case 0x416: case 0x616: + case 0x816: case 0xa16: case 0xc16: case 0xe16: + status = WMAX (state, instr); break; + case 0x002: case 0x102: case 0x202: case 0x302: + case 0x402: case 0x502: case 0x602: case 0x702: + status = WALIGNI (instr); break; + case 0x01a: case 0x11a: case 0x21a: case 0x31a: + case 0x41a: case 0x51a: case 0x61a: case 0x71a: + case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: + case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: + status = WSUB (state, instr); break; + case 0x01e: case 0x11e: case 0x21e: case 0x31e: + case 0x41e: case 0x51e: case 0x61e: case 0x71e: + case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: + case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: + status = WSHUFH (instr); break; + case 0x018: case 0x118: case 0x218: case 0x318: + case 0x418: case 0x518: case 0x618: case 0x718: + case 0x818: case 0x918: case 0xa18: case 0xb18: + case 0xc18: case 0xd18: case 0xe18: case 0xf18: + status = WADD (state, instr); break; + case 0x008: case 0x108: case 0x208: case 0x308: + case 0x408: case 0x508: case 0x608: case 0x708: + case 0x808: case 0x908: case 0xa08: case 0xb08: + case 0xc08: case 0xd08: case 0xe08: case 0xf08: + status = WPACK (state, instr); break; + case 0x201: case 0x203: case 0x205: case 0x207: + case 0x209: case 0x20b: case 0x20d: case 0x20f: + case 0x211: case 0x213: case 0x215: case 0x217: + case 0x219: case 0x21b: case 0x21d: case 0x21f: + switch (BITS (16, 19)) + { + case 0x0: status = TMIA (state, instr); break; + case 0x8: status = TMIAPH (state, instr); break; + case 0xc: + case 0xd: + case 0xe: + case 0xf: status = TMIAxy (state, instr); break; + default: break; + } + break; + default: + break; + } + return status; +} + +/* Process a possibly Intel(r) Wireless MMX(tm) technology instruction. + Return true if the instruction was handled. */ + +int +ARMul_HandleIwmmxt (ARMul_State * state, ARMword instr) +{ + int status = ARMul_BUSY; + + if (BITS (24, 27) == 0xe) + { + status = Process_Instruction (state, instr); + } + else if (BITS (25, 27) == 0x6) + { + if (BITS (4, 11) == 0x0 && BITS (20, 24) == 0x4) + status = TMCRR (state, instr); + else if (BITS (9, 11) == 0x0) + { + if (BIT (20) == 0x0) + status = WSTR (state, instr); + else if (BITS (20, 24) == 0x5) + status = TMRRC (state, instr); + else + status = WLDR (state, instr); + } + } + + if (status == ARMul_CANT) + { + /* If the instruction was a recognised but illegal, + perform the abort here rather than returning false. + If we return false then ARMul_MRC may be called which + will still abort, but which also perform the register + transfer... */ + ARMul_Abort (state, ARMul_UndefinedInstrV); + status = ARMul_DONE; + } + + return status == ARMul_DONE; +} + +int +Fetch_Iwmmxt_Register (unsigned int regnum, unsigned char * memory) +{ + if (regnum >= 16) + { + memcpy (memory, wC + (regnum - 16), sizeof wC [0]); + return sizeof wC [0]; + } + else + { + memcpy (memory, wR + regnum, sizeof wR [0]); + return sizeof wR [0]; + } +} + +int +Store_Iwmmxt_Register (unsigned int regnum, unsigned char * memory) +{ + if (regnum >= 16) + { + memcpy (wC + (regnum - 16), memory, sizeof wC [0]); + return sizeof wC [0]; + } + else + { + memcpy (wR + regnum, memory, sizeof wR [0]); + return sizeof wR [0]; + } +} |