#include "ArmAssembler.h" CArmAssembler::CArmAssembler( const WriteFunctionType& WriteFunction, const WriteAtFunctionType& WriteAtFunction, const TellFunctionType& TellFunction ) : m_WriteFunction(WriteFunction), m_WriteAtFunction(WriteAtFunction), m_TellFunction(TellFunction), m_nextLabelId(1) { } CArmAssembler::~CArmAssembler() { } CArmAssembler::LdrAddress CArmAssembler::MakeImmediateLdrAddress(uint32 immediate) { LdrAddress result; memset(&result, 0, sizeof(result)); assert((immediate & ~0xFFF) == 0); result.isImmediate = true; result.immediate = static_cast(immediate); return result; } CArmAssembler::ImmediateAluOperand CArmAssembler::MakeImmediateAluOperand(uint8 immediate, uint8 rotateAmount) { ImmediateAluOperand operand; operand.immediate = immediate; operand.rotate = rotateAmount; return operand; } CArmAssembler::RegisterAluOperand CArmAssembler::MakeRegisterAluOperand(CArmAssembler::REGISTER registerId, const AluLdrShift& shift) { RegisterAluOperand result; result.rm = registerId; result.shift = *reinterpret_cast(&shift); return result; } CArmAssembler::AluLdrShift CArmAssembler::MakeConstantShift(SHIFT shiftType, uint8 amount) { AluLdrShift result; result.typeBit = 0; result.type = shiftType; result.amount = amount; return result; } CArmAssembler::AluLdrShift CArmAssembler::MakeVariableShift(SHIFT shiftType, CArmAssembler::REGISTER registerId) { AluLdrShift result; result.typeBit = 1; result.type = shiftType; result.amount = registerId << 1; return result; } CArmAssembler::LABEL CArmAssembler::CreateLabel() { return m_nextLabelId++; } void CArmAssembler::MarkLabel(LABEL label) { m_labels[label] = m_TellFunction(); } void CArmAssembler::ResolveLabelReferences() { for(LabelReferenceMapType::iterator labelRef(m_labelReferences.begin()); m_labelReferences.end() != labelRef; labelRef++) { LabelMapType::iterator label(m_labels.find(labelRef->first)); if(label == m_labels.end()) { throw std::runtime_error("Invalid label."); } size_t referencePos = labelRef->second; size_t labelPos = label->second; int offset = static_cast(labelPos - referencePos) / 4; assert(offset >= 2); offset -= 2; m_WriteAtFunction(static_cast(referencePos + 0), static_cast(offset >> 0)); m_WriteAtFunction(static_cast(referencePos + 1), static_cast(offset >> 8)); m_WriteAtFunction(static_cast(referencePos + 2), static_cast(offset >> 16)); } m_labelReferences.clear(); } void CArmAssembler::CreateLabelReference(LABEL label) { LABELREF reference = m_TellFunction(); m_labelReferences.insert(LabelReferenceMapType::value_type(label, reference)); } void CArmAssembler::Add(REGISTER rd, REGISTER rn, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rn = rn; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_ADD; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Add(REGISTER rd, REGISTER rn, const ImmediateAluOperand& operand) { InstructionAlu instruction; instruction.operand = *reinterpret_cast(&operand); instruction.rd = rd; instruction.rn = rn; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_ADD; instruction.immediate = 1; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::And(REGISTER rd, REGISTER rn, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rn = rn; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_AND; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::BCc(CONDITION condition, LABEL label) { CreateLabelReference(label); uint32 opcode = (condition << 28) | (0x0A000000); WriteWord(opcode); } void CArmAssembler::Blx(REGISTER rn) { uint32 opcode = 0; opcode = (CONDITION_AL << 28) | (0x12FFF30) | (rn); WriteWord(opcode); } void CArmAssembler::Cmp(REGISTER rn, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rn = rn; instruction.rd = 0; instruction.setFlags = 1; instruction.opcode = ALU_OPCODE_CMP; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Eor(REGISTER rd, REGISTER rn, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rn = rn; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_EOR; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Ldr(REGISTER rd, REGISTER rbase, const LdrAddress& address) { uint32 opcode = 0; assert(address.isImmediate); opcode = (CONDITION_AL << 28) | (1 << 26) | (1 << 24) | (1 << 23) | (1 << 20) | (static_cast(rbase) << 16) | (static_cast(rd) << 12) | (static_cast(address.immediate)); WriteWord(opcode); } void CArmAssembler::Mov(REGISTER rd, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_MOV; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Mov(REGISTER rd, const RegisterAluOperand& operand) { InstructionAlu instruction; instruction.operand = *reinterpret_cast(&operand); instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_MOV; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Mov(REGISTER rd, const ImmediateAluOperand& operand) { MovCc(CONDITION_AL, rd, operand); } void CArmAssembler::MovCc(CONDITION condition, REGISTER rd, const ImmediateAluOperand& operand) { InstructionAlu instruction; instruction.operand = *reinterpret_cast(&operand); instruction.rd = rd; instruction.rn = 0; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_MOV; instruction.immediate = 1; instruction.condition = condition; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Mvn(REGISTER rd, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_MVN; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Or(REGISTER rd, REGISTER rn, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rn = rn; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_ORR; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Smull(REGISTER rdLow, REGISTER rdHigh, REGISTER rn, REGISTER rm) { uint32 opcode = 0; opcode = (CONDITION_AL << 28) | (0x06 << 21) | (rdHigh << 16) | (rdLow << 12) | (rm << 8) | (0x9 << 4) | (rn << 0); WriteWord(opcode); } void CArmAssembler::Str(REGISTER rd, REGISTER rbase, const LdrAddress& address) { uint32 opcode = 0; assert(address.isImmediate); opcode = (CONDITION_AL << 28) | (1 << 26) | (1 << 24) | (1 << 23) | (0 << 20) | (static_cast(rbase) << 16) | (static_cast(rd) << 12) | (static_cast(address.immediate)); WriteWord(opcode); } void CArmAssembler::Sub(REGISTER rd, REGISTER rn, REGISTER rm) { InstructionAlu instruction; instruction.operand = rm; instruction.rn = rn; instruction.rd = rd; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_SUB; instruction.immediate = 0; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Sub(REGISTER rd, REGISTER rn, const ImmediateAluOperand& operand) { InstructionAlu instruction; instruction.operand = *reinterpret_cast(&operand); instruction.rd = rd; instruction.rn = rn; instruction.setFlags = 0; instruction.opcode = ALU_OPCODE_SUB; instruction.immediate = 1; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::Teq(REGISTER rn, const ImmediateAluOperand& operand) { InstructionAlu instruction; instruction.operand = *reinterpret_cast(&operand); instruction.rd = 0; instruction.rn = rn; instruction.setFlags = 1; instruction.opcode = ALU_OPCODE_TEQ; instruction.immediate = 1; instruction.condition = CONDITION_AL; uint32 opcode = *reinterpret_cast(&instruction); WriteWord(opcode); } void CArmAssembler::WriteWord(uint32 value) { m_WriteFunction(reinterpret_cast(&value)[0]); m_WriteFunction(reinterpret_cast(&value)[1]); m_WriteFunction(reinterpret_cast(&value)[2]); m_WriteFunction(reinterpret_cast(&value)[3]); }