mirror of
https://github.com/citra-emu/citra.git
synced 2025-07-01 04:00:20 +00:00
124 lines
5.1 KiB
C++
124 lines
5.1 KiB
C++
#include "arithmetic.h"
|
|
#include "disassembler.h"
|
|
#include <binary_translation/ARM_funcs.h>
|
|
#include <binary_translation/instruction_block.h>
|
|
|
|
static RegisterInstruction<Arithmetic> register_instruction;
|
|
|
|
bool IsSupported(Arithmetic::Op op)
|
|
{
|
|
switch (op)
|
|
{
|
|
case Arithmetic::Op::BitwiseAnd: return true;
|
|
case Arithmetic::Op::BitwiseXor: return true;
|
|
case Arithmetic::Op::Subtract: return true;
|
|
case Arithmetic::Op::RevSubtract: return true;
|
|
case Arithmetic::Op::Add: return true;
|
|
case Arithmetic::Op::AddWithCarry: return true;
|
|
case Arithmetic::Op::SubtractWithCarry: return true;
|
|
case Arithmetic::Op::ReverseSubtractWithCarry: return true;
|
|
case Arithmetic::Op::BitwiseOr: return true;
|
|
case Arithmetic::Op::MoveAndShifts: return false; // Handled in MovShift.cpp
|
|
case Arithmetic::Op::BitwiseBitClear: return true;
|
|
case Arithmetic::Op::BitwiseNot: return false; // MVN
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool IsBitwise(Arithmetic::Op op)
|
|
{
|
|
return op == Arithmetic::Op::BitwiseAnd ||
|
|
op == Arithmetic::Op::BitwiseXor ||
|
|
op == Arithmetic::Op::BitwiseOr ||
|
|
op == Arithmetic::Op::BitwiseBitClear;
|
|
}
|
|
|
|
bool Arithmetic::Decode()
|
|
{
|
|
if (ReadFields({ CondDef(), FieldDef<3>(0), FieldDef<4>(&op), FieldDef<1>(&set_flags), FieldDef<4>(&rn),
|
|
FieldDef<4>(&rd), FieldDef<5>(&imm5), FieldDef<2>(&type), FieldDef<1>(0), FieldDef<4>(&rm)}))
|
|
{
|
|
form = Form::Register;
|
|
if (rd == Register::PC && set_flags) return false; // SEE SUBS PC, LR and related instructions;
|
|
if (rn == Register::PC) return false;
|
|
if (rm == Register::PC) return false;
|
|
return IsSupported(op);
|
|
}
|
|
if (ReadFields({ CondDef(), FieldDef<3>(1), FieldDef<4>(&op), FieldDef<1>(&set_flags), FieldDef<4>(&rn),
|
|
FieldDef<4>(&rd), FieldDef<12>(&imm12) }))
|
|
{
|
|
form = Form::Immediate;
|
|
if (rd == Register::PC && set_flags) return false; // SEE SUBS PC, LR and related instructions;
|
|
if (rn == Register::PC) return false;
|
|
return IsSupported(op);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Arithmetic::GenerateInstructionCode(InstructionBlock* instruction_block)
|
|
{
|
|
auto ir_builder = instruction_block->Module()->IrBuilder();
|
|
|
|
llvm::Value *left, *right, *carry_in;
|
|
ARMFuncs::ResultCarryOverflow result = { nullptr, nullptr, nullptr };
|
|
auto bitwise = IsBitwise(op);
|
|
|
|
carry_in = instruction_block->Read(Register::C);
|
|
|
|
left = instruction_block->Read(rn);
|
|
|
|
if (form == Form::Register)
|
|
{
|
|
if (bitwise)
|
|
{
|
|
auto shift_tn = ARMFuncs::DecodeImmShift(instruction_block, type, imm5);
|
|
auto shifted_carry = ARMFuncs::Shift_C(instruction_block, instruction_block->Read(rm), shift_tn.type, shift_tn.amount, carry_in);
|
|
right = shifted_carry.result;
|
|
result.carry = shifted_carry.carry;
|
|
}
|
|
else
|
|
{
|
|
auto shift_tn = ARMFuncs::DecodeImmShift(instruction_block, type, imm5);
|
|
right = ARMFuncs::Shift(instruction_block, instruction_block->Read(rm), shift_tn.type, shift_tn.amount, carry_in);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bitwise)
|
|
{
|
|
auto imm32_carry = ARMFuncs::ARMExpandImm_C(instruction_block, imm12, carry_in);
|
|
right = imm32_carry.result;
|
|
result.carry = imm32_carry.carry;
|
|
}
|
|
else
|
|
{
|
|
right = ARMFuncs::ARMExpandImm(instruction_block, imm12);
|
|
}
|
|
}
|
|
|
|
switch (op)
|
|
{
|
|
case Op::BitwiseAnd: result.result = ir_builder->CreateAnd(left, right); break;
|
|
case Op::BitwiseXor: result.result = ir_builder->CreateXor(left, right); break;
|
|
case Op::Subtract: result = ARMFuncs::AddWithCarry(instruction_block, left, ir_builder->CreateNot(right), ir_builder->getInt32(1)); break;
|
|
case Op::RevSubtract: result = ARMFuncs::AddWithCarry(instruction_block, ir_builder->CreateNot(left), right, ir_builder->getInt32(1)); break;
|
|
case Op::Add: result = ARMFuncs::AddWithCarry(instruction_block, left, right, ir_builder->getInt32(0)); break;
|
|
case Op::AddWithCarry: result = ARMFuncs::AddWithCarry(instruction_block, left, right, carry_in); break;
|
|
case Op::SubtractWithCarry: result = ARMFuncs::AddWithCarry(instruction_block, left, ir_builder->CreateNot(right), carry_in); break;
|
|
case Op::ReverseSubtractWithCarry: result = ARMFuncs::AddWithCarry(instruction_block, ir_builder->CreateNot(left), right, carry_in); break;
|
|
case Op::BitwiseOr: result.result = ir_builder->CreateOr(left, right); break;
|
|
case Op::BitwiseBitClear: result.result = ir_builder->CreateAnd(left, ir_builder->CreateNot(right)); break;
|
|
default: break;
|
|
}
|
|
|
|
instruction_block->Write(rd, result.result);
|
|
|
|
if (set_flags)
|
|
{
|
|
instruction_block->Write(Register::N, ir_builder->CreateICmpSLT(result.result, ir_builder->getInt32(0)));
|
|
instruction_block->Write(Register::Z, ir_builder->CreateICmpEQ(result.result, ir_builder->getInt32(0)));
|
|
instruction_block->Write(Register::C, result.carry);
|
|
if (result.overflow)
|
|
instruction_block->Write(Register::V, result.overflow);
|
|
}
|
|
} |