From 1eb4a9545b3a5aaa6af6915dc30d3484169de93c Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Tue, 19 Sep 2023 08:58:11 +0530 Subject: [PATCH] tests: complete exec tests (for now) Signed-off-by: Amneesh Singh --- .github/workflows/main.yml | 5 + apps/target/main.cc | 2 +- result-dev | 1 - src/cpu/arm/exec.cc | 215 ++++++++++-------------- src/meson.build | 4 +- tests/cpu/arm/exec.cc | 325 ++++++++++++++++++++++++++++++++++++- tests/meson.build | 2 +- 7 files changed, 411 insertions(+), 143 deletions(-) delete mode 120000 result-dev diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b0bafa1..d6b0f1a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,11 @@ jobs: extra_nix_config: | auto-optimise-store = true experimental-features = nix-command flakes + - uses: cachix/cachix-action@v12 + with: + name: pain + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - name: setup run: nix develop -c meson setup $BUILDDIR diff --git a/apps/target/main.cc b/apps/target/main.cc index 7116a5b..2a15301 100644 --- a/apps/target/main.cc +++ b/apps/target/main.cc @@ -90,7 +90,7 @@ main(int argc, const char* argv[]) { Cpu cpu(bus); while (true) { cpu.step(); - sleep(1); + sleep(2); } } catch (const std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; diff --git a/result-dev b/result-dev deleted file mode 120000 index 35d25b5..0000000 --- a/result-dev +++ /dev/null @@ -1 +0,0 @@ -/nix/store/6p64j7f5xl7rzf1i1avdn18nq9j0yisw-matar-dev \ No newline at end of file diff --git a/src/cpu/arm/exec.cc b/src/cpu/arm/exec.cc index 88b67e7..99d7f90 100644 --- a/src/cpu/arm/exec.cc +++ b/src/cpu/arm/exec.cc @@ -6,9 +6,10 @@ using namespace logger; void CpuImpl::exec_arm(const arm::Instruction instruction) { - auto cond = instruction.condition; - auto data = instruction.data; + Condition cond = instruction.condition; + arm::InstructionData data = instruction.data; + debug(cpsr.condition(cond)); if (!cpsr.condition(cond)) { return; } @@ -390,11 +391,6 @@ CpuImpl::exec_arm(const arm::Instruction instruction) { uint32_t result = 0; - bool overflow = cpsr.v(); - bool carry = cpsr.c(); - bool negative = cpsr.n(); - bool zero = cpsr.z(); - if (const uint32_t* immediate = std::get_if(&data.operand)) { op_2 = *immediate; @@ -419,144 +415,96 @@ CpuImpl::exec_arm(const arm::Instruction instruction) { op_1 += INSTRUCTION_SIZE; } + bool overflow = cpsr.v(); + bool carry = 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) { - case OpCode::AND: { + case OpCode::AND: + case OpCode::TST: result = op_1 & op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::EOR: { - result = op_1 ^ op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::SUB: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - result = op_1 - op_2; - negative = get_bit(result, 31); - carry = op_1 < op_2; - overflow = s1 != s2 && s2 == negative; - } break; - case OpCode::RSB: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - result = op_2 - op_1; - - negative = get_bit(result, 31); - carry = op_2 < op_1; - overflow = s1 != s2 && s1 == negative; - } break; - case OpCode::ADD: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - - // result_ is 33 bits - uint64_t result_ = op_2 + op_1; - result = result_ & 0xFFFFFFFF; - - negative = get_bit(result, 31); - carry = get_bit(result_, 32); - overflow = s1 == s2 && s1 != negative; - } break; - case OpCode::ADC: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - - uint64_t result_ = op_2 + op_1 + carry; - result = result_ & 0xFFFFFFFF; - - negative = get_bit(result, 31); - carry = get_bit(result_, 32); - overflow = s1 == s2 && s1 != negative; - } break; - case OpCode::SBC: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - - uint64_t result_ = op_1 - op_2 + carry - 1; - result = result_ & 0xFFFFFFFF; - - negative = get_bit(result, 31); - carry = get_bit(result_, 32); - overflow = s1 != s2 && s2 == negative; - } break; - case OpCode::RSC: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - - uint64_t result_ = op_1 - op_2 + carry - 1; - result = result_ & 0xFFFFFFFF; - - negative = get_bit(result, 31); - carry = get_bit(result_, 32); - overflow = s1 != s2 && s1 == negative; - } break; - case OpCode::TST: { result = op_1 & op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::TEQ: { + break; + case OpCode::EOR: + case OpCode::TEQ: result = op_1 ^ op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::CMP: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - - result = op_1 - op_2; - - negative = get_bit(result, 31); - carry = op_1 < op_2; - overflow = s1 != s2 && s2 == negative; - } break; - case OpCode::CMN: { - bool s1 = get_bit(op_1, 31); - bool s2 = get_bit(op_2, 31); - - uint64_t result_ = op_2 + op_1; - result = result_ & 0xFFFFFFFF; - - negative = get_bit(result, 31); - carry = get_bit(result_, 32); - overflow = s1 == s2 && s1 != negative; - } break; - case OpCode::ORR: { + break; + case OpCode::SUB: + case OpCode::CMP: + result = sub(op_1, op_2); + break; + case OpCode::RSB: + result = sub(op_2, op_1); + break; + case OpCode::ADD: + case OpCode::CMN: + result = add(op_1, op_2); + break; + case OpCode::ADC: + result = add(op_1, op_2, carry); + break; + case OpCode::SBC: + result = sbc(op_1, op_2, carry); + break; + case OpCode::RSC: + result = sbc(op_2, op_1, carry); + break; + case OpCode::ORR: result = op_1 | op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::MOV: { + break; + case OpCode::MOV: result = op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::BIC: { + break; + case OpCode::BIC: result = op_1 & ~op_2; - - negative = get_bit(result, 31); - } break; - case OpCode::MVN: { + break; + case OpCode::MVN: result = ~op_2; - - negative = get_bit(result, 31); - } break; + break; } - zero = result == 0; - - debug(carry); - debug(overflow); - debug(zero); - debug(negative); - - auto set_conditions = [this, carry, overflow, negative, zero]() { + auto set_conditions = [this, carry, overflow, result]() { cpsr.set_c(carry); cpsr.set_v(overflow); - cpsr.set_n(negative); - cpsr.set_z(zero); + cpsr.set_n(get_bit(result, 31)); + cpsr.set_z(result == 0); }; if (data.set) { @@ -564,6 +512,7 @@ CpuImpl::exec_arm(const arm::Instruction instruction) { if (cpsr.mode() == Mode::User) log_error("Running {} in User mode", typeid(data).name()); + spsr = cpsr; } else { set_conditions(); } diff --git a/src/meson.build b/src/meson.build index d8865d8..0c5f118 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,12 +6,12 @@ lib_sources = files( subdir('cpu') fmt = dependency('fmt', version : '>=10.1.0') -lib = library( +lib = static_library( meson.project_name(), lib_sources, dependencies: [fmt], include_directories: inc, - install: true + install: true, ) import('pkgconfig').generate(lib) diff --git a/tests/cpu/arm/exec.cc b/tests/cpu/arm/exec.cc index 424445b..0cc3026 100644 --- a/tests/cpu/arm/exec.cc +++ b/tests/cpu/arm/exec.cc @@ -2,6 +2,7 @@ #include "cpu/utility.hh" #include "util/bits.hh" #include +#include #include class CpuFixture { @@ -21,6 +22,12 @@ class CpuFixture { } CpuImpl cpu; + + private: + class Null : public std::streambuf { + public: + int overflow(int c) override { return c; } + }; }; #define TAG "arm execution" @@ -230,17 +237,18 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { .pre = true }; SingleDataTransfer* data_transfer = std::get_if(&data); - cpu.gpr[3] = 1596; - cpu.gpr[12] = 3; - cpu.gpr[7] = 6; - cpu.gpr[5] = -911111; + cpu.gpr[3] = 1596; + cpu.gpr[7] = 6; + cpu.gpr[5] = -911111; // shifted register (immediate) { + // 12768 + 6 cpu.bus->write_word(12774, 95995); exec(data); CHECK(cpu.gpr[5] == 95995); + cpu.gpr[5] = 0; } // shifted register (register) @@ -252,7 +260,9 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { .operand = 12, } }; - cpu.bus->write_word(12774, 3948123487); + cpu.gpr[12] = 2; + // 6384 + 6 + cpu.bus->write_word(6390, 3948123487); exec(data); CHECK(cpu.gpr[5] == 3948123487); @@ -733,4 +743,309 @@ TEST_CASE_METHOD(CpuFixture, "PSR Transfer", TAG) { } } +TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { + InstructionData data = + DataProcessing{ .operand = Shift{ .rm = 3, + .data = + ShiftData{ + .type = ShiftType::ROR, + .immediate = true, + .operand = 29, + } }, + .rd = 5, + .rn = 7, + .set = true, + .opcode = OpCode::AND }; + DataProcessing* processing = std::get_if(&data); + + // operand 1 + cpu.gpr[7] = -28717; + + // AND with shifted register (imediate) + { + // rm + cpu.gpr[3] = 1596; + exec(data); + // -28717 & 12768 + CHECK(cpu.gpr[5] == 448); + } + + // AND with shifted register (register) + { + processing->operand = Shift{ .rm = 3, + .data = ShiftData{ + .type = ShiftType::LSL, + .immediate = false, + .operand = 12, + } }; + // rm + cpu.gpr[3] = 1596; + // rs + cpu.gpr[12] = 2; + exec(data); + // -28717 & 6384 + CHECK(cpu.gpr[5] == 2256); + } + + // same as above but with rn (oprerand 1) = 15 + { + processing->rn = 15; + cpu.gpr[15] = -2871; + exec(data); + + // (-2871 + INSTRUCTION_SIZE) & 6384 + CHECK(cpu.gpr[5] == ((-2871 + INSTRUCTION_SIZE) & 6384)); + + // cleanup + processing->rn = 7; + } + + auto flags = [this](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); + + cpu.cpsr.set_n(false); + cpu.cpsr.set_z(false); + cpu.cpsr.set_v(false); + cpu.cpsr.set_c(false); + }; + + // immediate operand + processing->operand = static_cast(54924809); + // rs + cpu.gpr[12] = 2; + cpu.gpr[5] = 0; + + SECTION("AND") { + processing->opcode = OpCode::AND; + exec(data); + + // -28717 & 54924809 + CHECK(cpu.gpr[5] == 54920705); + + // check set flags + flags(false, false, false, false); + } + + // TST with immediate operand + SECTION("TST") { + processing->opcode = OpCode::TST; + + exec(data); + + // -28717 & 54924809 + CHECK(cpu.gpr[5] == 0); + + // check set flags + flags(false, false, false, false); + } + + SECTION("EOR") { + processing->opcode = OpCode::EOR; + exec(data); + + // -28717 ^ 54924809 + CHECK(cpu.gpr[5] == 4240021978); + + // check set flags + flags(true, false, false, false); + + // check zero flag + processing->operand = static_cast(-28717); + exec(data); + + // -28717 ^ -28717 + CHECK(cpu.gpr[5] == 0); + + // check set flags + flags(false, true, false, false); + } + + SECTION("TEQ") { + processing->opcode = OpCode::TEQ; + + exec(data); + + // -28717 ^ 54924809 + CHECK(cpu.gpr[5] == 0); + + // check set flags + flags(true, false, false, false); + } + + SECTION("SUB") { + processing->opcode = OpCode::SUB; + exec(data); + + // -28717 - 54924809 + CHECK(cpu.gpr[5] == static_cast(-54953526)); + + // check set flags + flags(true, false, false, true); + + // check zero flag + processing->operand = static_cast(-28717); + exec(data); + + // -28717 - (-28717) + CHECK(cpu.gpr[5] == 0); + + // check set flags + flags(false, true, false, true); + } + + SECTION("CMP") { + processing->opcode = OpCode::CMP; + + exec(data); + + // -28717 - 54924809 + CHECK(cpu.gpr[5] == 0); + + // check set flags + flags(true, false, false, true); + } + + SECTION("RSB") { + processing->opcode = OpCode::RSB; + exec(data); + + // +28717 + 54924809 + CHECK(cpu.gpr[5] == 54953526); + + // check set flags + flags(false, false, false, false); + } + + SECTION("ADD") { + processing->opcode = OpCode::ADD; + exec(data); + + // -28717 + 54924809 + CHECK(cpu.gpr[5] == 54896092); + + // check set flags + flags(false, false, false, false); + + // test zero flag + processing->operand = static_cast(28717); + exec(data); + + // -28717 + 28717 + CHECK(cpu.gpr[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; + + exec(data); + + CHECK(cpu.gpr[5] == (1ull << 32) - 2); + + // check set flags + flags(true, false, true, false); + } + + SECTION("CMN") { + processing->opcode = OpCode::CMN; + + exec(data); + + // -28717 + 54924809 + CHECK(cpu.gpr[5] == 0); + + // check set flags + flags(false, false, false, false); + } + + SECTION("ADC") { + processing->opcode = OpCode::ADC; + cpu.cpsr.set_c(true); + exec(data); + + // -28717 + 54924809 + carry + CHECK(cpu.gpr[5] == 54896093); + + // check set flags + flags(false, false, false, false); + } + + SECTION("SBC") { + processing->opcode = OpCode::SBC; + cpu.cpsr.set_c(false); + exec(data); + + // -28717 - 54924809 + carry - 1 + CHECK(cpu.gpr[5] == static_cast(-54953527)); + + // check set flags + flags(true, false, false, false); + } + + SECTION("RSC") { + processing->opcode = OpCode::RSC; + cpu.cpsr.set_c(false); + exec(data); + + // +28717 +54924809 + carry - 1 + CHECK(cpu.gpr[5] == 54953525); + + // check set flags + flags(false, false, false, false); + } + + SECTION("ORR") { + processing->opcode = OpCode::ORR; + exec(data); + + // -28717 | 54924809 + CHECK(cpu.gpr[5] == static_cast(-24613)); + + // check set flags + flags(true, false, false, false); + } + + SECTION("BIC") { + processing->opcode = OpCode::BIC; + exec(data); + + // -28717 & ~54924809 + CHECK(cpu.gpr[5] == static_cast(-54949422)); + + // check set flags + flags(true, false, false, false); + } + + SECTION("MVN") { + processing->opcode = OpCode::MVN; + exec(data); + + // ~54924809 + CHECK(cpu.gpr[5] == static_cast(-54924810)); + + // check set flags + flags(true, false, false, false); + } + + SECTION("R15 as destination") { + processing->opcode = OpCode::MVN; + processing->rd = 15; + cpu.gpr[15] = 0; + CHECK(cpu.spsr.raw() != cpu.cpsr.raw()); + exec(data); + + // ~54924809 + CHECK(cpu.gpr[15] == static_cast(-54924810)); + + // flags are not set + flags(false, false, false, false); + CHECK(cpu.spsr.raw() == cpu.cpsr.raw()); + } +} + #undef TAG diff --git a/tests/meson.build b/tests/meson.build index ee7be6a..49052a1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -15,7 +15,7 @@ catch2_tests = executable( dependencies: catch2, link_with: tests_deps, include_directories: [inc, src], - build_by_default: false + build_by_default: false, ) test('catch2 tests', catch2_tests)