Add more tests, prep for graphical output
All checks were successful
Test / build (push) Successful in 8s

This commit is contained in:
shylie 2025-07-08 11:53:56 -04:00
parent 21d143fa04
commit 060ca1d3cd
18 changed files with 339 additions and 25 deletions

View File

@ -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

View File

@ -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 ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣

View File

@ -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()

View File

@ -0,0 +1,8 @@
#include "foot.asm"
CNST. r30, #0x1000
loop:
CNST. r0, #0xEFFE
CNST.r [r0++], #29
CNST. r31, #loop

View File

@ -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<std::unique_ptr<Device>> 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();

View File

@ -34,12 +34,16 @@ void Emulator::run(const std::vector<uint16_t>& 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);

View File

@ -3,6 +3,8 @@
#include <fstream>
#include <iostream>
#include <SDL3/SDL.h>
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<PrintDevice>());
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<PrintDevice>());
emu.map_device(std::make_unique<MemoryViewDevice>(render, WIDTH, HEIGHT));
emu.run(read_file(file));
SDL_DestroyRenderer(render);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
else
{

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,4 +1,3 @@
#include <foot-emulator.h>
#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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,5 +1,7 @@
#include <foot-emulator.h>
#include <iostream>
inline foot::Emulator run_instruction(uint32_t instruction)
{
foot::Emulator emu;
@ -27,3 +29,14 @@ inline foot::Emulator run_instructions(const std::vector<uint32_t>& 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;
}

View File

@ -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;
}