#pragma once #include "io_device.hpp" #include #include #include #include #include #include namespace vc5 { enum class Op : uint16_t { Nop, Hlt, Jmp, Jnz, Mov, LoadWord, StoreWord, LoadByte, StoreByte, Cmp, Or, And, Xor, Shl, RShl, Shr, RShr, Add, Sub, RSub, // return from interrupt RetI, // load video character display LVCD, // load keyboard LKBD, }; const char* op_str(Op op); enum class Reg : uint16_t { R0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, R5 = 5, Rbp = 6, Rsp = 7, Rfl = 8, Rip = 9, }; enum class Flag : uint16_t { Zero = 0, Eq = 1, Be = 2, Lt = 3, Err = 4, }; 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: VM(IoDevice& device) : m_device(&device) { } void load(uint16_t offset, const uint8_t* data, size_t data_size); int run(); auto reg(Reg reg) const -> uint16_t; auto word(uint16_t addr) const -> uint16_t; auto byte(uint16_t addr) const -> uint8_t; 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); uint16_t eat_word(); private: int run_instruction(); 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_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; VcdDescript m_vcd_descriptor {}; KbdDescript m_kbd_descriptor {}; }; }