nearly works
This commit is contained in:
parent
d53c543ff4
commit
da56e9c8cc
53
programs/echo_keyboard.txt
Normal file
53
programs/echo_keyboard.txt
Normal 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
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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());
|
||||||
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
92
src/main.cpp
92
src/main.cpp
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
348
src/vm.cpp
348
src/vm.cpp
@ -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 ">.<";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
146
src/vm.hpp
146
src/vm.hpp
@ -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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user