internal void DataOpHandleSetFlagsAWC_NonCMP(uint opcode, uint Rd, AWCResult awcResult) { if (Rd == GeneralPurposeRegisters.PCRegisterIndex) { CPSR.RestoreCPUMode(); } else { DataOpHandleSetFlagsAWC_CMP(opcode, Rd, awcResult); } }
internal void DataOpHandleSetFlagsLogical_NonCMP(uint opcode, uint Rd, uint result, bool shift_carry) { if (Rd == GeneralPurposeRegisters.PCRegisterIndex) { CPSR.RestoreCPUMode(); } else { DataOpHandleSetFlagsLogical_CMP(opcode, Rd, result, shift_carry); } }
/*----------------------------------------------------------------------------*/ //---------------------------------------------------------------------------- internal uint normal_data_op(uint op_code, uint operation) { //extract the S bit value from the opcode bool SBit = ((op_code & s_mask) != 0); //extract destination register from opcode uint Rd = ((op_code & rd_mask) >> 12); //extract operand register and get first operand uint Rn = ((op_code & rn_mask) >> 16); uint a = get_reg(Rn); bool shift_carry = false; uint b; if ((op_code & imm_mask) == 0) { b = b_reg(op_code & op2_mask, ref shift_carry); } else { b = b_immediate(op_code & op2_mask, ref shift_carry); } uint rd = 0; switch (operation) /* R15s @@?! */ { case 0x0: rd = a & b; break; //AND case 0x1: rd = a ^ b; break; //EOR case 0x2: rd = a - b; break; //SUB case 0x3: rd = b - a; break; //RSB case 0x4: rd = a + b; break; //ADD case 0x5: rd = a + b; //ADC if (CPSR.cf) { ++rd; } break; case 0x6: rd = a - b - 1; //SBC if (CPSR.cf) { ++rd; } break; case 0x7: rd = b - a - 1; //RSC if (CPSR.cf) { ++rd; } break; case 0x8: rd = a & b; break; //TST case 0x9: rd = a ^ b; break; //TEQ case 0xa: rd = a - b; break; //CMP case 0xb: rd = a + b; break; //CMN case 0xc: rd = a | b; break; //ORR case 0xd: rd = b; break; //MOV case 0xe: rd = a & ~b; break; //BIC case 0xf: rd = ~b; break; //MVN }//switch if ((operation & 0xc) != 0x8) //Return result unless a compare { //write result into destination register mGPR[Rd] = rd; //if S bit is set and if the destination register is r15(pc) then this instruction has //special meaning. We are returning from a non-user mode to user-mode. //ie movs pc,r14 if (SBit && (Rd == GeneralPurposeRegisters.PCRegisterIndex)) { CPSR.RestoreCPUMode(); return(1); } //if } //if //if S bit is not set, we do not need to set any cpu flags, so we are done here if (!SBit) { return(1); } switch (operation) { //LOGICALs case 0x0: //AND case 0x1: //EOR case 0x8: //TST case 0x9: //TEQ case 0xc: //ORR case 0xd: //MOV case 0xe: //BIC case 0xf: //MVN CPSR.set_NZ(rd); CPSR.cf = shift_carry; break; case 0x2: //SUB case 0xa: //CMP CPSR.set_flags_sub(a, b, rd, true); break; case 0x6: //SBC CPSR.set_flags_sub(a, b, rd, CPSR.cf); break; case 0x3: //RSB CPSR.set_flags_sub(b, a, rd, true); break; case 0x7: //RSC CPSR.set_flags_sub(b, a, rd, CPSR.cf); break; case 0x4: //ADD case 0xb: //CMN CPSR.set_flags_add(a, b, rd, false); break; case 0x5: //ADC CPSR.set_flags_add(a, b, rd, CPSR.cf); break; default: break; } //switch return(1); } //normal_data_op
} //stm /// <summary> /// LDM - perform a multi-register load from memory /// </summary> /// <returns>Number of clock cycles executed</returns> internal uint ldm(uint opcode) { uint mode = (opcode & 0x01800000) >> 23; //isolate PU bits uint Rn = (opcode & rn_mask) >> 16; //get base register uint reg_list = opcode & 0x0000ffff; //get the register list bool writeback = (opcode & write_back_mask) != 0; //determine writeback flag bool userModeLoad = ((opcode & 0x00400000) != 0); //get user mode load flag uint address = Utils.valid_address(get_reg(Rn)); //make sure it is word aligned. int first_reg; uint count = bit_count(reg_list, out first_reg); bool includesPC = ((reg_list & 0x00008000) != 0); uint new_base; switch (mode) { case 0: new_base = address - 4 * count; address = new_base + 4; break; //DA (Decrement After) case 1: new_base = address + 4 * count; break; //IA (Increment After) case 2: new_base = address - 4 * count; address = new_base; break; //DB (Decrement Before) case 3: new_base = address + 4 * count; address = address + 4; break; //IB (Increment Before) default: return(0); } //switch if (writeback) { mGPR[Rn] = new_base; } uint reg = 0; uint extraCycles = 0; //check while (reg_list != 0) { if (Utils.lsb(reg_list)) { uint data = this.GetMemory(address, ARMPluginInterfaces.MemorySize.Word); if (reg == GeneralPurposeRegisters.PCRegisterIndex) { //We are loading a new value into the PC, check if entering Thumb or ARM mode extraCycles = 2; //if the user mode flag is set and r15 is being loaded, we are executing //LDM(3) and returning from an exception. We may be returning to Thumb mode or //ARM mode. In each case, force align the returning address. if (userModeLoad) { //we are returning from an exception, check if returning to ARM or Thumb mode if ((CPSR.SPSR & 0x0020) != 0) //check thumb bit of saved CPSR { data = data & 0xfffffffe; //returning to thumb mode, force align HWord } else { data = data & 0xfffffffc; //returning to arm mode, force align Word } } else if ((data & 0x01) != 0) { //Entering Thumb mode. Make sure destination address is HWord aligned CPSR.tf = true; data = data & 0xfffffffe; } //if else { //Entering ARM mode. Make sure destination address is Word aligned CPSR.tf = false; data = data & 0xfffffffc; } //else } //if if (userModeLoad & !includesPC) { mGPR.SetUserModeRegister(reg, data); } else { mGPR[reg] = data; } address += 4; } //if reg_list >>= 1; ++reg; } //while //if the PC was loaded during this instruction and the user mode load was specified //then we are returning from an exception. if (includesPC && userModeLoad) { CPSR.RestoreCPUMode(); } return(2 + count + extraCycles); } //ldm