diff options
author | notaz | 2009-05-21 18:48:31 +0300 |
---|---|---|
committer | notaz | 2009-05-21 18:48:31 +0300 |
commit | 2823a4c8196a02da86ee180cf55586d4e8c91a2f (patch) | |
tree | 7e9b3f35b55af9917b3a05dd32de14be9a8c3f3c /x86 | |
download | picogpsp-2823a4c8196a02da86ee180cf55586d4e8c91a2f.tar.gz picogpsp-2823a4c8196a02da86ee180cf55586d4e8c91a2f.tar.bz2 picogpsp-2823a4c8196a02da86ee180cf55586d4e8c91a2f.zip |
original source from gpsp09-2xb_src.tar.bz2
Diffstat (limited to 'x86')
-rw-r--r-- | x86/Makefile | 38 | ||||
-rw-r--r-- | x86/x86_emit.h | 2327 | ||||
-rw-r--r-- | x86/x86_stub.S | 501 |
3 files changed, 2866 insertions, 0 deletions
diff --git a/x86/Makefile b/x86/Makefile new file mode 100644 index 0000000..e0a5767 --- /dev/null +++ b/x86/Makefile @@ -0,0 +1,38 @@ +# gpSP makefile +# Gilead Kutnick - Exophase + +# Global definitions + +CC = gcc +STRIP = strip +AS = as + +PREFIX = /usr +OBJS = main.o cpu.o memory.o video.o input.o sound.o \ + cpu_threaded.o gui.o x86_stub.o cheats.o zip.o +BIN ?= gpsp.exe + +# Platform specific definitions + +VPATH += .. +CFLAGS += -DPC_BUILD +INCLUDES = -I${PREFIX}/include `sdl-config --cflags` +LIBS = -L${PREFIX}/lib `sdl-config --libs` -mconsole -lz + +# Compilation: + +.SUFFIXES: .c .S + +%.o: %.c + ${CC} ${CFLAGS} ${INCLUDES} -c -o $@ $< + +%.o: %.S + ${AS} -o $@ $< + +all: ${OBJS} + ${CC} ${OBJS} ${LIBS} -o ${BIN} + ${STRIP} ${BIN} + +clean: + rm -f *.o ${BIN} + diff --git a/x86/x86_emit.h b/x86/x86_emit.h new file mode 100644 index 0000000..6f02d07 --- /dev/null +++ b/x86/x86_emit.h @@ -0,0 +1,2327 @@ +/* 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 + */ + +#ifndef X86_EMIT_H +#define X86_EMIT_H + +u32 x86_update_gba(u32 pc); + +// Although these are defined as a function, don't call them as +// such (jump to it instead) +void x86_indirect_branch_arm(u32 address); +void x86_indirect_branch_thumb(u32 address); +void x86_indirect_branch_dual(u32 address); + +void function_cc execute_store_cpsr(u32 new_cpsr, u32 store_mask); + +void step_debug_x86(u32 pc); + +typedef enum +{ + x86_reg_number_eax, + x86_reg_number_ecx, + x86_reg_number_edx, + x86_reg_number_ebx, + x86_reg_number_esp, + x86_reg_number_ebp, + x86_reg_number_esi, + x86_reg_number_edi +} x86_reg_number; + +#define x86_emit_byte(value) \ + *translation_ptr = value; \ + translation_ptr++ \ + +#define x86_emit_dword(value) \ + *((u32 *)translation_ptr) = value; \ + translation_ptr += 4 \ + +typedef enum +{ + x86_mod_mem = 0, + x86_mod_mem_disp8 = 1, + x86_mod_mem_disp32 = 2, + x86_mod_reg = 3 +} x86_mod; + +#define x86_emit_mod_rm(mod, rm, spare) \ + x86_emit_byte((mod << 6) | (spare << 3) | rm) \ + +#define x86_emit_mem_op(dest, base, offset) \ + if(offset == 0) \ + { \ + x86_emit_mod_rm(x86_mod_mem, base, dest); \ + } \ + else \ + \ + if(((s32)offset < 127) && ((s32)offset > -128)) \ + { \ + x86_emit_mod_rm(x86_mod_mem_disp8, base, dest); \ + x86_emit_byte((s8)offset); \ + } \ + else \ + { \ + x86_emit_mod_rm(x86_mod_mem_disp32, base, dest); \ + x86_emit_dword(offset); \ + } \ + +#define x86_emit_reg_op(dest, source) \ + x86_emit_mod_rm(x86_mod_reg, source, dest) \ + + +typedef enum +{ + x86_opcode_mov_rm_reg = 0x89, + x86_opcode_mov_reg_rm = 0x8B, + x86_opcode_mov_reg_imm = 0xB8, + x86_opcode_mov_rm_imm = 0x00C7, + x86_opcode_ror_reg_imm = 0x01C1, + x86_opcode_shl_reg_imm = 0x04C1, + x86_opcode_shr_reg_imm = 0x05C1, + x86_opcode_sar_reg_imm = 0x07C1, + x86_opcode_push_reg = 0x50, + x86_opcode_push_rm = 0xFF, + x86_opcode_push_imm = 0x0668, + x86_opcode_call_offset = 0xE8, + x86_opcode_ret = 0xC3, + x86_opcode_test_rm_imm = 0x00F7, + x86_opcode_test_reg_rm = 0x85, + x86_opcode_mul_eax_rm = 0x04F7, + x86_opcode_imul_eax_rm = 0x05F7, + x86_opcode_idiv_eax_rm = 0x07F7, + x86_opcode_add_rm_imm = 0x0081, + x86_opcode_and_rm_imm = 0x0481, + x86_opcode_sub_rm_imm = 0x0581, + x86_opcode_xor_rm_imm = 0x0681, + x86_opcode_add_reg_rm = 0x03, + x86_opcode_adc_reg_rm = 0x13, + x86_opcode_or_reg_rm = 0x0B, + x86_opcode_sub_reg_rm = 0x2B, + x86_opcode_xor_reg_rm = 0x33, + x86_opcode_cmp_reg_rm = 0x39, + x86_opcode_cmp_rm_imm = 0x053B, + x86_opcode_lea_reg_rm = 0x8D, + x86_opcode_j = 0x80, + x86_opcode_jmp = 0xE9, + x86_opcode_jmp_reg = 0x04FF, + x86_opcode_ext = 0x0F +} x86_opcodes; + +typedef enum +{ + x86_condition_code_o = 0x00, + x86_condition_code_no = 0x01, + x86_condition_code_c = 0x02, + x86_condition_code_nc = 0x03, + x86_condition_code_z = 0x04, + x86_condition_code_nz = 0x05, + x86_condition_code_na = 0x06, + x86_condition_code_a = 0x07, + x86_condition_code_s = 0x08, + x86_condition_code_ns = 0x09, + x86_condition_code_p = 0x0A, + x86_condition_code_np = 0x0B, + x86_condition_code_l = 0x0C, + x86_condition_code_nl = 0x0D, + x86_condition_code_ng = 0x0E, + x86_condition_code_g = 0x0F +} x86_condition_codes; + +#define x86_relative_offset(source, offset, next) \ + ((u32)offset - ((u32)source + next)) \ + +#define x86_unequal_operands(op_a, op_b) \ + (x86_reg_number_##op_a != x86_reg_number_##op_b) \ + +#define x86_emit_opcode_1b_reg(opcode, dest, source) \ +{ \ + x86_emit_byte(x86_opcode_##opcode); \ + x86_emit_reg_op(x86_reg_number_##dest, x86_reg_number_##source); \ +} \ + +#define x86_emit_opcode_1b_mem(opcode, dest, base, offset) \ +{ \ + x86_emit_byte(x86_opcode_##opcode); \ + x86_emit_mem_op(x86_reg_number_##dest, x86_reg_number_##base, offset); \ +} \ + +#define x86_emit_opcode_1b(opcode, reg) \ + x86_emit_byte(x86_opcode_##opcode | x86_reg_number_##reg) \ + +#define x86_emit_opcode_1b_ext_reg(opcode, dest) \ + x86_emit_byte(x86_opcode_##opcode & 0xFF); \ + x86_emit_reg_op(x86_opcode_##opcode >> 8, x86_reg_number_##dest) \ + +#define x86_emit_opcode_1b_ext_mem(opcode, base, offset) \ + x86_emit_byte(x86_opcode_##opcode & 0xFF); \ + x86_emit_mem_op(x86_opcode_##opcode >> 8, x86_reg_number_##base, offset) \ + +#define x86_emit_mov_reg_mem(dest, base, offset) \ + x86_emit_opcode_1b_mem(mov_reg_rm, dest, base, offset) \ + +#define x86_emit_mov_mem_reg(source, base, offset) \ + x86_emit_opcode_1b_mem(mov_rm_reg, source, base, offset) \ + +#define x86_emit_mov_reg_reg(dest, source) \ + if(x86_unequal_operands(dest, source)) \ + { \ + x86_emit_opcode_1b_reg(mov_reg_rm, dest, source) \ + } \ + +#define x86_emit_mov_reg_imm(dest, imm) \ + x86_emit_opcode_1b(mov_reg_imm, dest); \ + x86_emit_dword(imm) \ + +#define x86_emit_mov_mem_imm(imm, base, offset) \ + x86_emit_opcode_1b_ext_mem(mov_rm_imm, base, offset); \ + x86_emit_dword(imm) \ + +#define x86_emit_shl_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(shl_reg_imm, dest); \ + x86_emit_byte(imm) \ + +#define x86_emit_shr_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(shr_reg_imm, dest); \ + x86_emit_byte(imm) \ + +#define x86_emit_sar_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(sar_reg_imm, dest); \ + x86_emit_byte(imm) \ + +#define x86_emit_ror_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(ror_reg_imm, dest); \ + x86_emit_byte(imm) \ + +#define x86_emit_add_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(add_reg_rm, dest, source) \ + +#define x86_emit_adc_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(adc_reg_rm, dest, source) \ + +#define x86_emit_sub_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(sub_reg_rm, dest, source) \ + +#define x86_emit_or_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(or_reg_rm, dest, source) \ + +#define x86_emit_xor_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(xor_reg_rm, dest, source) \ + +#define x86_emit_add_reg_imm(dest, imm) \ + if(imm != 0) \ + { \ + x86_emit_opcode_1b_ext_reg(add_rm_imm, dest); \ + x86_emit_dword(imm); \ + } \ + +#define x86_emit_sub_reg_imm(dest, imm) \ + if(imm != 0) \ + { \ + x86_emit_opcode_1b_ext_reg(sub_rm_imm, dest); \ + x86_emit_dword(imm); \ + } \ + +#define x86_emit_and_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(and_rm_imm, dest); \ + x86_emit_dword(imm) \ + +#define x86_emit_xor_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(xor_rm_imm, dest); \ + x86_emit_dword(imm) \ + +#define x86_emit_test_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(test_rm_imm, dest); \ + x86_emit_dword(imm) \ + +#define x86_emit_cmp_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(cmp_reg_rm, dest, source) \ + +#define x86_emit_test_reg_reg(dest, source) \ + x86_emit_opcode_1b_reg(test_reg_rm, dest, source) \ + +#define x86_emit_cmp_reg_imm(dest, imm) \ + x86_emit_opcode_1b_ext_reg(cmp_rm_imm, dest); \ + x86_emit_dword(imm) \ + +#define x86_emit_mul_eax_reg(source) \ + x86_emit_opcode_1b_ext_reg(mul_eax_rm, source) \ + +#define x86_emit_imul_eax_reg(source) \ + x86_emit_opcode_1b_ext_reg(imul_eax_rm, source) \ + +#define x86_emit_idiv_eax_reg(source) \ + x86_emit_opcode_1b_ext_reg(idiv_eax_rm, source) \ + +#define x86_emit_push_mem(base, offset) \ + x86_emit_opcode_1b_mem(push_rm, 0x06, base, offset) \ + +#define x86_emit_push_imm(imm) \ + x86_emit_byte(x86_opcode_push_imm); \ + x86_emit_dword(imm) \ + +#define x86_emit_call_offset(relative_offset) \ + x86_emit_byte(x86_opcode_call_offset); \ + x86_emit_dword(relative_offset) \ + +#define x86_emit_ret() \ + x86_emit_byte(x86_opcode_ret) \ + +#define x86_emit_lea_reg_mem(dest, base, offset) \ + x86_emit_opcode_1b_mem(lea_reg_rm, dest, base, offset) \ + +#define x86_emit_j_filler(condition_code, writeback_location) \ + x86_emit_byte(x86_opcode_ext); \ + x86_emit_byte(x86_opcode_j | condition_code); \ + (writeback_location) = translation_ptr; \ + translation_ptr += 4 \ + +#define x86_emit_j_offset(condition_code, offset) \ + x86_emit_byte(x86_opcode_ext); \ + x86_emit_byte(x86_opcode_j | condition_code); \ + x86_emit_dword(offset) \ + +#define x86_emit_jmp_filler(writeback_location) \ + x86_emit_byte(x86_opcode_jmp); \ + (writeback_location) = translation_ptr; \ + translation_ptr += 4 \ + +#define x86_emit_jmp_offset(offset) \ + x86_emit_byte(x86_opcode_jmp); \ + x86_emit_dword(offset) \ + +#define x86_emit_jmp_reg(source) \ + x86_emit_opcode_1b_ext_reg(jmp_reg, source) \ + +#define reg_base ebx +#define reg_cycles edi +#define reg_a0 eax +#define reg_a1 edx +#define reg_a2 ecx +#define reg_rv eax +#define reg_s0 esi + +#define generate_load_reg(ireg, reg_index) \ + x86_emit_mov_reg_mem(reg_##ireg, reg_base, reg_index * 4); \ + +#define generate_load_pc(ireg, new_pc) \ + x86_emit_mov_reg_imm(reg_##ireg, new_pc) \ + +#define generate_load_imm(ireg, imm) \ + x86_emit_mov_reg_imm(reg_##ireg, imm) \ + +#define generate_store_reg(ireg, reg_index) \ + x86_emit_mov_mem_reg(reg_##ireg, reg_base, reg_index * 4) \ + +#define generate_shift_left(ireg, imm) \ + x86_emit_shl_reg_imm(reg_##ireg, imm) \ + +#define generate_shift_right(ireg, imm) \ + x86_emit_shr_reg_imm(reg_##ireg, imm) \ + +#define generate_shift_right_arithmetic(ireg, imm) \ + x86_emit_sar_reg_imm(reg_##ireg, imm) \ + +#define generate_rotate_right(ireg, imm) \ + x86_emit_ror_reg_imm(reg_##ireg, imm) \ + +#define generate_add(ireg_dest, ireg_src) \ + x86_emit_add_reg_reg(reg_##ireg_dest, reg_##ireg_src) \ + +#define generate_sub(ireg_dest, ireg_src) \ + x86_emit_sub_reg_reg(reg_##ireg_dest, reg_##ireg_src) \ + +#define generate_or(ireg_dest, ireg_src) \ + x86_emit_or_reg_reg(reg_##ireg_dest, reg_##ireg_src) \ + +#define generate_xor(ireg_dest, ireg_src) \ + x86_emit_xor_reg_reg(reg_##ireg_dest, reg_##ireg_src) \ + +#define generate_add_imm(ireg, imm) \ + x86_emit_add_reg_imm(reg_##ireg, imm) \ + +#define generate_sub_imm(ireg, imm) \ + x86_emit_sub_reg_imm(reg_##ireg, imm) \ + +#define generate_xor_imm(ireg, imm) \ + x86_emit_xor_reg_imm(reg_##ireg, imm) \ + +#define generate_add_reg_reg_imm(ireg_dest, ireg_src, imm) \ + x86_emit_lea_reg_mem(reg_##ireg_dest, reg_##ireg_src, imm) \ + +#define generate_and_imm(ireg, imm) \ + x86_emit_and_reg_imm(reg_##ireg, imm) \ + +#define generate_mov(ireg_dest, ireg_src) \ + x86_emit_mov_reg_reg(reg_##ireg_dest, reg_##ireg_src) \ + +#define generate_multiply(ireg) \ + x86_emit_imul_eax_reg(reg_##ireg) \ + +#define generate_multiply_s64(ireg) \ + x86_emit_imul_eax_reg(reg_##ireg) \ + +#define generate_multiply_u64(ireg) \ + x86_emit_mul_eax_reg(reg_##ireg) \ + +#define generate_multiply_s64_add(ireg_src, ireg_lo, ireg_hi) \ + x86_emit_imul_eax_reg(reg_##ireg_src); \ + x86_emit_add_reg_reg(reg_a0, reg_##ireg_lo); \ + x86_emit_adc_reg_reg(reg_a1, reg_##ireg_hi) \ + +#define generate_multiply_u64_add(ireg_src, ireg_lo, ireg_hi) \ + x86_emit_mul_eax_reg(reg_##ireg_src); \ + x86_emit_add_reg_reg(reg_a0, reg_##ireg_lo); \ + x86_emit_adc_reg_reg(reg_a1, reg_##ireg_hi) \ + + +#define generate_function_call(function_location) \ + x86_emit_call_offset(x86_relative_offset(translation_ptr, \ + function_location, 4)); \ + +#define generate_exit_block() \ + x86_emit_ret(); \ + +#define generate_branch_filler_true(ireg_dest, ireg_src, writeback_location) \ + x86_emit_test_reg_imm(reg_##ireg_dest, 1); \ + x86_emit_j_filler(x86_condition_code_z, writeback_location) \ + +#define generate_branch_filler_false(ireg_dest, ireg_src, writeback_location) \ + x86_emit_test_reg_imm(reg_##ireg_dest, 1); \ + x86_emit_j_filler(x86_condition_code_nz, writeback_location) \ + +#define generate_branch_filler_equal(ireg_dest, ireg_src, writeback_location) \ + x86_emit_cmp_reg_reg(reg_##ireg_dest, reg_##ireg_src); \ + x86_emit_j_filler(x86_condition_code_nz, writeback_location) \ + +#define generate_branch_filler_not_equal(ireg_dest, ireg_src, \ + writeback_location) \ + x86_emit_cmp_reg_reg(reg_##ireg_dest, reg_##ireg_src); \ + x86_emit_j_filler(x86_condition_code_z, writeback_location) \ + +#define generate_update_pc(new_pc) \ + x86_emit_mov_reg_imm(eax, new_pc) \ + +#define generate_update_pc_reg() \ + generate_update_pc(pc); \ + generate_store_reg(a0, REG_PC) \ + +#define generate_cycle_update() \ + x86_emit_sub_reg_imm(reg_cycles, cycle_count); \ + cycle_count = 0 \ + +#define generate_branch_patch_conditional(dest, offset) \ + *((u32 *)(dest)) = x86_relative_offset(dest, offset, 4) \ + +#define generate_branch_patch_unconditional(dest, offset) \ + *((u32 *)(dest)) = x86_relative_offset(dest, offset, 4) \ + +#define generate_branch_no_cycle_update(writeback_location, new_pc) \ + if(pc == idle_loop_target_pc) \ + { \ + x86_emit_mov_reg_imm(eax, new_pc); \ + generate_function_call(x86_update_gba); \ + x86_emit_jmp_filler(writeback_location); \ + } \ + else \ + { \ + x86_emit_test_reg_reg(reg_cycles, reg_cycles); \ + x86_emit_j_offset(x86_condition_code_ns, 10); \ + x86_emit_mov_reg_imm(eax, new_pc); \ + generate_function_call(x86_update_gba); \ + x86_emit_jmp_filler(writeback_location); \ + } \ + +#define generate_branch_cycle_update(writeback_location, new_pc) \ + generate_cycle_update(); \ + generate_branch_no_cycle_update(writeback_location, new_pc) \ + +#define generate_conditional_branch(ireg_a, ireg_b, type, writeback_location) \ + generate_branch_filler_##type(ireg_a, ireg_b, writeback_location) \ + +// a0 holds the destination + +#define generate_indirect_branch_cycle_update(type) \ + generate_cycle_update(); \ + x86_emit_jmp_offset(x86_relative_offset(translation_ptr, \ + x86_indirect_branch_##type, 4)) \ + +#define generate_indirect_branch_no_cycle_update(type) \ + x86_emit_jmp_offset(x86_relative_offset(translation_ptr, \ + x86_indirect_branch_##type, 4)) \ + +#define generate_block_prologue() \ + +#define generate_block_extra_vars_arm() \ + void generate_indirect_branch_arm() \ + { \ + if(condition == 0x0E) \ + { \ + generate_indirect_branch_cycle_update(arm); \ + } \ + else \ + { \ + generate_indirect_branch_no_cycle_update(arm); \ + } \ + } \ + \ + void generate_indirect_branch_dual() \ + { \ + if(condition == 0x0E) \ + { \ + generate_indirect_branch_cycle_update(dual); \ + } \ + else \ + { \ + generate_indirect_branch_no_cycle_update(dual); \ + } \ + } \ + +#define generate_block_extra_vars_thumb() \ + + +#define translate_invalidate_dcache() \ + +#define block_prologue_size 0 + +#define calculate_z_flag(dest) \ + reg[REG_Z_FLAG] = (dest == 0) \ + +#define calculate_n_flag(dest) \ + reg[REG_N_FLAG] = ((signed)dest < 0) \ + +#define calculate_c_flag_sub(dest, src_a, src_b) \ + reg[REG_C_FLAG] = ((unsigned)src_b <= (unsigned)src_a) \ + +#define calculate_v_flag_sub(dest, src_a, src_b) \ + reg[REG_V_FLAG] = ((signed)src_b > (signed)src_a) != ((signed)dest < 0) \ + +#define calculate_c_flag_add(dest, src_a, src_b) \ + reg[REG_C_FLAG] = ((unsigned)dest < (unsigned)src_a) \ + +#define calculate_v_flag_add(dest, src_a, src_b) \ + reg[REG_V_FLAG] = ((signed)dest < (signed)src_a) != ((signed)src_b < 0) \ + + + +#define get_shift_imm() \ + u32 shift = (opcode >> 7) & 0x1F \ + +#define generate_shift_reg(ireg, name, flags_op) \ + generate_load_reg_pc(ireg, rm, 12); \ + generate_load_reg(a1, ((opcode >> 8) & 0x0F)); \ + generate_function_call(execute_##name##_##flags_op##_reg); \ + generate_mov(ireg, rv) \ + +u32 function_cc execute_lsl_no_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + value = 0; + else + value <<= shift; + } + return value; +} + +u32 function_cc execute_lsr_no_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + value = 0; + else + value >>= shift; + } + return value; +} + +u32 function_cc execute_asr_no_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + value = (s32)value >> 31; + else + value = (s32)value >> shift; + } + return value; +} + +u32 function_cc execute_ror_no_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + ror(value, value, shift); + } + + return value; +} + + +u32 function_cc execute_lsl_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + { + reg[REG_C_FLAG] = value & 0x01; + + if(shift != 32) + reg[REG_C_FLAG] = 0; + + value = 0; + } + else + { + reg[REG_C_FLAG] = (value >> (32 - shift)) & 0x01; + value <<= shift; + } + } + return value; +} + +u32 function_cc execute_lsr_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + { + reg[REG_C_FLAG] = value >> 31; + + if(shift != 32) + reg[REG_C_FLAG] = 0; + + value = 0; + } + else + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + value >>= shift; + } + } + return value; +} + +u32 function_cc execute_asr_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + { + value = (s32)value >> 31; + reg[REG_C_FLAG] = value & 0x01; + } + else + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + value = (s32)value >> shift; + } + } + return value; +} + +u32 function_cc execute_ror_flags_reg(u32 value, u32 shift) +{ + if(shift != 0) + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + ror(value, value, shift); + } + + return value; +} + +u32 function_cc execute_rrx_flags(u32 value) +{ + u32 c_flag = reg[REG_C_FLAG]; + reg[REG_C_FLAG] = value & 0x01; + return (value >> 1) | (c_flag << 31); +} + +u32 function_cc execute_rrx(u32 value) +{ + return (value >> 1) | (reg[REG_C_FLAG] << 31); +} + +#define generate_shift_imm_lsl_no_flags(ireg) \ + generate_load_reg_pc(ireg, rm, 8); \ + if(shift != 0) \ + { \ + generate_shift_left(ireg, shift); \ + } \ + +#define generate_shift_imm_lsr_no_flags(ireg) \ + if(shift != 0) \ + { \ + generate_load_reg_pc(ireg, rm, 8); \ + generate_shift_right(ireg, shift); \ + } \ + else \ + { \ + generate_load_imm(ireg, 0); \ + } \ + +#define generate_shift_imm_asr_no_flags(ireg) \ + generate_load_reg_pc(ireg, rm, 8); \ + if(shift != 0) \ + { \ + generate_shift_right_arithmetic(ireg, shift); \ + } \ + else \ + { \ + generate_shift_right_arithmetic(ireg, 31); \ + } \ + +#define generate_shift_imm_ror_no_flags(ireg) \ + if(shift != 0) \ + { \ + generate_load_reg_pc(ireg, rm, 8); \ + generate_rotate_right(ireg, shift); \ + } \ + else \ + { \ + generate_load_reg_pc(a0, rm, 8); \ + generate_function_call(execute_rrx); \ + generate_mov(ireg, rv); \ + } \ + +#define generate_shift_imm_lsl_flags(ireg) \ + generate_load_reg_pc(ireg, rm, 8); \ + if(shift != 0) \ + { \ + generate_mov(a1, ireg); \ + generate_shift_right(a1, (32 - shift)); \ + generate_and_imm(a1, 1); \ + generate_store_reg(a1, REG_C_FLAG); \ + generate_shift_left(ireg, shift); \ + } \ + +#define generate_shift_imm_lsr_flags(ireg) \ + if(shift != 0) \ + { \ + generate_load_reg_pc(ireg, rm, 8); \ + generate_mov(a1, ireg); \ + generate_shift_right(a1, shift - 1); \ + generate_and_imm(a1, 1); \ + generate_store_reg(a1, REG_C_FLAG); \ + generate_shift_right(ireg, shift); \ + } \ + else \ + { \ + generate_load_reg_pc(a1, rm, 8); \ + generate_shift_right(a1, 31); \ + generate_store_reg(a1, REG_C_FLAG); \ + generate_load_imm(ireg, 0); \ + } \ + +#define generate_shift_imm_asr_flags(ireg) \ + if(shift != 0) \ + { \ + generate_load_reg_pc(ireg, rm, 8); \ + generate_mov(a1, ireg); \ + generate_shift_right_arithmetic(a1, shift - 1); \ + generate_and_imm(a1, 1); \ + generate_store_reg(a1, REG_C_FLAG); \ + generate_shift_right_arithmetic(ireg, shift); \ + } \ + else \ + { \ + generate_load_reg_pc(a0, rm, 8); \ + generate_shift_right_arithmetic(ireg, 31); \ + generate_mov(a1, ireg); \ + generate_and_imm(a1, 1); \ + generate_store_reg(a1, REG_C_FLAG); \ + } \ + +#define generate_shift_imm_ror_flags(ireg) \ + generate_load_reg_pc(ireg, rm, 8); \ + if(shift != 0) \ + { \ + generate_mov(a1, ireg); \ + generate_shift_right(a1, shift - 1); \ + generate_and_imm(a1, 1); \ + generate_store_reg(a1, REG_C_FLAG); \ + generate_rotate_right(ireg, shift); \ + } \ + else \ + { \ + generate_function_call(execute_rrx_flags); \ + generate_mov(ireg, rv); \ + } \ + +#define generate_shift_imm(ireg, name, flags_op) \ + get_shift_imm(); \ + generate_shift_imm_##name##_##flags_op(ireg) \ + +#define generate_load_rm_sh(flags_op) \ + switch((opcode >> 4) & 0x07) \ + { \ + /* LSL imm */ \ + case 0x0: \ + { \ + generate_shift_imm(a0, lsl, flags_op); \ + break; \ + } \ + \ + /* LSL reg */ \ + case 0x1: \ + { \ + generate_shift_reg(a0, lsl, flags_op); \ + break; \ + } \ + \ + /* LSR imm */ \ + case 0x2: \ + { \ + generate_shift_imm(a0, lsr, flags_op); \ + break; \ + } \ + \ + /* LSR reg */ \ + case 0x3: \ + { \ + generate_shift_reg(a0, lsr, flags_op); \ + break; \ + } \ + \ + /* ASR imm */ \ + case 0x4: \ + { \ + generate_shift_imm(a0, asr, flags_op); \ + break; \ + } \ + \ + /* ASR reg */ \ + case 0x5: \ + { \ + generate_shift_reg(a0, asr, flags_op); \ + break; \ + } \ + \ + /* ROR imm */ \ + case 0x6: \ + { \ + generate_shift_imm(a0, ror, flags_op); \ + break; \ + } \ + \ + /* ROR reg */ \ + case 0x7: \ + { \ + generate_shift_reg(a0, ror, flags_op); \ + break; \ + } \ + } \ + +#define generate_load_offset_sh() \ + switch((opcode >> 5) & 0x03) \ + { \ + /* LSL imm */ \ + case 0x0: \ + { \ + generate_shift_imm(a1, lsl, no_flags); \ + break; \ + } \ + \ + /* LSR imm */ \ + case 0x1: \ + { \ + generate_shift_imm(a1, lsr, no_flags); \ + break; \ + } \ + \ + /* ASR imm */ \ + case 0x2: \ + { \ + generate_shift_imm(a1, asr, no_flags); \ + break; \ + } \ + \ + /* ROR imm */ \ + case 0x3: \ + { \ + generate_shift_imm(a1, ror, no_flags); \ + 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() \ + reg[REG_N_FLAG] = reg[REG_CPSR] >> 31; \ + reg[REG_Z_FLAG] = (reg[REG_CPSR] >> 30) & 0x01; \ + reg[REG_C_FLAG] = (reg[REG_CPSR] >> 29) & 0x01; \ + reg[REG_V_FLAG] = (reg[REG_CPSR] >> 28) & 0x01; \ + +#define collapse_flags() \ + reg[REG_CPSR] = (reg[REG_N_FLAG] << 31) | (reg[REG_Z_FLAG] << 30) | \ + (reg[REG_C_FLAG] << 29) | (reg[REG_V_FLAG] << 28) | \ + reg[REG_CPSR] & 0xFF \ + +// 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; \ + address = 0x00000018; \ + set_cpu_mode(MODE_IRQ); \ + } \ + +#define generate_load_reg_pc(ireg, reg_index, pc_offset) \ + if(reg_index == 15) \ + { \ + generate_load_pc(ireg, pc + pc_offset); \ + } \ + else \ + { \ + generate_load_reg(ireg, reg_index); \ + } \ + +#define generate_store_reg_pc_no_flags(ireg, reg_index) \ + generate_store_reg(ireg, reg_index); \ + if(reg_index == 15) \ + { \ + generate_mov(a0, ireg); \ + generate_indirect_branch_arm(); \ + } \ + +u32 function_cc execute_spsr_restore(u32 address) +{ + 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(); + + if(reg[REG_CPSR] & 0x20) + address |= 0x01; + } + + return address; +} + +#define generate_store_reg_pc_flags(ireg, reg_index) \ + generate_store_reg(ireg, reg_index); \ + if(reg_index == 15) \ + { \ + generate_mov(a0, ireg); \ + generate_function_call(execute_spsr_restore); \ + generate_mov(a0, rv); \ + generate_indirect_branch_dual(); \ + } \ + +typedef enum +{ + CONDITION_TRUE, + CONDITION_FALSE, + CONDITION_EQUAL, + CONDITION_NOT_EQUAL +} condition_check_type; + + +#define generate_condition_eq(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_Z_FLAG); \ + condition_check = CONDITION_TRUE \ + +#define generate_condition_ne(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_Z_FLAG); \ + condition_check = CONDITION_FALSE \ + +#define generate_condition_cs(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_C_FLAG); \ + condition_check = CONDITION_TRUE \ + +#define generate_condition_cc(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_C_FLAG); \ + condition_check = CONDITION_FALSE \ + +#define generate_condition_mi(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_N_FLAG); \ + condition_check = CONDITION_TRUE \ + +#define generate_condition_pl(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_N_FLAG); \ + condition_check = CONDITION_FALSE \ + +#define generate_condition_vs(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_V_FLAG); \ + condition_check = CONDITION_TRUE \ + +#define generate_condition_vc(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_V_FLAG); \ + condition_check = CONDITION_FALSE \ + +#define generate_condition_hi(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_C_FLAG); \ + generate_xor_imm(ireg_a, 1); \ + generate_load_reg(ireg_b, REG_Z_FLAG); \ + generate_or(ireg_a, ireg_b); \ + condition_check = CONDITION_FALSE \ + +#define generate_condition_ls(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_C_FLAG); \ + generate_xor_imm(ireg_a, 1); \ + generate_load_reg(ireg_b, REG_Z_FLAG); \ + generate_or(ireg_a, ireg_b); \ + condition_check = CONDITION_TRUE \ + +#define generate_condition_ge(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_N_FLAG); \ + generate_load_reg(ireg_b, REG_V_FLAG); \ + condition_check = CONDITION_EQUAL \ + +#define generate_condition_lt(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_N_FLAG); \ + generate_load_reg(ireg_b, REG_V_FLAG); \ + condition_check = CONDITION_NOT_EQUAL \ + +#define generate_condition_gt(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_N_FLAG); \ + generate_load_reg(ireg_b, REG_V_FLAG); \ + generate_xor(ireg_b, ireg_a); \ + generate_load_reg(a0, REG_Z_FLAG); \ + generate_or(ireg_a, ireg_b); \ + condition_check = CONDITION_FALSE \ + +#define generate_condition_le(ireg_a, ireg_b) \ + generate_load_reg(ireg_a, REG_N_FLAG); \ + generate_load_reg(ireg_b, REG_V_FLAG); \ + generate_xor(ireg_b, ireg_a); \ + generate_load_reg(a0, REG_Z_FLAG); \ + generate_or(ireg_a, ireg_b); \ + condition_check = CONDITION_TRUE \ + + +#define generate_condition(ireg_a, ireg_b) \ + switch(condition) \ + { \ + case 0x0: \ + generate_condition_eq(ireg_a, ireg_b); \ + break; \ + \ + case 0x1: \ + generate_condition_ne(ireg_a, ireg_b); \ + break; \ + \ + case 0x2: \ + generate_condition_cs(ireg_a, ireg_b); \ + break; \ + \ + case 0x3: \ + generate_condition_cc(ireg_a, ireg_b); \ + break; \ + \ + case 0x4: \ + generate_condition_mi(ireg_a, ireg_b); \ + break; \ + \ + case 0x5: \ + generate_condition_pl(ireg_a, ireg_b); \ + break; \ + \ + case 0x6: \ + generate_condition_vs(ireg_a, ireg_b); \ + break; \ + \ + case 0x7: \ + generate_condition_vc(ireg_a, ireg_b); \ + break; \ + \ + case 0x8: \ + generate_condition_hi(ireg_a, ireg_b); \ + break; \ + \ + case 0x9: \ + generate_condition_ls(ireg_a, ireg_b); \ + break; \ + \ + case 0xA: \ + generate_condition_ge(ireg_a, ireg_b); \ + break; \ + \ + case 0xB: \ + generate_condition_lt(ireg_a, ireg_b); \ + break; \ + \ + case 0xC: \ + generate_condition_gt(ireg_a, ireg_b); \ + break; \ + \ + case 0xD: \ + generate_condition_le(ireg_a, ireg_b); \ + break; \ + \ + case 0xE: \ + /* AL */ \ + break; \ + \ + case 0xF: \ + /* Reserved */ \ + break; \ + } \ + generate_cycle_update() \ + +#define generate_conditional_branch_type(ireg_a, ireg_b) \ + switch(condition_check) \ + { \ + case CONDITION_TRUE: \ + generate_conditional_branch(ireg_a, ireg_b, true, backpatch_address); \ + break; \ + \ + case CONDITION_FALSE: \ + generate_conditional_branch(ireg_a, ireg_b, false, backpatch_address); \ + break; \ + \ + case CONDITION_EQUAL: \ + generate_conditional_branch(ireg_a, ireg_b, equal, backpatch_address); \ + break; \ + \ + case CONDITION_NOT_EQUAL: \ + generate_conditional_branch(ireg_a, ireg_b, not_equal, \ + backpatch_address); \ + break; \ + } \ + +#define generate_branch() \ +{ \ + if(condition == 0x0E) \ + { \ + generate_branch_cycle_update( \ + block_exits[block_exit_position].branch_source, \ + block_exits[block_exit_position].branch_target); \ + } \ + else \ + { \ + generate_branch_no_cycle_update( \ + block_exits[block_exit_position].branch_source, \ + block_exits[block_exit_position].branch_target); \ + } \ + block_exit_position++; \ +} \ + +#define rm_op_reg rm +#define rm_op_imm imm + +#define arm_data_proc_reg_flags() \ + arm_decode_data_proc_reg(); \ + if(flag_status & 0x02) \ + { \ + generate_load_rm_sh(flags) \ + } \ + else \ + { \ + generate_load_rm_sh(no_flags); \ + } \ + +#define arm_data_proc_reg() \ + arm_decode_data_proc_reg(); \ + generate_load_rm_sh(no_flags) \ + +#define arm_data_proc_imm() \ + arm_decode_data_proc_imm(); \ + ror(imm, imm, imm_ror); \ + generate_load_imm(a0, imm) \ + +#define arm_data_proc_imm_flags() \ + arm_decode_data_proc_imm(); \ + if((flag_status & 0x02) && (imm_ror != 0)) \ + { \ + /* Generate carry flag from integer rotation */ \ + generate_load_imm(a0, ((imm >> (imm_ror - 1)) & 0x01)); \ + generate_store_reg(a0, REG_C_FLAG); \ + } \ + ror(imm, imm, imm_ror); \ + generate_load_imm(a0, imm) \ + + +#define arm_data_proc(name, type, flags_op) \ +{ \ + arm_data_proc_##type(); \ + generate_load_reg_pc(a1, rn, 8); \ + generate_function_call(execute_##name); \ + generate_store_reg_pc_##flags_op(rv, rd); \ +} \ + +#define arm_data_proc_test(name, type) \ +{ \ + arm_data_proc_##type(); \ + generate_load_reg_pc(a1, rn, 8); \ + generate_function_call(execute_##name); \ +} \ + +#define arm_data_proc_unary(name, type, flags_op) \ +{ \ + arm_data_proc_##type(); \ + generate_function_call(execute_##name); \ + generate_store_reg_pc_##flags_op(rv, rd); \ +} \ + +#define arm_data_proc_mov(type) \ +{ \ + arm_data_proc_##type(); \ + generate_store_reg_pc_no_flags(a0, rd); \ +} \ + +u32 function_cc execute_mul_flags(u32 dest) +{ + calculate_z_flag(dest); + calculate_n_flag(dest); +} + +#define arm_multiply_flags_yes() \ + generate_function_call(execute_mul_flags) \ + +#define arm_multiply_flags_no(_dest) \ + +#define arm_multiply_add_no() \ + +#define arm_multiply_add_yes() \ + generate_load_reg(a1, rn); \ + generate_add(a0, a1) \ + +#define arm_multiply(add_op, flags) \ +{ \ + arm_decode_multiply(); \ + generate_load_reg(a0, rm); \ + generate_load_reg(a1, rs); \ + generate_multiply(a1); \ + arm_multiply_add_##add_op(); \ + generate_store_reg(a0, rd); \ + arm_multiply_flags_##flags(); \ +} \ + +u32 function_cc execute_mul_long_flags(u32 dest_lo, u32 dest_hi) +{ + reg[REG_Z_FLAG] = (dest_lo == 0) & (dest_hi == 0); + calculate_n_flag(dest_hi); +} + +#define arm_multiply_long_flags_yes() \ + generate_function_call(execute_mul_long_flags) \ + +#define arm_multiply_long_flags_no(_dest) \ + +#define arm_multiply_long_add_yes(name) \ + generate_load_reg(a2, rdlo); \ + generate_load_reg(s0, rdhi); \ + generate_multiply_##name(a1, a2, s0) \ + +#define arm_multiply_long_add_no(name) \ + generate_multiply_##name(a1) \ + +#define arm_multiply_long(name, add_op, flags) \ +{ \ + arm_decode_multiply_long(); \ + generate_load_reg(a0, rm); \ + generate_load_reg(a1, rs); \ + arm_multiply_long_add_##add_op(name); \ + generate_store_reg(a0, rdlo); \ + generate_store_reg(a1, rdhi); \ + arm_multiply_long_flags_##flags(); \ +} \ + +u32 function_cc execute_read_cpsr() +{ + collapse_flags(); + return reg[REG_CPSR]; +} + +u32 function_cc execute_read_spsr() +{ + collapse_flags(); + return spsr[reg[CPU_MODE]]; +} + +#define arm_psr_read(op_type, psr_reg) \ + generate_function_call(execute_read_##psr_reg); \ + generate_store_reg(rv, rd) \ + +// store_mask and address are stored in the SAVE slots, since there's no real +// register space to nicely pass them. + +u32 function_cc execute_store_cpsr_body(u32 _cpsr) +{ + reg[REG_CPSR] = _cpsr; + if(reg[REG_SAVE] & 0xFF) + { + set_cpu_mode(cpu_modes[_cpsr & 0x1F]); + if((io_registers[REG_IE] & io_registers[REG_IF]) && + io_registers[REG_IME] && ((_cpsr & 0x80) == 0)) + { + reg_mode[MODE_IRQ][6] = reg[REG_SAVE2] + 4; + spsr[MODE_IRQ] = _cpsr; + reg[REG_CPSR] = (_cpsr & 0xFFFFFF00) | 0xD2; + set_cpu_mode(MODE_IRQ); + return 0x00000018; + } + } + + return 0; +} + + +void function_cc execute_store_spsr(u32 new_spsr, u32 store_mask) +{ + u32 _spsr = spsr[reg[CPU_MODE]]; + spsr[reg[CPU_MODE]] = (new_spsr & store_mask) | (_spsr & (~store_mask)); +} + +#define arm_psr_load_new_reg() \ + generate_load_reg(a0, rm) \ + +#define arm_psr_load_new_imm() \ + ror(imm, imm, imm_ror); \ + generate_load_imm(a0, imm) \ + +#define arm_psr_store(op_type, psr_reg) \ + arm_psr_load_new_##op_type(); \ + generate_load_imm(a1, psr_masks[psr_field]); \ + generate_load_pc(a2, (pc + 4)); \ + generate_function_call(execute_store_##psr_reg) \ + +#define arm_psr(op_type, transfer_type, psr_reg) \ +{ \ + arm_decode_psr_##op_type(); \ + arm_psr_##transfer_type(op_type, psr_reg); \ +} \ + +#define aligned_address_mask8 0xF0000000 +#define aligned_address_mask16 0xF0000001 +#define aligned_address_mask32 0xF0000003 + +#define read_memory(size, type, address, dest) \ +{ \ + u8 *map; \ + \ + if(((address >> 24) == 0) && (reg[REG_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 read_memory_s16(address, dest) \ +{ \ + u8 *map; \ + \ + if(((address >> 24) == 0) && (reg[REG_PC] >= 0x4000)) \ + { \ + dest = *((s16 *)((u8 *)&bios_read_protect + (address & 0x03))); \ + } \ + else \ + \ + 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 access_memory_generate_read_function(mem_size, mem_type) \ +u32 function_cc execute_load_##mem_type(u32 address) \ +{ \ + u32 dest; \ + read_memory(mem_size, mem_type, address, dest); \ + return dest; \ +} \ + +access_memory_generate_read_function(8, u8); +access_memory_generate_read_function(8, s8); +access_memory_generate_read_function(16, u16); +access_memory_generate_read_function(32, u32); + +u32 function_cc execute_load_s16(u32 address) +{ + u32 dest; + read_memory_s16(address, dest); + return dest; +} + +#define access_memory_generate_write_function(mem_size, mem_type) \ +void function_cc execute_store_##mem_type(u32 address, u32 source) \ +{ \ + u8 *map; \ + \ + if(((address & aligned_address_mask##mem_size) == 0) && \ + (map = memory_map_write[address >> 15])) \ + { \ + *((mem_type *)((u8 *)map + (address & 0x7FFF))) = source; \ + } \ + else \ + { \ + write_memory##mem_size(address, source); \ + } \ +} \ + +#define arm_access_memory_load(mem_type) \ + cycle_count += 2; \ + generate_function_call(execute_load_##mem_type); \ + generate_store_reg_pc_no_flags(rv, rd) \ + +#define arm_access_memory_store(mem_type) \ + cycle_count++; \ + generate_load_reg_pc(a1, rd, 12); \ + generate_load_pc(a2, (pc + 4)); \ + generate_function_call(execute_store_##mem_type) \ + +#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 load_reg_op reg[rd] \ + +#define store_reg_op reg_op \ + +#define arm_access_memory_adjust_op_up add +#define arm_access_memory_adjust_op_down sub +#define arm_access_memory_reverse_op_up sub +#define arm_access_memory_reverse_op_down add + +#define arm_access_memory_reg_pre(adjust_dir_op, reverse_dir_op) \ + generate_load_reg_pc(a0, rn, 8); \ + generate_##adjust_dir_op(a0, a1) \ + +#define arm_access_memory_reg_pre_wb(adjust_dir_op, reverse_dir_op) \ + arm_access_memory_reg_pre(adjust_dir_op, reverse_dir_op); \ + generate_store_reg(a0, rn) \ + +#define arm_access_memory_reg_post(adjust_dir_op, reverse_dir_op) \ + generate_load_reg(a0, rn); \ + generate_##adjust_dir_op(a0, a1); \ + generate_store_reg(a0, rn); \ + generate_##reverse_dir_op(a0, a1) \ + +#define arm_access_memory_imm_pre(adjust_dir_op, reverse_dir_op) \ + generate_load_reg_pc(a0, rn, 8); \ + generate_##adjust_dir_op##_imm(a0, offset) \ + +#define arm_access_memory_imm_pre_wb(adjust_dir_op, reverse_dir_op) \ + arm_access_memory_imm_pre(adjust_dir_op, reverse_dir_op); \ + generate_store_reg(a0, rn) \ + +#define arm_access_memory_imm_post(adjust_dir_op, reverse_dir_op) \ + generate_load_reg(a0, rn); \ + generate_##adjust_dir_op##_imm(a0, offset); \ + generate_store_reg(a0, rn); \ + generate_##reverse_dir_op##_imm(a0, offset) \ + + +#define arm_data_trans_reg(adjust_op, adjust_dir_op, reverse_dir_op) \ + arm_decode_data_trans_reg(); \ + generate_load_offset_sh(); \ + arm_access_memory_reg_##adjust_op(adjust_dir_op, reverse_dir_op) \ + +#define arm_data_trans_imm(adjust_op, adjust_dir_op, reverse_dir_op) \ + arm_decode_data_trans_imm(); \ + arm_access_memory_imm_##adjust_op(adjust_dir_op, reverse_dir_op) \ + +#define arm_data_trans_half_reg(adjust_op, adjust_dir_op, reverse_dir_op) \ + arm_decode_half_trans_r(); \ + generate_load_reg(a1, rm); \ + arm_access_memory_reg_##adjust_op(adjust_dir_op, reverse_dir_op) \ + +#define arm_data_trans_half_imm(adjust_op, adjust_dir_op, reverse_dir_op) \ + arm_decode_half_trans_of(); \ + arm_access_memory_imm_##adjust_op(adjust_dir_op, reverse_dir_op) \ + +#define arm_access_memory(access_type, direction, adjust_op, mem_type, \ + offset_type) \ +{ \ + arm_data_trans_##offset_type(adjust_op, \ + arm_access_memory_adjust_op_##direction, \ + arm_access_memory_reverse_op_##direction); \ + \ + arm_access_memory_##access_type(mem_type); \ +} \ + +#define word_bit_count(word) \ + (bit_count[word >> 8] + bit_count[word & 0xFF]) \ + +#define sprint_no(access_type, pre_op, post_op, wb) \ + +#define sprint_yes(access_type, pre_op, post_op, wb) \ + printf("sbit on %s %s %s %s\n", #access_type, #pre_op, #post_op, #wb) \ + +u32 function_cc execute_aligned_load32(u32 address) +{ + u8 *map; + if(!(address & 0xF0000000) && (map = memory_map_read[address >> 15])) + return address32(map, address & 0x7FFF); + else + return read_memory32(address); +} + +void function_cc execute_aligned_store32(u32 address, u32 source) +{ + u8 *map; + + if(!(address & 0xF0000000) && (map = memory_map_write[address >> 15])) + address32(map, address & 0x7FFF) = source; + else + write_memory32(address, source); +} + +#define arm_block_memory_load() \ + generate_function_call(execute_aligned_load32); \ + generate_store_reg(rv, i) \ + +#define arm_block_memory_store() \ + generate_load_reg_pc(a1, i, 8); \ + generate_function_call(execute_aligned_store32) \ + +#define arm_block_memory_final_load() \ + arm_block_memory_load() \ + +#define arm_block_memory_final_store() \ + generate_load_reg_pc(a1, i, 12); \ + generate_load_pc(a2, (pc + 4)); \ + generate_function_call(execute_store_u32) \ + +#define arm_block_memory_adjust_pc_store() \ + +#define arm_block_memory_adjust_pc_load() \ + if(reg_list & 0x8000) \ + { \ + generate_mov(a0, rv); \ + generate_indirect_branch_arm(); \ + } \ + +#define arm_block_memory_offset_down_a() \ + generate_add_imm(s0, -((word_bit_count(reg_list) * 4) - 4)) \ + +#define arm_block_memory_offset_down_b() \ + generate_add_imm(s0, -(word_bit_count(reg_list) * 4)) \ + +#define arm_block_memory_offset_no() \ + +#define arm_block_memory_offset_up() \ + generate_add_imm(s0, 4) \ + +#define arm_block_memory_writeback_down() \ + generate_load_reg(a0, rn) \ + generate_add_imm(a0, -(word_bit_count(reg_list) * 4)); \ + generate_store_reg(a0, rn) \ + +#define arm_block_memory_writeback_up() \ + generate_load_reg(a0, rn); \ + generate_add_imm(a0, (word_bit_count(reg_list) * 4)); \ + generate_store_reg(a0, rn) \ + +#define arm_block_memory_writeback_no() + +// Only emit writeback if the register is not in the list + +#define arm_block_memory_writeback_load(writeback_type) \ + if(!((reg_list >> rn) & 0x01)) \ + { \ + arm_block_memory_writeback_##writeback_type(); \ + } \ + +#define arm_block_memory_writeback_store(writeback_type) \ + arm_block_memory_writeback_##writeback_type() \ + +#define arm_block_memory(access_type, offset_type, writeback_type, s_bit) \ +{ \ + arm_decode_block_trans(); \ + u32 offset = 0; \ + u32 i; \ + \ + generate_load_reg(s0, rn); \ + arm_block_memory_offset_##offset_type(); \ + arm_block_memory_writeback_##access_type(writeback_type); \ + generate_and_imm(s0, ~0x03); \ + \ + for(i = 0; i < 16; i++) \ + { \ + if((reg_list >> i) & 0x01) \ + { \ + cycle_count++; \ + generate_add_reg_reg_imm(a0, s0, offset) \ + if(reg_list & ~((2 << i) - 1)) \ + { \ + arm_block_memory_##access_type(); \ + offset += 4; \ + } \ + else \ + { \ + arm_block_memory_final_##access_type(); \ + } \ + } \ + } \ + \ + arm_block_memory_adjust_pc_##access_type(); \ +} \ + +#define arm_swap(type) \ +{ \ + arm_decode_swap(); \ + cycle_count += 3; \ + generate_load_reg(a0, rn); \ + generate_function_call(execute_load_##type); \ + generate_mov(s0, rv); \ + generate_load_reg(a0, rn); \ + generate_load_reg(a1, rm); \ + generate_function_call(execute_store_##type); \ + generate_store_reg(s0, rd); \ +} \ + +#define thumb_rn_op_reg(_rn) \ + generate_load_reg(a0, _rn) \ + +#define thumb_rn_op_imm(_imm) \ + generate_load_imm(a0, _imm) \ + +// Types: add_sub, add_sub_imm, alu_op, imm +// Affects N/Z/C/V flags + +#define thumb_data_proc(type, name, rn_type, _rd, _rs, _rn) \ +{ \ + thumb_decode_##type(); \ + thumb_rn_op_##rn_type(_rn); \ + generate_load_reg(a1, _rs); \ + generate_function_call(execute_##name); \ + generate_store_reg(rv, _rd); \ +} \ + +#define thumb_data_proc_test(type, name, rn_type, _rs, _rn) \ +{ \ + thumb_decode_##type(); \ + thumb_rn_op_##rn_type(_rn); \ + generate_load_reg(a1, _rs); \ + generate_function_call(execute_##name); \ +} \ + +#define thumb_data_proc_unary(type, name, rn_type, _rd, _rn) \ +{ \ + thumb_decode_##type(); \ + thumb_rn_op_##rn_type(_rn); \ + generate_function_call(execute_##name); \ + generate_store_reg(rv, _rd); \ +} \ + +#define thumb_data_proc_mov(type, rn_type, _rd, _rn) \ +{ \ + thumb_decode_##type(); \ + thumb_rn_op_##rn_type(_rn); \ + generate_store_reg(a0, _rd); \ +} \ + +#define generate_store_reg_pc_thumb(ireg) \ + generate_store_reg(ireg, rd); \ + if(rd == 15) \ + { \ + generate_indirect_branch_cycle_update(thumb); \ + } \ + +#define thumb_data_proc_hi(name) \ +{ \ + thumb_decode_hireg_op(); \ + generate_load_reg_pc(a0, rs, 4); \ + generate_load_reg_pc(a1, rd, 4); \ + generate_function_call(execute_##name); \ + generate_store_reg_pc_thumb(rv); \ +} \ + +#define thumb_data_proc_test_hi(name) \ +{ \ + thumb_decode_hireg_op(); \ + generate_load_reg_pc(a0, rs, 4); \ + generate_load_reg_pc(a1, rd, 4); \ + generate_function_call(execute_##name); \ +} \ + +#define thumb_data_proc_unary_hi(name) \ +{ \ + thumb_decode_hireg_op(); \ + generate_load_reg_pc(a0, rn, 4); \ + generate_function_call(execute_##name); \ + generate_store_reg_pc_thumb(rv); \ +} \ + +#define thumb_data_proc_mov_hi() \ +{ \ + thumb_decode_hireg_op(); \ + generate_load_reg_pc(a0, rs, 4); \ + generate_store_reg_pc_thumb(a0); \ +} \ + +#define thumb_load_pc(_rd) \ +{ \ + thumb_decode_imm(); \ + generate_load_pc(a0, (((pc & ~2) + 4) + (imm * 4))); \ + generate_store_reg(a0, _rd); \ +} \ + +#define thumb_load_sp(_rd) \ +{ \ + thumb_decode_imm(); \ + generate_load_reg(a0, 13); \ + generate_add_imm(a0, (imm * 4)); \ + generate_store_reg(a0, _rd); \ +} \ + +#define thumb_adjust_sp_up() \ + generate_add_imm(a0, imm * 4) \ + +#define thumb_adjust_sp_down() \ + generate_sub_imm(a0, imm * 4) \ + + +#define thumb_adjust_sp(direction) \ +{ \ + thumb_decode_add_sp(); \ + generate_load_reg(a0, REG_SP); \ + thumb_adjust_sp_##direction(); \ + generate_store_reg(a0, REG_SP); \ +} \ + +// Decode types: shift, alu_op +// Operation types: lsl, lsr, asr, ror +// Affects N/Z/C flags + +u32 function_cc execute_lsl_reg_op(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + { + if(shift == 32) + reg[REG_C_FLAG] = value & 0x01; + else + reg[REG_C_FLAG] = 0; + + value = 0; + } + else + { + reg[REG_C_FLAG] = (value >> (32 - shift)) & 0x01; + value <<= shift; + } + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_lsr_reg_op(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + { + if(shift == 32) + reg[REG_C_FLAG] = (value >> 31) & 0x01; + else + reg[REG_C_FLAG] = 0; + + value = 0; + } + else + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + value >>= shift; + } + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_asr_reg_op(u32 value, u32 shift) +{ + if(shift != 0) + { + if(shift > 31) + { + value = (s32)value >> 31; + reg[REG_C_FLAG] = value & 0x01; + } + else + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + value = (s32)value >> shift; + } + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_ror_reg_op(u32 value, u32 shift) +{ + if(shift != 0) + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + ror(value, value, shift); + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_lsl_imm_op(u32 value, u32 shift) +{ + if(shift != 0) + { + reg[REG_C_FLAG] = (value >> (32 - shift)) & 0x01; + value <<= shift; + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_lsr_imm_op(u32 value, u32 shift) +{ + if(shift != 0) + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + value >>= shift; + } + else + { + reg[REG_C_FLAG] = value >> 31; + value = 0; + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_asr_imm_op(u32 value, u32 shift) +{ + if(shift != 0) + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + value = (s32)value >> shift; + } + else + { + value = (s32)value >> 31; + reg[REG_C_FLAG] = value & 0x01; + } + + calculate_flags_logic(value); + return value; +} + +u32 function_cc execute_ror_imm_op(u32 value, u32 shift) +{ + if(shift != 0) + { + reg[REG_C_FLAG] = (value >> (shift - 1)) & 0x01; + ror(value, value, shift); + } + else + { + u32 c_flag = reg[REG_C_FLAG]; + reg[REG_C_FLAG] = value & 0x01; + value = (value >> 1) | (c_flag << 31); + } + + calculate_flags_logic(value); + return value; +} + +#define generate_shift_load_operands_reg() \ + generate_load_reg(a0, rd); \ + generate_load_reg(a1, rs) \ + +#define generate_shift_load_operands_imm() \ + generate_load_reg(a0, rs); \ + generate_load_imm(a1, imm) \ + +#define thumb_shift(decode_type, op_type, value_type) \ +{ \ + thumb_decode_##decode_type(); \ + generate_shift_load_operands_##value_type(); \ + generate_function_call(execute_##op_type##_##value_type##_op); \ + generate_store_reg(rv, rd); \ +} \ + +// Operation types: imm, mem_reg, mem_imm + +#define thumb_access_memory_load(mem_type, reg_rd) \ + cycle_count += 2; \ + generate_function_call(execute_load_##mem_type); \ + generate_store_reg(rv, reg_rd) \ + +#define thumb_access_memory_store(mem_type, reg_rd) \ + cycle_count++; \ + generate_load_reg(a1, reg_rd); \ + generate_load_pc(a2, (pc + 2)); \ + generate_function_call(execute_store_##mem_type) \ + +#define thumb_access_memory_generate_address_pc_relative(offset, _rb, _ro) \ + generate_load_pc(a0, (offset)) \ + +#define thumb_access_memory_generate_address_reg_imm_sp(offset, _rb, _ro) \ + generate_load_reg(a0, _rb); \ + generate_add_imm(a0, (offset * 4)) \ + +#define thumb_access_memory_generate_address_reg_imm(offset, _rb, _ro) \ + generate_load_reg(a0, _rb); \ + generate_add_imm(a0, (offset)) \ + +#define thumb_access_memory_generate_address_reg_reg(offset, _rb, _ro) \ + generate_load_reg(a0, _rb); \ + generate_load_reg(a1, _ro); \ + generate_add(a0, a1) \ + +#define thumb_access_memory(access_type, op_type, _rd, _rb, _ro, \ + address_type, offset, mem_type) \ +{ \ + thumb_decode_##op_type(); \ + thumb_access_memory_generate_address_##address_type(offset, _rb, _ro); \ + thumb_access_memory_##access_type(mem_type, _rd); \ +} \ + +#define thumb_block_address_preadjust_up() \ + generate_add_imm(s0, (bit_count[reg_list] * 4)) \ + +#define thumb_block_address_preadjust_down() \ + generate_sub_imm(s0, (bit_count[reg_list] * 4)) \ + +#define thumb_block_address_preadjust_push_lr() \ + generate_sub_imm(s0, ((bit_count[reg_list] + 1) * 4)) \ + +#define thumb_block_address_preadjust_no() \ + +#define thumb_block_address_postadjust_no(base_reg) \ + generate_store_reg(s0, base_reg) \ + +#define thumb_block_address_postadjust_up(base_reg) \ + generate_add_reg_reg_imm(a0, s0, (bit_count[reg_list] * 4)); \ + generate_store_reg(a0, base_reg) \ + +#define thumb_block_address_postadjust_down(base_reg) \ + generate_mov(a0, s0); \ + generate_sub_imm(a0, (bit_count[reg_list] * 4)); \ + generate_store_reg(a0, base_reg) \ + +#define thumb_block_address_postadjust_pop_pc(base_reg) \ + generate_add_reg_reg_imm(a0, s0, ((bit_count[reg_list] + 1) * 4)); \ + generate_store_reg(a0, base_reg) \ + +#define thumb_block_address_postadjust_push_lr(base_reg) \ + generate_store_reg(s0, base_reg) \ + +#define thumb_block_memory_extra_no() \ + +#define thumb_block_memory_extra_up() \ + +#define thumb_block_memory_extra_down() \ + +#define thumb_block_memory_extra_pop_pc() \ + generate_add_reg_reg_imm(a0, s0, (bit_count[reg_list] * 4)); \ + generate_function_call(execute_aligned_load32); \ + generate_store_reg(rv, REG_PC); \ + generate_mov(a0, rv); \ + generate_indirect_branch_cycle_update(thumb) \ + +#define thumb_block_memory_extra_push_lr(base_reg) \ + generate_add_reg_reg_imm(a0, s0, (bit_count[reg_list] * 4)); \ + generate_load_reg(a1, REG_LR); \ + generate_function_call(execute_aligned_store32) \ + +#define thumb_block_memory_load() \ + generate_function_call(execute_aligned_load32); \ + generate_store_reg(rv, i) \ + +#define thumb_block_memory_store() \ + generate_load_reg(a1, i); \ + generate_function_call(execute_aligned_store32) \ + +#define thumb_block_memory_final_load() \ + thumb_block_memory_load() \ + +#define thumb_block_memory_final_store() \ + generate_load_reg(a1, i); \ + generate_load_pc(a2, (pc + 2)); \ + generate_function_call(execute_store_u32) \ + +#define thumb_block_memory_final_no(access_type) \ + thumb_block_memory_final_##access_type() \ + +#define thumb_block_memory_final_up(access_type) \ + thumb_block_memory_final_##access_type() \ + +#define thumb_block_memory_final_down(access_type) \ + thumb_block_memory_final_##access_type() \ + +#define thumb_block_memory_final_push_lr(access_type) \ + thumb_block_memory_##access_type() \ + +#define thumb_block_memory_final_pop_pc(access_type) \ + thumb_block_memory_##access_type() \ + +#define thumb_block_memory(access_type, pre_op, post_op, base_reg) \ +{ \ + thumb_decode_rlist(); \ + u32 i; \ + u32 offset = 0; \ + \ + generate_load_reg(s0, base_reg); \ + generate_and_imm(s0, ~0x03); \ + thumb_block_address_preadjust_##pre_op(); \ + thumb_block_address_postadjust_##post_op(base_reg); \ + \ + for(i = 0; i < 8; i++) \ + { \ + if((reg_list >> i) & 0x01) \ + { \ + cycle_count++; \ + generate_add_reg_reg_imm(a0, s0, offset) \ + if(reg_list & ~((2 << i) - 1)) \ + { \ + thumb_block_memory_##access_type(); \ + offset += 4; \ + } \ + else \ + { \ + thumb_block_memory_final_##post_op(access_type); \ + } \ + } \ + } \ + \ + thumb_block_memory_extra_##post_op(); \ +} \ + + +#define thumb_conditional_branch(condition) \ +{ \ + condition_check_type condition_check; \ + generate_cycle_update(); \ + generate_condition_##condition(a0, a1); \ + generate_conditional_branch_type(a0, a1); \ + generate_branch_no_cycle_update( \ + block_exits[block_exit_position].branch_source, \ + block_exits[block_exit_position].branch_target); \ + generate_branch_patch_conditional(backpatch_address, translation_ptr); \ + block_exit_position++; \ +} \ + +#define flags_vars(src_a, src_b) \ + u32 dest; \ + const u32 _sa = src_a; \ + const u32 _sb = src_b \ + +#define data_proc_generate_logic_function(name, expr) \ +u32 function_cc execute_##name(u32 rm, u32 rn) \ +{ \ + return expr; \ +} \ + \ +u32 function_cc execute_##name##s(u32 rm, u32 rn) \ +{ \ + u32 dest = expr; \ + calculate_z_flag(dest); \ + calculate_n_flag(dest); \ + return expr; \ +} \ + +#define data_proc_generate_logic_unary_function(name, expr) \ +u32 function_cc execute_##name(u32 rm) \ +{ \ + return expr; \ +} \ + \ +u32 function_cc execute_##name##s(u32 rm) \ +{ \ + u32 dest = expr; \ + calculate_z_flag(dest); \ + calculate_n_flag(dest); \ + return expr; \ +} \ + + +#define data_proc_generate_sub_function(name, src_a, src_b) \ +u32 function_cc execute_##name(u32 rm, u32 rn) \ +{ \ + return (src_a) - (src_b); \ +} \ + \ +u32 function_cc execute_##name##s(u32 rm, u32 rn) \ +{ \ + flags_vars(src_a, src_b); \ + dest = _sa - _sb; \ + calculate_flags_sub(dest, _sa, _sb); \ + return dest; \ +} \ + +#define data_proc_generate_add_function(name, src_a, src_b) \ +u32 function_cc execute_##name(u32 rm, u32 rn) \ +{ \ + return (src_a) + (src_b); \ +} \ + \ +u32 function_cc execute_##name##s(u32 rm, u32 rn) \ +{ \ + flags_vars(src_a, src_b); \ + dest = _sa + _sb; \ + calculate_flags_add(dest, _sa, _sb); \ + return dest; \ +} \ + +#define data_proc_generate_sub_test_function(name, src_a, src_b) \ +void function_cc execute_##name(u32 rm, u32 rn) \ +{ \ + flags_vars(src_a, src_b); \ + dest = _sa - _sb; \ + calculate_flags_sub(dest, _sa, _sb); \ +} \ + +#define data_proc_generate_add_test_function(name, src_a, src_b) \ +void function_cc execute_##name(u32 rm, u32 rn) \ +{ \ + flags_vars(src_a, src_b); \ + dest = _sa + _sb; \ + calculate_flags_add(dest, _sa, _sb); \ +} \ + +#define data_proc_generate_logic_test_function(name, expr) \ +void function_cc execute_##name(u32 rm, u32 rn) \ +{ \ + u32 dest = expr; \ + calculate_z_flag(dest); \ + calculate_n_flag(dest); \ +} \ + +u32 function_cc execute_neg(u32 rm) \ +{ \ + u32 dest = 0 - rm; \ + calculate_flags_sub(dest, 0, rm); \ + return dest; \ +} \ + +// Execute functions + +data_proc_generate_logic_function(and, rn & rm); +data_proc_generate_logic_function(eor, rn ^ rm); +data_proc_generate_logic_function(orr, rn | rm); +data_proc_generate_logic_function(bic, rn & (~rm)); +data_proc_generate_logic_function(mul, rn * rm); +data_proc_generate_logic_unary_function(mov, rm); +data_proc_generate_logic_unary_function(mvn, ~rm); + +data_proc_generate_sub_function(sub, rn, rm); +data_proc_generate_sub_function(rsb, rm, rn); +data_proc_generate_sub_function(sbc, rn, (rm + (reg[REG_C_FLAG] ^ 1))); +data_proc_generate_sub_function(rsc, (rm + reg[REG_C_FLAG] - 1), rn); +data_proc_generate_add_function(add, rn, rm); +data_proc_generate_add_function(adc, rn, rm + reg[REG_C_FLAG]); + +data_proc_generate_logic_test_function(tst, rn & rm); +data_proc_generate_logic_test_function(teq, rn ^ rm); +data_proc_generate_sub_test_function(cmp, rn, rm); +data_proc_generate_add_test_function(cmn, rn, rm); + +u32 function_cc execute_swi(u32 pc) +{ + reg_mode[MODE_SUPERVISOR][6] = pc; + collapse_flags(); + spsr[MODE_SUPERVISOR] = reg[REG_CPSR]; + reg[REG_CPSR] = (reg[REG_CPSR] & ~0x3F) | 0x13; + set_cpu_mode(MODE_SUPERVISOR); +} + +#define arm_conditional_block_header() \ +{ \ + condition_check_type condition_check; \ + generate_condition(a0, a1); \ + generate_conditional_branch_type(a0, a1); \ +} + +#define arm_b() \ + generate_branch() \ + +#define arm_bl() \ + generate_update_pc((pc + 4)); \ + generate_store_reg(a0, REG_LR); \ + generate_branch() \ + +#define arm_bx() \ + arm_decode_branchx(); \ + generate_load_reg(a0, rn); \ + generate_indirect_branch_dual(); \ + +#define arm_swi() \ + generate_swi_hle_handler((opcode >> 16) & 0xFF); \ + generate_update_pc((pc + 4)); \ + generate_function_call(execute_swi); \ + generate_branch() \ + +#define thumb_b() \ + generate_branch_cycle_update( \ + block_exits[block_exit_position].branch_source, \ + block_exits[block_exit_position].branch_target); \ + block_exit_position++ \ + +#define thumb_bl() \ + generate_update_pc(((pc + 2) | 0x01)); \ + generate_store_reg(a0, REG_LR); \ + generate_branch_cycle_update( \ + block_exits[block_exit_position].branch_source, \ + block_exits[block_exit_position].branch_target); \ + block_exit_position++ \ + +#define thumb_blh() \ +{ \ + thumb_decode_branch(); \ + generate_update_pc(((pc + 2) | 0x01)); \ + generate_load_reg(a1, REG_LR); \ + generate_store_reg(a0, REG_LR); \ + generate_mov(a0, a1); \ + generate_add_imm(a0, (offset * 2)); \ + generate_indirect_branch_cycle_update(thumb); \ +} \ + +#define thumb_bx() \ +{ \ + thumb_decode_hireg_op(); \ + generate_load_reg_pc(a0, rs, 4); \ + generate_indirect_branch_cycle_update(dual); \ +} \ + +#define thumb_swi() \ + generate_swi_hle_handler(opcode & 0xFF); \ + generate_update_pc((pc + 2)); \ + generate_function_call(execute_swi); \ + generate_branch_cycle_update( \ + block_exits[block_exit_position].branch_source, \ + block_exits[block_exit_position].branch_target); \ + block_exit_position++ \ + +u8 swi_hle_handle[256] = +{ + 0x0, // SWI 0: SoftReset + 0x0, // SWI 1: RegisterRAMReset + 0x0, // SWI 2: Halt + 0x0, // SWI 3: Stop/Sleep + 0x0, // SWI 4: IntrWait + 0x0, // SWI 5: VBlankIntrWait + 0x1, // SWI 6: Div + 0x0, // SWI 7: DivArm + 0x0, // SWI 8: Sqrt + 0x0, // SWI 9: ArcTan + 0x0, // SWI A: ArcTan2 + 0x0, // SWI B: CpuSet + 0x0, // SWI C: CpuFastSet + 0x0, // SWI D: GetBIOSCheckSum + 0x0, // SWI E: BgAffineSet + 0x0, // SWI F: ObjAffineSet + 0x0, // SWI 10: BitUnpack + 0x0, // SWI 11: LZ77UnCompWram + 0x0, // SWI 12: LZ77UnCompVram + 0x0, // SWI 13: HuffUnComp + 0x0, // SWI 14: RLUnCompWram + 0x0, // SWI 15: RLUnCompVram + 0x0, // SWI 16: Diff8bitUnFilterWram + 0x0, // SWI 17: Diff8bitUnFilterVram + 0x0, // SWI 18: Diff16bitUnFilter + 0x0, // SWI 19: SoundBias + 0x0, // SWI 1A: SoundDriverInit + 0x0, // SWI 1B: SoundDriverMode + 0x0, // SWI 1C: SoundDriverMain + 0x0, // SWI 1D: SoundDriverVSync + 0x0, // SWI 1E: SoundChannelClear + 0x0, // SWI 1F: MidiKey2Freq + 0x0, // SWI 20: SoundWhatever0 + 0x0, // SWI 21: SoundWhatever1 + 0x0, // SWI 22: SoundWhatever2 + 0x0, // SWI 23: SoundWhatever3 + 0x0, // SWI 24: SoundWhatever4 + 0x0, // SWI 25: MultiBoot + 0x0, // SWI 26: HardReset + 0x0, // SWI 27: CustomHalt + 0x0, // SWI 28: SoundDriverVSyncOff + 0x0, // SWI 29: SoundDriverVSyncOn + 0x0 // SWI 2A: SoundGetJumpList +}; + +void function_cc swi_hle_div() +{ + s32 result = (s32)reg[0] / (s32)reg[1]; + reg[1] = (s32)reg[0] % (s32)reg[1]; + reg[0] = result; + reg[3] = (result ^ (result >> 31)) - (result >> 31); +} + +#define generate_swi_hle_handler(_swi_number) \ +{ \ + u32 swi_number = _swi_number; \ + if(swi_hle_handle[swi_number]) \ + { \ + /* Div */ \ + if(swi_number == 0x06) \ + { \ + generate_function_call(swi_hle_div); \ + } \ + break; \ + } \ +} \ + +#define generate_translation_gate(type) \ + generate_update_pc(pc); \ + generate_indirect_branch_no_cycle_update(type) \ + +#define generate_step_debug() \ + generate_load_imm(a0, pc); \ + generate_function_call(step_debug_x86) \ + +#endif diff --git a/x86/x86_stub.S b/x86/x86_stub.S new file mode 100644 index 0000000..8fc16b7 --- /dev/null +++ b/x86/x86_stub.S @@ -0,0 +1,501 @@ +# 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 + +.align 4 + +.global _x86_update_gba +.global _x86_indirect_branch_arm +.global _x86_indirect_branch_thumb +.global _x86_indirect_branch_dual +.global _execute_store_u8 +.global _execute_store_u16 +.global _execute_store_u32 +.global _execute_store_cpsr +.global _execute_arm_translate +.global _step_debug_x86 + +.global _memory_map_read +.global _memory_map_write +.global _reg + +.global _oam_update + +.global _iwram +.global _ewram +.global _vram +.global _oam_ram +.global _bios_rom +.global _io_registers + +.extern _spsr + +.equ REG_SP, (13 * 4) +.equ REG_LR, (14 * 4) +.equ REG_PC, (15 * 4) +.equ REG_N_FLAG, (16 * 4) +.equ REG_Z_FLAG, (17 * 4) +.equ REG_C_FLAG, (18 * 4) +.equ REG_V_FLAG, (19 * 4) +.equ REG_CPSR, (20 * 4) +.equ REG_SAVE, (21 * 4) +.equ REG_SAVE2, (22 * 4) +.equ REG_SAVE3, (23 * 4) +.equ CPU_MODE, (29 * 4) +.equ CPU_HALT_STATE, (30 * 4) +.equ CHANGED_PC_STATUS, (31 * 4) + +# destroys ecx and edx + +.macro collapse_flag offset, shift + mov \offset(%ebx), %ecx + shl $\shift, %ecx + or %ecx, %edx +.endm + +.macro collapse_flags_no_update + xor %edx, %edx + collapse_flag REG_N_FLAG, 31 + collapse_flag REG_Z_FLAG, 30 + collapse_flag REG_C_FLAG, 29 + collapse_flag REG_V_FLAG, 28 + mov REG_CPSR(%ebx), %ecx + and $0xFF, %ecx + or %ecx, %edx +.endm + + +.macro collapse_flags + collapse_flags_no_update + mov %edx, REG_CPSR(%ebx) +.endm + +.macro extract_flag shift, offset + mov REG_CPSR(%ebx), %edx + shr $\shift, %edx + and $0x01, %edx + mov %edx, _reg + \offset +.endm + +.macro extract_flags + extract_flag 31, REG_N_FLAG + extract_flag 30, REG_Z_FLAG + extract_flag 29, REG_C_FLAG + extract_flag 28, REG_V_FLAG +.endm + +# Process a hardware event. Since an interrupt might be +# raised we have to check if the PC has changed. + +# eax: current address + +st: + .asciz "u\n" + +_x86_update_gba: + mov %eax, REG_PC(%ebx) # current PC = eax + collapse_flags # update cpsr, trashes ecx and edx + + call _update_gba # process the next event + + mov %eax, %edi # edi = new cycle count + # did the PC change? + cmpl $1, CHANGED_PC_STATUS(%ebx) + je lookup_pc + ret # if not, go back to caller + +# Perform this on an indirect branch that will definitely go to +# ARM code, IE anything that changes the PC in ARM mode except +# for BX and data processing to PC with the S bit set. + +# eax: GBA address to branch to +# edi: Cycle counter + +_x86_indirect_branch_arm: + call _block_lookup_address_arm + jmp *%eax + +# For indirect branches that'll definitely go to Thumb. In +# Thumb mode any indirect branches except for BX. + +_x86_indirect_branch_thumb: + call _block_lookup_address_thumb + jmp *%eax + +# For indirect branches that can go to either Thumb or ARM, +# mainly BX (also data processing to PC with S bit set, be +# sure to adjust the target with a 1 in the lowest bit for this) + +_x86_indirect_branch_dual: + call _block_lookup_address_dual + jmp *%eax + + +# General ext memory routines + +ext_store_ignore: + ret # ignore these writes + +write_epilogue: + cmp $0, %eax # 0 return means nothing happened + jz no_alert # if so we can leave + + collapse_flags # make sure flags are good for function call + cmp $2, %eax # see if it was an SMC trigger + je smc_write + +alert_loop: + call _update_gba # process the next event + + # see if the halt status has changed + mov CPU_HALT_STATE(%ebx), %edx + + cmp $0, %edx # 0 means it has + jnz alert_loop # if not go again + + mov %eax, %edi # edi = new cycle count + jmp lookup_pc # pc has definitely changed + +no_alert: + ret + +ext_store_eeprom: + jmp _write_eeprom # perform eeprom write + + +# 8bit ext memory routines + +ext_store_io8: + and $0x3FF, %eax # wrap around address + and $0xFF, %edx + call _write_io_register8 # perform 8bit I/O register write + jmp write_epilogue # see if it requires any system update + +ext_store_palette8: + and $0x3FE, %eax # wrap around address and align to 16bits + jmp ext_store_palette16b # perform 16bit palette write + +ext_store_vram8: + and $0x1FFFE, %eax # wrap around address and align to 16bits + mov %dl, %dh # copy lower 8bits of value into full 16bits + cmp $0x18000, %eax # see if address is in upper region + jb ext_store_vram8b + sub $0x8000, %eax # if so wrap down + +ext_store_vram8b: + mov %dx, _vram(%eax) # perform 16bit store + ret + +ext_store_oam8: + movl $1, _oam_update # flag OAM update + and $0x3FE, %eax # wrap around address and align to 16bits + mov %dl, %dh # copy lower 8bits of value into full 16bits + mov %dx, _oam_ram(%eax) # perform 16bit store + ret + +ext_store_backup: + and $0xFF, %edx # make value 8bit + and $0xFFFF, %eax # mask address + jmp _write_backup # perform backup write + +ext_store_u8_jtable: + .long ext_store_ignore # 0x00 BIOS, ignore + .long ext_store_ignore # 0x01 invalid, ignore + .long ext_store_ignore # 0x02 EWRAM, should have been hit already + .long ext_store_ignore # 0x03 IWRAM, should have been hit already + .long ext_store_io8 # 0x04 I/O registers + .long ext_store_palette8 # 0x05 Palette RAM + .long ext_store_vram8 # 0x06 VRAM + .long ext_store_oam8 # 0x07 OAM RAM + .long ext_store_ignore # 0x08 gamepak (no RTC accepted in 8bit) + .long ext_store_ignore # 0x09 gamepak, ignore + .long ext_store_ignore # 0x0A gamepak, ignore + .long ext_store_ignore # 0x0B gamepak, ignore + .long ext_store_ignore # 0x0C gamepak, ignore + .long ext_store_eeprom # 0x0D EEPROM (possibly) + .long ext_store_backup # 0x0E Flash ROM/SRAM + +ext_store_u8: + mov %eax, %ecx # ecx = address + shr $24, %ecx # ecx = address >> 24 + cmp $15, %ecx + ja ext_store_ignore + # ecx = ext_store_u8_jtable[address >> 24] + mov ext_store_u8_jtable(, %ecx, 4), %ecx + jmp *%ecx # jump to table index + +# eax: address to write to +# edx: value to write +# ecx: current pc + +_execute_store_u8: + mov %ecx, REG_PC(%ebx) # write out the PC + mov %eax, %ecx # ecx = address + test $0xF0000000, %ecx # check address range + jnz ext_store_u8 # if above perform an extended write + shr $15, %ecx # ecx = page number of address + # load the corresponding memory map offset + mov _memory_map_write(, %ecx, 4), %ecx + test %ecx, %ecx # see if it's NULL + jz ext_store_u8 # if so perform an extended write + and $0x7FFF, %eax # isolate the lower 15bits of the address + mov %dl, (%eax, %ecx) # store the value + # check for self-modifying code + testb $0xFF, -32768(%eax, %ecx) + jne smc_write + ret # return + +_execute_store_u16: + mov %ecx, REG_PC(%ebx) # write out the PC + and $~0x01, %eax # fix alignment + mov %eax, %ecx # ecx = address + test $0xF0000000, %ecx # check address range + jnz ext_store_u16 # if above perform an extended write + shr $15, %ecx # ecx = page number of address + # load the corresponding memory map offset + mov _memory_map_write(, %ecx, 4), %ecx + test %ecx, %ecx # see if it's NULL + jz ext_store_u16 # if so perform an extended write + and $0x7FFF, %eax # isolate the lower 15bits of the address + mov %dx, (%eax, %ecx) # store the value + # check for self-modifying code + testw $0xFFFF, -32768(%eax, %ecx) + jne smc_write + ret # return + +# 16bit ext memory routines + +ext_store_io16: + and $0x3FF, %eax # wrap around address + and $0xFFFF, %edx + call _write_io_register16 # perform 16bit I/O register write + jmp write_epilogue # see if it requires any system update + +ext_store_palette16: + and $0x3FF, %eax # wrap around address + +ext_store_palette16b: # entry point for 8bit write + mov %dx, _palette_ram(%eax) # write out palette value + mov %edx, %ecx # cx = dx + shl $11, %ecx # cx <<= 11 (red component is in high bits) + mov %dh, %cl # bottom bits of cx = top bits of dx + shr $2, %cl # move the blue component to the bottom of cl + and $0x03E0, %dx # isolate green component of dx + shl $1, %dx # make green component 6bits + or %edx, %ecx # combine green component into ecx + # write out the freshly converted palette value + mov %cx, _palette_ram_converted(%eax) + ret # done + +ext_store_vram16: + and $0x1FFFF, %eax # wrap around address + cmp $0x18000, %eax # see if address is in upper region + jb ext_store_vram16b + sub $0x8000, %eax # if so wrap down + +ext_store_vram16b: + mov %dx, _vram(%eax) # perform 16bit store + ret + +ext_store_oam16: + movl $1, _oam_update # flag OAM update + and $0x3FF, %eax # wrap around address + mov %dx, _oam_ram(%eax) # perform 16bit store + ret + +ext_store_rtc: + and $0xFFFF, %edx # make value 16bit + and $0xFF, %eax # mask address + jmp _write_rtc # write out RTC register + +ext_store_u16_jtable: + .long ext_store_ignore # 0x00 BIOS, ignore + .long ext_store_ignore # 0x01 invalid, ignore + .long ext_store_ignore # 0x02 EWRAM, should have been hit already + .long ext_store_ignore # 0x03 IWRAM, should have been hit already + .long ext_store_io16 # 0x04 I/O registers + .long ext_store_palette16 # 0x05 Palette RAM + .long ext_store_vram16 # 0x06 VRAM + .long ext_store_oam16 # 0x07 OAM RAM + .long ext_store_rtc # 0x08 gamepak or RTC + .long ext_store_ignore # 0x09 gamepak, ignore + .long ext_store_ignore # 0x0A gamepak, ignore + .long ext_store_ignore # 0x0B gamepak, ignore + .long ext_store_ignore # 0x0C gamepak, ignore + .long ext_store_eeprom # 0x0D EEPROM (possibly) + .long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit + +ext_store_u16: + mov %eax, %ecx # ecx = address + shr $24, %ecx # ecx = address >> 24 + cmp $15, %ecx + ja ext_store_ignore + # ecx = ext_store_u16_jtable[address >> 24] + mov ext_store_u16_jtable(, %ecx, 4), %ecx + jmp *%ecx # jump to table index + +_execute_store_u32: + mov %ecx, REG_PC(%ebx) # write out the PC + and $~0x03, %eax # fix alignment + mov %eax, %ecx # ecx = address + test $0xF0000000, %ecx # check address range + jnz ext_store_u32 # if above perform an extended write + shr $15, %ecx # ecx = page number of address + # load the corresponding memory map offset + mov _memory_map_write(, %ecx, 4), %ecx + test %ecx, %ecx # see if it's NULL + jz ext_store_u32 # if so perform an extended write + and $0x7FFF, %eax # isolate the lower 15bits of the address + mov %edx, (%eax, %ecx) # store the value + # check for self-modifying code + testl $0xFFFFFFFF, -32768(%eax, %ecx) + jne smc_write + ret # return it + +# 32bit ext memory routines + +ext_store_io32: + and $0x3FF, %eax # wrap around address + call _write_io_register32 # perform 32bit I/O register write + jmp write_epilogue # see if it requires any system update + +ext_store_palette32: + and $0x3FF, %eax # wrap around address + call ext_store_palette16b # write first 16bits + add $2, %eax # go to next address + shr $16, %edx # go to next 16bits + jmp ext_store_palette16b # write next 16bits + +ext_store_vram32: + and $0x1FFFF, %eax # wrap around address + cmp $0x18000, %eax # see if address is in upper region + jb ext_store_vram32b + sub $0x8000, %eax # if so wrap down + +ext_store_vram32b: + mov %edx, _vram(%eax) # perform 32bit store + ret + +ext_store_oam32: + movl $1, _oam_update # flag OAM update + and $0x3FF, %eax # wrap around address + mov %edx, _oam_ram(%eax) # perform 32bit store + ret + +ext_store_u32_jtable: + .long ext_store_ignore # 0x00 BIOS, ignore + .long ext_store_ignore # 0x01 invalid, ignore + .long ext_store_ignore # 0x02 EWRAM, should have been hit already + .long ext_store_ignore # 0x03 IWRAM, should have been hit already + .long ext_store_io32 # 0x04 I/O registers + .long ext_store_palette32 # 0x05 Palette RAM + .long ext_store_vram32 # 0x06 VRAM + .long ext_store_oam32 # 0x07 OAM RAM + .long ext_store_ignore # 0x08 gamepak, ignore (no RTC in 32bit) + .long ext_store_ignore # 0x09 gamepak, ignore + .long ext_store_ignore # 0x0A gamepak, ignore + .long ext_store_ignore # 0x0B gamepak, ignore + .long ext_store_ignore # 0x0C gamepak, ignore + .long ext_store_eeprom # 0x0D EEPROM (possibly) + .long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit + + +ext_store_u32: + mov %eax, %ecx # ecx = address + shr $24, %ecx # ecx = address >> 24 + cmp $15, %ecx + ja ext_store_ignore + # ecx = ext_store_u32_jtable[address >> 24] + mov ext_store_u32_jtable(, %ecx, 4), %ecx + jmp *%ecx + +# %eax = new_cpsr +# %edx = store_mask + +_execute_store_cpsr: + mov %edx, REG_SAVE(%ebx) # save store_mask + mov %ecx, REG_SAVE2(%ebx) # save PC too + + mov %eax, %ecx # ecx = new_cpsr + and %edx, %ecx # ecx = new_cpsr & store_mask + mov REG_CPSR(%ebx), %eax # eax = cpsr + not %edx # edx = ~store_mask + and %edx, %eax # eax = cpsr & ~store_mask + or %ecx, %eax # eax = new cpsr combined with old + + call _execute_store_cpsr_body # do the dirty work in this C function + + extract_flags # pull out flag vars from new CPSR + + cmp $0, %eax # see if return value is 0 + jnz changed_pc_cpsr # might have changed the PC + + ret # return + +changed_pc_cpsr: + add $4, %esp # get rid of current return address + call _block_lookup_address_arm # lookup new PC + jmp *%eax + +smc_write: + call _flush_translation_cache_ram + +lookup_pc: + add $4, %esp + movl $0, CHANGED_PC_STATUS(%ebx) + mov REG_PC(%ebx), %eax + testl $0x20, REG_CPSR(%ebx) + jz lookup_pc_arm + +lookup_pc_thumb: + call _block_lookup_address_thumb + jmp *%eax + +lookup_pc_arm: + call _block_lookup_address_arm + jmp *%eax + +# eax: cycle counter + +_execute_arm_translate: + movl $_reg, %ebx # load base register + extract_flags # load flag variables + movl %eax, %edi # load edi cycle counter + + movl REG_PC(%ebx), %eax # load PC + + testl $0x20, REG_CPSR(%ebx) + jnz 1f + + call _block_lookup_address_arm + jmp *%eax # jump to it + +1: + call _block_lookup_address_thumb + jmp *%eax + +_step_debug_x86: + collapse_flags +# mov $100, %edi + mov %edi, %edx + jmp _step_debug + +.comm _memory_map_read 0x8000 +.comm _memory_map_write 0x8000 +.comm _reg 0x100 + + |