From c1c8bcf0b3a4572cf311c056e33f78e1fe2a3c02 Mon Sep 17 00:00:00 2001 From: sfja Date: Thu, 29 Jan 2026 23:19:00 +0100 Subject: [PATCH] auto scroll --- Makefile | 2 +- programs/boot.vc5asm | 125 ++++++++++++++++++++++++++++++++++------ programs/term.txt | 52 +++++++++++++++++ src/assembler.cpp | 74 +++++++++++++++++------- src/assembler.hpp | 3 + src/builder.cpp | 131 +++++------------------------------------- src/builder.hpp | 47 ++++++++------- src/scanner.cpp | 2 +- src/scanner.hpp | 3 + src/vm.cpp | 35 +++++++++-- src/vm.hpp | 8 ++- vim/syntax/vc5asm.vim | 8 ++- 12 files changed, 305 insertions(+), 185 deletions(-) create mode 100644 programs/term.txt diff --git a/Makefile b/Makefile index 1ff3d33..0a2cd31 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ MAKEFLAGS += -j16 -CXXFLAGS := -std=c++23 -Wall -Wextra -pedantic-errors -fsanitize=address +CXXFLAGS := -std=c++23 -Wall -Wextra -pedantic-errors -fsanitize=address -g -ggdb LDFLAGS := CXXFLAGS += $(shell pkgconf sdl2 --cflags) diff --git a/programs/boot.vc5asm b/programs/boot.vc5asm index e0bfff9..59cbb30 100644 --- a/programs/boot.vc5asm +++ b/programs/boot.vc5asm @@ -1,10 +1,12 @@ -const fl_zero 0x1 -const fl_eq 0x2 -const fl_be 0x4 -const fl_lt 0x8 -const fl_err 0xa +const fl_zero 1 << 0 +const fl_eq 1 << 1 +const fl_be 1 << 2 +const fl_ab 1 << 3 +const fl_lt 1 << 4 +const fl_gt 1 << 5 +const fl_err 1 << 6 const vcd_base 0x2000 @@ -66,7 +68,7 @@ key_press_int: mov [rsp], r1 add rsp, 2 - call print_u16 + call term_putc sub rsp, 2 jmp .leave @@ -81,7 +83,9 @@ key_press_int: .leave: reti + const term_width 20 +const term_height 12 term_y: dw 0 @@ -94,24 +98,113 @@ term_putc: mov rbp, rsp mov r1, [rbp-6] - - mov r0, [counter] - add r0, vcd_base + + cmp r1, '\n' + mov r0, rfl + and r0, fl_eq + jnz r0, .next_row + cmp r1, ' ' - mov r2, rfl - and r2, fl_eq - jnz r2, .incr + mov r0, rfl + and r0, fl_eq + jnz r0, .next_col + + cmp r1, '\0' + mov r0, rfl + and r0, fl_eq + jnz r0, .next_col + + mov r0, [term_y] + mul r0, term_width + add r0, vcd_base + mov r2, [term_x] + add r0, r2 mov byte [r0], r1 -.incr: - mov r0, [counter] - add r0, 1 - mov [counter], r0 + +.next_col: + mov r2, [term_x] + add r2, 1 + mov [term_x], r2 + cmp r2, term_width + mov r0, rfl + and r0, fl_lt + jnz r0, .leave + +.next_row: + mov r0, 0 + mov [term_x], r0 + + mov r2, [term_y] + add r2, 1 + cmp r2, term_height + mov r0, rfl + and r0, fl_lt + jnz r0, .increment_y + + call term_scroll + jmp .leave + +.increment_y: + mov [term_y], r2 + .leave: mov rsp, rbp sub rsp, 2 mov rbp, [rsp] ret +term_scroll: + mov r1, 0 ; row + jmp .l1_cond +.l1_body: + mov r2, 0 ; col + jmp .l2_cond +.l2_body: + mov r0, term_width + mul r0, r1 + add r0, r2 + mov r4, byte [vcd_base + term_width + r0] + mov byte [vcd_base + r0], r4 + + add r2, 1 +.l2_cond: + cmp r2, term_width + mov r0, rfl + and r0, fl_lt + jnz r0, .l2_body + + add r1, 1 +.l1_cond: + cmp r1, term_height - 1 + mov r0, rfl + and r0, fl_lt + jnz r0, .l1_body + + mov r2, 0 + jmp .l3_cond +.l3_body: + mov r0, ' ' + mov byte [vcd_base + (term_height - 1) * term_width + r2], r0 + + add r2, 1 +.l3_cond: + cmp r2, term_width + mov r0, rfl + and r0, fl_lt + jnz r0, .l3_body + + ret + + + + + + + + + + + print_u16: mov [rsp], rbp add rsp, 2 diff --git a/programs/term.txt b/programs/term.txt new file mode 100644 index 0000000..5e97a21 --- /dev/null +++ b/programs/term.txt @@ -0,0 +1,52 @@ + +const term_width: u16 = 20; +const term_height: u16 = 12; + +static term_x: u16 = 0; +static term_y: u16 = 0; + +static vcd: *u16 = 0x2000; + +fn term_putc(ch: u16) +{ + if (ch == '\n') + continue 'next_line; + + if (ch == '\0') + continue 'next_col; + if (ch == ' ') + continue 'next_col; + + vcd[term_y * term_width + term_x] = ch; + +'next_col: + term_x += 1; + if (term_x < term_width) + return + +'next_line: + term_x = 0; + + if (term_y + 1 >= term_height) { + term_scroll(); + } else { + term_y += 1; + } +} + +fn term_scroll() +{ + for row: u16 in 0..term_height - 1 { + for col: u16 in 0..term_width { + + vcd[row * term_width + col] + = vcd[(row + 1) * term_width + col]; + } + } + for col: u16 in 0..term_width { + vcd[(term_height - 1) * term_width + col] = ' '; + } +} + +// vim: syntax=rust + diff --git a/src/assembler.cpp b/src/assembler.cpp index 0ee0e6b..dd27700 100644 --- a/src/assembler.cpp +++ b/src/assembler.cpp @@ -245,6 +245,8 @@ enum class Mnemonic { add, sub, rsub, + mul, + imul, call, ret, reti, @@ -276,6 +278,8 @@ static const auto mnemonic_map { "add", M::add }, { "sub", M::sub }, { "rsub", M::rsub }, + { "mul", M::mul }, + { "imul", M::imul }, { "call", M::call }, { "ret", M::ret }, { "reti", M::reti }, @@ -459,7 +463,9 @@ void Assembler::assemble_line(const Line& line) case M::rshr: case M::add: case M::sub: - case M::rsub: { + case M::rsub: + case M::mul: + case M::imul: { auto dst = std::unique_ptr {}; auto op1_store = std::unique_ptr {}; auto op2 = std::unique_ptr {}; @@ -519,6 +525,12 @@ void Assembler::assemble_line(const Line& line) case Mnemonic::rsub: l.rsub_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); break; + case M::mul: + l.mul_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case M::imul: + l.imul_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; default: assert(false && "unhandled"); } @@ -554,6 +566,12 @@ void Assembler::assemble_line(const Line& line) case Mnemonic::rsub: l.rsub_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); break; + case M::mul: + l.mul_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case M::imul: + l.imul_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; default: assert(false && "unhandled"); } @@ -672,6 +690,9 @@ auto Assembler::eval_operand(const Expr& expr) -> std::unique_ptr return eval_operand_mem(*expr.as_unary()); case Expr::Ty::MemByte: { auto evaled = eval_operand_mem(*expr.as_unary()); + if (not evaled) + return nullptr; + switch (evaled->ty) { case EOT::MemWordImm: evaled->ty = EOT::MemByteImm; @@ -695,7 +716,10 @@ auto Assembler::eval_operand(const Expr& expr) -> std::unique_ptr case Expr::Ty::Shl: case Expr::Ty::Shr: case Expr::Ty::Add: - case Expr::Ty::Sub: { + case Expr::Ty::Sub: + case Expr::Ty::Mul: + case Expr::Ty::Div: + case Expr::Ty::Mod: { return eval_operand_to_imm(expr); } } @@ -715,7 +739,10 @@ auto Assembler::eval_operand_mem(const Expr& expr) case Expr::Ty::Xor: case Expr::Ty::And: case Expr::Ty::Shl: - case Expr::Ty::Shr: { + case Expr::Ty::Shr: + case Expr::Ty::Mul: + case Expr::Ty::Div: + case Expr::Ty::Mod: { auto op = eval_operand_to_imm(expr); if (not op) return nullptr; @@ -822,7 +849,9 @@ auto Assembler::eval_operand_to_imm(const Expr& expr) return std::make_unique(loc, EOT::Imm, *result); } case Expr::Ty::Reg: - error(loc, "registers cannot be part of an expression"); + if (not m_second_pass) { + error(loc, "registers cannot be part of an expression"); + } return nullptr; case Expr::Ty::Int: return std::make_unique( @@ -843,7 +872,10 @@ auto Assembler::eval_operand_to_imm(const Expr& expr) case Expr::Ty::Shl: case Expr::Ty::Shr: case Expr::Ty::Add: - case Expr::Ty::Sub: { + case Expr::Ty::Sub: + case Expr::Ty::Mul: + case Expr::Ty::Div: + case Expr::Ty::Mod: { auto& [left_expr, right_expr] = expr.as_binary(); auto left = eval_operand_to_imm(*left_expr); if (not left) @@ -886,15 +918,12 @@ auto Assembler::binary_op(Expr::Ty exprTy, uint16_t left, uint16_t right) return (uint16_t)((int16_t)left + (int16_t)right); case Expr::Ty::Sub: return (uint16_t)((int16_t)left - (int16_t)right); - - // taken from vc3 - // case Expr::Ty::Mul: - // return (uint16_t)((int16_t)left * (int16_t)right); - // case Expr::Ty::Div: - // return (uint16_t)((int16_t)left / (int16_t)right); - // case Expr::Ty::Mod: - // return (uint16_t)((int16_t)left % (int16_t)right); - + case Expr::Ty::Mul: + return (uint16_t)((int16_t)left * (int16_t)right); + case Expr::Ty::Div: + return (uint16_t)((int16_t)left / (int16_t)right); + case Expr::Ty::Mod: + return (uint16_t)((int16_t)left % (int16_t)right); default: assert(false && "unhandled"); } @@ -1087,13 +1116,16 @@ auto Parser::parse_binary(int prec) -> std::unique_ptr using Op = std::tuple; constexpr auto ops = std::array { - Op { TT::Pipe, T::Or, 5 }, - Op { TT::Hat, T::Xor, 4 }, - Op { TT::Ampersand, T::And, 3 }, - Op { TT::LtLt, T::Shl, 2 }, - Op { TT::GtGt, T::Shr, 2 }, - Op { TT::Plus, T::Add, 1 }, - Op { TT::Minus, T::Sub, 1 }, + Op { TT::Pipe, T::Or, 6 }, + Op { TT::Hat, T::Xor, 5 }, + Op { TT::Ampersand, T::And, 4 }, + Op { TT::LtLt, T::Shl, 3 }, + Op { TT::GtGt, T::Shr, 3 }, + Op { TT::Plus, T::Add, 2 }, + Op { TT::Minus, T::Sub, 2 }, + Op { TT::Aster, T::Mul, 1 }, + Op { TT::Slash, T::Div, 1 }, + Op { TT::Perc, T::Mod, 1 }, }; if (prec == 0) { diff --git a/src/assembler.hpp b/src/assembler.hpp index c4176b6..34dadc5 100644 --- a/src/assembler.hpp +++ b/src/assembler.hpp @@ -44,6 +44,9 @@ namespace asmer { Shr, Add, Sub, + Mul, + Div, + Mod, }; using Ptr = std::unique_ptr; diff --git a/src/builder.cpp b/src/builder.cpp index c7d8c21..bc1155b 100644 --- a/src/builder.cpp +++ b/src/builder.cpp @@ -1,3 +1,4 @@ +#define DEFINE_BINARY_INSTRUCTIONS #include "builder.hpp" #include "vm.hpp" #include @@ -235,125 +236,21 @@ void Builder::cmp_imm(Reg op1, uint16_t op2) i.build(*this); } -void Builder::or_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::Or); -} +#define X(NAME, OP) \ + void Builder::NAME##_reg(Reg dst, Reg op1, Reg op2) \ + { \ + LOG; \ + binary_reg(dst, op1, op2, Op::OP); \ + } \ + void Builder::NAME##_imm(Reg dst, Reg op1, uint16_t op2) \ + { \ + LOG; \ + binary_imm(dst, op1, op2, Op::OP); \ + } -void Builder::and_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::And); -} +BINARY_INSTRUCTIONS -void Builder::xor_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::Xor); -} - -void Builder::shl_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::Shl); -} - -void Builder::rshl_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::RShl); -} - -void Builder::shr_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::Shr); -} - -void Builder::rshr_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::RShr); -} - -void Builder::add_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::Add); -} - -void Builder::sub_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::Sub); -} - -void Builder::rsub_reg(Reg dst, Reg op1, Reg op2) -{ - LOG; - binary_reg(dst, op1, op2, Op::RSub); -} - -void Builder::or_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::Xor); -} - -void Builder::and_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::And); -} - -void Builder::xor_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::Xor); -} - -void Builder::shl_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::Shl); -} - -void Builder::rshl_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::RShl); -} - -void Builder::shr_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::Shr); -} - -void Builder::rshr_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::RShr); -} - -void Builder::add_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::Add); -} - -void Builder::sub_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::Sub); -} - -void Builder::rsub_imm(Reg dst, Reg op1, uint16_t op2) -{ - LOG; - binary_imm(dst, op1, op2, Op::RSub); -} +#undef X void Builder::binary_reg(Reg dst, Reg op1, Reg op2, Op op) { diff --git a/src/builder.hpp b/src/builder.hpp index 842c0a5..92b854a 100644 --- a/src/builder.hpp +++ b/src/builder.hpp @@ -4,6 +4,20 @@ #include #include +#define BINARY_INSTRUCTIONS \ + X(or, Or) \ + X(and, And) \ + X(xor, Xor) \ + X(shl, Shl) \ + X(rshl, RShl) \ + X(shr, Shr) \ + X(rshr, RShr) \ + X(add, Add) \ + X(sub, Sub) \ + X(rsub, RSub) \ + X(mul, Mul) \ + X(imul, IMul) + namespace vc5::tools { using namespace vc5; @@ -33,26 +47,15 @@ public: void stb_imm(uint16_t dst, Reg op2); void cmp_reg(Reg op1, Reg op2); void cmp_imm(Reg op1, uint16_t op2); - void or_reg(Reg dst, Reg op1, Reg op2); - void and_reg(Reg dst, Reg op1, Reg op2); - void xor_reg(Reg dst, Reg op1, Reg op2); - void shl_reg(Reg dst, Reg op1, Reg op2); - void rshl_reg(Reg dst, Reg op1, Reg op2); - void shr_reg(Reg dst, Reg op1, Reg op2); - void rshr_reg(Reg dst, Reg op1, Reg op2); - void add_reg(Reg dst, Reg op1, Reg op2); - void sub_reg(Reg dst, Reg op1, Reg op2); - void rsub_reg(Reg dst, Reg op1, Reg op2); - void or_imm(Reg dst, Reg op1, uint16_t op2); - void and_imm(Reg dst, Reg op1, uint16_t op2); - void xor_imm(Reg dst, Reg op1, uint16_t op2); - void shl_imm(Reg dst, Reg op1, uint16_t op2); - void rshl_imm(Reg dst, Reg op1, uint16_t op2); - void shr_imm(Reg dst, Reg op1, uint16_t op2); - void rshr_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 rsub_imm(Reg dst, Reg op1, uint16_t op2); + +#define X(NAME, OP) \ + void NAME##_reg(Reg dst, Reg op1, Reg op2); \ + void NAME##_imm(Reg dst, Reg op1, uint16_t op2); + + BINARY_INSTRUCTIONS + +#undef X + void call_reg(Reg op1); void call_imm(uint16_t op1); void ret(); @@ -103,3 +106,7 @@ private: }; } + +#ifndef DEFINE_BINARY_INSTRUCTIONS +#undef BINARY_INSTRUCTIONS +#endif diff --git a/src/scanner.cpp b/src/scanner.cpp index 660512a..f99ffe2 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -131,7 +131,7 @@ auto Scanner::next() -> Tok } return tok(TT::Gt, loc); } - if (test_in("\n()[].,:|^+-!")) { + if (test_in("\n()[].,:|^+-*/%!")) { auto ty = static_cast(current()); step(); return tok(ty, loc); diff --git a/src/scanner.hpp b/src/scanner.hpp index 01b7ea9..14222c7 100644 --- a/src/scanner.hpp +++ b/src/scanner.hpp @@ -54,6 +54,9 @@ struct Tok { Ampersand = '&', Plus = '+', Minus = '-', + Aster = '*', + Slash = '/', + Perc = '%', Exclam = '!', Lt = '<', Gt = '>', diff --git a/src/vm.cpp b/src/vm.cpp index 58a0161..cbf7d90 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -411,11 +411,21 @@ int VM::run_instruction() } else { *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Be)); } + if (op1 > op2) { + *m_rfl |= 1u << std::to_underlying(Flag::Ab); + } else { + *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Ab)); + } if ((int16_t)op1 < (int16_t)op2) { *m_rfl |= 1u << std::to_underlying(Flag::Lt); } else { *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Lt)); } + if ((int16_t)op1 > (int16_t)op2) { + *m_rfl |= 1u << std::to_underlying(Flag::Gt); + } else { + *m_rfl &= (uint16_t)~(1u << std::to_underlying(Flag::Gt)); + } break; } case Op::Or: @@ -427,13 +437,18 @@ int VM::run_instruction() case Op::RShr: case Op::Add: case Op::Sub: - case Op::RSub: { + case Op::RSub: + case Op::Mul: + case Op::IMul: { auto op1 = ins.op1(); auto op2 = ins.op2_or_imm(); auto dst_reg = ins.dst_reg(); uint16_t* dst = &m_regs[std::to_underlying(dst_reg)]; + auto as_u16 = [](auto v) { return static_cast(v); }; + auto as_i16 = [](auto v) { return static_cast(v); }; + switch (op) { case Op::Or: *dst = op1 | op2; @@ -445,16 +460,16 @@ int VM::run_instruction() *dst = op1 & op2; break; case Op::Shl: - *dst = static_cast(op1 << op2); + *dst = as_u16(op1 << op2); break; case Op::RShl: - *dst = static_cast(op2 << op1); + *dst = as_u16(op2 << op1); break; case Op::Shr: - *dst = static_cast(op1 >> op2); + *dst = as_u16(op1 >> op2); break; case Op::RShr: - *dst = static_cast(op2 >> op1); + *dst = as_u16(op2 >> op1); break; case Op::Add: *dst = op1 + op2; @@ -465,6 +480,12 @@ int VM::run_instruction() case Op::RSub: *dst = op2 - op1; break; + case Op::Mul: + *dst = op1 * op2; + break; + case Op::IMul: + *dst = as_u16(as_i16(op1) * as_i16(op2)); + break; default: break; } @@ -647,6 +668,10 @@ auto vc5::op_str(Op op) -> std::string_view return "Sub"; case Op::RSub: return "RSub"; + case Op::Mul: + return "Mul"; + case Op::IMul: + return "IMul"; case Op::Call: return "Call"; case Op::Ret: diff --git a/src/vm.hpp b/src/vm.hpp index 3ad7508..3eaaa91 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -33,6 +33,8 @@ enum class Op : uint16_t { Add, Sub, RSub, + Mul, + IMul, Call, Ret, @@ -82,8 +84,10 @@ enum class Flag : uint16_t { Zero = 0, Eq = 1, Be = 2, - Lt = 3, - Err = 4, + Ab = 3, + Lt = 4, + Gt = 5, + Err = 6, }; struct VcdDescript { diff --git a/vim/syntax/vc5asm.vim b/vim/syntax/vc5asm.vim index 386914e..2652e73 100644 --- a/vim/syntax/vc5asm.vim +++ b/vim/syntax/vc5asm.vim @@ -8,12 +8,17 @@ if exists("b:current_syntax") endif 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 +syn keyword Keyword or and xor shl rshl shr rshr add sub rsub mul imul syn keyword Keyword call ret reti lkbd lvcd dskr dskw +syn keyword Type byte word syn keyword Operator r0 r1 r2 r3 r4 r5 rbp rsp rfl rip syn match Operator '+' syn match Operator '-' +syn match Operator '*' +syn match Operator '/' +syn match Operator '%' syn match Operator '=' syn match Operator '<' syn match Operator '>' @@ -21,7 +26,6 @@ syn match Operator '|' syn match Operator '^' syn match Operator '&' - syn match Number '0' syn match Number '[1-9][0-9]*' syn match Number '0b[01]*'