make program work sorta
This commit is contained in:
parent
7878663268
commit
eab5a313ab
@ -1,13 +1,20 @@
|
|||||||
|
|
||||||
|
|
||||||
const KBD_STATUS 0x1ffc
|
const fl_zero 0x1
|
||||||
const KBD_CODE 0x1ffe
|
const fl_eq 0x2
|
||||||
const VCD 0x2000
|
const fl_be 0x4
|
||||||
|
const fl_lt 0x8
|
||||||
|
const fl_err 0xa
|
||||||
|
|
||||||
const FL_EQ 0x2
|
const vcd_base 0x2000
|
||||||
|
|
||||||
const KBD_FLAG_IS_RELEASE 0x1
|
const kbd_status 0x1ffc
|
||||||
|
const kbd_code 0x1ffe
|
||||||
|
const kbd_flag_is_release 0x1
|
||||||
|
|
||||||
|
const counter 0x600
|
||||||
|
|
||||||
|
_start:
|
||||||
mov rsp, 0x1000
|
mov rsp, 0x1000
|
||||||
mov rbp, rsp
|
mov rbp, rsp
|
||||||
|
|
||||||
@ -18,7 +25,7 @@ const KBD_FLAG_IS_RELEASE 0x1
|
|||||||
; setup video character display (VCD)
|
; setup video character display (VCD)
|
||||||
; descriptor table structure:
|
; descriptor table structure:
|
||||||
; [addr + 0] memory map base
|
; [addr + 0] memory map base
|
||||||
mov r0, VCD
|
mov r0, vcd_base
|
||||||
mov [0x700], r0
|
mov [0x700], r0
|
||||||
lvcd 0x700
|
lvcd 0x700
|
||||||
|
|
||||||
@ -27,9 +34,9 @@ const KBD_FLAG_IS_RELEASE 0x1
|
|||||||
; [addr + 0] status address
|
; [addr + 0] status address
|
||||||
; [addr + 2] keycode address
|
; [addr + 2] keycode address
|
||||||
; [addr + 4] keyboard interrupt handler
|
; [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
|
||||||
mov [0x704], r0
|
mov [0x704], r0
|
||||||
mov r0, key_press_int
|
mov r0, key_press_int
|
||||||
mov [0x706], r0
|
mov [0x706], r0
|
||||||
@ -37,33 +44,91 @@ const KBD_FLAG_IS_RELEASE 0x1
|
|||||||
|
|
||||||
; character counter
|
; character counter
|
||||||
mov r0, 0
|
mov r0, 0
|
||||||
mov [0x600], r0
|
mov [counter], r0
|
||||||
|
|
||||||
main_loop:
|
main_loop:
|
||||||
hlt
|
hlt
|
||||||
jmp main_loop
|
jmp main_loop
|
||||||
|
|
||||||
key_press_int:
|
key_press_int:
|
||||||
mov r0, [KBD_STATUS]
|
mov r0, [kbd_status]
|
||||||
and r0, r0, KBD_FLAG_IS_RELEASE
|
and r0, kbd_flag_is_release
|
||||||
jnz r0, .leave
|
jnz r0, .leave
|
||||||
|
|
||||||
mov r0, [0x600]
|
mov r0, [counter]
|
||||||
add r0, r0, VCD
|
add r0, vcd_base
|
||||||
mov r1, [KBD_CODE]
|
mov r1, [kbd_code]
|
||||||
cmp r1, 44 ; spacebar
|
cmp r1, 44 ; spacebar
|
||||||
mov r2, rfl
|
mov r2, rfl
|
||||||
and r2, r2, FL_EQ
|
and r2, fl_eq
|
||||||
jnz r2, .incr
|
; jnz r2, .incr
|
||||||
add r1, r1, 61
|
; add r1, 61
|
||||||
mov byte [r0], r1
|
; mov byte [r0], r1
|
||||||
.incr:
|
;.incr:
|
||||||
mov r0, [0x600]
|
; mov r0, [counter]
|
||||||
add r0, r0, 1
|
; add r0, 1
|
||||||
mov [0x600], r0
|
; mov [counter], r0
|
||||||
|
|
||||||
|
jnz r2, .print_space
|
||||||
|
add r1, 61 ; scancode letter -> ascii
|
||||||
|
mov [rsp], r0
|
||||||
|
add rsp, 2
|
||||||
|
call print_char
|
||||||
|
jmp .leave
|
||||||
|
.print_space:
|
||||||
|
mov r0, ' '
|
||||||
|
mov [rsp], r0
|
||||||
|
add rsp, 2
|
||||||
|
call print_char
|
||||||
.leave:
|
.leave:
|
||||||
reti
|
reti
|
||||||
|
|
||||||
|
print_char:
|
||||||
|
mov [rsp], rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
add rsp, 2
|
||||||
|
|
||||||
|
mov r1, [rbp-6]
|
||||||
|
|
||||||
|
add r0, vcd_base
|
||||||
|
mov r1, [kbd_code]
|
||||||
|
cmp r1, ' '
|
||||||
|
mov r2, rfl
|
||||||
|
and r2, fl_eq
|
||||||
|
jnz r2, .incr
|
||||||
|
add r1, 61
|
||||||
|
mov byte [r0], r1
|
||||||
|
.incr:
|
||||||
|
mov r0, [counter]
|
||||||
|
add r0, 1
|
||||||
|
mov [counter], r0
|
||||||
|
.leave:
|
||||||
|
sub rsp, 2
|
||||||
|
mov rbp, [rsp]
|
||||||
|
ret
|
||||||
|
|
||||||
|
;print_int:
|
||||||
|
; mov [rsp], rbp
|
||||||
|
; add rsp, 2
|
||||||
|
; 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
|
||||||
|
|
||||||
|
|
||||||
;scancode_to_char:
|
;scancode_to_char:
|
||||||
; cmp r1, 61
|
; cmp r1, 61
|
||||||
;
|
;
|
||||||
|
|||||||
@ -101,7 +101,11 @@ auto Assembler::assemble_file()
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(value->ty == EOT::Imm);
|
assert(value->ty == EOT::Imm);
|
||||||
m_syms[std::string(stmt->ident)] = value->as_imm();
|
if (auto result
|
||||||
|
= define_sym(std::string(stmt->ident), value->as_imm());
|
||||||
|
not result) {
|
||||||
|
error(stmt->loc, result.error());
|
||||||
|
}
|
||||||
|
|
||||||
} else if (parser.next_is_align()) {
|
} else if (parser.next_is_align()) {
|
||||||
auto stmt = parser.parse_align();
|
auto stmt = parser.parse_align();
|
||||||
@ -172,9 +176,20 @@ auto Assembler::assemble_file()
|
|||||||
m_second_pass = true;
|
m_second_pass = true;
|
||||||
|
|
||||||
for (const auto& line : lines) {
|
for (const auto& line : lines) {
|
||||||
|
if (line->labels) {
|
||||||
|
for (const auto& label : *line->labels) {
|
||||||
|
if (not label.is_local) {
|
||||||
|
m_superlabel = label.ident;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assemble_line(*line);
|
assemble_line(*line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_failed)
|
||||||
|
return std::unexpected("assembling failed");
|
||||||
|
|
||||||
return std::move(m_program);
|
return std::move(m_program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,10 +197,17 @@ void Assembler::define_labels(const std::vector<Label>& labels)
|
|||||||
{
|
{
|
||||||
for (const auto& label : labels) {
|
for (const auto& label : labels) {
|
||||||
if (label.is_local) {
|
if (label.is_local) {
|
||||||
m_syms[label_ident(label)] = m_builder.ip();
|
if (auto result = define_sym(label_ident(label), m_builder.ip());
|
||||||
|
not result) {
|
||||||
|
error(label.loc, result.error());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_superlabel = label.ident;
|
m_superlabel = label.ident;
|
||||||
m_syms[std::string(label.ident)] = m_builder.ip();
|
if (auto result
|
||||||
|
= define_sym(std::string(label.ident), m_builder.ip());
|
||||||
|
not result) {
|
||||||
|
error(label.loc, result.error());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,36 +227,62 @@ auto Assembler::sublabel_ident(std::string_view ident) const -> std::string
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum class Mnemonic {
|
enum class Mnemonic {
|
||||||
// clang-format off
|
db,
|
||||||
db, dw, nop,
|
dw,
|
||||||
hlt, jmp, jnz,
|
nop,
|
||||||
mov, cmp,
|
hlt,
|
||||||
or_, and_, xor_,
|
jmp,
|
||||||
shl, rshl, shr,
|
jnz,
|
||||||
rshr, add, sub,
|
mov,
|
||||||
rsub, reti, lvcd,
|
cmp,
|
||||||
lkbd, dskr, dskw,
|
or_,
|
||||||
// clang-format on
|
and_,
|
||||||
|
xor_,
|
||||||
|
shl,
|
||||||
|
rshl,
|
||||||
|
shr,
|
||||||
|
rshr,
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
rsub,
|
||||||
|
call,
|
||||||
|
ret,
|
||||||
|
reti,
|
||||||
|
lvcd,
|
||||||
|
lkbd,
|
||||||
|
dskr,
|
||||||
|
dskw,
|
||||||
};
|
};
|
||||||
|
|
||||||
using M = Mnemonic;
|
using M = Mnemonic;
|
||||||
|
|
||||||
static const auto mnemonic_map
|
static const auto mnemonic_map
|
||||||
= std::unordered_map<std::string_view, Mnemonic> {
|
= std::unordered_map<std::string_view, Mnemonic> {
|
||||||
// clang-format off
|
{ "db", M::db },
|
||||||
{ "db", M::db }, { "dw", M::dw },
|
{ "dw", M::dw },
|
||||||
{ "nop", M::nop }, { "hlt", M::hlt },
|
{ "nop", M::nop },
|
||||||
{ "jmp", M::jmp }, { "jnz", M::jnz },
|
{ "hlt", M::hlt },
|
||||||
{ "mov", M::mov }, { "cmp", M::cmp },
|
{ "jmp", M::jmp },
|
||||||
{ "or", M::or_ }, { "and", M::and_ },
|
{ "jnz", M::jnz },
|
||||||
{ "xor", M::xor_ }, { "shl", M::shl },
|
{ "mov", M::mov },
|
||||||
{ "rshl", M::rshl }, { "shr", M::shr },
|
{ "cmp", M::cmp },
|
||||||
{ "rshr", M::rshr }, { "add", M::add },
|
{ "or", M::or_ },
|
||||||
{ "sub", M::sub }, { "rsub", M::rsub },
|
{ "and", M::and_ },
|
||||||
{ "reti", M::reti }, { "lvcd", M::lvcd },
|
{ "xor", M::xor_ },
|
||||||
{ "lkbd", M::lkbd }, { "dskr", M::dskr },
|
{ "shl", M::shl },
|
||||||
|
{ "rshl", M::rshl },
|
||||||
|
{ "shr", M::shr },
|
||||||
|
{ "rshr", M::rshr },
|
||||||
|
{ "add", M::add },
|
||||||
|
{ "sub", M::sub },
|
||||||
|
{ "rsub", M::rsub },
|
||||||
|
{ "call", M::call },
|
||||||
|
{ "ret", M::ret },
|
||||||
|
{ "reti", M::reti },
|
||||||
|
{ "lvcd", M::lvcd },
|
||||||
|
{ "lkbd", M::lkbd },
|
||||||
|
{ "dskr", M::dskr },
|
||||||
{ "dskw", M::dskw },
|
{ "dskw", M::dskw },
|
||||||
// clang-format on
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -412,11 +460,24 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
case M::add:
|
case M::add:
|
||||||
case M::sub:
|
case M::sub:
|
||||||
case M::rsub: {
|
case M::rsub: {
|
||||||
if (arg_count_wrong(line, 3))
|
auto dst = std::unique_ptr<EvaledOperand> {};
|
||||||
return;
|
auto op1_store = std::unique_ptr<EvaledOperand> {};
|
||||||
auto dst = eval_operand(*line.args[0]);
|
auto op2 = std::unique_ptr<EvaledOperand> {};
|
||||||
auto op1 = eval_operand(*line.args[1]);
|
|
||||||
auto op2 = eval_operand(*line.args[2]);
|
EvaledOperand* op1;
|
||||||
|
|
||||||
|
if (line.args.size() == 3) {
|
||||||
|
dst = eval_operand(*line.args[0]);
|
||||||
|
op1_store = eval_operand(*line.args[1]);
|
||||||
|
op1 = &*op1_store;
|
||||||
|
op2 = eval_operand(*line.args[2]);
|
||||||
|
} else if (line.args.size() == 2) {
|
||||||
|
dst = eval_operand(*line.args[0]);
|
||||||
|
op1 = &*dst;
|
||||||
|
op2 = eval_operand(*line.args[1]);
|
||||||
|
} else {
|
||||||
|
operation_not_supported();
|
||||||
|
}
|
||||||
|
|
||||||
if (not dst or not op1 or not op2)
|
if (not dst or not op1 or not op2)
|
||||||
return;
|
return;
|
||||||
@ -501,6 +562,28 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Mnemonic::call: {
|
||||||
|
if (arg_count_wrong(line, 1))
|
||||||
|
return;
|
||||||
|
auto op = eval_operand(*line.args[0]);
|
||||||
|
if (not op)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (op->is_reg()) {
|
||||||
|
l.call_reg(op->as_reg());
|
||||||
|
} else if (op->is_imm()) {
|
||||||
|
l.call_imm(op->as_imm());
|
||||||
|
} else {
|
||||||
|
operation_not_supported();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Mnemonic::ret: {
|
||||||
|
if (arg_count_wrong(line, 0))
|
||||||
|
return;
|
||||||
|
l.ret();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case M::reti: {
|
case M::reti: {
|
||||||
if (arg_count_wrong(line, 0))
|
if (arg_count_wrong(line, 0))
|
||||||
return;
|
return;
|
||||||
@ -715,32 +798,28 @@ auto Assembler::eval_operand_to_imm(const Expr& expr)
|
|||||||
auto loc = expr.loc;
|
auto loc = expr.loc;
|
||||||
switch (expr.ty) {
|
switch (expr.ty) {
|
||||||
case Expr::Ty::Ident: {
|
case Expr::Ty::Ident: {
|
||||||
auto ident = std::string(expr.as_ident());
|
auto result = get_sym(std::string(expr.as_ident()));
|
||||||
if (!m_syms.contains(ident)) {
|
if (not result) {
|
||||||
if (not m_second_pass)
|
if (not m_second_pass)
|
||||||
return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 });
|
return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 });
|
||||||
|
|
||||||
error(expr.loc,
|
error(expr.loc, result.error());
|
||||||
std::format("symbol \"{}\" not defined", expr.as_ident()));
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = m_syms[ident];
|
return std::make_unique<EO>(loc, EOT::Imm, *result);
|
||||||
return std::make_unique<EO>(loc, EOT::Imm, value);
|
|
||||||
}
|
}
|
||||||
case Expr::Ty::SubLabel: {
|
case Expr::Ty::SubLabel: {
|
||||||
auto ident = sublabel_ident(expr.as_ident());
|
auto result = get_sym(sublabel_ident(expr.as_ident()));
|
||||||
if (!m_syms.contains(ident)) {
|
if (not result) {
|
||||||
if (not m_second_pass)
|
if (not m_second_pass)
|
||||||
return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 });
|
return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 });
|
||||||
|
|
||||||
error(expr.loc,
|
error(expr.loc, result.error());
|
||||||
std::format("symbol \"{}\" not defined", expr.as_ident()));
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = m_syms[ident];
|
return std::make_unique<EO>(loc, EOT::Imm, *result);
|
||||||
return std::make_unique<EO>(loc, EOT::Imm, value);
|
|
||||||
}
|
}
|
||||||
case Expr::Ty::Reg:
|
case Expr::Ty::Reg:
|
||||||
error(loc, "registers cannot be part of an expression");
|
error(loc, "registers cannot be part of an expression");
|
||||||
@ -830,6 +909,32 @@ bool Assembler::arg_count_wrong(const Line& ins, size_t count)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Assembler::define_sym(std::string ident, uint16_t value)
|
||||||
|
-> std::expected<void, std::string>
|
||||||
|
{
|
||||||
|
std::println("define_sym(\"{}\", {})", ident, value);
|
||||||
|
|
||||||
|
if (m_syms.contains(ident)) {
|
||||||
|
return std::unexpected(
|
||||||
|
std::format("symbol \"{}\" already defined", ident));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_syms[std::move(ident)] = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Assembler::get_sym(const std::string& ident)
|
||||||
|
-> std::expected<uint16_t, std::string>
|
||||||
|
{
|
||||||
|
std::println(" get_sym(\"{}\")", ident);
|
||||||
|
|
||||||
|
if (not m_syms.contains(ident)) {
|
||||||
|
return std::unexpected(std::format("symbol \"{}\" not defined", ident));
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_syms[ident];
|
||||||
|
}
|
||||||
|
|
||||||
void Assembler::error(Loc loc, std::string_view message)
|
void Assembler::error(Loc loc, std::string_view message)
|
||||||
{
|
{
|
||||||
m_failed = true;
|
m_failed = true;
|
||||||
@ -1003,15 +1108,19 @@ auto Parser::parse_binary(int prec) -> std::unique_ptr<Expr>
|
|||||||
|
|
||||||
auto left = parse_binary(prec - 1);
|
auto left = parse_binary(prec - 1);
|
||||||
|
|
||||||
auto should_continue = false;
|
auto should_continue = true;
|
||||||
while (should_continue) {
|
while (should_continue) {
|
||||||
should_continue = false;
|
should_continue = false;
|
||||||
for (auto [op, ty, p] : ops) {
|
for (auto [op, ty, p] : ops) {
|
||||||
if (prec >= p and eat(op)) {
|
if (prec >= p and eat(op)) {
|
||||||
auto right = parse_binary(prec - 1);
|
auto right = parse_binary(prec - 1);
|
||||||
|
|
||||||
left = std::make_unique<Expr>(loc,
|
left = std::make_unique<Expr>(loc,
|
||||||
ty,
|
ty,
|
||||||
Expr::Binary { std::move(left), std::move(right) });
|
Expr::Binary { std::move(left), std::move(right) });
|
||||||
|
|
||||||
|
should_continue = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1022,13 +1131,14 @@ auto Parser::parse_binary(int prec) -> std::unique_ptr<Expr>
|
|||||||
auto Parser::parse_prefix() -> std::unique_ptr<Expr>
|
auto Parser::parse_prefix() -> std::unique_ptr<Expr>
|
||||||
{
|
{
|
||||||
auto loc = current_loc();
|
auto loc = current_loc();
|
||||||
auto expr = parse_operand();
|
|
||||||
|
auto next = [&]() { return parse_operand(); };
|
||||||
if (eat('-')) {
|
if (eat('-')) {
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::Negate, std::move(expr));
|
return std::make_unique<Expr>(loc, Expr::Ty::Negate, next());
|
||||||
} else if (eat('!')) {
|
} else if (eat('!')) {
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::Not, std::move(expr));
|
return std::make_unique<Expr>(loc, Expr::Ty::Not, next());
|
||||||
} else {
|
} else {
|
||||||
return expr;
|
return next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -234,6 +234,11 @@ namespace asmer {
|
|||||||
/// true means fail
|
/// true means fail
|
||||||
bool arg_count_wrong(const Line& ins, size_t count);
|
bool arg_count_wrong(const Line& ins, size_t count);
|
||||||
|
|
||||||
|
auto define_sym(std::string ident, uint16_t value)
|
||||||
|
-> std::expected<void, std::string>;
|
||||||
|
auto get_sym(const std::string& ident)
|
||||||
|
-> std::expected<uint16_t, std::string>;
|
||||||
|
|
||||||
void error(Loc loc, std::string_view message);
|
void error(Loc loc, std::string_view message);
|
||||||
|
|
||||||
std::string_view m_filename;
|
std::string_view m_filename;
|
||||||
|
|||||||
@ -373,6 +373,29 @@ void Builder::binary_imm(Reg dst, Reg op1, uint16_t op2, Op op)
|
|||||||
i.build(*this);
|
i.build(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Builder::call_reg(Reg op1)
|
||||||
|
{
|
||||||
|
LOG;
|
||||||
|
auto i = InsBuilder(Op::Call);
|
||||||
|
i.op1_reg(op1);
|
||||||
|
i.build(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::call_imm(uint16_t op1)
|
||||||
|
{
|
||||||
|
LOG;
|
||||||
|
auto i = InsBuilder(Op::Call);
|
||||||
|
i.imm(op1);
|
||||||
|
i.build(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::ret()
|
||||||
|
{
|
||||||
|
LOG;
|
||||||
|
auto i = InsBuilder(Op::Ret);
|
||||||
|
i.build(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void Builder::reti()
|
void Builder::reti()
|
||||||
{
|
{
|
||||||
LOG;
|
LOG;
|
||||||
|
|||||||
@ -53,6 +53,9 @@ 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 call_reg(Reg op1);
|
||||||
|
void call_imm(uint16_t op1);
|
||||||
|
void ret();
|
||||||
void reti();
|
void reti();
|
||||||
void lvcd_reg(Reg op1);
|
void lvcd_reg(Reg op1);
|
||||||
void lvcd_imm(uint16_t op1);
|
void lvcd_imm(uint16_t op1);
|
||||||
|
|||||||
@ -70,6 +70,8 @@ auto IoDevice::create() -> std::expected<std::unique_ptr<IoDevice>, std::string>
|
|||||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||||
return std::unexpected(SDL_GetError());
|
return std::unexpected(SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_StopTextInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
@ -204,8 +206,8 @@ auto IoDevice::poll_event() -> std::unique_ptr<IoEvent>
|
|||||||
= (event.key.keysym.mod & KMOD_RALT) != 0,
|
= (event.key.keysym.mod & KMOD_RALT) != 0,
|
||||||
});
|
});
|
||||||
// default:
|
// default:
|
||||||
// std::println(stderr, "warning: unhandled event '{}'",
|
// std::println(
|
||||||
// event.type);
|
// stderr, "warning: unhandled event '{}'", event.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
154
src/vm.cpp
154
src/vm.cpp
@ -6,7 +6,9 @@
|
|||||||
#include <print>
|
#include <print>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace vc5;
|
using namespace vc5;
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
@ -16,6 +18,93 @@ void VM::load(uint16_t offset, const uint8_t* data, size_t data_size)
|
|||||||
std::memcpy(&m_mem[offset], data, data_size);
|
std::memcpy(&m_mem[offset], data, data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Debugger {
|
||||||
|
public:
|
||||||
|
explicit Debugger(VM& vm)
|
||||||
|
: vm(&vm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_on_line(bool halted)
|
||||||
|
{
|
||||||
|
if (halted) {
|
||||||
|
m_halted_last_call = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_continue) {
|
||||||
|
if (m_halted_last_call and m_break_on_halt) {
|
||||||
|
m_continue = false;
|
||||||
|
} else {
|
||||||
|
auto ip = vm->reg(Reg::Rip);
|
||||||
|
for (const auto& addr : m_break_on_address) {
|
||||||
|
if (addr == ip) {
|
||||||
|
m_continue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_continue)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_halted_last_call = false;
|
||||||
|
|
||||||
|
auto line = std::string();
|
||||||
|
do {
|
||||||
|
std::print("> ");
|
||||||
|
std::cout.flush();
|
||||||
|
std::getline(std::cin, line);
|
||||||
|
|
||||||
|
if (line == "r") {
|
||||||
|
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],
|
||||||
|
vm->reg(static_cast<Reg>(reg)),
|
||||||
|
vm->reg(static_cast<Reg>(reg)));
|
||||||
|
}
|
||||||
|
} else if (line == "c") {
|
||||||
|
m_continue = true;
|
||||||
|
break;
|
||||||
|
} else if (line == "brhlt") {
|
||||||
|
m_break_on_halt = true;
|
||||||
|
} else if (line.starts_with("p")) {
|
||||||
|
uint16_t v
|
||||||
|
= std::strtoul(line.c_str() + 2, nullptr, 16) & 0xfffe;
|
||||||
|
std::println("[{: 4x}] {: 4x}", v, vm->word(v));
|
||||||
|
} else if (line.starts_with("br")) {
|
||||||
|
uint16_t v
|
||||||
|
= std::strtoul(line.c_str() + 3, nullptr, 16) & 0xfffe;
|
||||||
|
m_break_on_address.push_back(v);
|
||||||
|
}
|
||||||
|
} while (!line.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VM* vm;
|
||||||
|
std::vector<uint16_t> m_break_on_address {};
|
||||||
|
bool m_halted_last_call = false;
|
||||||
|
bool m_break_on_halt = false;
|
||||||
|
bool m_continue = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int VM::run()
|
int VM::run()
|
||||||
{
|
{
|
||||||
constexpr uint16_t bootloader_size = 512;
|
constexpr uint16_t bootloader_size = 512;
|
||||||
@ -23,6 +112,8 @@ int VM::run()
|
|||||||
m_disk->read(&m_mem[i * m_disk->block_size], i);
|
m_disk->read(&m_mem[i * m_disk->block_size], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto debugger = Debugger(*this);
|
||||||
|
|
||||||
m_on = true;
|
m_on = true;
|
||||||
while (m_on) {
|
while (m_on) {
|
||||||
if (not m_halted) {
|
if (not m_halted) {
|
||||||
@ -31,42 +122,12 @@ int VM::run()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (not m_halted) {
|
// debugger.run_on_line(m_halted);
|
||||||
// auto line = std::string();
|
|
||||||
// do {
|
|
||||||
// std::print("> ");
|
|
||||||
// std::cout.flush();
|
|
||||||
// std::getline(std::cin, line);
|
|
||||||
//
|
|
||||||
// if (line == "r") {
|
|
||||||
// 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],
|
|
||||||
// m_regs[reg],
|
|
||||||
// m_regs[reg]);
|
|
||||||
// }
|
|
||||||
// } else if (line.starts_with("p")) {
|
|
||||||
// uint16_t v
|
|
||||||
// = std::strtoul(line.c_str() + 2, nullptr, 16) &
|
|
||||||
// 0xfffe;
|
|
||||||
// std::println("[{: 4x}] {: 4x}", v, word(v));
|
|
||||||
// }
|
|
||||||
// } while (!line.empty());
|
|
||||||
// }
|
|
||||||
|
|
||||||
poll_events();
|
poll_events();
|
||||||
|
if (m_halted) {
|
||||||
|
std::this_thread::sleep_for(15ms);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -255,7 +316,7 @@ int VM::run_instruction()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::LoadWord: {
|
case Op::LoadWord: {
|
||||||
auto addr = ins.mem_addr(ins.op2(), eat_word());
|
auto addr = ins.mem_addr(ins.op1(), eat_word());
|
||||||
|
|
||||||
if ((addr & 0b1) != 0) {
|
if ((addr & 0b1) != 0) {
|
||||||
std::println(stderr,
|
std::println(stderr,
|
||||||
@ -293,7 +354,7 @@ int VM::run_instruction()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::LoadByte: {
|
case Op::LoadByte: {
|
||||||
auto addr = ins.mem_addr(ins.op2(), 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));
|
||||||
@ -384,6 +445,22 @@ int VM::run_instruction()
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Op::Call: {
|
||||||
|
auto op1 = ins.op1_or_imm();
|
||||||
|
|
||||||
|
auto return_addr = word(*m_rip);
|
||||||
|
set_word(*m_rsp, return_addr);
|
||||||
|
*m_rsp -= 2;
|
||||||
|
|
||||||
|
*m_rip = op1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Op::Ret: {
|
||||||
|
*m_rsp -= 2;
|
||||||
|
auto return_addr = word(*m_rsp);
|
||||||
|
*m_rip = return_addr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Op::RetI: {
|
case Op::RetI: {
|
||||||
*m_rsp -= 2;
|
*m_rsp -= 2;
|
||||||
auto return_addr = word(*m_rsp);
|
auto return_addr = word(*m_rsp);
|
||||||
@ -469,6 +546,7 @@ void VM::poll_events()
|
|||||||
set_word(d.keycode_addr, event->as_key_event().key);
|
set_word(d.keycode_addr, event->as_key_event().key);
|
||||||
interrupt(d.int_handler);
|
interrupt(d.int_handler);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,6 +622,10 @@ const char* vc5::op_str(Op op)
|
|||||||
return "Sub";
|
return "Sub";
|
||||||
case Op::RSub:
|
case Op::RSub:
|
||||||
return "RSub";
|
return "RSub";
|
||||||
|
case Op::Call:
|
||||||
|
return "Call";
|
||||||
|
case Op::Ret:
|
||||||
|
return "Ret";
|
||||||
case Op::RetI:
|
case Op::RetI:
|
||||||
return "RetI";
|
return "RetI";
|
||||||
case Op::LVCD:
|
case Op::LVCD:
|
||||||
|
|||||||
@ -33,6 +33,9 @@ enum class Op : uint16_t {
|
|||||||
Sub,
|
Sub,
|
||||||
RSub,
|
RSub,
|
||||||
|
|
||||||
|
Call,
|
||||||
|
Ret,
|
||||||
|
|
||||||
// return from interrupt
|
// return from interrupt
|
||||||
RetI,
|
RetI,
|
||||||
// load video character display
|
// load video character display
|
||||||
|
|||||||
@ -9,7 +9,7 @@ endif
|
|||||||
|
|
||||||
syn keyword Keyword const align db dw
|
syn keyword Keyword const align db dw
|
||||||
syn keyword Keyword nop hlt jmp jnz mov cmp or and xor shl rshl shr rshr add sub rsub
|
syn keyword Keyword nop hlt jmp jnz mov cmp or and xor shl rshl shr rshr add sub rsub
|
||||||
syn keyword Keyword reti lkbd lvcd dskr dskw
|
syn keyword Keyword call ret reti lkbd lvcd dskr dskw
|
||||||
syn keyword Operator r0 r1 r2 r3 r4 r5 rbp rsp rfl rip
|
syn keyword Operator r0 r1 r2 r3 r4 r5 rbp rsp rfl rip
|
||||||
|
|
||||||
syn match Operator '+'
|
syn match Operator '+'
|
||||||
@ -27,6 +27,8 @@ syn match Number '[1-9][0-9]*'
|
|||||||
syn match Number '0b[01]*'
|
syn match Number '0b[01]*'
|
||||||
syn match Number '0x[0-9a-fA-F]*'
|
syn match Number '0x[0-9a-fA-F]*'
|
||||||
|
|
||||||
|
syn match Character "'\\\?.'"
|
||||||
|
|
||||||
syn region String start=+"+ skip=+\\\\\|\\"+ end=+"+
|
syn region String start=+"+ skip=+\\\\\|\\"+ end=+"+
|
||||||
|
|
||||||
syn match Comment ";.*$"
|
syn match Comment ";.*$"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user