diff --git a/src/io_device.cpp b/src/io_device.cpp index 34df0dd..1772394 100644 --- a/src/io_device.cpp +++ b/src/io_device.cpp @@ -15,60 +15,57 @@ namespace { bool sdl_is_initialized = false; -struct Char { - explicit constexpr Char(char ch, uint64_t data) - : ch(ch) - , data(data) - { +constexpr uint64_t char_data(uint8_t ch) +{ + 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::string> { - auto profile = ScreenProfile(ch_size, ch_width, ch_height); - auto px_width= profile.px_width; - auto px_height = profile.px_height; + auto profile = ScreenProfile(); + auto width = profile.width_raw_px(); + auto height = profile.height_raw_px(); if (!sdl_is_initialized) { 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_Renderer* renderer; - if (SDL_CreateWindowAndRenderer(px_width, px_height, 0, &window, &renderer) + if (SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer) != 0) { 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_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, - px_width, - px_height); + width, + height); if (texture == nullptr) { 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 it = std::find_if(charset.begin(), charset.end(), [&](Char ch) { - return ch.ch == value; - }); - uint64_t char_data = it->to_u64(); - SDL_Color* buffer; int pitch; int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch); @@ -129,29 +121,42 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value) return std::unexpected(SDL_GetError()); } - auto [ch_size, ch_width, ch_height, px_width, px_height] = m_profile; - std::println("ch_size = {}, ch_width = {}, ch_height = {}, px_width = {}, px_height = {}", ch_size, ch_width, ch_height, px_width, px_height); + constexpr auto white = SDL_Color { 0xff, 0xff, 0xff, 0xff }; + constexpr auto black = SDL_Color { 0x00, 0x00, 0x00, 0xff }; - for (int ch_y = 0; ch_y < 8; ++ch_y) { - for (int ch_x = 0; ch_x < 8; ++ch_x) { - bool ch - = char_data >> (ch_y * 8 + (8 - ch_x - 1)) - & 1; + int ch_size = m_profile.ch_size_px(); + int px_size = m_profile.px_size_raw_px(); + int raw_width = m_profile.width_raw_px(); + int width_ch = m_profile.width_ch(); - for (int px_y = 0; px_y < ch_size; ++px_y) { - for (int px_x = 0; px_x < ch_size; ++px_x) { + auto set_pixel = [&](int x, int y, bool ch) { + int raw_base_x = x * px_size; + int raw_base_y = y * px_size; - int x = (offset % 4 * ch_size + ch_x) * 4 - + px_x; - int y = (offset / 4 * ch_size + ch_y) * 4 - + px_y; + 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) { - buffer[y * px_width + x] = ch - ? SDL_Color { 0xff, 0xff, 0xff, 0xff } - : SDL_Color { 0x00, 0x00, 0x00, 0xff }; - } + 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) { + uint8_t row = char_data >> (7 - px_y) * ch_size & 0xff; + + for (int px_x = 0; px_x < ch_size; ++px_x) { + bool ch = row >> (7 - px_x) & 1; + + set_pixel(base_x + px_x, base_y + px_y, ch); + } } SDL_UnlockTexture(m_texture); @@ -161,3 +166,33 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value) return {}; } + +auto IoDevice::poll_event() + -> std::unique_ptr +{ + std::lock_guard lock(mx); + + SDL_Event event; + if (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + return std::make_unique(IoEvent::Type::Quit, IoEvent::Quit{}); + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) { + return std::make_unique(IoEvent::Type::Quit, IoEvent::Quit{}); + } + return std::make_unique( + IoEvent::Type::KeyPress, + IoEvent::KeyPress { static_cast(event.key.keysym.scancode) }); + case SDL_KEYUP: + return std::make_unique( + IoEvent::Type::KeyRelease, + IoEvent::KeyRelease { static_cast(event.key.keysym.scancode) }); + // default: + // std::println(stderr, "warning: unhandled event '{}'", event.type); + } + } + + return {}; +} + diff --git a/src/io_device.hpp b/src/io_device.hpp index 84bebe9..9b55670 100644 --- a/src/io_device.hpp +++ b/src/io_device.hpp @@ -5,6 +5,7 @@ #include #include #include +#include struct SDL_Window; struct SDL_Renderer; @@ -12,26 +13,61 @@ struct SDL_Texture; namespace vc5 { -struct ScreenProfile { - 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) - { - } +namespace events { - int ch_size; - int ch_width; - int ch_height; - int px_width; - int px_height; + +} + +class IoEvent { +public: + enum class Type { + Quit, + KeyPress, + KeyRelease, + }; + + struct Quit{}; + + struct KeyPress{ uint16_t key; }; + + struct KeyRelease{ uint16_t key; }; + + using Data = std::variant; + + 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(m_data); } + auto as_key_press() -> KeyPress& { return std::get(m_data); } + auto as_key_release() -> KeyRelease& { return std::get(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 { public: - static auto create(int ch_size, int ch_width, int ch_height) + static auto create() -> std::expected, std::string>; explicit IoDevice(ScreenProfile profile, @@ -49,6 +85,8 @@ public: auto set_char(uint16_t offset, uint8_t value) -> std::expected; + auto poll_event() -> std::unique_ptr; + private: std::mutex mx; diff --git a/src/main.cpp b/src/main.cpp index c08eef4..142f00c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include using namespace std::chrono_literals; @@ -17,10 +19,14 @@ using R = Reg; int main() { - auto device = IoDevice::create(4, 40, 24).value(); - device->set_char(0, 'A').value(); + auto device = IoDevice::create().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(ch)).value(); + offset += 1; + } constexpr R r0 = R::R0; constexpr R r1 = R::R1; @@ -76,6 +82,15 @@ int main() for (uint16_t i = 0; i < 32; i += 2) { 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) diff --git a/src/vm.cpp b/src/vm.cpp index 2337ca0..9eeed06 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -47,7 +47,7 @@ int VM::run_instruction() ip, ins.operator uint16_t(), op_str(op), - m_regs[reg_id(Reg::R0)]); + m_regs[reg_value(Reg::R0)]); switch (op) { case Op::Nop: @@ -80,7 +80,7 @@ int VM::run_instruction() if ((addr & 0b1) != 0) { std::println(stderr, "error: invalid address alignment"); - *m_rfl |= 1 << flag_id(Flag::Err); + *m_rfl |= 1 << flag_value(Flag::Err); goto halt_execution; } @@ -98,7 +98,7 @@ int VM::run_instruction() if ((addr & 0b1) != 0) { std::println(stderr, "error: invalid address alignment"); - *m_rfl |= 1 << flag_id(Flag::Err); + *m_rfl |= 1 << flag_value(Flag::Err); goto halt_execution; } @@ -127,19 +127,19 @@ int VM::run_instruction() auto op2 = ins.op2_or_imm(); if (op1 == op2) { - *m_rfl |= 1u << flag_id(Flag::Eq); + *m_rfl |= 1u << flag_value(Flag::Eq); } else { - *m_rfl &= (uint16_t)~(1u << flag_id(Flag::Eq)); + *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Eq)); } if (op1 < op2) { - *m_rfl |= 1u << flag_id(Flag::Be); + *m_rfl |= 1u << flag_value(Flag::Be); } 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) { - *m_rfl |= 1u << flag_id(Flag::Lt); + *m_rfl |= 1u << flag_value(Flag::Lt); } else { - *m_rfl &= (uint16_t)~(1u << flag_id(Flag::Lt)); + *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Lt)); } break; } @@ -202,6 +202,15 @@ halt_execution: return 0; } +uint16_t VM::eat() +{ + uint16_t ins = 0; + ins |= static_cast(m_mem[*m_rip]) << 8; + ins |= m_mem[*m_rip + 1]; + *m_rip += 2; + return ins; +} + const char* vc5::op_str(Op op) { switch (op) { @@ -248,3 +257,4 @@ const char* vc5::op_str(Op op) } return ">.<"; } + diff --git a/src/vm.hpp b/src/vm.hpp index 8e92316..a7cda64 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -47,6 +47,11 @@ enum class Reg : uint16_t { Rip = 9, }; +constexpr uint16_t reg_value(Reg reg) +{ + return std::to_underlying(reg); +} + enum class Flag : uint16_t { Zero = 0, Eq = 1, @@ -55,7 +60,10 @@ enum class Flag : uint16_t { Err = 4, }; -struct Ins; +constexpr uint16_t flag_value(Flag flag) +{ + return std::to_underlying(flag); +} class VM { public: @@ -68,7 +76,7 @@ public: 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 @@ -78,6 +86,7 @@ public: value |= m_mem[addr + 1]; return value; } + uint8_t byte(uint16_t addr) const { return m_mem[addr]; @@ -86,32 +95,14 @@ public: private: 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(); - - inline uint16_t eat() - { - uint16_t ins = 0; - ins |= static_cast(m_mem[*m_rip]) << 8; - ins |= m_mem[*m_rip + 1]; - *m_rip += 2; - return ins; - } + uint16_t eat(); std::array m_mem = {}; std::array m_regs = {}; - uint16_t* m_rfl = &m_regs[reg_id(Reg::Rfl)]; - uint16_t* m_rip = &m_regs[reg_id(Reg::Rip)]; + uint16_t* m_rfl = &m_regs[reg_value(Reg::Rfl)]; + uint16_t* m_rip = &m_regs[reg_value(Reg::Rip)]; bool m_halted = false; };