thumb: add execution of instructions
also arm: fix some instructions Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace matar {
|
namespace matar {
|
||||||
uint32_t
|
uint32_t
|
||||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
eval_shift(ShiftType shift_type, uint32_t value, uint32_t amount, bool& carry) {
|
||||||
uint32_t eval = 0;
|
uint32_t eval = 0;
|
||||||
|
|
||||||
switch (shift_type) {
|
switch (shift_type) {
|
||||||
@@ -48,4 +48,43 @@ eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
|||||||
|
|
||||||
return eval;
|
return eval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
sub(uint32_t a, uint32_t b, bool& carry, bool& overflow) {
|
||||||
|
bool s1 = get_bit(a, 31);
|
||||||
|
bool s2 = get_bit(b, 31);
|
||||||
|
|
||||||
|
uint32_t result = a - b;
|
||||||
|
|
||||||
|
carry = b <= a;
|
||||||
|
overflow = s1 != s2 && s2 == get_bit(result, 31);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
add(uint32_t a, uint32_t b, bool& carry, bool& overflow, bool c) {
|
||||||
|
bool s1 = get_bit(a, 31);
|
||||||
|
bool s2 = get_bit(b, 31);
|
||||||
|
|
||||||
|
uint64_t result = a + b + c;
|
||||||
|
|
||||||
|
carry = get_bit(result, 32);
|
||||||
|
overflow = s1 == s2 && s2 != get_bit(result, 31);
|
||||||
|
|
||||||
|
return result & 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
sbc(uint32_t a, uint32_t b, bool& carry, bool& overflow, bool c) {
|
||||||
|
bool s1 = get_bit(a, 31);
|
||||||
|
bool s2 = get_bit(b, 31);
|
||||||
|
|
||||||
|
uint64_t result = a - b - !c;
|
||||||
|
|
||||||
|
carry = get_bit(result, 32);
|
||||||
|
overflow = s1 != s2 && s2 == get_bit(result, 31);
|
||||||
|
|
||||||
|
return result & 0xFFFFFFFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,5 +40,14 @@ struct Shift {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry);
|
eval_shift(ShiftType shift_type, uint32_t value, uint32_t amount, bool& carry);
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
sub(uint32_t a, uint32_t b, bool& carry, bool& overflow);
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
add(uint32_t a, uint32_t b, bool& carry, bool& overflow, bool c = 0);
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
sbc(uint32_t a, uint32_t b, bool& carry, bool& overflow, bool c);
|
||||||
}
|
}
|
||||||
|
@@ -307,6 +307,7 @@ Instruction::exec(CpuImpl& cpu) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: clean this shit
|
||||||
// account for decrement
|
// account for decrement
|
||||||
if (!data.up)
|
if (!data.up)
|
||||||
address -= (n_regs - 1) * alignment;
|
address -= (n_regs - 1) * alignment;
|
||||||
@@ -419,44 +420,6 @@ Instruction::exec(CpuImpl& cpu) {
|
|||||||
bool overflow = cpu.cpsr.v();
|
bool overflow = cpu.cpsr.v();
|
||||||
bool carry = cpu.cpsr.c();
|
bool carry = cpu.cpsr.c();
|
||||||
|
|
||||||
auto sub = [&carry, &overflow](uint32_t a, uint32_t b) -> uint32_t {
|
|
||||||
bool s1 = get_bit(a, 31);
|
|
||||||
bool s2 = get_bit(b, 31);
|
|
||||||
|
|
||||||
uint32_t result = a - b;
|
|
||||||
|
|
||||||
carry = b <= a;
|
|
||||||
overflow = s1 != s2 && s2 == get_bit(result, 31);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto add = [&carry, &overflow](
|
|
||||||
uint32_t a, uint32_t b, bool c = 0) -> uint32_t {
|
|
||||||
bool s1 = get_bit(a, 31);
|
|
||||||
bool s2 = get_bit(b, 31);
|
|
||||||
|
|
||||||
// 33 bits
|
|
||||||
uint64_t result_ = a + b + c;
|
|
||||||
uint32_t result = result_ & 0xFFFFFFFF;
|
|
||||||
|
|
||||||
carry = get_bit(result_, 32);
|
|
||||||
overflow = s1 == s2 && s2 != get_bit(result, 31);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto sbc = [&carry,
|
|
||||||
&overflow](uint32_t a, uint32_t b, bool c) -> uint32_t {
|
|
||||||
bool s1 = get_bit(a, 31);
|
|
||||||
bool s2 = get_bit(b, 31);
|
|
||||||
|
|
||||||
uint64_t result_ = a - b + c - 1;
|
|
||||||
uint32_t result = result_ & 0xFFFFFFFF;
|
|
||||||
|
|
||||||
carry = get_bit(result_, 32);
|
|
||||||
overflow = s1 != s2 && s2 == get_bit(result, 31);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (data.opcode) {
|
switch (data.opcode) {
|
||||||
case OpCode::AND:
|
case OpCode::AND:
|
||||||
case OpCode::TST:
|
case OpCode::TST:
|
||||||
@@ -469,23 +432,23 @@ Instruction::exec(CpuImpl& cpu) {
|
|||||||
break;
|
break;
|
||||||
case OpCode::SUB:
|
case OpCode::SUB:
|
||||||
case OpCode::CMP:
|
case OpCode::CMP:
|
||||||
result = sub(op_1, op_2);
|
result = sub(op_1, op_2, carry, overflow);
|
||||||
break;
|
break;
|
||||||
case OpCode::RSB:
|
case OpCode::RSB:
|
||||||
result = sub(op_2, op_1);
|
result = sub(op_2, op_1, carry, overflow);
|
||||||
break;
|
break;
|
||||||
case OpCode::ADD:
|
case OpCode::ADD:
|
||||||
case OpCode::CMN:
|
case OpCode::CMN:
|
||||||
result = add(op_1, op_2);
|
result = add(op_1, op_2, carry, overflow);
|
||||||
break;
|
break;
|
||||||
case OpCode::ADC:
|
case OpCode::ADC:
|
||||||
result = add(op_1, op_2, carry);
|
result = add(op_1, op_2, carry, overflow, carry);
|
||||||
break;
|
break;
|
||||||
case OpCode::SBC:
|
case OpCode::SBC:
|
||||||
result = sbc(op_1, op_2, carry);
|
result = sbc(op_1, op_2, carry, overflow, carry);
|
||||||
break;
|
break;
|
||||||
case OpCode::RSC:
|
case OpCode::RSC:
|
||||||
result = sbc(op_2, op_1, carry);
|
result = sbc(op_2, op_1, carry, overflow, carry);
|
||||||
break;
|
break;
|
||||||
case OpCode::ORR:
|
case OpCode::ORR:
|
||||||
result = op_1 | op_2;
|
result = op_1 | op_2;
|
||||||
|
@@ -212,18 +212,19 @@ using InstructionData = std::variant<BranchAndExchange,
|
|||||||
SoftwareInterrupt>;
|
SoftwareInterrupt>;
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
Condition condition;
|
|
||||||
InstructionData data;
|
|
||||||
|
|
||||||
Instruction(uint32_t insn);
|
Instruction(uint32_t insn);
|
||||||
Instruction(Condition condition, InstructionData data) noexcept
|
Instruction(Condition condition, InstructionData data)
|
||||||
: condition(condition)
|
: condition(condition)
|
||||||
, data(data){};
|
, data(data){};
|
||||||
|
|
||||||
void exec(CpuImpl& cpu);
|
void exec(CpuImpl& cpu);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
std::string disassemble();
|
std::string disassemble();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Condition condition;
|
||||||
|
InstructionData data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#include "cpu-impl.hh"
|
#include "cpu-impl.hh"
|
||||||
|
#include "cpu/arm/instruction.hh"
|
||||||
|
#include "cpu/thumb/instruction.hh"
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
#include "util/log.hh"
|
#include "util/log.hh"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -11,9 +13,9 @@ CpuImpl::CpuImpl(const Bus& bus) noexcept
|
|||||||
, gpr({ 0 })
|
, gpr({ 0 })
|
||||||
, cpsr(0)
|
, cpsr(0)
|
||||||
, spsr(0)
|
, spsr(0)
|
||||||
, is_flushed(false)
|
|
||||||
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
|
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
|
||||||
, spsr_banked({ 0, 0, 0, 0, 0 }) {
|
, spsr_banked({ 0, 0, 0, 0, 0 })
|
||||||
|
, is_flushed(false) {
|
||||||
cpsr.set_mode(Mode::Supervisor);
|
cpsr.set_mode(Mode::Supervisor);
|
||||||
cpsr.set_irq_disabled(true);
|
cpsr.set_irq_disabled(true);
|
||||||
cpsr.set_fiq_disabled(true);
|
cpsr.set_fiq_disabled(true);
|
||||||
@@ -120,25 +122,38 @@ CpuImpl::step() {
|
|||||||
uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE;
|
uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE;
|
||||||
|
|
||||||
if (cpsr.state() == State::Arm) {
|
if (cpsr.state() == State::Arm) {
|
||||||
uint32_t x = bus->read_word(cur_pc);
|
arm::Instruction instruction(bus->read_word(cur_pc));
|
||||||
arm::Instruction instruction(x);
|
|
||||||
|
#ifdef DISASSEMBLER
|
||||||
|
glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble());
|
||||||
|
#endif
|
||||||
|
|
||||||
instruction.exec(*this);
|
instruction.exec(*this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
thumb::Instruction instruction(bus->read_halfword(cur_pc));
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
glogger.info("{:#034b}", x);
|
glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble(cur_pc));
|
||||||
glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble());
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
instruction.exec(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance PC
|
||||||
|
{
|
||||||
|
size_t size = cpsr.state() == State::Arm ? arm::INSTRUCTION_SIZE
|
||||||
|
: thumb::INSTRUCTION_SIZE;
|
||||||
|
|
||||||
if (is_flushed) {
|
if (is_flushed) {
|
||||||
// if flushed, do not increment the PC, instead set it to two
|
// if flushed, do not increment the PC, instead set it to two
|
||||||
// instructions ahead to account for flushed "fetch" and "decode"
|
// instructions ahead to account for flushed "fetch" and "decode"
|
||||||
// instructions
|
// instructions
|
||||||
pc += 2 * arm::INSTRUCTION_SIZE;
|
pc += 2 * size;
|
||||||
is_flushed = false;
|
is_flushed = false;
|
||||||
} else {
|
} else {
|
||||||
// if not flushed continue like normal
|
// if not flushed continue like normal
|
||||||
pc += arm::INSTRUCTION_SIZE;
|
pc += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "arm/instruction.hh"
|
||||||
#include "bus.hh"
|
#include "bus.hh"
|
||||||
#include "cpu/arm/instruction.hh"
|
|
||||||
#include "cpu/psr.hh"
|
#include "cpu/psr.hh"
|
||||||
|
#include "thumb/instruction.hh"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ class CpuImpl {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend void arm::Instruction::exec(CpuImpl& cpu);
|
friend void arm::Instruction::exec(CpuImpl& cpu);
|
||||||
|
friend void thumb::Instruction::exec(CpuImpl& cpu);
|
||||||
|
|
||||||
static constexpr uint8_t GPR_COUNT = 16;
|
static constexpr uint8_t GPR_COUNT = 16;
|
||||||
|
|
||||||
@@ -32,13 +34,18 @@ class CpuImpl {
|
|||||||
Psr cpsr; // current program status register
|
Psr cpsr; // current program status register
|
||||||
Psr spsr; // status program status register
|
Psr spsr; // status program status register
|
||||||
|
|
||||||
|
static constexpr uint8_t SP_INDEX = 13;
|
||||||
|
static_assert(SP_INDEX < GPR_COUNT);
|
||||||
|
uint32_t& sp = gpr[SP_INDEX];
|
||||||
|
|
||||||
|
static constexpr uint8_t LR_INDEX = 14;
|
||||||
|
static_assert(LR_INDEX < GPR_COUNT);
|
||||||
|
uint32_t& lr = gpr[LR_INDEX];
|
||||||
|
|
||||||
static constexpr uint8_t PC_INDEX = 15;
|
static constexpr uint8_t PC_INDEX = 15;
|
||||||
static_assert(PC_INDEX < GPR_COUNT);
|
static_assert(PC_INDEX < GPR_COUNT);
|
||||||
|
|
||||||
uint32_t& pc = gpr[PC_INDEX];
|
uint32_t& pc = gpr[PC_INDEX];
|
||||||
|
|
||||||
bool is_flushed;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_SVC_FIRST - 1> svc;
|
std::array<uint32_t, GPR_COUNT - GPR_SVC_FIRST - 1> svc;
|
||||||
@@ -57,5 +64,7 @@ class CpuImpl {
|
|||||||
Psr irq;
|
Psr irq;
|
||||||
Psr und;
|
Psr und;
|
||||||
} spsr_banked; // banked saved program status registers
|
} spsr_banked; // banked saved program status registers
|
||||||
|
|
||||||
|
bool is_flushed;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace matar::thumb {
|
namespace matar::thumb {
|
||||||
std::string
|
std::string
|
||||||
Instruction::disassemble() {
|
Instruction::disassemble(uint32_t pc) {
|
||||||
return std::visit(
|
return std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
[](MoveShiftedRegister& data) {
|
[](MoveShiftedRegister& data) {
|
||||||
@@ -90,8 +90,7 @@ Instruction::disassemble() {
|
|||||||
data.word);
|
data.word);
|
||||||
},
|
},
|
||||||
[](AddOffsetStackPointer& data) {
|
[](AddOffsetStackPointer& data) {
|
||||||
return fmt::format(
|
return fmt::format("ADD SP,#{:d}", data.word);
|
||||||
"ADD SP,#{}{:d}", (data.sign ? '-' : '+'), data.word);
|
|
||||||
},
|
},
|
||||||
[](PushPopRegister& data) {
|
[](PushPopRegister& data) {
|
||||||
std::string regs;
|
std::string regs;
|
||||||
@@ -130,18 +129,24 @@ Instruction::disassemble() {
|
|||||||
return fmt::format(
|
return fmt::format(
|
||||||
"{} R{}!,{{{}}}", (data.load ? "LDMIA" : "STMIA"), data.rb, regs);
|
"{} R{}!,{{{}}}", (data.load ? "LDMIA" : "STMIA"), data.rb, regs);
|
||||||
},
|
},
|
||||||
[](SoftwareInterrupt) { return std::string("SWI"); },
|
[](SoftwareInterrupt& data) {
|
||||||
[](ConditionalBranch& data) {
|
return fmt::format("SWI {:d}", data.vector);
|
||||||
return fmt::format(
|
|
||||||
"B{} {:d}", stringify(data.condition), data.offset);
|
|
||||||
},
|
},
|
||||||
[](UnconditionalBranch& data) {
|
[pc](ConditionalBranch& data) {
|
||||||
return fmt::format("B {:d}", data.offset);
|
return fmt::format(
|
||||||
|
"B{} #{:d}",
|
||||||
|
stringify(data.condition),
|
||||||
|
static_cast<int32_t>(data.offset + pc + 2 * INSTRUCTION_SIZE));
|
||||||
|
},
|
||||||
|
[pc](UnconditionalBranch& data) {
|
||||||
|
return fmt::format(
|
||||||
|
"B #{:d}",
|
||||||
|
static_cast<int32_t>(data.offset + pc + 2 * INSTRUCTION_SIZE));
|
||||||
},
|
},
|
||||||
[](LongBranchWithLink& data) {
|
[](LongBranchWithLink& data) {
|
||||||
// duh this manual be empty for H = 0
|
// duh this manual be empty for H = 0
|
||||||
return fmt::format(
|
return fmt::format(
|
||||||
"BL{} {:d}", (data.high ? "H" : ""), data.offset);
|
"BL{} #{:d}", (data.high ? "H" : ""), data.offset);
|
||||||
},
|
},
|
||||||
[](auto) { return std::string("unknown instruction"); } },
|
[](auto) { return std::string("unknown instruction"); } },
|
||||||
data);
|
data);
|
||||||
|
384
src/cpu/thumb/exec.cc
Normal file
384
src/cpu/thumb/exec.cc
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
#include "cpu/cpu-impl.hh"
|
||||||
|
#include "instruction.hh"
|
||||||
|
#include "util/bits.hh"
|
||||||
|
#include "util/log.hh"
|
||||||
|
|
||||||
|
namespace matar::thumb {
|
||||||
|
void
|
||||||
|
Instruction::exec(CpuImpl& cpu) {
|
||||||
|
auto set_cc = [&cpu](bool c, bool v, bool n, bool z) {
|
||||||
|
cpu.cpsr.set_c(c);
|
||||||
|
cpu.cpsr.set_v(v);
|
||||||
|
cpu.cpsr.set_n(n);
|
||||||
|
cpu.cpsr.set_z(z);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::visit(
|
||||||
|
overloaded{
|
||||||
|
[&cpu, set_cc](MoveShiftedRegister& data) {
|
||||||
|
if (data.opcode == ShiftType::ROR)
|
||||||
|
glogger.error("Invalid opcode in {}", typeid(data).name());
|
||||||
|
|
||||||
|
bool carry = cpu.cpsr.c();
|
||||||
|
|
||||||
|
uint32_t shifted =
|
||||||
|
eval_shift(data.opcode, cpu.gpr[data.rs], data.offset, carry);
|
||||||
|
|
||||||
|
cpu.gpr[data.rd] = shifted;
|
||||||
|
|
||||||
|
set_cc(carry, cpu.cpsr.v(), get_bit(shifted, 31), shifted == 0);
|
||||||
|
},
|
||||||
|
[&cpu, set_cc](AddSubtract& data) {
|
||||||
|
uint32_t offset =
|
||||||
|
data.imm ? static_cast<uint32_t>(static_cast<int8_t>(data.offset))
|
||||||
|
: cpu.gpr[data.offset];
|
||||||
|
uint32_t result = 0;
|
||||||
|
bool carry = cpu.cpsr.c();
|
||||||
|
bool overflow = cpu.cpsr.v();
|
||||||
|
|
||||||
|
switch (data.opcode) {
|
||||||
|
case AddSubtract::OpCode::ADD:
|
||||||
|
result = add(cpu.gpr[data.rs], offset, carry, overflow);
|
||||||
|
break;
|
||||||
|
case AddSubtract::OpCode::SUB:
|
||||||
|
result = sub(cpu.gpr[data.rs], offset, carry, overflow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.gpr[data.rd] = result;
|
||||||
|
set_cc(carry, overflow, get_bit(result, 31), result == 0);
|
||||||
|
},
|
||||||
|
[&cpu, set_cc](MovCmpAddSubImmediate& data) {
|
||||||
|
uint32_t result = 0;
|
||||||
|
bool carry = cpu.cpsr.c();
|
||||||
|
bool overflow = cpu.cpsr.v();
|
||||||
|
|
||||||
|
switch (data.opcode) {
|
||||||
|
case MovCmpAddSubImmediate::OpCode::MOV:
|
||||||
|
result = data.offset;
|
||||||
|
carry = 0;
|
||||||
|
break;
|
||||||
|
case MovCmpAddSubImmediate::OpCode::ADD:
|
||||||
|
result =
|
||||||
|
add(cpu.gpr[data.rd], data.offset, carry, overflow);
|
||||||
|
break;
|
||||||
|
case MovCmpAddSubImmediate::OpCode::SUB:
|
||||||
|
case MovCmpAddSubImmediate::OpCode::CMP:
|
||||||
|
result =
|
||||||
|
sub(cpu.gpr[data.rd], data.offset, carry, overflow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cc(carry, overflow, get_bit(result, 31), result == 0);
|
||||||
|
if (data.opcode != MovCmpAddSubImmediate::OpCode::CMP)
|
||||||
|
cpu.gpr[data.rd] = result;
|
||||||
|
},
|
||||||
|
[&cpu, set_cc](AluOperations& data) {
|
||||||
|
uint32_t op_1 = cpu.gpr[data.rd];
|
||||||
|
uint32_t op_2 = cpu.gpr[data.rs];
|
||||||
|
uint32_t result = 0;
|
||||||
|
|
||||||
|
bool carry = cpu.cpsr.c();
|
||||||
|
bool overflow = cpu.cpsr.v();
|
||||||
|
|
||||||
|
switch (data.opcode) {
|
||||||
|
case AluOperations::OpCode::AND:
|
||||||
|
case AluOperations::OpCode::TST:
|
||||||
|
result = op_1 & op_2;
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::EOR:
|
||||||
|
result = op_1 ^ op_2;
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::LSL:
|
||||||
|
result = eval_shift(ShiftType::LSL, op_1, op_2, carry);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::LSR:
|
||||||
|
result = eval_shift(ShiftType::LSR, op_1, op_2, carry);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::ASR:
|
||||||
|
result = eval_shift(ShiftType::ASR, op_1, op_2, carry);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::ADC:
|
||||||
|
result = add(op_1, op_2, carry, overflow, carry);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::SBC:
|
||||||
|
result = sbc(op_1, op_2, carry, overflow, carry);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::ROR:
|
||||||
|
result = eval_shift(ShiftType::ROR, op_1, op_2, carry);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::NEG:
|
||||||
|
result = -op_2;
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::CMP:
|
||||||
|
result = sub(op_1, op_2, carry, overflow);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::CMN:
|
||||||
|
result = add(op_1, op_2, carry, overflow);
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::ORR:
|
||||||
|
result = op_1 | op_2;
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::MUL:
|
||||||
|
result = op_1 * op_2;
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::BIC:
|
||||||
|
result = op_1 & ~op_2;
|
||||||
|
break;
|
||||||
|
case AluOperations::OpCode::MVN:
|
||||||
|
result = ~op_2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.opcode != AluOperations::OpCode::TST &&
|
||||||
|
data.opcode != AluOperations::OpCode::CMP &&
|
||||||
|
data.opcode != AluOperations::OpCode::CMN)
|
||||||
|
cpu.gpr[data.rd] = result;
|
||||||
|
|
||||||
|
set_cc(carry, overflow, get_bit(result, 31), result == 0);
|
||||||
|
},
|
||||||
|
[&cpu, set_cc](HiRegisterOperations& data) {
|
||||||
|
uint32_t op_1 = cpu.gpr[data.rd];
|
||||||
|
uint32_t op_2 = cpu.gpr[data.rs];
|
||||||
|
|
||||||
|
bool carry = cpu.cpsr.c();
|
||||||
|
bool overflow = cpu.cpsr.v();
|
||||||
|
|
||||||
|
// PC is already current + 4, so dont need to do that
|
||||||
|
if (data.rd == cpu.PC_INDEX)
|
||||||
|
rst_bit(op_1, 0);
|
||||||
|
|
||||||
|
if (data.rs == cpu.PC_INDEX)
|
||||||
|
rst_bit(op_2, 0);
|
||||||
|
|
||||||
|
switch (data.opcode) {
|
||||||
|
case HiRegisterOperations::OpCode::ADD: {
|
||||||
|
cpu.gpr[data.rd] = add(op_1, op_2, carry, overflow);
|
||||||
|
|
||||||
|
if (data.rd == cpu.PC_INDEX)
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
} break;
|
||||||
|
case HiRegisterOperations::OpCode::CMP: {
|
||||||
|
uint32_t result = sub(op_1, op_2, carry, overflow);
|
||||||
|
set_cc(carry, overflow, get_bit(result, 31), result == 0);
|
||||||
|
} break;
|
||||||
|
case HiRegisterOperations::OpCode::MOV: {
|
||||||
|
cpu.gpr[data.rd] = op_2;
|
||||||
|
|
||||||
|
if (data.rd == cpu.PC_INDEX)
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
} break;
|
||||||
|
case HiRegisterOperations::OpCode::BX: {
|
||||||
|
State state = static_cast<State>(op_2 & 1);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
cpu.cpsr.set_state(state);
|
||||||
|
|
||||||
|
// copy to PC
|
||||||
|
cpu.pc = op_2;
|
||||||
|
|
||||||
|
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||||
|
rst_bit(cpu.pc, 0);
|
||||||
|
|
||||||
|
if (state == State::Arm)
|
||||||
|
rst_bit(cpu.pc, 1);
|
||||||
|
|
||||||
|
// pc is affected so flush the pipeline
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](PcRelativeLoad& data) {
|
||||||
|
uint32_t pc = cpu.pc;
|
||||||
|
rst_bit(pc, 1);
|
||||||
|
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_word(pc + data.word);
|
||||||
|
},
|
||||||
|
[&cpu](LoadStoreRegisterOffset& data) {
|
||||||
|
uint32_t address = cpu.gpr[data.rb] + cpu.gpr[data.ro];
|
||||||
|
|
||||||
|
if (data.load) {
|
||||||
|
if (data.byte) {
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_byte(address);
|
||||||
|
} else {
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_word(address);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data.byte) {
|
||||||
|
cpu.bus->write_byte(address, cpu.gpr[data.rd] & 0xFF);
|
||||||
|
} else {
|
||||||
|
cpu.bus->write_word(address, cpu.gpr[data.rd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](LoadStoreSignExtendedHalfword& data) {
|
||||||
|
uint32_t address = cpu.gpr[data.rb] + cpu.gpr[data.ro];
|
||||||
|
|
||||||
|
switch (data.s << 1 | data.h) {
|
||||||
|
case 0b00:
|
||||||
|
cpu.bus->write_halfword(address, cpu.gpr[data.rd] & 0xFFFF);
|
||||||
|
break;
|
||||||
|
case 0b01:
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_halfword(address);
|
||||||
|
break;
|
||||||
|
case 0b10:
|
||||||
|
// sign extend and load the byte
|
||||||
|
cpu.gpr[data.rd] =
|
||||||
|
(static_cast<int32_t>(cpu.bus->read_byte(address))
|
||||||
|
<< 24) >>
|
||||||
|
24;
|
||||||
|
break;
|
||||||
|
case 0b11:
|
||||||
|
// sign extend the halfword
|
||||||
|
cpu.gpr[data.rd] =
|
||||||
|
(static_cast<int32_t>(cpu.bus->read_halfword(address))
|
||||||
|
<< 16) >>
|
||||||
|
16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unreachable
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](LoadStoreImmediateOffset& data) {
|
||||||
|
uint32_t address = cpu.gpr[data.rb] + data.offset;
|
||||||
|
|
||||||
|
if (data.load) {
|
||||||
|
if (data.byte) {
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_byte(address);
|
||||||
|
} else {
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_word(address);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data.byte) {
|
||||||
|
cpu.bus->write_byte(address, cpu.gpr[data.rd] & 0xFF);
|
||||||
|
} else {
|
||||||
|
cpu.bus->write_word(address, cpu.gpr[data.rd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](LoadStoreHalfword& data) {
|
||||||
|
uint32_t address = cpu.gpr[data.rb] + data.offset;
|
||||||
|
|
||||||
|
if (data.load) {
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_halfword(address);
|
||||||
|
} else {
|
||||||
|
cpu.bus->write_halfword(address, cpu.gpr[data.rd] & 0xFFFF);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](SpRelativeLoad& data) {
|
||||||
|
uint32_t address = cpu.sp + data.word;
|
||||||
|
|
||||||
|
if (data.load) {
|
||||||
|
cpu.gpr[data.rd] = cpu.bus->read_word(address);
|
||||||
|
} else {
|
||||||
|
cpu.bus->write_word(address, cpu.gpr[data.rd]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](LoadAddress& data) {
|
||||||
|
if (data.sp) {
|
||||||
|
cpu.gpr[data.rd] = cpu.sp + data.word;
|
||||||
|
} else {
|
||||||
|
// PC is already current + 4, so dont need to do that
|
||||||
|
// force bit 1 to 0
|
||||||
|
cpu.gpr[data.rd] = (cpu.pc & ~(1 << 1)) + data.word;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](AddOffsetStackPointer& data) { cpu.sp += data.word; },
|
||||||
|
[&cpu](PushPopRegister& data) {
|
||||||
|
if (data.load) {
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
if (get_bit(data.regs, i)) {
|
||||||
|
cpu.gpr[i] = cpu.bus->read_word(cpu.sp);
|
||||||
|
cpu.sp += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.pclr) {
|
||||||
|
cpu.pc = cpu.bus->read_word(cpu.sp);
|
||||||
|
cpu.sp += 4;
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data.pclr) {
|
||||||
|
cpu.sp -= 4;
|
||||||
|
cpu.bus->write_word(cpu.sp, cpu.lr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int8_t i = 7; i >= 0; i--) {
|
||||||
|
if (get_bit(data.regs, i)) {
|
||||||
|
cpu.sp -= 4;
|
||||||
|
cpu.bus->write_word(cpu.sp, cpu.gpr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&cpu](MultipleLoad& data) {
|
||||||
|
uint32_t rb = cpu.gpr[data.rb];
|
||||||
|
|
||||||
|
if (data.load) {
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
if (get_bit(data.regs, i)) {
|
||||||
|
cpu.gpr[i] = cpu.bus->read_word(rb);
|
||||||
|
rb += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int8_t i = 7; i >= 0; i--) {
|
||||||
|
if (get_bit(data.regs, i)) {
|
||||||
|
rb -= 4;
|
||||||
|
cpu.bus->write_word(rb, cpu.gpr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.gpr[data.rb] = rb;
|
||||||
|
},
|
||||||
|
[&cpu](ConditionalBranch& data) {
|
||||||
|
if (data.condition == Condition::AL)
|
||||||
|
glogger.warn("Condition 1110 (AL) is undefined");
|
||||||
|
|
||||||
|
if (!cpu.cpsr.condition(data.condition))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cpu.pc += data.offset;
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
},
|
||||||
|
[&cpu](SoftwareInterrupt& data) {
|
||||||
|
// next instruction is one instruction behind PC
|
||||||
|
cpu.lr = cpu.pc - INSTRUCTION_SIZE;
|
||||||
|
cpu.spsr = cpu.cpsr;
|
||||||
|
cpu.pc = data.vector;
|
||||||
|
cpu.cpsr.set_state(State::Arm);
|
||||||
|
cpu.chg_mode(Mode::Supervisor);
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
},
|
||||||
|
[&cpu](UnconditionalBranch& data) {
|
||||||
|
cpu.pc += data.offset;
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
},
|
||||||
|
[&cpu](LongBranchWithLink& data) {
|
||||||
|
// 12 bit integer
|
||||||
|
int32_t offset = data.offset;
|
||||||
|
|
||||||
|
if (data.high) {
|
||||||
|
uint32_t old_pc = cpu.pc;
|
||||||
|
|
||||||
|
cpu.pc = cpu.lr + offset;
|
||||||
|
cpu.lr = (old_pc - INSTRUCTION_SIZE) | 1;
|
||||||
|
cpu.is_flushed = true;
|
||||||
|
} else {
|
||||||
|
// 12 + 11 = 23 bit
|
||||||
|
offset <<= 11;
|
||||||
|
// sign extend
|
||||||
|
offset = (offset << 9) >> 9;
|
||||||
|
cpu.lr = cpu.pc + offset;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[](auto& data) {
|
||||||
|
glogger.error("Unknown thumb format : {}", typeid(data).name());
|
||||||
|
} },
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
#include "instruction.hh"
|
#include "instruction.hh"
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
|
#include "util/log.hh"
|
||||||
|
|
||||||
namespace matar::thumb {
|
namespace matar::thumb {
|
||||||
Instruction::Instruction(uint16_t insn) {
|
Instruction::Instruction(uint16_t insn) {
|
||||||
@@ -55,16 +56,20 @@ Instruction::Instruction(uint16_t insn) {
|
|||||||
HiRegisterOperations::OpCode opcode =
|
HiRegisterOperations::OpCode opcode =
|
||||||
static_cast<HiRegisterOperations::OpCode>(bit_range(insn, 8, 9));
|
static_cast<HiRegisterOperations::OpCode>(bit_range(insn, 8, 9));
|
||||||
|
|
||||||
|
if (opcode == HiRegisterOperations::OpCode::BX && hi_1)
|
||||||
|
glogger.warn("H1 set with BX");
|
||||||
|
|
||||||
rd += (hi_1 ? LO_GPR_COUNT : 0);
|
rd += (hi_1 ? LO_GPR_COUNT : 0);
|
||||||
rs += (hi_2 ? LO_GPR_COUNT : 0);
|
rs += (hi_2 ? LO_GPR_COUNT : 0);
|
||||||
|
|
||||||
data = HiRegisterOperations{ .rd = rd, .rs = rs, .opcode = opcode };
|
data = HiRegisterOperations{ .rd = rd, .rs = rs, .opcode = opcode };
|
||||||
// Format 6: PC-relative load
|
// Format 6: PC-relative load
|
||||||
} else if ((insn & 0xF800) == 0x4800) {
|
} else if ((insn & 0xF800) == 0x4800) {
|
||||||
uint8_t word = bit_range(insn, 0, 7);
|
uint16_t word = bit_range(insn, 0, 7);
|
||||||
uint8_t rd = bit_range(insn, 8, 10);
|
uint8_t rd = bit_range(insn, 8, 10);
|
||||||
|
|
||||||
data = PcRelativeLoad{ .word = word, .rd = rd };
|
data =
|
||||||
|
PcRelativeLoad{ .word = static_cast<uint16_t>(word << 2), .rd = rd };
|
||||||
|
|
||||||
// Format 7: Load/store with register offset
|
// Format 7: Load/store with register offset
|
||||||
} else if ((insn & 0xF200) == 0x5000) {
|
} else if ((insn & 0xF200) == 0x5000) {
|
||||||
@@ -91,13 +96,16 @@ Instruction::Instruction(uint16_t insn) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Format 9: Load/store with immediate offset
|
// Format 9: Load/store with immediate offset
|
||||||
} else if ((insn & 0xF000) == 0x6000) {
|
} else if ((insn & 0xE000) == 0x6000) {
|
||||||
uint8_t rd = bit_range(insn, 0, 2);
|
uint8_t rd = bit_range(insn, 0, 2);
|
||||||
uint8_t rb = bit_range(insn, 3, 5);
|
uint8_t rb = bit_range(insn, 3, 5);
|
||||||
uint8_t offset = bit_range(insn, 6, 10);
|
uint8_t offset = bit_range(insn, 6, 10);
|
||||||
bool load = get_bit(insn, 11);
|
bool load = get_bit(insn, 11);
|
||||||
bool byte = get_bit(insn, 12);
|
bool byte = get_bit(insn, 12);
|
||||||
|
|
||||||
|
if (!byte)
|
||||||
|
offset <<= 2;
|
||||||
|
|
||||||
data = LoadStoreImmediateOffset{
|
data = LoadStoreImmediateOffset{
|
||||||
.rd = rd, .rb = rb, .offset = offset, .load = load, .byte = byte
|
.rd = rd, .rb = rb, .offset = offset, .load = load, .byte = byte
|
||||||
};
|
};
|
||||||
@@ -109,40 +117,43 @@ Instruction::Instruction(uint16_t insn) {
|
|||||||
uint8_t offset = bit_range(insn, 6, 10);
|
uint8_t offset = bit_range(insn, 6, 10);
|
||||||
bool load = get_bit(insn, 11);
|
bool load = get_bit(insn, 11);
|
||||||
|
|
||||||
|
offset <<= 1;
|
||||||
|
|
||||||
data = LoadStoreHalfword{
|
data = LoadStoreHalfword{
|
||||||
.rd = rd, .rb = rb, .offset = offset, .load = load
|
.rd = rd, .rb = rb, .offset = offset, .load = load
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format 11: SP-relative load/store
|
// Format 11: SP-relative load/store
|
||||||
} else if ((insn & 0xF000) == 0x9000) {
|
} else if ((insn & 0xF000) == 0x9000) {
|
||||||
uint8_t word = bit_range(insn, 0, 7);
|
uint16_t word = bit_range(insn, 0, 7);
|
||||||
uint8_t rd = bit_range(insn, 8, 10);
|
uint8_t rd = bit_range(insn, 8, 10);
|
||||||
bool load = get_bit(insn, 11);
|
bool load = get_bit(insn, 11);
|
||||||
|
|
||||||
|
word <<= 2;
|
||||||
|
|
||||||
data = SpRelativeLoad{ .word = word, .rd = rd, .load = load };
|
data = SpRelativeLoad{ .word = word, .rd = rd, .load = load };
|
||||||
|
|
||||||
// Format 12: Load address
|
// Format 12: Load address
|
||||||
} else if ((insn & 0xF000) == 0xA000) {
|
} else if ((insn & 0xF000) == 0xA000) {
|
||||||
uint8_t word = bit_range(insn, 0, 7);
|
uint16_t word = bit_range(insn, 0, 7);
|
||||||
uint8_t rd = bit_range(insn, 8, 10);
|
uint8_t rd = bit_range(insn, 8, 10);
|
||||||
bool sp = get_bit(insn, 11);
|
bool sp = get_bit(insn, 11);
|
||||||
|
|
||||||
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
|
word <<= 2;
|
||||||
|
|
||||||
// Format 12: Load address
|
|
||||||
} else if ((insn & 0xF000) == 0xA000) {
|
|
||||||
uint8_t word = bit_range(insn, 0, 7);
|
|
||||||
uint8_t rd = bit_range(insn, 8, 10);
|
|
||||||
bool sp = get_bit(insn, 11);
|
|
||||||
|
|
||||||
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
|
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
|
||||||
|
|
||||||
// Format 13: Add offset to stack pointer
|
// Format 13: Add offset to stack pointer
|
||||||
} else if ((insn & 0xFF00) == 0xB000) {
|
} else if ((insn & 0xFF00) == 0xB000) {
|
||||||
uint8_t word = bit_range(insn, 0, 6);
|
int16_t word = static_cast<int16_t>(bit_range(insn, 0, 6));
|
||||||
bool sign = get_bit(insn, 7);
|
bool sign = get_bit(insn, 7);
|
||||||
|
|
||||||
data = AddOffsetStackPointer{ .word = word, .sign = sign };
|
word <<= 2;
|
||||||
|
word = static_cast<int16_t>(word * (sign ? -1 : 1));
|
||||||
|
|
||||||
|
data = AddOffsetStackPointer{
|
||||||
|
.word = word,
|
||||||
|
};
|
||||||
|
|
||||||
// Format 14: Push/pop registers
|
// Format 14: Push/pop registers
|
||||||
} else if ((insn & 0xF600) == 0xB400) {
|
} else if ((insn & 0xF600) == 0xB400) {
|
||||||
@@ -162,30 +173,41 @@ Instruction::Instruction(uint16_t insn) {
|
|||||||
|
|
||||||
// Format 17: Software interrupt
|
// Format 17: Software interrupt
|
||||||
} else if ((insn & 0xFF00) == 0xDF00) {
|
} else if ((insn & 0xFF00) == 0xDF00) {
|
||||||
data = SoftwareInterrupt{};
|
uint8_t vector = bit_range(insn, 0, 7);
|
||||||
|
|
||||||
|
data = SoftwareInterrupt{ .vector = vector };
|
||||||
|
|
||||||
// Format 16: Conditional branch
|
// Format 16: Conditional branch
|
||||||
} else if ((insn & 0xF000) == 0xD000) {
|
} else if ((insn & 0xF000) == 0xD000) {
|
||||||
uint16_t offset = bit_range(insn, 0, 7);
|
int32_t offset = bit_range(insn, 0, 7);
|
||||||
Condition condition = static_cast<Condition>(bit_range(insn, 8, 11));
|
Condition condition = static_cast<Condition>(bit_range(insn, 8, 11));
|
||||||
|
|
||||||
data = ConditionalBranch{ .offset = static_cast<uint16_t>(offset << 1),
|
offset <<= 1;
|
||||||
.condition = condition };
|
|
||||||
|
// sign extend the 9 bit integer
|
||||||
|
offset = (offset << 23) >> 23;
|
||||||
|
|
||||||
|
data = ConditionalBranch{ .offset = offset, .condition = condition };
|
||||||
|
|
||||||
// Format 18: Unconditional branch
|
// Format 18: Unconditional branch
|
||||||
} else if ((insn & 0xF800) == 0xE000) {
|
} else if ((insn & 0xF800) == 0xE000) {
|
||||||
uint16_t offset = bit_range(insn, 0, 10);
|
int32_t offset = bit_range(insn, 0, 10);
|
||||||
|
|
||||||
data =
|
offset <<= 1;
|
||||||
UnconditionalBranch{ .offset = static_cast<uint16_t>(offset << 1) };
|
|
||||||
|
// sign extend the 12 bit integer
|
||||||
|
offset = (offset << 20) >> 20;
|
||||||
|
|
||||||
|
data = UnconditionalBranch{ .offset = offset };
|
||||||
|
|
||||||
// Format 19: Long branch with link
|
// Format 19: Long branch with link
|
||||||
} else if ((insn & 0xF000) == 0xF000) {
|
} else if ((insn & 0xF000) == 0xF000) {
|
||||||
uint16_t offset = bit_range(insn, 0, 10);
|
uint16_t offset = bit_range(insn, 0, 10);
|
||||||
bool high = get_bit(insn, 11);
|
bool high = get_bit(insn, 11);
|
||||||
|
|
||||||
data = LongBranchWithLink{ .offset = static_cast<uint16_t>(offset << 1),
|
offset <<= 1;
|
||||||
.high = high };
|
|
||||||
|
data = LongBranchWithLink{ .offset = offset, .high = high };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,10 @@
|
|||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
namespace matar::thumb {
|
namespace matar {
|
||||||
|
class CpuImpl;
|
||||||
|
|
||||||
|
namespace thumb {
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/utility/variant/visit
|
// https://en.cppreference.com/w/cpp/utility/variant/visit
|
||||||
template<class... Ts>
|
template<class... Ts>
|
||||||
@@ -170,7 +173,7 @@ stringify(HiRegisterOperations::OpCode opcode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PcRelativeLoad {
|
struct PcRelativeLoad {
|
||||||
uint8_t word;
|
uint16_t word;
|
||||||
uint8_t rd;
|
uint8_t rd;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -206,20 +209,19 @@ struct LoadStoreHalfword {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SpRelativeLoad {
|
struct SpRelativeLoad {
|
||||||
uint8_t word;
|
uint16_t word;
|
||||||
uint8_t rd;
|
uint8_t rd;
|
||||||
bool load;
|
bool load;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LoadAddress {
|
struct LoadAddress {
|
||||||
uint8_t word;
|
uint16_t word;
|
||||||
uint8_t rd;
|
uint8_t rd;
|
||||||
bool sp;
|
bool sp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AddOffsetStackPointer {
|
struct AddOffsetStackPointer {
|
||||||
uint8_t word;
|
int16_t word;
|
||||||
bool sign;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PushPopRegister {
|
struct PushPopRegister {
|
||||||
@@ -235,14 +237,16 @@ struct MultipleLoad {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ConditionalBranch {
|
struct ConditionalBranch {
|
||||||
uint16_t offset;
|
int32_t offset;
|
||||||
Condition condition;
|
Condition condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoftwareInterrupt {};
|
struct SoftwareInterrupt {
|
||||||
|
uint8_t vector;
|
||||||
|
};
|
||||||
|
|
||||||
struct UnconditionalBranch {
|
struct UnconditionalBranch {
|
||||||
uint16_t offset;
|
int32_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LongBranchWithLink {
|
struct LongBranchWithLink {
|
||||||
@@ -271,12 +275,17 @@ using InstructionData = std::variant<MoveShiftedRegister,
|
|||||||
LongBranchWithLink>;
|
LongBranchWithLink>;
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
InstructionData data;
|
|
||||||
|
|
||||||
Instruction(uint16_t insn);
|
Instruction(uint16_t insn);
|
||||||
|
Instruction(InstructionData data)
|
||||||
|
: data(data) {}
|
||||||
|
|
||||||
|
void exec(CpuImpl& cpu);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
std::string disassemble();
|
std::string disassemble(uint32_t pc = 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
InstructionData data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
lib_sources += files(
|
lib_sources += files(
|
||||||
'instruction.cc'
|
'instruction.cc',
|
||||||
|
'exec.cc'
|
||||||
)
|
)
|
||||||
|
|
||||||
if get_option('disassembler')
|
if get_option('disassembler')
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
|
#include "cpu/cpu-fixture.hh"
|
||||||
#include "cpu/cpu-impl.hh"
|
#include "cpu/cpu-impl.hh"
|
||||||
#include "fixture.hh"
|
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
#include <limits>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
using namespace matar;
|
using namespace matar;
|
||||||
|
|
||||||
@@ -14,11 +12,11 @@ using namespace arm;
|
|||||||
TEST_CASE_METHOD(CpuFixture, "Branch and Exchange", TAG) {
|
TEST_CASE_METHOD(CpuFixture, "Branch and Exchange", TAG) {
|
||||||
InstructionData data = BranchAndExchange{ .rn = 3 };
|
InstructionData data = BranchAndExchange{ .rn = 3 };
|
||||||
|
|
||||||
setr(3, 342890);
|
setr(3, 342800);
|
||||||
|
|
||||||
exec(data);
|
exec(data);
|
||||||
|
|
||||||
CHECK(getr(15) == 342890);
|
CHECK(getr(15) == 342800);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(CpuFixture, "Branch", TAG) {
|
TEST_CASE_METHOD(CpuFixture, "Branch", TAG) {
|
||||||
@@ -806,14 +804,11 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) {
|
|||||||
|
|
||||||
processing->opcode = OpCode::AND;
|
processing->opcode = OpCode::AND;
|
||||||
|
|
||||||
cpsr.set_z(false);
|
|
||||||
set_psr(cpsr);
|
|
||||||
exec(data, Condition::EQ);
|
exec(data, Condition::EQ);
|
||||||
|
|
||||||
// condition is false
|
// condition is false
|
||||||
CHECK(getr(5) == 0);
|
CHECK(getr(5) == 0);
|
||||||
|
|
||||||
cpsr = psr();
|
|
||||||
cpsr.set_z(true);
|
cpsr.set_z(true);
|
||||||
set_psr(cpsr);
|
set_psr(cpsr);
|
||||||
exec(data, Condition::EQ);
|
exec(data, Condition::EQ);
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
tests_sources += files(
|
tests_sources += files(
|
||||||
'fixture.cc',
|
|
||||||
'instruction.cc',
|
'instruction.cc',
|
||||||
'exec.cc'
|
'exec.cc'
|
||||||
)
|
)
|
@@ -1,4 +1,4 @@
|
|||||||
#include "fixture.hh"
|
#include "cpu-fixture.hh"
|
||||||
|
|
||||||
Psr
|
Psr
|
||||||
CpuFixture::psr(bool spsr) {
|
CpuFixture::psr(bool spsr) {
|
||||||
@@ -37,7 +37,7 @@ CpuFixture::set_psr(Psr psr, bool spsr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need these workarounds to just use the public API and not private
|
// We need these workarounds to just use the public API and not private
|
||||||
// fields. Assuming that these work correctly is necessary. Besides, all it
|
// fields. Assuming that these work correctly is necessary. Besides, all that
|
||||||
// matters is that the public API is correct.
|
// matters is that the public API is correct.
|
||||||
uint32_t
|
uint32_t
|
||||||
CpuFixture::getr_(uint8_t r, CpuImpl& cpu) {
|
CpuFixture::getr_(uint8_t r, CpuImpl& cpu) {
|
@@ -15,6 +15,11 @@ class CpuFixture {
|
|||||||
instruction.exec(cpu);
|
instruction.exec(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exec(thumb::InstructionData data) {
|
||||||
|
thumb::Instruction instruction(data);
|
||||||
|
instruction.exec(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
void reset(uint32_t value = 0) { setr(15, value + 8); }
|
void reset(uint32_t value = 0) { setr(15, value + 8); }
|
||||||
|
|
||||||
uint32_t getr(uint8_t r) { return getr_(r, cpu); }
|
uint32_t getr(uint8_t r) { return getr_(r, cpu); }
|
@@ -1,2 +1,6 @@
|
|||||||
|
tests_sources += files(
|
||||||
|
'cpu-fixture.cc'
|
||||||
|
)
|
||||||
|
|
||||||
subdir('arm')
|
subdir('arm')
|
||||||
subdir('thumb')
|
subdir('thumb')
|
990
tests/cpu/thumb/exec.cc
Normal file
990
tests/cpu/thumb/exec.cc
Normal file
@@ -0,0 +1,990 @@
|
|||||||
|
#include "cpu/cpu-fixture.hh"
|
||||||
|
#include "cpu/cpu-impl.hh"
|
||||||
|
#include "cpu/thumb/instruction.hh"
|
||||||
|
#include "util/bits.hh"
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
using namespace matar;
|
||||||
|
|
||||||
|
#define TAG "[thumb][execution]"
|
||||||
|
|
||||||
|
using namespace thumb;
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Move Shifted Register", TAG) {
|
||||||
|
InstructionData data = MoveShiftedRegister{
|
||||||
|
.rd = 3, .rs = 5, .offset = 15, .opcode = ShiftType::LSL
|
||||||
|
};
|
||||||
|
MoveShiftedRegister* move = std::get_if<MoveShiftedRegister>(&data);
|
||||||
|
|
||||||
|
SECTION("LSL") {
|
||||||
|
setr(3, 0);
|
||||||
|
setr(5, 6687);
|
||||||
|
// LSL
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 219119616);
|
||||||
|
|
||||||
|
setr(5, 0);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("LSR") {
|
||||||
|
move->opcode = ShiftType::LSR;
|
||||||
|
setr(5, -1827489745);
|
||||||
|
// LSR
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 75301);
|
||||||
|
CHECK(!psr().n());
|
||||||
|
|
||||||
|
setr(5, 4444);
|
||||||
|
// zero flag
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ASR") {
|
||||||
|
setr(5, -1827489745);
|
||||||
|
move->opcode = ShiftType::ASR;
|
||||||
|
// ASR
|
||||||
|
exec(data);
|
||||||
|
CHECK(psr().n());
|
||||||
|
CHECK(getr(3) == 4294911525);
|
||||||
|
|
||||||
|
setr(5, 500);
|
||||||
|
// zero flag
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Add/Subtract", TAG) {
|
||||||
|
InstructionData data = AddSubtract{ .rd = 5,
|
||||||
|
.rs = 2,
|
||||||
|
.offset = 7,
|
||||||
|
.opcode = AddSubtract::OpCode::ADD,
|
||||||
|
.imm = false };
|
||||||
|
AddSubtract* add = std::get_if<AddSubtract>(&data);
|
||||||
|
setr(2, 378427891);
|
||||||
|
setr(7, -666666);
|
||||||
|
|
||||||
|
SECTION("ADD") {
|
||||||
|
// register
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 377761225);
|
||||||
|
|
||||||
|
add->imm = true;
|
||||||
|
setr(2, (1u << 31) - 1);
|
||||||
|
// immediate and overflow
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 2147483654);
|
||||||
|
CHECK(psr().v());
|
||||||
|
|
||||||
|
setr(2, -7);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
add->imm = true;
|
||||||
|
|
||||||
|
SECTION("SUB") {
|
||||||
|
add->opcode = AddSubtract::OpCode::SUB;
|
||||||
|
setr(2, -((1u << 31) - 1));
|
||||||
|
add->offset = 4;
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 2147483645);
|
||||||
|
CHECK(psr().v());
|
||||||
|
|
||||||
|
setr(2, ~0u);
|
||||||
|
add->offset = -4;
|
||||||
|
// carry
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 3);
|
||||||
|
CHECK(psr().c());
|
||||||
|
|
||||||
|
setr(2, 0);
|
||||||
|
add->offset = 0;
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Move/Compare/Add/Subtract Immediate", TAG) {
|
||||||
|
InstructionData data = MovCmpAddSubImmediate{
|
||||||
|
.offset = 251, .rd = 5, .opcode = MovCmpAddSubImmediate::OpCode::MOV
|
||||||
|
};
|
||||||
|
MovCmpAddSubImmediate* move = std::get_if<MovCmpAddSubImmediate>(&data);
|
||||||
|
|
||||||
|
SECTION("MOV") {
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 251);
|
||||||
|
|
||||||
|
move->offset = 0;
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("CMP") {
|
||||||
|
setr(5, 251);
|
||||||
|
move->opcode = MovCmpAddSubImmediate::OpCode::CMP;
|
||||||
|
CHECK(!psr().z());
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 251);
|
||||||
|
CHECK(psr().z());
|
||||||
|
|
||||||
|
// overflow
|
||||||
|
setr(5, -((1u << 31) - 1));
|
||||||
|
CHECK(!psr().v());
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 2147483649);
|
||||||
|
CHECK(psr().v());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ADD") {
|
||||||
|
move->opcode = MovCmpAddSubImmediate::OpCode::ADD;
|
||||||
|
setr(5, (1u << 31) - 1);
|
||||||
|
// immediate and overflow
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 2147483898);
|
||||||
|
CHECK(psr().v());
|
||||||
|
|
||||||
|
setr(5, -251);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("SUB") {
|
||||||
|
// same as CMP but loaded
|
||||||
|
setr(5, 251);
|
||||||
|
move->opcode = MovCmpAddSubImmediate::OpCode::SUB;
|
||||||
|
CHECK(!psr().z());
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
|
||||||
|
// overflow
|
||||||
|
setr(5, -((1u << 31) - 1));
|
||||||
|
CHECK(!psr().v());
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 2147483398);
|
||||||
|
CHECK(psr().v());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "ALU Operations", TAG) {
|
||||||
|
InstructionData data =
|
||||||
|
AluOperations{ .rd = 1, .rs = 3, .opcode = AluOperations::OpCode::AND };
|
||||||
|
AluOperations* alu = std::get_if<AluOperations>(&data);
|
||||||
|
|
||||||
|
setr(1, 328940001);
|
||||||
|
setr(3, -991);
|
||||||
|
|
||||||
|
SECTION("AND") {
|
||||||
|
// 328940001 & -991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 328939553);
|
||||||
|
CHECK(!psr().n());
|
||||||
|
|
||||||
|
setr(3, 0);
|
||||||
|
CHECK(!psr().z());
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("EOR") {
|
||||||
|
alu->opcode = AluOperations::OpCode::EOR;
|
||||||
|
// 328940001 ^ -991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 3966027200);
|
||||||
|
CHECK(psr().n());
|
||||||
|
|
||||||
|
setr(3, 3966027200);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
CHECK(!psr().n());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("LSL") {
|
||||||
|
setr(3, 3);
|
||||||
|
alu->opcode = AluOperations::OpCode::LSL;
|
||||||
|
// 328940001 << 3
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 2631520008);
|
||||||
|
CHECK(psr().n());
|
||||||
|
|
||||||
|
setr(1, 0);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("LSR") {
|
||||||
|
alu->opcode = AluOperations::OpCode::LSR;
|
||||||
|
setr(3, 991);
|
||||||
|
// 328940001 >> 991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
|
||||||
|
setr(1, -83885328);
|
||||||
|
setr(3, 5);
|
||||||
|
// -83885328 >> 5
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 131596311);
|
||||||
|
CHECK(!psr().z());
|
||||||
|
CHECK(!psr().n());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ASR") {
|
||||||
|
alu->opcode = AluOperations::OpCode::ASR;
|
||||||
|
setr(3, 991);
|
||||||
|
// 328940001 >> 991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
|
||||||
|
setr(1, -83885328);
|
||||||
|
setr(3, 5);
|
||||||
|
// -83885328 >> 5
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 4292345879);
|
||||||
|
CHECK(!psr().z());
|
||||||
|
CHECK(psr().n());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ADC") {
|
||||||
|
alu->opcode = AluOperations::OpCode::ADC;
|
||||||
|
setr(3, (1u << 31) - 1);
|
||||||
|
Psr cpsr = psr();
|
||||||
|
cpsr.set_c(true);
|
||||||
|
set_psr(cpsr);
|
||||||
|
// 2147483647 + 328940001 + 1
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 2476423649);
|
||||||
|
CHECK(psr().v());
|
||||||
|
CHECK(psr().n());
|
||||||
|
CHECK(!psr().c());
|
||||||
|
|
||||||
|
setr(3, -328940001);
|
||||||
|
setr(1, 328940001);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("SBC") {
|
||||||
|
alu->opcode = AluOperations::OpCode::SBC;
|
||||||
|
setr(3, -((1u << 31) - 1));
|
||||||
|
|
||||||
|
Psr cpsr = psr();
|
||||||
|
cpsr.set_c(false);
|
||||||
|
set_psr(cpsr);
|
||||||
|
|
||||||
|
// 328940001 - -2147483647 - 1
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 2476423647);
|
||||||
|
CHECK(psr().v());
|
||||||
|
CHECK(psr().n());
|
||||||
|
CHECK(!psr().c());
|
||||||
|
|
||||||
|
setr(1, -34892);
|
||||||
|
setr(3, -34893);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ROR") {
|
||||||
|
setr(3, 993);
|
||||||
|
alu->opcode = AluOperations::OpCode::ROR;
|
||||||
|
// 328940001 ROR 993
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 2311953648);
|
||||||
|
CHECK(psr().n());
|
||||||
|
CHECK(psr().c());
|
||||||
|
|
||||||
|
setr(1, 0);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("TST") {
|
||||||
|
alu->opcode = AluOperations::OpCode::TST;
|
||||||
|
// 328940001 & -991
|
||||||
|
exec(data);
|
||||||
|
// no change
|
||||||
|
CHECK(getr(1) == 328940001);
|
||||||
|
|
||||||
|
setr(3, 0);
|
||||||
|
CHECK(!psr().z());
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 328940001);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("NEG") {
|
||||||
|
alu->opcode = AluOperations::OpCode::NEG;
|
||||||
|
// -(-991)
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 991);
|
||||||
|
|
||||||
|
setr(3, 0);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("CMP") {
|
||||||
|
alu->opcode = AluOperations::OpCode::CMP;
|
||||||
|
setr(3, -((1u << 31) - 1));
|
||||||
|
// 328940001 - -2147483647
|
||||||
|
exec(data);
|
||||||
|
// no change
|
||||||
|
CHECK(getr(1) == 328940001);
|
||||||
|
CHECK(psr().v());
|
||||||
|
CHECK(psr().n());
|
||||||
|
CHECK(!psr().c());
|
||||||
|
|
||||||
|
setr(1, -34892);
|
||||||
|
setr(3, -34892);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
// no change (-34892)
|
||||||
|
CHECK(getr(1) == 4294932404);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("CMN") {
|
||||||
|
alu->opcode = AluOperations::OpCode::CMN;
|
||||||
|
setr(3, (1u << 31) - 1);
|
||||||
|
// 2147483647 + 328940001
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 328940001);
|
||||||
|
CHECK(psr().v());
|
||||||
|
CHECK(psr().n());
|
||||||
|
CHECK(!psr().c());
|
||||||
|
|
||||||
|
setr(3, -328940001);
|
||||||
|
setr(1, 328940001);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 328940001);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ORR") {
|
||||||
|
alu->opcode = AluOperations::OpCode::ORR;
|
||||||
|
// 328940001 | -991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 4294966753);
|
||||||
|
CHECK(psr().n());
|
||||||
|
|
||||||
|
setr(1, 0);
|
||||||
|
setr(3, 0);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("MUL") {
|
||||||
|
alu->opcode = AluOperations::OpCode::MUL;
|
||||||
|
// 328940001 * -991 (lower 32 bits) (-325979540991 & 0xFFFFFFFF)
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 437973505);
|
||||||
|
|
||||||
|
setr(3, 0);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BIC") {
|
||||||
|
alu->opcode = AluOperations::OpCode::BIC;
|
||||||
|
// 328940001 & ~ -991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 448);
|
||||||
|
CHECK(!psr().n());
|
||||||
|
|
||||||
|
setr(3, ~0u);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("MVN") {
|
||||||
|
alu->opcode = AluOperations::OpCode::MVN;
|
||||||
|
//~ -991
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 990);
|
||||||
|
CHECK(!psr().n());
|
||||||
|
|
||||||
|
setr(3, 24358);
|
||||||
|
// negative
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 4294942937);
|
||||||
|
CHECK(psr().n());
|
||||||
|
|
||||||
|
setr(3, ~0u);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Hi Register Operations/Branch Exchange", TAG) {
|
||||||
|
InstructionData data = HiRegisterOperations{
|
||||||
|
.rd = 5, .rs = 15, .opcode = HiRegisterOperations::OpCode::ADD
|
||||||
|
};
|
||||||
|
HiRegisterOperations* hi = std::get_if<HiRegisterOperations>(&data);
|
||||||
|
|
||||||
|
setr(15, 3452948950);
|
||||||
|
setr(5, 958656720);
|
||||||
|
|
||||||
|
SECTION("ADD") {
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(5) == 116638374);
|
||||||
|
|
||||||
|
// hi + hi
|
||||||
|
hi->rd = 14;
|
||||||
|
hi->rs = 15;
|
||||||
|
setr(14, 42589);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(14) == 3452991539);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("CMP") {
|
||||||
|
hi->opcode = HiRegisterOperations::OpCode::CMP;
|
||||||
|
exec(data);
|
||||||
|
|
||||||
|
// no change
|
||||||
|
CHECK(getr(5) == 958656720);
|
||||||
|
CHECK(!psr().n());
|
||||||
|
CHECK(!psr().c());
|
||||||
|
CHECK(!psr().v());
|
||||||
|
CHECK(!psr().z());
|
||||||
|
|
||||||
|
setr(15, 958656720);
|
||||||
|
// zero
|
||||||
|
exec(data);
|
||||||
|
// no change
|
||||||
|
CHECK(getr(5) == 958656720);
|
||||||
|
CHECK(psr().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("MOV") {
|
||||||
|
hi->opcode = HiRegisterOperations::OpCode::MOV;
|
||||||
|
exec(data);
|
||||||
|
|
||||||
|
CHECK(getr(5) == 3452948950);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BX") {
|
||||||
|
hi->opcode = HiRegisterOperations::OpCode::BX;
|
||||||
|
hi->rs = 10;
|
||||||
|
|
||||||
|
SECTION("Arm") {
|
||||||
|
setr(10, 2189988);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 2189988);
|
||||||
|
// switched to arm
|
||||||
|
CHECK(psr().state() == State::Arm);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Thumb") {
|
||||||
|
setr(10, 2189989);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 2189988);
|
||||||
|
|
||||||
|
// switched to thumb
|
||||||
|
CHECK(psr().state() == State::Thumb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "PC Relative Load", TAG) {
|
||||||
|
InstructionData data = PcRelativeLoad{ .word = 380, .rd = 0 };
|
||||||
|
|
||||||
|
setr(15, 13804);
|
||||||
|
// 13804 + 380
|
||||||
|
bus.write_word(14184, 489753492);
|
||||||
|
|
||||||
|
CHECK(getr(0) == 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(0) == 489753492);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Load/Store with Register Offset", TAG) {
|
||||||
|
InstructionData data = LoadStoreRegisterOffset{
|
||||||
|
.rd = 3, .rb = 0, .ro = 7, .byte = false, .load = false
|
||||||
|
};
|
||||||
|
LoadStoreRegisterOffset* load = std::get_if<LoadStoreRegisterOffset>(&data);
|
||||||
|
|
||||||
|
setr(7, 9910);
|
||||||
|
setr(0, 1034);
|
||||||
|
setr(3, 389524259);
|
||||||
|
|
||||||
|
SECTION("store") {
|
||||||
|
// 9910 + 1034
|
||||||
|
CHECK(bus.read_word(10944) == 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(10944) == 389524259);
|
||||||
|
|
||||||
|
// byte
|
||||||
|
load->byte = true;
|
||||||
|
bus.write_word(10944, 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(10944) == 35);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("load") {
|
||||||
|
load->load = true;
|
||||||
|
bus.write_word(10944, 11123489);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 11123489);
|
||||||
|
|
||||||
|
// byte
|
||||||
|
load->byte = true;
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 33);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Load/Store Sign Extended Byte/Halfword", TAG) {
|
||||||
|
InstructionData data = LoadStoreSignExtendedHalfword{
|
||||||
|
.rd = 3, .rb = 0, .ro = 7, .s = false, .h = false
|
||||||
|
};
|
||||||
|
LoadStoreSignExtendedHalfword* load =
|
||||||
|
std::get_if<LoadStoreSignExtendedHalfword>(&data);
|
||||||
|
|
||||||
|
setr(7, 9910);
|
||||||
|
setr(0, 1034);
|
||||||
|
setr(3, 389524259);
|
||||||
|
|
||||||
|
SECTION("SH = 00") {
|
||||||
|
// 9910 + 1034
|
||||||
|
CHECK(bus.read_word(10944) == 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(10944) == 43811);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("SH = 01") {
|
||||||
|
load->h = true;
|
||||||
|
bus.write_word(10944, 11123489);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 47905);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("SH = 10") {
|
||||||
|
load->s = true;
|
||||||
|
bus.write_word(10944, 34521594);
|
||||||
|
exec(data);
|
||||||
|
// sign extended 250 byte (0xFA)
|
||||||
|
CHECK(getr(3) == 4294967290);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("SH = 11") {
|
||||||
|
load->s = true;
|
||||||
|
load->h = true;
|
||||||
|
bus.write_word(10944, 11123489);
|
||||||
|
// sign extended 47905 halfword (0xBB21)
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 4294949665);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Load/Store with Immediate Offset", TAG) {
|
||||||
|
InstructionData data = LoadStoreImmediateOffset{
|
||||||
|
.rd = 3, .rb = 0, .offset = 110, .load = false, .byte = false
|
||||||
|
};
|
||||||
|
LoadStoreImmediateOffset* load =
|
||||||
|
std::get_if<LoadStoreImmediateOffset>(&data);
|
||||||
|
|
||||||
|
setr(0, 1034);
|
||||||
|
setr(3, 389524259);
|
||||||
|
|
||||||
|
SECTION("store") {
|
||||||
|
// 110 + 1034
|
||||||
|
CHECK(bus.read_word(1144) == 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(1144) == 389524259);
|
||||||
|
|
||||||
|
// byte
|
||||||
|
load->byte = true;
|
||||||
|
bus.write_word(1144, 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(1144) == 35);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("load") {
|
||||||
|
load->load = true;
|
||||||
|
bus.write_word(1144, 11123489);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 11123489);
|
||||||
|
|
||||||
|
// byte
|
||||||
|
load->byte = true;
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 33);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Load/Store Halfword", TAG) {
|
||||||
|
InstructionData data =
|
||||||
|
LoadStoreHalfword{ .rd = 3, .rb = 0, .offset = 110, .load = false };
|
||||||
|
LoadStoreHalfword* load = std::get_if<LoadStoreHalfword>(&data);
|
||||||
|
|
||||||
|
setr(0, 1034);
|
||||||
|
setr(3, 389524259);
|
||||||
|
|
||||||
|
SECTION("store") {
|
||||||
|
// 110 + 1034
|
||||||
|
CHECK(bus.read_word(1144) == 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(1144) == 43811);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("load") {
|
||||||
|
load->load = true;
|
||||||
|
bus.write_word(1144, 11123489);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(3) == 47905);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "SP Relative Load", TAG) {
|
||||||
|
InstructionData data =
|
||||||
|
SpRelativeLoad{ .word = 808, .rd = 1, .load = false };
|
||||||
|
SpRelativeLoad* load = std::get_if<SpRelativeLoad>(&data);
|
||||||
|
|
||||||
|
setr(1, 2349505744);
|
||||||
|
// sp
|
||||||
|
setr(13, 336);
|
||||||
|
|
||||||
|
SECTION("store") {
|
||||||
|
// 110 + 1034
|
||||||
|
CHECK(bus.read_word(1144) == 0);
|
||||||
|
exec(data);
|
||||||
|
CHECK(bus.read_word(1144) == 2349505744);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("load") {
|
||||||
|
load->load = true;
|
||||||
|
bus.write_word(1144, 11123489);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 11123489);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Load Address", TAG) {
|
||||||
|
InstructionData data = LoadAddress{ .word = 808, .rd = 1, .sp = false };
|
||||||
|
LoadAddress* load = std::get_if<LoadAddress>(&data);
|
||||||
|
|
||||||
|
// pc
|
||||||
|
setr(15, 336485);
|
||||||
|
// sp
|
||||||
|
setr(13, 69879977);
|
||||||
|
|
||||||
|
SECTION("PC") {
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 337293);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("SP") {
|
||||||
|
load->sp = true;
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(1) == 69880785);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Add Offset to Stack Pointer", TAG) {
|
||||||
|
InstructionData data = AddOffsetStackPointer{ .word = 473 };
|
||||||
|
AddOffsetStackPointer* add = std::get_if<AddOffsetStackPointer>(&data);
|
||||||
|
|
||||||
|
// sp
|
||||||
|
setr(13, 69879977);
|
||||||
|
|
||||||
|
SECTION("positive") {
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(13) == 69880450);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("negative") {
|
||||||
|
add->word = -473;
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(13) == 69879504);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) {
|
||||||
|
InstructionData data =
|
||||||
|
PushPopRegister{ .regs = 0b11010011, .pclr = false, .load = false };
|
||||||
|
PushPopRegister* push = std::get_if<PushPopRegister>(&data);
|
||||||
|
// registers = 0, 1, 4, 6, 7
|
||||||
|
|
||||||
|
SECTION("push (store)") {
|
||||||
|
|
||||||
|
// populate registers
|
||||||
|
setr(0, 237164);
|
||||||
|
setr(1, 679785111);
|
||||||
|
setr(4, 905895898);
|
||||||
|
setr(6, 131313333);
|
||||||
|
setr(7, 131);
|
||||||
|
|
||||||
|
auto checker = [this]() {
|
||||||
|
// address
|
||||||
|
CHECK(bus.read_word(5548) == 237164);
|
||||||
|
CHECK(bus.read_word(5552) == 679785111);
|
||||||
|
CHECK(bus.read_word(5556) == 905895898);
|
||||||
|
CHECK(bus.read_word(5560) == 131313333);
|
||||||
|
CHECK(bus.read_word(5564) == 131);
|
||||||
|
};
|
||||||
|
|
||||||
|
// set stack pointer to top of stack
|
||||||
|
setr(13, 5568);
|
||||||
|
|
||||||
|
SECTION("without LR") {
|
||||||
|
exec(data);
|
||||||
|
checker();
|
||||||
|
CHECK(getr(13) == 5548);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("with LR") {
|
||||||
|
push->pclr = true;
|
||||||
|
// populate lr
|
||||||
|
setr(14, 999304);
|
||||||
|
// add another word on stack (5568 + 4)
|
||||||
|
setr(13, 5572);
|
||||||
|
exec(data);
|
||||||
|
|
||||||
|
CHECK(bus.read_word(5568) == 999304);
|
||||||
|
checker();
|
||||||
|
CHECK(getr(13) == 5548);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("pop (load)") {
|
||||||
|
push->load = true;
|
||||||
|
|
||||||
|
// populate memory
|
||||||
|
bus.write_word(5548, 237164);
|
||||||
|
bus.write_word(5552, 679785111);
|
||||||
|
bus.write_word(5556, 905895898);
|
||||||
|
bus.write_word(5560, 131313333);
|
||||||
|
bus.write_word(5564, 131);
|
||||||
|
|
||||||
|
auto checker = [this]() {
|
||||||
|
CHECK(getr(0) == 237164);
|
||||||
|
CHECK(getr(1) == 679785111);
|
||||||
|
CHECK(getr(2) == 0);
|
||||||
|
CHECK(getr(3) == 0);
|
||||||
|
CHECK(getr(4) == 905895898);
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(getr(6) == 131313333);
|
||||||
|
CHECK(getr(7) == 131);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
setr(i, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// set stack pointer to bottom of stack
|
||||||
|
setr(13, 5548);
|
||||||
|
|
||||||
|
SECTION("without SP") {
|
||||||
|
exec(data);
|
||||||
|
checker();
|
||||||
|
CHECK(getr(13) == 5568);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("with SP") {
|
||||||
|
push->pclr = true;
|
||||||
|
// populate next address
|
||||||
|
bus.write_word(5568, 93333912);
|
||||||
|
exec(data);
|
||||||
|
|
||||||
|
CHECK(getr(15) == 93333912);
|
||||||
|
checker();
|
||||||
|
CHECK(getr(13) == 5572);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Multiple Load/Store", TAG) {
|
||||||
|
InstructionData data =
|
||||||
|
MultipleLoad{ .regs = 0b11010101, .rb = 2, .load = false };
|
||||||
|
MultipleLoad* push = std::get_if<MultipleLoad>(&data);
|
||||||
|
// registers = 0, 1, 4, 6, 7
|
||||||
|
|
||||||
|
SECTION("push (store)") {
|
||||||
|
|
||||||
|
// populate registers
|
||||||
|
setr(0, 237164);
|
||||||
|
setr(4, 905895898);
|
||||||
|
setr(6, 131313333);
|
||||||
|
setr(7, 131);
|
||||||
|
|
||||||
|
// set R2 (base) to top of stack
|
||||||
|
setr(2, 5568);
|
||||||
|
|
||||||
|
exec(data);
|
||||||
|
|
||||||
|
CHECK(bus.read_word(5548) == 237164);
|
||||||
|
CHECK(bus.read_word(5552) == 5568);
|
||||||
|
CHECK(bus.read_word(5556) == 905895898);
|
||||||
|
CHECK(bus.read_word(5560) == 131313333);
|
||||||
|
CHECK(bus.read_word(5564) == 131);
|
||||||
|
// write back
|
||||||
|
CHECK(getr(2) == 5548);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("pop (load)") {
|
||||||
|
push->load = true;
|
||||||
|
|
||||||
|
// populate memory
|
||||||
|
bus.write_word(5548, 237164);
|
||||||
|
bus.write_word(5552, 679785111);
|
||||||
|
bus.write_word(5556, 905895898);
|
||||||
|
bus.write_word(5560, 131313333);
|
||||||
|
bus.write_word(5564, 131);
|
||||||
|
|
||||||
|
// base
|
||||||
|
setr(2, 5548);
|
||||||
|
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(0) == 237164);
|
||||||
|
CHECK(getr(1) == 0);
|
||||||
|
CHECK(getr(2) == 5568); // write back
|
||||||
|
CHECK(getr(3) == 0);
|
||||||
|
CHECK(getr(4) == 905895898);
|
||||||
|
CHECK(getr(5) == 0);
|
||||||
|
CHECK(getr(6) == 131313333);
|
||||||
|
CHECK(getr(7) == 131);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Conditional Branch", TAG) {
|
||||||
|
InstructionData data =
|
||||||
|
ConditionalBranch{ .offset = -192, .condition = Condition::EQ };
|
||||||
|
ConditionalBranch* branch = std::get_if<ConditionalBranch>(&data);
|
||||||
|
|
||||||
|
setr(15, 4589344);
|
||||||
|
|
||||||
|
SECTION("z") {
|
||||||
|
Psr cpsr = psr();
|
||||||
|
// condition is false
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589344);
|
||||||
|
|
||||||
|
cpsr.set_z(true);
|
||||||
|
set_psr(cpsr);
|
||||||
|
// condition is true
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589152);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("c") {
|
||||||
|
branch->condition = Condition::CS;
|
||||||
|
Psr cpsr = psr();
|
||||||
|
// condition is false
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589344);
|
||||||
|
|
||||||
|
cpsr.set_c(true);
|
||||||
|
set_psr(cpsr);
|
||||||
|
// condition is true
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589152);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("n") {
|
||||||
|
branch->condition = Condition::MI;
|
||||||
|
Psr cpsr = psr();
|
||||||
|
// condition is false
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589344);
|
||||||
|
|
||||||
|
cpsr.set_n(true);
|
||||||
|
set_psr(cpsr);
|
||||||
|
// condition is true
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589152);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("v") {
|
||||||
|
branch->condition = Condition::VS;
|
||||||
|
Psr cpsr = psr();
|
||||||
|
// condition is false
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589344);
|
||||||
|
|
||||||
|
cpsr.set_v(true);
|
||||||
|
set_psr(cpsr);
|
||||||
|
// condition is true
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4589152);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Software Interrupt", TAG) {
|
||||||
|
InstructionData data = SoftwareInterrupt{ .vector = 33 };
|
||||||
|
|
||||||
|
setr(15, 4492);
|
||||||
|
exec(data);
|
||||||
|
CHECK(psr().raw() == psr(true).raw());
|
||||||
|
CHECK(getr(14) == 4490);
|
||||||
|
CHECK(getr(15) == 33);
|
||||||
|
CHECK(psr().state() == State::Arm);
|
||||||
|
CHECK(psr().mode() == Mode::Supervisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Unconditional Branch", TAG) {
|
||||||
|
InstructionData data = UnconditionalBranch{ .offset = -920 };
|
||||||
|
|
||||||
|
setr(15, 4589344);
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(15) == 4588424);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CpuFixture, "Long Branch With Link", TAG) {
|
||||||
|
InstructionData data = LongBranchWithLink{ .offset = 3262, .high = false };
|
||||||
|
LongBranchWithLink* branch = std::get_if<LongBranchWithLink>(&data);
|
||||||
|
|
||||||
|
// high
|
||||||
|
setr(15, 4589344);
|
||||||
|
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(14) == 2881312);
|
||||||
|
|
||||||
|
// low
|
||||||
|
branch->high = true;
|
||||||
|
exec(data);
|
||||||
|
CHECK(getr(14) == 4589343);
|
||||||
|
CHECK(getr(15) == 2884574);
|
||||||
|
}
|
@@ -178,11 +178,12 @@ TEST_CASE("PC Relative Load", TAG) {
|
|||||||
PcRelativeLoad* ldr = nullptr;
|
PcRelativeLoad* ldr = nullptr;
|
||||||
|
|
||||||
REQUIRE((ldr = std::get_if<PcRelativeLoad>(&instruction.data)));
|
REQUIRE((ldr = std::get_if<PcRelativeLoad>(&instruction.data)));
|
||||||
CHECK(ldr->word == 230);
|
// 230 << 2
|
||||||
|
CHECK(ldr->word == 920);
|
||||||
CHECK(ldr->rd == 2);
|
CHECK(ldr->rd == 2);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "LDR R2,[PC,#230]");
|
CHECK(instruction.disassemble() == "LDR R2,[PC,#920]");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,21 +248,32 @@ TEST_CASE("Load/Store with Immediate Offset", TAG) {
|
|||||||
REQUIRE((ldr = std::get_if<LoadStoreImmediateOffset>(&instruction.data)));
|
REQUIRE((ldr = std::get_if<LoadStoreImmediateOffset>(&instruction.data)));
|
||||||
CHECK(ldr->rd == 5);
|
CHECK(ldr->rd == 5);
|
||||||
CHECK(ldr->rb == 3);
|
CHECK(ldr->rb == 3);
|
||||||
CHECK(ldr->offset == 22);
|
// 22 << 4 when byte == false
|
||||||
|
CHECK(ldr->offset == 88);
|
||||||
CHECK(ldr->byte == false);
|
CHECK(ldr->byte == false);
|
||||||
CHECK(ldr->load == false);
|
CHECK(ldr->load == false);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "STR R5,[R3,#22]");
|
CHECK(instruction.disassemble() == "STR R5,[R3,#88]");
|
||||||
|
|
||||||
ldr->byte = true;
|
ldr->load = true;
|
||||||
|
CHECK(instruction.disassemble() == "LDR R5,[R3,#88]");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// byte
|
||||||
|
raw = 0b0111010110011101;
|
||||||
|
instruction = Instruction(raw);
|
||||||
|
|
||||||
|
INFO(instruction.data.index());
|
||||||
|
REQUIRE((ldr = std::get_if<LoadStoreImmediateOffset>(&instruction.data)));
|
||||||
|
CHECK(ldr->byte == true);
|
||||||
|
CHECK(ldr->offset == 22);
|
||||||
|
|
||||||
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "STRB R5,[R3,#22]");
|
CHECK(instruction.disassemble() == "STRB R5,[R3,#22]");
|
||||||
|
|
||||||
ldr->load = true;
|
ldr->load = true;
|
||||||
CHECK(instruction.disassemble() == "LDRB R5,[R3,#22]");
|
CHECK(instruction.disassemble() == "LDRB R5,[R3,#22]");
|
||||||
|
|
||||||
ldr->byte = false;
|
|
||||||
CHECK(instruction.disassemble() == "LDR R5,[R3,#22]");
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,14 +285,15 @@ TEST_CASE("Load/Store Halfword", TAG) {
|
|||||||
REQUIRE((ldr = std::get_if<LoadStoreHalfword>(&instruction.data)));
|
REQUIRE((ldr = std::get_if<LoadStoreHalfword>(&instruction.data)));
|
||||||
CHECK(ldr->rd == 5);
|
CHECK(ldr->rd == 5);
|
||||||
CHECK(ldr->rb == 3);
|
CHECK(ldr->rb == 3);
|
||||||
CHECK(ldr->offset == 26);
|
// 26 << 1
|
||||||
|
CHECK(ldr->offset == 52);
|
||||||
CHECK(ldr->load == false);
|
CHECK(ldr->load == false);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "STRH R5,[R3,#26]");
|
CHECK(instruction.disassemble() == "STRH R5,[R3,#52]");
|
||||||
|
|
||||||
ldr->load = true;
|
ldr->load = true;
|
||||||
CHECK(instruction.disassemble() == "LDRH R5,[R3,#26]");
|
CHECK(instruction.disassemble() == "LDRH R5,[R3,#52]");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,14 +304,15 @@ TEST_CASE("SP-Relative Load/Store", TAG) {
|
|||||||
|
|
||||||
REQUIRE((ldr = std::get_if<SpRelativeLoad>(&instruction.data)));
|
REQUIRE((ldr = std::get_if<SpRelativeLoad>(&instruction.data)));
|
||||||
CHECK(ldr->rd == 4);
|
CHECK(ldr->rd == 4);
|
||||||
CHECK(ldr->word == 157);
|
// 157 << 2
|
||||||
|
CHECK(ldr->word == 628);
|
||||||
CHECK(ldr->load == false);
|
CHECK(ldr->load == false);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "STR R4,[SP,#157]");
|
CHECK(instruction.disassemble() == "STR R4,[SP,#628]");
|
||||||
|
|
||||||
ldr->load = true;
|
ldr->load = true;
|
||||||
CHECK(instruction.disassemble() == "LDR R4,[SP,#157]");
|
CHECK(instruction.disassemble() == "LDR R4,[SP,#628]");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,15 +322,16 @@ TEST_CASE("Load Adress", TAG) {
|
|||||||
LoadAddress* add = nullptr;
|
LoadAddress* add = nullptr;
|
||||||
|
|
||||||
REQUIRE((add = std::get_if<LoadAddress>(&instruction.data)));
|
REQUIRE((add = std::get_if<LoadAddress>(&instruction.data)));
|
||||||
CHECK(add->word == 143);
|
// 143 << 2
|
||||||
|
CHECK(add->word == 572);
|
||||||
CHECK(add->rd == 1);
|
CHECK(add->rd == 1);
|
||||||
CHECK(add->sp == false);
|
CHECK(add->sp == false);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "ADD R1,PC,#143");
|
CHECK(instruction.disassemble() == "ADD R1,PC,#572");
|
||||||
|
|
||||||
add->sp = true;
|
add->sp = true;
|
||||||
CHECK(instruction.disassemble() == "ADD R1,SP,#143");
|
CHECK(instruction.disassemble() == "ADD R1,SP,#572");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,14 +341,21 @@ TEST_CASE("Add Offset to Stack Pointer", TAG) {
|
|||||||
AddOffsetStackPointer* add = nullptr;
|
AddOffsetStackPointer* add = nullptr;
|
||||||
|
|
||||||
REQUIRE((add = std::get_if<AddOffsetStackPointer>(&instruction.data)));
|
REQUIRE((add = std::get_if<AddOffsetStackPointer>(&instruction.data)));
|
||||||
CHECK(add->word == 37);
|
// 37 << 2
|
||||||
CHECK(add->sign == false);
|
CHECK(add->word == 148);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "ADD SP,#+37");
|
CHECK(instruction.disassemble() == "ADD SP,#148");
|
||||||
|
#endif
|
||||||
|
|
||||||
add->sign = true;
|
raw = 0b1011000010100101;
|
||||||
CHECK(instruction.disassemble() == "ADD SP,#-37");
|
instruction = Instruction(raw);
|
||||||
|
|
||||||
|
REQUIRE((add = std::get_if<AddOffsetStackPointer>(&instruction.data)));
|
||||||
|
CHECK(add->word == -148);
|
||||||
|
|
||||||
|
#ifdef DISASSEMBLER
|
||||||
|
CHECK(instruction.disassemble() == "ADD SP,#-148");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,17 +402,18 @@ TEST_CASE("Multiple Load/Store", TAG) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Conditional Branch", TAG) {
|
TEST_CASE("Conditional Branch", TAG) {
|
||||||
uint16_t raw = 0b1101100101110100;
|
uint16_t raw = 0b1101100110110100;
|
||||||
Instruction instruction(raw);
|
Instruction instruction(raw);
|
||||||
ConditionalBranch* b = nullptr;
|
ConditionalBranch* b = nullptr;
|
||||||
|
|
||||||
REQUIRE((b = std::get_if<ConditionalBranch>(&instruction.data)));
|
REQUIRE((b = std::get_if<ConditionalBranch>(&instruction.data)));
|
||||||
// 116 << 2
|
// (-76 << 1)
|
||||||
CHECK(b->offset == 232);
|
CHECK(b->offset == -152);
|
||||||
CHECK(b->condition == Condition::LS);
|
CHECK(b->condition == Condition::LS);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "BLS 232");
|
// (-76 << 1) + PC (0) + 4
|
||||||
|
CHECK(instruction.disassemble() == "BLS #-148");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +425,7 @@ TEST_CASE("SoftwareInterrupt") {
|
|||||||
REQUIRE((swi = std::get_if<SoftwareInterrupt>(&instruction.data)));
|
REQUIRE((swi = std::get_if<SoftwareInterrupt>(&instruction.data)));
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "SWI");
|
CHECK(instruction.disassemble() == "SWI 51");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,11 +435,12 @@ TEST_CASE("Unconditional Branch") {
|
|||||||
UnconditionalBranch* b = nullptr;
|
UnconditionalBranch* b = nullptr;
|
||||||
|
|
||||||
REQUIRE((b = std::get_if<UnconditionalBranch>(&instruction.data)));
|
REQUIRE((b = std::get_if<UnconditionalBranch>(&instruction.data)));
|
||||||
// 1843 << 2
|
// (2147483443 << 1)
|
||||||
REQUIRE(b->offset == 3686);
|
REQUIRE(b->offset == -410);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "B 3686");
|
// (2147483443 << 1) + PC(0) + 4
|
||||||
|
CHECK(instruction.disassemble() == "B #-406");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,10 +455,10 @@ TEST_CASE("Long Branch with link") {
|
|||||||
CHECK(bl->high == false);
|
CHECK(bl->high == false);
|
||||||
|
|
||||||
#ifdef DISASSEMBLER
|
#ifdef DISASSEMBLER
|
||||||
CHECK(instruction.disassemble() == "BL 2520");
|
CHECK(instruction.disassemble() == "BL #2520");
|
||||||
|
|
||||||
bl->high = true;
|
bl->high = true;
|
||||||
CHECK(instruction.disassemble() == "BLH 2520");
|
CHECK(instruction.disassemble() == "BLH #2520");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
tests_sources += files(
|
tests_sources += files(
|
||||||
'instruction.cc'
|
'instruction.cc',
|
||||||
|
'exec.cc'
|
||||||
)
|
)
|
Reference in New Issue
Block a user