vc5/src/vm.hpp
2026-01-13 23:01:39 +01:00

134 lines
2.2 KiB
C++

#pragma once
#include "io_device.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <span>
#include <utility>
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<uint8_t, 65536> m_mem = {};
std::array<uint16_t, 10> 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 {};
};
}