diff --git a/programs/echo_keyboard.txt b/programs/echo_keyboard.txt new file mode 100644 index 0000000..334888f --- /dev/null +++ b/programs/echo_keyboard.txt @@ -0,0 +1,53 @@ + +%define KBD_STATUS 0x1ffc +%define KBD_CODE 0x1ffe +%define VCD 0x2000 + +%define KBD_FLAG_IS_RELEASE 0x1 + + mov rsp, 0x1000 + mov rbp, rsp + + ; setup vcd + mov r0, VCD + mov [0x700], r0 + lvcd 0x700 + + ; setup kbd + mov r0, KBD_STATUS + mov [0x702], r0 + mov r0, KBD_CODE + mov [0x704], r0 + mov r0, key_press_int + mov [0x706], r0 + lkbd 0x702 + + ; init counter + mov r0, 0 + mov [0x600], r0 + +main_loop: + hlt + jmp main_loop + +key_press_int: + mov r0, [KBD_STATUS] + and r0, r0, KBD_FLAG_IS_RELEASE + jnz r0, .leave + + mov r0, [0x600] + add r0, r0, VCD + mov r1, [KBC_CODE] + add r1, r1, 61 + mov byte [r0], r1 + + mov r0, [0x600] + add r0, r0, 1 + mov [0x600], r0 + +.leave: + reti + + +; vim: syntax=nasm + diff --git a/src/builder.cpp b/src/builder.cpp index a906185..78cac52 100644 --- a/src/builder.cpp +++ b/src/builder.cpp @@ -184,6 +184,9 @@ void Builder::store_byte_reg(Reg dst, uint16_t offset, Reg op2) i.imm_without_flag(offset); i.op2_reg(op2); i.build(*this); + std::println("store_byte_reg = {:08b}{:08b}", + this->m_data[m_ip - 4], + this->m_data[m_ip - 3]); } void Builder::store_byte_imm(uint16_t dst, Reg op2) @@ -327,3 +330,37 @@ void Builder::binary_imm(Reg dst, Reg op1, uint16_t op2, Op op) i.imm(op2); i.build(*this); } + +void Builder::reti() +{ + auto i = InsBuilder(Op::RetI); + i.build(*this); +} + +void Builder::lvcd_reg(Reg op1) +{ + auto i = InsBuilder(Op::LVCD); + i.op1_reg(op1); + i.build(*this); +} + +void Builder::lvcd_imm(uint16_t op1) +{ + auto i = InsBuilder(Op::LVCD); + i.imm(op1); + i.build(*this); +} + +void Builder::lkbd_reg(Reg op1) +{ + auto i = InsBuilder(Op::LKBD); + i.op1_reg(op1); + i.build(*this); +} + +void Builder::lkbd_imm(uint16_t op1) +{ + auto i = InsBuilder(Op::LKBD); + i.imm(op1); + i.build(*this); +} diff --git a/src/builder.hpp b/src/builder.hpp index 69b1adc..9135844 100644 --- a/src/builder.hpp +++ b/src/builder.hpp @@ -53,6 +53,11 @@ public: void add_imm(Reg dst, Reg op1, uint16_t op2); void sub_imm(Reg dst, Reg op1, uint16_t op2); void rsub_imm(Reg dst, Reg op1, uint16_t op2); + void reti(); + void lvcd_reg(Reg op1); + void lvcd_imm(uint16_t op1); + void lkbd_reg(Reg op1); + void lkbd_imm(uint16_t op1); uint16_t ip() const { diff --git a/src/io_device.cpp b/src/io_device.cpp index 1772394..e5f7e17 100644 --- a/src/io_device.cpp +++ b/src/io_device.cpp @@ -18,7 +18,7 @@ bool sdl_is_initialized = false; constexpr uint64_t char_data(uint8_t ch) { switch (ch) { - // clang-format off + // clang-format off case ' ': return 0x0000000000000000; case '0': return 0x003C666E7666663C; @@ -54,14 +54,13 @@ constexpr uint64_t char_data(uint8_t ch) case 'Y': return 0x0066663C18181818; case 'Z': return 0x007E7E0C18307E7E; default: return 0x55AA55AA55AA55AA; - // clang-format on + // clang-format on } }; } -auto IoDevice::create() - -> std::expected, std::string> +auto IoDevice::create() -> std::expected, std::string> { auto profile = ScreenProfile(); auto width = profile.width_raw_px(); @@ -167,8 +166,7 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value) return {}; } -auto IoDevice::poll_event() - -> std::unique_ptr +auto IoDevice::poll_event() -> std::unique_ptr { std::lock_guard lock(mx); @@ -176,23 +174,45 @@ auto IoDevice::poll_event() if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: - return std::make_unique(IoEvent::Type::Quit, IoEvent::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::Quit, IoEvent::Quit {}); } - return std::make_unique( - IoEvent::Type::KeyPress, - IoEvent::KeyPress { static_cast(event.key.keysym.scancode) }); + return std::make_unique(IoEvent::Type::KeyPress, + IoEvent::KeyEvent { + .key = static_cast(event.key.keysym.scancode), + .shift_pressed + = (event.key.keysym.mod & KMOD_SHIFT) != 0, + .ctrl_pressed = (event.key.keysym.mod & KMOD_CTRL) != 0, + .alt_pressed = (event.key.keysym.mod & KMOD_LALT) != 0, + .altgr_pressed + = (event.key.keysym.mod & KMOD_RALT) != 0, + + }); 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 std::make_unique(IoEvent::Type::KeyRelease, + IoEvent::KeyEvent { + .key = static_cast(event.key.keysym.scancode), + .shift_pressed + = (event.key.keysym.mod & KMOD_SHIFT) != 0, + .ctrl_pressed = (event.key.keysym.mod & KMOD_CTRL) != 0, + .alt_pressed = (event.key.keysym.mod & KMOD_LALT) != 0, + .altgr_pressed + = (event.key.keysym.mod & KMOD_RALT) != 0, + }); + // default: + // std::println(stderr, "warning: unhandled event '{}'", + // event.type); } } return {}; } +void IoDevice::set_title(const std::string& title) +{ + SDL_SetWindowTitle(m_window, title.c_str()); +} diff --git a/src/io_device.hpp b/src/io_device.hpp index 9b55670..aa5be75 100644 --- a/src/io_device.hpp +++ b/src/io_device.hpp @@ -15,7 +15,6 @@ namespace vc5 { namespace events { - } class IoEvent { @@ -26,23 +25,36 @@ public: KeyRelease, }; - struct Quit{}; + struct Quit { }; - struct KeyPress{ uint16_t key; }; + struct KeyEvent { + uint16_t key; + bool shift_pressed; + bool ctrl_pressed; + bool alt_pressed; + bool altgr_pressed; + }; - struct KeyRelease{ uint16_t key; }; - - using Data = std::variant; + 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); } + Type type() const + { + return m_type; + } + auto as_quit() -> Quit& + { + return std::get(m_data); + } + auto as_key_event() -> KeyEvent& + { + return std::get(m_data); + } private: Type m_type; @@ -55,14 +67,32 @@ struct ScreenProfile { 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 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_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(); } + 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 { @@ -87,6 +117,8 @@ public: auto poll_event() -> std::unique_ptr; + void set_title(const std::string& title); + private: std::mutex mx; diff --git a/src/main.cpp b/src/main.cpp index 142f00c..05758f0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,16 @@ #include "builder.hpp" #include "io_device.hpp" +#include "vm.hpp" #include #include -#include -#include -#include -#include -#include #include +#include +#include #include +#include #include - +#include +#include using namespace std::chrono_literals; @@ -21,13 +21,6 @@ int main() { auto device = IoDevice::create().value(); - 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; constexpr R r2 = R::R2; @@ -39,32 +32,67 @@ int main() constexpr R rfl = R::Rfl; constexpr R rip = R::Rip; - auto program = std::array(); + auto program = std::array(); auto l = Builder(program.data()); - l.mov_imm(r0, 4); + l.mov_imm(rsp, 0x1000); + l.mov_reg(rbp, rsp); - auto l0 = l.ip(); - l.cmp_imm(r0, 0); - l.mov_reg(r1, rfl); - l.and_imm(r1, r1, 1 << std::to_underlying(Flag::Eq)); + l.mov_imm(r0, 0x2000); + l.store_word_imm(0x700, r0); + l.lvcd_imm(0x700); - auto i0 = l.ip(); - l.jnz_imm(r1, 0); + l.mov_imm(r0, 0x1ffc); + l.store_word_imm(0x702, r0); + l.mov_imm(r0, 0x1ffe); + l.store_word_imm(0x704, r0); + auto to_set_key_press_int = l.ip(); + l.mov_imm(r0, 0); + l.store_word_imm(0x706, r0); + l.lkbd_imm(0x702); - l.sub_imm(r0, r0, 1); - l.jmp_imm(l0); + l.mov_imm(r0, 0); + l.store_word_imm(0x600, r0); - auto l1 = l.ip(); + auto main_loop = l.ip(); l.hlt(); + l.jmp_imm(main_loop); - l.set_ip(i0 + 2); - l.push(l1); + auto key_press_int = l.ip(); + l.load_word_imm(r0, 0x1ffc); + l.and_imm(r0, r0, 1); + auto to_set_key_press_int_leave = l.ip(); + l.jnz_imm(r0, 0); - auto vm = VM(); + l.load_word_imm(r0, 0x600); + l.add_imm(r0, r0, 0x2000); + // l.load_word_imm(r1, 0x1ffe); + // l.add_imm(r1, r1, 61); + l.mov_imm(r1, 'A'); + l.nop(); + l.nop(); + l.store_byte_reg(r0, 0, r1); + l.nop(); + l.nop(); + + l.load_word_imm(r0, 0x600); + l.add_imm(r0, r0, 1); + l.store_word_imm(0x600, r0); + + auto key_press_int_leave = l.ip(); + l.reti(); + + l.set_ip(to_set_key_press_int + 2); + l.push(key_press_int); + l.set_ip(to_set_key_press_int_leave + 2); + l.push(key_press_int_leave); + + auto vm = VM(*device); vm.load(0, program.data(), program.size()); + device->set_title("vc5"); + vm.run(); std::println("--- regs ---"); @@ -82,19 +110,9 @@ 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) { return "detect_leaks=0"; } - diff --git a/src/vm.cpp b/src/vm.cpp index 9eeed06..bb548e7 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1,53 +1,178 @@ #include "vm.hpp" #include #include +#include +#include using namespace vc5; -namespace { - -uint16_t mem_addr(Ins& ins, uint16_t imm) +void VM::load(uint16_t offset, const uint8_t* data, size_t data_size) { - bool is_imm = ins >> 6 & 1; - if (is_imm) { - return imm; - } - - auto base = ins.op2(); - auto offset = static_cast(imm); - if (offset >= 0) { - return base + static_cast(offset); - } else { - return base - static_cast(-offset); - } -} - + std::memcpy(&m_mem[offset], data, data_size); } int VM::run() { - m_halted = false; - while (not m_halted) { - int result = run_instruction(); - if (result != 0) - return result; + m_on = true; + while (m_on) { + if (not m_halted) { + int result = run_instruction(); + if (result != 0) + return result; + } + + poll_events(); } return 0; } +uint16_t VM::reg(Reg reg) const +{ + return m_regs[std::to_underlying(reg)]; +} + +uint16_t VM::word(uint16_t addr) const +{ + if ((addr & 0b1) != 0) { + throw std::invalid_argument( + std::format("invalid address alignment, addr = {}", addr)); + } + + uint16_t value = 0; + value |= static_cast(m_mem[addr]) << 8; + value |= m_mem[addr + 1]; + return value; +} + +uint8_t VM::byte(uint16_t addr) const +{ + return m_mem[addr]; +} + +void VM::set_reg(Reg reg, uint16_t value) +{ + m_regs[std::to_underlying(reg)] = value; +} + +void VM::set_word(uint16_t addr, uint16_t value) +{ + if ((addr & 0b1) != 0) { + throw std::invalid_argument( + std::format("invalid address alignment, addr = {}", addr)); + } + + m_mem[addr] = value >> 8; + m_mem[addr + 1] = value & 0xff; +} + +void VM::set_byte(uint16_t addr, uint8_t value) +{ + m_mem[addr] = value; +} + +uint16_t VM::eat_word() +{ + auto ins = word(*m_rip); + *m_rip += 2; + return ins; +} + +namespace { + +struct InsReader { + InsReader(VM& vm) + : vm(&vm) + , ins(vm.eat_word()) + { + } + + VM* vm; + uint16_t ins; + + auto to_u16() const -> uint16_t + { + return ins; + } + + auto dst_reg() const -> Reg + { + return static_cast(ins >> 13 & 0b111); + } + + auto op1_reg() const -> Reg + { + return static_cast(ins >> 10 & 0b111); + } + + auto op2_reg() const -> Reg + { + return static_cast(ins >> 7 & 0b111); + } + + auto op1() const -> uint16_t + { + return vm->reg(op1_reg()); + } + + auto op2() const -> uint16_t + { + + return vm->reg(op2_reg()); + } + + auto op1_or_imm() -> uint16_t + { + return reg_val_or_imm(6, 10, 0b111); + } + + auto op2_or_imm() -> uint16_t + { + return reg_val_or_imm(6, 7, 0b111); + } + + auto reg_val_or_imm( + uint16_t is_imm_bit, uint16_t reg_bit, uint16_t reg_mask) -> uint16_t + { + bool is_imm = (ins >> is_imm_bit & 1) != 0; + if (is_imm) { + return vm->eat_word(); + } else { + return vm->reg(static_cast(ins >> reg_bit & reg_mask)); + } + } + + auto mem_addr(uint16_t imm) const -> uint16_t + { + bool is_imm = to_u16() >> 6 & 1; + if (is_imm) { + return imm; + } + + auto base = op2(); + auto offset = static_cast(imm); + if (offset >= 0) { + return base + static_cast(offset); + } else { + return base - static_cast(-offset); + } + } +}; + +} + int VM::run_instruction() { auto ip = *m_rip; - if (ip > 64) + if (ip > 0x400) return -1; - auto ins = Ins(*this); - auto op = static_cast(ins & 0b11'1111); - std::println("[{: 4x}]: {:04x} {}, r0 = {}", + auto ins = InsReader(*this); + auto op = static_cast(ins.to_u16() & 0b11'1111); + std::println(" [{: 4x}]: {:04x} {}, r0 = {}", ip, - ins.operator uint16_t(), + ins.to_u16(), op_str(op), - m_regs[reg_value(Reg::R0)]); + m_regs[std::to_underlying(Reg::R0)]); switch (op) { case Op::Nop: @@ -70,55 +195,64 @@ int VM::run_instruction() break; } case Op::Mov: { - auto dst = static_cast(ins >> 12 & 0b1111); + auto dst = static_cast(ins.to_u16() >> 12 & 0b1111); auto src = ins.reg_val_or_imm(6, 7, 0b1111); - m_regs[dst] = src; + set_reg(dst, src); break; } case Op::LoadWord: { - auto addr = mem_addr(ins, eat()); + auto addr = ins.mem_addr(eat_word()); if ((addr & 0b1) != 0) { - std::println(stderr, "error: invalid address alignment"); - *m_rfl |= 1 << flag_value(Flag::Err); + std::println(stderr, + "error: invalid address alignment, addr = {}", + addr); + *m_rfl |= 1 << std::to_underlying(Flag::Err); + m_on = false; goto halt_execution; } auto reg = ins.dst_reg(); - - uint16_t value = 0; - value |= static_cast(m_mem[addr]) << 16; - value |= m_mem[addr + 1]; - - m_regs[reg] = value; + set_reg(reg, word(addr)); break; } case Op::StoreWord: { - auto addr = mem_addr(ins, eat()); + auto addr = ins.mem_addr(eat_word()); if ((addr & 0b1) != 0) { - std::println(stderr, "error: invalid address alignment"); - *m_rfl |= 1 << flag_value(Flag::Err); + std::println(stderr, + "error: invalid address alignment, addr = {}", + addr); + *m_rfl |= 1 << std::to_underlying(Flag::Err); + m_on = false; goto halt_execution; } - auto src = ins.op2(); - m_mem[addr] = src >> 16; - m_mem[addr + 1] = src & 0xff; + + if (m_vcd_loaded) { + vcd_maybe_send_byte(addr, src >> 8); + vcd_maybe_send_byte(addr + 1, src & 0xff); + } + + set_word(addr, src); break; } case Op::LoadByte: { - auto addr = mem_addr(ins, eat()); + auto addr = ins.mem_addr(eat_word()); auto reg = ins.dst_reg(); - m_regs[reg] = m_mem[addr]; + set_reg(reg, byte(addr)); break; } case Op::StoreByte: { - auto addr = mem_addr(ins, eat()); + auto addr = ins.mem_addr(eat_word()); auto src = ins.op2(); + if (m_vcd_loaded) { + vcd_maybe_send_byte(addr, src & 0xff); + } + m_mem[addr] = src & 0xff; break; } @@ -127,19 +261,19 @@ int VM::run_instruction() auto op2 = ins.op2_or_imm(); if (op1 == op2) { - *m_rfl |= 1u << flag_value(Flag::Eq); + *m_rfl |= 1u << std::to_underlying(Flag::Eq); } else { - *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Eq)); + *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Eq)); } if (op1 < op2) { - *m_rfl |= 1u << flag_value(Flag::Be); + *m_rfl |= 1u << std::to_underlying(Flag::Be); } else { - *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Be)); + *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Be)); } if ((int16_t)op1 < (int16_t)op2) { - *m_rfl |= 1u << flag_value(Flag::Lt); + *m_rfl |= 1u << std::to_underlying(Flag::Lt); } else { - *m_rfl &= (uint16_t)~(1u << flag_value(Flag::Lt)); + *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Lt)); } break; } @@ -157,7 +291,7 @@ int VM::run_instruction() auto op2 = ins.op2_or_imm(); auto dst_reg = ins.dst_reg(); - uint16_t* dst = &m_regs[dst_reg]; + uint16_t* dst = &m_regs[std::to_underlying(dst_reg)]; switch (op) { case Op::Or: @@ -196,19 +330,116 @@ int VM::run_instruction() break; } + case Op::RetI: { + *m_rsp -= 2; + auto return_addr = word(*m_rsp); + + *m_rip = return_addr; + + m_interrupts_enabled = true; + break; + } + case Op::LVCD: { + auto op1 = ins.op1_or_imm(); + m_vcd_descriptor = VcdDescript { + .map_base_addr = word(op1), + }; + m_vcd_loaded = true; + + break; + } + case Op::LKBD: { + auto op1 = ins.op1_or_imm(); + m_kbd_descriptor = KbdDescript { + .status_addr = word(op1), + .keycode_addr = word(op1 + 2), + .int_handler = word(op1 + 4), + }; + m_kbd_loaded = true; + } } halt_execution: return 0; } -uint16_t VM::eat() +void VM::poll_events() { - uint16_t ins = 0; - ins |= static_cast(m_mem[*m_rip]) << 8; - ins |= m_mem[*m_rip + 1]; - *m_rip += 2; - return ins; + while (true) { + auto event = m_device->poll_event(); + + if (not event) + break; + + if (event->type() == IoEvent::Type::Quit) { + m_on = false; + break; + } + + if (event->type() == IoEvent::Type::KeyPress + or event->type() == IoEvent::Type::KeyRelease) { + if (m_kbd_loaded) { + auto& d = m_kbd_descriptor; + + uint16_t flags = 0; + if (event->type() == IoEvent::Type::KeyRelease) { + flags |= 1 << std::to_underlying(KbdStatusFlag::Type); + } + if (event->as_key_event().shift_pressed) { + flags |= 1 << std::to_underlying(KbdStatusFlag::Shift); + } + if (event->as_key_event().ctrl_pressed) { + flags |= 1 << std::to_underlying(KbdStatusFlag::Ctrl); + } + if (event->as_key_event().alt_pressed) { + flags |= 1 << std::to_underlying(KbdStatusFlag::Alt); + } + if (event->as_key_event().altgr_pressed) { + flags |= 1 << std::to_underlying(KbdStatusFlag::AltGr); + } + + set_word(d.status_addr, flags); + set_word(d.keycode_addr, event->as_key_event().key); + interrupt(d.int_handler); + } + } + } +} + +void VM::vcd_maybe_send_byte(uint16_t addr, uint8_t value) +{ + if (not m_vcd_loaded) + return; + + constexpr uint16_t screen_size = 40 * 24; + + const auto& d = m_vcd_descriptor; + + std::println(" addr = {}", addr); + std::println(" printing = {}", + addr >= d.map_base_addr and addr < d.map_base_addr + screen_size); + + if (addr >= d.map_base_addr and addr < d.map_base_addr + screen_size) { + + std::println( + "printing {} or '{}' at {}", value, static_cast(value), addr); + + m_device->set_char(addr - d.map_base_addr, value).value(); + } +} + +void VM::interrupt(uint16_t handler_addr) +{ + if (not m_interrupts_enabled) + return; + + set_word(*m_rsp, *m_rip); + *m_rsp += 2; + + *m_rip = handler_addr; + + m_halted = false; + m_interrupts_enabled = false; } const char* vc5::op_str(Op op) @@ -254,7 +485,12 @@ const char* vc5::op_str(Op op) return "Sub"; case Op::RSub: return "RSub"; + case Op::RetI: + return "RetI"; + case Op::LVCD: + return "LVCD"; + case Op::LKBD: + return "LKBD"; } return ">.<"; } - diff --git a/src/vm.hpp b/src/vm.hpp index a7cda64..ca763fe 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -1,5 +1,6 @@ #pragma once +#include "io_device.hpp" #include #include #include @@ -30,6 +31,13 @@ enum class Op : uint16_t { Add, Sub, RSub, + + // return from interrupt + RetI, + // load video character display + LVCD, + // load keyboard + LKBD, }; const char* op_str(Op op); @@ -47,11 +55,6 @@ 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, @@ -60,114 +63,71 @@ enum class Flag : uint16_t { Err = 4, }; -constexpr uint16_t flag_value(Flag flag) -{ - return std::to_underlying(flag); -} +struct VcdDescript { + uint16_t map_base_addr; +}; + +struct KbdDescript { + uint16_t status_addr; + uint16_t keycode_addr; + uint16_t int_handler; +}; + +enum class KbdStatusFlag : uint16_t { + /// 0 Press + /// 1 Release + Type = 0, + Shift = 1, + Ctrl = 2, + Alt = 3, + AltGr = 4, +}; class VM { public: - void load(uint16_t offset, const uint8_t* data, size_t data_size) + VM(IoDevice& device) + : m_device(&device) { - std::memcpy(&m_mem[offset], data, data_size); } + void load(uint16_t offset, const uint8_t* data, size_t data_size); int run(); - uint16_t reg(Reg reg) const - { - return m_regs[reg_value(reg)]; - }; + auto reg(Reg reg) const -> uint16_t; + auto word(uint16_t addr) const -> uint16_t; + auto byte(uint16_t addr) const -> uint8_t; - uint16_t word(uint16_t addr) const - { - uint16_t value = 0; - value |= static_cast(m_mem[addr]) << 8; - value |= m_mem[addr + 1]; - return value; - } + void set_reg(Reg reg, uint16_t value); + void set_word(uint16_t addr, uint16_t value); + void set_byte(uint16_t addr, uint8_t value); - uint8_t byte(uint16_t addr) const - { - return m_mem[addr]; - } + uint16_t eat_word(); private: - friend struct Ins; - int run_instruction(); - uint16_t eat(); + void poll_events(); + + void vcd_maybe_send_byte(uint16_t addr, uint8_t value); + + void interrupt(uint16_t handler_addr); std::array m_mem = {}; std::array m_regs = {}; - uint16_t* m_rfl = &m_regs[reg_value(Reg::Rfl)]; - uint16_t* m_rip = &m_regs[reg_value(Reg::Rip)]; + uint16_t* m_rsp = &m_regs[std::to_underlying(Reg::Rsp)]; + uint16_t* m_rfl = &m_regs[std::to_underlying(Reg::Rfl)]; + uint16_t* m_rip = &m_regs[std::to_underlying(Reg::Rip)]; + IoDevice* m_device; + + bool m_on = true; bool m_halted = false; -}; + bool m_interrupts_enabled = true; + bool m_vcd_loaded = false; + bool m_kbd_loaded = false; -struct Ins { - Ins(VM& vm) - : vm(&vm) - , ins(vm.eat()) - { - } - - VM* vm; - uint16_t ins; - - operator uint16_t() - { - return ins; - } - - inline uint16_t dst_reg() const - { - return ins >> 13 & 0b111; - } - - inline uint16_t op1_reg() const - { - return ins >> 10 & 0b111; - } - - inline uint16_t op2_reg() const - { - return ins >> 7 & 0b111; - } - - inline uint16_t op1() const - { - return vm->m_regs[op1_reg()]; - } - - inline uint16_t op2() const - { - - return vm->m_regs[op2_reg()]; - } - - inline uint16_t op1_or_imm() - { - return reg_val_or_imm(6, 10, 0b111); - } - - inline uint16_t op2_or_imm() - { - return reg_val_or_imm(6, 7, 0b111); - } - - inline uint16_t reg_val_or_imm( - uint16_t is_imm_bit, uint16_t reg_bit, uint16_t reg_mask) - { - bool is_imm = (ins >> is_imm_bit & 1) != 0; - if (is_imm) { - return vm->eat(); - } else { - return vm->m_regs[ins >> reg_bit & reg_mask]; - } - } + VcdDescript m_vcd_descriptor {}; + KbdDescript m_kbd_descriptor {}; }; }