This commit is contained in:
sfj 2026-01-13 16:25:12 +01:00
parent 9fab1ca0cf
commit d53c543ff4
5 changed files with 211 additions and 122 deletions

View File

@ -15,60 +15,57 @@ namespace {
bool sdl_is_initialized = false; bool sdl_is_initialized = false;
struct Char { constexpr uint64_t char_data(uint8_t ch)
explicit constexpr Char(char ch, uint64_t data)
: ch(ch)
, data(data)
{ {
switch (ch) {
// clang-format off
case ' ': return 0x0000000000000000;
case '0': return 0x003C666E7666663C;
case '1': return 0x001838181818183C;
case '2': return 0x003C66060C18307E;
case '3': return 0x003C66060C06663C;
case '4': return 0x000C1C3C6C7E0C0C;
case 'A': return 0x00187E66667E6666;
case 'B': return 0x00787E667C667E7C;
case 'C': return 0x003C7E6060607E3C;
case 'D': return 0x007C7E6666667E7C;
case 'E': return 0x007E7E607E607E7E;
case 'F': return 0x007E7E6078786060;
case 'G': return 0x007E7E606E667E7E;
case 'H': return 0x0066667E7E666666;
case 'I': return 0x007E7E1818187E7E;
case 'J': return 0x007E7E0606667E3C;
case 'K': return 0x0066666E7C7C6E66;
case 'L': return 0x0060606060607E7E;
case 'M': return 0x00E7FFDBC3C3C3C3;
case 'N': return 0x0066767E6E666666;
case 'O': return 0x003C66666666663C;
case 'P': return 0x007C7C667E7C6060;
case 'Q': return 0x003C7E66666E7E3F;
case 'R': return 0x007C7E667E7C6C66;
case 'S': return 0x003C66603C06663C;
case 'T': return 0x007E7E1818181818;
case 'U': return 0x0066666666667E3C;
case 'V': return 0x00666666667E3C18;
case 'W': return 0x00C3C3C3DBDBFFE7;
case 'X': return 0x0066663C3C666666;
case 'Y': return 0x0066663C18181818;
case 'Z': return 0x007E7E0C18307E7E;
default: return 0x55AA55AA55AA55AA;
// clang-format on
} }
auto to_u64() const -> uint64_t
{
return data;
}
char ch;
uint64_t data;
};
constexpr auto charset = std::array {
Char(' ', 0x0000000000000000),
Char('A', 0x66667E66667E1800),
Char('B', 0x7C7E667C667E7800),
Char('C', 0x3C7E6060607E3C00),
Char('D', 0x7C7E6666667E7C00),
Char('E', 0x7E7E607E607E7E00),
Char('F', 0x60607878607E7E00),
Char('G', 0x7E7E666E607E7E00),
Char('H', 0x6666667E7E666600),
Char('I', 0x7E7E1818187E7E00),
Char('J', 0x3C7E6606067E7E00),
Char('K', 0x666E7C7C6E666600),
Char('L', 0x7E7E606060606000),
Char('M', 0xC3C3C3C3DBFFE700),
Char('N', 0x6666666E7E766600),
Char('O', 0x3C66666666663C00),
Char('P', 0x60607C7E667C7C00),
Char('Q', 0x3F7E6E66667E3C00),
Char('R', 0x666C7C7E667E7C00),
Char('S', 0x3C66063C60663C00),
Char('T', 0x18181818187E7E00),
Char('U', 0x3C7E666666666600),
Char('V', 0x183C7E6666666600),
Char('W', 0xE7FFDBDBC3C3C300),
Char('X', 0x6666663C3C666600),
Char('Y', 0x181818183C666600),
Char('Z', 0x7E7E30180C7E7E00),
}; };
} }
auto IoDevice::create(int ch_size, int ch_width, int ch_height) auto IoDevice::create()
-> std::expected<std::unique_ptr<IoDevice>, std::string> -> std::expected<std::unique_ptr<IoDevice>, std::string>
{ {
auto profile = ScreenProfile(ch_size, ch_width, ch_height); auto profile = ScreenProfile();
auto px_width= profile.px_width; auto width = profile.width_raw_px();
auto px_height = profile.px_height; auto height = profile.height_raw_px();
if (!sdl_is_initialized) { if (!sdl_is_initialized) {
if (SDL_Init(SDL_INIT_VIDEO) != 0) { if (SDL_Init(SDL_INIT_VIDEO) != 0) {
@ -79,7 +76,7 @@ auto IoDevice::create(int ch_size, int ch_width, int ch_height)
SDL_Window* window; SDL_Window* window;
SDL_Renderer* renderer; SDL_Renderer* renderer;
if (SDL_CreateWindowAndRenderer(px_width, px_height, 0, &window, &renderer) if (SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer)
!= 0) { != 0) {
return std::unexpected(SDL_GetError()); return std::unexpected(SDL_GetError());
} }
@ -87,8 +84,8 @@ auto IoDevice::create(int ch_size, int ch_width, int ch_height)
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_Texture* texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA32, SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STREAMING, SDL_TEXTUREACCESS_STREAMING,
px_width, width,
px_height); height);
if (texture == nullptr) { if (texture == nullptr) {
return std::unexpected(SDL_GetError()); return std::unexpected(SDL_GetError());
@ -117,11 +114,6 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value)
{ {
auto lock = std::lock_guard(mx); auto lock = std::lock_guard(mx);
auto it = std::find_if(charset.begin(), charset.end(), [&](Char ch) {
return ch.ch == value;
});
uint64_t char_data = it->to_u64();
SDL_Color* buffer; SDL_Color* buffer;
int pitch; int pitch;
int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch); int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch);
@ -129,28 +121,41 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value)
return std::unexpected(SDL_GetError()); return std::unexpected(SDL_GetError());
} }
auto [ch_size, ch_width, ch_height, px_width, px_height] = m_profile; constexpr auto white = SDL_Color { 0xff, 0xff, 0xff, 0xff };
std::println("ch_size = {}, ch_width = {}, ch_height = {}, px_width = {}, px_height = {}", ch_size, ch_width, ch_height, px_width, px_height); constexpr auto black = SDL_Color { 0x00, 0x00, 0x00, 0xff };
for (int ch_y = 0; ch_y < 8; ++ch_y) { int ch_size = m_profile.ch_size_px();
for (int ch_x = 0; ch_x < 8; ++ch_x) { int px_size = m_profile.px_size_raw_px();
bool ch int raw_width = m_profile.width_raw_px();
= char_data >> (ch_y * 8 + (8 - ch_x - 1)) int width_ch = m_profile.width_ch();
& 1;
auto set_pixel = [&](int x, int y, bool ch) {
int raw_base_x = x * px_size;
int raw_base_y = y * px_size;
for (int raw_px_y = 0; raw_px_y < px_size; ++raw_px_y) {
for (int raw_px_x = 0; raw_px_x < px_size; ++raw_px_x) {
int x = raw_base_x + raw_px_x;
int y = raw_base_y + raw_px_y;
buffer[y * raw_width + x] = ch ? white : black;
}
}
};
uint64_t char_data = ::char_data(value);
int base_x = offset % width_ch * ch_size;
int base_y = offset / width_ch * ch_size;
for (int px_y = 0; px_y < ch_size; ++px_y) { for (int px_y = 0; px_y < ch_size; ++px_y) {
uint8_t row = char_data >> (7 - px_y) * ch_size & 0xff;
for (int px_x = 0; px_x < ch_size; ++px_x) { for (int px_x = 0; px_x < ch_size; ++px_x) {
bool ch = row >> (7 - px_x) & 1;
int x = (offset % 4 * ch_size + ch_x) * 4 set_pixel(base_x + px_x, base_y + px_y, ch);
+ px_x;
int y = (offset / 4 * ch_size + ch_y) * 4
+ px_y;
buffer[y * px_width + x] = ch
? SDL_Color { 0xff, 0xff, 0xff, 0xff }
: SDL_Color { 0x00, 0x00, 0x00, 0xff };
}
}
} }
} }
@ -161,3 +166,33 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value)
return {}; return {};
} }
auto IoDevice::poll_event()
-> std::unique_ptr<IoEvent>
{
std::lock_guard lock(mx);
SDL_Event event;
if (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
return std::make_unique<IoEvent>(IoEvent::Type::Quit, IoEvent::Quit{});
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) {
return std::make_unique<IoEvent>(IoEvent::Type::Quit, IoEvent::Quit{});
}
return std::make_unique<IoEvent>(
IoEvent::Type::KeyPress,
IoEvent::KeyPress { static_cast<uint16_t>(event.key.keysym.scancode) });
case SDL_KEYUP:
return std::make_unique<IoEvent>(
IoEvent::Type::KeyRelease,
IoEvent::KeyRelease { static_cast<uint16_t>(event.key.keysym.scancode) });
// default:
// std::println(stderr, "warning: unhandled event '{}'", event.type);
}
}
return {};
}

View File

@ -5,6 +5,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <variant>
struct SDL_Window; struct SDL_Window;
struct SDL_Renderer; struct SDL_Renderer;
@ -12,26 +13,61 @@ struct SDL_Texture;
namespace vc5 { namespace vc5 {
struct ScreenProfile { namespace events {
explicit ScreenProfile(int ch_size, int ch_width, int ch_height)
: ch_size(ch_size)
, ch_width(ch_width)
, ch_height(ch_height)
, px_width(ch_size * 8 * ch_width)
, px_height(ch_size * 8 * ch_height)
{
} }
int ch_size; class IoEvent {
int ch_width; public:
int ch_height; enum class Type {
int px_width; Quit,
int px_height; KeyPress,
KeyRelease,
};
struct Quit{};
struct KeyPress{ uint16_t key; };
struct KeyRelease{ uint16_t key; };
using Data = std::variant<Quit, KeyPress, KeyRelease>;
explicit IoEvent(Type type, Data data)
: m_type(type)
, m_data(std::move(data))
{}
Type type() const { return m_type; }
auto as_quit() -> Quit& { return std::get<Quit>(m_data); }
auto as_key_press() -> KeyPress& { return std::get<KeyPress>(m_data); }
auto as_key_release() -> KeyRelease& { return std::get<KeyRelease>(m_data); }
private:
Type m_type;
Data m_data;
};
struct ScreenProfile {
int m_px_size_raw_px = 4;
int m_ch_size_px = 8;
int m_width_ch = 40;
int m_height_ch = 24;
constexpr int px_size_raw_px() const { return m_px_size_raw_px; }
constexpr int ch_size_px() const { return m_ch_size_px; }
constexpr int width_ch() const { return m_width_ch; }
constexpr int height_ch() const { return m_height_ch; }
constexpr int width_raw_px() const { return px_size_raw_px() * ch_size_px() * width_ch(); }
constexpr int height_raw_px() const { return px_size_raw_px() * ch_size_px() * height_ch(); }
}; };
class IoDevice { class IoDevice {
public: public:
static auto create(int ch_size, int ch_width, int ch_height) static auto create()
-> std::expected<std::unique_ptr<IoDevice>, std::string>; -> std::expected<std::unique_ptr<IoDevice>, std::string>;
explicit IoDevice(ScreenProfile profile, explicit IoDevice(ScreenProfile profile,
@ -49,6 +85,8 @@ public:
auto set_char(uint16_t offset, uint8_t value) auto set_char(uint16_t offset, uint8_t value)
-> std::expected<void, std::string>; -> std::expected<void, std::string>;
auto poll_event() -> std::unique_ptr<IoEvent>;
private: private:
std::mutex mx; std::mutex mx;

View File

@ -8,6 +8,8 @@
#include <cstdlib> #include <cstdlib>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <iostream>
#include <string>
using namespace std::chrono_literals; using namespace std::chrono_literals;
@ -17,10 +19,14 @@ using R = Reg;
int main() int main()
{ {
auto device = IoDevice::create(4, 40, 24).value(); auto device = IoDevice::create().value();
device->set_char(0, 'A').value();
std::this_thread::sleep_for(1s); auto message = std::string("A-0123456789");
uint16_t offset = 0;
for (auto ch : message) {
device->set_char(offset, static_cast<uint8_t>(ch)).value();
offset += 1;
}
constexpr R r0 = R::R0; constexpr R r0 = R::R0;
constexpr R r1 = R::R1; constexpr R r1 = R::R1;
@ -76,6 +82,15 @@ int main()
for (uint16_t i = 0; i < 32; i += 2) { for (uint16_t i = 0; i < 32; i += 2) {
std::println("{: 4x}\t{}", i, vm.word(i)); std::println("{: 4x}\t{}", i, vm.word(i));
} }
while (true) {
auto event = device->poll_event();
if (event && event->type() == IoEvent::Type::Quit) {
break;
}
}
} }
extern "C" const char* __asan_default_options(void) extern "C" const char* __asan_default_options(void)

View File

@ -47,7 +47,7 @@ int VM::run_instruction()
ip, ip,
ins.operator uint16_t(), ins.operator uint16_t(),
op_str(op), op_str(op),
m_regs[reg_id(Reg::R0)]); m_regs[reg_value(Reg::R0)]);
switch (op) { switch (op) {
case Op::Nop: case Op::Nop:
@ -80,7 +80,7 @@ int VM::run_instruction()
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
std::println(stderr, "error: invalid address alignment"); std::println(stderr, "error: invalid address alignment");
*m_rfl |= 1 << flag_id(Flag::Err); *m_rfl |= 1 << flag_value(Flag::Err);
goto halt_execution; goto halt_execution;
} }
@ -98,7 +98,7 @@ int VM::run_instruction()
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
std::println(stderr, "error: invalid address alignment"); std::println(stderr, "error: invalid address alignment");
*m_rfl |= 1 << flag_id(Flag::Err); *m_rfl |= 1 << flag_value(Flag::Err);
goto halt_execution; goto halt_execution;
} }
@ -127,19 +127,19 @@ int VM::run_instruction()
auto op2 = ins.op2_or_imm(); auto op2 = ins.op2_or_imm();
if (op1 == op2) { if (op1 == op2) {
*m_rfl |= 1u << flag_id(Flag::Eq); *m_rfl |= 1u << flag_value(Flag::Eq);
} else { } else {
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Eq)); *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Eq));
} }
if (op1 < op2) { if (op1 < op2) {
*m_rfl |= 1u << flag_id(Flag::Be); *m_rfl |= 1u << flag_value(Flag::Be);
} else { } else {
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Be)); *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Be));
} }
if ((int16_t)op1 < (int16_t)op2) { if ((int16_t)op1 < (int16_t)op2) {
*m_rfl |= 1u << flag_id(Flag::Lt); *m_rfl |= 1u << flag_value(Flag::Lt);
} else { } else {
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Lt)); *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Lt));
} }
break; break;
} }
@ -202,6 +202,15 @@ halt_execution:
return 0; return 0;
} }
uint16_t VM::eat()
{
uint16_t ins = 0;
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 8;
ins |= m_mem[*m_rip + 1];
*m_rip += 2;
return ins;
}
const char* vc5::op_str(Op op) const char* vc5::op_str(Op op)
{ {
switch (op) { switch (op) {
@ -248,3 +257,4 @@ const char* vc5::op_str(Op op)
} }
return ">.<"; return ">.<";
} }

View File

@ -47,6 +47,11 @@ enum class Reg : uint16_t {
Rip = 9, Rip = 9,
}; };
constexpr uint16_t reg_value(Reg reg)
{
return std::to_underlying(reg);
}
enum class Flag : uint16_t { enum class Flag : uint16_t {
Zero = 0, Zero = 0,
Eq = 1, Eq = 1,
@ -55,7 +60,10 @@ enum class Flag : uint16_t {
Err = 4, Err = 4,
}; };
struct Ins; constexpr uint16_t flag_value(Flag flag)
{
return std::to_underlying(flag);
}
class VM { class VM {
public: public:
@ -68,7 +76,7 @@ public:
uint16_t reg(Reg reg) const uint16_t reg(Reg reg) const
{ {
return m_regs[reg_id(reg)]; return m_regs[reg_value(reg)];
}; };
uint16_t word(uint16_t addr) const uint16_t word(uint16_t addr) const
@ -78,6 +86,7 @@ public:
value |= m_mem[addr + 1]; value |= m_mem[addr + 1];
return value; return value;
} }
uint8_t byte(uint16_t addr) const uint8_t byte(uint16_t addr) const
{ {
return m_mem[addr]; return m_mem[addr];
@ -86,32 +95,14 @@ public:
private: private:
friend struct Ins; friend struct Ins;
static constexpr uint16_t reg_id(Reg reg)
{
return std::to_underlying(reg);
}
static constexpr uint16_t flag_id(Flag flag)
{
return std::to_underlying(flag);
}
int run_instruction(); int run_instruction();
uint16_t eat();
inline uint16_t eat()
{
uint16_t ins = 0;
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 8;
ins |= m_mem[*m_rip + 1];
*m_rip += 2;
return ins;
}
std::array<uint8_t, 65536> m_mem = {}; std::array<uint8_t, 65536> m_mem = {};
std::array<uint16_t, 10> m_regs = {}; std::array<uint16_t, 10> m_regs = {};
uint16_t* m_rfl = &m_regs[reg_id(Reg::Rfl)]; uint16_t* m_rfl = &m_regs[reg_value(Reg::Rfl)];
uint16_t* m_rip = &m_regs[reg_id(Reg::Rip)]; uint16_t* m_rip = &m_regs[reg_value(Reg::Rip)];
bool m_halted = false; bool m_halted = false;
}; };