A64: Implement addsub instructions

This commit is contained in:
MerryMage
2018-01-07 11:31:20 +00:00
parent d1cef6ffb0
commit c09e69bb97
15 changed files with 796 additions and 84 deletions

View File

@@ -9,6 +9,114 @@
namespace Dynarmic {
namespace A64 {
bool TranslatorVisitor::ADD_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto result = ir.Add(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADDS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto result = ir.Add(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto result = ir.Sub(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUBS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto result = ir.Sub(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
@@ -27,5 +135,199 @@ bool TranslatorVisitor::ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Re
return true;
}
bool TranslatorVisitor::ADDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
if (shift == 0b11) return ReservedValue();
if (!sf && imm6.Bit<5>()) return ReservedValue();
u8 shift_amount = imm6.ZeroExtend<u8>();
auto operand1 = X(datasize, Rn);
auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
auto result = ir.Add(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
if (shift == 0b11) return ReservedValue();
if (!sf && imm6.Bit<5>()) return ReservedValue();
u8 shift_amount = imm6.ZeroExtend<u8>();
auto operand1 = X(datasize, Rn);
auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
auto result = ir.Sub(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUBS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
if (shift == 0b11) return ReservedValue();
if (!sf && imm6.Bit<5>()) return ReservedValue();
u8 shift_amount = imm6.ZeroExtend<u8>();
auto operand1 = X(datasize, Rn);
auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
auto result = ir.Sub(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADD_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) return ReservedValue();
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto operand2 = ExtendReg(datasize, Rm, option, shift);
auto result = ir.Add(operand1, operand2);
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADDS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) return ReservedValue();
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto operand2 = ExtendReg(datasize, Rm, option, shift);
auto result = ir.Add(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUB_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) return ReservedValue();
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto operand2 = ExtendReg(datasize, Rm, option, shift);
auto result = ir.Sub(operand1, operand2);
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUBS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) return ReservedValue();
auto operand1 = Rn == Reg::SP ? SP(datasize) : X(datasize, Rn);
auto operand2 = ExtendReg(datasize, Rm, option, shift);
auto result = ir.Sub(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
auto operand1 = X(datasize, Rn);
auto operand2 = X(datasize, Rm);
auto result = ir.AddWithCarry(operand1, operand2, ir.GetCFlag());
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
auto operand1 = X(datasize, Rn);
auto operand2 = X(datasize, Rm);
auto result = ir.AddWithCarry(operand1, operand2, ir.GetCFlag());
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SBC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
auto operand1 = X(datasize, Rn);
auto operand2 = X(datasize, Rm);
auto result = ir.SubWithCarry(operand1, operand2, ir.GetCFlag());
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SBCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
auto operand1 = X(datasize, Rn);
auto operand2 = X(datasize, Rm);
auto result = ir.SubWithCarry(operand1, operand2, ir.GetCFlag());
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
} // namespace A64
} // namespace Dynarmic

View File

@@ -25,6 +25,18 @@ bool TranslatorVisitor::ReservedValue() {
return false;
}
IR::U32U64 TranslatorVisitor::I(size_t bitsize, u64 value) {
switch (bitsize) {
case 32:
return ir.Imm32(static_cast<u32>(value));
case 64:
return ir.Imm64(value);
default:
ASSERT_MSG(false, "Imm - get: Invalid bitsize");
return {};
}
}
IR::U32U64 TranslatorVisitor::X(size_t bitsize, Reg reg) {
switch (bitsize) {
case 32:
@@ -50,6 +62,31 @@ void TranslatorVisitor::X(size_t bitsize, Reg reg, IR::U32U64 value) {
}
}
IR::U32U64 TranslatorVisitor::SP(size_t bitsize) {
switch (bitsize) {
case 32:
return ir.LeastSignificantWord(ir.GetSP());
case 64:
return ir.GetSP();
default:
ASSERT_MSG(false, "SP - get : Invalid bitsize");
return {};
}
}
void TranslatorVisitor::SP(size_t bitsize, IR::U32U64 value) {
switch (bitsize) {
case 32:
ir.SetSP(ir.ZeroExtendWordToLong(value));
break;
case 64:
ir.SetSP(value);
break;
default:
ASSERT_MSG(false, "SP - : Invalid bitsize");
}
}
IR::U32U64 TranslatorVisitor::ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount) {
auto result = X(bitsize, reg);
switch (shift.ZeroExtend()) {
@@ -66,5 +103,81 @@ IR::U32U64 TranslatorVisitor::ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR
return {};
}
IR::U32U64 TranslatorVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
ASSERT(shift <= 4);
ASSERT(bitsize == 32 || bitsize == 64);
IR::UAny val = X(bitsize, reg);
size_t len;
IR::U32U64 extended;
bool signed_extend;
switch (option.ZeroExtend()) {
case 0b000: { // UXTB
val = ir.LeastSignificantByte(val);
len = 8;
signed_extend = false;
break;
}
case 0b001: { // UXTH
val = ir.LeastSignificantHalf(val);
len = 16;
signed_extend = false;
break;
}
case 0b010: { // UXTW
if (bitsize != 32) {
val = ir.LeastSignificantWord(val);
}
len = 32;
signed_extend = false;
break;
}
case 0b011: { // UXTX
len = 64;
signed_extend = false;
break;
}
case 0b100: { // SXTB
val = ir.LeastSignificantByte(val);
len = 8;
signed_extend = true;
break;
}
case 0b101: { // SXTH
val = ir.LeastSignificantHalf(val);
len = 16;
signed_extend = true;
break;
}
case 0b110: { // SXTW
if (bitsize != 32) {
val = ir.LeastSignificantWord(val);
}
len = 32;
signed_extend = true;
break;
}
case 0b111: { // SXTX
len = 64;
signed_extend = true;
break;
}
default:
ASSERT_MSG(false, "Unreachable");
}
if (len < bitsize) {
if (bitsize == 32) {
extended = signed_extend ? ir.SignExtendToWord(val) : ir.ZeroExtendToWord(val);
} else {
extended = signed_extend ? ir.SignExtendToLong(val) : ir.ZeroExtendToLong(val);
}
} else {
extended = val;
}
return ir.LogicalShiftLeft(extended, ir.Imm8(shift));
}
} // namespace A64
} // namespace Dynarmic

View File

@@ -25,10 +25,14 @@ struct TranslatorVisitor final {
bool UnpredictableInstruction();
bool ReservedValue();
IR::U32U64 I(size_t bitsize, u64 value);
IR::U32U64 X(size_t bitsize, Reg reg);
void X(size_t bitsize, Reg reg, IR::U32U64 value);
IR::U32U64 SP(size_t bitsize);
void SP(size_t bitsize, IR::U32U64 value);
IR::U32U64 ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount);
IR::U32U64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift);
// Data processing - Immediate - PC relative addressing
bool ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd);