nkeynes@30: /** nkeynes@586: * $Id$ nkeynes@30: * nkeynes@30: * ARM7TDMI CPU emulation core. nkeynes@30: * nkeynes@30: * Copyright (c) 2005 Nathan Keynes. nkeynes@30: * nkeynes@30: * This program is free software; you can redistribute it and/or modify nkeynes@30: * it under the terms of the GNU General Public License as published by nkeynes@30: * the Free Software Foundation; either version 2 of the License, or nkeynes@30: * (at your option) any later version. nkeynes@30: * nkeynes@30: * This program is distributed in the hope that it will be useful, nkeynes@30: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@30: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@30: * GNU General Public License for more details. nkeynes@30: */ nkeynes@2: nkeynes@37: #define MODULE aica_module nkeynes@37: #include "dream.h" nkeynes@431: #include "dreamcast.h" nkeynes@73: #include "mem.h" nkeynes@7: #include "aica/armcore.h" nkeynes@73: #include "aica/aica.h" nkeynes@2: nkeynes@51: #define STM_R15_OFFSET 12 nkeynes@51: nkeynes@2: struct arm_registers armr; nkeynes@2: nkeynes@35: void arm_set_mode( int mode ); nkeynes@35: nkeynes@35: uint32_t arm_exceptions[][2] = {{ MODE_SVC, 0x00000000 }, nkeynes@35: { MODE_UND, 0x00000004 }, nkeynes@35: { MODE_SVC, 0x00000008 }, nkeynes@35: { MODE_ABT, 0x0000000C }, nkeynes@35: { MODE_ABT, 0x00000010 }, nkeynes@35: { MODE_IRQ, 0x00000018 }, nkeynes@35: { MODE_FIQ, 0x0000001C } }; nkeynes@35: nkeynes@35: #define EXC_RESET 0 nkeynes@35: #define EXC_UNDEFINED 1 nkeynes@35: #define EXC_SOFTWARE 2 nkeynes@35: #define EXC_PREFETCH_ABORT 3 nkeynes@35: #define EXC_DATA_ABORT 4 nkeynes@35: #define EXC_IRQ 5 nkeynes@35: #define EXC_FAST_IRQ 6 nkeynes@35: nkeynes@35: uint32_t arm_cpu_freq = ARM_BASE_RATE; nkeynes@35: uint32_t arm_cpu_period = 1000 / ARM_BASE_RATE; nkeynes@35: nkeynes@73: #define CYCLES_PER_SAMPLE ((ARM_BASE_RATE * 1000000) / AICA_SAMPLE_RATE) nkeynes@43: nkeynes@43: static struct breakpoint_struct arm_breakpoints[MAX_BREAKPOINTS]; nkeynes@43: static int arm_breakpoint_count = 0; nkeynes@43: nkeynes@586: void arm_set_breakpoint( uint32_t pc, breakpoint_type_t type ) nkeynes@43: { nkeynes@43: arm_breakpoints[arm_breakpoint_count].address = pc; nkeynes@43: arm_breakpoints[arm_breakpoint_count].type = type; nkeynes@43: arm_breakpoint_count++; nkeynes@43: } nkeynes@43: nkeynes@586: gboolean arm_clear_breakpoint( uint32_t pc, breakpoint_type_t type ) nkeynes@43: { nkeynes@43: int i; nkeynes@43: nkeynes@43: for( i=0; i= 8 || reg <= 12 ) nkeynes@66: return &armr.user_r[reg-8]; nkeynes@66: } nkeynes@66: } nkeynes@66: return &armr.r[reg]; nkeynes@66: } nkeynes@66: nkeynes@66: #define USER_R(n) *arm_user_reg(n) nkeynes@66: nkeynes@66: /** nkeynes@37: * Set the CPSR to the specified value. nkeynes@37: * nkeynes@37: * @param value values to set in CPSR nkeynes@37: * @param fields set of mask values to define which sections of the nkeynes@37: * CPSR to set (one of the SET_CPSR_* values above) nkeynes@37: */ nkeynes@37: void arm_set_cpsr( uint32_t value, uint32_t fields ) nkeynes@37: { nkeynes@37: if( IS_PRIVILEGED_MODE() ) { nkeynes@37: if( fields & SET_CPSR_CONTROL ) { nkeynes@37: int mode = value & CPSR_MODE; nkeynes@37: arm_set_mode( mode ); nkeynes@37: armr.t = ( value & CPSR_T ); /* Technically illegal to change */ nkeynes@37: armr.cpsr = (armr.cpsr & 0xFFFFFF00) | (value & 0x000000FF); nkeynes@37: } nkeynes@37: nkeynes@37: /* Middle 16 bits not currently defined */ nkeynes@37: } nkeynes@37: if( fields & SET_CPSR_FLAGS ) { nkeynes@37: /* Break flags directly out of given value - don't bother writing nkeynes@37: * back to CPSR nkeynes@37: */ nkeynes@37: armr.n = ( value & CPSR_N ); nkeynes@37: armr.z = ( value & CPSR_Z ); nkeynes@37: armr.c = ( value & CPSR_C ); nkeynes@37: armr.v = ( value & CPSR_V ); nkeynes@37: } nkeynes@37: } nkeynes@37: nkeynes@37: void arm_set_spsr( uint32_t value, uint32_t fields ) nkeynes@37: { nkeynes@37: /* Only defined if we actually have an SPSR register */ nkeynes@37: if( IS_EXCEPTION_MODE() ) { nkeynes@37: if( fields & SET_CPSR_CONTROL ) { nkeynes@37: armr.spsr = (armr.spsr & 0xFFFFFF00) | (value & 0x000000FF); nkeynes@37: } nkeynes@37: nkeynes@37: /* Middle 16 bits not currently defined */ nkeynes@37: nkeynes@37: if( fields & SET_CPSR_FLAGS ) { nkeynes@37: armr.spsr = (armr.spsr & 0x00FFFFFF) | (value & 0xFF000000); nkeynes@37: } nkeynes@37: } nkeynes@37: } nkeynes@37: nkeynes@35: /** nkeynes@35: * Raise an ARM exception (other than reset, which uses arm_reset(). nkeynes@35: * @param exception one of the EXC_* exception codes defined above. nkeynes@35: */ nkeynes@35: void arm_raise_exception( int exception ) nkeynes@35: { nkeynes@35: int mode = arm_exceptions[exception][0]; nkeynes@37: uint32_t spsr = arm_get_cpsr(); nkeynes@35: arm_set_mode( mode ); nkeynes@37: armr.spsr = spsr; nkeynes@63: armr.r[14] = armr.r[15] + 4; nkeynes@59: armr.cpsr = (spsr & 0xFFFFFF00) | mode | CPSR_I; nkeynes@35: if( mode == MODE_FIQ ) nkeynes@35: armr.cpsr |= CPSR_F; nkeynes@35: armr.r[15] = arm_exceptions[exception][1]; nkeynes@35: } nkeynes@35: nkeynes@37: void arm_restore_cpsr( void ) nkeynes@35: { nkeynes@35: int spsr = armr.spsr; nkeynes@35: int mode = spsr & CPSR_MODE; nkeynes@35: arm_set_mode( mode ); nkeynes@35: armr.cpsr = spsr; nkeynes@37: armr.n = ( spsr & CPSR_N ); nkeynes@37: armr.z = ( spsr & CPSR_Z ); nkeynes@37: armr.c = ( spsr & CPSR_C ); nkeynes@37: armr.v = ( spsr & CPSR_V ); nkeynes@37: armr.t = ( spsr & CPSR_T ); nkeynes@35: } nkeynes@35: nkeynes@35: nkeynes@35: nkeynes@35: /** nkeynes@35: * Change the current executing ARM mode to the requested mode. nkeynes@35: * Saves any required registers to banks and restores those for the nkeynes@35: * correct mode. (Note does not actually update CPSR at the moment). nkeynes@35: */ nkeynes@35: void arm_set_mode( int targetMode ) nkeynes@35: { nkeynes@35: int currentMode = armr.cpsr & CPSR_MODE; nkeynes@35: if( currentMode == targetMode ) nkeynes@35: return; nkeynes@35: nkeynes@35: switch( currentMode ) { nkeynes@35: case MODE_USER: nkeynes@35: case MODE_SYS: nkeynes@35: armr.user_r[5] = armr.r[13]; nkeynes@35: armr.user_r[6] = armr.r[14]; nkeynes@35: break; nkeynes@35: case MODE_SVC: nkeynes@35: armr.svc_r[0] = armr.r[13]; nkeynes@35: armr.svc_r[1] = armr.r[14]; nkeynes@35: armr.svc_r[2] = armr.spsr; nkeynes@35: break; nkeynes@35: case MODE_ABT: nkeynes@35: armr.abt_r[0] = armr.r[13]; nkeynes@35: armr.abt_r[1] = armr.r[14]; nkeynes@35: armr.abt_r[2] = armr.spsr; nkeynes@35: break; nkeynes@35: case MODE_UND: nkeynes@35: armr.und_r[0] = armr.r[13]; nkeynes@35: armr.und_r[1] = armr.r[14]; nkeynes@35: armr.und_r[2] = armr.spsr; nkeynes@35: break; nkeynes@35: case MODE_IRQ: nkeynes@35: armr.irq_r[0] = armr.r[13]; nkeynes@35: armr.irq_r[1] = armr.r[14]; nkeynes@35: armr.irq_r[2] = armr.spsr; nkeynes@35: break; nkeynes@35: case MODE_FIQ: nkeynes@35: armr.fiq_r[0] = armr.r[8]; nkeynes@35: armr.fiq_r[1] = armr.r[9]; nkeynes@35: armr.fiq_r[2] = armr.r[10]; nkeynes@35: armr.fiq_r[3] = armr.r[11]; nkeynes@35: armr.fiq_r[4] = armr.r[12]; nkeynes@35: armr.fiq_r[5] = armr.r[13]; nkeynes@35: armr.fiq_r[6] = armr.r[14]; nkeynes@35: armr.fiq_r[7] = armr.spsr; nkeynes@35: armr.r[8] = armr.user_r[0]; nkeynes@35: armr.r[9] = armr.user_r[1]; nkeynes@35: armr.r[10] = armr.user_r[2]; nkeynes@35: armr.r[11] = armr.user_r[3]; nkeynes@35: armr.r[12] = armr.user_r[4]; nkeynes@35: break; nkeynes@35: } nkeynes@35: nkeynes@35: switch( targetMode ) { nkeynes@35: case MODE_USER: nkeynes@35: case MODE_SYS: nkeynes@35: armr.r[13] = armr.user_r[5]; nkeynes@35: armr.r[14] = armr.user_r[6]; nkeynes@35: break; nkeynes@35: case MODE_SVC: nkeynes@35: armr.r[13] = armr.svc_r[0]; nkeynes@35: armr.r[14] = armr.svc_r[1]; nkeynes@35: armr.spsr = armr.svc_r[2]; nkeynes@35: break; nkeynes@35: case MODE_ABT: nkeynes@35: armr.r[13] = armr.abt_r[0]; nkeynes@35: armr.r[14] = armr.abt_r[1]; nkeynes@35: armr.spsr = armr.abt_r[2]; nkeynes@35: break; nkeynes@35: case MODE_UND: nkeynes@35: armr.r[13] = armr.und_r[0]; nkeynes@35: armr.r[14] = armr.und_r[1]; nkeynes@35: armr.spsr = armr.und_r[2]; nkeynes@35: break; nkeynes@35: case MODE_IRQ: nkeynes@35: armr.r[13] = armr.irq_r[0]; nkeynes@35: armr.r[14] = armr.irq_r[1]; nkeynes@35: armr.spsr = armr.irq_r[2]; nkeynes@35: break; nkeynes@35: case MODE_FIQ: nkeynes@35: armr.user_r[0] = armr.r[8]; nkeynes@35: armr.user_r[1] = armr.r[9]; nkeynes@35: armr.user_r[2] = armr.r[10]; nkeynes@35: armr.user_r[3] = armr.r[11]; nkeynes@35: armr.user_r[4] = armr.r[12]; nkeynes@35: armr.r[8] = armr.fiq_r[0]; nkeynes@35: armr.r[9] = armr.fiq_r[1]; nkeynes@35: armr.r[10] = armr.fiq_r[2]; nkeynes@35: armr.r[11] = armr.fiq_r[3]; nkeynes@35: armr.r[12] = armr.fiq_r[4]; nkeynes@35: armr.r[13] = armr.fiq_r[5]; nkeynes@35: armr.r[14] = armr.fiq_r[6]; nkeynes@35: armr.spsr = armr.fiq_r[7]; nkeynes@35: break; nkeynes@35: } nkeynes@35: } nkeynes@35: nkeynes@5: /* Page references are as per ARM DDI 0100E (June 2000) */ nkeynes@2: nkeynes@11: #define MEM_READ_BYTE( addr ) arm_read_byte(addr) nkeynes@11: #define MEM_READ_WORD( addr ) arm_read_word(addr) nkeynes@11: #define MEM_READ_LONG( addr ) arm_read_long(addr) nkeynes@11: #define MEM_WRITE_BYTE( addr, val ) arm_write_byte(addr, val) nkeynes@11: #define MEM_WRITE_WORD( addr, val ) arm_write_word(addr, val) nkeynes@11: #define MEM_WRITE_LONG( addr, val ) arm_write_long(addr, val) nkeynes@2: nkeynes@5: nkeynes@5: #define IS_NOTBORROW( result, op1, op2 ) (op2 > op1 ? 0 : 1) nkeynes@5: #define IS_CARRY( result, op1, op2 ) (result < op1 ? 1 : 0) nkeynes@5: #define IS_SUBOVERFLOW( result, op1, op2 ) (((op1^op2) & (result^op1)) >> 31) nkeynes@5: #define IS_ADDOVERFLOW( result, op1, op2 ) (((op1&op2) & (result^op1)) >> 31) nkeynes@5: nkeynes@7: #define PC armr.r[15] nkeynes@2: nkeynes@5: /* Instruction fields */ nkeynes@5: #define COND(ir) (ir>>28) nkeynes@5: #define GRP(ir) ((ir>>26)&0x03) nkeynes@5: #define OPCODE(ir) ((ir>>20)&0x1F) nkeynes@5: #define IFLAG(ir) (ir&0x02000000) nkeynes@5: #define SFLAG(ir) (ir&0x00100000) nkeynes@5: #define PFLAG(ir) (ir&0x01000000) nkeynes@5: #define UFLAG(ir) (ir&0x00800000) nkeynes@5: #define BFLAG(ir) (ir&0x00400000) nkeynes@46: #define WFLAG(ir) (ir&0x00200000) nkeynes@5: #define LFLAG(ir) SFLAG(ir) nkeynes@5: #define RN(ir) (armr.r[((ir>>16)&0x0F)] + (((ir>>16)&0x0F) == 0x0F ? 4 : 0)) nkeynes@37: #define RD(ir) (armr.r[((ir>>12)&0x0F)] + (((ir>>12)&0x0F) == 0x0F ? 4 : 0)) nkeynes@5: #define RDn(ir) ((ir>>12)&0x0F) nkeynes@37: #define RS(ir) (armr.r[((ir>>8)&0x0F)] + (((ir>>8)&0x0F) == 0x0F ? 4 : 0)) nkeynes@37: #define RM(ir) (armr.r[(ir&0x0F)] + (((ir&0x0F) == 0x0F ? 4 : 0)) ) nkeynes@5: #define LRN(ir) armr.r[((ir>>16)&0x0F)] nkeynes@5: #define LRD(ir) armr.r[((ir>>12)&0x0F)] nkeynes@5: #define LRS(ir) armr.r[((ir>>8)&0x0F)] nkeynes@5: #define LRM(ir) armr.r[(ir&0x0F)] nkeynes@5: nkeynes@5: #define IMM8(ir) (ir&0xFF) nkeynes@5: #define IMM12(ir) (ir&0xFFF) nkeynes@7: #define SHIFTIMM(ir) ((ir>>7)&0x1F) nkeynes@7: #define IMMROT(ir) ((ir>>7)&0x1E) nkeynes@37: #define ROTIMM12(ir) ROTATE_RIGHT_LONG(IMM8(ir),IMMROT(ir)) nkeynes@431: #define SIGNEXT24(n) (((n)&0x00800000) ? ((n)|0xFF000000) : ((n)&0x00FFFFFF)) nkeynes@5: #define SHIFT(ir) ((ir>>4)&0x07) nkeynes@5: #define DISP24(ir) ((ir&0x00FFFFFF)) nkeynes@37: #define UNDEF(ir) do{ arm_raise_exception( EXC_UNDEFINED ); return TRUE; } while(0) nkeynes@46: #define UNIMP(ir) do{ PC-=4; ERROR( "Halted on unimplemented instruction at %08x, opcode = %04x", PC, ir ); dreamcast_stop(); return FALSE; }while(0) nkeynes@7: nkeynes@37: /** nkeynes@37: * Determine the value of the shift-operand for a data processing instruction, nkeynes@37: * without determing a value for shift_C (optimized form for instructions that nkeynes@37: * don't require shift_C ). nkeynes@37: * @see s5.1 Addressing Mode 1 - Data-processing operands (p A5-2, 218) nkeynes@37: */ nkeynes@5: static uint32_t arm_get_shift_operand( uint32_t ir ) nkeynes@5: { nkeynes@5: uint32_t operand, tmp; nkeynes@5: if( IFLAG(ir) == 0 ) { nkeynes@5: operand = RM(ir); nkeynes@5: switch(SHIFT(ir)) { nkeynes@5: case 0: /* (Rm << imm) */ nkeynes@5: operand = operand << SHIFTIMM(ir); nkeynes@5: break; nkeynes@5: case 1: /* (Rm << Rs) */ nkeynes@5: tmp = RS(ir)&0xFF; nkeynes@5: if( tmp > 31 ) operand = 0; nkeynes@5: else operand = operand << tmp; nkeynes@5: break; nkeynes@5: case 2: /* (Rm >> imm) */ nkeynes@5: operand = operand >> SHIFTIMM(ir); nkeynes@5: break; nkeynes@5: case 3: /* (Rm >> Rs) */ nkeynes@5: tmp = RS(ir) & 0xFF; nkeynes@5: if( tmp > 31 ) operand = 0; nkeynes@5: else operand = operand >> ir; nkeynes@5: break; nkeynes@5: case 4: /* (Rm >>> imm) */ nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) operand = ((int32_t)operand) >> 31; nkeynes@5: else operand = ((int32_t)operand) >> tmp; nkeynes@5: break; nkeynes@5: case 5: /* (Rm >>> Rs) */ nkeynes@5: tmp = RS(ir) & 0xFF; nkeynes@5: if( tmp > 31 ) operand = ((int32_t)operand) >> 31; nkeynes@5: else operand = ((int32_t)operand) >> tmp; nkeynes@5: break; nkeynes@5: case 6: nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) /* RRX aka rotate with carry */ nkeynes@7: operand = (operand >> 1) | (armr.c<<31); nkeynes@5: else nkeynes@5: operand = ROTATE_RIGHT_LONG(operand,tmp); nkeynes@5: break; nkeynes@5: case 7: nkeynes@5: tmp = RS(ir)&0x1F; nkeynes@5: operand = ROTATE_RIGHT_LONG(operand,tmp); nkeynes@5: break; nkeynes@5: } nkeynes@5: } else { nkeynes@5: operand = IMM8(ir); nkeynes@5: tmp = IMMROT(ir); nkeynes@5: operand = ROTATE_RIGHT_LONG(operand, tmp); nkeynes@5: } nkeynes@5: return operand; nkeynes@5: } nkeynes@5: nkeynes@5: /** nkeynes@37: * Determine the value of the shift-operand for a data processing instruction, nkeynes@37: * and set armr.shift_c accordingly. nkeynes@37: * @see s5.1 Addressing Mode 1 - Data-processing operands (p A5-2, 218) nkeynes@5: */ nkeynes@5: static uint32_t arm_get_shift_operand_s( uint32_t ir ) nkeynes@5: { nkeynes@5: uint32_t operand, tmp; nkeynes@5: if( IFLAG(ir) == 0 ) { nkeynes@5: operand = RM(ir); nkeynes@5: switch(SHIFT(ir)) { nkeynes@5: case 0: /* (Rm << imm) */ nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) { /* Rm */ nkeynes@5: armr.shift_c = armr.c; nkeynes@5: } else { /* Rm << imm */ nkeynes@5: armr.shift_c = (operand >> (32-tmp)) & 0x01; nkeynes@5: operand = operand << tmp; nkeynes@5: } nkeynes@5: break; nkeynes@5: case 1: /* (Rm << Rs) */ nkeynes@5: tmp = RS(ir)&0xFF; nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = armr.c; nkeynes@5: } else { nkeynes@5: if( tmp <= 32 ) nkeynes@5: armr.shift_c = (operand >> (32-tmp)) & 0x01; nkeynes@5: else armr.shift_c = 0; nkeynes@5: if( tmp < 32 ) nkeynes@5: operand = operand << tmp; nkeynes@5: else operand = 0; nkeynes@5: } nkeynes@5: break; nkeynes@5: case 2: /* (Rm >> imm) */ nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = operand >> 31; nkeynes@5: operand = 0; nkeynes@5: } else { nkeynes@5: armr.shift_c = (operand >> (tmp-1)) & 0x01; nkeynes@5: operand = RM(ir) >> tmp; nkeynes@5: } nkeynes@5: break; nkeynes@5: case 3: /* (Rm >> Rs) */ nkeynes@5: tmp = RS(ir) & 0xFF; nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = armr.c; nkeynes@5: } else { nkeynes@5: if( tmp <= 32 ) nkeynes@5: armr.shift_c = (operand >> (tmp-1))&0x01; nkeynes@5: else armr.shift_c = 0; nkeynes@5: if( tmp < 32 ) nkeynes@5: operand = operand >> tmp; nkeynes@5: else operand = 0; nkeynes@5: } nkeynes@5: break; nkeynes@5: case 4: /* (Rm >>> imm) */ nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = operand >> 31; nkeynes@5: operand = -armr.shift_c; nkeynes@5: } else { nkeynes@5: armr.shift_c = (operand >> (tmp-1)) & 0x01; nkeynes@5: operand = ((int32_t)operand) >> tmp; nkeynes@5: } nkeynes@5: break; nkeynes@5: case 5: /* (Rm >>> Rs) */ nkeynes@5: tmp = RS(ir) & 0xFF; nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = armr.c; nkeynes@5: } else { nkeynes@5: if( tmp < 32 ) { nkeynes@5: armr.shift_c = (operand >> (tmp-1))&0x01; nkeynes@5: operand = ((int32_t)operand) >> tmp; nkeynes@5: } else { nkeynes@5: armr.shift_c = operand >> 31; nkeynes@5: operand = ((int32_t)operand) >> 31; nkeynes@5: } nkeynes@5: } nkeynes@5: break; nkeynes@5: case 6: nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) { /* RRX aka rotate with carry */ nkeynes@5: armr.shift_c = operand&0x01; nkeynes@7: operand = (operand >> 1) | (armr.c<<31); nkeynes@5: } else { nkeynes@5: armr.shift_c = operand>>(tmp-1); nkeynes@5: operand = ROTATE_RIGHT_LONG(operand,tmp); nkeynes@5: } nkeynes@5: break; nkeynes@5: case 7: nkeynes@5: tmp = RS(ir)&0xFF; nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = armr.c; nkeynes@5: } else { nkeynes@5: tmp &= 0x1F; nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = operand>>31; nkeynes@5: } else { nkeynes@5: armr.shift_c = (operand>>(tmp-1))&0x1; nkeynes@5: operand = ROTATE_RIGHT_LONG(operand,tmp); nkeynes@5: } nkeynes@5: } nkeynes@5: break; nkeynes@5: } nkeynes@5: } else { nkeynes@5: operand = IMM8(ir); nkeynes@5: tmp = IMMROT(ir); nkeynes@5: if( tmp == 0 ) { nkeynes@5: armr.shift_c = armr.c; nkeynes@5: } else { nkeynes@5: operand = ROTATE_RIGHT_LONG(operand, tmp); nkeynes@5: armr.shift_c = operand>>31; nkeynes@5: } nkeynes@5: } nkeynes@5: return operand; nkeynes@5: } nkeynes@5: nkeynes@5: /** nkeynes@5: * Another variant of the shifter code for index-based memory addressing. nkeynes@5: * Distinguished by the fact that it doesn't support register shifts, and nkeynes@5: * ignores the I flag (WTF do the load/store instructions use the I flag to nkeynes@5: * mean the _exact opposite_ of what it means for the data processing nkeynes@5: * instructions ???) nkeynes@5: */ nkeynes@5: static uint32_t arm_get_address_index( uint32_t ir ) nkeynes@5: { nkeynes@5: uint32_t operand = RM(ir); nkeynes@7: uint32_t tmp; nkeynes@7: nkeynes@5: switch(SHIFT(ir)) { nkeynes@5: case 0: /* (Rm << imm) */ nkeynes@5: operand = operand << SHIFTIMM(ir); nkeynes@5: break; nkeynes@5: case 2: /* (Rm >> imm) */ nkeynes@5: operand = operand >> SHIFTIMM(ir); nkeynes@5: break; nkeynes@5: case 4: /* (Rm >>> imm) */ nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) operand = ((int32_t)operand) >> 31; nkeynes@5: else operand = ((int32_t)operand) >> tmp; nkeynes@5: break; nkeynes@5: case 6: nkeynes@5: tmp = SHIFTIMM(ir); nkeynes@5: if( tmp == 0 ) /* RRX aka rotate with carry */ nkeynes@7: operand = (operand >> 1) | (armr.c<<31); nkeynes@5: else nkeynes@5: operand = ROTATE_RIGHT_LONG(operand,tmp); nkeynes@5: break; nkeynes@5: default: UNIMP(ir); nkeynes@5: } nkeynes@5: return operand; nkeynes@5: } nkeynes@5: nkeynes@37: /** nkeynes@37: * Determine the address operand of a load/store instruction, including nkeynes@37: * applying any pre/post adjustments to the address registers. nkeynes@37: * @see s5.2 Addressing Mode 2 - Load and Store Word or Unsigned Byte nkeynes@37: * @param The instruction word. nkeynes@37: * @return The calculated address nkeynes@37: */ nkeynes@5: static uint32_t arm_get_address_operand( uint32_t ir ) nkeynes@5: { nkeynes@431: uint32_t addr=0; nkeynes@5: nkeynes@5: /* I P U . W */ nkeynes@5: switch( (ir>>21)&0x1D ) { nkeynes@5: case 0: /* Rn -= imm offset (post-indexed) [5.2.8 A5-28] */ nkeynes@5: case 1: nkeynes@5: addr = RN(ir); nkeynes@7: LRN(ir) = addr - IMM12(ir); nkeynes@5: break; nkeynes@5: case 4: /* Rn += imm offsett (post-indexed) [5.2.8 A5-28] */ nkeynes@5: case 5: nkeynes@5: addr = RN(ir); nkeynes@7: LRN(ir) = addr + IMM12(ir); nkeynes@5: break; nkeynes@5: case 8: /* Rn - imm offset [5.2.2 A5-20] */ nkeynes@5: addr = RN(ir) - IMM12(ir); nkeynes@5: break; nkeynes@5: case 9: /* Rn -= imm offset (pre-indexed) [5.2.5 A5-24] */ nkeynes@5: addr = RN(ir) - IMM12(ir); nkeynes@7: LRN(ir) = addr; nkeynes@5: break; nkeynes@5: case 12: /* Rn + imm offset [5.2.2 A5-20] */ nkeynes@5: addr = RN(ir) + IMM12(ir); nkeynes@5: break; nkeynes@5: case 13: /* Rn += imm offset [5.2.5 A5-24 ] */ nkeynes@5: addr = RN(ir) + IMM12(ir); nkeynes@7: LRN(ir) = addr; nkeynes@5: break; nkeynes@5: case 16: /* Rn -= Rm (post-indexed) [5.2.10 A5-32 ] */ nkeynes@5: case 17: nkeynes@5: addr = RN(ir); nkeynes@7: LRN(ir) = addr - arm_get_address_index(ir); nkeynes@5: break; nkeynes@5: case 20: /* Rn += Rm (post-indexed) [5.2.10 A5-32 ] */ nkeynes@5: case 21: nkeynes@5: addr = RN(ir); nkeynes@7: LRN(ir) = addr - arm_get_address_index(ir); nkeynes@5: break; nkeynes@5: case 24: /* Rn - Rm [5.2.4 A5-23] */ nkeynes@5: addr = RN(ir) - arm_get_address_index(ir); nkeynes@5: break; nkeynes@5: case 25: /* RN -= Rm (pre-indexed) [5.2.7 A5-26] */ nkeynes@5: addr = RN(ir) - arm_get_address_index(ir); nkeynes@7: LRN(ir) = addr; nkeynes@5: break; nkeynes@5: case 28: /* Rn + Rm [5.2.4 A5-23] */ nkeynes@5: addr = RN(ir) + arm_get_address_index(ir); nkeynes@5: break; nkeynes@5: case 29: /* RN += Rm (pre-indexed) [5.2.7 A5-26] */ nkeynes@5: addr = RN(ir) + arm_get_address_index(ir); nkeynes@7: LRN(ir) = addr; nkeynes@5: break; nkeynes@5: } nkeynes@5: return addr; nkeynes@5: } nkeynes@5: nkeynes@30: gboolean arm_execute_instruction( void ) nkeynes@2: { nkeynes@59: uint32_t pc; nkeynes@59: uint32_t ir; nkeynes@49: uint32_t operand, operand2, tmp, tmp2, cond; nkeynes@66: int i; nkeynes@2: nkeynes@52: tmp = armr.int_pending & (~armr.cpsr); nkeynes@51: if( tmp ) { nkeynes@51: if( tmp & CPSR_F ) { nkeynes@51: arm_raise_exception( EXC_FAST_IRQ ); nkeynes@51: } else { nkeynes@51: arm_raise_exception( EXC_IRQ ); nkeynes@51: } nkeynes@51: } nkeynes@51: nkeynes@59: ir = MEM_READ_LONG(PC); nkeynes@59: pc = PC + 4; nkeynes@37: PC = pc; nkeynes@2: nkeynes@37: /** nkeynes@37: * Check the condition bits first - if the condition fails return nkeynes@37: * immediately without actually looking at the rest of the instruction. nkeynes@37: */ nkeynes@37: switch( COND(ir) ) { nkeynes@37: case 0: /* EQ */ nkeynes@37: cond = armr.z; nkeynes@37: break; nkeynes@37: case 1: /* NE */ nkeynes@37: cond = !armr.z; nkeynes@37: break; nkeynes@37: case 2: /* CS/HS */ nkeynes@37: cond = armr.c; nkeynes@37: break; nkeynes@37: case 3: /* CC/LO */ nkeynes@37: cond = !armr.c; nkeynes@37: break; nkeynes@37: case 4: /* MI */ nkeynes@37: cond = armr.n; nkeynes@37: break; nkeynes@37: case 5: /* PL */ nkeynes@37: cond = !armr.n; nkeynes@37: break; nkeynes@37: case 6: /* VS */ nkeynes@37: cond = armr.v; nkeynes@37: break; nkeynes@37: case 7: /* VC */ nkeynes@37: cond = !armr.v; nkeynes@37: break; nkeynes@37: case 8: /* HI */ nkeynes@37: cond = armr.c && !armr.z; nkeynes@37: break; nkeynes@37: case 9: /* LS */ nkeynes@37: cond = (!armr.c) || armr.z; nkeynes@37: break; nkeynes@37: case 10: /* GE */ nkeynes@37: cond = (armr.n == armr.v); nkeynes@37: break; nkeynes@37: case 11: /* LT */ nkeynes@37: cond = (armr.n != armr.v); nkeynes@37: break; nkeynes@37: case 12: /* GT */ nkeynes@37: cond = (!armr.z) && (armr.n == armr.v); nkeynes@37: break; nkeynes@37: case 13: /* LE */ nkeynes@37: cond = armr.z || (armr.n != armr.v); nkeynes@37: break; nkeynes@37: case 14: /* AL */ nkeynes@37: cond = 1; nkeynes@37: break; nkeynes@37: case 15: /* (NV) */ nkeynes@431: default: nkeynes@37: cond = 0; nkeynes@37: UNDEF(ir); nkeynes@37: } nkeynes@86: if( cond ) { nkeynes@5: nkeynes@37: /** nkeynes@37: * Condition passed, now for the actual instructions... nkeynes@37: */ nkeynes@37: switch( GRP(ir) ) { nkeynes@37: case 0: nkeynes@37: if( (ir & 0x0D900000) == 0x01000000 ) { nkeynes@37: /* Instructions that aren't actual data processing even though nkeynes@37: * they sit in the DP instruction block. nkeynes@37: */ nkeynes@37: switch( ir & 0x0FF000F0 ) { nkeynes@37: case 0x01200010: /* BX Rd */ nkeynes@37: armr.t = ir & 0x01; nkeynes@37: armr.r[15] = RM(ir) & 0xFFFFFFFE; nkeynes@37: break; nkeynes@37: case 0x01000000: /* MRS Rd, CPSR */ nkeynes@37: LRD(ir) = arm_get_cpsr(); nkeynes@37: break; nkeynes@37: case 0x01400000: /* MRS Rd, SPSR */ nkeynes@37: LRD(ir) = armr.spsr; nkeynes@37: break; nkeynes@37: case 0x01200000: /* MSR CPSR, Rd */ nkeynes@37: arm_set_cpsr( RM(ir), ir ); nkeynes@37: break; nkeynes@37: case 0x01600000: /* MSR SPSR, Rd */ nkeynes@37: arm_set_spsr( RM(ir), ir ); nkeynes@37: break; nkeynes@37: case 0x03200000: /* MSR CPSR, imm */ nkeynes@37: arm_set_cpsr( ROTIMM12(ir), ir ); nkeynes@37: break; nkeynes@37: case 0x03600000: /* MSR SPSR, imm */ nkeynes@37: arm_set_spsr( ROTIMM12(ir), ir ); nkeynes@37: break; nkeynes@37: default: nkeynes@37: UNIMP(ir); nkeynes@37: } nkeynes@37: } else if( (ir & 0x0E000090) == 0x00000090 ) { nkeynes@37: /* Neither are these */ nkeynes@37: switch( (ir>>5)&0x03 ) { nkeynes@37: case 0: nkeynes@37: /* Arithmetic extension area */ nkeynes@37: switch(OPCODE(ir)) { nkeynes@37: case 0: /* MUL */ nkeynes@51: LRN(ir) = RM(ir) * RS(ir); nkeynes@37: break; nkeynes@37: case 1: /* MULS */ nkeynes@51: tmp = RM(ir) * RS(ir); nkeynes@51: LRN(ir) = tmp; nkeynes@51: armr.n = tmp>>31; nkeynes@51: armr.z = (tmp == 0); nkeynes@37: break; nkeynes@37: case 2: /* MLA */ nkeynes@51: LRN(ir) = RM(ir) * RS(ir) + RD(ir); nkeynes@37: break; nkeynes@37: case 3: /* MLAS */ nkeynes@51: tmp = RM(ir) * RS(ir) + RD(ir); nkeynes@51: LRN(ir) = tmp; nkeynes@51: armr.n = tmp>>31; nkeynes@51: armr.z = (tmp == 0); nkeynes@37: break; nkeynes@37: case 8: /* UMULL */ nkeynes@37: case 9: /* UMULLS */ nkeynes@37: case 10: /* UMLAL */ nkeynes@37: case 11: /* UMLALS */ nkeynes@37: case 12: /* SMULL */ nkeynes@37: case 13: /* SMULLS */ nkeynes@37: case 14: /* SMLAL */ nkeynes@37: case 15: /* SMLALS */ nkeynes@63: UNIMP(ir); nkeynes@37: break; nkeynes@37: case 16: /* SWP */ nkeynes@51: tmp = arm_read_long( RN(ir) ); nkeynes@51: switch( RN(ir) & 0x03 ) { nkeynes@51: case 1: nkeynes@51: tmp = ROTATE_RIGHT_LONG(tmp, 8); nkeynes@51: break; nkeynes@51: case 2: nkeynes@51: tmp = ROTATE_RIGHT_LONG(tmp, 16); nkeynes@51: break; nkeynes@51: case 3: nkeynes@51: tmp = ROTATE_RIGHT_LONG(tmp, 24); nkeynes@51: break; nkeynes@51: } nkeynes@51: arm_write_long( RN(ir), RM(ir) ); nkeynes@51: LRD(ir) = tmp; nkeynes@37: break; nkeynes@37: case 20: /* SWPB */ nkeynes@51: tmp = arm_read_byte( RN(ir) ); nkeynes@51: arm_write_byte( RN(ir), RM(ir) ); nkeynes@51: LRD(ir) = tmp; nkeynes@37: break; nkeynes@37: default: nkeynes@37: UNIMP(ir); nkeynes@5: } nkeynes@5: break; nkeynes@37: case 1: nkeynes@37: if( LFLAG(ir) ) { nkeynes@37: /* LDRH */ nkeynes@37: } else { nkeynes@37: /* STRH */ nkeynes@37: } nkeynes@49: UNIMP(ir); nkeynes@5: break; nkeynes@37: case 2: nkeynes@37: if( LFLAG(ir) ) { nkeynes@37: /* LDRSB */ nkeynes@37: } else { nkeynes@37: } nkeynes@49: UNIMP(ir); nkeynes@5: break; nkeynes@37: case 3: nkeynes@37: if( LFLAG(ir) ) { nkeynes@37: /* LDRSH */ nkeynes@37: } else { nkeynes@37: } nkeynes@49: UNIMP(ir); nkeynes@5: break; nkeynes@37: } nkeynes@37: } else { nkeynes@37: /* Data processing */ nkeynes@37: nkeynes@37: switch(OPCODE(ir)) { nkeynes@37: case 0: /* AND Rd, Rn, operand */ nkeynes@37: LRD(ir) = RN(ir) & arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 1: /* ANDS Rd, Rn, operand */ nkeynes@37: operand = arm_get_shift_operand_s(ir) & RN(ir); nkeynes@37: LRD(ir) = operand; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: } nkeynes@37: break; nkeynes@37: case 2: /* EOR Rd, Rn, operand */ nkeynes@37: LRD(ir) = RN(ir) ^ arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 3: /* EORS Rd, Rn, operand */ nkeynes@37: operand = arm_get_shift_operand_s(ir) ^ RN(ir); nkeynes@37: LRD(ir) = operand; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: } nkeynes@37: break; nkeynes@37: case 4: /* SUB Rd, Rn, operand */ nkeynes@37: LRD(ir) = RN(ir) - arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 5: /* SUBS Rd, Rn, operand */ nkeynes@37: operand = RN(ir); nkeynes@37: operand2 = arm_get_shift_operand(ir); nkeynes@37: tmp = operand - operand2; nkeynes@37: LRD(ir) = tmp; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = tmp>>31; nkeynes@37: armr.z = (tmp == 0); nkeynes@37: armr.c = IS_NOTBORROW(tmp,operand,operand2); nkeynes@37: armr.v = IS_SUBOVERFLOW(tmp,operand,operand2); nkeynes@37: } nkeynes@37: break; nkeynes@37: case 6: /* RSB Rd, operand, Rn */ nkeynes@37: LRD(ir) = arm_get_shift_operand(ir) - RN(ir); nkeynes@37: break; nkeynes@37: case 7: /* RSBS Rd, operand, Rn */ nkeynes@37: operand = arm_get_shift_operand(ir); nkeynes@37: operand2 = RN(ir); nkeynes@37: tmp = operand - operand2; nkeynes@37: LRD(ir) = tmp; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = tmp>>31; nkeynes@37: armr.z = (tmp == 0); nkeynes@37: armr.c = IS_NOTBORROW(tmp,operand,operand2); nkeynes@37: armr.v = IS_SUBOVERFLOW(tmp,operand,operand2); nkeynes@37: } nkeynes@37: break; nkeynes@37: case 8: /* ADD Rd, Rn, operand */ nkeynes@37: LRD(ir) = RN(ir) + arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 9: /* ADDS Rd, Rn, operand */ nkeynes@37: operand = arm_get_shift_operand(ir); nkeynes@37: operand2 = RN(ir); nkeynes@37: tmp = operand + operand2; nkeynes@37: LRD(ir) = tmp; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = tmp>>31; nkeynes@37: armr.z = (tmp == 0); nkeynes@37: armr.c = IS_CARRY(tmp,operand,operand2); nkeynes@37: armr.v = IS_ADDOVERFLOW(tmp,operand,operand2); nkeynes@37: } nkeynes@37: break; nkeynes@37: case 10: /* ADC */ nkeynes@49: LRD(ir) = RN(ir) + arm_get_shift_operand(ir) + nkeynes@49: (armr.c ? 1 : 0); nkeynes@49: break; nkeynes@37: case 11: /* ADCS */ nkeynes@49: operand = arm_get_shift_operand(ir); nkeynes@49: operand2 = RN(ir); nkeynes@49: tmp = operand + operand2; nkeynes@49: tmp2 = tmp + armr.c ? 1 : 0; nkeynes@49: LRD(ir) = tmp2; nkeynes@49: if( RDn(ir) == 15 ) { nkeynes@49: arm_restore_cpsr(); nkeynes@49: } else { nkeynes@49: armr.n = tmp >> 31; nkeynes@49: armr.z = (tmp == 0 ); nkeynes@49: armr.c = IS_CARRY(tmp,operand,operand2) || nkeynes@49: (tmp2 < tmp); nkeynes@49: armr.v = IS_ADDOVERFLOW(tmp,operand, operand2) || nkeynes@49: ((tmp&0x80000000) != (tmp2&0x80000000)); nkeynes@49: } nkeynes@49: break; nkeynes@37: case 12: /* SBC */ nkeynes@49: LRD(ir) = RN(ir) - arm_get_shift_operand(ir) - nkeynes@49: (armr.c ? 0 : 1); nkeynes@49: break; nkeynes@37: case 13: /* SBCS */ nkeynes@49: operand = RN(ir); nkeynes@49: operand2 = arm_get_shift_operand(ir); nkeynes@49: tmp = operand - operand2; nkeynes@49: tmp2 = tmp - (armr.c ? 0 : 1); nkeynes@49: if( RDn(ir) == 15 ) { nkeynes@49: arm_restore_cpsr(); nkeynes@49: } else { nkeynes@49: armr.n = tmp >> 31; nkeynes@49: armr.z = (tmp == 0 ); nkeynes@49: armr.c = IS_NOTBORROW(tmp,operand,operand2) && nkeynes@49: (tmp2> 31; nkeynes@49: armr.z = (tmp == 0 ); nkeynes@49: armr.c = IS_NOTBORROW(tmp,operand,operand2) && nkeynes@49: (tmp2>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: break; nkeynes@37: case 19: /* TEQ Rn, operand */ nkeynes@37: operand = arm_get_shift_operand_s(ir) ^ RN(ir); nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: break; nkeynes@37: case 21: /* CMP Rn, operand */ nkeynes@37: operand = RN(ir); nkeynes@37: operand2 = arm_get_shift_operand(ir); nkeynes@37: tmp = operand - operand2; nkeynes@37: armr.n = tmp>>31; nkeynes@37: armr.z = (tmp == 0); nkeynes@37: armr.c = IS_NOTBORROW(tmp,operand,operand2); nkeynes@37: armr.v = IS_SUBOVERFLOW(tmp,operand,operand2); nkeynes@37: break; nkeynes@37: case 23: /* CMN Rn, operand */ nkeynes@37: operand = RN(ir); nkeynes@37: operand2 = arm_get_shift_operand(ir); nkeynes@37: tmp = operand + operand2; nkeynes@37: armr.n = tmp>>31; nkeynes@37: armr.z = (tmp == 0); nkeynes@37: armr.c = IS_CARRY(tmp,operand,operand2); nkeynes@37: armr.v = IS_ADDOVERFLOW(tmp,operand,operand2); nkeynes@37: break; nkeynes@37: case 24: /* ORR Rd, Rn, operand */ nkeynes@37: LRD(ir) = RN(ir) | arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 25: /* ORRS Rd, Rn, operand */ nkeynes@37: operand = arm_get_shift_operand_s(ir) | RN(ir); nkeynes@37: LRD(ir) = operand; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: } nkeynes@37: break; nkeynes@37: case 26: /* MOV Rd, operand */ nkeynes@37: LRD(ir) = arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 27: /* MOVS Rd, operand */ nkeynes@37: operand = arm_get_shift_operand_s(ir); nkeynes@37: LRD(ir) = operand; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: } nkeynes@37: break; nkeynes@37: case 28: /* BIC Rd, Rn, operand */ nkeynes@37: LRD(ir) = RN(ir) & (~arm_get_shift_operand(ir)); nkeynes@37: break; nkeynes@37: case 29: /* BICS Rd, Rn, operand */ nkeynes@37: operand = RN(ir) & (~arm_get_shift_operand_s(ir)); nkeynes@37: LRD(ir) = operand; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: } nkeynes@37: break; nkeynes@37: case 30: /* MVN Rd, operand */ nkeynes@37: LRD(ir) = ~arm_get_shift_operand(ir); nkeynes@37: break; nkeynes@37: case 31: /* MVNS Rd, operand */ nkeynes@37: operand = ~arm_get_shift_operand_s(ir); nkeynes@37: LRD(ir) = operand; nkeynes@37: if( RDn(ir) == 15 ) { nkeynes@37: arm_restore_cpsr(); nkeynes@37: } else { nkeynes@37: armr.n = operand>>31; nkeynes@37: armr.z = (operand == 0); nkeynes@37: armr.c = armr.shift_c; nkeynes@37: } nkeynes@37: break; nkeynes@37: default: nkeynes@37: UNIMP(ir); nkeynes@37: } nkeynes@5: } nkeynes@37: break; nkeynes@37: case 1: /* Load/store */ nkeynes@37: operand = arm_get_address_operand(ir); nkeynes@37: switch( (ir>>20)&0x17 ) { nkeynes@37: case 0: case 16: case 18: /* STR Rd, address */ nkeynes@37: arm_write_long( operand, RD(ir) ); nkeynes@37: break; nkeynes@37: case 1: case 17: case 19: /* LDR Rd, address */ nkeynes@37: LRD(ir) = arm_read_long(operand); nkeynes@37: break; nkeynes@37: case 2: /* STRT Rd, address */ nkeynes@37: arm_write_long_user( operand, RD(ir) ); nkeynes@37: break; nkeynes@37: case 3: /* LDRT Rd, address */ nkeynes@37: LRD(ir) = arm_read_long_user( operand ); nkeynes@37: break; nkeynes@37: case 4: case 20: case 22: /* STRB Rd, address */ nkeynes@37: arm_write_byte( operand, RD(ir) ); nkeynes@37: break; nkeynes@37: case 5: case 21: case 23: /* LDRB Rd, address */ nkeynes@37: LRD(ir) = arm_read_byte( operand ); nkeynes@37: break; nkeynes@37: case 6: /* STRBT Rd, address */ nkeynes@37: arm_write_byte_user( operand, RD(ir) ); nkeynes@37: break; nkeynes@37: case 7: /* LDRBT Rd, address */ nkeynes@37: LRD(ir) = arm_read_byte_user( operand ); nkeynes@37: break; nkeynes@37: } nkeynes@37: break; nkeynes@37: case 2: /* Load/store multiple, branch*/ nkeynes@37: if( (ir & 0x02000000) == 0x02000000 ) { /* B[L] imm24 */ nkeynes@37: operand = (SIGNEXT24(ir&0x00FFFFFF) << 2); nkeynes@37: if( (ir & 0x01000000) == 0x01000000 ) { nkeynes@37: armr.r[14] = pc; /* BL */ nkeynes@37: } nkeynes@37: armr.r[15] = pc + 4 + operand; nkeynes@37: } else { /* Load/store multiple */ nkeynes@81: gboolean needRestore = FALSE; nkeynes@46: operand = RN(ir); nkeynes@66: nkeynes@66: switch( (ir & 0x01D00000) >> 20 ) { nkeynes@66: case 0: /* STMDA */ nkeynes@81: if( ir & 0x8000 ) { nkeynes@81: arm_write_long( operand, armr.r[15]+4 ); nkeynes@81: operand -= 4; nkeynes@81: } nkeynes@81: for( i=14; i>= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0; i-- ) { nkeynes@66: if( (ir & (1<= 0x00200000 ) { nkeynes@86: armr.running = FALSE; nkeynes@536: WARN( "ARM Halted: BRANCH to invalid address %08X at %08X", armr.r[15], pc ); nkeynes@81: return FALSE; nkeynes@81: } nkeynes@37: return TRUE; nkeynes@2: }