From 060ca1d3cd32fe0e7ba31934c2458e8bfedc9236 Mon Sep 17 00:00:00 2001 From: shylie Date: Tue, 8 Jul 2025 11:53:56 -0400 Subject: [PATCH] Add more tests, prep for graphical output --- .gitea/workflows/test.yml | 2 +- DOCS.txt | 2 +- emulator/CMakeLists.txt | 4 +- emulator/customasm/memview.asm | 8 +++ emulator/include/foot-emulator.h | 4 +- emulator/src/foot-emulator.cpp | 22 +++---- emulator/src/main.cpp | 93 ++++++++++++++++++++++++++++- emulator/tests/CMakeLists.txt | 8 +++ emulator/tests/arng-instruction.cpp | 18 ++++++ emulator/tests/band-instruction.cpp | 23 +++++++ emulator/tests/bwng-instruction.cpp | 18 ++++++ emulator/tests/bwor-instruction.cpp | 23 +++++++ emulator/tests/bxor-instruction.cpp | 23 +++++++ emulator/tests/cnst-instruction.cpp | 23 ++++++- emulator/tests/long-instruction.cpp | 30 ++++++++++ emulator/tests/srsh-instruction.cpp | 31 ++++++++++ emulator/tests/test-common.h | 13 ++++ emulator/tests/zlsh-instruction.cpp | 19 ++++++ 18 files changed, 339 insertions(+), 25 deletions(-) create mode 100644 emulator/customasm/memview.asm create mode 100644 emulator/tests/arng-instruction.cpp create mode 100644 emulator/tests/band-instruction.cpp create mode 100644 emulator/tests/bwng-instruction.cpp create mode 100644 emulator/tests/bwor-instruction.cpp create mode 100644 emulator/tests/bxor-instruction.cpp create mode 100644 emulator/tests/long-instruction.cpp create mode 100644 emulator/tests/srsh-instruction.cpp create mode 100644 emulator/tests/zlsh-instruction.cpp diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 236d335..ed6f973 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -13,4 +13,4 @@ jobs: - name: Build run: cmake --build emulator/build - name: Test - run: cd emulator/build && ctest . + run: cd emulator/build && ctest --output-on-failure diff --git a/DOCS.txt b/DOCS.txt index ab2ad9d..d73baa8 100644 --- a/DOCS.txt +++ b/DOCS.txt @@ -25,7 +25,7 @@ ╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣ ║ BXOR ║ Bitwise xor ║ D = A ^ B ║ CCCR ║ 0100 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 001 ║ A < B ║ ╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣ -║ URSH ║ Unsigned right shift ║ D = A >> B ║ CCCR ║ 0101 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 010 ║ A <= B ║ +║ RESERVED ║ ║ ║ CCCR ║ 0101 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 010 ║ A <= B ║ ╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣ ║ SRSH ║ Signed right shift ║ D = A >> B ║ CCCR ║ 0110 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 011 ║ A = B ║ ╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣ diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index bef228e..6170018 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -5,8 +5,10 @@ project(foot-emulator) add_library(foot-emulator src/foot-emulator.cpp include/foot-emulator.h) target_include_directories(foot-emulator PUBLIC include) +find_package(SDL3 REQUIRED CONFIG) + add_executable(foot-emulator-cli src/main.cpp) -target_link_libraries(foot-emulator-cli PUBLIC foot-emulator) +target_link_libraries(foot-emulator-cli PUBLIC foot-emulator SDL3::SDL3) if(PROJECT_IS_TOP_LEVEL) enable_testing() diff --git a/emulator/customasm/memview.asm b/emulator/customasm/memview.asm new file mode 100644 index 0000000..5993a65 --- /dev/null +++ b/emulator/customasm/memview.asm @@ -0,0 +1,8 @@ +#include "foot.asm" + +CNST. r30, #0x1000 + +loop: +CNST. r0, #0xEFFE +CNST.r [r0++], #29 +CNST. r31, #loop diff --git a/emulator/include/foot-emulator.h b/emulator/include/foot-emulator.h index e1c2373..c0e8ba6 100644 --- a/emulator/include/foot-emulator.h +++ b/emulator/include/foot-emulator.h @@ -33,7 +33,7 @@ class Device public: virtual ~Device() = default; - virtual void update() = 0; + virtual bool update() = 0; virtual uint16_t mapped_memory_size() const { return assigned_memory_size; } protected: @@ -77,6 +77,7 @@ private: uint16_t dummy_value; std::vector> devices; uint16_t mapped_base; + bool keep_running; void read_instruction(); @@ -89,7 +90,6 @@ private: void BWOR(); void BAND(); void BXOR(); - void URSH(); void SRSH(); void ZLSH(); void CLSH(); diff --git a/emulator/src/foot-emulator.cpp b/emulator/src/foot-emulator.cpp index add4a9d..47d495d 100644 --- a/emulator/src/foot-emulator.cpp +++ b/emulator/src/foot-emulator.cpp @@ -34,12 +34,16 @@ void Emulator::run(const std::vector& program) { std::copy(program.begin(), program.end(), memory.begin()); - while (true) + keep_running = true; + while (keep_running) { run_instruction(); for (auto& device : devices) { - device->update(); + if (device->update()) + { + keep_running = false; + } } } } @@ -69,10 +73,10 @@ uint16_t& Emulator::decode_operand(Operand operand) return registers[operand.register_index]; case Operand::AddressingMode::IndirectAutoIncrement: - return memory[++registers[operand.register_index]]; + return memory[registers[operand.register_index]++]; case Operand::AddressingMode::IndirectAutoDecrement: - return memory[--registers[operand.register_index]]; + return memory[registers[operand.register_index]--]; case Operand::AddressingMode::Indirect: return memory[registers[operand.register_index]]; @@ -151,7 +155,6 @@ void Emulator::run_instruction() break; case 5: - URSH(); break; case 6: @@ -286,15 +289,6 @@ void Emulator::BXOR() d = a ^ b; } -void Emulator::URSH() -{ - uint16_t a = decode_operand(instruction & 0x000000FF); - uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); - uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); - - d = a >> b; -} - void Emulator::SRSH() { uint16_t a = decode_operand(instruction & 0x000000FF); diff --git a/emulator/src/main.cpp b/emulator/src/main.cpp index 3f4cf18..cba563b 100644 --- a/emulator/src/main.cpp +++ b/emulator/src/main.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace { @@ -34,7 +36,7 @@ public: { } - void update() override + bool update() override { uint16_t& value = memory(0); if (value != 0) @@ -42,18 +44,88 @@ public: std::cout << char(value); value = 0; } + + return false; } }; +class MemoryViewDevice : public foot::Device +{ +public: + MemoryViewDevice(SDL_Renderer* render, uint8_t width, uint8_t height) : + Device(width * height / 2), + render(render), + texture(SDL_CreateTexture(render, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height)), + width(width), + height(height) + {} + ~MemoryViewDevice() + { + SDL_DestroyTexture(texture); + } + + bool update() override + { + int pitch; + uint8_t* pixels; + if (!SDL_LockTexture(texture, nullptr, (void**)&pixels, &pitch)) + { + std::cout << SDL_GetError() << std::endl; + } + int idx = 0; + for (int i = 0; i < width / 2; i++) + { + for (int j = 0; j < height; j++) + { + const uint16_t mem = memory(i + j * width / 2); + + const uint8_t left = mem & 0xFF; + const uint8_t right = mem >> 8; + + pixels[idx + 0] = left; + pixels[idx + 1] = left; + pixels[idx + 2] = left; + pixels[idx + 3] = right; + pixels[idx + 4] = right; + pixels[idx + 5] = right; + + idx += 6; + } + } + SDL_UnlockTexture(texture); + + SDL_RenderPresent(render); + + SDL_Event event; + bool stop = false; + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_EVENT_QUIT) + { + stop = true; + } + } + + return stop; + } + +private: + SDL_Renderer* render; + SDL_Texture* texture; + uint8_t width; + uint8_t height; +}; + } int main(int argc, char** argv) { - foot::Emulator emu; - emu.map_device(std::make_unique()); if (argc > 1) { + constexpr int WIDTH = 64; + constexpr int HEIGHT = 64; + std::ifstream file(argv[1], std::ios::binary); if (!file.is_open()) @@ -62,7 +134,22 @@ int main(int argc, char** argv) return 1; } + SDL_Window* window; + SDL_Renderer* render; + + SDL_Init(SDL_INIT_VIDEO); + SDL_CreateWindowAndRenderer("foot emulator", WIDTH * 4, HEIGHT * 4, 0, &window, &render); + + foot::Emulator emu; + emu.map_device(std::make_unique()); + emu.map_device(std::make_unique(render, WIDTH, HEIGHT)); + emu.run(read_file(file)); + + SDL_DestroyRenderer(render); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; } else { diff --git a/emulator/tests/CMakeLists.txt b/emulator/tests/CMakeLists.txt index dc68f0e..e674353 100644 --- a/emulator/tests/CMakeLists.txt +++ b/emulator/tests/CMakeLists.txt @@ -9,3 +9,11 @@ function(test name) endfunction() test(cnst-instruction) +test(bwng-instruction) +test(arng-instruction) +test(long-instruction) +test(bwor-instruction) +test(band-instruction) +test(bxor-instruction) +test(srsh-instruction) +test(zlsh-instruction) diff --git a/emulator/tests/arng-instruction.cpp b/emulator/tests/arng-instruction.cpp new file mode 100644 index 0000000..dc16f92 --- /dev/null +++ b/emulator/tests/arng-instruction.cpp @@ -0,0 +1,18 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // ARNG + // dst - 0, Direct + // a - 0, Direct + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x01200220 }); + if (!check(0xBA99, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/band-instruction.cpp b/emulator/tests/band-instruction.cpp new file mode 100644 index 0000000..586daf0 --- /dev/null +++ b/emulator/tests/band-instruction.cpp @@ -0,0 +1,23 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // CNST + // dst - 1, Direct + // imm - 0x2345 + // + // BAND + // dst - 0, Direct + // a - 0, Direct + // b - 1, Direct + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x00212345, 0x03202021 }); + if (!check(0x4567 & 0x2345, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/bwng-instruction.cpp b/emulator/tests/bwng-instruction.cpp new file mode 100644 index 0000000..4abf780 --- /dev/null +++ b/emulator/tests/bwng-instruction.cpp @@ -0,0 +1,18 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // BWNG + // dst - 0, Direct + // a - 0, Direct + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x01200120 }); + if (!check(0xBA98, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/bwor-instruction.cpp b/emulator/tests/bwor-instruction.cpp new file mode 100644 index 0000000..9ea7d85 --- /dev/null +++ b/emulator/tests/bwor-instruction.cpp @@ -0,0 +1,23 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // CNST + // dst - 1, Direct + // imm - 0x2345 + // + // BWOR + // dst - 0, Direct + // a - 0, Direct + // b - 1, Direct + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x00212345, 0x02202021 }); + if (!check(0x4567 | 0x2345, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/bxor-instruction.cpp b/emulator/tests/bxor-instruction.cpp new file mode 100644 index 0000000..95b72e2 --- /dev/null +++ b/emulator/tests/bxor-instruction.cpp @@ -0,0 +1,23 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // CNST + // dst - 1, Direct + // imm - 0x2345 + // + // BXOR + // dst - 0, Direct + // a - 0, Direct + // b - 1, Direct + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x00212345, 0x04202021 }); + if (!check(0x4567 ^ 0x2345, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/cnst-instruction.cpp b/emulator/tests/cnst-instruction.cpp index c68abac..10351f2 100644 --- a/emulator/tests/cnst-instruction.cpp +++ b/emulator/tests/cnst-instruction.cpp @@ -1,4 +1,3 @@ -#include #include "test-common.h" int main(int argc, char** argv) @@ -8,7 +7,7 @@ int main(int argc, char** argv) // imm - 0xFFFF { foot::Emulator emu = run_instruction(0x0020FFFF); - if (emu.register_at(0) != 0xFFFF) { return 1; } + if (!check(0xFFFF, emu.register_at(0))) { return 1; } } // CNST @@ -20,7 +19,25 @@ int main(int argc, char** argv) // imm = 0x7777 { foot::Emulator emu = run_instructions({ 0x0020FFFF, 0x00807777 }); - if (emu.memory_at(0xFFFF) != 0x7777) { return 1; } + if (!check(0x7777, emu.memory_at(0xFFFF))) { return 1; } + } + + // CNST + // dst - 30, Direct + // imm = 0x0010 + // + // CNST + // dst 0, Indirect with Auto-Increment + // imm = 0x1234 + { + bool failed = false; + foot::Emulator emu = run_instructions({ 0x003E0010, 0x10401234 }); + for (int i = 0; i < 0x10; i++) + { + if (!check(0x1234, emu.memory_at(i))) { failed = true; } + } + + if (failed) { return 1; } } return 0; diff --git a/emulator/tests/long-instruction.cpp b/emulator/tests/long-instruction.cpp new file mode 100644 index 0000000..abd1a3d --- /dev/null +++ b/emulator/tests/long-instruction.cpp @@ -0,0 +1,30 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // LONG + // dst - 0, Direct + // a - 0, Direct + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x01200320 }); + if (!check(0x0000, emu.register_at(0))) { return 1; } + } + + // CNST + // dst - 0, Direct + // imm - 0x0000 + // + // LONG + // dst - 0, Direct + // a - 0, Direct + { + foot::Emulator emu = run_instructions({ 0x00200000, 0x01200320 }); + if (!check(0x0001, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/srsh-instruction.cpp b/emulator/tests/srsh-instruction.cpp new file mode 100644 index 0000000..3cc74e0 --- /dev/null +++ b/emulator/tests/srsh-instruction.cpp @@ -0,0 +1,31 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // SRSH + // dst - 0, Direct + // a - 0, Direct + // b - 2, Immediate + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x06200220 }); + if (!check(0x4567 >> 2, emu.register_at(0))) { return 1; } + } + + // CNST + // dst - 0, Direct + // imm - 0x8000 + // + // SRSH + // dst - 0, Direct + // a - 0, Direct + { + foot::Emulator emu = run_instructions({ 0x00208000, 0x06200220 }); + if (!check((-0x8000) >> 2, emu.register_at(0))) { return 1; } + } + + return 0; +} diff --git a/emulator/tests/test-common.h b/emulator/tests/test-common.h index 2cdf7a7..b17bbb4 100644 --- a/emulator/tests/test-common.h +++ b/emulator/tests/test-common.h @@ -1,5 +1,7 @@ #include +#include + inline foot::Emulator run_instruction(uint32_t instruction) { foot::Emulator emu; @@ -27,3 +29,14 @@ inline foot::Emulator run_instructions(const std::vector& instructions return emu; } + +inline bool check(uint16_t expected, uint16_t actual) +{ + if (actual != expected) + { + std::cout << std::hex << "Expected " << expected << ", got " << actual << ".\n"; + return false; + } + + return true; +} diff --git a/emulator/tests/zlsh-instruction.cpp b/emulator/tests/zlsh-instruction.cpp new file mode 100644 index 0000000..e9218bc --- /dev/null +++ b/emulator/tests/zlsh-instruction.cpp @@ -0,0 +1,19 @@ +#include "test-common.h" + +int main(int argc, char** argv) +{ + // CNST + // dst - 0, Direct + // imm - 0x4567 + // + // ZLSH + // dst - 0, Direct + // a - 0, Direct + // b - 2, Immediate + { + foot::Emulator emu = run_instructions({ 0x00204567, 0x07200220 }); + if (!check(0x159C, emu.register_at(0))) { return 1; } + } + + return 0; +}