From 0f098749295bbe79493cfa0da9054d0a0523c913 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 27 Sep 2023 22:40:44 +0530 Subject: [PATCH] cpu: get rid of the test workaround now can we remove the pimpl? Signed-off-by: Amneesh Singh --- src/cpu/arm/exec.cc | 260 ++++++++--------- src/cpu/arm/instruction.hh | 7 +- src/cpu/cpu-impl.cc | 2 +- src/cpu/cpu-impl.hh | 5 +- src/cpu/psr.cc | 4 +- src/cpu/psr.hh | 2 +- tests/cpu/arm/exec.cc | 557 ++++++++++++++++++------------------- tests/cpu/arm/fixture.cc | 96 +++++++ tests/cpu/arm/fixture.hh | 37 +++ tests/cpu/arm/meson.build | 1 + 10 files changed, 551 insertions(+), 420 deletions(-) create mode 100644 tests/cpu/arm/fixture.cc create mode 100644 tests/cpu/arm/fixture.hh diff --git a/src/cpu/arm/exec.cc b/src/cpu/arm/exec.cc index e48ad02..86a12c6 100644 --- a/src/cpu/arm/exec.cc +++ b/src/cpu/arm/exec.cc @@ -2,23 +2,20 @@ #include "util/bits.hh" #include "util/log.hh" -namespace matar { +namespace matar::arm { void -CpuImpl::exec(const arm::Instruction instruction) { - Condition cond = instruction.condition; - arm::InstructionData data = instruction.data; - - if (!cpsr.condition(cond)) { +Instruction::exec(CpuImpl& cpu) { + if (!cpu.cpsr.condition(condition)) { return; } - auto pc_error = [](uint8_t r) { - if (r == PC_INDEX) + auto pc_error = [cpu](uint8_t r) { + if (r == cpu.PC_INDEX) glogger.error("Using PC (R15) as operand register"); }; - auto pc_warn = [](uint8_t r) { - if (r == PC_INDEX) + auto pc_warn = [cpu](uint8_t r) { + if (r == cpu.PC_INDEX) glogger.warn("Using PC (R15) as operand register"); }; @@ -26,38 +23,39 @@ CpuImpl::exec(const arm::Instruction instruction) { std::visit( overloaded{ - [this, pc_warn](BranchAndExchange& data) { + [&cpu, pc_warn](BranchAndExchange& data) { State state = static_cast(data.rn & 1); pc_warn(data.rn); // set state - cpsr.set_state(state); + cpu.cpsr.set_state(state); // copy to PC - pc = gpr[data.rn]; + cpu.pc = cpu.gpr[data.rn]; // ignore [1:0] bits for arm and 0 bit for thumb - rst_bit(pc, 0); + rst_bit(cpu.pc, 0); if (state == State::Arm) - rst_bit(pc, 1); + rst_bit(cpu.pc, 1); // pc is affected so flush the pipeline - is_flushed = true; + cpu.is_flushed = true; }, - [this](Branch& data) { + [&cpu](Branch& data) { if (data.link) - gpr[14] = pc - INSTRUCTION_SIZE; + cpu.gpr[14] = cpu.pc - INSTRUCTION_SIZE; // data.offset accounts for two instructions ahead when // disassembling, so need to adjust - pc = static_cast(pc) - 2 * INSTRUCTION_SIZE + data.offset; + cpu.pc = + static_cast(cpu.pc) - 2 * INSTRUCTION_SIZE + data.offset; // pc is affected so flush the pipeline - is_flushed = true; + cpu.is_flushed = true; }, - [this, pc_error](Multiply& data) { + [&cpu, pc_error](Multiply& data) { if (data.rd == data.rm) glogger.error("rd and rm are not distinct in {}", typeid(data).name()); @@ -66,16 +64,16 @@ CpuImpl::exec(const arm::Instruction instruction) { pc_error(data.rd); pc_error(data.rd); - gpr[data.rd] = - gpr[data.rm] * gpr[data.rs] + (data.acc ? gpr[data.rn] : 0); + cpu.gpr[data.rd] = cpu.gpr[data.rm] * cpu.gpr[data.rs] + + (data.acc ? cpu.gpr[data.rn] : 0); if (data.set) { - cpsr.set_z(gpr[data.rd] == 0); - cpsr.set_n(get_bit(gpr[data.rd], 31)); - cpsr.set_c(0); + cpu.cpsr.set_z(cpu.gpr[data.rd] == 0); + cpu.cpsr.set_n(get_bit(cpu.gpr[data.rd], 31)); + cpu.cpsr.set_c(0); } }, - [this, pc_error](MultiplyLong& data) { + [&cpu, pc_error](MultiplyLong& data) { if (data.rdhi == data.rdlo || data.rdhi == data.rm || data.rdlo == data.rm) glogger.error("rdhi, rdlo and rm are not distinct in {}", @@ -91,58 +89,60 @@ CpuImpl::exec(const arm::Instruction instruction) { return static_cast(x); }; - uint64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) + - (data.acc ? (cast(gpr[data.rdhi]) << 32) | - cast(gpr[data.rdlo]) - : 0); + uint64_t eval = + cast(cpu.gpr[data.rm]) * cast(cpu.gpr[data.rs]) + + (data.acc ? (cast(cpu.gpr[data.rdhi]) << 32) | + cast(cpu.gpr[data.rdlo]) + : 0); - gpr[data.rdlo] = bit_range(eval, 0, 31); - gpr[data.rdhi] = bit_range(eval, 32, 63); + cpu.gpr[data.rdlo] = bit_range(eval, 0, 31); + cpu.gpr[data.rdhi] = bit_range(eval, 32, 63); } else { auto cast = [](uint32_t x) -> int64_t { return static_cast(static_cast(x)); }; - int64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) + - (data.acc ? (cast(gpr[data.rdhi]) << 32) | - cast(gpr[data.rdlo]) + int64_t eval = cast(cpu.gpr[data.rm]) * cast(cpu.gpr[data.rs]) + + (data.acc ? (cast(cpu.gpr[data.rdhi]) << 32) | + cast(cpu.gpr[data.rdlo]) : 0); - gpr[data.rdlo] = bit_range(eval, 0, 31); - gpr[data.rdhi] = bit_range(eval, 32, 63); + cpu.gpr[data.rdlo] = bit_range(eval, 0, 31); + cpu.gpr[data.rdhi] = bit_range(eval, 32, 63); } if (data.set) { - cpsr.set_z(gpr[data.rdhi] == 0 && gpr[data.rdlo] == 0); - cpsr.set_n(get_bit(gpr[data.rdhi], 31)); - cpsr.set_c(0); - cpsr.set_v(0); + cpu.cpsr.set_z(cpu.gpr[data.rdhi] == 0 && + cpu.gpr[data.rdlo] == 0); + cpu.cpsr.set_n(get_bit(cpu.gpr[data.rdhi], 31)); + cpu.cpsr.set_c(0); + cpu.cpsr.set_v(0); } }, [](Undefined) { glogger.warn("Undefined instruction"); }, - [this, pc_error](SingleDataSwap& data) { + [&cpu, pc_error](SingleDataSwap& data) { pc_error(data.rm); pc_error(data.rn); pc_error(data.rd); if (data.byte) { - gpr[data.rd] = bus->read_byte(gpr[data.rn]); - bus->write_byte(gpr[data.rn], gpr[data.rm] & 0xFF); + cpu.gpr[data.rd] = cpu.bus->read_byte(cpu.gpr[data.rn]); + cpu.bus->write_byte(cpu.gpr[data.rn], cpu.gpr[data.rm] & 0xFF); } else { - gpr[data.rd] = bus->read_word(gpr[data.rn]); - bus->write_word(gpr[data.rn], gpr[data.rm]); + cpu.gpr[data.rd] = cpu.bus->read_word(cpu.gpr[data.rn]); + cpu.bus->write_word(cpu.gpr[data.rn], cpu.gpr[data.rm]); } }, - [this, pc_warn, pc_error](SingleDataTransfer& data) { + [&cpu, pc_warn, pc_error](SingleDataTransfer& data) { uint32_t offset = 0; - uint32_t address = gpr[data.rn]; + uint32_t address = cpu.gpr[data.rn]; if (!data.pre && data.write) glogger.warn("Write-back enabled with post-indexing in {}", typeid(data).name()); - if (data.rn == PC_INDEX && data.write) + if (data.rn == cpu.PC_INDEX && data.write) glogger.warn("Write-back enabled with base register as PC {}", typeid(data).name()); @@ -156,22 +156,22 @@ CpuImpl::exec(const arm::Instruction instruction) { } else if (const Shift* shift = std::get_if(&data.offset)) { uint8_t amount = (shift->data.immediate ? shift->data.operand - : gpr[shift->data.operand] & 0xFF); + : cpu.gpr[shift->data.operand] & 0xFF); - bool carry = cpsr.c(); + bool carry = cpu.cpsr.c(); if (!shift->data.immediate) pc_error(shift->data.operand); pc_error(shift->rm); - offset = - eval_shift(shift->data.type, gpr[shift->rm], amount, carry); + offset = eval_shift( + shift->data.type, cpu.gpr[shift->rm], amount, carry); - cpsr.set_c(carry); + cpu.cpsr.set_c(carry); } // PC is always two instructions ahead - if (data.rn == PC_INDEX) + if (data.rn == cpu.PC_INDEX) address -= 2 * INSTRUCTION_SIZE; if (data.pre) @@ -181,35 +181,35 @@ CpuImpl::exec(const arm::Instruction instruction) { if (data.load) { // byte if (data.byte) - gpr[data.rd] = bus->read_byte(address); + cpu.gpr[data.rd] = cpu.bus->read_byte(address); // word else - gpr[data.rd] = bus->read_word(address); + cpu.gpr[data.rd] = cpu.bus->read_word(address); // store } else { // take PC into consideration - if (data.rd == PC_INDEX) + if (data.rd == cpu.PC_INDEX) address += INSTRUCTION_SIZE; // byte if (data.byte) - bus->write_byte(address, gpr[data.rd] & 0xFF); + cpu.bus->write_byte(address, cpu.gpr[data.rd] & 0xFF); // word else - bus->write_word(address, gpr[data.rd]); + cpu.bus->write_word(address, cpu.gpr[data.rd]); } if (!data.pre) address += (data.up ? offset : -offset); if (!data.pre || data.write) - gpr[data.rn] = address; + cpu.gpr[data.rn] = address; - if (data.rd == PC_INDEX && data.load) - is_flushed = true; + if (data.rd == cpu.PC_INDEX && data.load) + cpu.is_flushed = true; }, - [this, pc_warn, pc_error](HalfwordTransfer& data) { - uint32_t address = gpr[data.rn]; + [&cpu, pc_warn, pc_error](HalfwordTransfer& data) { + uint32_t address = cpu.gpr[data.rn]; uint32_t offset = 0; if (!data.pre && data.write) @@ -225,13 +225,13 @@ CpuImpl::exec(const arm::Instruction instruction) { // offset is register number (4 bits) when not an immediate if (!data.imm) { pc_error(data.offset); - offset = gpr[data.offset]; + offset = cpu.gpr[data.offset]; } else { offset = data.offset; } // PC is always two instructions ahead - if (data.rn == PC_INDEX) + if (data.rn == cpu.PC_INDEX) address -= 2 * INSTRUCTION_SIZE; if (data.pre) @@ -243,62 +243,62 @@ CpuImpl::exec(const arm::Instruction instruction) { if (data.sign) { // halfword if (data.half) { - gpr[data.rd] = bus->read_halfword(address); + cpu.gpr[data.rd] = cpu.bus->read_halfword(address); // sign extend the halfword - gpr[data.rd] = - (static_cast(gpr[data.rd]) << 16) >> 16; + cpu.gpr[data.rd] = + (static_cast(cpu.gpr[data.rd]) << 16) >> 16; // byte } else { - gpr[data.rd] = bus->read_byte(address); + cpu.gpr[data.rd] = cpu.bus->read_byte(address); // sign extend the byte - gpr[data.rd] = - (static_cast(gpr[data.rd]) << 24) >> 24; + cpu.gpr[data.rd] = + (static_cast(cpu.gpr[data.rd]) << 24) >> 24; } // unsigned halfword } else if (data.half) { - gpr[data.rd] = bus->read_halfword(address); + cpu.gpr[data.rd] = cpu.bus->read_halfword(address); } // store } else { // take PC into consideration - if (data.rd == PC_INDEX) + if (data.rd == cpu.PC_INDEX) address += INSTRUCTION_SIZE; // halfword if (data.half) - bus->write_halfword(address, gpr[data.rd]); + cpu.bus->write_halfword(address, cpu.gpr[data.rd]); } if (!data.pre) address += (data.up ? offset : -offset); if (!data.pre || data.write) - gpr[data.rn] = address; + cpu.gpr[data.rn] = address; - if (data.rd == PC_INDEX && data.load) - is_flushed = true; + if (data.rd == cpu.PC_INDEX && data.load) + cpu.is_flushed = true; }, - [this, pc_error](BlockDataTransfer& data) { - uint32_t address = gpr[data.rn]; - Mode mode = cpsr.mode(); + [&cpu, pc_error](BlockDataTransfer& data) { + uint32_t address = cpu.gpr[data.rn]; + Mode mode = cpu.cpsr.mode(); uint8_t alignment = 4; // word uint8_t i = 0; uint8_t n_regs = std::popcount(data.regs); pc_error(data.rn); - if (cpsr.mode() == Mode::User && data.s) { + if (cpu.cpsr.mode() == Mode::User && data.s) { glogger.error("Bit S is set outside priviliged modes in {}", typeid(data).name()); } // we just change modes to load user registers - if ((!get_bit(data.regs, PC_INDEX) && data.s) || + if ((!get_bit(data.regs, cpu.PC_INDEX) && data.s) || (!data.load && data.s)) { - chg_mode(Mode::User); + cpu.chg_mode(Mode::User); if (data.write) { glogger.error( @@ -315,22 +315,22 @@ CpuImpl::exec(const arm::Instruction instruction) { address += (data.up ? alignment : -alignment); if (data.load) { - if (get_bit(data.regs, PC_INDEX) && data.s && data.load) { - // current mode's spsr is already loaded when it was + if (get_bit(data.regs, cpu.PC_INDEX) && data.s && data.load) { + // current mode's cpu.spsr is already loaded when it was // switched - spsr = cpsr; + cpu.spsr = cpu.cpsr; } - for (i = 0; i < GPR_COUNT; i++) { + for (i = 0; i < cpu.GPR_COUNT; i++) { if (get_bit(data.regs, i)) { - gpr[i] = bus->read_word(address); + cpu.gpr[i] = cpu.bus->read_word(address); address += alignment; } } } else { - for (i = 0; i < GPR_COUNT; i++) { + for (i = 0; i < cpu.GPR_COUNT; i++) { if (get_bit(data.regs, i)) { - bus->write_word(address, gpr[i]); + cpu.bus->write_word(address, cpu.gpr[i]); address += alignment; } } @@ -346,37 +346,37 @@ CpuImpl::exec(const arm::Instruction instruction) { address -= alignment; if (!data.pre || data.write) - gpr[data.rn] = address; + cpu.gpr[data.rn] = address; - if (data.load && get_bit(data.regs, PC_INDEX)) - is_flushed = true; + if (data.load && get_bit(data.regs, cpu.PC_INDEX)) + cpu.is_flushed = true; // load back the original mode registers - chg_mode(mode); + cpu.chg_mode(mode); }, - [this, pc_error](PsrTransfer& data) { - if (data.spsr && cpsr.mode() == Mode::User) { - glogger.error("Accessing SPSR in User mode in {}", + [&cpu, pc_error](PsrTransfer& data) { + if (data.spsr && cpu.cpsr.mode() == Mode::User) { + glogger.error("Accessing CPU.SPSR in User mode in {}", typeid(data).name()); } - Psr& psr = data.spsr ? spsr : cpsr; + Psr& psr = data.spsr ? cpu.spsr : cpu.cpsr; switch (data.type) { case PsrTransfer::Type::Mrs: pc_error(data.operand); - gpr[data.operand] = psr.raw(); + cpu.gpr[data.operand] = psr.raw(); break; case PsrTransfer::Type::Msr: pc_error(data.operand); - if (cpsr.mode() != Mode::User) { - psr.set_all(gpr[data.operand]); + if (cpu.cpsr.mode() != Mode::User) { + psr.set_all(cpu.gpr[data.operand]); } break; case PsrTransfer::Type::Msr_flg: uint32_t operand = - (data.imm ? data.operand : gpr[data.operand]); + (data.imm ? data.operand : cpu.gpr[data.operand]); psr.set_n(get_bit(operand, 31)); psr.set_z(get_bit(operand, 30)); psr.set_c(get_bit(operand, 29)); @@ -384,10 +384,10 @@ CpuImpl::exec(const arm::Instruction instruction) { break; } }, - [this, pc_error](DataProcessing& data) { + [&cpu, pc_error](DataProcessing& data) { using OpCode = DataProcessing::OpCode; - uint32_t op_1 = gpr[data.rn]; + uint32_t op_1 = cpu.gpr[data.rn]; uint32_t op_2 = 0; uint32_t result = 0; @@ -398,26 +398,26 @@ CpuImpl::exec(const arm::Instruction instruction) { } else if (const Shift* shift = std::get_if(&data.operand)) { uint8_t amount = (shift->data.immediate ? shift->data.operand - : gpr[shift->data.operand] & 0xFF); + : cpu.gpr[shift->data.operand] & 0xFF); - bool carry = cpsr.c(); + bool carry = cpu.cpsr.c(); if (!shift->data.immediate) pc_error(shift->data.operand); pc_error(shift->rm); - op_2 = - eval_shift(shift->data.type, gpr[shift->rm], amount, carry); + op_2 = eval_shift( + shift->data.type, cpu.gpr[shift->rm], amount, carry); - cpsr.set_c(carry); + cpu.cpsr.set_c(carry); // PC is 12 bytes ahead when shifting - if (data.rn == PC_INDEX) + if (data.rn == cpu.PC_INDEX) op_1 += INSTRUCTION_SIZE; } - bool overflow = cpsr.v(); - bool carry = cpsr.c(); + bool overflow = cpu.cpsr.v(); + bool carry = cpu.cpsr.c(); auto sub = [&carry, &overflow](uint32_t a, uint32_t b) -> uint32_t { bool s1 = get_bit(a, 31); @@ -501,19 +501,19 @@ CpuImpl::exec(const arm::Instruction instruction) { break; } - auto set_conditions = [this, carry, overflow, result]() { - cpsr.set_c(carry); - cpsr.set_v(overflow); - cpsr.set_n(get_bit(result, 31)); - cpsr.set_z(result == 0); + auto set_conditions = [&cpu, carry, overflow, result]() { + cpu.cpsr.set_c(carry); + cpu.cpsr.set_v(overflow); + cpu.cpsr.set_n(get_bit(result, 31)); + cpu.cpsr.set_z(result == 0); }; if (data.set) { - if (data.rd == PC_INDEX) { - if (cpsr.mode() == Mode::User) + if (data.rd == cpu.PC_INDEX) { + if (cpu.cpsr.mode() == Mode::User) glogger.error("Running {} in User mode", typeid(data).name()); - spsr = cpsr; + cpu.spsr = cpu.cpsr; } else { set_conditions(); } @@ -523,15 +523,15 @@ CpuImpl::exec(const arm::Instruction instruction) { data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) { set_conditions(); } else { - gpr[data.rd] = result; - if (data.rd == PC_INDEX || data.opcode == OpCode::MVN) - is_flushed = true; + cpu.gpr[data.rd] = result; + if (data.rd == cpu.PC_INDEX || data.opcode == OpCode::MVN) + cpu.is_flushed = true; } }, - [this](SoftwareInterrupt) { - chg_mode(Mode::Supervisor); - pc = 0x08; - spsr = cpsr; + [&cpu](SoftwareInterrupt) { + cpu.chg_mode(Mode::Supervisor); + cpu.pc = 0x08; + cpu.spsr = cpu.cpsr; }, [](auto& data) { glogger.error("Unimplemented {} instruction", typeid(data).name()); diff --git a/src/cpu/arm/instruction.hh b/src/cpu/arm/instruction.hh index 03d6d44..ceffa39 100644 --- a/src/cpu/arm/instruction.hh +++ b/src/cpu/arm/instruction.hh @@ -5,7 +5,10 @@ #include #include -namespace matar::arm { +namespace matar { +class CpuImpl; + +namespace arm { // https://en.cppreference.com/w/cpp/utility/variant/visit template @@ -216,9 +219,11 @@ struct Instruction { Instruction(Condition condition, InstructionData data) noexcept : condition(condition) , data(data){}; + void exec(CpuImpl& cpu); #ifdef DISASSEMBLER std::string disassemble(); #endif }; } +} diff --git a/src/cpu/cpu-impl.cc b/src/cpu/cpu-impl.cc index 5da109e..0e3ba48 100644 --- a/src/cpu/cpu-impl.cc +++ b/src/cpu/cpu-impl.cc @@ -123,7 +123,7 @@ CpuImpl::step() { uint32_t x = bus->read_word(cur_pc); arm::Instruction instruction(x); - exec(instruction); + instruction.exec(*this); #ifdef DISASSEMBLER glogger.info("{:#034b}", x); diff --git a/src/cpu/cpu-impl.hh b/src/cpu/cpu-impl.hh index a622871..af46bb5 100644 --- a/src/cpu/cpu-impl.hh +++ b/src/cpu/cpu-impl.hh @@ -13,12 +13,9 @@ class CpuImpl { void step(); void chg_mode(const Mode to); - void exec(const arm::Instruction instruction); - // TODO: get rid of this -#ifndef MATAR_CPU_TESTS private: -#endif + friend void arm::Instruction::exec(CpuImpl& cpu); static constexpr uint8_t GPR_COUNT = 16; diff --git a/src/cpu/psr.cc b/src/cpu/psr.cc index c3b4848..23ebfd7 100644 --- a/src/cpu/psr.cc +++ b/src/cpu/psr.cc @@ -13,7 +13,7 @@ Psr::raw() const { void Psr::set_all(uint32_t raw) { - psr = raw & ~PSR_CLEAR_RESERVED; + psr = raw; } Mode @@ -91,7 +91,7 @@ Psr::condition(Condition cond) const { case Condition::LE: return z() || (n() != v()); case Condition::AL: - return true && state() == State::Arm; + return true; } return false; diff --git a/src/cpu/psr.hh b/src/cpu/psr.hh index 21988b8..7b24574 100644 --- a/src/cpu/psr.hh +++ b/src/cpu/psr.hh @@ -61,7 +61,7 @@ stringify(Condition cond) { CASE(GT) CASE(LE) case Condition::AL: { - // empty + return ""; } } diff --git a/tests/cpu/arm/exec.cc b/tests/cpu/arm/exec.cc index 6462f9f..533a806 100644 --- a/tests/cpu/arm/exec.cc +++ b/tests/cpu/arm/exec.cc @@ -1,7 +1,5 @@ -#define MATAR_CPU_TESTS #include "cpu/cpu-impl.hh" -#undef MATAR_CPU_TESTS - +#include "fixture.hh" #include "util/bits.hh" #include #include @@ -9,31 +7,6 @@ using namespace matar; -class CpuFixture { - public: - CpuFixture() - : cpu(Bus(Memory(std::array(), - std::vector(Header::HEADER_SIZE)))) {} - - protected: - void exec(arm::InstructionData data, Condition condition = Condition::AL) { - arm::Instruction instruction(condition, data); - cpu.exec(instruction); - } - - void reset(uint32_t value = 0) { - cpu.pc = value + arm::INSTRUCTION_SIZE * 2; - } - - CpuImpl cpu; - - private: - class Null : public std::streambuf { - public: - int overflow(int c) override { return c; } - }; -}; - #define TAG "[arm][execution]" using namespace arm; @@ -41,11 +14,11 @@ using namespace arm; TEST_CASE_METHOD(CpuFixture, "Branch and Exchange", TAG) { InstructionData data = BranchAndExchange{ .rn = 3 }; - cpu.gpr[3] = 342890; + setr(3, 342890); exec(data); - CHECK(cpu.pc == 342890); + CHECK(getr(15) == 342890); } TEST_CASE_METHOD(CpuFixture, "Branch", TAG) { @@ -54,16 +27,16 @@ TEST_CASE_METHOD(CpuFixture, "Branch", TAG) { exec(data); - CHECK(cpu.pc == 3489748); - CHECK(cpu.gpr[14] == 0); + CHECK(getr(15) == 3489748); + CHECK(getr(14) == 0); // with link reset(); branch->link = true; exec(data); - CHECK(cpu.pc == 3489748); - CHECK(cpu.gpr[14] == 0 + INSTRUCTION_SIZE); + CHECK(getr(15) == 3489748); + CHECK(getr(14) == 0 + INSTRUCTION_SIZE); } TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { @@ -72,15 +45,15 @@ TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { }; Multiply* multiply = std::get_if(&data); - cpu.gpr[10] = 234912349; - cpu.gpr[11] = 124897; - cpu.gpr[3] = 99999; + setr(10, 234912349); + setr(11, 124897); + setr(3, 99999); { uint32_t result = 234912349ull * 124897ull & 0xFFFFFFFF; exec(data); - CHECK(cpu.gpr[5] == result); + CHECK(getr(5) == result); } // with accumulate @@ -89,7 +62,7 @@ TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { multiply->acc = true; exec(data); - CHECK(cpu.gpr[5] == result); + CHECK(getr(5) == result); } // with set @@ -98,19 +71,19 @@ TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { multiply->set = true; exec(data); - CHECK(cpu.gpr[5] == result); - CHECK(cpu.cpsr.n() == get_bit(result, 31)); + CHECK(getr(5) == result); + CHECK(psr().n() == get_bit(result, 31)); } // with set and zero { - cpu.gpr[10] = 0; - cpu.gpr[3] = 0; + setr(10, 0); + setr(3, 0); exec(data); - CHECK(cpu.gpr[5] == 0); - CHECK(cpu.cpsr.n() == false); - CHECK(cpu.cpsr.z() == true); + CHECK(getr(5) == 0); + CHECK(psr().n() == false); + CHECK(psr().z() == true); } } @@ -125,33 +98,33 @@ TEST_CASE_METHOD(CpuFixture, "Multiply Long", TAG) { MultiplyLong* multiply_long = std::get_if(&data); - cpu.gpr[10] = 234912349; - cpu.gpr[11] = 124897; + setr(10, 234912349); + setr(11, 124897); // unsigned { uint64_t result = 234912349ull * 124897ull; exec(data); - CHECK(cpu.gpr[3] == bit_range(result, 0, 31)); - CHECK(cpu.gpr[5] == bit_range(result, 32, 63)); + CHECK(getr(3) == bit_range(result, 0, 31)); + CHECK(getr(5) == bit_range(result, 32, 63)); } // signed { int64_t result = 234912349ll * -124897ll; - cpu.gpr[11] *= -1; + setr(11, getr(11) * -1); multiply_long->uns = false; exec(data); - CHECK(cpu.gpr[3] == static_cast(bit_range(result, 0, 31))); - CHECK(cpu.gpr[5] == static_cast(bit_range(result, 32, 63))); + CHECK(getr(3) == static_cast(bit_range(result, 0, 31))); + CHECK(getr(5) == static_cast(bit_range(result, 32, 63))); } // accumulate { - cpu.gpr[3] = 99999; - cpu.gpr[5] = -444333391; + setr(3, 99999); + setr(5, -444333391); int64_t result = 234912349ll * -124897ll + (99999ll | -444333391ll << 32); @@ -159,14 +132,14 @@ TEST_CASE_METHOD(CpuFixture, "Multiply Long", TAG) { multiply_long->acc = true; exec(data); - CHECK(cpu.gpr[3] == static_cast(bit_range(result, 0, 31))); - CHECK(cpu.gpr[5] == static_cast(bit_range(result, 32, 63))); + CHECK(getr(3) == static_cast(bit_range(result, 0, 31))); + CHECK(getr(5) == static_cast(bit_range(result, 32, 63))); } // set { - cpu.gpr[3] = 99999; - cpu.gpr[5] = -444333391; + setr(3, 99999); + setr(5, -444333391); int64_t result = 234912349ll * -124897ll + (99999ll | -444333391ll << 32); @@ -174,24 +147,24 @@ TEST_CASE_METHOD(CpuFixture, "Multiply Long", TAG) { multiply_long->set = true; exec(data); - CHECK(cpu.gpr[3] == static_cast(bit_range(result, 0, 31))); - CHECK(cpu.gpr[5] == static_cast(bit_range(result, 32, 63))); - CHECK(cpu.cpsr.n() == true); - CHECK(cpu.cpsr.z() == false); + CHECK(getr(3) == static_cast(bit_range(result, 0, 31))); + CHECK(getr(5) == static_cast(bit_range(result, 32, 63))); + CHECK(psr().n() == true); + CHECK(psr().z() == false); } // zero { - cpu.gpr[10] = 0; - cpu.gpr[5] = 0; - cpu.gpr[3] = 0; + setr(10, 0); + setr(5, 0); + setr(3, 0); exec(data); - CHECK(cpu.gpr[3] == 0); - CHECK(cpu.gpr[5] == 0); - CHECK(cpu.cpsr.n() == false); - CHECK(cpu.cpsr.z() == true); + CHECK(getr(3) == 0); + CHECK(getr(5) == 0); + CHECK(psr().n() == false); + CHECK(psr().z() == true); } } @@ -200,25 +173,24 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Swap", TAG) { SingleDataSwap{ .rm = 3, .rd = 4, .rn = 9, .byte = false }; SingleDataSwap* swap = std::get_if(&data); - cpu.gpr[9] = 0x3FED; - cpu.gpr[3] = 94235087; - cpu.gpr[3] = -259039045; - cpu.bus->write_word(cpu.gpr[9], 3241011111); + setr(9, 0x3FED); + setr(3, 94235087); + setr(3, -259039045); + bus.write_word(getr(9), 3241011111); SECTION("word") { exec(data); - CHECK(cpu.gpr[4] == 3241011111); - CHECK(cpu.bus->read_word(cpu.gpr[9]) == - static_cast(-259039045)); + CHECK(getr(4) == 3241011111); + CHECK(bus.read_word(getr(9)) == static_cast(-259039045)); } SECTION("byte") { swap->byte = true; exec(data); - CHECK(cpu.gpr[4] == (3241011111 & 0xFF)); - CHECK(cpu.bus->read_byte(cpu.gpr[9]) == + CHECK(getr(4) == (3241011111 & 0xFF)); + CHECK(bus.read_byte(getr(9)) == static_cast(-259039045 & 0xFF)); } } @@ -241,18 +213,18 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { .pre = true }; SingleDataTransfer* data_transfer = std::get_if(&data); - cpu.gpr[3] = 1596; - cpu.gpr[7] = 6; - cpu.gpr[5] = -911111; + setr(3, 1596); + setr(7, 6); + setr(5, -911111); // shifted register (immediate) { // 12768 + 6 - cpu.bus->write_word(12774, 95995); + bus.write_word(12774, 95995); exec(data); - CHECK(cpu.gpr[5] == 95995); - cpu.gpr[5] = 0; + CHECK(getr(5) == 95995); + setr(5, 0); } // shifted register (register) @@ -264,62 +236,62 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { .operand = 12, } }; - cpu.gpr[12] = 2; + setr(12, 2); // 6384 + 6 - cpu.bus->write_word(6390, 3948123487); + bus.write_word(6390, 3948123487); exec(data); - CHECK(cpu.gpr[5] == 3948123487); + CHECK(getr(5) == 3948123487); } // immediate { data_transfer->offset = static_cast(3489); // 6 + 3489 - cpu.bus->write_word(3495, 68795467); + bus.write_word(3495, 68795467); exec(data); - CHECK(cpu.gpr[5] == 68795467); + CHECK(getr(5) == 68795467); } // down { - cpu.gpr[7] = 18044; + setr(7, 18044); data_transfer->up = false; // 18044 - 3489 - cpu.bus->write_word(14555, 5949595); + bus.write_word(14555, 5949595); exec(data); - CHECK(cpu.gpr[5] == 5949595); + CHECK(getr(5) == 5949595); // no write back - CHECK(cpu.gpr[7] == 18044); + CHECK(getr(7) == 18044); } // write { data_transfer->write = true; - cpu.bus->write_word(14555, 967844); + bus.write_word(14555, 967844); exec(data); - CHECK(cpu.gpr[5] == 967844); + CHECK(getr(5) == 967844); // 18044 - 3489 - CHECK(cpu.gpr[7] == 14555); + CHECK(getr(7) == 14555); } // post { data_transfer->write = false; data_transfer->pre = false; - cpu.bus->write_word(14555, 61119); + bus.write_word(14555, 61119); exec(data); - CHECK(cpu.gpr[5] == 61119); + CHECK(getr(5) == 61119); // 14555 - 3489 - CHECK(cpu.gpr[7] == 11066); + CHECK(getr(7) == 11066); } // store @@ -328,21 +300,21 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { exec(data); - CHECK(cpu.bus->read_word(11066) == 61119); + CHECK(bus.read_word(11066) == 61119); // 11066 - 3489 - CHECK(cpu.gpr[7] == 7577); + CHECK(getr(7) == 7577); } // r15 as rn { - data_transfer->rn = cpu.PC_INDEX; - cpu.gpr[15] = 7577; + data_transfer->rn = 15; + setr(15, 7577); exec(data); - CHECK(cpu.bus->read_word(7577 - 2 * INSTRUCTION_SIZE) == 61119); + CHECK(bus.read_word(7577 - 2 * INSTRUCTION_SIZE) == 61119); // 7577 - 3489 - CHECK(cpu.gpr[15] == 4088 - 2 * INSTRUCTION_SIZE); + CHECK(getr(15) == 4088 - 2 * INSTRUCTION_SIZE); // cleanup data_transfer->rn = 7; @@ -351,31 +323,31 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { // r15 as rd { // 4088 - data_transfer->rd = cpu.PC_INDEX; - cpu.gpr[15] = 444444; + data_transfer->rd = 15; + setr(15, 444444); exec(data); - CHECK(cpu.bus->read_word(7577 + INSTRUCTION_SIZE) == 444444); + CHECK(bus.read_word(7577 + INSTRUCTION_SIZE) == 444444); // 7577 - 3489 - CHECK(cpu.gpr[7] == 4088 + INSTRUCTION_SIZE); + CHECK(getr(7) == 4088 + INSTRUCTION_SIZE); // cleanup data_transfer->rd = 5; - cpu.gpr[7] -= INSTRUCTION_SIZE; + setr(7, getr(7) - INSTRUCTION_SIZE); } // byte { data_transfer->byte = true; - cpu.gpr[5] = 458267584; + setr(5, 458267584); exec(data); - CHECK(cpu.bus->read_word(4088) == (458267584 & 0xFF)); + CHECK(bus.read_word(4088) == (458267584 & 0xFF)); // 4088 - 3489 - CHECK(cpu.gpr[7] == 599); + CHECK(getr(7) == 599); } } @@ -392,17 +364,17 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { .pre = true }; HalfwordTransfer* hw_transfer = std::get_if(&data); - cpu.gpr[12] = 8404; - cpu.gpr[11] = 459058287; - cpu.gpr[10] = 900; + setr(12, 8404); + setr(11, 459058287); + setr(10, 900); // register offset { // 900 + 8404 - cpu.bus->write_word(9304, 3948123487); + bus.write_word(9304, 3948123487); exec(data); - CHECK(cpu.gpr[11] == (3948123487 & 0xFFFF)); + CHECK(getr(11) == (3948123487 & 0xFFFF)); } // immediate offset @@ -410,49 +382,49 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { hw_transfer->imm = true; hw_transfer->offset = 167; // 900 + 167 - cpu.bus->write_word(1067, 594633302); + bus.write_word(1067, 594633302); exec(data); - CHECK(cpu.gpr[11] == (594633302 & 0xFFFF)); + CHECK(getr(11) == (594633302 & 0xFFFF)); } // down { hw_transfer->up = false; // 900 - 167 - cpu.bus->write_word(733, 222221); + bus.write_word(733, 222221); exec(data); - CHECK(cpu.gpr[11] == (222221 & 0xFFFF)); + CHECK(getr(11) == (222221 & 0xFFFF)); // no write back - CHECK(cpu.gpr[10] == 900); + CHECK(getr(10) == 900); } // write { hw_transfer->write = true; // 900 - 167 - cpu.bus->write_word(733, 100000005); + bus.write_word(733, 100000005); exec(data); - CHECK(cpu.gpr[11] == (100000005 & 0xFFFF)); + CHECK(getr(11) == (100000005 & 0xFFFF)); // 900 - 167 - CHECK(cpu.gpr[10] == 733); + CHECK(getr(10) == 733); } // post { hw_transfer->pre = false; hw_transfer->write = false; - cpu.bus->write_word(733, 6111909); + bus.write_word(733, 6111909); exec(data); - CHECK(cpu.gpr[11] == (6111909 & 0xFFFF)); + CHECK(getr(11) == (6111909 & 0xFFFF)); // 733 - 167 - CHECK(cpu.gpr[10] == 566); + CHECK(getr(10) == 566); } // store @@ -461,22 +433,22 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { exec(data); - CHECK(cpu.bus->read_halfword(566) == (6111909 & 0xFFFF)); + CHECK(bus.read_halfword(566) == (6111909 & 0xFFFF)); // 566 - 167 - CHECK(cpu.gpr[10] == 399); + CHECK(getr(10) == 399); } // r15 as rn { - hw_transfer->rn = cpu.PC_INDEX; - cpu.gpr[15] = 399; + hw_transfer->rn = 15; + setr(15, 399); exec(data); - CHECK(cpu.bus->read_halfword(399 - 2 * INSTRUCTION_SIZE) == + CHECK(bus.read_halfword(399 - 2 * INSTRUCTION_SIZE) == (6111909 & 0xFFFF)); // 399 - 167 - CHECK(cpu.gpr[15] == 232 - 2 * INSTRUCTION_SIZE); + CHECK(getr(15) == 232 - 2 * INSTRUCTION_SIZE); // cleanup hw_transfer->rn = 10; @@ -484,43 +456,43 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { // r15 as rd { - hw_transfer->rd = cpu.PC_INDEX; - cpu.gpr[15] = 224; + hw_transfer->rd = 15; + setr(15, 224); exec(data); - CHECK(cpu.bus->read_halfword(399 + INSTRUCTION_SIZE) == 224); + CHECK(bus.read_halfword(399 + INSTRUCTION_SIZE) == 224); // 399 - 167 - CHECK(cpu.gpr[10] == 232 + INSTRUCTION_SIZE); + CHECK(getr(10) == 232 + INSTRUCTION_SIZE); // cleanup hw_transfer->rd = 11; - cpu.gpr[10] = 399; + setr(10, 399); } // signed halfword { hw_transfer->load = true; hw_transfer->sign = true; - cpu.bus->write_halfword(399, -12345); + bus.write_halfword(399, -12345); exec(data); - CHECK(cpu.gpr[11] == static_cast(-12345)); + CHECK(getr(11) == static_cast(-12345)); // 399 - 167 - CHECK(cpu.gpr[10] == 232); + CHECK(getr(10) == 232); } // signed byte { hw_transfer->half = false; - cpu.bus->write_byte(232, -56); + bus.write_byte(232, -56); exec(data); - CHECK(cpu.gpr[11] == static_cast(-56)); + CHECK(getr(11) == static_cast(-56)); // 232 - 167 - CHECK(cpu.gpr[10] == 65); + CHECK(getr(10) == 65); } } @@ -538,84 +510,84 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { // load SECTION("load") { // populate memory - cpu.bus->write_word(3448, 38947234); - cpu.bus->write_word(3452, 237164); - cpu.bus->write_word(3456, 679785111); - cpu.bus->write_word(3460, 905895898); - cpu.bus->write_word(3464, 131313333); - cpu.bus->write_word(3468, 131); - cpu.bus->write_word(3472, 989231); - cpu.bus->write_word(3476, 6); + bus.write_word(3448, 38947234); + bus.write_word(3452, 237164); + bus.write_word(3456, 679785111); + bus.write_word(3460, 905895898); + bus.write_word(3464, 131313333); + bus.write_word(3468, 131); + bus.write_word(3472, 989231); + bus.write_word(3476, 6); - auto checker = [](decltype(cpu.gpr)& gpr, uint32_t rnval = 0) { - CHECK(gpr[0] == 237164); - CHECK(gpr[1] == 0); - CHECK(gpr[2] == 0); - CHECK(gpr[3] == 0); - CHECK(gpr[4] == 0); - CHECK(gpr[5] == 0); - CHECK(gpr[6] == 679785111); - CHECK(gpr[7] == 905895898); - CHECK(gpr[8] == 131313333); - CHECK(gpr[9] == 0); - CHECK(gpr[10] == rnval); - CHECK(gpr[11] == 131); - CHECK(gpr[12] == 0); - CHECK(gpr[13] == 989231); - CHECK(gpr[14] == 0); - CHECK(gpr[15] == 6); + auto checker = [this](uint32_t rnval = 0) { + CHECK(getr(0) == 237164); + CHECK(getr(1) == 0); + CHECK(getr(2) == 0); + CHECK(getr(3) == 0); + CHECK(getr(4) == 0); + CHECK(getr(5) == 0); + CHECK(getr(6) == 679785111); + CHECK(getr(7) == 905895898); + CHECK(getr(8) == 131313333); + CHECK(getr(9) == 0); + CHECK(getr(10) == rnval); + CHECK(getr(11) == 131); + CHECK(getr(12) == 0); + CHECK(getr(13) == 989231); + CHECK(getr(14) == 0); + CHECK(getr(15) == 6); for (uint8_t i = 0; i < 16; i++) { - gpr[i] = 0; + setr(i, 0); } }; - cpu.gpr[10] = 3448; + setr(10, 3448); exec(data); - checker(cpu.gpr, 3448); + checker(3448); // with write - cpu.gpr[10] = 3448; + setr(10, 3448); block_transfer->write = true; exec(data); - checker(cpu.gpr, 3448 + INSTRUCTION_SIZE); + checker(3448 + INSTRUCTION_SIZE); // decrement block_transfer->write = false; block_transfer->up = false; // adjust rn - cpu.gpr[10] = 3480; + setr(10, 3480); exec(data); - checker(cpu.gpr, 3480); + checker(3480); // with write - cpu.gpr[10] = 3480; + setr(10, 3480); block_transfer->write = true; exec(data); - checker(cpu.gpr, 3480 - INSTRUCTION_SIZE); + checker(3480 - INSTRUCTION_SIZE); // post increment block_transfer->write = false; block_transfer->up = true; block_transfer->pre = false; // adjust rn - cpu.gpr[10] = 3452; + setr(10, 3452); exec(data); - checker(cpu.gpr, 3452 + INSTRUCTION_SIZE); + checker(3452 + INSTRUCTION_SIZE); // post decrement block_transfer->up = false; // adjust rn - cpu.gpr[10] = 3476; + setr(10, 3476); exec(data); - checker(cpu.gpr, 3476 - INSTRUCTION_SIZE); + checker(3476 - INSTRUCTION_SIZE); // with s bit cpu.chg_mode(Mode::Fiq); block_transfer->s = true; - CHECK(cpu.cpsr.raw() != cpu.spsr.raw()); + CHECK(psr().raw() != psr(true).raw()); exec(data); - CHECK(cpu.cpsr.raw() == cpu.spsr.raw()); + CHECK(psr().raw() == psr(true).raw()); } // store @@ -623,28 +595,28 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { block_transfer->load = false; // populate registers - cpu.gpr[0] = 237164; - cpu.gpr[6] = 679785111; - cpu.gpr[7] = 905895898; - cpu.gpr[8] = 131313333; - cpu.gpr[11] = 131; - cpu.gpr[13] = 989231; - cpu.gpr[15] = 6; + setr(0, 237164); + setr(6, 679785111); + setr(7, 905895898); + setr(8, 131313333); + setr(11, 131); + setr(13, 989231); + setr(15, 6); auto checker = [this]() { - CHECK(cpu.bus->read_word(5548) == 237164); - CHECK(cpu.bus->read_word(5552) == 679785111); - CHECK(cpu.bus->read_word(5556) == 905895898); - CHECK(cpu.bus->read_word(5560) == 131313333); - CHECK(cpu.bus->read_word(5564) == 131); - CHECK(cpu.bus->read_word(5568) == 989231); - CHECK(cpu.bus->read_word(5572) == 6); + 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); + CHECK(bus.read_word(5568) == 989231); + CHECK(bus.read_word(5572) == 6); for (uint8_t i = 0; i < 8; i++) - cpu.bus->write_word(5548 + i * 4, 0); + bus.write_word(5548 + i * 4, 0); }; - cpu.gpr[10] = 5544; // base + setr(10, 5544); // base exec(data); checker(); @@ -652,7 +624,7 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { block_transfer->write = false; block_transfer->up = false; // adjust rn - cpu.gpr[10] = 5576; + setr(10, 5576); exec(data); checker(); @@ -660,14 +632,14 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { block_transfer->up = true; block_transfer->pre = false; // adjust rn - cpu.gpr[10] = 5548; + setr(10, 5548); exec(data); checker(); // post decrement block_transfer->up = false; // adjust rn - cpu.gpr[10] = 5572; + setr(10, 5572); exec(data); checker(); @@ -676,7 +648,7 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { block_transfer->s = true; cpu.chg_mode(Mode::Supervisor); // User's R13 is different (unset at this point) - CHECK(cpu.bus->read_word(5568) == 0); + CHECK(bus.read_word(5568) == 0); exec(data); } } @@ -691,59 +663,58 @@ TEST_CASE_METHOD(CpuFixture, "PSR Transfer", TAG) { PsrTransfer* psr_transfer = std::get_if(&data); SECTION("MRS") { - cpu.gpr[12] = 12389398; - CHECK(cpu.cpsr.raw() != cpu.gpr[12]); + setr(12, 12389398); + + CHECK(psr().raw() != getr(12)); exec(data); - CHECK(cpu.cpsr.raw() == cpu.gpr[12]); + CHECK(psr().raw() == getr(12)); psr_transfer->spsr = true; // with SPSR - CHECK(cpu.spsr.raw() != cpu.gpr[12]); + CHECK(psr(true).raw() != getr(12)); exec(data); - CHECK(cpu.spsr.raw() == cpu.gpr[12]); + CHECK(psr(true).raw() == getr(12)); } // MSR SECTION("MSR") { psr_transfer->type = PsrTransfer::Type::Msr; - - cpu.gpr[12] = 0; // go to the reserved bits - cpu.gpr[12] |= 16556 << 8; + setr(12, 16556u << 8); - CHECK(cpu.cpsr.raw() != cpu.gpr[12]); + CHECK(psr().raw() != getr(12)); exec(data); - CHECK(cpu.cpsr.raw() == cpu.gpr[12]); + CHECK(psr().raw() == getr(12)); psr_transfer->spsr = true; // with SPSR - CHECK(cpu.spsr.raw() != cpu.gpr[12]); + CHECK(psr(true).raw() != getr(12)); exec(data); - CHECK(cpu.spsr.raw() == cpu.gpr[12]); + CHECK(psr(true).raw() == getr(12)); } // MSR_flg SECTION("MSR_flg") { psr_transfer->type = PsrTransfer::Type::Msr_flg; - cpu.gpr[12] = 1490352945; + setr(12, 1490352945); // go to the reserved bits exec(data); - CHECK(cpu.cpsr.n() == get_bit(1490352945, 31)); - CHECK(cpu.cpsr.z() == get_bit(1490352945, 30)); - CHECK(cpu.cpsr.c() == get_bit(1490352945, 29)); - CHECK(cpu.cpsr.v() == get_bit(1490352945, 28)); + CHECK(psr().n() == get_bit(1490352945, 31)); + CHECK(psr().z() == get_bit(1490352945, 30)); + CHECK(psr().c() == get_bit(1490352945, 29)); + CHECK(psr().v() == get_bit(1490352945, 28)); // with SPSR and immediate operand psr_transfer->operand = 99333394; psr_transfer->imm = true; psr_transfer->spsr = true; exec(data); - CHECK(cpu.spsr.n() == get_bit(9933394, 31)); - CHECK(cpu.spsr.z() == get_bit(9933394, 30)); - CHECK(cpu.spsr.c() == get_bit(9933394, 29)); - CHECK(cpu.spsr.v() == get_bit(9933394, 28)); + CHECK(psr(true).n() == get_bit(9933394, 31)); + CHECK(psr(true).z() == get_bit(9933394, 30)); + CHECK(psr(true).c() == get_bit(9933394, 29)); + CHECK(psr(true).v() == get_bit(9933394, 28)); } } @@ -765,15 +736,15 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { DataProcessing* processing = std::get_if(&data); // operand 1 - cpu.gpr[7] = -28717; + setr(7, -28717); // AND with shifted register (imediate) { // rm - cpu.gpr[3] = 1596; + setr(3, 1596); exec(data); // -28717 & 12768 - CHECK(cpu.gpr[5] == 448); + CHECK(getr(5) == 448); } // AND with shifted register (register) @@ -785,63 +756,72 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { .operand = 12, } }; // rm - cpu.gpr[3] = 1596; + setr(3, 1596); // rs - cpu.gpr[12] = 2; + setr(12, 2); exec(data); // -28717 & 6384 - CHECK(cpu.gpr[5] == 2256); + CHECK(getr(5) == 2256); } // same as above but with rn (oprerand 1) = 15 { - processing->rn = cpu.PC_INDEX; - cpu.gpr[15] = -2871; + processing->rn = 15; + setr(15, -2871); exec(data); // (-2871 + INSTRUCTION_SIZE) & 6384 - CHECK(cpu.gpr[5] == ((-2871 + INSTRUCTION_SIZE) & 6384)); + CHECK(getr(5) == ((-2871 + INSTRUCTION_SIZE) & 6384)); // cleanup processing->rn = 7; } auto reset_flags = [this]() { - cpu.cpsr.set_n(false); - cpu.cpsr.set_z(false); - cpu.cpsr.set_v(false); - cpu.cpsr.set_c(false); + Psr cpsr = psr(); + cpsr.set_n(false); + cpsr.set_z(false); + cpsr.set_v(false); + cpsr.set_c(false); + set_psr(cpsr); }; auto flags = [this, reset_flags](bool n, bool z, bool v, bool c) { - CHECK(cpu.cpsr.n() == n); - CHECK(cpu.cpsr.z() == z); - CHECK(cpu.cpsr.v() == v); - CHECK(cpu.cpsr.c() == c); + CHECK(psr().n() == n); + CHECK(psr().z() == z); + CHECK(psr().v() == v); + CHECK(psr().c() == c); reset_flags(); }; // immediate operand processing->operand = static_cast(54924809); // rs - cpu.gpr[12] = 2; - cpu.gpr[5] = 0; + setr(12, 2); + setr(5, 0); reset_flags(); SECTION("AND (with condition check)") { + Psr cpsr = psr(); + processing->opcode = OpCode::AND; - cpu.cpsr.set_z(false); + + cpsr.set_z(false); + set_psr(cpsr); exec(data, Condition::EQ); // condition is false - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); - cpu.cpsr.set_z(true); + cpsr = psr(); + cpsr.set_z(true); + set_psr(cpsr); + exec(data, Condition::EQ); exec(data, Condition::EQ); // -28717 & 54924809 // condition is true now - CHECK(cpu.gpr[5] == 54920705); + CHECK(getr(5) == 54920705); // check set flags flags(false, false, false, false); @@ -854,26 +834,32 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 & 54924809 - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(false, false, false, false); } SECTION("EOR (with condition check)") { + Psr cpsr = psr(); + processing->opcode = OpCode::EOR; - cpu.cpsr.set_c(true); + + cpsr.set_c(true); + set_psr(cpsr); exec(data, Condition::CC); // condition fails - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); - cpu.cpsr.set_c(false); + cpsr = psr(); + cpsr.set_c(false); + set_psr(cpsr); exec(data, Condition::CC); // -28717 ^ 54924809 // condition is true now - CHECK(cpu.gpr[5] == 4240021978); + CHECK(getr(5) == 4240021978); // check set flags flags(true, false, false, false); @@ -883,7 +869,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 ^ -28717 - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(false, true, false, false); @@ -895,7 +881,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 ^ 54924809 - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(true, false, false, false); @@ -906,7 +892,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 - 54924809 - CHECK(cpu.gpr[5] == static_cast(-54953526)); + CHECK(getr(5) == static_cast(-54953526)); // check set flags flags(true, false, false, true); @@ -916,7 +902,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 - (-28717) - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(false, true, false, true); @@ -928,7 +914,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 - 54924809 - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(true, false, false, true); @@ -939,7 +925,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // +28717 + 54924809 - CHECK(cpu.gpr[5] == 54953526); + CHECK(getr(5) == 54953526); // check set flags flags(false, false, false, false); @@ -950,7 +936,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 + 54924809 - CHECK(cpu.gpr[5] == 54896092); + CHECK(getr(5) == 54896092); // check set flags flags(false, false, false, false); @@ -960,18 +946,18 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 + 28717 - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(false, true, false, false); // test overflow flag processing->operand = static_cast((1u << 31) - 1); - cpu.gpr[7] = (1u << 31) - 1; + setr(7, (1u << 31) - 1); exec(data); - CHECK(cpu.gpr[5] == (1ull << 32) - 2); + CHECK(getr(5) == (1ull << 32) - 2); // check set flags flags(true, false, true, false); @@ -983,43 +969,52 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 + 54924809 - CHECK(cpu.gpr[5] == 0); + CHECK(getr(5) == 0); // check set flags flags(false, false, false, false); } SECTION("ADC") { + Psr cpsr = psr(); processing->opcode = OpCode::ADC; - cpu.cpsr.set_c(true); + + cpsr.set_c(true); + set_psr(cpsr); exec(data); // -28717 + 54924809 + carry - CHECK(cpu.gpr[5] == 54896093); + CHECK(getr(5) == 54896093); // check set flags flags(false, false, false, false); } SECTION("SBC") { + Psr cpsr = psr(); processing->opcode = OpCode::SBC; - cpu.cpsr.set_c(false); + + cpsr.set_c(false); + set_psr(cpsr); exec(data); // -28717 - 54924809 + carry - 1 - CHECK(cpu.gpr[5] == static_cast(-54953527)); + CHECK(getr(5) == static_cast(-54953527)); // check set flags flags(true, false, false, false); } SECTION("RSC") { + Psr cpsr = psr(); processing->opcode = OpCode::RSC; - cpu.cpsr.set_c(false); + + cpsr.set_c(false); + set_psr(cpsr); exec(data); // +28717 +54924809 + carry - 1 - CHECK(cpu.gpr[5] == 54953525); + CHECK(getr(5) == 54953525); // check set flags flags(false, false, false, false); @@ -1030,7 +1025,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 | 54924809 - CHECK(cpu.gpr[5] == static_cast(-24613)); + CHECK(getr(5) == static_cast(-24613)); // check set flags flags(true, false, false, false); @@ -1041,7 +1036,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // -28717 & ~54924809 - CHECK(cpu.gpr[5] == static_cast(-54949422)); + CHECK(getr(5) == static_cast(-54949422)); // check set flags flags(true, false, false, false); @@ -1052,7 +1047,7 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { exec(data); // ~54924809 - CHECK(cpu.gpr[5] == static_cast(-54924810)); + CHECK(getr(5) == static_cast(-54924810)); // check set flags flags(true, false, false, false); @@ -1060,17 +1055,17 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { SECTION("R15 as destination") { processing->opcode = OpCode::MVN; - processing->rd = cpu.PC_INDEX; - cpu.gpr[15] = 0; - CHECK(cpu.spsr.raw() != cpu.cpsr.raw()); + processing->rd = 15; + setr(15, 0); + CHECK(psr(true).raw() != psr().raw()); exec(data); // ~54924809 - CHECK(cpu.gpr[15] == static_cast(-54924810)); + CHECK(getr(15) == static_cast(-54924810)); // flags are not set flags(false, false, false, false); - CHECK(cpu.spsr.raw() == cpu.cpsr.raw()); + CHECK(psr(true).raw() == psr().raw()); } } diff --git a/tests/cpu/arm/fixture.cc b/tests/cpu/arm/fixture.cc new file mode 100644 index 0000000..b7a2272 --- /dev/null +++ b/tests/cpu/arm/fixture.cc @@ -0,0 +1,96 @@ +#include "fixture.hh" + +Psr +CpuFixture::psr(bool spsr) { + Psr psr(0); + CpuImpl tmp = cpu; + arm::Instruction instruction( + Condition::AL, + arm::PsrTransfer{ .operand = 0, + .spsr = spsr, + .type = arm::PsrTransfer::Type::Mrs, + .imm = false }); + + instruction.exec(tmp); + + psr.set_all(getr_(0, tmp)); + return psr; +} + +void +CpuFixture::set_psr(Psr psr, bool spsr) { + // R0 + uint32_t old = getr(0); + + setr(0, psr.raw()); + + arm::Instruction instruction( + Condition::AL, + arm::PsrTransfer{ .operand = 0, + .spsr = spsr, + .type = arm::PsrTransfer::Type::Msr, + .imm = false }); + + instruction.exec(cpu); + + setr(0, old); +} + +// We need these workarounds to just use the public API and not private +// fields. Assuming that these work correctly is necessary. Besides, all it +// matters is that the public API is correct. +uint32_t +CpuFixture::getr_(uint8_t r, CpuImpl& cpu) { + size_t addr = 13000; + size_t offset = r == 15 ? 4 : 0; + uint32_t word = bus.read_word(addr + offset); + CpuImpl tmp = cpu; + uint32_t ret = 0xFFFFFFFF; + uint8_t base = r ? 0 : 1; + + // set R0/R1 = 0 + arm::Instruction zero( + Condition::AL, + arm::DataProcessing{ .operand = 0u, + .rd = base, + .rn = 0, + .set = false, + .opcode = arm::DataProcessing::OpCode::MOV }); + + // get register + arm::Instruction get( + Condition::AL, + arm::SingleDataTransfer{ .offset = static_cast(addr), + .rd = r, + .rn = base, + .load = false, + .write = false, + .byte = false, + .up = true, + .pre = true }); + + zero.exec(tmp); + get.exec(tmp); + + addr += offset; + + ret = bus.read_word(addr); + + bus.write_word(addr, word); + + return ret; +} + +void +CpuFixture::setr_(uint8_t r, uint32_t value, CpuImpl& cpu) { + // set register + arm::Instruction set( + Condition::AL, + arm::DataProcessing{ .operand = value, + .rd = r, + .rn = 0, + .set = false, + .opcode = arm::DataProcessing::OpCode::MOV }); + + set.exec(cpu); +} diff --git a/tests/cpu/arm/fixture.hh b/tests/cpu/arm/fixture.hh new file mode 100644 index 0000000..46d6287 --- /dev/null +++ b/tests/cpu/arm/fixture.hh @@ -0,0 +1,37 @@ +#include "cpu/cpu-impl.hh" + +using namespace matar; + +class CpuFixture { + public: + CpuFixture() + : bus(Memory(std::array(), + std::vector(Header::HEADER_SIZE))) + , cpu(bus) {} + + protected: + void exec(arm::InstructionData data, Condition condition = Condition::AL) { + arm::Instruction instruction(condition, data); + instruction.exec(cpu); + } + + void reset(uint32_t value = 0) { setr(15, value + 8); } + + uint32_t getr(uint8_t r) { return getr_(r, cpu); } + + void setr(uint8_t r, uint32_t value) { setr_(r, value, cpu); } + + Psr psr(bool spsr = false); + + void set_psr(Psr psr, bool spsr = false); + + Bus bus; + CpuImpl cpu; + + private: + // hack to get a register + uint32_t getr_(uint8_t r, CpuImpl& cpu); + + // hack to set a register + void setr_(uint8_t r, uint32_t value, CpuImpl& cpu); +}; diff --git a/tests/cpu/arm/meson.build b/tests/cpu/arm/meson.build index 840fc8f..0aa1cfd 100644 --- a/tests/cpu/arm/meson.build +++ b/tests/cpu/arm/meson.build @@ -1,4 +1,5 @@ tests_sources += files( + 'fixture.cc', 'instruction.cc', 'exec.cc' ) \ No newline at end of file