Add more tests, prep for graphical output
All checks were successful
Test / build (push) Successful in 8s
All checks were successful
Test / build (push) Successful in 8s
This commit is contained in:
parent
21d143fa04
commit
060ca1d3cd
@ -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
|
||||
|
2
DOCS.txt
2
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 ║
|
||||
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
|
||||
|
@ -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()
|
||||
|
8
emulator/customasm/memview.asm
Normal file
8
emulator/customasm/memview.asm
Normal file
@ -0,0 +1,8 @@
|
||||
#include "foot.asm"
|
||||
|
||||
CNST. r30, #0x1000
|
||||
|
||||
loop:
|
||||
CNST. r0, #0xEFFE
|
||||
CNST.r [r0++], #29
|
||||
CNST. r31, #loop
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
|
18
emulator/tests/arng-instruction.cpp
Normal file
18
emulator/tests/arng-instruction.cpp
Normal 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;
|
||||
}
|
23
emulator/tests/band-instruction.cpp
Normal file
23
emulator/tests/band-instruction.cpp
Normal 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;
|
||||
}
|
18
emulator/tests/bwng-instruction.cpp
Normal file
18
emulator/tests/bwng-instruction.cpp
Normal 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;
|
||||
}
|
23
emulator/tests/bwor-instruction.cpp
Normal file
23
emulator/tests/bwor-instruction.cpp
Normal 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;
|
||||
}
|
23
emulator/tests/bxor-instruction.cpp
Normal file
23
emulator/tests/bxor-instruction.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
30
emulator/tests/long-instruction.cpp
Normal file
30
emulator/tests/long-instruction.cpp
Normal 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;
|
||||
}
|
31
emulator/tests/srsh-instruction.cpp
Normal file
31
emulator/tests/srsh-instruction.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
19
emulator/tests/zlsh-instruction.cpp
Normal file
19
emulator/tests/zlsh-instruction.cpp
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user