| /* 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/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #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 0x71: |
| case 0x73: |
| { |
| ARMword valn, valm; |
| /* SDIV<c> <Rd>,<Rn>,<Rm> |
| UDIV<c> <Rd>,<Rn>,<Rm> |
| instr[31,28] = cond |
| instr[27,20] = 0111 0001 (SDIV), 0111 0011 (UDIV) |
| instr[21,21] = sign |
| instr[19,16] = Rn |
| instr[15,12] = 1111 |
| instr[11, 8] = Rd |
| instr[ 7, 4] = 1111 |
| instr[ 3, 0] = Rm */ |
| /* These bit-positions are confusing! |
| instr[15,12] = Rd |
| instr[11, 8] = 1111 */ |
| |
| #if 0 /* This is what I would expect: */ |
| Rn = BITS (16, 19); |
| Rd = BITS (8, 11); |
| Rm = BITS (0, 3); |
| #else /* This seem to work: */ |
| Rd = BITS (16, 19); |
| Rm = BITS (8, 11); |
| Rn = BITS (0, 3); |
| #endif |
| if (Rn == 15 || Rd == 15 || Rm == 15 |
| || Rn == 13 || Rd == 13 || Rm == 13) |
| { |
| ARMul_UndefInstr (state, instr); |
| state->Emulate = FALSE; |
| break; |
| } |
| |
| valn = state->Reg[Rn]; |
| valm = state->Reg[Rm]; |
| |
| if (valm == 0) |
| { |
| #if 0 |
| /* Exceptions: UsageFault, address 20 |
| Note: UsageFault is for Cortex-M; I don't know what it would be on non-Cortex-M. */ |
| ARMul_Abort (state, address); |
| #endif |
| printf ("Unhandled v6 insn: %cDIV divide by zero exception\n", "SU"[BIT(21)]); |
| } |
| else |
| { |
| if(BIT(21)) |
| { |
| val = valn / valm; |
| } |
| else |
| { |
| val = ((ARMsword)valn / (ARMsword)valm); |
| } |
| state->Reg[Rd] = val; |
| } |
| 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. */ |
| |
| 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)) |
| |