assembler nearly works

This commit is contained in:
sfja 2026-01-22 23:59:56 +01:00
parent 8cfcc54e12
commit 680e952d04
11 changed files with 584 additions and 95 deletions

View File

@ -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)

87
programs/boot.vc5asm Normal file
View File

@ -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

58
src/asm_main.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "assembler.hpp"
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <print>
#include <string_view>
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 <input file> -o <output file>");
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;
}
}

View File

@ -11,6 +11,7 @@
#include <fstream>
#include <ios>
#include <memory>
#include <print>
#include <string_view>
#include <unordered_map>
#include <utility>
@ -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::vector<uint8_t>, std::string>
{
auto parser = Parser(m_text);
auto parser = Parser(m_filename, m_text);
auto lines = std::vector<std::unique_ptr<Line>>();
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,11 +144,15 @@ auto Assembler::assemble_file()
stmt->loc, nullptr, "db", std::move(ops)));
} else {
auto line = parser.parse_line();
if (not line) {
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<EvaledOperand>
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<EO>(
expr.loc, EOT::Imm, static_cast<uint16_t>(expr.as_int()));
@ -574,10 +775,9 @@ auto Assembler::eval_operand_to_imm(const Expr& expr)
return std::make_unique<EO>(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 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<Const>
{
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<Const>
auto ident = m_tok.text;
step();
auto expr = parse_expr();
if (not expr)
return nullptr;
return std::make_unique<Const>(loc, ident, std::move(expr));
}
@ -655,6 +875,7 @@ auto Parser::parse_align() -> std::unique_ptr<Align>
{
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<Align>
auto ident = m_tok.text;
step();
auto expr = parse_expr();
if (not expr)
return nullptr;
return std::make_unique<Align>(loc, ident, std::move(expr));
}
@ -674,15 +897,15 @@ auto Parser::parse_line() -> std::unique_ptr<Line>
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,19 +925,27 @@ auto Parser::parse_line() -> std::unique_ptr<Line>
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(',')) {
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;
}
first = false;
auto arg = parse_expr();
if (not arg) {
@ -722,9 +953,11 @@ auto Parser::parse_line() -> std::unique_ptr<Line>
}
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<Line>(
@ -790,17 +1023,17 @@ auto Parser::parse_prefix() -> std::unique_ptr<Expr>
namespace {
const auto reg_idents = std::unordered_map<std::string_view, vc5::Reg> {
{ "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, vc5::Reg> {
{ 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<Expr>
if (test(TT::Ident)) {
auto ident = m_tok.text;
step();
if (reg_idents.contains(ident)) {
return std::make_unique<Expr>(
loc, Expr::Ty::Reg, reg_idents.at(ident));
} else {
return std::make_unique<Expr>(loc, Expr::Ty::Ident, ident);
}
} else if (reg_idents.contains(m_tok.ty)) {
auto ty = m_tok.ty;
step();
return std::make_unique<Expr>(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<Expr>
}
auto value
= std::string_view(m_tok.text.data() - 1, m_tok.text.size() + 1);
step();
return std::make_unique<Expr>(loc, Expr::Ty::SubLabel, value);
} else if (test(TT::Int)) {
@ -862,6 +1095,8 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
return std::make_unique<Expr>(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<Expr>
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<Expr>(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<Expr>(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);
}

View File

@ -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<Const>;
@ -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<uint8_t> m_program;
Builder m_builder;

View File

@ -3,6 +3,7 @@
#include "io_device.hpp"
#include "vm.hpp"
#include <SDL2/SDL_main.h>
#include <cstdint>
#include <print>
#include <string>
@ -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");
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)

View File

@ -4,6 +4,7 @@
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
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<TT>(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>",
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(),

View File

@ -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("<file>")) 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<std::string_view, Tok::Ty>;
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;

2
vim/ftdetect/vc5asm.vim Normal file
View File

@ -0,0 +1,2 @@
autocmd BufNewFile,BufRead *.vc5asm setfiletype vc5asm

2
vim/ftplugin/vc5asm.vim Normal file
View File

@ -0,0 +1,2 @@
setlocal commentstring=;%s

37
vim/syntax/vc5asm.vim Normal file
View File

@ -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"