This commit is contained in:
sfja 2026-01-14 23:11:19 +01:00
parent 2e7ab57cc1
commit 2b423a40ed
9 changed files with 213 additions and 52 deletions

View File

@ -3,19 +3,30 @@
%define KBD_CODE 0x1ffe %define KBD_CODE 0x1ffe
%define VCD 0x2000 %define VCD 0x2000
%define FL_EQ 1 << 1 %define FL_EQ 0x2
%define FL_EQ 0x4
%define KBD_FLAG_IS_RELEASE 0x1 %define KBD_FLAG_IS_RELEASE 0x1
mov rsp, 0x1000 mov rsp, 0x1000
mov rbp, rsp mov rbp, rsp
; setup vcd mov r0, 512
mov r1, 1
dskr r0, r1
; setup video character display (VCD)
; descriptor table structure:
; [addr + 0] memory map base
mov r0, VCD mov r0, VCD
mov [0x700], r0 mov [0x700], r0
lvcd 0x700 lvcd 0x700
; setup kbd ; setup keyboard (KBD)
; descriptor table structure:
; [addr + 0] status address
; [addr + 2] keycode address
; [addr + 4] keyboard interrupt handler
mov r0, KBD_STATUS mov r0, KBD_STATUS
mov [0x702], r0 mov [0x702], r0
mov r0, KBD_CODE mov r0, KBD_CODE
@ -24,7 +35,7 @@
mov [0x706], r0 mov [0x706], r0
lkbd 0x702 lkbd 0x702
; init counter ; character counter
mov r0, 0 mov r0, 0
mov [0x600], r0 mov [0x600], r0
@ -50,10 +61,31 @@ key_press_int:
mov r0, [0x600] mov r0, [0x600]
add r0, r0, 1 add r0, r0, 1
mov [0x600], r0 mov [0x600], r0
.leave: .leave:
reti reti
scancode_to_char:
cmp r1, 61
mov r2, rfl
and r2, r2, FL_LT
jnz .not_letter
; if r1 > 86 { goto .not_letter }
cmp r1, 86
mov r2, rfl
xor r2, 0xffff
and r2, r2, FL_EQ | FL_LT
mov r4, rfl
and r4, r4, FL_EQ
shr r4, r4, 2
.not_letter:
61 >= 61
; vim: syntax=nasm ; vim: syntax=nasm

36
src/block_device.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "block_device.hpp"
#include <cstdint>
#include <cstring>
#include <fstream>
#include <ios>
using namespace vc5;
void MemoryDisk::read(uint8_t* data, uint16_t block)
{
std::memcpy(data, &m_data[block * block_size], block_size);
}
void MemoryDisk::write(const uint8_t* data, uint16_t block)
{
std::memcpy(&m_data[block * block_size], data, block_size);
}
auto FileDisk::block_count() -> uint16_t
{
m_file.seekg(0, std::ios_base::end);
std::streamsize location = m_file.tellg();
return location / block_size & 0xffff;
}
void FileDisk::read(uint8_t* data, uint16_t block)
{
m_file.seekg(block * block_size);
m_file.read(reinterpret_cast<char*>(data), block_size);
}
void FileDisk::write(const uint8_t* data, uint16_t block)
{
m_file.seekg(block * block_size);
m_file.write(reinterpret_cast<const char*>(data), block_size);
}

66
src/block_device.hpp Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <ios>
#include <vector>
namespace vc5 {
namespace fs = std::filesystem;
class BlockDevice {
public:
virtual ~BlockDevice() = default;
static constexpr uint16_t block_size = 512;
virtual auto block_count() -> uint16_t = 0;
virtual void read(uint8_t* data, uint16_t block) = 0;
virtual void write(const uint8_t* data, uint16_t block) = 0;
};
class MemoryDisk final : public BlockDevice {
public:
explicit MemoryDisk(uint16_t block_count)
: m_data(block_size * block_count, 0)
, m_block_count(block_count)
{
}
auto block_count() -> uint16_t override
{
return m_block_count;
}
void read(uint8_t* data, uint16_t block) override;
void write(const uint8_t* data, uint16_t block) override;
auto data() -> uint8_t*
{
return m_data.data();
}
private:
std::vector<uint8_t> m_data;
uint16_t m_block_count;
};
class FileDisk final : public BlockDevice {
public:
explicit FileDisk(fs::path file_path)
: m_file(file_path, std::ios_base::binary)
{
}
auto block_count() -> uint16_t override;
void read(uint8_t* data, uint16_t block) override;
void write(const uint8_t* data, uint16_t block) override;
private:
std::fstream m_file;
};
}

View File

@ -361,3 +361,19 @@ void Builder::lkbd_imm(uint16_t op1)
i.imm(op1); i.imm(op1);
i.build(*this); i.build(*this);
} }
void Builder::dskr(Reg dst, Reg op1)
{
auto i = InsBuilder(Op::DSKR);
i.dst_reg(dst);
i.op1_reg(op1);
i.build(*this);
}
void Builder::dskw(Reg dst, Reg op1)
{
auto i = InsBuilder(Op::DSKW);
i.dst_reg(dst);
i.op1_reg(op1);
i.build(*this);
}

View File

@ -58,6 +58,8 @@ public:
void lvcd_imm(uint16_t op1); void lvcd_imm(uint16_t op1);
void lkbd_reg(Reg op1); void lkbd_reg(Reg op1);
void lkbd_imm(uint16_t op1); void lkbd_imm(uint16_t op1);
void dskr(Reg dst, Reg op1);
void dskw(Reg dst, Reg op1);
uint16_t ip() const uint16_t ip() const
{ {

View File

@ -113,8 +113,6 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value)
{ {
auto lock = std::lock_guard(mx); auto lock = std::lock_guard(mx);
std::println("char = '{}' or '{}'", static_cast<char>(value), value);
SDL_Color* buffer; SDL_Color* buffer;
int pitch; int pitch;
int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch); int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch);
@ -161,7 +159,7 @@ auto IoDevice::set_char(uint16_t offset, uint8_t value)
} }
SDL_UnlockTexture(m_texture); SDL_UnlockTexture(m_texture);
SDL_RenderCopy(m_renderer, m_texture, NULL, NULL); SDL_RenderCopy(m_renderer, m_texture, nullptr, nullptr);
SDL_RenderPresent(m_renderer); SDL_RenderPresent(m_renderer);

View File

@ -1,44 +1,32 @@
#include "block_device.hpp"
#include "builder.hpp" #include "builder.hpp"
#include "io_device.hpp" #include "io_device.hpp"
#include "vm.hpp" #include "vm.hpp"
#include <SDL2/SDL_main.h> #include <SDL2/SDL_main.h>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <print> #include <print>
#include <string> #include <string>
#include <thread>
#include <utility>
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace vc5; using namespace vc5;
using R = Reg; using namespace vc5::regs;
int main() int main()
{ {
auto device = IoDevice::create().value(); auto device = IoDevice::create().value();
device->set_title("vc5");
constexpr R r0 = R::R0; auto disk = MemoryDisk(128);
constexpr R r1 = R::R1;
constexpr R r2 = R::R2;
constexpr R r3 = R::R3;
constexpr R r4 = R::R4;
constexpr R r5 = R::R5;
constexpr R rbp = R::Rbp;
constexpr R rsp = R::Rsp;
constexpr R rfl = R::Rfl;
constexpr R rip = R::Rip;
auto program = std::array<uint8_t, 65536>(); auto l = Builder(disk.data());
auto l = Builder(program.data());
l.mov_imm(rsp, 0x1000); l.mov_imm(rsp, 0x1000);
l.mov_reg(rbp, rsp); l.mov_reg(rbp, rsp);
l.mov_imm(r0, 512);
l.mov_imm(r1, 1);
l.dskr(r0, r1);
l.mov_imm(r0, 0x2000); l.mov_imm(r0, 0x2000);
l.store_word_imm(0x700, r0); l.store_word_imm(0x700, r0);
l.lvcd_imm(0x700); l.lvcd_imm(0x700);
@ -70,7 +58,7 @@ int main()
l.load_word_imm(r1, 0x1ffe); l.load_word_imm(r1, 0x1ffe);
l.cmp_imm(r1, 44); l.cmp_imm(r1, 44);
l.mov_reg(r2, rfl); l.mov_reg(r2, rfl);
l.and_imm(r2, r2, 1 << std::to_underlying(Flag::Eq)); l.and_imm(r2, r2, 2);
auto to_set_key_press_incr = l.ip(); auto to_set_key_press_incr = l.ip();
l.jnz_imm(r2, 0); l.jnz_imm(r2, 0);
l.add_imm(r1, r1, 61); l.add_imm(r1, r1, 61);
@ -90,28 +78,8 @@ int main()
l.set_ip(to_set_key_press_int_leave + 2); l.set_ip(to_set_key_press_int_leave + 2);
l.push(key_press_int_leave); l.push(key_press_int_leave);
auto vm = VM(*device); auto vm = VM(*device, disk);
vm.load(0, program.data(), program.size());
device->set_title("vc5");
vm.run(); vm.run();
std::println("--- regs ---");
std::println("r0\t{}", vm.reg(r0));
std::println("r1\t{}", vm.reg(r1));
std::println("r2\t{}", vm.reg(r2));
std::println("r3\t{}", vm.reg(r3));
std::println("r4\t{}", vm.reg(r4));
std::println("r5\t{}", vm.reg(r5));
std::println("rbp\t{}", vm.reg(rbp));
std::println("rsp\t{}", vm.reg(rsp));
std::println("rfl\t{}", vm.reg(rfl));
std::println("rip\t{}", vm.reg(rip));
std::println("--- mem ---");
for (uint16_t i = 0; i < 32; i += 2) {
std::println("{: 4x}\t{}", i, vm.word(i));
}
} }
extern "C" const char* __asan_default_options(void) extern "C" const char* __asan_default_options(void)

View File

@ -18,6 +18,11 @@ void VM::load(uint16_t offset, const uint8_t* data, size_t data_size)
int VM::run() int VM::run()
{ {
constexpr uint16_t bootloader_size = 512;
for (uint16_t i = 0; i * m_disk->block_size < bootloader_size; ++i) {
m_disk->read(&m_mem[i * m_disk->block_size], i);
}
m_on = true; m_on = true;
while (m_on) { while (m_on) {
if (not m_halted) { if (not m_halted) {
@ -405,6 +410,19 @@ int VM::run_instruction()
.int_handler = word(op1 + 4), .int_handler = word(op1 + 4),
}; };
m_kbd_loaded = true; m_kbd_loaded = true;
break;
}
case Op::DSKR: {
auto addr = ins.dst();
auto block = ins.op1();
m_disk->read(&m_mem[addr], block);
break;
}
case Op::DSKW: {
auto addr = ins.op1();
auto block = ins.dst();
m_disk->write(&m_mem[addr], block);
break;
} }
} }
@ -532,6 +550,10 @@ const char* vc5::op_str(Op op)
return "LVCD"; return "LVCD";
case Op::LKBD: case Op::LKBD:
return "LKBD"; return "LKBD";
case Op::DSKR:
return "DSKR";
case Op::DSKW:
return "DSKW";
} }
return ">.<"; return ">.<";
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "block_device.hpp"
#include "io_device.hpp" #include "io_device.hpp"
#include <array> #include <array>
#include <cstddef> #include <cstddef>
@ -38,6 +39,11 @@ enum class Op : uint16_t {
LVCD, LVCD,
// load keyboard // load keyboard
LKBD, LKBD,
// disk read
DSKR,
// disk write
DSKW,
}; };
const char* op_str(Op op); const char* op_str(Op op);
@ -55,6 +61,19 @@ enum class Reg : uint16_t {
Rip = 9, Rip = 9,
}; };
namespace regs {
constexpr Reg r0 = Reg::R0;
constexpr Reg r1 = Reg::R1;
constexpr Reg r2 = Reg::R2;
constexpr Reg r3 = Reg::R3;
constexpr Reg r4 = Reg::R4;
constexpr Reg r5 = Reg::R5;
constexpr Reg rbp = Reg::Rbp;
constexpr Reg rsp = Reg::Rsp;
constexpr Reg rfl = Reg::Rfl;
constexpr Reg rip = Reg::Rip;
}
enum class Flag : uint16_t { enum class Flag : uint16_t {
Zero = 0, Zero = 0,
Eq = 1, Eq = 1,
@ -85,8 +104,9 @@ enum class KbdStatusFlag : uint16_t {
class VM { class VM {
public: public:
VM(IoDevice& device) VM(IoDevice& device, BlockDevice& disk)
: m_device(&device) : m_device(&device)
, m_disk(&disk)
{ {
} }
@ -119,6 +139,7 @@ private:
uint16_t* m_rip = &m_regs[std::to_underlying(Reg::Rip)]; uint16_t* m_rip = &m_regs[std::to_underlying(Reg::Rip)];
IoDevice* m_device; IoDevice* m_device;
BlockDevice* m_disk;
bool m_on = true; bool m_on = true;
bool m_halted = false; bool m_halted = false;