5 Commits

Author SHA1 Message Date
ed01ed80cd tests: add tests for memory
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-24 18:04:28 +05:30
8e26cadc9a chore: revert util/crypto
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-24 17:45:19 +05:30
6e56828dfd tests/arm/exec: test conditions
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-24 17:38:11 +05:30
5fcc75bc9a tests: add tests for internal utilities
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-24 17:36:38 +05:30
560bd5bfa1 tests: add tests for bus
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-23 23:20:05 +05:30
14 changed files with 351 additions and 36 deletions

2
.envrc
View File

@@ -1 +1 @@
use flake .#matar-clang use flake

View File

@@ -4,7 +4,8 @@ project('matar', 'cpp',
default_options : ['warning_level=3', default_options : ['warning_level=3',
'werror=true', 'werror=true',
'optimization=3', 'optimization=3',
'cpp_std=c++20']) 'cpp_std=c++20',
'default_library=static'])
compiler = meson.get_compiler('cpp') compiler = meson.get_compiler('cpp')

View File

@@ -1,8 +1,8 @@
#include "memory.hh" #include "memory.hh"
#include "header.hh" #include "header.hh"
#include "util/bits.hh" #include "util/bits.hh"
#include "util/crypto.hh"
#include "util/log.hh" #include "util/log.hh"
#include "util/utils.hh"
#include <bitset> #include <bitset>
#include <stdexcept> #include <stdexcept>

View File

@@ -14,19 +14,19 @@ get_bit(Int num, size_t n) {
template<std::integral Int> template<std::integral Int>
inline void inline void
set_bit(Int& num, size_t n) { set_bit(Int& num, size_t n) {
num |= (1 << n); num |= (static_cast<Int>(1) << n);
} }
template<std::integral Int> template<std::integral Int>
inline void inline void
rst_bit(Int& num, size_t n) { rst_bit(Int& num, size_t n) {
num &= ~(1 << n); num &= ~(static_cast<Int>(1) << n);
} }
template<std::integral Int> template<std::integral Int>
inline void inline void
chg_bit(Int& num, size_t n, bool x) { chg_bit(Int& num, size_t n, bool x) {
num = (num & ~(1 << n)) | (x << n); num = (num & ~(static_cast<Int>(1) << n)) | (static_cast<Int>(x) << n);
} }
/// read range of bits from start to end inclusive /// read range of bits from start to end inclusive
@@ -36,5 +36,5 @@ bit_range(Int num, size_t start, size_t end) {
// NOTE: we do not require -1 if it is a signed integral // NOTE: we do not require -1 if it is a signed integral
Int left = Int left =
std::numeric_limits<Int>::digits - (std::is_unsigned<Int>::value) - end; std::numeric_limits<Int>::digits - (std::is_unsigned<Int>::value) - end;
return num << left >> (left + start); return static_cast<Int>(num << left) >> (left + start);
} }

View File

@@ -6,12 +6,12 @@
namespace logging { namespace logging {
namespace ansi { namespace ansi {
static constexpr std::string_view RED = "\033[31m"; static constexpr auto RED = "\033[31m";
static constexpr std::string_view YELLOW = "\033[33m"; static constexpr auto YELLOW = "\033[33m";
static constexpr std::string_view MAGENTA = "\033[35m"; static constexpr auto MAGENTA = "\033[35m";
static constexpr std::string_view WHITE = "\033[37m"; static constexpr auto WHITE = "\033[37m";
static constexpr std::string_view BOLD = "\033[1m"; static constexpr auto BOLD = "\033[1m";
static constexpr std::string_view RESET = "\033[0m"; static constexpr auto RESET = "\033[0m";
} }
using fmt::print; using fmt::print;
@@ -20,8 +20,9 @@ class Logger {
using LogLevel = matar::LogLevel; using LogLevel = matar::LogLevel;
public: public:
Logger(LogLevel level = LogLevel::Debug) Logger(LogLevel level = LogLevel::Debug, FILE* stream = stderr)
: level(0) { : level(0)
, stream(stream) {
set_level(level); set_level(level);
} }
@@ -69,14 +70,14 @@ class Logger {
void set_level(LogLevel level) { void set_level(LogLevel level) {
this->level = (static_cast<uint8_t>(level) << 1) - 1; this->level = (static_cast<uint8_t>(level) << 1) - 1;
} }
void set_stream(std::ostream& stream) { this->stream = stream; } void set_stream(FILE* stream) { this->stream = stream; }
private: private:
uint8_t level; uint8_t level;
std::reference_wrapper<std::ostream> stream = std::clog; FILE* stream;
}; };
} }
extern logging::Logger glogger; extern logging::Logger glogger;
#define debug(x) logger.debug("{} = {}", #x, x); #define debug(x) glogger.debug("{} = {}", #x, x);

43
tests/bus.cc Normal file
View File

@@ -0,0 +1,43 @@
#include "bus.hh"
#include <catch2/catch_test_macros.hpp>
static constexpr auto TAG = "[bus]";
using namespace matar;
class BusFixture {
public:
BusFixture()
: bus(Memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
std::vector<uint8_t>(Header::HEADER_SIZE))) {}
protected:
Bus bus;
};
TEST_CASE_METHOD(BusFixture, "Byte", TAG) {
CHECK(bus.read_byte(3349) == 0);
bus.write_byte(3349, 0xEC);
CHECK(bus.read_byte(3349) == 0xEC);
CHECK(bus.read_word(3349) == 0xEC);
CHECK(bus.read_halfword(3349) == 0xEC);
}
TEST_CASE_METHOD(BusFixture, "Halfword", TAG) {
CHECK(bus.read_halfword(33750745) == 0);
bus.write_halfword(33750745, 0x1A4A);
CHECK(bus.read_halfword(33750745) == 0x1A4A);
CHECK(bus.read_word(33750745) == 0x1A4A);
CHECK(bus.read_byte(33750745) == 0x4A);
}
TEST_CASE_METHOD(BusFixture, "Word", TAG) {
CHECK(bus.read_word(100724276) == 0);
bus.write_word(100724276, 0x3ACC491D);
CHECK(bus.read_word(100724276) == 0x3ACC491D);
CHECK(bus.read_halfword(100724276) == 0x491D);
CHECK(bus.read_byte(100724276) == 0x1D);
}

View File

@@ -13,7 +13,6 @@ class CpuFixture {
std::vector<uint8_t>(Header::HEADER_SIZE)))) {} std::vector<uint8_t>(Header::HEADER_SIZE)))) {}
protected: protected:
// TODO: test with other conditions
void exec(arm::InstructionData data, Condition condition = Condition::AL) { void exec(arm::InstructionData data, Condition condition = Condition::AL) {
arm::Instruction instruction(condition, data); arm::Instruction instruction(condition, data);
cpu.exec_arm(instruction); cpu.exec_arm(instruction);
@@ -32,7 +31,7 @@ class CpuFixture {
}; };
}; };
#define TAG "arm execution" static constexpr auto TAG = "[arm][execution]";
using namespace arm; using namespace arm;
@@ -804,29 +803,41 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) {
processing->rn = 7; processing->rn = 7;
} }
auto flags = [this](bool n, bool z, bool v, bool c) { auto reset_flags = [this]() {
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_n(false);
cpu.cpsr.set_z(false); cpu.cpsr.set_z(false);
cpu.cpsr.set_v(false); cpu.cpsr.set_v(false);
cpu.cpsr.set_c(false); cpu.cpsr.set_c(false);
}; };
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);
reset_flags();
};
// immediate operand // immediate operand
processing->operand = static_cast<uint32_t>(54924809); processing->operand = static_cast<uint32_t>(54924809);
// rs // rs
cpu.gpr[12] = 2; cpu.gpr[12] = 2;
cpu.gpr[5] = 0; cpu.gpr[5] = 0;
reset_flags();
SECTION("AND") { SECTION("AND (with condition check)") {
processing->opcode = OpCode::AND; processing->opcode = OpCode::AND;
exec(data); cpu.cpsr.set_z(false);
exec(data, Condition::EQ);
// condition is false
CHECK(cpu.gpr[5] == 0);
cpu.cpsr.set_z(true);
exec(data, Condition::EQ);
// -28717 & 54924809 // -28717 & 54924809
// condition is true now
CHECK(cpu.gpr[5] == 54920705); CHECK(cpu.gpr[5] == 54920705);
// check set flags // check set flags
@@ -846,11 +857,19 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) {
flags(false, false, false, false); flags(false, false, false, false);
} }
SECTION("EOR") { SECTION("EOR (with condition check)") {
processing->opcode = OpCode::EOR; processing->opcode = OpCode::EOR;
exec(data); cpu.cpsr.set_c(true);
exec(data, Condition::CC);
// condition fails
CHECK(cpu.gpr[5] == 0);
cpu.cpsr.set_c(false);
exec(data, Condition::CC);
// -28717 ^ 54924809 // -28717 ^ 54924809
// condition is true now
CHECK(cpu.gpr[5] == 4240021978); CHECK(cpu.gpr[5] == 4240021978);
// check set flags // check set flags
@@ -1051,5 +1070,3 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) {
CHECK(cpu.spsr.raw() == cpu.cpsr.raw()); CHECK(cpu.spsr.raw() == cpu.cpsr.raw());
} }
} }
#undef TAG

View File

@@ -1,7 +1,7 @@
#include "cpu/arm/instruction.hh" #include "cpu/arm/instruction.hh"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#define TAG "disassembler" static constexpr auto TAG = "[arm][disassembly]";
using namespace matar; using namespace matar;
using namespace arm; using namespace arm;
@@ -467,5 +467,3 @@ TEST_CASE("Software Interrupt", TAG) {
CHECK(instruction.condition == Condition::EQ); CHECK(instruction.condition == Condition::EQ);
CHECK(instruction.disassemble() == "SWIEQ"); CHECK(instruction.disassemble() == "SWIEQ");
} }
#undef TAG

121
tests/memory.cc Normal file
View File

@@ -0,0 +1,121 @@
#include "memory.hh"
#include <catch2/catch_test_macros.hpp>
static constexpr auto TAG = "[memory]";
using namespace matar;
class MemFixture {
public:
MemFixture()
: memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
std::vector<uint8_t>(Header::HEADER_SIZE)) {}
protected:
Memory memory;
};
TEST_CASE_METHOD(MemFixture, "bios", TAG) {
memory.write(0, 0xAC);
CHECK(memory.read(0) == 0xAC);
memory.write(0x3FFF, 0x48);
CHECK(memory.read(0x3FFF) == 0x48);
memory.write(0x2A56, 0x10);
CHECK(memory.read(0x2A56) == 0x10);
}
TEST_CASE_METHOD(MemFixture, "board wram", TAG) {
memory.write(0x2000000, 0xAC);
CHECK(memory.read(0x2000000) == 0xAC);
memory.write(0x203FFFF, 0x48);
CHECK(memory.read(0x203FFFF) == 0x48);
memory.write(0x2022A56, 0x10);
CHECK(memory.read(0x2022A56) == 0x10);
}
TEST_CASE_METHOD(MemFixture, "chip wram", TAG) {
memory.write(0x3000000, 0xAC);
CHECK(memory.read(0x3000000) == 0xAC);
memory.write(0x3007FFF, 0x48);
CHECK(memory.read(0x3007FFF) == 0x48);
memory.write(0x3002A56, 0x10);
CHECK(memory.read(0x3002A56) == 0x10);
}
TEST_CASE_METHOD(MemFixture, "palette ram", TAG) {
memory.write(0x5000000, 0xAC);
CHECK(memory.read(0x5000000) == 0xAC);
memory.write(0x50003FF, 0x48);
CHECK(memory.read(0x50003FF) == 0x48);
memory.write(0x5000156, 0x10);
CHECK(memory.read(0x5000156) == 0x10);
}
TEST_CASE_METHOD(MemFixture, "video ram", TAG) {
memory.write(0x6000000, 0xAC);
CHECK(memory.read(0x6000000) == 0xAC);
memory.write(0x6017FFF, 0x48);
CHECK(memory.read(0x6017FFF) == 0x48);
memory.write(0x6012A56, 0x10);
CHECK(memory.read(0x6012A56) == 0x10);
}
TEST_CASE_METHOD(MemFixture, "oam obj ram", TAG) {
memory.write(0x7000000, 0xAC);
CHECK(memory.read(0x7000000) == 0xAC);
memory.write(0x70003FF, 0x48);
CHECK(memory.read(0x70003FF) == 0x48);
memory.write(0x7000156, 0x10);
CHECK(memory.read(0x7000156) == 0x10);
}
TEST_CASE("rom", TAG) {
// 32 megabyte ROM
Memory memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
std::vector<uint8_t>(32 * 1024 * 1024));
SECTION("ROM1") {
memory.write(0x8000000, 0xAC);
CHECK(memory.read(0x8000000) == 0xAC);
memory.write(0x9FFFFFF, 0x48);
CHECK(memory.read(0x9FFFFFF) == 0x48);
memory.write(0x8ef0256, 0x10);
CHECK(memory.read(0x8ef0256) == 0x10);
}
SECTION("ROM2") {
memory.write(0xA000000, 0xAC);
CHECK(memory.read(0xA000000) == 0xAC);
memory.write(0xBFFFFFF, 0x48);
CHECK(memory.read(0xBFFFFFF) == 0x48);
memory.write(0xAEF0256, 0x10);
CHECK(memory.read(0xAEF0256) == 0x10);
}
SECTION("ROM3") {
memory.write(0xC000000, 0xAC);
CHECK(memory.read(0xC000000) == 0xAC);
memory.write(0xDFFFFFF, 0x48);
CHECK(memory.read(0xDFFFFFF) == 0x48);
memory.write(0xCEF0256, 0x10);
CHECK(memory.read(0xCEF0256) == 0x10);
}
}

View File

@@ -5,10 +5,13 @@ tests_deps = [
src = include_directories('../src') src = include_directories('../src')
tests_sources = files( tests_sources = files(
'main.cc' 'main.cc',
'bus.cc',
'memory.cc'
) )
subdir('cpu') subdir('cpu')
subdir('util')
catch2 = dependency('catch2', version: '>=3.4.0', static: true) catch2 = dependency('catch2', version: '>=3.4.0', static: true)
catch2_tests = executable( catch2_tests = executable(

106
tests/util/bits.cc Normal file
View File

@@ -0,0 +1,106 @@
#include "util/bits.hh"
#include <catch2/catch_test_macros.hpp>
static constexpr auto TAG = "[util][bits]";
TEST_CASE("8 bits", TAG) {
uint8_t num = 45;
CHECK(get_bit(num, 0));
CHECK(!get_bit(num, 1));
CHECK(get_bit(num, 5));
CHECK(!get_bit(num, 6));
CHECK(!get_bit(num, 7));
set_bit(num, 6);
CHECK(get_bit(num, 6));
rst_bit(num, 6);
CHECK(!get_bit(num, 6));
chg_bit(num, 5, false);
CHECK(!get_bit(num, 5));
chg_bit(num, 5, true);
CHECK(get_bit(num, 5));
// 0b0110
CHECK(bit_range(num, 1, 4) == 6);
}
TEST_CASE("16 bits", TAG) {
uint16_t num = 34587;
CHECK(get_bit(num, 0));
CHECK(get_bit(num, 1));
CHECK(!get_bit(num, 5));
CHECK(!get_bit(num, 14));
CHECK(get_bit(num, 15));
set_bit(num, 14);
CHECK(get_bit(num, 14));
rst_bit(num, 14);
CHECK(!get_bit(num, 14));
chg_bit(num, 5, true);
CHECK(get_bit(num, 5));
// num = 45
chg_bit(num, 5, false);
CHECK(!get_bit(num, 5));
// 0b1000110
CHECK(bit_range(num, 2, 8) == 70);
}
TEST_CASE("32 bits", TAG) {
uint32_t num = 3194142523;
CHECK(get_bit(num, 0));
CHECK(get_bit(num, 1));
CHECK(get_bit(num, 12));
CHECK(get_bit(num, 29));
CHECK(!get_bit(num, 30));
CHECK(get_bit(num, 31));
set_bit(num, 30);
CHECK(get_bit(num, 30));
rst_bit(num, 30);
CHECK(!get_bit(num, 30));
chg_bit(num, 12, false);
CHECK(!get_bit(num, 12));
chg_bit(num, 12, true);
CHECK(get_bit(num, 12));
// 0b10011000101011111100111
CHECK(bit_range(num, 3, 25) == 5003239);
}
TEST_CASE("64 bits", TAG) {
uint64_t num = 58943208889991935;
CHECK(get_bit(num, 0));
CHECK(get_bit(num, 1));
CHECK(!get_bit(num, 10));
CHECK(get_bit(num, 55));
CHECK(!get_bit(num, 60));
set_bit(num, 63);
CHECK(get_bit(num, 63));
rst_bit(num, 63);
CHECK(!get_bit(num, 63));
chg_bit(num, 10, true);
CHECK(get_bit(num, 10));
chg_bit(num, 10, false);
CHECK(!get_bit(num, 10));
// 0b011010001
CHECK(bit_range(num, 39, 47) == 209);
}

21
tests/util/crypto.cc Normal file
View File

@@ -0,0 +1,21 @@
#include "util/crypto.hh"
#include <catch2/catch_test_macros.hpp>
static constexpr auto TAG = "[util][crypto]";
TEST_CASE("sha256 matar", TAG) {
std::array<uint8_t, 5> data = { 'm', 'a', 't', 'a', 'r' };
CHECK(crypto::sha256(data) ==
"3b02a908fd5743c0e868675bb6ae77d2a62b3b5f7637413238e2a1e0e94b6a53");
}
TEST_CASE("sha256 forgis", TAG) {
std::array<uint8_t, 32> data = { 'i', ' ', 'p', 'u', 't', ' ', 't', 'h',
'e', ' ', 'n', 'e', 'w', ' ', 'f', 'o',
'r', 'g', 'i', 's', ' ', 'o', 'n', ' ',
't', 'h', 'e', ' ', 'j', 'e', 'e', 'p' };
CHECK(crypto::sha256(data) ==
"cfddca2ce2673f355518cbe2df2a8522693c54723a469e8b36a4f68b90d2b759");
}

4
tests/util/meson.build Normal file
View File

@@ -0,0 +1,4 @@
tests_sources += files(
'bits.cc',
'crypto.cc'
)