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.imm_without_flag(offset);
i.op2_reg(op2); i.op2_reg(op2);
i.build(*this); 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) 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.imm(op2);
i.build(*this); 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 add_imm(Reg dst, Reg op1, uint16_t op2);
void sub_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 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 uint16_t ip() const
{ {

View File

@ -60,8 +60,7 @@ constexpr uint64_t char_data(uint8_t ch)
} }
auto IoDevice::create() auto IoDevice::create() -> std::expected<std::unique_ptr<IoDevice>, std::string>
-> std::expected<std::unique_ptr<IoDevice>, std::string>
{ {
auto profile = ScreenProfile(); auto profile = ScreenProfile();
auto width = profile.width_raw_px(); auto width = profile.width_raw_px();
@ -167,8 +166,7 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value)
return {}; return {};
} }
auto IoDevice::poll_event() auto IoDevice::poll_event() -> std::unique_ptr<IoEvent>
-> std::unique_ptr<IoEvent>
{ {
std::lock_guard lock(mx); std::lock_guard lock(mx);
@ -176,23 +174,45 @@ auto IoDevice::poll_event()
if (SDL_PollEvent(&event)) { if (SDL_PollEvent(&event)) {
switch (event.type) { switch (event.type) {
case SDL_QUIT: 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: case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) { 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>( return std::make_unique<IoEvent>(IoEvent::Type::KeyPress,
IoEvent::Type::KeyPress, IoEvent::KeyEvent {
IoEvent::KeyPress { static_cast<uint16_t>(event.key.keysym.scancode) }); .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: case SDL_KEYUP:
return std::make_unique<IoEvent>( return std::make_unique<IoEvent>(IoEvent::Type::KeyRelease,
IoEvent::Type::KeyRelease, IoEvent::KeyEvent {
IoEvent::KeyRelease { static_cast<uint16_t>(event.key.keysym.scancode) }); .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: // default:
// std::println(stderr, "warning: unhandled event '{}'", event.type); // std::println(stderr, "warning: unhandled event '{}'",
// event.type);
} }
} }
return {}; 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 { namespace events {
} }
class IoEvent { class IoEvent {
@ -26,23 +25,36 @@ public:
KeyRelease, 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<Quit, KeyEvent>;
using Data = std::variant<Quit, KeyPress, KeyRelease>;
explicit IoEvent(Type type, Data data) explicit IoEvent(Type type, Data data)
: m_type(type) : m_type(type)
, m_data(std::move(data)) , m_data(std::move(data))
{} {
}
Type type() const { return m_type; } Type type() const
auto as_quit() -> Quit& { return std::get<Quit>(m_data); } {
auto as_key_press() -> KeyPress& { return std::get<KeyPress>(m_data); } return m_type;
auto as_key_release() -> KeyRelease& { return std::get<KeyRelease>(m_data); } }
auto as_quit() -> Quit&
{
return std::get<Quit>(m_data);
}
auto as_key_event() -> KeyEvent&
{
return std::get<KeyEvent>(m_data);
}
private: private:
Type m_type; Type m_type;
@ -55,14 +67,32 @@ struct ScreenProfile {
int m_width_ch = 40; int m_width_ch = 40;
int m_height_ch = 24; int m_height_ch = 24;
constexpr int px_size_raw_px() const { return m_px_size_raw_px; } constexpr int px_size_raw_px() const
constexpr int ch_size_px() const { return m_ch_size_px; } {
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 width_ch() const
constexpr int height_ch() const { return m_height_ch; } {
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 width_raw_px() const
constexpr int height_raw_px() const { return px_size_raw_px() * ch_size_px() * height_ch(); } {
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 {
@ -87,6 +117,8 @@ public:
auto poll_event() -> std::unique_ptr<IoEvent>; auto poll_event() -> std::unique_ptr<IoEvent>;
void set_title(const std::string& title);
private: private:
std::mutex mx; std::mutex mx;

View File

@ -1,16 +1,16 @@
#include "builder.hpp" #include "builder.hpp"
#include "io_device.hpp" #include "io_device.hpp"
#include "vm.hpp"
#include <SDL2/SDL_main.h> #include <SDL2/SDL_main.h>
#include <array> #include <array>
#include <cstdint>
#include <print>
#include <utility>
#include <cstdlib>
#include <thread>
#include <chrono> #include <chrono>
#include <cstdint>
#include <cstdlib>
#include <iostream> #include <iostream>
#include <print>
#include <string> #include <string>
#include <thread>
#include <utility>
using namespace std::chrono_literals; using namespace std::chrono_literals;
@ -21,13 +21,6 @@ int main()
{ {
auto device = IoDevice::create().value(); 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 r0 = R::R0;
constexpr R r1 = R::R1; constexpr R r1 = R::R1;
constexpr R r2 = R::R2; constexpr R r2 = R::R2;
@ -39,32 +32,67 @@ int main()
constexpr R rfl = R::Rfl; constexpr R rfl = R::Rfl;
constexpr R rip = R::Rip; constexpr R rip = R::Rip;
auto program = std::array<uint8_t, 65535>(); auto program = std::array<uint8_t, 65536>();
auto l = Builder(program.data()); 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.mov_imm(r0, 0x2000);
l.cmp_imm(r0, 0); l.store_word_imm(0x700, r0);
l.mov_reg(r1, rfl); l.lvcd_imm(0x700);
l.and_imm(r1, r1, 1 << std::to_underlying(Flag::Eq));
auto i0 = l.ip(); l.mov_imm(r0, 0x1ffc);
l.jnz_imm(r1, 0); 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.mov_imm(r0, 0);
l.jmp_imm(l0); l.store_word_imm(0x600, r0);
auto l1 = l.ip(); auto main_loop = l.ip();
l.hlt(); l.hlt();
l.jmp_imm(main_loop);
l.set_ip(i0 + 2); auto key_press_int = l.ip();
l.push(l1); 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()); vm.load(0, program.data(), program.size());
device->set_title("vc5");
vm.run(); vm.run();
std::println("--- regs ---"); std::println("--- regs ---");
@ -82,19 +110,9 @@ 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)
{ {
return "detect_leaks=0"; return "detect_leaks=0";
} }

View File

@ -1,53 +1,178 @@
#include "vm.hpp" #include "vm.hpp"
#include <cstdint> #include <cstdint>
#include <print> #include <print>
#include <stdexcept>
#include <utility>
using namespace vc5; 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 { namespace {
uint16_t mem_addr(Ins& ins, uint16_t imm) struct InsReader {
{ InsReader(VM& vm)
bool is_imm = ins >> 6 & 1; : 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<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) { if (is_imm) {
return imm; return imm;
} }
auto base = ins.op2(); auto base = op2();
auto offset = static_cast<int16_t>(imm); auto offset = static_cast<int16_t>(imm);
if (offset >= 0) { if (offset >= 0) {
return base + static_cast<uint16_t>(offset); return base + static_cast<uint16_t>(offset);
} else { } else {
return base - static_cast<uint16_t>(-offset); 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() int VM::run_instruction()
{ {
auto ip = *m_rip; auto ip = *m_rip;
if (ip > 64) if (ip > 0x400)
return -1; return -1;
auto ins = Ins(*this); auto ins = InsReader(*this);
auto op = static_cast<Op>(ins & 0b11'1111); auto op = static_cast<Op>(ins.to_u16() & 0b11'1111);
std::println("[{: 4x}]: {:04x} {}, r0 = {}", std::println(" [{: 4x}]: {:04x} {}, r0 = {}",
ip, ip,
ins.operator uint16_t(), ins.to_u16(),
op_str(op), op_str(op),
m_regs[reg_value(Reg::R0)]); m_regs[std::to_underlying(Reg::R0)]);
switch (op) { switch (op) {
case Op::Nop: case Op::Nop:
@ -70,55 +195,64 @@ int VM::run_instruction()
break; break;
} }
case Op::Mov: { 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); auto src = ins.reg_val_or_imm(6, 7, 0b1111);
m_regs[dst] = src; set_reg(dst, src);
break; break;
} }
case Op::LoadWord: { case Op::LoadWord: {
auto addr = mem_addr(ins, eat()); auto addr = ins.mem_addr(eat_word());
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
std::println(stderr, "error: invalid address alignment"); std::println(stderr,
*m_rfl |= 1 << flag_value(Flag::Err); "error: invalid address alignment, addr = {}",
addr);
*m_rfl |= 1 << std::to_underlying(Flag::Err);
m_on = false;
goto halt_execution; goto halt_execution;
} }
auto reg = ins.dst_reg(); auto reg = ins.dst_reg();
set_reg(reg, word(addr));
uint16_t value = 0;
value |= static_cast<uint16_t>(m_mem[addr]) << 16;
value |= m_mem[addr + 1];
m_regs[reg] = value;
break; break;
} }
case Op::StoreWord: { case Op::StoreWord: {
auto addr = mem_addr(ins, eat()); auto addr = ins.mem_addr(eat_word());
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
std::println(stderr, "error: invalid address alignment"); std::println(stderr,
*m_rfl |= 1 << flag_value(Flag::Err); "error: invalid address alignment, addr = {}",
addr);
*m_rfl |= 1 << std::to_underlying(Flag::Err);
m_on = false;
goto halt_execution; goto halt_execution;
} }
auto src = ins.op2(); 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; break;
} }
case Op::LoadByte: { case Op::LoadByte: {
auto addr = mem_addr(ins, eat()); auto addr = ins.mem_addr(eat_word());
auto reg = ins.dst_reg(); auto reg = ins.dst_reg();
m_regs[reg] = m_mem[addr]; set_reg(reg, byte(addr));
break; break;
} }
case Op::StoreByte: { case Op::StoreByte: {
auto addr = mem_addr(ins, eat()); auto addr = ins.mem_addr(eat_word());
auto src = ins.op2(); auto src = ins.op2();
if (m_vcd_loaded) {
vcd_maybe_send_byte(addr, src & 0xff);
}
m_mem[addr] = src & 0xff; m_mem[addr] = src & 0xff;
break; break;
} }
@ -127,19 +261,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_value(Flag::Eq); *m_rfl |= 1u << std::to_underlying(Flag::Eq);
} else { } else {
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Eq)); *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Eq));
} }
if (op1 < op2) { if (op1 < op2) {
*m_rfl |= 1u << flag_value(Flag::Be); *m_rfl |= 1u << std::to_underlying(Flag::Be);
} else { } 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) { if ((int16_t)op1 < (int16_t)op2) {
*m_rfl |= 1u << flag_value(Flag::Lt); *m_rfl |= 1u << std::to_underlying(Flag::Lt);
} else { } else {
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Lt)); *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Lt));
} }
break; break;
} }
@ -157,7 +291,7 @@ int VM::run_instruction()
auto op2 = ins.op2_or_imm(); auto op2 = ins.op2_or_imm();
auto dst_reg = ins.dst_reg(); 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) { switch (op) {
case Op::Or: case Op::Or:
@ -196,19 +330,116 @@ int VM::run_instruction()
break; 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: halt_execution:
return 0; return 0;
} }
uint16_t VM::eat() void VM::poll_events()
{ {
uint16_t ins = 0; while (true) {
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 8; auto event = m_device->poll_event();
ins |= m_mem[*m_rip + 1];
*m_rip += 2; if (not event)
return ins; 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) const char* vc5::op_str(Op op)
@ -254,7 +485,12 @@ const char* vc5::op_str(Op op)
return "Sub"; return "Sub";
case Op::RSub: case Op::RSub:
return "RSub"; return "RSub";
case Op::RetI:
return "RetI";
case Op::LVCD:
return "LVCD";
case Op::LKBD:
return "LKBD";
} }
return ">.<"; return ">.<";
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "io_device.hpp"
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@ -30,6 +31,13 @@ enum class Op : uint16_t {
Add, Add,
Sub, Sub,
RSub, RSub,
// return from interrupt
RetI,
// load video character display
LVCD,
// load keyboard
LKBD,
}; };
const char* op_str(Op op); const char* op_str(Op op);
@ -47,11 +55,6 @@ 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,
@ -60,114 +63,71 @@ enum class Flag : uint16_t {
Err = 4, Err = 4,
}; };
constexpr uint16_t flag_value(Flag flag) struct VcdDescript {
{ uint16_t map_base_addr;
return std::to_underlying(flag); };
}
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 { class VM {
public: 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(); int run();
uint16_t reg(Reg reg) const auto reg(Reg reg) const -> uint16_t;
{ auto word(uint16_t addr) const -> uint16_t;
return m_regs[reg_value(reg)]; auto byte(uint16_t addr) const -> uint8_t;
};
uint16_t word(uint16_t addr) const void set_reg(Reg reg, uint16_t value);
{ void set_word(uint16_t addr, uint16_t value);
uint16_t value = 0; void set_byte(uint16_t addr, uint8_t value);
value |= static_cast<uint16_t>(m_mem[addr]) << 8;
value |= m_mem[addr + 1];
return value;
}
uint8_t byte(uint16_t addr) const uint16_t eat_word();
{
return m_mem[addr];
}
private: private:
friend struct Ins;
int run_instruction(); 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<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_value(Reg::Rfl)]; uint16_t* m_rsp = &m_regs[std::to_underlying(Reg::Rsp)];
uint16_t* m_rip = &m_regs[reg_value(Reg::Rip)]; 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_halted = false;
}; bool m_interrupts_enabled = true;
bool m_vcd_loaded = false;
bool m_kbd_loaded = false;
struct Ins { VcdDescript m_vcd_descriptor {};
Ins(VM& vm) KbdDescript m_kbd_descriptor {};
: 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];
}
}
}; };
} }