diff --git a/Makefile b/Makefile index c54650f..5909eba 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,18 @@ CXXFLAGS += $(shell pkgconf sdl2 --libs) build_dir = build obj_dir = $(build_dir)/obj -sources := $(shell find src/ -name *.cpp) +sources := $(shell find src/ -name *.cpp -and -not -name *main.cpp) -all: $(build_dir)/vc5 +vc5_sources := $(sources) src/main.cpp +asm_sources := $(sources) src/asm_main.cpp -$(build_dir)/vc5: $(sources:%.cpp=$(obj_dir)/%.o) +all: $(build_dir)/vc5 $(build_dir)/asm + +$(build_dir)/vc5: $(vc5_sources:%.cpp=$(obj_dir)/%.o) + @mkdir -p $(dir $@) + g++ $^ -o $@ $(CXXFLAGS) $(LDFLAGS) + +$(build_dir)/asm: $(asm_sources:%.cpp=$(obj_dir)/%.o) @mkdir -p $(dir $@) g++ $^ -o $@ $(CXXFLAGS) $(LDFLAGS) diff --git a/programs/boot.vc5asm b/programs/boot.vc5asm new file mode 100644 index 0000000..0d4f51b --- /dev/null +++ b/programs/boot.vc5asm @@ -0,0 +1,87 @@ + + +const KBD_STATUS 0x1ffc +const KBD_CODE 0x1ffe +const VCD 0x2000 + +const FL_EQ 0x2 +const FL_EQ 0x4 + +const KBD_FLAG_IS_RELEASE 0x1 + + mov rsp, 0x1000 + mov rbp, rsp + + 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 [0x700], r0 + lvcd 0x700 + + ; setup keyboard (KBD) + ; descriptor table structure: + ; [addr + 0] status address + ; [addr + 2] keycode address + ; [addr + 4] keyboard interrupt handler + mov r0, KBD_STATUS + mov [0x702], r0 + mov r0, KBD_CODE + mov [0x704], r0 + mov r0, key_press_int + mov [0x706], r0 + lkbd 0x702 + + ; character counter + mov r0, 0 + mov [0x600], r0 + +main_loop: + hlt + jmp main_loop + +key_press_int: + mov r0, [KBD_STATUS] + and r0, r0, KBD_FLAG_IS_RELEASE + jnz r0, .leave + + mov r0, [0x600] + add r0, r0, VCD + mov r1, [KBC_CODE] + cmp r1, 44 ; spacebar + mov r2, rfl + and r2, r2, FL_EQ + jnz r2, .incr + add r1, r1, 61 + mov byte [r0], r1 +.incr: + mov r0, [0x600] + add r0, r0, 1 + mov [0x600], r0 +.leave: + 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 + + + diff --git a/src/asm_main.cpp b/src/asm_main.cpp new file mode 100644 index 0000000..09eb4f6 --- /dev/null +++ b/src/asm_main.cpp @@ -0,0 +1,58 @@ +#include "assembler.hpp" +#include +#include +#include +#include +#include + +using namespace std::literals; +namespace fs = std::filesystem; + +int main(int argc, char** argv) +{ + auto input_filename = std::string_view(); + auto output_filename = std::string_view(); + + int i = 0; + while (i < argc) { + auto arg = std::string_view(argv[i]); + if (arg == "--help") { + std::println("asm -o "); + return EXIT_SUCCESS; + } else if (arg == "-o") { + i += 1; + if (i >= argc) { + std::println(stderr, "error: expected filename after -o"); + return EXIT_FAILURE; + } + output_filename = argv[i]; + i += 1; + } else if (arg.starts_with("-")) { + std::println(stderr, "error: unknown flag \"{}\"", arg); + return EXIT_FAILURE; + } else { + input_filename = argv[i]; + i += 1; + } + } + + if (input_filename.empty()) { + std::println(stderr, "error: no input file specified"); + return EXIT_FAILURE; + } + + auto input_path = fs::path(input_filename); + auto output_path = fs::path(output_filename); + + if (output_path.empty()) { + output_path = input_path; + output_path.replace_extension("o"); + } + + auto result = vc5::tools::assemble_file(input_path, output_path); + + if (not result) { + std::println(stderr, "error: {}", result.error()); + return EXIT_FAILURE; + } +} diff --git a/src/assembler.cpp b/src/assembler.cpp index 4d0c91e..9691be3 100644 --- a/src/assembler.cpp +++ b/src/assembler.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -48,9 +49,14 @@ auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path) input_file.read(&text[0], size); } - auto assembler = Assembler(text); + auto input_filename = std::string(input_path); + + auto assembler = Assembler(input_filename, text); auto result = assembler.assemble_file(); + if (not result) + return std::unexpected(result.error()); + auto& program = *result; { @@ -72,15 +78,20 @@ auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path) auto Assembler::assemble_file() -> std::expected, std::string> { - auto parser = Parser(m_text); + auto parser = Parser(m_filename, m_text); auto lines = std::vector>(); while (true) { + parser.eat_whitespace(); + if (parser.is_done()) { + break; + } if (parser.next_is_const()) { auto stmt = parser.parse_const(); if (not stmt) { - return std::unexpected("parsing failed"); + parser.try_recover(); + continue; } auto value = eval_operand_to_imm(*stmt->expr); @@ -94,7 +105,8 @@ auto Assembler::assemble_file() } else if (parser.next_is_align()) { auto stmt = parser.parse_align(); if (not stmt) { - return std::unexpected("parsing failed"); + parser.try_recover(); + continue; } auto value = eval_operand_to_imm(*stmt->expr); @@ -132,10 +144,14 @@ auto Assembler::assemble_file() stmt->loc, nullptr, "db", std::move(ops))); } else { - auto line = parser.parse_line(); if (not line) { - break; + if (parser.is_done()) { + break; + } + + parser.try_recover(); + continue; } lines.push_back(std::move(line)); } @@ -148,6 +164,19 @@ auto Assembler::assemble_file() m_builder.set_ip(0); m_second_pass = true; + for (const auto& line : lines) { + std::println("[{}] = {}", m_builder.ip(), line->ident); + assemble_line(*line); + } + + for (size_t i = 0; i < 64; i += 4) { + std::println("{:02x} {:02x} {:02x} {:02x}", + m_program[i], + m_program[i + 1], + m_program[i + 2], + m_program[i + 3]); + } + return std::move(m_program); } @@ -187,8 +216,8 @@ static const auto mnemonic_map { "nop", M::nop }, { "hlt", M::hlt }, { "jmp", M::jmp }, { "jnz", M::jnz }, { "mov", M::mov }, { "cmp", M::cmp }, - { "or_", M::or_ }, { "and_", M::and_ }, - { "xor_", M::xor_ }, { "shl", M::shl }, + { "or", M::or_ }, { "and", M::and_ }, + { "xor", M::xor_ }, { "shl", M::shl }, { "rshl", M::rshl }, { "shr", M::shr }, { "rshr", M::rshr }, { "add", M::add }, { "sub", M::sub }, { "rsub", M::rsub }, @@ -204,13 +233,13 @@ void Assembler::assemble_line(const Line& line) { auto loc = line.loc; - constexpr auto Reg = EOT::Reg; - constexpr auto Imm = EOT::Imm; - constexpr auto Str = EOT::Str; - constexpr auto MemByteImm = EOT::MemByteImm; - constexpr auto MemByteReg = EOT::MemByteReg; - constexpr auto MemWordImm = EOT::MemWordImm; - constexpr auto MemWordReg = EOT::MemWordReg; + [[maybe_unused]] constexpr auto Reg = EOT::Reg; + [[maybe_unused]] constexpr auto Imm = EOT::Imm; + [[maybe_unused]] constexpr auto Str = EOT::Str; + [[maybe_unused]] constexpr auto MemByteImm = EOT::MemByteImm; + [[maybe_unused]] constexpr auto MemByteReg = EOT::MemByteReg; + [[maybe_unused]] constexpr auto MemWordImm = EOT::MemWordImm; + [[maybe_unused]] constexpr auto MemWordReg = EOT::MemWordReg; auto& l = m_builder; @@ -269,6 +298,9 @@ void Assembler::assemble_line(const Line& line) if (arg_count_wrong(line, 1)) return; auto op = eval_operand(*line.args[0]); + if (not op) + return; + if (op->is_reg()) { l.jmp_reg(op->as_reg()); } else if (op->is_imm()) { @@ -279,10 +311,14 @@ void Assembler::assemble_line(const Line& line) break; } case M::jnz: { - if (arg_count_wrong(line, 1)) + if (arg_count_wrong(line, 2)) return; auto op1 = eval_operand(*line.args[0]); auto op2 = eval_operand(*line.args[1]); + + if (not op1 or op2) + return; + if (not op1->is_reg()) operation_not_supported(); if (op2->is_reg()) { @@ -300,6 +336,9 @@ void Assembler::assemble_line(const Line& line) auto dst = eval_operand(*line.args[0]); auto src = eval_operand(*line.args[1]); + if (not dst or src) + return; + auto dst_ty = dst->ty; auto src_ty = src->ty; @@ -338,14 +377,19 @@ void Assembler::assemble_line(const Line& line) auto op1 = eval_operand(*line.args[0]); auto op2 = eval_operand(*line.args[1]); + if (not op1 or not op2) + return; + + if (not op1->is_reg()) + operation_not_supported(); + if (op2->is_reg()) { - + l.cmp_reg(op1->as_reg(), op2->as_reg()); } else if (op2->is_imm()) { - + l.cmp_imm(op1->as_reg(), op2->as_imm()); } else { operation_not_supported(); } - break; } case M::or_: @@ -357,13 +401,168 @@ void Assembler::assemble_line(const Line& line) case M::rshr: case M::add: case M::sub: - case M::rsub: - case M::reti: - case M::lvcd: - case M::lkbd: - case M::dskr: - case M::dskw: + case M::rsub: { + if (arg_count_wrong(line, 3)) + return; + auto dst = eval_operand(*line.args[0]); + auto op1 = eval_operand(*line.args[1]); + auto op2 = eval_operand(*line.args[2]); + + if (not dst or not op1 or not op2) + return; + + if (not dst->is_reg()) + operation_not_supported(); + if (not op1->is_reg()) + operation_not_supported(); + + if (op2->is_reg()) { + switch (m) { + case Mnemonic::or_: + l.or_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::and_: + l.and_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::xor_: + l.xor_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::shl: + l.shl_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::rshl: + l.rshl_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::shr: + l.shr_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::rshr: + l.rshr_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::add: + l.add_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::sub: + l.sub_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + case Mnemonic::rsub: + l.rsub_reg(dst->as_reg(), op1->as_reg(), op2->as_reg()); + break; + default: + assert(false && "unhandled"); + } + } else if (op2->is_imm()) { + switch (m) { + case Mnemonic::or_: + l.or_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::and_: + l.and_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::xor_: + l.xor_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::shl: + l.shl_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::rshl: + l.rshl_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::shr: + l.shr_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::rshr: + l.rshr_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::add: + l.add_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::sub: + l.sub_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + case Mnemonic::rsub: + l.rsub_imm(dst->as_reg(), op1->as_reg(), op2->as_imm()); + break; + default: + assert(false && "unhandled"); + } + } else { + operation_not_supported(); + } break; + } + case M::reti: { + if (arg_count_wrong(line, 0)) + return; + l.reti(); + break; + } + case M::lvcd: { + if (arg_count_wrong(line, 1)) + return; + + auto op1 = eval_operand(*line.args[0]); + if (not op1) + return; + + if (op1->is_reg()) { + l.lvcd_reg(op1->as_reg()); + } else if (op1->is_imm()) { + l.lvcd_imm(op1->as_imm()); + } else { + operation_not_supported(); + } + break; + } + case M::lkbd: { + if (arg_count_wrong(line, 1)) + return; + + auto op1 = eval_operand(*line.args[0]); + if (not op1) + return; + + if (op1->is_reg()) { + l.lkbd_reg(op1->as_reg()); + } else if (op1->is_imm()) { + l.lkbd_imm(op1->as_imm()); + } else { + operation_not_supported(); + } + break; + } + case M::dskr: { + if (arg_count_wrong(line, 2)) + return; + + auto dst = eval_operand(*line.args[0]); + auto op1 = eval_operand(*line.args[1]); + + if (not dst or not op1) + return; + + if (not dst->is_reg() or not op1->is_reg()) + operation_not_supported(); + + l.dskr(dst->as_reg(), op1->as_reg()); + break; + } + case M::dskw: { + if (arg_count_wrong(line, 2)) + return; + + auto dst = eval_operand(*line.args[0]); + auto op1 = eval_operand(*line.args[1]); + + if (not dst or not op1) + return; + + if (not dst->is_reg() or not op1->is_reg()) + operation_not_supported(); + + l.dskw(dst->as_reg(), op1->as_reg()); + break; + } } } @@ -403,8 +602,9 @@ 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: - break; + case Expr::Ty::Sub: { + return eval_operand_to_imm(expr); + } } assert(false && "unexhaustive"); } @@ -535,6 +735,7 @@ auto Assembler::eval_operand_to_imm(const Expr& expr) } case Expr::Ty::Reg: error(loc, "registers cannot be part of an expression"); + return nullptr; case Expr::Ty::Int: return std::make_unique( expr.loc, EOT::Imm, static_cast(expr.as_int())); @@ -574,10 +775,9 @@ auto Assembler::eval_operand_to_imm(const Expr& expr) return std::make_unique(loc, EOT::Imm, binary_op(expr.ty, left->as_imm(), right->as_imm())); - break; } } - assert(false && "unexhaustive"); + assert(false); } auto Assembler::binary_op(Expr::Ty exprTy, uint16_t left, uint16_t right) @@ -608,7 +808,7 @@ auto Assembler::binary_op(Expr::Ty exprTy, uint16_t left, uint16_t right) // return (uint16_t)((int16_t)left % (int16_t)right); default: - assert(false && "unexhaustive"); + assert(false && "unhandled"); } } @@ -616,15 +816,32 @@ bool Assembler::arg_count_wrong(const Line& ins, size_t count) { if (ins.args.size() != count) { error(ins.loc, std::format("expected {} operands", count)); - return false; + return true; } - return true; + return false; } void Assembler::error(Loc loc, std::string_view message) { m_failed = true; - loc.print_error(m_text, message); + loc.print_error(m_text, message, m_filename); +} + +void Parser::eat_whitespace() +{ + while (eat('\n')) { } +} + +void Parser::try_recover() +{ + while (not test(TT::Eof) and not test('\n')) { + step(); + } +} + +auto Parser::is_done() const -> bool +{ + return test(TT::Eof); } auto Parser::next_is_const() const -> bool @@ -636,6 +853,7 @@ auto Parser::parse_const() -> std::unique_ptr { assert(test(TT::KwConst)); auto loc = m_tok.loc; + step(); if (!test(TT::Ident)) { error(current_loc(), "expected identifier"); return nullptr; @@ -643,6 +861,8 @@ auto Parser::parse_const() -> std::unique_ptr auto ident = m_tok.text; step(); auto expr = parse_expr(); + if (not expr) + return nullptr; return std::make_unique(loc, ident, std::move(expr)); } @@ -655,6 +875,7 @@ auto Parser::parse_align() -> std::unique_ptr { assert(test(TT::KwAlign)); auto loc = m_tok.loc; + step(); if (!test(TT::Ident)) { error(current_loc(), "expected identifier"); return nullptr; @@ -662,6 +883,8 @@ auto Parser::parse_align() -> std::unique_ptr auto ident = m_tok.text; step(); auto expr = parse_expr(); + if (not expr) + return nullptr; return std::make_unique(loc, ident, std::move(expr)); } @@ -674,15 +897,15 @@ auto Parser::parse_line() -> std::unique_ptr auto ident = std::string_view(); while (true) { - while (eat('\n')) { } + eat_whitespace(); - if (test(TT::Ident) or eat('.')) { + if (test(TT::Ident) or test('.')) { bool is_local = false; if (eat('.')) { is_local = true; } if (not test(TT::Ident)) { - error(current_loc(), "expected ident"); + error(current_loc(), "expected identifier"); return nullptr; } ident = m_tok.text; @@ -702,29 +925,39 @@ auto Parser::parse_line() -> std::unique_ptr labels->push_back(Label { loc, ident, is_local }); continue; } else { + error(current_loc(), "expected identifier"); return nullptr; } } auto args = Line::Args(); - auto first = true; - while (not test(TT::Eof) and not eat('\n')) { - if (not first and not eat(',')) { - error(current_loc(), "expected ','"); - return nullptr; - } - first = false; + if (not test(TT::Eof) and not test('\n')) { auto arg = parse_expr(); if (not arg) { return nullptr; } + args.push_back(std::move(arg)); + while (not test(TT::Eof) and not test('\n')) { + + if (not eat(',')) { + error(current_loc(), "expected ','"); + return nullptr; + } + + auto arg = parse_expr(); + if (not arg) { + return nullptr; + } + args.push_back(std::move(arg)); + } } if (not test(TT::Eof) and not eat('\n')) { error(current_loc(), "expected line ending"); + return nullptr; } return std::make_unique( @@ -790,17 +1023,17 @@ auto Parser::parse_prefix() -> std::unique_ptr namespace { -const auto reg_idents = std::unordered_map { - { "R0", vc5::Reg::R0 }, - { "R1", vc5::Reg::R1 }, - { "R2", vc5::Reg::R2 }, - { "R3", vc5::Reg::R3 }, - { "R4", vc5::Reg::R4 }, - { "R5", vc5::Reg::R5 }, - { "Rbp", vc5::Reg::Rbp }, - { "Rsp", vc5::Reg::Rsp }, - { "Rfl", vc5::Reg::Rfl }, - { "Rip", vc5::Reg::Rip }, +const auto reg_idents = std::unordered_map { + { TT::KwR0, vc5::Reg::R0 }, + { TT::KwR1, vc5::Reg::R1 }, + { TT::KwR2, vc5::Reg::R2 }, + { TT::KwR3, vc5::Reg::R3 }, + { TT::KwR4, vc5::Reg::R4 }, + { TT::KwR5, vc5::Reg::R5 }, + { TT::KwRbp, vc5::Reg::Rbp }, + { TT::KwRsp, vc5::Reg::Rsp }, + { TT::KwRfl, vc5::Reg::Rfl }, + { TT::KwRip, vc5::Reg::Rip }, }; } @@ -811,12 +1044,11 @@ auto Parser::parse_operand() -> std::unique_ptr if (test(TT::Ident)) { auto ident = m_tok.text; step(); - if (reg_idents.contains(ident)) { - return std::make_unique( - loc, Expr::Ty::Reg, reg_idents.at(ident)); - } else { - return std::make_unique(loc, Expr::Ty::Ident, ident); - } + return std::make_unique(loc, Expr::Ty::Ident, ident); + } else if (reg_idents.contains(m_tok.ty)) { + auto ty = m_tok.ty; + step(); + return std::make_unique(loc, Expr::Ty::Reg, reg_idents.at(ty)); } else if (eat('.')) { if (!test(TT::Ident)) { error(current_loc(), "expected identifier"); @@ -824,6 +1056,7 @@ auto Parser::parse_operand() -> std::unique_ptr } auto value = std::string_view(m_tok.text.data() - 1, m_tok.text.size() + 1); + step(); return std::make_unique(loc, Expr::Ty::SubLabel, value); } else if (test(TT::Int)) { @@ -862,6 +1095,8 @@ auto Parser::parse_operand() -> std::unique_ptr return std::make_unique(loc, Expr::Ty::Str, std::move(value)); } else if (eat('(')) { auto expr = parse_expr(); + if (not expr) + return nullptr; if (not eat(')')) { error(current_loc(), "expected ')'"); return nullptr; @@ -869,30 +1104,34 @@ auto Parser::parse_operand() -> std::unique_ptr return expr; } else if (eat('[')) { auto expr = parse_expr(); + if (not expr) + return nullptr; if (not eat(']')) { error(current_loc(), "expected ']'"); return nullptr; } return std::make_unique(loc, Expr::Ty::Mem, std::move(expr)); - } else if (test(TT::Ident) and m_tok.text == "byte") { - step(); + } else if (eat(TT::KwByte)) { if (not eat('[')) { error(current_loc(), "expected '['"); return nullptr; } auto expr = parse_expr(); + if (not expr) + return nullptr; if (not eat(']')) { error(current_loc(), "expected ']'"); return nullptr; } return std::make_unique(loc, Expr::Ty::MemByte, std::move(expr)); - } else if (test(TT::Ident) and m_tok.text == "word") { - step(); + } else if (eat(TT::KwWord)) { if (not eat('[')) { error(current_loc(), "expected '['"); return nullptr; } auto expr = parse_expr(); + if (not expr) + return nullptr; if (not eat(']')) { error(current_loc(), "expected ']'"); return nullptr; @@ -941,5 +1180,5 @@ auto Parser::current_loc() const -> Loc void Parser::error(Loc loc, std::string_view message) { m_error_occured = true; - loc.print_error(m_text, message); + loc.print_error(m_text, message, m_filename); } diff --git a/src/assembler.hpp b/src/assembler.hpp index 855b327..359ff7a 100644 --- a/src/assembler.hpp +++ b/src/assembler.hpp @@ -105,13 +105,18 @@ namespace asmer { class Parser { public: - explicit Parser(std::string_view text) - : m_text(text) - , m_lexer(text, Scanner::Kind::Assembler) + explicit Parser(std::string_view filename, std::string_view text) + : m_filename(filename) + , m_text(text) + , m_lexer(m_filename, text, Scanner::Kind::Assembler) , m_tok(m_lexer.next()) { } + void eat_whitespace(); + void try_recover(); + auto is_done() const -> bool; + auto next_is_const() const -> bool; auto parse_const() -> std::unique_ptr; @@ -141,6 +146,7 @@ namespace asmer { void error(Loc loc, std::string_view message); + std::string_view m_filename; std::string_view m_text; Scanner m_lexer; Tok m_tok; @@ -193,8 +199,9 @@ namespace asmer { class Assembler { public: - explicit Assembler(std::string_view text) - : m_text(text) + explicit Assembler(std::string_view filename, std::string_view text) + : m_filename(filename) + , m_text(text) , m_program(65536) , m_builder(m_program.data()) { @@ -226,6 +233,7 @@ namespace asmer { void error(Loc loc, std::string_view message); + std::string_view m_filename; std::string_view m_text; std::vector m_program; Builder m_builder; diff --git a/src/main.cpp b/src/main.cpp index 13abd9a..4e3ec46 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "io_device.hpp" #include "vm.hpp" #include +#include #include #include @@ -11,14 +12,31 @@ using namespace std::chrono_literals; using namespace vc5; using namespace vc5::regs; -int main() +static void make_program(uint8_t* data); + +int main(int argc, char** argv) { auto device = IoDevice::create().value(); device->set_title("vc5"); - auto disk = MemoryDisk(128); + if (argc < 2) { + auto disk = MemoryDisk(128); - auto l = tools::Builder(disk.data()); + make_program(disk.data()); + + auto vm = VM(*device, disk); + vm.run(); + } else { + auto disk = FileDisk(argv[1]); + + auto vm = VM(*device, disk); + vm.run(); + } +} + +void make_program(uint8_t* data) +{ + auto l = tools::Builder(data); l.mov_imm(rsp, 0x1000); l.mov_reg(rbp, rsp); @@ -77,9 +95,6 @@ int main() l.push(key_press_incr); l.set_ip(to_set_key_press_int_leave + 2); l.push(key_press_int_leave); - - auto vm = VM(*device, disk); - vm.run(); } extern "C" const char* __asan_default_options(void) diff --git a/src/scanner.cpp b/src/scanner.cpp index 4284f37..660512a 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace vc5::tools; using namespace std::literals; @@ -130,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); @@ -140,9 +141,21 @@ auto Scanner::next() -> Tok return next(); } -const auto Scanner::keywords_kind_assembler = KeywordMap { +const Scanner::KeywordMap Scanner::keywords_kind_assembler = { { "const", TT::KwConst }, { "align", TT::KwAlign }, + { "word", TT::KwWord }, + { "byte", TT::KwByte }, + { "r0", TT::KwR0 }, + { "r1", TT::KwR1 }, + { "r2", TT::KwR2 }, + { "r3", TT::KwR3 }, + { "r4", TT::KwR4 }, + { "r5", TT::KwR5 }, + { "rbp", TT::KwRbp }, + { "rsp", TT::KwRsp }, + { "rfl", TT::KwRfl }, + { "rip", TT::KwRip }, }; void Scanner::step() @@ -209,19 +222,22 @@ auto Scanner::is_assembler() const -> bool void Scanner::error(Loc loc, std::string_view message) { m_error_occured = true; - loc.print_error(m_text, message); + loc.print_error(m_text, message, m_filename); } -void Loc::print_error(std::string_view text, std::string_view message) const +void Loc::print_error(std::string_view text, + std::string_view message, + std::string_view file) const { constexpr auto type = "error"sv; constexpr auto clear = "\x1b[0m"sv; - constexpr auto bold_red = "\x1b[1;31m"sv; - constexpr auto bold_white = "\x1b[1;37m"sv; + constexpr auto bold_red = "\x1b[1;91m"sv; + constexpr auto bold_white = "\x1b[1;97m"sv; constexpr auto cyan = "\x1b[0;36m"sv; - constexpr auto gray = "\x1b[0;37m"sv; - constexpr auto light_gray = "\x1b[1;30m"sv; + constexpr auto gray = "\x1b[0;90m"sv; + constexpr auto light_gray = "\x1b[0;37m"sv; + constexpr auto green = "\x1b[0;32m"sv; auto start = text.find_last_of('\n', idx) + 1; auto end = text.find_first_of('\n', idx); @@ -240,13 +256,13 @@ void Loc::print_error(std::string_view text, std::string_view message) const " {8: <{9}}{10}|\n" " {11}{12}{13}|{14}{15}\n" " {16: <{17}}" - "{18}|${19: <{20}}{21}^ {22}{23}{24}", + "{18}|{19: <{20}}{21}^ {22}{23}{24}\n", bold_red, type, bold_white, message, cyan, - "", + file, line, col, "", @@ -255,7 +271,7 @@ void Loc::print_error(std::string_view text, std::string_view message) const light_gray, linenr_str, gray, - light_gray, + green, line_text, "", linenr_str.size(), diff --git a/src/scanner.hpp b/src/scanner.hpp index 36d9255..01b7ea9 100644 --- a/src/scanner.hpp +++ b/src/scanner.hpp @@ -11,7 +11,9 @@ struct Loc { int line; int col; - void print_error(std::string_view text, std::string_view message) const; + void print_error(std::string_view text, + std::string_view message, + std::string_view file = std::string_view("")) const; }; struct Tok { @@ -25,12 +27,27 @@ struct Tok { Str, KwConst, KwAlign, + KwWord, Newline = '\n', + KwByte, + KwR0, + KwR1, + KwR2, + KwR3, + KwR4, + KwR5, + KwRbp, + KwRsp, + KwRfl, + KwRip, + LtLt, + GtGt, LParen = '(', RParen = ')', LBracket = '[', RBracket = ']', Comma = ',', + Dot = '.', Colon = ':', Pipe = '|', Hat = '^', @@ -40,8 +57,6 @@ struct Tok { Exclam = '!', Lt = '<', Gt = '>', - LtLt, - GtGt, }; std::string_view text; @@ -53,8 +68,10 @@ class Scanner { public: enum class Kind { Assembler }; - explicit Scanner(std::string_view text, Kind kind) - : m_text(text) + explicit Scanner( + std::string_view filename, std::string_view text, Kind kind) + : m_filename(filename) + , m_text(text) , m_keywords(&keywords_kind_assembler) , m_kind(kind) { @@ -85,6 +102,7 @@ private: using KeywordMap = std::unordered_map; static const KeywordMap keywords_kind_assembler; + std::string_view m_filename; std::string_view m_text; const KeywordMap* m_keywords; size_t m_idx = 0; diff --git a/vim/ftdetect/vc5asm.vim b/vim/ftdetect/vc5asm.vim new file mode 100644 index 0000000..0995d06 --- /dev/null +++ b/vim/ftdetect/vc5asm.vim @@ -0,0 +1,2 @@ +autocmd BufNewFile,BufRead *.vc5asm setfiletype vc5asm + diff --git a/vim/ftplugin/vc5asm.vim b/vim/ftplugin/vc5asm.vim new file mode 100644 index 0000000..a5299dd --- /dev/null +++ b/vim/ftplugin/vc5asm.vim @@ -0,0 +1,2 @@ +setlocal commentstring=;%s + diff --git a/vim/syntax/vc5asm.vim b/vim/syntax/vc5asm.vim new file mode 100644 index 0000000..9cbde2b --- /dev/null +++ b/vim/syntax/vc5asm.vim @@ -0,0 +1,37 @@ +" Vim syntax file +" Language: vc5asm +" Maintainer: SFJ +" Latest Revision: 1 January 1984 + +if exists("b:current_syntax") + finish +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 reti lkbd lvcd dskr dskw +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 '&' + + +syn match Number '0' +syn match Number '[1-9][0-9]*' +syn match Number '0b[01]*' +syn match Number '0x[0-9a-fA-F]*' + +syn region String start=+"+ skip=+\\\\\|\\"+ end=+"+ + +syn match Comment ";.*$" + +syn match Identifier '[a-zA-Z_]\w*' + +let b:current_syntax = "vc5asm" +