functions

This commit is contained in:
sfja 2026-01-25 22:34:45 +01:00
parent eab5a313ab
commit 8cb0ac6070
8 changed files with 153 additions and 216 deletions

View File

@ -61,91 +61,98 @@ key_press_int:
cmp r1, 44 ; spacebar cmp r1, 44 ; spacebar
mov r2, rfl mov r2, rfl
and r2, fl_eq and r2, fl_eq
; jnz r2, .incr
; add r1, 61
; mov byte [r0], r1
;.incr:
; mov r0, [counter]
; add r0, 1
; mov [counter], r0
jnz r2, .print_space jnz r2, .print_space
add r1, 61 ; scancode letter -> ascii add r1, 61 ; scancode letter -> ascii
mov [rsp], r0
mov [rsp], r1
add rsp, 2 add rsp, 2
call print_char ;call print_char
call print_u16
sub rsp, 2
jmp .leave jmp .leave
.print_space: .print_space:
mov r0, ' ' mov r1, ' '
mov [rsp], r0
mov [rsp], r1
add rsp, 2 add rsp, 2
call print_char call print_char
sub rsp, 2
.leave: .leave:
reti reti
print_char: print_char:
mov [rsp], rbp mov [rsp], rbp
mov rbp, rsp
add rsp, 2 add rsp, 2
mov rbp, rsp
mov r1, [rbp-6] mov r1, [rbp-6]
mov r0, [counter]
add r0, vcd_base add r0, vcd_base
mov r1, [kbd_code]
cmp r1, ' ' cmp r1, ' '
mov r2, rfl mov r2, rfl
and r2, fl_eq and r2, fl_eq
jnz r2, .incr jnz r2, .incr
add r1, 61
mov byte [r0], r1 mov byte [r0], r1
.incr: .incr:
mov r0, [counter] mov r0, [counter]
add r0, 1 add r0, 1
mov [counter], r0 mov [counter], r0
.leave: .leave:
mov rsp, rbp
sub rsp, 2 sub rsp, 2
mov rbp, [rsp] mov rbp, [rsp]
ret ret
;print_int: print_u16:
; mov [rsp], rbp mov [rsp], rbp
; add rsp, 2 add rsp, 2
; mov rbp, rsp mov rbp, rsp
; add rsp, rsp, 2
;
; mov r1, [rbp-2]
;
; mov r2, 10000
; mov r3, 0
; jmp .c_10000
;.b_10000:
; add r3, 1
; sub r1, r2
;.c_10000:
; cmp r2, r1
; mov r0, rfl
; and r0, fl_eq | fl_lt
; jnz r0, .b_10000
; add r3, 48
mov r1, [rbp-6] ; value to print
mov r4, 0 ; place index
;scancode_to_char: jmp .digits_cond
; cmp r1, 61 .digits_body:
; mov r2, .places
; mov r2, rfl add r2, r4
; and r2, r2, FL_LT mov r2, [r2] ; place
; jnz .not_letter
; mov r3, 0 ; place occurences
; ; if r1 > 86 { goto .not_letter } jmp .place_cond
; cmp r1, 86 .place_body:
; mov r2, rfl add r3, 1
; xor r2, 0xffff sub r1, r2
; and r2, r2, FL_EQ | FL_LT .place_cond:
; cmp r2, r1
; mov r0, rfl
; mov r4, rfl and r0, fl_eq | fl_lt
; and r4, r4, FL_EQ jnz r0, .place_body
; shr r4, r4, 2
mov r0, r3
add r0, 48
mov [rsp], r0
add rsp, 2
call print_char
sub rsp, 2
add r4, 2
.digits_cond:
cmp r4, 10
mov r0, rfl
and r0, fl_lt
jnz r0, .digits_body
.leave:
mov rsp, rbp
sub rsp, 2
mov rbp, [rsp]
ret
.places:
dw 10000, 1000, 100, 10, 1

View File

@ -406,24 +406,24 @@ void Assembler::assemble_line(const Line& line)
l.mov_imm(dst->as_reg(), src->as_imm()); l.mov_imm(dst->as_reg(), src->as_imm());
} else if (dst_ty == Reg and src_ty == MemWordReg) { } else if (dst_ty == Reg and src_ty == MemWordReg) {
auto [reg, offset] = src->as_reg_imm_pair(); auto [reg, offset] = src->as_reg_imm_pair();
l.load_word_reg(dst->as_reg(), reg, offset); l.ldw_reg(dst->as_reg(), reg, offset);
} else if (dst_ty == Reg and src_ty == MemWordImm) { } else if (dst_ty == Reg and src_ty == MemWordImm) {
l.load_word_imm(dst->as_reg(), src->as_imm()); l.ldw_imm(dst->as_reg(), src->as_imm());
} else if (dst_ty == MemWordReg and src_ty == Reg) { } else if (dst_ty == MemWordReg and src_ty == Reg) {
auto [reg, offset] = dst->as_reg_imm_pair(); auto [reg, offset] = dst->as_reg_imm_pair();
l.store_word_reg(reg, offset, src->as_reg()); l.stw_reg(reg, offset, src->as_reg());
} else if (dst_ty == MemWordImm and src_ty == Reg) { } else if (dst_ty == MemWordImm and src_ty == Reg) {
l.store_word_imm(dst->as_imm(), src->as_reg()); l.stw_imm(dst->as_imm(), src->as_reg());
} else if (dst_ty == Reg and src_ty == MemByteReg) { } else if (dst_ty == Reg and src_ty == MemByteReg) {
auto [reg, offset] = src->as_reg_imm_pair(); auto [reg, offset] = src->as_reg_imm_pair();
l.load_byte_reg(dst->as_reg(), reg, offset); l.ldb_reg(dst->as_reg(), reg, offset);
} else if (dst_ty == Reg and src_ty == MemByteImm) { } else if (dst_ty == Reg and src_ty == MemByteImm) {
l.load_byte_imm(dst->as_reg(), src->as_imm()); l.ldb_imm(dst->as_reg(), src->as_imm());
} else if (dst_ty == MemByteReg and src_ty == Reg) { } else if (dst_ty == MemByteReg and src_ty == Reg) {
auto [reg, offset] = dst->as_reg_imm_pair(); auto [reg, offset] = dst->as_reg_imm_pair();
l.store_byte_reg(reg, offset, src->as_reg()); l.stb_reg(reg, offset, src->as_reg());
} else if (dst_ty == MemByteImm and src_ty == Reg) { } else if (dst_ty == MemByteImm and src_ty == Reg) {
l.store_byte_imm(dst->as_imm(), src->as_reg()); l.stb_imm(dst->as_imm(), src->as_reg());
} else { } else {
operation_not_supported(); operation_not_supported();
} }
@ -912,8 +912,6 @@ bool Assembler::arg_count_wrong(const Line& ins, size_t count)
auto Assembler::define_sym(std::string ident, uint16_t value) auto Assembler::define_sym(std::string ident, uint16_t value)
-> std::expected<void, std::string> -> std::expected<void, std::string>
{ {
std::println("define_sym(\"{}\", {})", ident, value);
if (m_syms.contains(ident)) { if (m_syms.contains(ident)) {
return std::unexpected( return std::unexpected(
std::format("symbol \"{}\" already defined", ident)); std::format("symbol \"{}\" already defined", ident));
@ -926,8 +924,6 @@ auto Assembler::define_sym(std::string ident, uint16_t value)
auto Assembler::get_sym(const std::string& ident) auto Assembler::get_sym(const std::string& ident)
-> std::expected<uint16_t, std::string> -> std::expected<uint16_t, std::string>
{ {
std::println(" get_sym(\"{}\")", ident);
if (not m_syms.contains(ident)) { if (not m_syms.contains(ident)) {
return std::unexpected(std::format("symbol \"{}\" not defined", ident)); return std::unexpected(std::format("symbol \"{}\" not defined", ident));
} }

View File

@ -141,77 +141,77 @@ void Builder::mov_imm(Reg dst, uint16_t imm)
i.build(*this); i.build(*this);
} }
void Builder::load_word_reg(Reg dst, Reg addr, uint16_t offset) void Builder::ldw_reg(Reg dst, Reg addr, uint16_t offset)
{ {
LOG; LOG;
auto i = InsBuilder(Op::LoadWord); auto i = InsBuilder(Op::LdW);
i.dst_reg(dst); i.dst_reg(dst);
i.op1_reg(addr); i.op1_reg(addr);
i.imm_without_flag(offset); i.imm_without_flag(offset);
i.build(*this); i.build(*this);
} }
void Builder::load_word_imm(Reg dst, uint16_t addr) void Builder::ldw_imm(Reg dst, uint16_t addr)
{ {
LOG; LOG;
auto i = InsBuilder(Op::LoadWord); auto i = InsBuilder(Op::LdW);
i.dst_reg(dst); i.dst_reg(dst);
i.imm(addr); i.imm(addr);
i.build(*this); i.build(*this);
} }
void Builder::store_word_reg(Reg dst, uint16_t offset, Reg op2) void Builder::stw_reg(Reg dst, uint16_t offset, Reg op2)
{ {
LOG; LOG;
auto i = InsBuilder(Op::StoreWord); auto i = InsBuilder(Op::StW);
i.dst_reg(dst); i.dst_reg(dst);
i.imm_without_flag(offset); i.imm_without_flag(offset);
i.op2_reg(op2); i.op2_reg(op2);
i.build(*this); i.build(*this);
} }
void Builder::store_word_imm(uint16_t dst, Reg op2) void Builder::stw_imm(uint16_t dst, Reg op2)
{ {
LOG; LOG;
auto i = InsBuilder(Op::StoreWord); auto i = InsBuilder(Op::StW);
i.imm(dst); i.imm(dst);
i.op2_reg(op2); i.op2_reg(op2);
i.build(*this); i.build(*this);
} }
void Builder::load_byte_reg(Reg dst, Reg addr, uint16_t offset) void Builder::ldb_reg(Reg dst, Reg addr, uint16_t offset)
{ {
LOG; LOG;
auto i = InsBuilder(Op::LoadByte); auto i = InsBuilder(Op::LdB);
i.dst_reg(dst); i.dst_reg(dst);
i.op1_reg(addr); i.op1_reg(addr);
i.imm_without_flag(offset); i.imm_without_flag(offset);
i.build(*this); i.build(*this);
} }
void Builder::load_byte_imm(Reg dst, uint16_t addr) void Builder::ldb_imm(Reg dst, uint16_t addr)
{ {
LOG; LOG;
auto i = InsBuilder(Op::LoadByte); auto i = InsBuilder(Op::LdB);
i.dst_reg(dst); i.dst_reg(dst);
i.imm(addr); i.imm(addr);
i.build(*this); i.build(*this);
} }
void Builder::store_byte_reg(Reg dst, uint16_t offset, Reg op2) void Builder::stb_reg(Reg dst, uint16_t offset, Reg op2)
{ {
LOG; LOG;
auto i = InsBuilder(Op::StoreByte); auto i = InsBuilder(Op::StB);
i.dst_reg(dst); i.dst_reg(dst);
i.imm_without_flag(offset); i.imm_without_flag(offset);
i.op2_reg(op2); i.op2_reg(op2);
i.build(*this); i.build(*this);
} }
void Builder::store_byte_imm(uint16_t dst, Reg op2) void Builder::stb_imm(uint16_t dst, Reg op2)
{ {
LOG; LOG;
auto i = InsBuilder(Op::StoreByte); auto i = InsBuilder(Op::StB);
i.imm(dst); i.imm(dst);
i.op2_reg(op2); i.op2_reg(op2);
i.build(*this); i.build(*this);

View File

@ -23,14 +23,14 @@ public:
void jnz_imm(Reg op1, uint16_t op2); void jnz_imm(Reg op1, uint16_t op2);
void mov_reg(Reg dst, Reg src); void mov_reg(Reg dst, Reg src);
void mov_imm(Reg dst, uint16_t imm); void mov_imm(Reg dst, uint16_t imm);
void load_word_reg(Reg dst, Reg addr, uint16_t offset); void ldw_reg(Reg dst, Reg addr, uint16_t offset);
void load_word_imm(Reg dst, uint16_t addr); void ldw_imm(Reg dst, uint16_t addr);
void store_word_reg(Reg dst, uint16_t offset, Reg op2); void stw_reg(Reg dst, uint16_t offset, Reg op2);
void store_word_imm(uint16_t dst, Reg op2); void stw_imm(uint16_t dst, Reg op2);
void load_byte_reg(Reg dst, Reg addr, uint16_t offset); void ldb_reg(Reg dst, Reg addr, uint16_t offset);
void load_byte_imm(Reg dst, uint16_t addr); void ldb_imm(Reg dst, uint16_t addr);
void store_byte_reg(Reg dst, uint16_t offset, Reg op2); void stb_reg(Reg dst, uint16_t offset, Reg op2);
void store_byte_imm(uint16_t dst, Reg op2); void stb_imm(uint16_t dst, Reg op2);
void cmp_reg(Reg op1, Reg op2); void cmp_reg(Reg op1, Reg op2);
void cmp_imm(Reg op1, uint16_t op2); void cmp_imm(Reg op1, uint16_t op2);
void or_reg(Reg dst, Reg op1, Reg op2); void or_reg(Reg dst, Reg op1, Reg op2);

View File

@ -26,6 +26,11 @@ constexpr uint64_t char_data(uint8_t ch)
case '2': return 0x003C66060C18307E; case '2': return 0x003C66060C18307E;
case '3': return 0x003C66060C06663C; case '3': return 0x003C66060C06663C;
case '4': return 0x000C1C3C6C7E0C0C; case '4': return 0x000C1C3C6C7E0C0C;
case '5': return 0x007E607C0606663C;
case '6': return 0x003C66607C66663C;
case '7': return 0x007F63060C181818;
case '8': return 0x003C66663C66663C;
case '9': return 0x003C66663E06663C;
case 'A': return 0x00187E66667E6666; case 'A': return 0x00187E66667E6666;
case 'B': return 0x00787E667C667E7C; case 'B': return 0x00787E667C667E7C;
@ -173,7 +178,7 @@ auto IoDevice::poll_event() -> std::unique_ptr<IoEvent>
std::lock_guard lock(mx); std::lock_guard lock(mx);
SDL_Event event; SDL_Event event;
if (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
switch (event.type) { switch (event.type) {
case SDL_QUIT: case SDL_QUIT:
return std::make_unique<IoEvent>( return std::make_unique<IoEvent>(

View File

@ -273,13 +273,13 @@ struct InsReader {
int VM::run_instruction() int VM::run_instruction()
{ {
auto ip = *m_rip; auto ip = *m_rip;
if (ip > 0x400) if (ip >= 0x400)
return -1; return -1;
auto ins = InsReader(*this); auto ins = InsReader(*this);
auto op = static_cast<Op>(ins.to_u16() & 0b11'1111); auto op = static_cast<Op>(ins.to_u16() & 0b11'1111);
std::println( std::println(
" [{: 2x}]: 0x{:04x} {: <9} {:04b} {:04b} {:04b} {:04b} {:04x}", " [{: >2x}]: 0x{:04x} {: <6} {:04b} {:04b} {:04b} {:04b} {:04x}",
ip, ip,
ins.to_u16(), ins.to_u16(),
op_str(op), op_str(op),
@ -293,10 +293,36 @@ int VM::run_instruction()
case Op::Nop: case Op::Nop:
break; break;
case Op::Hlt: case Op::Hlt:
if (not m_interrupts_enabled) {
std::println("warning: halted without interrupts enabled");
}
m_halted = true; m_halted = true;
break; break;
case Op::Jmp: { case Op::Jmp: {
auto op1 = ins.op1_or_imm(); auto op1 = ins.op1_or_imm();
if (op1 == 1) {
for (uint16_t reg = 0; reg < 10; ++reg) {
constexpr auto reg_strs = std::array {
"r0"sv,
"r1"sv,
"r2"sv,
"r3"sv,
"r4"sv,
"r5"sv,
"rbp"sv,
"rsp"sv,
"rfl"sv,
"rip"sv,
};
std::println(" {: <3} | {:04x} {: 5}",
reg_strs[reg],
this->reg(static_cast<Reg>(reg)),
this->reg(static_cast<Reg>(reg)));
}
break;
}
*m_rip = op1; *m_rip = op1;
break; break;
} }
@ -315,7 +341,7 @@ int VM::run_instruction()
set_reg(dst, src); set_reg(dst, src);
break; break;
} }
case Op::LoadWord: { case Op::LdW: {
auto addr = ins.mem_addr(ins.op1(), eat_word()); auto addr = ins.mem_addr(ins.op1(), eat_word());
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
@ -331,7 +357,7 @@ int VM::run_instruction()
set_reg(reg, word(addr)); set_reg(reg, word(addr));
break; break;
} }
case Op::StoreWord: { case Op::StW: {
auto addr = ins.mem_addr(ins.dst(), eat_word()); auto addr = ins.mem_addr(ins.dst(), eat_word());
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
@ -353,14 +379,14 @@ int VM::run_instruction()
break; break;
} }
case Op::LoadByte: { case Op::LdB: {
auto addr = ins.mem_addr(ins.op1(), eat_word()); auto addr = ins.mem_addr(ins.op1(), eat_word());
auto reg = ins.dst_reg(); auto reg = ins.dst_reg();
set_reg(reg, byte(addr)); set_reg(reg, byte(addr));
break; break;
} }
case Op::StoreByte: { case Op::StB: {
auto addr = ins.mem_addr(ins.dst(), eat_word()); auto addr = ins.mem_addr(ins.dst(), eat_word());
auto src = ins.op2(); auto src = ins.op2();
@ -448,9 +474,9 @@ int VM::run_instruction()
case Op::Call: { case Op::Call: {
auto op1 = ins.op1_or_imm(); auto op1 = ins.op1_or_imm();
auto return_addr = word(*m_rip); auto return_addr = *m_rip;
set_word(*m_rsp, return_addr); set_word(*m_rsp, return_addr);
*m_rsp -= 2; *m_rsp += 2;
*m_rip = op1; *m_rip = op1;
break; break;
@ -464,7 +490,6 @@ int VM::run_instruction()
case Op::RetI: { case Op::RetI: {
*m_rsp -= 2; *m_rsp -= 2;
auto return_addr = word(*m_rsp); auto return_addr = word(*m_rsp);
*m_rip = return_addr; *m_rip = return_addr;
m_interrupts_enabled = true; m_interrupts_enabled = true;
@ -579,7 +604,7 @@ void VM::interrupt(uint16_t handler_addr)
m_interrupts_enabled = false; m_interrupts_enabled = false;
} }
const char* vc5::op_str(Op op) auto vc5::op_str(Op op) -> std::string_view
{ {
switch (op) { switch (op) {
case Op::Nop: case Op::Nop:
@ -592,14 +617,14 @@ const char* vc5::op_str(Op op)
return "Jnz"; return "Jnz";
case Op::Mov: case Op::Mov:
return "Mov"; return "Mov";
case Op::LoadWord: case Op::LdW:
return "LoadWord"; return "LdW";
case Op::StoreWord: case Op::StW:
return "StoreWord"; return "StW";
case Op::LoadByte: case Op::LdB:
return "LoadByte"; return "LdB";
case Op::StoreByte: case Op::StB:
return "StoreByte"; return "StB";
case Op::Cmp: case Op::Cmp:
return "Cmp"; return "Cmp";
case Op::Or: case Op::Or:

View File

@ -7,6 +7,7 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <span> #include <span>
#include <string_view>
#include <utility> #include <utility>
namespace vc5 { namespace vc5 {
@ -17,10 +18,10 @@ enum class Op : uint16_t {
Jmp, Jmp,
Jnz, Jnz,
Mov, Mov,
LoadWord, LdW,
StoreWord, StW,
LoadByte, LdB,
StoreByte, StB,
Cmp, Cmp,
Or, Or,
And, And,
@ -49,7 +50,7 @@ enum class Op : uint16_t {
DSKW, DSKW,
}; };
const char* op_str(Op op); auto op_str(Op op) -> std::string_view;
enum class Reg : uint16_t { enum class Reg : uint16_t {
R0 = 0, R0 = 0,

View File

@ -4,6 +4,7 @@
#include "vm.hpp" #include "vm.hpp"
#include <SDL2/SDL_main.h> #include <SDL2/SDL_main.h>
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include <print> #include <print>
#include <string> #include <string>
@ -11,118 +12,20 @@ using namespace std::chrono_literals;
using namespace vc5; using namespace vc5;
static void make_program(uint8_t* data);
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
auto device = IoDevice::create().value(); auto device = IoDevice::create().value();
device->set_title("vc5"); device->set_title("vc5");
if (argc < 2) { if (argc > 2) {
auto disk = MemoryDisk(128); std::println("error: no boot disk (file name) specified");
return EXIT_FAILURE;
make_program(disk.data());
std::println("memory disk");
for (size_t i = 0; i < 128; i += 4) {
std::println("{:02x} {:02x} {:02x} {:02x}",
disk.data()[i],
disk.data()[i + 1],
disk.data()[i + 2],
disk.data()[i + 3]);
} }
auto vm = VM(*device, disk);
vm.run();
} else {
auto disk = FileDisk(argv[1]); auto disk = FileDisk(argv[1]);
auto memory_disk = MemoryDisk(128);
make_program(memory_disk.data());
auto file_block = std::vector<uint8_t>(BlockDevice::block_size);
disk.read(file_block.data(), 0);
auto memory_block = std::vector<uint8_t>(BlockDevice::block_size);
memory_disk.read(memory_block.data(), 0);
std::println("file disk");
for (size_t i = 0; i < 128; i += 4) {
std::println("{:02x} {:02x} {:02x} {:02x}",
file_block[i],
file_block[i + 1],
file_block[i + 2],
file_block[i + 3]);
}
auto vm = VM(*device, disk); auto vm = VM(*device, disk);
vm.run(); vm.run();
}
}
void make_program(uint8_t* data)
{
using namespace vc5::regs;
auto l = tools::Builder(data);
l.mov_imm(rsp, 0x1000);
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.store_word_imm(0x700, r0);
l.lvcd_imm(0x700);
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.mov_imm(r0, 0);
l.store_word_imm(0x600, r0);
auto main_loop = l.ip();
l.hlt();
l.jmp_imm(main_loop);
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);
l.load_word_imm(r0, 0x600);
l.add_imm(r0, r0, 0x2000);
l.load_word_imm(r1, 0x1ffe);
l.cmp_imm(r1, 44);
l.mov_reg(r2, rfl);
l.and_imm(r2, r2, 2);
auto to_set_key_press_incr = l.ip();
l.jnz_imm(r2, 0);
l.add_imm(r1, r1, 61);
l.store_byte_reg(r0, 0, r1);
auto key_press_incr = l.ip();
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_incr + 2);
l.push(key_press_incr);
l.set_ip(to_set_key_press_int_leave + 2);
l.push(key_press_int_leave);
} }
extern "C" const char* __asan_default_options(void) extern "C" const char* __asan_default_options(void)