summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.common2
-rw-r--r--cpu_orig.c4090
2 files changed, 4091 insertions, 1 deletions
diff --git a/Makefile.common b/Makefile.common
index 3a42edc..d7a0c96 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -6,7 +6,7 @@ ifeq ($(HAVE_GRIFFIN), 1)
SOURCES_C := $(CORE_DIR)/gpsp_griffin.c
else
SOURCES_C := $(CORE_DIR)/main.c \
- $(CORE_DIR)/cpu.c \
+ $(CORE_DIR)/cpu_orig.c \
$(CORE_DIR)/gba_memory.c \
$(CORE_DIR)/video.c \
$(CORE_DIR)/input.c \
diff --git a/cpu_orig.c b/cpu_orig.c
new file mode 100644
index 0000000..9b1f3c2
--- /dev/null
+++ b/cpu_orig.c
@@ -0,0 +1,4090 @@
+/* gameplaySP
+ *
+ * Copyright (C) 2006 Exophase <exophase@gmail.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+// Important todo:
+// - stm reglist writeback when base is in the list needs adjustment
+// - block memory needs psr swapping and user mode reg swapping
+
+#include "common.h"
+
+u32 memory_region_access_read_u8[16];
+u32 memory_region_access_read_s8[16];
+u32 memory_region_access_read_u16[16];
+u32 memory_region_access_read_s16[16];
+u32 memory_region_access_read_u32[16];
+u32 memory_region_access_write_u8[16];
+u32 memory_region_access_write_u16[16];
+u32 memory_region_access_write_u32[16];
+u32 memory_reads_u8;
+u32 memory_reads_s8;
+u32 memory_reads_u16;
+u32 memory_reads_s16;
+u32 memory_reads_u32;
+u32 memory_writes_u8;
+u32 memory_writes_u16;
+u32 memory_writes_u32;
+
+const u8 bit_count[256] =
+{
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3,
+ 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4,
+ 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2,
+ 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,
+ 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4,
+ 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
+ 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2,
+ 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
+ 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
+ 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5,
+ 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6,
+ 7, 7, 8
+};
+
+
+#ifdef REGISTER_USAGE_ANALYZE
+
+u64 instructions_total = 0;
+
+u64 arm_reg_freq[16];
+u64 arm_reg_access_total = 0;
+u64 arm_instructions_total = 0;
+
+u64 thumb_reg_freq[16];
+u64 thumb_reg_access_total = 0;
+u64 thumb_instructions_total = 0;
+
+// mla/long mla's addition operand are not counted yet.
+
+#define using_register(instruction_set, register, type) \
+ instruction_set##_reg_freq[register]++; \
+ instruction_set##_reg_access_total++ \
+
+#define using_register_list(instruction_set, rlist, count) \
+{ \
+ u32 i; \
+ for(i = 0; i < count; i++) \
+ { \
+ if((reg_list >> i) & 0x01) \
+ { \
+ using_register(instruction_set, i, memory_target); \
+ } \
+ } \
+} \
+
+#define using_instruction(instruction_set) \
+ instruction_set##_instructions_total++; \
+ instructions_total++ \
+
+int sort_tagged_element(const void *_a, const void *_b)
+{
+ const u64 *a = _a;
+ const u64 *b = _b;
+
+ return (int)(b[1] - a[1]);
+}
+
+void print_register_usage(void)
+{
+ u32 i;
+ u64 arm_reg_freq_tagged[32];
+ u64 thumb_reg_freq_tagged[32];
+ double percent;
+ double percent_total = 0.0;
+
+ for(i = 0; i < 16; i++)
+ {
+ arm_reg_freq_tagged[i * 2] = i;
+ arm_reg_freq_tagged[(i * 2) + 1] = arm_reg_freq[i];
+ thumb_reg_freq_tagged[i * 2] = i;
+ thumb_reg_freq_tagged[(i * 2) + 1] = thumb_reg_freq[i];
+ }
+
+ qsort(arm_reg_freq_tagged, 16, sizeof(u64) * 2, sort_tagged_element);
+ qsort(thumb_reg_freq_tagged, 16, sizeof(u64) * 2, sort_tagged_element);
+
+ printf("ARM register usage (%lf%% ARM instructions):\n",
+ (arm_instructions_total * 100.0) / instructions_total);
+ for(i = 0; i < 16; i++)
+ {
+ percent = (arm_reg_freq_tagged[(i * 2) + 1] * 100.0) /
+ arm_reg_access_total;
+ percent_total += percent;
+ printf("r%02d: %lf%% (-- %lf%%)\n",
+ (u32)arm_reg_freq_tagged[(i * 2)], percent, percent_total);
+ }
+
+ percent_total = 0.0;
+
+ printf("\nThumb register usage (%lf%% Thumb instructions):\n",
+ (thumb_instructions_total * 100.0) / instructions_total);
+ for(i = 0; i < 16; i++)
+ {
+ percent = (thumb_reg_freq_tagged[(i * 2) + 1] * 100.0) /
+ thumb_reg_access_total;
+ percent_total += percent;
+ printf("r%02d: %lf%% (-- %lf%%)\n",
+ (u32)thumb_reg_freq_tagged[(i * 2)], percent, percent_total);
+ }
+
+ memset(arm_reg_freq, 0, sizeof(u64) * 16);
+ memset(thumb_reg_freq, 0, sizeof(u64) * 16);
+ arm_reg_access_total = 0;
+ thumb_reg_access_total = 0;
+}
+
+#else
+
+#define using_register(instruction_set, register, type) \
+
+#define using_register_list(instruction_set, rlist, count) \
+
+#define using_instruction(instruction_set) \
+
+#endif
+
+
+#define arm_decode_data_proc_reg(opcode) \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rd, op_dest); \
+ using_register(arm, rn, op_src); \
+ using_register(arm, rm, op_src) \
+
+#define arm_decode_data_proc_imm(opcode) \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 imm; \
+ ror(imm, opcode & 0xFF, ((opcode >> 8) & 0x0F) * 2); \
+ using_register(arm, rd, op_dest); \
+ using_register(arm, rn, op_src) \
+
+#define arm_decode_psr_reg(opcode) \
+ u32 psr_field = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rd, op_dest); \
+ using_register(arm, rm, op_src) \
+
+#define arm_decode_psr_imm(opcode) \
+ u32 psr_field = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 imm; \
+ ror(imm, opcode & 0xFF, ((opcode >> 8) & 0x0F) * 2); \
+ using_register(arm, rd, op_dest) \
+
+#define arm_decode_branchx(opcode) \
+ u32 rn = opcode & 0x0F; \
+ using_register(arm, rn, branch_target) \
+
+#define arm_decode_multiply() \
+ u32 rd = (opcode >> 16) & 0x0F; \
+ u32 rn = (opcode >> 12) & 0x0F; \
+ u32 rs = (opcode >> 8) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rd, op_dest); \
+ using_register(arm, rn, op_src); \
+ using_register(arm, rm, op_src) \
+
+#define arm_decode_multiply_long() \
+ u32 rdhi = (opcode >> 16) & 0x0F; \
+ u32 rdlo = (opcode >> 12) & 0x0F; \
+ u32 rn = (opcode >> 8) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rdhi, op_dest); \
+ using_register(arm, rdlo, op_dest); \
+ using_register(arm, rn, op_src); \
+ using_register(arm, rm, op_src) \
+
+#define arm_decode_swap() \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rd, memory_target); \
+ using_register(arm, rn, memory_base); \
+ using_register(arm, rm, memory_target) \
+
+#define arm_decode_half_trans_r() \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rd, memory_target); \
+ using_register(arm, rn, memory_base); \
+ using_register(arm, rm, memory_offset) \
+
+#define arm_decode_half_trans_of() \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 offset = ((opcode >> 4) & 0xF0) | (opcode & 0x0F); \
+ using_register(arm, rd, memory_target); \
+ using_register(arm, rn, memory_base) \
+
+#define arm_decode_data_trans_imm() \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 offset = opcode & 0x0FFF; \
+ using_register(arm, rd, memory_target); \
+ using_register(arm, rn, memory_base) \
+
+#define arm_decode_data_trans_reg() \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 rd = (opcode >> 12) & 0x0F; \
+ u32 rm = opcode & 0x0F; \
+ using_register(arm, rd, memory_target); \
+ using_register(arm, rn, memory_base); \
+ using_register(arm, rm, memory_offset) \
+
+#define arm_decode_block_trans() \
+ u32 rn = (opcode >> 16) & 0x0F; \
+ u32 reg_list = opcode & 0xFFFF; \
+ using_register(arm, rn, memory_base); \
+ using_register_list(arm, reg_list, 16) \
+
+#define arm_decode_branch() \
+ s32 offset = ((s32)(opcode & 0xFFFFFF) << 8) >> 6 \
+
+
+#define thumb_decode_shift() \
+ u32 imm = (opcode >> 6) & 0x1F; \
+ u32 rs = (opcode >> 3) & 0x07; \
+ u32 rd = opcode & 0x07; \
+ using_register(thumb, rd, op_dest); \
+ using_register(thumb, rs, op_shift) \
+
+#define thumb_decode_add_sub() \
+ u32 rn = (opcode >> 6) & 0x07; \
+ u32 rs = (opcode >> 3) & 0x07; \
+ u32 rd = opcode & 0x07; \
+ using_register(thumb, rd, op_dest); \
+ using_register(thumb, rn, op_src); \
+ using_register(thumb, rn, op_src) \
+
+#define thumb_decode_add_sub_imm() \
+ u32 imm = (opcode >> 6) & 0x07; \
+ u32 rs = (opcode >> 3) & 0x07; \
+ u32 rd = opcode & 0x07; \
+ using_register(thumb, rd, op_src_dest); \
+ using_register(thumb, rs, op_src) \
+
+#define thumb_decode_imm() \
+ u32 imm = opcode & 0xFF; \
+ using_register(thumb, ((opcode >> 8) & 0x07), op_dest) \
+
+#define thumb_decode_alu_op() \
+ u32 rs = (opcode >> 3) & 0x07; \
+ u32 rd = opcode & 0x07; \
+ using_register(thumb, rd, op_src_dest); \
+ using_register(thumb, rs, op_src) \
+
+#define thumb_decode_hireg_op() \
+ u32 rs = (opcode >> 3) & 0x0F; \
+ u32 rd = ((opcode >> 4) & 0x08) | (opcode & 0x07); \
+ using_register(thumb, rd, op_src_dest); \
+ using_register(thumb, rs, op_src) \
+
+
+#define thumb_decode_mem_reg() \
+ u32 ro = (opcode >> 6) & 0x07; \
+ u32 rb = (opcode >> 3) & 0x07; \
+ u32 rd = opcode & 0x07; \
+ using_register(thumb, rd, memory_target); \
+ using_register(thumb, rb, memory_base); \
+ using_register(thumb, ro, memory_offset) \
+
+
+#define thumb_decode_mem_imm() \
+ u32 imm = (opcode >> 6) & 0x1F; \
+ u32 rb = (opcode >> 3) & 0x07; \
+ u32 rd = opcode & 0x07; \
+ using_register(thumb, rd, memory_target); \
+ using_register(thumb, rb, memory_base) \
+
+
+#define thumb_decode_add_sp() \
+ u32 imm = opcode & 0x7F; \
+ using_register(thumb, REG_SP, op_dest) \
+
+#define thumb_decode_rlist() \
+ u32 reg_list = opcode & 0xFF; \
+ using_register_list(thumb, rlist, 8) \
+
+#define thumb_decode_branch_cond() \
+ s32 offset = (s8)(opcode & 0xFF) \
+
+#define thumb_decode_swi() \
+ u32 comment = opcode & 0xFF \
+
+#define thumb_decode_branch() \
+ u32 offset = opcode & 0x07FF \
+
+
+#define get_shift_register(dest) \
+ u32 shift = reg[(opcode >> 8) & 0x0F]; \
+ using_register(arm, ((opcode >> 8) & 0x0F), op_shift); \
+ dest = reg[rm]; \
+ if(rm == 15) \
+ dest += 4 \
+
+
+#define calculate_z_flag(dest) \
+ z_flag = (dest == 0) \
+
+#define calculate_n_flag(dest) \
+ n_flag = ((signed)dest < 0) \
+
+#define calculate_c_flag_sub(dest, src_a, src_b) \
+ c_flag = ((unsigned)src_b <= (unsigned)src_a) \
+
+#define calculate_v_flag_sub(dest, src_a, src_b) \
+ v_flag = ((signed)src_b > (signed)src_a) != ((signed)dest < 0) \
+
+#define calculate_c_flag_add(dest, src_a, src_b) \
+ c_flag = ((unsigned)dest < (unsigned)src_a) \
+
+#define calculate_v_flag_add(dest, src_a, src_b) \
+ v_flag = ((signed)dest < (signed)src_a) != ((signed)src_b < 0) \
+
+
+#define calculate_reg_sh() \
+ u32 reg_sh = 0; \
+ switch((opcode >> 4) & 0x07) \
+ { \
+ /* LSL imm */ \
+ case 0x0: \
+ { \
+ reg_sh = reg[rm] << ((opcode >> 7) & 0x1F); \
+ break; \
+ } \
+ \
+ /* LSL reg */ \
+ case 0x1: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift <= 31) \
+ reg_sh = reg_sh << shift; \
+ else \
+ reg_sh = 0; \
+ break; \
+ } \
+ \
+ /* LSR imm */ \
+ case 0x2: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ if(imm == 0) \
+ reg_sh = 0; \
+ else \
+ reg_sh = reg[rm] >> imm; \
+ break; \
+ } \
+ \
+ /* LSR reg */ \
+ case 0x3: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift <= 31) \
+ reg_sh = reg_sh >> shift; \
+ else \
+ reg_sh = 0; \
+ break; \
+ } \
+ \
+ /* ASR imm */ \
+ case 0x4: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ reg_sh = reg[rm]; \
+ \
+ if(imm == 0) \
+ reg_sh = (s32)reg_sh >> 31; \
+ else \
+ reg_sh = (s32)reg_sh >> imm; \
+ break; \
+ } \
+ \
+ /* ASR reg */ \
+ case 0x5: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift <= 31) \
+ reg_sh = (s32)reg_sh >> shift; \
+ else \
+ reg_sh = (s32)reg_sh >> 31; \
+ break; \
+ } \
+ \
+ /* ROR imm */ \
+ case 0x6: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ \
+ if(imm == 0) \
+ reg_sh = (reg[rm] >> 1) | (c_flag << 31); \
+ else \
+ ror(reg_sh, reg[rm], imm); \
+ break; \
+ } \
+ \
+ /* ROR reg */ \
+ case 0x7: \
+ { \
+ get_shift_register(reg_sh); \
+ ror(reg_sh, reg_sh, shift); \
+ break; \
+ } \
+ } \
+
+#define calculate_reg_sh_flags() \
+ u32 reg_sh = 0; \
+ switch((opcode >> 4) & 0x07) \
+ { \
+ /* LSL imm */ \
+ case 0x0: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ reg_sh = reg[rm]; \
+ \
+ if(imm != 0) \
+ { \
+ c_flag = (reg_sh >> (32 - imm)) & 0x01; \
+ reg_sh <<= imm; \
+ } \
+ \
+ break; \
+ } \
+ \
+ /* LSL reg */ \
+ case 0x1: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift != 0) \
+ { \
+ if(shift > 31) \
+ { \
+ if(shift == 32) \
+ c_flag = reg_sh & 0x01; \
+ else \
+ c_flag = 0; \
+ reg_sh = 0; \
+ } \
+ else \
+ { \
+ c_flag = (reg_sh >> (32 - shift)) & 0x01; \
+ reg_sh <<= shift; \
+ } \
+ } \
+ break; \
+ } \
+ \
+ /* LSR imm */ \
+ case 0x2: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ reg_sh = reg[rm]; \
+ if(imm == 0) \
+ { \
+ c_flag = reg_sh >> 31; \
+ reg_sh = 0; \
+ } \
+ else \
+ { \
+ c_flag = (reg_sh >> (imm - 1)) & 0x01; \
+ reg_sh >>= imm; \
+ } \
+ break; \
+ } \
+ \
+ /* LSR reg */ \
+ case 0x3: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift != 0) \
+ { \
+ if(shift > 31) \
+ { \
+ if(shift == 32) \
+ c_flag = (reg_sh >> 31) & 0x01; \
+ else \
+ c_flag = 0; \
+ reg_sh = 0; \
+ } \
+ else \
+ { \
+ c_flag = (reg_sh >> (shift - 1)) & 0x01; \
+ reg_sh >>= shift; \
+ } \
+ } \
+ break; \
+ } \
+ \
+ /* ASR imm */ \
+ case 0x4: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ reg_sh = reg[rm]; \
+ if(imm == 0) \
+ { \
+ reg_sh = (s32)reg_sh >> 31; \
+ c_flag = reg_sh & 0x01; \
+ } \
+ else \
+ { \
+ c_flag = (reg_sh >> (imm - 1)) & 0x01; \
+ reg_sh = (s32)reg_sh >> imm; \
+ } \
+ break; \
+ } \
+ \
+ /* ASR reg */ \
+ case 0x5: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift != 0) \
+ { \
+ if(shift > 31) \
+ { \
+ reg_sh = (s32)reg_sh >> 31; \
+ c_flag = reg_sh & 0x01; \
+ } \
+ else \
+ { \
+ c_flag = (reg_sh >> (shift - 1)) & 0x01; \
+ reg_sh = (s32)reg_sh >> shift; \
+ } \
+ } \
+ break; \
+ } \
+ \
+ /* ROR imm */ \
+ case 0x6: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ reg_sh = reg[rm]; \
+ if(imm == 0) \
+ { \
+ u32 old_c_flag = c_flag; \
+ c_flag = reg_sh & 0x01; \
+ reg_sh = (reg_sh >> 1) | (old_c_flag << 31); \
+ } \
+ else \
+ { \
+ c_flag = (reg_sh >> (imm - 1)) & 0x01; \
+ ror(reg_sh, reg_sh, imm); \
+ } \
+ break; \
+ } \
+ \
+ /* ROR reg */ \
+ case 0x7: \
+ { \
+ get_shift_register(reg_sh); \
+ if(shift != 0) \
+ { \
+ c_flag = (reg_sh >> (shift - 1)) & 0x01; \
+ ror(reg_sh, reg_sh, shift); \
+ } \
+ break; \
+ } \
+ } \
+
+#define calculate_reg_offset() \
+ u32 reg_offset = 0; \
+ switch((opcode >> 5) & 0x03) \
+ { \
+ /* LSL imm */ \
+ case 0x0: \
+ { \
+ reg_offset = reg[rm] << ((opcode >> 7) & 0x1F); \
+ break; \
+ } \
+ \
+ /* LSR imm */ \
+ case 0x1: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ if(imm == 0) \
+ reg_offset = 0; \
+ else \
+ reg_offset = reg[rm] >> imm; \
+ break; \
+ } \
+ \
+ /* ASR imm */ \
+ case 0x2: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ if(imm == 0) \
+ reg_offset = (s32)reg[rm] >> 31; \
+ else \
+ reg_offset = (s32)reg[rm] >> imm; \
+ break; \
+ } \
+ \
+ /* ROR imm */ \
+ case 0x3: \
+ { \
+ u32 imm = (opcode >> 7) & 0x1F; \
+ if(imm == 0) \
+ reg_offset = (reg[rm] >> 1) | (c_flag << 31); \
+ else \
+ ror(reg_offset, reg[rm], imm); \
+ break; \
+ } \
+ } \
+
+#define calculate_flags_add(dest, src_a, src_b) \
+ calculate_z_flag(dest); \
+ calculate_n_flag(dest); \
+ calculate_c_flag_add(dest, src_a, src_b); \
+ calculate_v_flag_add(dest, src_a, src_b) \
+
+#define calculate_flags_sub(dest, src_a, src_b) \
+ calculate_z_flag(dest); \
+ calculate_n_flag(dest); \
+ calculate_c_flag_sub(dest, src_a, src_b); \
+ calculate_v_flag_sub(dest, src_a, src_b) \
+
+#define calculate_flags_logic(dest) \
+ calculate_z_flag(dest); \
+ calculate_n_flag(dest) \
+
+#define extract_flags() \
+ n_flag = reg[REG_CPSR] >> 31; \
+ z_flag = (reg[REG_CPSR] >> 30) & 0x01; \
+ c_flag = (reg[REG_CPSR] >> 29) & 0x01; \
+ v_flag = (reg[REG_CPSR] >> 28) & 0x01; \
+
+#define collapse_flags() \
+ reg[REG_CPSR] = (n_flag << 31) | (z_flag << 30) | (c_flag << 29) | \
+ (v_flag << 28) | (reg[REG_CPSR] & 0xFF) \
+
+#define memory_region(r_dest, l_dest, address) \
+ r_dest = memory_regions[address >> 24]; \
+ l_dest = memory_limits[address >> 24] \
+
+
+#define pc_region() \
+ memory_region(pc_region, pc_limit, pc) \
+
+#define check_pc_region() \
+ new_pc_region = (pc >> 15); \
+ if(new_pc_region != pc_region) \
+ { \
+ pc_region = new_pc_region; \
+ pc_address_block = memory_map_read[new_pc_region]; \
+ \
+ if(!pc_address_block) \
+ pc_address_block = load_gamepak_page(pc_region & 0x3FF); \
+ } \
+
+u32 branch_targets = 0;
+u32 high_frequency_branch_targets = 0;
+
+#define BRANCH_ACTIVITY_THRESHOLD 50
+
+#define arm_update_pc() \
+ pc = reg[REG_PC] \
+
+#define arm_pc_offset(val) \
+ pc += val; \
+ reg[REG_PC] = pc \
+
+#define arm_pc_offset_update(val) \
+ pc += val; \
+ reg[REG_PC] = pc \
+
+#define arm_pc_offset_update_direct(val) \
+ pc = val; \
+ reg[REG_PC] = pc \
+
+
+// It should be okay to still generate result flags, spsr will overwrite them.
+// This is pretty infrequent (returning from interrupt handlers, et al) so
+// probably not worth optimizing for.
+
+#define check_for_interrupts() \
+ if((io_registers[REG_IE] & io_registers[REG_IF]) && \
+ io_registers[REG_IME] && ((reg[REG_CPSR] & 0x80) == 0)) \
+ { \
+ reg_mode[MODE_IRQ][6] = reg[REG_PC] + 4; \
+ spsr[MODE_IRQ] = reg[REG_CPSR]; \
+ reg[REG_CPSR] = 0xD2; \
+ reg[REG_PC] = 0x00000018; \
+ arm_update_pc(); \
+ set_cpu_mode(MODE_IRQ); \
+ goto arm_loop; \
+ } \
+
+#define arm_spsr_restore() \
+ if(rd == 15) \
+ { \
+ if(reg[CPU_MODE] != MODE_USER) \
+ { \
+ reg[REG_CPSR] = spsr[reg[CPU_MODE]]; \
+ extract_flags(); \
+ set_cpu_mode(cpu_modes[reg[REG_CPSR] & 0x1F]); \
+ check_for_interrupts(); \
+ } \
+ arm_update_pc(); \
+ \
+ if(reg[REG_CPSR] & 0x20) \
+ goto thumb_loop; \
+ } \
+
+#define arm_data_proc_flags_reg() \
+ arm_decode_data_proc_reg(opcode); \
+ calculate_reg_sh_flags() \
+
+#define arm_data_proc_reg() \
+ arm_decode_data_proc_reg(opcode); \
+ calculate_reg_sh() \
+
+#define arm_data_proc_flags_imm() \
+ arm_decode_data_proc_imm(opcode) \
+
+#define arm_data_proc_imm() \
+ arm_decode_data_proc_imm(opcode) \
+
+#define arm_data_proc(expr, type) \
+{ \
+ u32 dest; \
+ arm_pc_offset(8); \
+ arm_data_proc_##type(); \
+ dest = expr; \
+ arm_pc_offset(-4); \
+ reg[rd] = dest; \
+ \
+ if(rd == 15) \
+ { \
+ arm_update_pc(); \
+ } \
+} \
+
+#define flags_vars(src_a, src_b) \
+ u32 dest; \
+ const u32 _sa = src_a; \
+ const u32 _sb = src_b \
+
+#define arm_data_proc_logic_flags(expr, type) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_proc_flags_##type(); \
+ u32 dest = expr; \
+ calculate_flags_logic(dest); \
+ arm_pc_offset(-4); \
+ reg[rd] = dest; \
+ arm_spsr_restore(); \
+} \
+
+#define arm_data_proc_add_flags(src_a, src_b, type) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_proc_##type(); \
+ flags_vars(src_a, src_b); \
+ dest = _sa + _sb; \
+ calculate_flags_add(dest, _sa, _sb); \
+ arm_pc_offset(-4); \
+ reg[rd] = dest; \
+ arm_spsr_restore(); \
+}
+
+#define arm_data_proc_sub_flags(src_a, src_b, type) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_proc_##type(); \
+ flags_vars(src_a, src_b); \
+ dest = _sa - _sb; \
+ calculate_flags_sub(dest, _sa, _sb); \
+ arm_pc_offset(-4); \
+ reg[rd] = dest; \
+ arm_spsr_restore(); \
+} \
+
+#define arm_data_proc_test_logic(expr, type) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_proc_flags_##type(); \
+ u32 dest = expr; \
+ calculate_flags_logic(dest); \
+ arm_pc_offset(-4); \
+} \
+
+#define arm_data_proc_test_add(src_a, src_b, type) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_proc_##type(); \
+ flags_vars(src_a, src_b); \
+ dest = _sa + _sb; \
+ calculate_flags_add(dest, _sa, _sb); \
+ arm_pc_offset(-4); \
+} \
+
+#define arm_data_proc_test_sub(src_a, src_b, type) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_proc_##type(); \
+ flags_vars(src_a, src_b); \
+ dest = _sa - _sb; \
+ calculate_flags_sub(dest, _sa, _sb); \
+ arm_pc_offset(-4); \
+} \
+
+#define arm_multiply_flags_yes(_dest) \
+ calculate_z_flag(_dest); \
+ calculate_n_flag(_dest); \
+
+#define arm_multiply_flags_no(_dest) \
+
+#define arm_multiply_long_flags_yes(_dest_lo, _dest_hi) \
+ z_flag = (_dest_lo == 0) & (_dest_hi == 0); \
+ calculate_n_flag(_dest_hi) \
+
+#define arm_multiply_long_flags_no(_dest_lo, _dest_hi) \
+
+#define arm_multiply(add_op, flags) \
+{ \
+ u32 dest; \
+ arm_decode_multiply(); \
+ dest = (reg[rm] * reg[rs]) add_op; \
+ arm_multiply_flags_##flags(dest); \
+ reg[rd] = dest; \
+ arm_pc_offset(4); \
+} \
+
+#define arm_multiply_long_addop(type) \
+ + ((type##64)((((type##64)reg[rdhi]) << 32) | reg[rdlo])); \
+
+#define arm_multiply_long(add_op, flags, type) \
+{ \
+ type##64 dest; \
+ u32 dest_lo; \
+ u32 dest_hi; \
+ arm_decode_multiply_long(); \
+ dest = ((type##64)((type##32)reg[rm]) * \
+ (type##64)((type##32)reg[rn])) add_op; \
+ dest_lo = (u32)dest; \
+ dest_hi = (u32)(dest >> 32); \
+ arm_multiply_long_flags_##flags(dest_lo, dest_hi); \
+ reg[rdlo] = dest_lo; \
+ reg[rdhi] = dest_hi; \
+ arm_pc_offset(4); \
+} \
+
+const u32 psr_masks[16] =
+{
+ 0x00000000, 0x000000FF, 0x0000FF00, 0x0000FFFF, 0x00FF0000,
+ 0x00FF00FF, 0x00FFFF00, 0x00FFFFFF, 0xFF000000, 0xFF0000FF,
+ 0xFF00FF00, 0xFF00FFFF, 0xFFFF0000, 0xFFFF00FF, 0xFFFFFF00,
+ 0xFFFFFFFF
+};
+
+#define arm_psr_read(dummy, psr_reg) \
+ collapse_flags(); \
+ reg[rd] = psr_reg \
+
+#define arm_psr_store_cpsr(source) \
+ reg[REG_CPSR] = (source & store_mask) | (reg[REG_CPSR] & (~store_mask)); \
+ extract_flags(); \
+ if(store_mask & 0xFF) \
+ { \
+ set_cpu_mode(cpu_modes[reg[REG_CPSR] & 0x1F]); \
+ check_for_interrupts(); \
+ } \
+
+#define arm_psr_store_spsr(source) \
+ u32 _psr = spsr[reg[CPU_MODE]]; \
+ spsr[reg[CPU_MODE]] = (source & store_mask) | (_psr & (~store_mask)) \
+
+#define arm_psr_store(source, psr_reg) \
+ const u32 store_mask = psr_masks[psr_field]; \
+ arm_psr_store_##psr_reg(source) \
+
+#define arm_psr_src_reg reg[rm]
+
+#define arm_psr_src_imm imm
+
+#define arm_psr(op_type, transfer_type, psr_reg) \
+{ \
+ arm_decode_psr_##op_type(opcode); \
+ arm_pc_offset(4); \
+ arm_psr_##transfer_type(arm_psr_src_##op_type, psr_reg); \
+} \
+
+#define arm_data_trans_reg() \
+ arm_decode_data_trans_reg(); \
+ calculate_reg_offset() \
+
+#define arm_data_trans_imm() \
+ arm_decode_data_trans_imm() \
+
+#define arm_data_trans_half_reg() \
+ arm_decode_half_trans_r() \
+
+#define arm_data_trans_half_imm() \
+ arm_decode_half_trans_of() \
+
+#define aligned_address_mask8 0xF0000000
+#define aligned_address_mask16 0xF0000001
+#define aligned_address_mask32 0xF0000003
+
+#define fast_read_memory(size, type, address, dest) \
+{ \
+ u8 *map; \
+ u32 _address = address; \
+ \
+ if(_address < 0x10000000) \
+ { \
+ memory_region_access_read_##type[_address >> 24]++; \
+ memory_reads_##type++; \
+ } \
+ if(((_address >> 24) == 0) && (pc >= 0x4000)) \
+ { \
+ dest = *((type *)((u8 *)&bios_read_protect + (_address & 0x03))); \
+ } \
+ else \
+ \
+ if(((_address & aligned_address_mask##size) == 0) && \
+ (map = memory_map_read[_address >> 15])) \
+ { \
+ dest = *((type *)((u8 *)map + (_address & 0x7FFF))); \
+ } \
+ else \
+ { \
+ dest = (type)read_memory##size(_address); \
+ } \
+} \
+
+#define fast_read_memory_s16(address, dest) \
+{ \
+ u8 *map; \
+ u32 _address = address; \
+ if(_address < 0x10000000) \
+ { \
+ memory_region_access_read_s16[_address >> 24]++; \
+ memory_reads_s16++; \
+ } \
+ if(((_address & aligned_address_mask16) == 0) && \
+ (map = memory_map_read[_address >> 15])) \
+ { \
+ dest = *((s16 *)((u8 *)map + (_address & 0x7FFF))); \
+ } \
+ else \
+ { \
+ dest = (s16)read_memory16_signed(_address); \
+ } \
+} \
+
+
+#define fast_write_memory(size, type, address, value) \
+{ \
+ u8 *map; \
+ u32 _address = (address) & ~(aligned_address_mask##size & 0x03); \
+ if(_address < 0x10000000) \
+ { \
+ memory_region_access_write_##type[_address >> 24]++; \
+ memory_writes_##type++; \
+ } \
+ \
+ if(((_address & aligned_address_mask##size) == 0) && \
+ (map = memory_map_write[_address >> 15])) \
+ { \
+ *((type *)((u8 *)map + (_address & 0x7FFF))) = value; \
+ } \
+ else \
+ { \
+ cpu_alert = write_memory##size(_address, value); \
+ if(cpu_alert) \
+ goto alert; \
+ } \
+} \
+
+#define load_aligned32(address, dest) \
+{ \
+ u32 _address = address; \
+ u8 *map = memory_map_read[_address >> 15]; \
+ if(_address < 0x10000000) \
+ { \
+ memory_region_access_read_u32[_address >> 24]++; \
+ memory_reads_u32++; \
+ } \
+ if(map) \
+ { \
+ dest = address32(map, _address & 0x7FFF); \
+ } \
+ else \
+ { \
+ dest = read_memory32(_address); \
+ } \
+} \
+
+#define store_aligned32(address, value) \
+{ \
+ u32 _address = address; \
+ u8 *map = memory_map_write[_address >> 15]; \
+ if(_address < 0x10000000) \
+ { \
+ memory_region_access_write_u32[_address >> 24]++; \
+ memory_writes_u32++; \
+ } \
+ if(map) \
+ { \
+ address32(map, _address & 0x7FFF) = value; \
+ } \
+ else \
+ { \
+ cpu_alert = write_memory32(_address, value); \
+ if(cpu_alert) \
+ goto alert; \
+ } \
+} \
+
+#define load_memory_u8(address, dest) \
+ fast_read_memory(8, u8, address, dest) \
+
+#define load_memory_u16(address, dest) \
+ fast_read_memory(16, u16, address, dest) \
+
+#define load_memory_u32(address, dest) \
+ fast_read_memory(32, u32, address, dest) \
+
+#define load_memory_s8(address, dest) \
+ fast_read_memory(8, s8, address, dest) \
+
+#define load_memory_s16(address, dest) \
+ fast_read_memory_s16(address, dest) \
+
+#define store_memory_u8(address, value) \
+ fast_write_memory(8, u8, address, value) \
+
+#define store_memory_u16(address, value) \
+ fast_write_memory(16, u16, address, value) \
+
+#define store_memory_u32(address, value) \
+ fast_write_memory(32, u32, address, value) \
+
+#define no_op \
+
+#define arm_access_memory_writeback_yes(off_op) \
+ reg[rn] = address off_op \
+
+#define arm_access_memory_writeback_no(off_op) \
+
+#define arm_access_memory_pc_preadjust_load() \
+
+#define arm_access_memory_pc_preadjust_store() \
+ u32 reg_op = reg[rd]; \
+ if(rd == 15) \
+ reg_op += 4 \
+
+#define arm_access_memory_pc_postadjust_load() \
+ arm_update_pc() \
+
+#define arm_access_memory_pc_postadjust_store() \
+
+#define load_reg_op reg[rd] \
+
+#define store_reg_op reg_op \
+
+#define arm_access_memory(access_type, off_op, off_type, mem_type, \
+ wb, wb_off_op) \
+{ \
+ arm_pc_offset(8); \
+ arm_data_trans_##off_type(); \
+ u32 address = reg[rn] off_op; \
+ arm_access_memory_pc_preadjust_##access_type(); \
+ \
+ arm_pc_offset(-4); \
+ arm_access_memory_writeback_##wb(wb_off_op); \
+ access_type##_memory_##mem_type(address, access_type##_reg_op); \
+ arm_access_memory_pc_postadjust_##access_type(); \
+} \
+
+#define word_bit_count(word) \
+ (bit_count[word >> 8] + bit_count[word & 0xFF]) \
+
+#define sprint_no(access_type, offset_type, writeback_type) \
+
+#define sprint_yes(access_type, offset_type, writeback_type) \
+ printf("sbit on %s %s %s\n", #access_type, #offset_type, #writeback_type) \
+
+#define arm_block_writeback_load() \
+ if(!((reg_list >> rn) & 0x01)) \
+ { \
+ reg[rn] = address; \
+ } \
+
+#define arm_block_writeback_store() \
+ reg[rn] = address \
+
+#define arm_block_writeback_yes(access_type) \
+ arm_block_writeback_##access_type() \
+
+#define arm_block_writeback_no(access_type) \
+
+#define load_block_memory(address, dest) \
+ dest = address32(address_region, (address + offset) & 0x7FFF) \
+
+#define store_block_memory(address, dest) \
+ address32(address_region, (address + offset) & 0x7FFF) = dest \
+
+#define arm_block_memory_offset_down_a() \
+ (base - (word_bit_count(reg_list) * 4) + 4) \
+
+#define arm_block_memory_offset_down_b() \
+ (base - (word_bit_count(reg_list) * 4)) \
+
+#define arm_block_memory_offset_no() \
+ (base) \
+
+#define arm_block_memory_offset_up() \
+ (base + 4) \
+
+#define arm_block_memory_writeback_down() \
+ reg[rn] = base - (word_bit_count(reg_list) * 4) \
+
+#define arm_block_memory_writeback_up() \
+ reg[rn] = base + (word_bit_count(reg_list) * 4) \
+
+#define arm_block_memory_writeback_no() \
+
+#define arm_block_memory_load_pc() \
+ load_aligned32(address, pc); \
+ reg[REG_PC] = pc \
+
+#define arm_block_memory_store_pc() \
+ store_aligned32(address, pc + 4) \
+
+#define arm_block_memory(access_type, offset_type, writeback_type, s_bit) \
+{ \
+ arm_decode_block_trans(); \
+ u32 base = reg[rn]; \
+ u32 address = arm_block_memory_offset_##offset_type() & 0xFFFFFFFC; \
+ u32 i; \
+ \
+ arm_block_memory_writeback_##writeback_type(); \
+ \
+ for(i = 0; i < 15; i++) \
+ { \
+ if((reg_list >> i) & 0x01) \
+ { \
+ access_type##_aligned32(address, reg[i]); \
+ address += 4; \
+ } \
+ } \
+ \
+ arm_pc_offset(4); \
+ if(reg_list & 0x8000) \
+ { \
+ arm_block_memory_##access_type##_pc(); \
+ } \
+} \
+
+#define arm_swap(type) \
+{ \
+ arm_decode_swap(); \
+ u32 temp; \
+ load_memory_##type(reg[rn], temp); \
+ store_memory_##type(reg[rn], reg[rm]); \
+ reg[rd] = temp; \
+ arm_pc_offset(4); \
+} \
+
+#define arm_next_instruction() \
+{ \
+ arm_pc_offset(4); \
+ goto skip_instruction; \
+} \
+
+#define thumb_update_pc() \
+ pc = reg[REG_PC] \
+
+#define thumb_pc_offset(val) \
+ pc += val; \
+ reg[REG_PC] = pc \
+
+#define thumb_pc_offset_update(val) \
+ pc += val; \
+ reg[REG_PC] = pc \
+
+#define thumb_pc_offset_update_direct(val) \
+ pc = val; \
+ reg[REG_PC] = pc \
+
+// Types: add_sub, add_sub_imm, alu_op, imm
+// Affects N/Z/C/V flags
+
+#define thumb_add(type, dest_reg, src_a, src_b) \
+{ \
+ thumb_decode_##type(); \
+ const u32 _sa = src_a; \
+ const u32 _sb = src_b; \
+ u32 dest = _sa + _sb; \
+ calculate_flags_add(dest, _sa, _sb); \
+ reg[dest_reg] = dest; \
+ thumb_pc_offset(2); \
+} \
+
+#define thumb_add_noflags(type, dest_reg, src_a, src_b) \
+{ \
+ thumb_decode_##type(); \
+ u32 dest = (src_a) + (src_b); \
+ reg[dest_reg] = dest; \
+ thumb_pc_offset(2); \
+} \
+
+#define thumb_sub(type, dest_reg, src_a, src_b) \
+{ \
+ thumb_decode_##type(); \
+ const u32 _sa = src_a; \
+ const u32 _sb = src_b; \
+ u32 dest = _sa - _sb; \
+ calculate_flags_sub(dest, _sa, _sb); \
+ reg[dest_reg] = dest; \
+ thumb_pc_offset(2); \
+} \
+
+// Affects N/Z flags
+
+#define thumb_logic(type, dest_reg, expr) \
+{ \
+ thumb_decode_##type(); \
+ u32 dest = expr; \
+ calculate_flags_logic(dest); \
+ reg[dest_reg] = dest; \
+ thumb_pc_offset(2); \
+} \
+
+// Decode types: shift, alu_op
+// Operation types: lsl, lsr, asr, ror
+// Affects N/Z/C flags
+
+#define thumb_shift_lsl_reg() \
+ u32 shift = reg[rs]; \
+ u32 dest = reg[rd]; \
+ if(shift != 0) \
+ { \
+ if(shift > 31) \
+ { \
+ if(shift == 32) \
+ c_flag = dest & 0x01; \
+ else \
+ c_flag = 0; \
+ dest = 0; \
+ } \
+ else \
+ { \
+ c_flag = (dest >> (32 - shift)) & 0x01; \
+ dest <<= shift; \
+ } \
+ } \
+
+#define thumb_shift_lsr_reg() \
+ u32 shift = reg[rs]; \
+ u32 dest = reg[rd]; \
+ if(shift != 0) \
+ { \
+ if(shift > 31) \
+ { \
+ if(shift == 32) \
+ c_flag = dest >> 31; \
+ else \
+ c_flag = 0; \
+ dest = 0; \
+ } \
+ else \
+ { \
+ c_flag = (dest >> (shift - 1)) & 0x01; \
+ dest >>= shift; \
+ } \
+ } \
+
+#define thumb_shift_asr_reg() \
+ u32 shift = reg[rs]; \
+ u32 dest = reg[rd]; \
+ if(shift != 0) \
+ { \
+ if(shift > 31) \
+ { \
+ dest = (s32)dest >> 31; \
+ c_flag = dest & 0x01; \
+ } \
+ else \
+ { \
+ c_flag = (dest >> (shift - 1)) & 0x01; \
+ dest = (s32)dest >> shift; \
+ } \
+ } \
+
+#define thumb_shift_ror_reg() \
+ u32 shift = reg[rs]; \
+ u32 dest = reg[rd]; \
+ if(shift != 0) \
+ { \
+ c_flag = (dest >> (shift - 1)) & 0x01; \
+ ror(dest, dest, shift); \
+ } \
+
+#define thumb_shift_lsl_imm() \
+ u32 dest = reg[rs]; \
+ if(imm != 0) \
+ { \
+ c_flag = (dest >> (32 - imm)) & 0x01; \
+ dest <<= imm; \
+ } \
+
+#define thumb_shift_lsr_imm() \
+ u32 dest; \
+ if(imm == 0) \
+ { \
+ dest = 0; \
+ c_flag = reg[rs] >> 31; \
+ } \
+ else \
+ { \
+ dest = reg[rs]; \
+ c_flag = (dest >> (imm - 1)) & 0x01; \
+ dest >>= imm; \
+ } \
+
+#define thumb_shift_asr_imm() \
+ u32 dest; \
+ if(imm == 0) \
+ { \
+ dest = (s32)reg[rs] >> 31; \
+ c_flag = dest & 0x01; \
+ } \
+ else \
+ { \
+ dest = reg[rs]; \
+ c_flag = (dest >> (imm - 1)) & 0x01; \
+ dest = (s32)dest >> imm; \
+ } \
+
+#define thumb_shift_ror_imm() \
+ u32 dest = reg[rs]; \
+ if(imm == 0) \
+ { \
+ u32 old_c_flag = c_flag; \
+ c_flag = dest & 0x01; \
+ dest = (dest >> 1) | (old_c_flag << 31); \
+ } \
+ else \
+ { \
+ c_flag = (dest >> (imm - 1)) & 0x01; \
+ ror(dest, dest, imm); \
+ } \
+
+#define thumb_shift(decode_type, op_type, value_type) \
+{ \
+ thumb_decode_##decode_type(); \
+ thumb_shift_##op_type##_##value_type(); \
+ calculate_flags_logic(dest); \
+ reg[rd] = dest; \
+ thumb_pc_offset(2); \
+} \
+
+#define thumb_test_add(type, src_a, src_b) \
+{ \
+ thumb_decode_##type(); \
+ const u32 _sa = src_a; \
+ const u32 _sb = src_b; \
+ u32 dest = _sa + _sb; \
+ calculate_flags_add(dest, src_a, src_b); \
+ thumb_pc_offset(2); \
+} \
+
+#define thumb_test_sub(type, src_a, src_b) \
+{ \
+ thumb_decode_##type(); \
+ const u32 _sa = src_a; \
+ const u32 _sb = src_b; \
+ u32 dest = _sa - _sb; \
+ calculate_flags_sub(dest, src_a, src_b); \
+ thumb_pc_offset(2); \
+} \
+
+#define thumb_test_logic(type, expr) \
+{ \
+ thumb_decode_##type(); \
+ u32 dest = expr; \
+ calculate_flags_logic(dest); \
+ thumb_pc_offset(2); \
+}
+
+#define thumb_hireg_op(expr) \
+{ \
+ thumb_pc_offset(4); \
+ thumb_decode_hireg_op(); \
+ u32 dest = expr; \
+ thumb_pc_offset(-2); \
+ if(rd == 15) \
+ { \
+ reg[REG_PC] = dest & ~0x01; \
+ thumb_update_pc(); \
+ } \
+ else \
+ { \
+ reg[rd] = dest; \
+ } \
+} \
+
+// Operation types: imm, mem_reg, mem_imm
+
+#define thumb_access_memory(access_type, op_type, address, reg_op, \
+ mem_type) \
+{ \
+ thumb_decode_##op_type(); \
+ access_type##_memory_##mem_type(address, reg_op); \
+ thumb_pc_offset(2); \
+} \
+
+#define thumb_block_address_preadjust_no_op() \
+
+#define thumb_block_address_preadjust_up() \
+ address += bit_count[reg_list] * 4 \
+
+#define thumb_block_address_preadjust_down() \
+ address -= bit_count[reg_list] * 4 \
+
+#define thumb_block_address_preadjust_push_lr() \
+ address -= (bit_count[reg_list] + 1) * 4 \
+
+#define thumb_block_address_postadjust_no_op() \
+
+#define thumb_block_address_postadjust_up() \
+ address += offset \
+
+#define thumb_block_address_postadjust_down() \
+ address -= offset \
+
+#define thumb_block_address_postadjust_pop_pc() \
+ load_memory_u32(address + offset, pc); \
+ pc &= ~0x01; \
+ reg[REG_PC] = pc; \
+ address += offset + 4 \
+
+#define thumb_block_address_postadjust_push_lr() \
+ store_memory_u32(address + offset, reg[REG_LR]); \
+
+#define thumb_block_memory_wb_load(base_reg) \
+ if(!((reg_list >> base_reg) & 0x01)) \
+ { \
+ reg[base_reg] = address; \
+ } \
+
+#define thumb_block_memory_wb_store(base_reg) \
+ reg[base_reg] = address \
+
+#define thumb_block_memory(access_type, pre_op, post_op, base_reg) \
+{ \
+ u32 i; \
+ u32 offset = 0; \
+ thumb_decode_rlist(); \
+ using_register(thumb, base_reg, memory_base); \
+ u32 address = reg[base_reg] & ~0x03; \
+ thumb_block_address_preadjust_##pre_op(); \
+ \
+ for(i = 0; i < 8; i++) \
+ { \
+ if((reg_list >> i) & 1) \
+ { \
+ access_type##_aligned32(address + offset, reg[i]); \
+ offset += 4; \
+ } \
+ } \
+ \
+ thumb_pc_offset(2); \
+ \
+ thumb_block_address_postadjust_##post_op(); \
+ thumb_block_memory_wb_##access_type(base_reg); \
+} \
+
+#define thumb_conditional_branch(condition) \
+{ \
+ thumb_decode_branch_cond(); \
+ if(condition) \
+ { \
+ thumb_pc_offset((offset * 2) + 4); \
+ } \
+ else \
+ { \
+ thumb_pc_offset(2); \
+ } \
+} \
+
+// When a mode change occurs from non-FIQ to non-FIQ retire the current
+// reg[13] and reg[14] into reg_mode[cpu_mode][5] and reg_mode[cpu_mode][6]
+// respectively and load into reg[13] and reg[14] reg_mode[new_mode][5] and
+// reg_mode[new_mode][6]. When swapping to/from FIQ retire/load reg[8]
+// through reg[14] to/from reg_mode[MODE_FIQ][0] through reg_mode[MODE_FIQ][6].
+
+u32 reg_mode[7][7];
+
+u32 cpu_modes[32] =
+{
+ MODE_INVALID, MODE_INVALID, MODE_INVALID, MODE_INVALID, MODE_INVALID,
+ MODE_INVALID, MODE_INVALID, MODE_INVALID, MODE_INVALID, MODE_INVALID,
+ MODE_INVALID, MODE_INVALID, MODE_INVALID, MODE_INVALID, MODE_INVALID,
+ MODE_INVALID, MODE_USER, MODE_FIQ, MODE_IRQ, MODE_SUPERVISOR, MODE_INVALID,
+ MODE_INVALID, MODE_INVALID, MODE_ABORT, MODE_INVALID, MODE_INVALID,
+ MODE_INVALID, MODE_INVALID, MODE_UNDEFINED, MODE_INVALID, MODE_INVALID,
+ MODE_USER
+};
+
+u32 cpu_modes_cpsr[7] = { 0x10, 0x11, 0x12, 0x13, 0x17, 0x1B, 0x1F };
+
+// When switching modes set spsr[new_mode] to cpsr. Modifying PC as the
+// target of a data proc instruction will set cpsr to spsr[cpu_mode].
+
+u32 initial_reg[64];
+u32 *reg = initial_reg;
+u32 spsr[6];
+
+// ARM/Thumb mode is stored in the flags directly, this is simpler than
+// shadowing it since it has a constant 1bit represenation.
+
+char *reg_names[16] =
+{
+ " r0", " r1", " r2", " r3", " r4", " r5", " r6", " r7",
+ " r8", " r9", "r10", " fp", " ip", " sp", " lr", " pc"
+};
+
+char *cpu_mode_names[] =
+{
+ "user", "irq", "fiq", "svsr", "abrt", "undf", "invd"
+};
+
+u32 instruction_count = 0;
+
+u32 output_field = 0;
+const u32 num_output_fields = 2;
+
+u32 last_instruction = 0;
+
+u32 in_interrupt = 0;
+
+void set_cpu_mode(cpu_mode_type new_mode)
+{
+ u32 i;
+ cpu_mode_type cpu_mode = reg[CPU_MODE];
+
+ if(cpu_mode == new_mode)
+ return;
+
+ if(new_mode == MODE_FIQ)
+ {
+ for(i = 8; i < 15; i++)
+ reg_mode[cpu_mode][i - 8] = reg[i];
+ }
+ else
+ {
+ reg_mode[cpu_mode][5] = reg[REG_SP];
+ reg_mode[cpu_mode][6] = reg[REG_LR];
+ }
+
+ if(cpu_mode == MODE_FIQ)
+ {
+ for(i = 8; i < 15; i++)
+ reg[i] = reg_mode[new_mode][i - 8];
+ }
+ else
+ {
+ reg[REG_SP] = reg_mode[new_mode][5];
+ reg[REG_LR] = reg_mode[new_mode][6];
+ }
+
+ reg[CPU_MODE] = new_mode;
+}
+
+void raise_interrupt(irq_type irq_raised)
+{
+ // The specific IRQ must be enabled in IE, master IRQ enable must be on,
+ // and it must be on in the flags.
+ io_registers[REG_IF] |= irq_raised;
+
+ if((io_registers[REG_IE] & irq_raised) && io_registers[REG_IME] &&
+ ((reg[REG_CPSR] & 0x80) == 0))
+ {
+ bios_read_protect = 0xe55ec002;
+
+ // Interrupt handler in BIOS
+ reg_mode[MODE_IRQ][6] = reg[REG_PC] + 4;
+ spsr[MODE_IRQ] = reg[REG_CPSR];
+ reg[REG_CPSR] = 0xD2;
+ reg[REG_PC] = 0x00000018;
+
+ bios_region_read_allow();
+
+ set_cpu_mode(MODE_IRQ);
+ reg[CPU_HALT_STATE] = CPU_ACTIVE;
+ reg[CHANGED_PC_STATUS] = 1;
+ }
+}
+
+#ifndef HAVE_DYNAREC
+u8 *memory_map_read [8 * 1024];
+u8 *memory_map_write[8 * 1024];
+#endif
+
+void execute_arm(u32 cycles)
+{
+ u32 pc = reg[REG_PC];
+ u32 opcode;
+ u32 condition;
+ u32 n_flag, z_flag, c_flag, v_flag;
+ u32 pc_region = (pc >> 15);
+ u8 *pc_address_block = memory_map_read[pc_region];
+ u32 new_pc_region;
+ s32 cycles_remaining;
+ u32 cycles_per_instruction = global_cycles_per_instruction;
+ cpu_alert_type cpu_alert;
+
+ u32 old_pc;
+ u32 thumb_opcode_val = 0, arm_opcode_val = 0;
+
+ if(!pc_address_block)
+ pc_address_block = load_gamepak_page(pc_region & 0x3FF);
+
+ while(1)
+ {
+ cycles_remaining = cycles;
+ pc = reg[REG_PC];
+ extract_flags();
+
+ if(reg[REG_CPSR] & 0x20)
+ goto thumb_loop;
+
+ do
+ {
+arm_loop:
+
+ collapse_flags();
+ cycles_per_instruction = global_cycles_per_instruction;
+
+ old_pc = pc;
+
+ /* Execute ARM instruction */
+ using_instruction(arm);
+ check_pc_region();
+ pc &= ~0x03;
+ opcode = address32(pc_address_block, (pc & 0x7FFF));
+ condition = opcode >> 28;
+
+ switch(condition)
+ {
+ case 0x0:
+ /* EQ */
+ if(!z_flag)
+ arm_next_instruction();
+ break;
+ case 0x1:
+ /* NE */
+ if(z_flag)
+ arm_next_instruction();
+ break;
+ case 0x2:
+ /* CS */
+ if(!c_flag)
+ arm_next_instruction();
+ break;
+ case 0x3:
+ /* CC */
+ if(c_flag)
+ arm_next_instruction();
+ break;
+ case 0x4:
+ /* MI */
+ if(!n_flag)
+ arm_next_instruction();
+ break;
+
+ case 0x5:
+ /* PL */
+ if(n_flag)
+ arm_next_instruction();
+ break;
+
+ case 0x6:
+ /* VS */
+ if(!v_flag)
+ arm_next_instruction();
+ break;
+
+ case 0x7:
+ /* VC */
+ if(v_flag)
+ arm_next_instruction();
+ break;
+
+ case 0x8:
+ /* HI */
+ if((c_flag == 0) | z_flag)
+ arm_next_instruction();
+ break;
+
+ case 0x9:
+ /* LS */
+ if(c_flag & (z_flag ^ 1))
+ arm_next_instruction();
+ break;
+
+ case 0xA:
+ /* GE */
+ if(n_flag != v_flag)
+ arm_next_instruction();
+ break;
+
+ case 0xB:
+ /* LT */
+ if(n_flag == v_flag)
+ arm_next_instruction();
+ break;
+
+ case 0xC:
+ /* GT */
+ if(z_flag | (n_flag != v_flag))
+ arm_next_instruction();
+ break;
+
+ case 0xD:
+ /* LE */
+ if((z_flag == 0) & (n_flag == v_flag))
+ arm_next_instruction();
+ break;
+
+ case 0xE:
+ /* AL */
+ break;
+
+ case 0xF:
+ /* Reserved - treat as "never" */
+ arm_next_instruction();
+ break;
+ }
+
+ switch((opcode >> 20) & 0xFF)
+ {
+ case 0x00:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn], -rm */
+ arm_access_memory(store, no_op, half_reg, u16, yes, - reg[rm]);
+ }
+ else
+ {
+ /* MUL rd, rm, rs */
+ arm_multiply(no_op, no);
+ }
+ }
+ else
+ {
+ /* AND rd, rn, reg_op */
+ arm_data_proc(reg[rn] & reg_sh, reg);
+ }
+ break;
+
+ case 0x01:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 0:
+ /* MULS rd, rm, rs */
+ arm_multiply(no_op, yes);
+ break;
+
+ case 1:
+ /* LDRH rd, [rn], -rm */
+ arm_access_memory(load, no_op, half_reg, u16, yes, - reg[rm]);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], -rm */
+ arm_access_memory(load, no_op, half_reg, s8, yes, - reg[rm]);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], -rm */
+ arm_access_memory(load, no_op, half_reg, s16, yes, - reg[rm]);
+ break;
+ }
+ }
+ else
+ {
+ /* ANDS rd, rn, reg_op */
+ arm_data_proc_logic_flags(reg[rn] & reg_sh, reg);
+ }
+ break;
+
+ case 0x02:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn], -rm */
+ arm_access_memory(store, no_op, half_reg, u16, yes, - reg[rm]);
+ }
+ else
+ {
+ /* MLA rd, rm, rs, rn */
+ arm_multiply(+ reg[rn], no);
+ }
+ }
+ else
+ {
+ /* EOR rd, rn, reg_op */
+ arm_data_proc(reg[rn] ^ reg_sh, reg);
+ }
+ break;
+
+ case 0x03:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 0:
+ /* MLAS rd, rm, rs, rn */
+ arm_multiply(+ reg[rn], yes);
+ break;
+
+ case 1:
+ /* LDRH rd, [rn], -rm */
+ arm_access_memory(load, no_op, half_reg, u16, yes, - reg[rm]);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], -rm */
+ arm_access_memory(load, no_op, half_reg, s8, yes, - reg[rm]);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], -rm */
+ arm_access_memory(load, no_op, half_reg, s16, yes, - reg[rm]);
+ break;
+ }
+ }
+ else
+ {
+ /* EORS rd, rn, reg_op */
+ arm_data_proc_logic_flags(reg[rn] ^ reg_sh, reg);
+ }
+ break;
+
+ case 0x04:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn], -imm */
+ arm_access_memory(store, no_op, half_imm, u16, yes, - offset);
+ }
+ else
+ {
+ /* SUB rd, rn, reg_op */
+ arm_data_proc(reg[rn] - reg_sh, reg);
+ }
+ break;
+
+ case 0x05:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn], -imm */
+ arm_access_memory(load, no_op, half_imm, u16, yes, - offset);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], -imm */
+ arm_access_memory(load, no_op, half_imm, s8, yes, - offset);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], -imm */
+ arm_access_memory(load, no_op, half_imm, s16, yes, - offset);
+ break;
+ }
+ }
+ else
+ {
+ /* SUBS rd, rn, reg_op */
+ arm_data_proc_sub_flags(reg[rn], reg_sh, reg);
+ }
+ break;
+
+ case 0x06:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn], -imm */
+ arm_access_memory(store, no_op, half_imm, u16, yes, - offset);
+ }
+ else
+ {
+ /* RSB rd, rn, reg_op */
+ arm_data_proc(reg_sh - reg[rn], reg);
+ }
+ break;
+
+ case 0x07:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn], -imm */
+ arm_access_memory(load, no_op, half_imm, u16, yes, - offset);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], -imm */
+ arm_access_memory(load, no_op, half_imm, s8, yes, - offset);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], -imm */
+ arm_access_memory(load, no_op, half_imm, s16, yes, - offset);
+ break;
+ }
+ }
+ else
+ {
+ /* RSBS rd, rn, reg_op */
+ arm_data_proc_sub_flags(reg_sh, reg[rn], reg);
+ }
+ break;
+
+ case 0x08:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn], +rm */
+ arm_access_memory(store, no_op, half_reg, u16, yes, + reg[rm]);
+ }
+ else
+ {
+ /* UMULL rd, rm, rs */
+ arm_multiply_long(no_op, no, u);
+ }
+ }
+ else
+ {
+ /* ADD rd, rn, reg_op */
+ arm_data_proc(reg[rn] + reg_sh, reg);
+ }
+ break;
+
+ case 0x09:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 0:
+ /* UMULLS rdlo, rdhi, rm, rs */
+ arm_multiply_long(no_op, yes, u);
+ break;
+
+ case 1:
+ /* LDRH rd, [rn], +rm */
+ arm_access_memory(load, no_op, half_reg, u16, yes, + reg[rm]);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], +rm */
+ arm_access_memory(load, no_op, half_reg, s8, yes, + reg[rm]);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], +rm */
+ arm_access_memory(load, no_op, half_reg, s16, yes, + reg[rm]);
+ break;
+ }
+ }
+ else
+ {
+ /* ADDS rd, rn, reg_op */
+ arm_data_proc_add_flags(reg[rn], reg_sh, reg);
+ }
+ break;
+
+ case 0x0A:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn], +rm */
+ arm_access_memory(store, no_op, half_reg, u16, yes, + reg[rm]);
+ }
+ else
+ {
+ /* UMLAL rd, rm, rs */
+ arm_multiply_long(arm_multiply_long_addop(u), no, u);
+ }
+ }
+ else
+ {
+ /* ADC rd, rn, reg_op */
+ arm_data_proc(reg[rn] + reg_sh + c_flag, reg);
+ }
+ break;
+
+ case 0x0B:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 0:
+ /* UMLALS rdlo, rdhi, rm, rs */
+ arm_multiply_long(arm_multiply_long_addop(u), yes, u);
+ break;
+
+ case 1:
+ /* LDRH rd, [rn], +rm */
+ arm_access_memory(load, no_op, half_reg, u16, yes, + reg[rm]);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], +rm */
+ arm_access_memory(load, no_op, half_reg, s8, yes, + reg[rm]);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], +rm */
+ arm_access_memory(load, no_op, half_reg, s16, yes, + reg[rm]);
+ break;
+ }
+ }
+ else
+ {
+ /* ADCS rd, rn, reg_op */
+ arm_data_proc_add_flags(reg[rn], reg_sh + c_flag, reg);
+ }
+ break;
+
+ case 0x0C:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn], +imm */
+ arm_access_memory(store, no_op, half_imm, u16, yes, + offset);
+ }
+ else
+ {
+ /* SMULL rd, rm, rs */
+ arm_multiply_long(no_op, no, s);
+ }
+ }
+ else
+ {
+ /* SBC rd, rn, reg_op */
+ arm_data_proc(reg[rn] - (reg_sh + (c_flag ^ 1)), reg);
+ }
+ break;
+
+ case 0x0D:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 0:
+ /* SMULLS rdlo, rdhi, rm, rs */
+ arm_multiply_long(no_op, yes, s);
+ break;
+
+ case 1:
+ /* LDRH rd, [rn], +imm */
+ arm_access_memory(load, no_op, half_imm, u16, yes, + offset);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], +imm */
+ arm_access_memory(load, no_op, half_imm, s8, yes, + offset);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], +imm */
+ arm_access_memory(load, no_op, half_imm, s16, yes, + offset);
+ break;
+ }
+ }
+ else
+ {
+ /* SBCS rd, rn, reg_op */
+ arm_data_proc_sub_flags(reg[rn], (reg_sh + (c_flag ^ 1)), reg);
+ }
+ break;
+
+ case 0x0E:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn], +imm */
+ arm_access_memory(store, no_op, half_imm, u16, yes, + offset);
+ }
+ else
+ {
+ /* SMLAL rd, rm, rs */
+ arm_multiply_long(arm_multiply_long_addop(s), no, s);
+ }
+ }
+ else
+ {
+ /* RSC rd, rn, reg_op */
+ arm_data_proc(reg_sh - reg[rn] + c_flag - 1, reg);
+ }
+ break;
+
+ case 0x0F:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 0:
+ /* SMLALS rdlo, rdhi, rm, rs */
+ arm_multiply_long(arm_multiply_long_addop(s), yes, s);
+ break;
+
+ case 1:
+ /* LDRH rd, [rn], +imm */
+ arm_access_memory(load, no_op, half_imm, u16, yes, + offset);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn], +imm */
+ arm_access_memory(load, no_op, half_imm, s8, yes, + offset);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn], +imm */
+ arm_access_memory(load, no_op, half_imm, s16, yes, + offset);
+ break;
+ }
+ }
+ else
+ {
+ /* RSCS rd, rn, reg_op */
+ arm_data_proc_sub_flags((reg_sh + c_flag - 1), reg[rn], reg);
+ }
+ break;
+
+ case 0x10:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn - rm] */
+ arm_access_memory(store, - reg[rm], half_reg, u16, no, no_op);
+ }
+ else
+ {
+ /* SWP rd, rm, [rn] */
+ arm_swap(u32);
+ }
+ }
+ else
+ {
+ /* MRS rd, cpsr */
+ arm_psr(reg, read, reg[REG_CPSR]);
+ }
+ break;
+
+ case 0x11:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn - rm] */
+ arm_access_memory(load, - reg[rm], half_reg, u16, no, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn - rm] */
+ arm_access_memory(load, - reg[rm], half_reg, s8, no, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn - rm] */
+ arm_access_memory(load, - reg[rm], half_reg, s16, no, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* TST rd, rn, reg_op */
+ arm_data_proc_test_logic(reg[rn] & reg_sh, reg);
+ }
+ break;
+
+ case 0x12:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn - rm]! */
+ arm_access_memory(store, - reg[rm], half_reg, u16, yes, no_op);
+ }
+ else
+ {
+ if(opcode & 0x10)
+ {
+ /* BX rn */
+ arm_decode_branchx(opcode);
+ u32 src = reg[rn];
+ if(src & 0x01)
+ {
+ src -= 1;
+ arm_pc_offset_update_direct(src);
+ reg[REG_CPSR] |= 0x20;
+ goto thumb_loop;
+ }
+ else
+ {
+ arm_pc_offset_update_direct(src);
+ }
+ }
+ else
+ {
+ /* MSR cpsr, rm */
+ arm_psr(reg, store, cpsr);
+ }
+ }
+ break;
+
+ case 0x13:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn - rm]! */
+ arm_access_memory(load, - reg[rm], half_reg, u16, yes, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn - rm]! */
+ arm_access_memory(load, - reg[rm], half_reg, s8, yes, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn - rm]! */
+ arm_access_memory(load, - reg[rm], half_reg, s16, yes, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* TEQ rd, rn, reg_op */
+ arm_data_proc_test_logic(reg[rn] ^ reg_sh, reg);
+ }
+ break;
+
+ case 0x14:
+ if((opcode & 0x90) == 0x90)
+ {
+ if(opcode & 0x20)
+ {
+ /* STRH rd, [rn - imm] */
+ arm_access_memory(store, - offset, half_imm, u16, no, no_op);
+ }
+ else
+ {
+ /* SWPB rd, rm, [rn] */
+ arm_swap(u8);
+ }
+ }
+ else
+ {
+ /* MRS rd, spsr */
+ arm_psr(reg, read, spsr[reg[CPU_MODE]]);
+ }
+ break;
+
+ case 0x15:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn - imm] */
+ arm_access_memory(load, - offset, half_imm, u16, no, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn - imm] */
+ arm_access_memory(load, - offset, half_imm, s8, no, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn - imm] */
+ arm_access_memory(load, - offset, half_imm, s16, no, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* CMP rn, reg_op */
+ arm_data_proc_test_sub(reg[rn], reg_sh, reg);
+ }
+ break;
+
+ case 0x16:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn - imm]! */
+ arm_access_memory(store, - offset, half_imm, u16, yes, no_op);
+ }
+ else
+ {
+ /* MSR spsr, rm */
+ arm_psr(reg, store, spsr);
+ }
+ break;
+
+ case 0x17:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn - imm]! */
+ arm_access_memory(load, - offset, half_imm, u16, yes, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn - imm]! */
+ arm_access_memory(load, - offset, half_imm, s8, yes, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn - imm]! */
+ arm_access_memory(load, - offset, half_imm, s16, yes, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* CMN rd, rn, reg_op */
+ arm_data_proc_test_add(reg[rn], reg_sh, reg);
+ }
+ break;
+
+ case 0x18:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn + rm] */
+ arm_access_memory(store, + reg[rm], half_reg, u16, no, no_op);
+ }
+ else
+ {
+ /* ORR rd, rn, reg_op */
+ arm_data_proc(reg[rn] | reg_sh, reg);
+ }
+ break;
+
+ case 0x19:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn + rm] */
+ arm_access_memory(load, + reg[rm], half_reg, u16, no, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn + rm] */
+ arm_access_memory(load, + reg[rm], half_reg, s8, no, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn + rm] */
+ arm_access_memory(load, + reg[rm], half_reg, s16, no, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* ORRS rd, rn, reg_op */
+ arm_data_proc_logic_flags(reg[rn] | reg_sh, reg);
+ }
+ break;
+
+ case 0x1A:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn + rm]! */
+ arm_access_memory(store, + reg[rm], half_reg, u16, yes, no_op);
+ }
+ else
+ {
+ /* MOV rd, reg_op */
+ arm_data_proc(reg_sh, reg);
+ }
+ break;
+
+ case 0x1B:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn + rm]! */
+ arm_access_memory(load, + reg[rm], half_reg, u16, yes, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn + rm]! */
+ arm_access_memory(load, + reg[rm], half_reg, s8, yes, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn + rm]! */
+ arm_access_memory(load, + reg[rm], half_reg, s16, yes, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* MOVS rd, reg_op */
+ arm_data_proc_logic_flags(reg_sh, reg);
+ }
+ break;
+
+ case 0x1C:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn + imm] */
+ arm_access_memory(store, + offset, half_imm, u16, no, no_op);
+ }
+ else
+ {
+ /* BIC rd, rn, reg_op */
+ arm_data_proc(reg[rn] & (~reg_sh), reg);
+ }
+ break;
+
+ case 0x1D:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn + imm] */
+ arm_access_memory(load, + offset, half_imm, u16, no, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn + imm] */
+ arm_access_memory(load, + offset, half_imm, s8, no, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn + imm] */
+ arm_access_memory(load, + offset, half_imm, s16, no, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* BICS rd, rn, reg_op */
+ arm_data_proc_logic_flags(reg[rn] & (~reg_sh), reg);
+ }
+ break;
+
+ case 0x1E:
+ if((opcode & 0x90) == 0x90)
+ {
+ /* STRH rd, [rn + imm]! */
+ arm_access_memory(store, + offset, half_imm, u16, yes, no_op);
+ }
+ else
+ {
+ /* MVN rd, reg_op */
+ arm_data_proc(~reg_sh, reg);
+ }
+ break;
+
+ case 0x1F:
+ if((opcode & 0x90) == 0x90)
+ {
+ switch((opcode >> 5) & 0x03)
+ {
+ case 1:
+ /* LDRH rd, [rn + imm]! */
+ arm_access_memory(load, + offset, half_imm, u16, yes, no_op);
+ break;
+
+ case 2:
+ /* LDRSB rd, [rn + imm]! */
+ arm_access_memory(load, + offset, half_imm, s8, yes, no_op);
+ break;
+
+ case 3:
+ /* LDRSH rd, [rn + imm]! */
+ arm_access_memory(load, + offset, half_imm, s16, yes, no_op);
+ break;
+ }
+ }
+ else
+ {
+ /* MVNS rd, rn, reg_op */
+ arm_data_proc_logic_flags(~reg_sh, reg);
+ }
+ break;
+
+ case 0x20:
+ /* AND rd, rn, imm */
+ arm_data_proc(reg[rn] & imm, imm);
+ break;
+
+ case 0x21:
+ /* ANDS rd, rn, imm */
+ arm_data_proc_logic_flags(reg[rn] & imm, imm);
+ break;
+
+ case 0x22:
+ /* EOR rd, rn, imm */
+ arm_data_proc(reg[rn] ^ imm, imm);
+ break;
+
+ case 0x23:
+ /* EORS rd, rn, imm */
+ arm_data_proc_logic_flags(reg[rn] ^ imm, imm);
+ break;
+
+ case 0x24:
+ /* SUB rd, rn, imm */
+ arm_data_proc(reg[rn] - imm, imm);
+ break;
+
+ case 0x25:
+ /* SUBS rd, rn, imm */
+ arm_data_proc_sub_flags(reg[rn], imm, imm);
+ break;
+
+ case 0x26:
+ /* RSB rd, rn, imm */
+ arm_data_proc(imm - reg[rn], imm);
+ break;
+
+ case 0x27:
+ /* RSBS rd, rn, imm */
+ arm_data_proc_sub_flags(imm, reg[rn], imm);
+ break;
+
+ case 0x28:
+ /* ADD rd, rn, imm */
+ arm_data_proc(reg[rn] + imm, imm);
+ break;
+
+ case 0x29:
+ /* ADDS rd, rn, imm */
+ arm_data_proc_add_flags(reg[rn], imm, imm);
+ break;
+
+ case 0x2A:
+ /* ADC rd, rn, imm */
+ arm_data_proc(reg[rn] + imm + c_flag, imm);
+ break;
+
+ case 0x2B:
+ /* ADCS rd, rn, imm */
+ arm_data_proc_add_flags(reg[rn] + imm, c_flag, imm);
+ break;
+
+ case 0x2C:
+ /* SBC rd, rn, imm */
+ arm_data_proc(reg[rn] - imm + c_flag - 1, imm);
+ break;
+
+ case 0x2D:
+ /* SBCS rd, rn, imm */
+ arm_data_proc_sub_flags(reg[rn], (imm + (c_flag ^ 1)), imm);
+ break;
+
+ case 0x2E:
+ /* RSC rd, rn, imm */
+ arm_data_proc(imm - reg[rn] + c_flag - 1, imm);
+ break;
+
+ case 0x2F:
+ /* RSCS rd, rn, imm */
+ arm_data_proc_sub_flags((imm + c_flag - 1), reg[rn], imm);
+ break;
+
+ case 0x30 ... 0x31:
+ /* TST rn, imm */
+ arm_data_proc_test_logic(reg[rn] & imm, imm);
+ break;
+
+ case 0x32:
+ /* MSR cpsr, imm */
+ arm_psr(imm, store, cpsr);
+ break;
+
+ case 0x33:
+ /* TEQ rn, imm */
+ arm_data_proc_test_logic(reg[rn] ^ imm, imm);
+ break;
+
+ case 0x34 ... 0x35:
+ /* CMP rn, imm */
+ arm_data_proc_test_sub(reg[rn], imm, imm);
+ break;
+
+ case 0x36:
+ /* MSR spsr, imm */
+ arm_psr(imm, store, spsr);
+ break;
+
+ case 0x37:
+ /* CMN rn, imm */
+ arm_data_proc_test_add(reg[rn], imm, imm);
+ break;
+
+ case 0x38:
+ /* ORR rd, rn, imm */
+ arm_data_proc(reg[rn] | imm, imm);
+ break;
+
+ case 0x39:
+ /* ORRS rd, rn, imm */
+ arm_data_proc_logic_flags(reg[rn] | imm, imm);
+ break;
+
+ case 0x3A:
+ /* MOV rd, imm */
+ arm_data_proc(imm, imm);
+ break;
+
+ case 0x3B:
+ /* MOVS rd, imm */
+ arm_data_proc_logic_flags(imm, imm);
+ break;
+
+ case 0x3C:
+ /* BIC rd, rn, imm */
+ arm_data_proc(reg[rn] & (~imm), imm);
+ break;
+
+ case 0x3D:
+ /* BICS rd, rn, imm */
+ arm_data_proc_logic_flags(reg[rn] & (~imm), imm);
+ break;
+
+ case 0x3E:
+ /* MVN rd, imm */
+ arm_data_proc(~imm, imm);
+ break;
+
+ case 0x3F:
+ /* MVNS rd, imm */
+ arm_data_proc_logic_flags(~imm, imm);
+ break;
+
+ case 0x40:
+ /* STR rd, [rn], -imm */
+ arm_access_memory(store, no_op, imm, u32, yes, - offset);
+ break;
+
+ case 0x41:
+ /* LDR rd, [rn], -imm */
+ arm_access_memory(load, no_op, imm, u32, yes, - offset);
+ break;
+
+ case 0x42:
+ /* STRT rd, [rn], -imm */
+ arm_access_memory(store, no_op, imm, u32, yes, - offset);
+ break;
+
+ case 0x43:
+ /* LDRT rd, [rn], -imm */
+ arm_access_memory(load, no_op, imm, u32, yes, - offset);
+ break;
+
+ case 0x44:
+ /* STRB rd, [rn], -imm */
+ arm_access_memory(store, no_op, imm, u8, yes, - offset);
+ break;
+
+ case 0x45:
+ /* LDRB rd, [rn], -imm */
+ arm_access_memory(load, no_op, imm, u8, yes, - offset);
+ break;
+
+ case 0x46:
+ /* STRBT rd, [rn], -imm */
+ arm_access_memory(store, no_op, imm, u8, yes, - offset);
+ break;
+
+ case 0x47:
+ /* LDRBT rd, [rn], -imm */
+ arm_access_memory(load, no_op, imm, u8, yes, - offset);
+ break;
+
+ case 0x48:
+ /* STR rd, [rn], +imm */
+ arm_access_memory(store, no_op, imm, u32, yes, + offset);
+ break;
+
+ case 0x49:
+ /* LDR rd, [rn], +imm */
+ arm_access_memory(load, no_op, imm, u32, yes, + offset);
+ break;
+
+ case 0x4A:
+ /* STRT rd, [rn], +imm */
+ arm_access_memory(store, no_op, imm, u32, yes, + offset);
+ break;
+
+ case 0x4B:
+ /* LDRT rd, [rn], +imm */
+ arm_access_memory(load, no_op, imm, u32, yes, + offset);
+ break;
+
+ case 0x4C:
+ /* STRB rd, [rn], +imm */
+ arm_access_memory(store, no_op, imm, u8, yes, + offset);
+ break;
+
+ case 0x4D:
+ /* LDRB rd, [rn], +imm */
+ arm_access_memory(load, no_op, imm, u8, yes, + offset);
+ break;
+
+ case 0x4E:
+ /* STRBT rd, [rn], +imm */
+ arm_access_memory(store, no_op, imm, u8, yes, + offset);
+ break;
+
+ case 0x4F:
+ /* LDRBT rd, [rn], +imm */
+ arm_access_memory(load, no_op, imm, u8, yes, + offset);
+ break;
+
+ case 0x50:
+ /* STR rd, [rn - imm] */
+ arm_access_memory(store, - offset, imm, u32, no, no_op);
+ break;
+
+ case 0x51:
+ /* LDR rd, [rn - imm] */
+ arm_access_memory(load, - offset, imm, u32, no, no_op);
+ break;
+
+ case 0x52:
+ /* STR rd, [rn - imm]! */
+ arm_access_memory(store, - offset, imm, u32, yes, no_op);
+ break;
+
+ case 0x53:
+ /* LDR rd, [rn - imm]! */
+ arm_access_memory(load, - offset, imm, u32, yes, no_op);
+ break;
+
+ case 0x54:
+ /* STRB rd, [rn - imm] */
+ arm_access_memory(store, - offset, imm, u8, no, no_op);
+ break;
+
+ case 0x55:
+ /* LDRB rd, [rn - imm] */
+ arm_access_memory(load, - offset, imm, u8, no, no_op);
+ break;
+
+ case 0x56:
+ /* STRB rd, [rn - imm]! */
+ arm_access_memory(store, - offset, imm, u8, yes, no_op);
+ break;
+
+ case 0x57:
+ /* LDRB rd, [rn - imm]! */
+ arm_access_memory(load, - offset, imm, u8, yes, no_op);
+ break;
+
+ case 0x58:
+ /* STR rd, [rn + imm] */
+ arm_access_memory(store, + offset, imm, u32, no, no_op);
+ break;
+
+ case 0x59:
+ /* LDR rd, [rn + imm] */
+ arm_access_memory(load, + offset, imm, u32, no, no_op);
+ break;
+
+ case 0x5A:
+ /* STR rd, [rn + imm]! */
+ arm_access_memory(store, + offset, imm, u32, yes, no_op);
+ break;
+
+ case 0x5B:
+ /* LDR rd, [rn + imm]! */
+ arm_access_memory(load, + offset, imm, u32, yes, no_op);
+ break;
+
+ case 0x5C:
+ /* STRB rd, [rn + imm] */
+ arm_access_memory(store, + offset, imm, u8, no, no_op);
+ break;
+
+ case 0x5D:
+ /* LDRB rd, [rn + imm] */
+ arm_access_memory(load, + offset, imm, u8, no, no_op);
+ break;
+
+ case 0x5E:
+ /* STRB rd, [rn + imm]! */
+ arm_access_memory(store, + offset, imm, u8, yes, no_op);
+ break;
+
+ case 0x5F:
+ /* LDRBT rd, [rn + imm]! */
+ arm_access_memory(load, + offset, imm, u8, yes, no_op);
+ break;
+
+ case 0x60:
+ /* STR rd, [rn], -reg_op */
+ arm_access_memory(store, no_op, reg, u32, yes, - reg_offset);
+ break;
+
+ case 0x61:
+ /* LDR rd, [rn], -reg_op */
+ arm_access_memory(load, no_op, reg, u32, yes, - reg_offset);
+ break;
+
+ case 0x62:
+ /* STRT rd, [rn], -reg_op */
+ arm_access_memory(store, no_op, reg, u32, yes, - reg_offset);
+ break;
+
+ case 0x63:
+ /* LDRT rd, [rn], -reg_op */
+ arm_access_memory(load, no_op, reg, u32, yes, - reg_offset);
+ break;
+
+ case 0x64:
+ /* STRB rd, [rn], -reg_op */
+ arm_access_memory(store, no_op, reg, u8, yes, - reg_offset);
+ break;
+
+ case 0x65:
+ /* LDRB rd, [rn], -reg_op */
+ arm_access_memory(load, no_op, reg, u8, yes, - reg_offset);
+ break;
+
+ case 0x66:
+ /* STRBT rd, [rn], -reg_op */
+ arm_access_memory(store, no_op, reg, u8, yes, - reg_offset);
+ break;
+
+ case 0x67:
+ /* LDRBT rd, [rn], -reg_op */
+ arm_access_memory(load, no_op, reg, u8, yes, - reg_offset);
+ break;
+
+ case 0x68:
+ /* STR rd, [rn], +reg_op */
+ arm_access_memory(store, no_op, reg, u32, yes, + reg_offset);
+ break;
+
+ case 0x69:
+ /* LDR rd, [rn], +reg_op */
+ arm_access_memory(load, no_op, reg, u32, yes, + reg_offset);
+ break;
+
+ case 0x6A:
+ /* STRT rd, [rn], +reg_op */
+ arm_access_memory(store, no_op, reg, u32, yes, + reg_offset);
+ break;
+
+ case 0x6B:
+ /* LDRT rd, [rn], +reg_op */
+ arm_access_memory(load, no_op, reg, u32, yes, + reg_offset);
+ break;
+
+ case 0x6C:
+ /* STRB rd, [rn], +reg_op */
+ arm_access_memory(store, no_op, reg, u8, yes, + reg_offset);
+ break;
+
+ case 0x6D:
+ /* LDRB rd, [rn], +reg_op */
+ arm_access_memory(load, no_op, reg, u8, yes, + reg_offset);
+ break;
+
+ case 0x6E:
+ /* STRBT rd, [rn], +reg_op */
+ arm_access_memory(store, no_op, reg, u8, yes, + reg_offset);
+ break;
+
+ case 0x6F:
+ /* LDRBT rd, [rn], +reg_op */
+ arm_access_memory(load, no_op, reg, u8, yes, + reg_offset);
+ break;
+
+ case 0x70:
+ /* STR rd, [rn - reg_op] */
+ arm_access_memory(store, - reg_offset, reg, u32, no, no_op);
+ break;
+
+ case 0x71:
+ /* LDR rd, [rn - reg_op] */
+ arm_access_memory(load, - reg_offset, reg, u32, no, no_op);
+ break;
+
+ case 0x72:
+ /* STR rd, [rn - reg_op]! */
+ arm_access_memory(store, - reg_offset, reg, u32, yes, no_op);
+ break;
+
+ case 0x73:
+ /* LDR rd, [rn - reg_op]! */
+ arm_access_memory(load, - reg_offset, reg, u32, yes, no_op);
+ break;
+
+ case 0x74:
+ /* STRB rd, [rn - reg_op] */
+ arm_access_memory(store, - reg_offset, reg, u8, no, no_op);
+ break;
+
+ case 0x75:
+ /* LDRB rd, [rn - reg_op] */
+ arm_access_memory(load, - reg_offset, reg, u8, no, no_op);
+ break;
+
+ case 0x76:
+ /* STRB rd, [rn - reg_op]! */
+ arm_access_memory(store, - reg_offset, reg, u8, yes, no_op);
+ break;
+
+ case 0x77:
+ /* LDRB rd, [rn - reg_op]! */
+ arm_access_memory(load, - reg_offset, reg, u8, yes, no_op);
+ break;
+
+ case 0x78:
+ /* STR rd, [rn + reg_op] */
+ arm_access_memory(store, + reg_offset, reg, u32, no, no_op);
+ break;
+
+ case 0x79:
+ /* LDR rd, [rn + reg_op] */
+ arm_access_memory(load, + reg_offset, reg, u32, no, no_op);
+ break;
+
+ case 0x7A:
+ /* STR rd, [rn + reg_op]! */
+ arm_access_memory(store, + reg_offset, reg, u32, yes, no_op);
+ break;
+
+ case 0x7B:
+ /* LDR rd, [rn + reg_op]! */
+ arm_access_memory(load, + reg_offset, reg, u32, yes, no_op);
+ break;
+
+ case 0x7C:
+ /* STRB rd, [rn + reg_op] */
+ arm_access_memory(store, + reg_offset, reg, u8, no, no_op);
+ break;
+
+ case 0x7D:
+ /* LDRB rd, [rn + reg_op] */
+ arm_access_memory(load, + reg_offset, reg, u8, no, no_op);
+ break;
+
+ case 0x7E:
+ /* STRB rd, [rn + reg_op]! */
+ arm_access_memory(store, + reg_offset, reg, u8, yes, no_op);
+ break;
+
+ case 0x7F:
+ /* LDRBT rd, [rn + reg_op]! */
+ arm_access_memory(load, + reg_offset, reg, u8, yes, no_op);
+ break;
+
+ case 0x80:
+ /* STMDA rn, rlist */
+ arm_block_memory(store, down_a, no, no);
+ break;
+
+ case 0x81:
+ /* LDMDA rn, rlist */
+ arm_block_memory(load, down_a, no, no);
+ break;
+
+ case 0x82:
+ /* STMDA rn!, rlist */
+ arm_block_memory(store, down_a, down, no);
+ break;
+
+ case 0x83:
+ /* LDMDA rn!, rlist */
+ arm_block_memory(load, down_a, down, no);
+ break;
+
+ case 0x84:
+ /* STMDA rn, rlist^ */
+ arm_block_memory(store, down_a, no, yes);
+ break;
+
+ case 0x85:
+ /* LDMDA rn, rlist^ */
+ arm_block_memory(load, down_a, no, yes);
+ break;
+
+ case 0x86:
+ /* STMDA rn!, rlist^ */
+ arm_block_memory(store, down_a, down, yes);
+ break;
+
+ case 0x87:
+ /* LDMDA rn!, rlist^ */
+ arm_block_memory(load, down_a, down, yes);
+ break;
+
+ case 0x88:
+ /* STMIA rn, rlist */
+ arm_block_memory(store, no, no, no);
+ break;
+
+ case 0x89:
+ /* LDMIA rn, rlist */
+ arm_block_memory(load, no, no, no);
+ break;
+
+ case 0x8A:
+ /* STMIA rn!, rlist */
+ arm_block_memory(store, no, up, no);
+ break;
+
+ case 0x8B:
+ /* LDMIA rn!, rlist */
+ arm_block_memory(load, no, up, no);
+ break;
+
+ case 0x8C:
+ /* STMIA rn, rlist^ */
+ arm_block_memory(store, no, no, yes);
+ break;
+
+ case 0x8D:
+ /* LDMIA rn, rlist^ */
+ arm_block_memory(load, no, no, yes);
+ break;
+
+ case 0x8E:
+ /* STMIA rn!, rlist^ */
+ arm_block_memory(store, no, up, yes);
+ break;
+
+ case 0x8F:
+ /* LDMIA rn!, rlist^ */
+ arm_block_memory(load, no, up, yes);
+ break;
+
+ case 0x90:
+ /* STMDB rn, rlist */
+ arm_block_memory(store, down_b, no, no);
+ break;
+
+ case 0x91:
+ /* LDMDB rn, rlist */
+ arm_block_memory(load, down_b, no, no);
+ break;
+
+ case 0x92:
+ /* STMDB rn!, rlist */
+ arm_block_memory(store, down_b, down, no);
+ break;
+
+ case 0x93:
+ /* LDMDB rn!, rlist */
+ arm_block_memory(load, down_b, down, no);
+ break;
+
+ case 0x94:
+ /* STMDB rn, rlist^ */
+ arm_block_memory(store, down_b, no, yes);
+ break;
+
+ case 0x95:
+ /* LDMDB rn, rlist^ */
+ arm_block_memory(load, down_b, no, yes);
+ break;
+
+ case 0x96:
+ /* STMDB rn!, rlist^ */
+ arm_block_memory(store, down_b, down, yes);
+ break;
+
+ case 0x97:
+ /* LDMDB rn!, rlist^ */
+ arm_block_memory(load, down_b, down, yes);
+ break;
+
+ case 0x98:
+ /* STMIB rn, rlist */
+ arm_block_memory(store, up, no, no);
+ break;
+
+ case 0x99:
+ /* LDMIB rn, rlist */
+ arm_block_memory(load, up, no, no);
+ break;
+
+ case 0x9A:
+ /* STMIB rn!, rlist */
+ arm_block_memory(store, up, up, no);
+ break;
+
+ case 0x9B:
+ /* LDMIB rn!, rlist */
+ arm_block_memory(load, up, up, no);
+ break;
+
+ case 0x9C:
+ /* STMIB rn, rlist^ */
+ arm_block_memory(store, up, no, yes);
+ break;
+
+ case 0x9D:
+ /* LDMIB rn, rlist^ */
+ arm_block_memory(load, up, no, yes);
+ break;
+
+ case 0x9E:
+ /* STMIB rn!, rlist^ */
+ arm_block_memory(store, up, up, yes);
+ break;
+
+ case 0x9F:
+ /* LDMIB rn!, rlist^ */
+ arm_block_memory(load, up, up, yes);
+ break;
+
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ {
+ /* B offset */
+ arm_decode_branch();
+ arm_pc_offset_update(offset + 8);
+ break;
+ }
+
+ case 0xB0 ... 0xBF:
+ {
+ /* BL offset */
+ arm_decode_branch();
+ reg[REG_LR] = pc + 4;
+ arm_pc_offset_update(offset + 8);
+ break;
+ }
+
+ case 0xC0 ... 0xEF:
+ /* coprocessor instructions, reserved on GBA */
+ break;
+
+ case 0xF0 ... 0xFF:
+ {
+ /* SWI comment */
+ u32 swi_comment = opcode & 0x00FFFFFF;
+
+ switch(swi_comment >> 16)
+ {
+ /* Jump to BIOS SWI handler */
+ default:
+ reg_mode[MODE_SUPERVISOR][6] = pc + 4;
+ collapse_flags();
+ spsr[MODE_SUPERVISOR] = reg[REG_CPSR];
+ reg[REG_PC] = 0x00000008;
+ arm_update_pc();
+ reg[REG_CPSR] = (reg[REG_CPSR] & ~0x1F) | 0x13;
+ set_cpu_mode(MODE_SUPERVISOR);
+ break;
+ }
+ break;
+ }
+ }
+
+skip_instruction:
+
+ /* End of Execute ARM instruction */
+ cycles_remaining -= cycles_per_instruction;
+ } while(cycles_remaining > 0);
+
+ collapse_flags();
+ cycles = update_gba();
+ continue;
+
+ do
+ {
+thumb_loop:
+
+ collapse_flags();
+
+ old_pc = pc;
+
+ /* Execute THUMB instruction */
+
+ using_instruction(thumb);
+ check_pc_region();
+ pc &= ~0x01;
+ opcode = address16(pc_address_block, (pc & 0x7FFF));
+ thumb_opcode_val = (opcode >> 8) & 0xFF;
+
+ switch(thumb_opcode_val)
+ {
+ case 0x00 ... 0x07:
+ /* LSL rd, rs, offset */
+ thumb_shift(shift, lsl, imm);
+ break;
+
+ case 0x08 ... 0x0F:
+ /* LSR rd, rs, offset */
+ thumb_shift(shift, lsr, imm);
+ break;
+
+ case 0x10 ... 0x17:
+ /* ASR rd, rs, offset */
+ thumb_shift(shift, asr, imm);
+ break;
+
+ case 0x18 ... 0x19:
+ /* ADD rd, rs, rn */
+ thumb_add(add_sub, rd, reg[rs], reg[rn]);
+ break;
+
+ case 0x1A ... 0x1B:
+ /* SUB rd, rs, rn */
+ thumb_sub(add_sub, rd, reg[rs], reg[rn]);
+ break;
+
+ case 0x1C ... 0x1D:
+ /* ADD rd, rs, imm */
+ thumb_add(add_sub_imm, rd, reg[rs], imm);
+ break;
+
+ case 0x1E ... 0x1F:
+ /* SUB rd, rs, imm */
+ thumb_sub(add_sub_imm, rd, reg[rs], imm);
+ break;
+
+ case 0x20:
+ /* MOV r0, imm */
+ case 0x21:
+ /* MOV r1, imm */
+ case 0x22:
+ /* MOV r2, imm */
+ case 0x23:
+ /* MOV r3, imm */
+ case 0x24:
+ /* MOV r4, imm */
+ case 0x25:
+ /* MOV r5, imm */
+ case 0x26:
+ /* MOV r6, imm */
+ case 0x27:
+ /* MOV r7, imm */
+ thumb_logic(imm, thumb_opcode_val & 0x7, imm);
+ break;
+
+ case 0x28:
+ /* CMP r0, imm */
+ case 0x29:
+ /* CMP r1, imm */
+ case 0x2A:
+ /* CMP r2, imm */
+ case 0x2B:
+ /* CMP r3, imm */
+ case 0x2C:
+ /* CMP r4, imm */
+ case 0x2D:
+ /* CMP r5, imm */
+ case 0x2E:
+ /* CMP r6, imm */
+ case 0x2F:
+ /* CMP r7, imm */
+ thumb_test_sub(imm, reg[thumb_opcode_val & 0x7], imm);
+ break;
+
+ case 0x30:
+ /* ADD r0, imm */
+ case 0x31:
+ /* ADD r1, imm */
+ case 0x32:
+ /* ADD r2, imm */
+ case 0x33:
+ /* ADD r3, imm */
+ case 0x34:
+ /* ADD r4, imm */
+ case 0x35:
+ /* ADD r5, imm */
+ case 0x36:
+ /* ADD r6, imm */
+ case 0x37:
+ /* ADD r7, imm */
+ thumb_add(imm, thumb_opcode_val & 0x7, reg[thumb_opcode_val & 0x7], imm);
+ break;
+
+ case 0x38:
+ /* SUB r0, imm */
+ case 0x39:
+ /* SUB r1, imm */
+ case 0x3A:
+ /* SUB r2, imm */
+ case 0x3B:
+ /* SUB r3, imm */
+ case 0x3C:
+ /* SUB r4, imm */
+ case 0x3D:
+ /* SUB r5, imm */
+ case 0x3E:
+ /* SUB r6, imm */
+ case 0x3F:
+ /* SUB r7, imm */
+ thumb_sub(imm, thumb_opcode_val & 0x7, reg[thumb_opcode_val & 0x7], imm);
+ break;
+
+ case 0x40:
+ switch((opcode >> 6) & 0x03)
+ {
+ case 0x00:
+ /* AND rd, rs */
+ thumb_logic(alu_op, rd, reg[rd] & reg[rs]);
+ break;
+
+ case 0x01:
+ /* EOR rd, rs */
+ thumb_logic(alu_op, rd, reg[rd] ^ reg[rs]);
+ break;
+
+ case 0x02:
+ /* LSL rd, rs */
+ thumb_shift(alu_op, lsl, reg);
+ break;
+
+ case 0x03:
+ /* LSR rd, rs */
+ thumb_shift(alu_op, lsr, reg);
+ break;
+ }
+ break;
+
+ case 0x41:
+ switch((opcode >> 6) & 0x03)
+ {
+ case 0x00:
+ /* ASR rd, rs */
+ thumb_shift(alu_op, asr, reg);
+ break;
+
+ case 0x01:
+ /* ADC rd, rs */
+ thumb_add(alu_op, rd, reg[rd] + reg[rs], c_flag);
+ break;
+
+ case 0x02:
+ /* SBC rd, rs */
+ thumb_sub(alu_op, rd, reg[rd] - reg[rs], (c_flag ^ 1));
+ break;
+
+ case 0x03:
+ /* ROR rd, rs */
+ thumb_shift(alu_op, ror, reg);
+ break;
+ }
+ break;
+
+ case 0x42:
+ switch((opcode >> 6) & 0x03)
+ {
+ case 0x00:
+ /* TST rd, rs */
+ thumb_test_logic(alu_op, reg[rd] & reg[rs]);
+ break;
+
+ case 0x01:
+ /* NEG rd, rs */
+ thumb_sub(alu_op, rd, 0, reg[rs]);
+ break;
+
+ case 0x02:
+ /* CMP rd, rs */
+ thumb_test_sub(alu_op, reg[rd], reg[rs]);
+ break;
+
+ case 0x03:
+ /* CMN rd, rs */
+ thumb_test_add(alu_op, reg[rd], reg[rs]);
+ break;
+ }
+ break;
+
+ case 0x43:
+ switch((opcode >> 6) & 0x03)
+ {
+ case 0x00:
+ /* ORR rd, rs */
+ thumb_logic(alu_op, rd, reg[rd] | reg[rs]);
+ break;
+
+ case 0x01:
+ /* MUL rd, rs */
+ thumb_logic(alu_op, rd, reg[rd] * reg[rs]);
+ break;
+
+ case 0x02:
+ /* BIC rd, rs */
+ thumb_logic(alu_op, rd, reg[rd] & (~reg[rs]));
+ break;
+
+ case 0x03:
+ /* MVN rd, rs */
+ thumb_logic(alu_op, rd, ~reg[rs]);
+ break;
+ }
+ break;
+
+ case 0x44:
+ /* ADD rd, rs */
+ thumb_hireg_op(reg[rd] + reg[rs]);
+ break;
+
+ case 0x45:
+ /* CMP rd, rs */
+ {
+ thumb_pc_offset(4);
+ thumb_decode_hireg_op();
+ u32 _sa = reg[rd];
+ u32 _sb = reg[rs];
+ u32 dest = _sa - _sb;
+ thumb_pc_offset(-2);
+ calculate_flags_sub(dest, _sa, _sb);
+ }
+ break;
+
+ case 0x46:
+ /* MOV rd, rs */
+ thumb_hireg_op(reg[rs]);
+ break;
+
+ case 0x47:
+ /* BX rs */
+ {
+ thumb_decode_hireg_op();
+ u32 src;
+ thumb_pc_offset(4);
+ src = reg[rs];
+ if(src & 0x01)
+ {
+ src -= 1;
+ thumb_pc_offset_update_direct(src);
+ }
+ else
+ {
+ /* Switch to ARM mode */
+ thumb_pc_offset_update_direct(src);
+ reg[REG_CPSR] &= ~0x20;
+ collapse_flags();
+ goto arm_loop;
+ }
+ }
+ break;
+
+ case 0x48:
+ /* LDR r0, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[0], u32);
+ break;
+
+ case 0x49:
+ /* LDR r1, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[1], u32);
+ break;
+
+ case 0x4A:
+ /* LDR r2, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[2], u32);
+ break;
+
+ case 0x4B:
+ /* LDR r3, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[3], u32);
+ break;
+
+ case 0x4C:
+ /* LDR r4, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[4], u32);
+ break;
+
+ case 0x4D:
+ /* LDR r5, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[5], u32);
+ break;
+
+ case 0x4E:
+ /* LDR r6, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[6], u32);
+ break;
+
+ case 0x4F:
+ /* LDR r7, [pc + imm] */
+ thumb_access_memory(load, imm, (pc & ~2) + (imm * 4) + 4, reg[7], u32);
+ break;
+
+ case 0x50 ... 0x51:
+ /* STR rd, [rb + ro] */
+ thumb_access_memory(store, mem_reg, reg[rb] + reg[ro], reg[rd], u32);
+ break;
+
+ case 0x52 ... 0x53:
+ /* STRH rd, [rb + ro] */
+ thumb_access_memory(store, mem_reg, reg[rb] + reg[ro], reg[rd], u16);
+ break;
+
+ case 0x54 ... 0x55:
+ /* STRB rd, [rb + ro] */
+ thumb_access_memory(store, mem_reg, reg[rb] + reg[ro], reg[rd], u8);
+ break;
+
+ case 0x56 ... 0x57:
+ /* LDSB rd, [rb + ro] */
+ thumb_access_memory(load, mem_reg, reg[rb] + reg[ro], reg[rd], s8);
+ break;
+
+ case 0x58 ... 0x59:
+ /* LDR rd, [rb + ro] */
+ thumb_access_memory(load, mem_reg, reg[rb] + reg[ro], reg[rd], u32);
+ break;
+
+ case 0x5A ... 0x5B:
+ /* LDRH rd, [rb + ro] */
+ thumb_access_memory(load, mem_reg, reg[rb] + reg[ro], reg[rd], u16);
+ break;
+
+ case 0x5C ... 0x5D:
+ /* LDRB rd, [rb + ro] */
+ thumb_access_memory(load, mem_reg, reg[rb] + reg[ro], reg[rd], u8);
+ break;
+
+ case 0x5E ... 0x5F:
+ /* LDSH rd, [rb + ro] */
+ thumb_access_memory(load, mem_reg, reg[rb] + reg[ro], reg[rd], s16);
+ break;
+
+ case 0x60 ... 0x67:
+ /* STR rd, [rb + imm] */
+ thumb_access_memory(store, mem_imm, reg[rb] + (imm * 4), reg[rd], u32);
+ break;
+
+ case 0x68 ... 0x6F:
+ /* LDR rd, [rb + imm] */
+ thumb_access_memory(load, mem_imm, reg[rb] + (imm * 4), reg[rd], u32);
+ break;
+
+ case 0x70 ... 0x77:
+ /* STRB rd, [rb + imm] */
+ thumb_access_memory(store, mem_imm, reg[rb] + imm, reg[rd], u8);
+ break;
+
+ case 0x78 ... 0x7F:
+ /* LDRB rd, [rb + imm] */
+ thumb_access_memory(load, mem_imm, reg[rb] + imm, reg[rd], u8);
+ break;
+
+ case 0x80 ... 0x87:
+ /* STRH rd, [rb + imm] */
+ thumb_access_memory(store, mem_imm, reg[rb] + (imm * 2), reg[rd], u16);
+ break;
+
+ case 0x88 ... 0x8F:
+ /* LDRH rd, [rb + imm] */
+ thumb_access_memory(load, mem_imm, reg[rb] + (imm * 2), reg[rd], u16);
+ break;
+
+ case 0x90:
+ /* STR r0, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[0], u32);
+ break;
+
+ case 0x91:
+ /* STR r1, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[1], u32);
+ break;
+
+ case 0x92:
+ /* STR r2, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[2], u32);
+ break;
+
+ case 0x93:
+ /* STR r3, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[3], u32);
+ break;
+
+ case 0x94:
+ /* STR r4, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[4], u32);
+ break;
+
+ case 0x95:
+ /* STR r5, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[5], u32);
+ break;
+
+ case 0x96:
+ /* STR r6, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[6], u32);
+ break;
+
+ case 0x97:
+ /* STR r7, [sp + imm] */
+ thumb_access_memory(store, imm, reg[REG_SP] + (imm * 4), reg[7], u32);
+ break;
+
+ case 0x98:
+ /* LDR r0, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[0], u32);
+ break;
+
+ case 0x99:
+ /* LDR r1, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[1], u32);
+ break;
+
+ case 0x9A:
+ /* LDR r2, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[2], u32);
+ break;
+
+ case 0x9B:
+ /* LDR r3, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[3], u32);
+ break;
+
+ case 0x9C:
+ /* LDR r4, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[4], u32);
+ break;
+
+ case 0x9D:
+ /* LDR r5, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[5], u32);
+ break;
+
+ case 0x9E:
+ /* LDR r6, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[6], u32);
+ break;
+
+ case 0x9F:
+ /* LDR r7, [sp + imm] */
+ thumb_access_memory(load, imm, reg[REG_SP] + (imm * 4), reg[7], u32);
+ break;
+
+ case 0xA0:
+ /* ADD r0, pc, +imm */
+ thumb_add_noflags(imm, 0, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA1:
+ /* ADD r1, pc, +imm */
+ thumb_add_noflags(imm, 1, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA2:
+ /* ADD r2, pc, +imm */
+ thumb_add_noflags(imm, 2, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA3:
+ /* ADD r3, pc, +imm */
+ thumb_add_noflags(imm, 3, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA4:
+ /* ADD r4, pc, +imm */
+ thumb_add_noflags(imm, 4, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA5:
+ /* ADD r5, pc, +imm */
+ thumb_add_noflags(imm, 5, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA6:
+ /* ADD r6, pc, +imm */
+ thumb_add_noflags(imm, 6, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA7:
+ /* ADD r7, pc, +imm */
+ thumb_add_noflags(imm, 7, (pc & ~2) + 4, (imm * 4));
+ break;
+
+ case 0xA8:
+ /* ADD r0, sp, +imm */
+ thumb_add_noflags(imm, 0, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xA9:
+ /* ADD r1, sp, +imm */
+ thumb_add_noflags(imm, 1, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xAA:
+ /* ADD r2, sp, +imm */
+ thumb_add_noflags(imm, 2, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xAB:
+ /* ADD r3, sp, +imm */
+ thumb_add_noflags(imm, 3, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xAC:
+ /* ADD r4, sp, +imm */
+ thumb_add_noflags(imm, 4, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xAD:
+ /* ADD r5, sp, +imm */
+ thumb_add_noflags(imm, 5, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xAE:
+ /* ADD r6, sp, +imm */
+ thumb_add_noflags(imm, 6, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xAF:
+ /* ADD r7, sp, +imm */
+ thumb_add_noflags(imm, 7, reg[REG_SP], (imm * 4));
+ break;
+
+ case 0xB0 ... 0xB3:
+ if((opcode >> 7) & 0x01)
+ {
+ /* ADD sp, -imm */
+ thumb_add_noflags(add_sp, 13, reg[REG_SP], -(imm * 4));
+ }
+ else
+ {
+ /* ADD sp, +imm */
+ thumb_add_noflags(add_sp, 13, reg[REG_SP], (imm * 4));
+ }
+ break;
+
+ case 0xB4:
+ /* PUSH rlist */
+ thumb_block_memory(store, down, no_op, 13);
+ break;
+
+ case 0xB5:
+ /* PUSH rlist, lr */
+ thumb_block_memory(store, push_lr, push_lr, 13);
+ break;
+
+ case 0xBC:
+ /* POP rlist */
+ thumb_block_memory(load, no_op, up, 13);
+ break;
+
+ case 0xBD:
+ /* POP rlist, pc */
+ thumb_block_memory(load, no_op, pop_pc, 13);
+ break;
+
+ case 0xC0:
+ /* STMIA r0!, rlist */
+ thumb_block_memory(store, no_op, up, 0);
+ break;
+
+ case 0xC1:
+ /* STMIA r1!, rlist */
+ thumb_block_memory(store, no_op, up, 1);
+ break;
+
+ case 0xC2:
+ /* STMIA r2!, rlist */
+ thumb_block_memory(store, no_op, up, 2);
+ break;
+
+ case 0xC3:
+ /* STMIA r3!, rlist */
+ thumb_block_memory(store, no_op, up, 3);
+ break;
+
+ case 0xC4:
+ /* STMIA r4!, rlist */
+ thumb_block_memory(store, no_op, up, 4);
+ break;
+
+ case 0xC5:
+ /* STMIA r5!, rlist */
+ thumb_block_memory(store, no_op, up, 5);
+ break;
+
+ case 0xC6:
+ /* STMIA r6!, rlist */
+ thumb_block_memory(store, no_op, up, 6);
+ break;
+
+ case 0xC7:
+ /* STMIA r7!, rlist */
+ thumb_block_memory(store, no_op, up, 7);
+ break;
+
+ case 0xC8:
+ /* LDMIA r0!, rlist */
+ thumb_block_memory(load, no_op, up, 0);
+ break;
+
+ case 0xC9:
+ /* LDMIA r1!, rlist */
+ thumb_block_memory(load, no_op, up, 1);
+ break;
+
+ case 0xCA:
+ /* LDMIA r2!, rlist */
+ thumb_block_memory(load, no_op, up, 2);
+ break;
+
+ case 0xCB:
+ /* LDMIA r3!, rlist */
+ thumb_block_memory(load, no_op, up, 3);
+ break;
+
+ case 0xCC:
+ /* LDMIA r4!, rlist */
+ thumb_block_memory(load, no_op, up, 4);
+ break;
+
+ case 0xCD:
+ /* LDMIA r5!, rlist */
+ thumb_block_memory(load, no_op, up, 5);
+ break;
+
+ case 0xCE:
+ /* LDMIA r6!, rlist */
+ thumb_block_memory(load, no_op, up, 6);
+ break;
+
+ case 0xCF:
+ /* LDMIA r7!, rlist */
+ thumb_block_memory(load, no_op, up, 7);
+ break;
+
+ case 0xD0:
+ /* BEQ label */
+ thumb_conditional_branch(z_flag == 1);
+ break;
+
+ case 0xD1:
+ /* BNE label */
+ thumb_conditional_branch(z_flag == 0);
+ break;
+
+ case 0xD2:
+ /* BCS label */
+ thumb_conditional_branch(c_flag == 1);
+ break;
+
+ case 0xD3:
+ /* BCC label */
+ thumb_conditional_branch(c_flag == 0);
+ break;
+
+ case 0xD4:
+ /* BMI label */
+ thumb_conditional_branch(n_flag == 1);
+ break;
+
+ case 0xD5:
+ /* BPL label */
+ thumb_conditional_branch(n_flag == 0);
+ break;
+
+ case 0xD6:
+ /* BVS label */
+ thumb_conditional_branch(v_flag == 1);
+ break;
+
+ case 0xD7:
+ /* BVC label */
+ thumb_conditional_branch(v_flag == 0);
+ break;
+
+ case 0xD8:
+ /* BHI label */
+ thumb_conditional_branch(c_flag & (z_flag ^ 1));
+ break;
+
+ case 0xD9:
+ /* BLS label */
+ thumb_conditional_branch((c_flag == 0) | z_flag);
+ break;
+
+ case 0xDA:
+ /* BGE label */
+ thumb_conditional_branch(n_flag == v_flag);
+ break;
+
+ case 0xDB:
+ /* BLT label */
+ thumb_conditional_branch(n_flag != v_flag);
+ break;
+
+ case 0xDC:
+ /* BGT label */
+ thumb_conditional_branch((z_flag == 0) & (n_flag == v_flag));
+ break;
+
+ case 0xDD:
+ /* BLE label */
+ thumb_conditional_branch(z_flag | (n_flag != v_flag));
+ break;
+
+ case 0xDF:
+ {
+ /* SWI comment */
+ u32 swi_comment = opcode & 0xFF;
+
+ switch(swi_comment)
+ {
+ default:
+ reg_mode[MODE_SUPERVISOR][6] = pc + 2;
+ spsr[MODE_SUPERVISOR] = reg[REG_CPSR];
+ reg[REG_PC] = 0x00000008;
+ thumb_update_pc();
+ reg[REG_CPSR] = (reg[REG_CPSR] & ~0x3F) | 0x13;
+ set_cpu_mode(MODE_SUPERVISOR);
+ collapse_flags();
+ goto arm_loop;
+ }
+ break;
+ }
+
+ case 0xE0 ... 0xE7:
+ {
+ /* B label */
+ thumb_decode_branch();
+ thumb_pc_offset_update(((s32)(offset << 21) >> 20) + 4);
+ break;
+ }
+
+ case 0xF0 ... 0xF7:
+ {
+ /* (low word) BL label */
+ thumb_decode_branch();
+ reg[REG_LR] = pc + 4 + ((s32)(offset << 21) >> 9);
+ thumb_pc_offset(2);
+ break;
+ }
+
+ case 0xF8 ... 0xFF:
+ {
+ /* (high word) BL label */
+ thumb_decode_branch();
+ u32 lr = (pc + 2) | 0x01;
+ pc = reg[REG_LR] + (offset * 2);
+ reg[REG_LR] = lr;
+ reg[REG_PC] = pc;
+ break;
+ }
+ }
+
+ /* End of Execute THUMB instruction */
+ cycles_remaining -= cycles_per_instruction;
+ } while(cycles_remaining > 0);
+
+ collapse_flags();
+ cycles = update_gba();
+ continue;
+
+ alert:
+
+ if(cpu_alert == CPU_ALERT_IRQ)
+ cycles = cycles_remaining;
+ else
+ {
+ collapse_flags();
+
+ while(reg[CPU_HALT_STATE] != CPU_ACTIVE)
+ cycles = update_gba();
+ }
+ }
+}
+
+void init_cpu(void)
+{
+ u32 i;
+
+ for(i = 0; i < 16; i++)
+ reg[i] = 0;
+
+ reg[REG_SP] = 0x03007F00;
+ reg[REG_PC] = 0x08000000;
+ reg[REG_CPSR] = 0x0000001F;
+ reg[CPU_HALT_STATE] = CPU_ACTIVE;
+ reg[CPU_MODE] = MODE_USER;
+ reg[CHANGED_PC_STATUS] = 0;
+
+ reg_mode[MODE_USER][5] = 0x03007F00;
+ reg_mode[MODE_IRQ][5] = 0x03007FA0;
+ reg_mode[MODE_FIQ][5] = 0x03007FA0;
+ reg_mode[MODE_SUPERVISOR][5] = 0x03007FE0;
+}
+
+void move_reg(u32 *new_reg)
+{
+ u32 i;
+
+ for(i = 0; i < 32; i++)
+ new_reg[i] = reg[i];
+
+ reg = new_reg;
+}
+
+
+#define cpu_savestate_builder(type) \
+void cpu_##type##_savestate(void) \
+{ \
+ state_mem_##type(reg, 0x100); \
+ state_mem_##type##_array(spsr); \
+ state_mem_##type##_array(reg_mode); \
+}
+
+cpu_savestate_builder(read)
+cpu_savestate_builder(write)