nearly works

This commit is contained in:
sfja 2026-01-13 23:01:39 +01:00
parent d53c543ff4
commit da56e9c8cc
8 changed files with 586 additions and 225 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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
{

View File

@ -60,8 +60,7 @@ constexpr uint64_t char_data(uint8_t ch)
}
auto IoDevice::create()
-> std::expected<std::unique_ptr<IoDevice>, std::string>
auto IoDevice::create() -> std::expected<std::unique_ptr<IoDevice>, 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<IoEvent>
auto IoDevice::poll_event() -> std::unique_ptr<IoEvent>
{
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>(IoEvent::Type::Quit, IoEvent::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::Quit, IoEvent::Quit {});
}
return std::make_unique<IoEvent>(
IoEvent::Type::KeyPress,
IoEvent::KeyPress { static_cast<uint16_t>(event.key.keysym.scancode) });
return std::make_unique<IoEvent>(IoEvent::Type::KeyPress,
IoEvent::KeyEvent {
.key = static_cast<uint16_t>(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>(
IoEvent::Type::KeyRelease,
IoEvent::KeyRelease { static_cast<uint16_t>(event.key.keysym.scancode) });
return std::make_unique<IoEvent>(IoEvent::Type::KeyRelease,
IoEvent::KeyEvent {
.key = static_cast<uint16_t>(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);
// std::println(stderr, "warning: unhandled event '{}'",
// event.type);
}
}
return {};
}
void IoDevice::set_title(const std::string& title)
{
SDL_SetWindowTitle(m_window, title.c_str());
}

View File

@ -15,7 +15,6 @@ namespace vc5 {
namespace events {
}
class IoEvent {
@ -28,21 +27,34 @@ public:
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<Quit, KeyPress, KeyRelease>;
using Data = std::variant<Quit, KeyEvent>;
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); }
Type type() const
{
return m_type;
}
auto as_quit() -> Quit&
{
return std::get<Quit>(m_data);
}
auto as_key_event() -> KeyEvent&
{
return std::get<KeyEvent>(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<IoEvent>;
void set_title(const std::string& title);
private:
std::mutex mx;

View File

@ -1,16 +1,16 @@
#include "builder.hpp"
#include "io_device.hpp"
#include "vm.hpp"
#include <SDL2/SDL_main.h>
#include <array>
#include <cstdint>
#include <print>
#include <utility>
#include <cstdlib>
#include <thread>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <print>
#include <string>
#include <thread>
#include <utility>
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<uint8_t>(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<uint8_t, 65535>();
auto program = std::array<uint8_t, 65536>();
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";
}

View File

@ -1,19 +1,154 @@
#include "vm.hpp"
#include <cstdint>
#include <print>
#include <stdexcept>
#include <utility>
using namespace vc5;
void VM::load(uint16_t offset, const uint8_t* data, size_t data_size)
{
std::memcpy(&m_mem[offset], data, data_size);
}
int VM::run()
{
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<uint16_t>(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 {
uint16_t mem_addr(Ins& ins, uint16_t imm)
struct InsReader {
InsReader(VM& vm)
: vm(&vm)
, ins(vm.eat_word())
{
bool is_imm = ins >> 6 & 1;
}
VM* vm;
uint16_t ins;
auto to_u16() const -> uint16_t
{
return ins;
}
auto dst_reg() const -> Reg
{
return static_cast<Reg>(ins >> 13 & 0b111);
}
auto op1_reg() const -> Reg
{
return static_cast<Reg>(ins >> 10 & 0b111);
}
auto op2_reg() const -> Reg
{
return static_cast<Reg>(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<Reg>(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 = ins.op2();
auto base = op2();
auto offset = static_cast<int16_t>(imm);
if (offset >= 0) {
return base + static_cast<uint16_t>(offset);
@ -21,33 +156,23 @@ uint16_t mem_addr(Ins& ins, uint16_t imm)
return base - static_cast<uint16_t>(-offset);
}
}
};
}
int VM::run()
{
m_halted = false;
while (not m_halted) {
int result = run_instruction();
if (result != 0)
return result;
}
return 0;
}
int VM::run_instruction()
{
auto ip = *m_rip;
if (ip > 64)
if (ip > 0x400)
return -1;
auto ins = Ins(*this);
auto op = static_cast<Op>(ins & 0b11'1111);
auto ins = InsReader(*this);
auto op = static_cast<Op>(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<uint16_t>(ins >> 12 & 0b1111);
auto dst = static_cast<Reg>(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<uint16_t>(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<uint16_t>(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<char>(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 ">.<";
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "io_device.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
@ -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<uint16_t>(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<uint8_t, 65536> m_mem = {};
std::array<uint16_t, 10> 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 {};
};
}