assembler nearly works
This commit is contained in:
parent
8cfcc54e12
commit
680e952d04
13
Makefile
13
Makefile
@ -10,11 +10,18 @@ CXXFLAGS += $(shell pkgconf sdl2 --libs)
|
|||||||
build_dir = build
|
build_dir = build
|
||||||
obj_dir = $(build_dir)/obj
|
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 $@)
|
@mkdir -p $(dir $@)
|
||||||
g++ $^ -o $@ $(CXXFLAGS) $(LDFLAGS)
|
g++ $^ -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
|
|||||||
87
programs/boot.vc5asm
Normal file
87
programs/boot.vc5asm
Normal 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
58
src/asm_main.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <print>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#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);
|
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();
|
auto result = assembler.assemble_file();
|
||||||
|
|
||||||
|
if (not result)
|
||||||
|
return std::unexpected(result.error());
|
||||||
|
|
||||||
auto& program = *result;
|
auto& program = *result;
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -72,15 +78,20 @@ auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path)
|
|||||||
auto Assembler::assemble_file()
|
auto Assembler::assemble_file()
|
||||||
-> std::expected<std::vector<uint8_t>, std::string>
|
-> 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>>();
|
auto lines = std::vector<std::unique_ptr<Line>>();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
parser.eat_whitespace();
|
||||||
|
if (parser.is_done()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (parser.next_is_const()) {
|
if (parser.next_is_const()) {
|
||||||
auto stmt = parser.parse_const();
|
auto stmt = parser.parse_const();
|
||||||
if (not stmt) {
|
if (not stmt) {
|
||||||
return std::unexpected("parsing failed");
|
parser.try_recover();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = eval_operand_to_imm(*stmt->expr);
|
auto value = eval_operand_to_imm(*stmt->expr);
|
||||||
@ -94,7 +105,8 @@ auto Assembler::assemble_file()
|
|||||||
} else if (parser.next_is_align()) {
|
} else if (parser.next_is_align()) {
|
||||||
auto stmt = parser.parse_align();
|
auto stmt = parser.parse_align();
|
||||||
if (not stmt) {
|
if (not stmt) {
|
||||||
return std::unexpected("parsing failed");
|
parser.try_recover();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = eval_operand_to_imm(*stmt->expr);
|
auto value = eval_operand_to_imm(*stmt->expr);
|
||||||
@ -132,11 +144,15 @@ auto Assembler::assemble_file()
|
|||||||
stmt->loc, nullptr, "db", std::move(ops)));
|
stmt->loc, nullptr, "db", std::move(ops)));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
auto line = parser.parse_line();
|
auto line = parser.parse_line();
|
||||||
if (not line) {
|
if (not line) {
|
||||||
|
if (parser.is_done()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parser.try_recover();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
lines.push_back(std::move(line));
|
lines.push_back(std::move(line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +164,19 @@ auto Assembler::assemble_file()
|
|||||||
m_builder.set_ip(0);
|
m_builder.set_ip(0);
|
||||||
m_second_pass = true;
|
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);
|
return std::move(m_program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,8 +216,8 @@ static const auto mnemonic_map
|
|||||||
{ "nop", M::nop }, { "hlt", M::hlt },
|
{ "nop", M::nop }, { "hlt", M::hlt },
|
||||||
{ "jmp", M::jmp }, { "jnz", M::jnz },
|
{ "jmp", M::jmp }, { "jnz", M::jnz },
|
||||||
{ "mov", M::mov }, { "cmp", M::cmp },
|
{ "mov", M::mov }, { "cmp", M::cmp },
|
||||||
{ "or_", M::or_ }, { "and_", M::and_ },
|
{ "or", M::or_ }, { "and", M::and_ },
|
||||||
{ "xor_", M::xor_ }, { "shl", M::shl },
|
{ "xor", M::xor_ }, { "shl", M::shl },
|
||||||
{ "rshl", M::rshl }, { "shr", M::shr },
|
{ "rshl", M::rshl }, { "shr", M::shr },
|
||||||
{ "rshr", M::rshr }, { "add", M::add },
|
{ "rshr", M::rshr }, { "add", M::add },
|
||||||
{ "sub", M::sub }, { "rsub", M::rsub },
|
{ "sub", M::sub }, { "rsub", M::rsub },
|
||||||
@ -204,13 +233,13 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
{
|
{
|
||||||
auto loc = line.loc;
|
auto loc = line.loc;
|
||||||
|
|
||||||
constexpr auto Reg = EOT::Reg;
|
[[maybe_unused]] constexpr auto Reg = EOT::Reg;
|
||||||
constexpr auto Imm = EOT::Imm;
|
[[maybe_unused]] constexpr auto Imm = EOT::Imm;
|
||||||
constexpr auto Str = EOT::Str;
|
[[maybe_unused]] constexpr auto Str = EOT::Str;
|
||||||
constexpr auto MemByteImm = EOT::MemByteImm;
|
[[maybe_unused]] constexpr auto MemByteImm = EOT::MemByteImm;
|
||||||
constexpr auto MemByteReg = EOT::MemByteReg;
|
[[maybe_unused]] constexpr auto MemByteReg = EOT::MemByteReg;
|
||||||
constexpr auto MemWordImm = EOT::MemWordImm;
|
[[maybe_unused]] constexpr auto MemWordImm = EOT::MemWordImm;
|
||||||
constexpr auto MemWordReg = EOT::MemWordReg;
|
[[maybe_unused]] constexpr auto MemWordReg = EOT::MemWordReg;
|
||||||
|
|
||||||
auto& l = m_builder;
|
auto& l = m_builder;
|
||||||
|
|
||||||
@ -269,6 +298,9 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
if (arg_count_wrong(line, 1))
|
if (arg_count_wrong(line, 1))
|
||||||
return;
|
return;
|
||||||
auto op = eval_operand(*line.args[0]);
|
auto op = eval_operand(*line.args[0]);
|
||||||
|
if (not op)
|
||||||
|
return;
|
||||||
|
|
||||||
if (op->is_reg()) {
|
if (op->is_reg()) {
|
||||||
l.jmp_reg(op->as_reg());
|
l.jmp_reg(op->as_reg());
|
||||||
} else if (op->is_imm()) {
|
} else if (op->is_imm()) {
|
||||||
@ -279,10 +311,14 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case M::jnz: {
|
case M::jnz: {
|
||||||
if (arg_count_wrong(line, 1))
|
if (arg_count_wrong(line, 2))
|
||||||
return;
|
return;
|
||||||
auto op1 = eval_operand(*line.args[0]);
|
auto op1 = eval_operand(*line.args[0]);
|
||||||
auto op2 = eval_operand(*line.args[1]);
|
auto op2 = eval_operand(*line.args[1]);
|
||||||
|
|
||||||
|
if (not op1 or op2)
|
||||||
|
return;
|
||||||
|
|
||||||
if (not op1->is_reg())
|
if (not op1->is_reg())
|
||||||
operation_not_supported();
|
operation_not_supported();
|
||||||
if (op2->is_reg()) {
|
if (op2->is_reg()) {
|
||||||
@ -300,6 +336,9 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
auto dst = eval_operand(*line.args[0]);
|
auto dst = eval_operand(*line.args[0]);
|
||||||
auto src = eval_operand(*line.args[1]);
|
auto src = eval_operand(*line.args[1]);
|
||||||
|
|
||||||
|
if (not dst or src)
|
||||||
|
return;
|
||||||
|
|
||||||
auto dst_ty = dst->ty;
|
auto dst_ty = dst->ty;
|
||||||
auto src_ty = src->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 op1 = eval_operand(*line.args[0]);
|
||||||
auto op2 = eval_operand(*line.args[1]);
|
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()) {
|
if (op2->is_reg()) {
|
||||||
|
l.cmp_reg(op1->as_reg(), op2->as_reg());
|
||||||
} else if (op2->is_imm()) {
|
} else if (op2->is_imm()) {
|
||||||
|
l.cmp_imm(op1->as_reg(), op2->as_imm());
|
||||||
} else {
|
} else {
|
||||||
operation_not_supported();
|
operation_not_supported();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case M::or_:
|
case M::or_:
|
||||||
@ -357,13 +401,168 @@ void Assembler::assemble_line(const Line& line)
|
|||||||
case M::rshr:
|
case M::rshr:
|
||||||
case M::add:
|
case M::add:
|
||||||
case M::sub:
|
case M::sub:
|
||||||
case M::rsub:
|
case M::rsub: {
|
||||||
case M::reti:
|
if (arg_count_wrong(line, 3))
|
||||||
case M::lvcd:
|
return;
|
||||||
case M::lkbd:
|
auto dst = eval_operand(*line.args[0]);
|
||||||
case M::dskr:
|
auto op1 = eval_operand(*line.args[1]);
|
||||||
case M::dskw:
|
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;
|
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::Shl:
|
||||||
case Expr::Ty::Shr:
|
case Expr::Ty::Shr:
|
||||||
case Expr::Ty::Add:
|
case Expr::Ty::Add:
|
||||||
case Expr::Ty::Sub:
|
case Expr::Ty::Sub: {
|
||||||
break;
|
return eval_operand_to_imm(expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert(false && "unexhaustive");
|
assert(false && "unexhaustive");
|
||||||
}
|
}
|
||||||
@ -535,6 +735,7 @@ auto Assembler::eval_operand_to_imm(const Expr& expr)
|
|||||||
}
|
}
|
||||||
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");
|
||||||
|
return nullptr;
|
||||||
case Expr::Ty::Int:
|
case Expr::Ty::Int:
|
||||||
return std::make_unique<EO>(
|
return std::make_unique<EO>(
|
||||||
expr.loc, EOT::Imm, static_cast<uint16_t>(expr.as_int()));
|
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,
|
return std::make_unique<EO>(loc,
|
||||||
EOT::Imm,
|
EOT::Imm,
|
||||||
binary_op(expr.ty, left->as_imm(), right->as_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)
|
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);
|
// return (uint16_t)((int16_t)left % (int16_t)right);
|
||||||
|
|
||||||
default:
|
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) {
|
if (ins.args.size() != count) {
|
||||||
error(ins.loc, std::format("expected {} operands", 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)
|
void Assembler::error(Loc loc, std::string_view message)
|
||||||
{
|
{
|
||||||
m_failed = true;
|
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
|
auto Parser::next_is_const() const -> bool
|
||||||
@ -636,6 +853,7 @@ auto Parser::parse_const() -> std::unique_ptr<Const>
|
|||||||
{
|
{
|
||||||
assert(test(TT::KwConst));
|
assert(test(TT::KwConst));
|
||||||
auto loc = m_tok.loc;
|
auto loc = m_tok.loc;
|
||||||
|
step();
|
||||||
if (!test(TT::Ident)) {
|
if (!test(TT::Ident)) {
|
||||||
error(current_loc(), "expected identifier");
|
error(current_loc(), "expected identifier");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -643,6 +861,8 @@ auto Parser::parse_const() -> std::unique_ptr<Const>
|
|||||||
auto ident = m_tok.text;
|
auto ident = m_tok.text;
|
||||||
step();
|
step();
|
||||||
auto expr = parse_expr();
|
auto expr = parse_expr();
|
||||||
|
if (not expr)
|
||||||
|
return nullptr;
|
||||||
return std::make_unique<Const>(loc, ident, std::move(expr));
|
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));
|
assert(test(TT::KwAlign));
|
||||||
auto loc = m_tok.loc;
|
auto loc = m_tok.loc;
|
||||||
|
step();
|
||||||
if (!test(TT::Ident)) {
|
if (!test(TT::Ident)) {
|
||||||
error(current_loc(), "expected identifier");
|
error(current_loc(), "expected identifier");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -662,6 +883,8 @@ auto Parser::parse_align() -> std::unique_ptr<Align>
|
|||||||
auto ident = m_tok.text;
|
auto ident = m_tok.text;
|
||||||
step();
|
step();
|
||||||
auto expr = parse_expr();
|
auto expr = parse_expr();
|
||||||
|
if (not expr)
|
||||||
|
return nullptr;
|
||||||
return std::make_unique<Align>(loc, ident, std::move(expr));
|
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();
|
auto ident = std::string_view();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
while (eat('\n')) { }
|
eat_whitespace();
|
||||||
|
|
||||||
if (test(TT::Ident) or eat('.')) {
|
if (test(TT::Ident) or test('.')) {
|
||||||
bool is_local = false;
|
bool is_local = false;
|
||||||
if (eat('.')) {
|
if (eat('.')) {
|
||||||
is_local = true;
|
is_local = true;
|
||||||
}
|
}
|
||||||
if (not test(TT::Ident)) {
|
if (not test(TT::Ident)) {
|
||||||
error(current_loc(), "expected ident");
|
error(current_loc(), "expected identifier");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ident = m_tok.text;
|
ident = m_tok.text;
|
||||||
@ -702,19 +925,27 @@ auto Parser::parse_line() -> std::unique_ptr<Line>
|
|||||||
labels->push_back(Label { loc, ident, is_local });
|
labels->push_back(Label { loc, ident, is_local });
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
error(current_loc(), "expected identifier");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto args = Line::Args();
|
auto args = Line::Args();
|
||||||
|
|
||||||
auto first = true;
|
if (not test(TT::Eof) and not test('\n')) {
|
||||||
while (not test(TT::Eof) and not eat('\n')) {
|
|
||||||
if (not first and not eat(',')) {
|
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 ','");
|
error(current_loc(), "expected ','");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
first = false;
|
|
||||||
|
|
||||||
auto arg = parse_expr();
|
auto arg = parse_expr();
|
||||||
if (not arg) {
|
if (not arg) {
|
||||||
@ -722,9 +953,11 @@ auto Parser::parse_line() -> std::unique_ptr<Line>
|
|||||||
}
|
}
|
||||||
args.push_back(std::move(arg));
|
args.push_back(std::move(arg));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (not test(TT::Eof) and not eat('\n')) {
|
if (not test(TT::Eof) and not eat('\n')) {
|
||||||
error(current_loc(), "expected line ending");
|
error(current_loc(), "expected line ending");
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<Line>(
|
return std::make_unique<Line>(
|
||||||
@ -790,17 +1023,17 @@ auto Parser::parse_prefix() -> std::unique_ptr<Expr>
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const auto reg_idents = std::unordered_map<std::string_view, vc5::Reg> {
|
const auto reg_idents = std::unordered_map<TT, vc5::Reg> {
|
||||||
{ "R0", vc5::Reg::R0 },
|
{ TT::KwR0, vc5::Reg::R0 },
|
||||||
{ "R1", vc5::Reg::R1 },
|
{ TT::KwR1, vc5::Reg::R1 },
|
||||||
{ "R2", vc5::Reg::R2 },
|
{ TT::KwR2, vc5::Reg::R2 },
|
||||||
{ "R3", vc5::Reg::R3 },
|
{ TT::KwR3, vc5::Reg::R3 },
|
||||||
{ "R4", vc5::Reg::R4 },
|
{ TT::KwR4, vc5::Reg::R4 },
|
||||||
{ "R5", vc5::Reg::R5 },
|
{ TT::KwR5, vc5::Reg::R5 },
|
||||||
{ "Rbp", vc5::Reg::Rbp },
|
{ TT::KwRbp, vc5::Reg::Rbp },
|
||||||
{ "Rsp", vc5::Reg::Rsp },
|
{ TT::KwRsp, vc5::Reg::Rsp },
|
||||||
{ "Rfl", vc5::Reg::Rfl },
|
{ TT::KwRfl, vc5::Reg::Rfl },
|
||||||
{ "Rip", vc5::Reg::Rip },
|
{ TT::KwRip, vc5::Reg::Rip },
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -811,12 +1044,11 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
|||||||
if (test(TT::Ident)) {
|
if (test(TT::Ident)) {
|
||||||
auto ident = m_tok.text;
|
auto ident = m_tok.text;
|
||||||
step();
|
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);
|
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('.')) {
|
} else if (eat('.')) {
|
||||||
if (!test(TT::Ident)) {
|
if (!test(TT::Ident)) {
|
||||||
error(current_loc(), "expected identifier");
|
error(current_loc(), "expected identifier");
|
||||||
@ -824,6 +1056,7 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
|||||||
}
|
}
|
||||||
auto value
|
auto value
|
||||||
= std::string_view(m_tok.text.data() - 1, m_tok.text.size() + 1);
|
= std::string_view(m_tok.text.data() - 1, m_tok.text.size() + 1);
|
||||||
|
step();
|
||||||
|
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::SubLabel, value);
|
return std::make_unique<Expr>(loc, Expr::Ty::SubLabel, value);
|
||||||
} else if (test(TT::Int)) {
|
} 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));
|
return std::make_unique<Expr>(loc, Expr::Ty::Str, std::move(value));
|
||||||
} else if (eat('(')) {
|
} else if (eat('(')) {
|
||||||
auto expr = parse_expr();
|
auto expr = parse_expr();
|
||||||
|
if (not expr)
|
||||||
|
return nullptr;
|
||||||
if (not eat(')')) {
|
if (not eat(')')) {
|
||||||
error(current_loc(), "expected ')'");
|
error(current_loc(), "expected ')'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -869,30 +1104,34 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
|||||||
return expr;
|
return expr;
|
||||||
} else if (eat('[')) {
|
} else if (eat('[')) {
|
||||||
auto expr = parse_expr();
|
auto expr = parse_expr();
|
||||||
|
if (not expr)
|
||||||
|
return nullptr;
|
||||||
if (not eat(']')) {
|
if (not eat(']')) {
|
||||||
error(current_loc(), "expected ']'");
|
error(current_loc(), "expected ']'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::Mem, std::move(expr));
|
return std::make_unique<Expr>(loc, Expr::Ty::Mem, std::move(expr));
|
||||||
} else if (test(TT::Ident) and m_tok.text == "byte") {
|
} else if (eat(TT::KwByte)) {
|
||||||
step();
|
|
||||||
if (not eat('[')) {
|
if (not eat('[')) {
|
||||||
error(current_loc(), "expected '['");
|
error(current_loc(), "expected '['");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto expr = parse_expr();
|
auto expr = parse_expr();
|
||||||
|
if (not expr)
|
||||||
|
return nullptr;
|
||||||
if (not eat(']')) {
|
if (not eat(']')) {
|
||||||
error(current_loc(), "expected ']'");
|
error(current_loc(), "expected ']'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::MemByte, std::move(expr));
|
return std::make_unique<Expr>(loc, Expr::Ty::MemByte, std::move(expr));
|
||||||
} else if (test(TT::Ident) and m_tok.text == "word") {
|
} else if (eat(TT::KwWord)) {
|
||||||
step();
|
|
||||||
if (not eat('[')) {
|
if (not eat('[')) {
|
||||||
error(current_loc(), "expected '['");
|
error(current_loc(), "expected '['");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto expr = parse_expr();
|
auto expr = parse_expr();
|
||||||
|
if (not expr)
|
||||||
|
return nullptr;
|
||||||
if (not eat(']')) {
|
if (not eat(']')) {
|
||||||
error(current_loc(), "expected ']'");
|
error(current_loc(), "expected ']'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -941,5 +1180,5 @@ auto Parser::current_loc() const -> Loc
|
|||||||
void Parser::error(Loc loc, std::string_view message)
|
void Parser::error(Loc loc, std::string_view message)
|
||||||
{
|
{
|
||||||
m_error_occured = true;
|
m_error_occured = true;
|
||||||
loc.print_error(m_text, message);
|
loc.print_error(m_text, message, m_filename);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,13 +105,18 @@ namespace asmer {
|
|||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
explicit Parser(std::string_view text)
|
explicit Parser(std::string_view filename, std::string_view text)
|
||||||
: m_text(text)
|
: m_filename(filename)
|
||||||
, m_lexer(text, Scanner::Kind::Assembler)
|
, m_text(text)
|
||||||
|
, m_lexer(m_filename, text, Scanner::Kind::Assembler)
|
||||||
, m_tok(m_lexer.next())
|
, m_tok(m_lexer.next())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void eat_whitespace();
|
||||||
|
void try_recover();
|
||||||
|
auto is_done() const -> bool;
|
||||||
|
|
||||||
auto next_is_const() const -> bool;
|
auto next_is_const() const -> bool;
|
||||||
auto parse_const() -> std::unique_ptr<Const>;
|
auto parse_const() -> std::unique_ptr<Const>;
|
||||||
|
|
||||||
@ -141,6 +146,7 @@ namespace asmer {
|
|||||||
|
|
||||||
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_text;
|
std::string_view m_text;
|
||||||
Scanner m_lexer;
|
Scanner m_lexer;
|
||||||
Tok m_tok;
|
Tok m_tok;
|
||||||
@ -193,8 +199,9 @@ namespace asmer {
|
|||||||
|
|
||||||
class Assembler {
|
class Assembler {
|
||||||
public:
|
public:
|
||||||
explicit Assembler(std::string_view text)
|
explicit Assembler(std::string_view filename, std::string_view text)
|
||||||
: m_text(text)
|
: m_filename(filename)
|
||||||
|
, m_text(text)
|
||||||
, m_program(65536)
|
, m_program(65536)
|
||||||
, m_builder(m_program.data())
|
, m_builder(m_program.data())
|
||||||
{
|
{
|
||||||
@ -226,6 +233,7 @@ namespace asmer {
|
|||||||
|
|
||||||
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_text;
|
std::string_view m_text;
|
||||||
std::vector<uint8_t> m_program;
|
std::vector<uint8_t> m_program;
|
||||||
Builder m_builder;
|
Builder m_builder;
|
||||||
|
|||||||
25
src/main.cpp
25
src/main.cpp
@ -3,6 +3,7 @@
|
|||||||
#include "io_device.hpp"
|
#include "io_device.hpp"
|
||||||
#include "vm.hpp"
|
#include "vm.hpp"
|
||||||
#include <SDL2/SDL_main.h>
|
#include <SDL2/SDL_main.h>
|
||||||
|
#include <cstdint>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -11,14 +12,31 @@ using namespace std::chrono_literals;
|
|||||||
using namespace vc5;
|
using namespace vc5;
|
||||||
using namespace vc5::regs;
|
using namespace vc5::regs;
|
||||||
|
|
||||||
int main()
|
static void make_program(uint8_t* data);
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
auto device = IoDevice::create().value();
|
auto device = IoDevice::create().value();
|
||||||
device->set_title("vc5");
|
device->set_title("vc5");
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
auto disk = MemoryDisk(128);
|
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_imm(rsp, 0x1000);
|
||||||
l.mov_reg(rbp, rsp);
|
l.mov_reg(rbp, rsp);
|
||||||
@ -77,9 +95,6 @@ int main()
|
|||||||
l.push(key_press_incr);
|
l.push(key_press_incr);
|
||||||
l.set_ip(to_set_key_press_int_leave + 2);
|
l.set_ip(to_set_key_press_int_leave + 2);
|
||||||
l.push(key_press_int_leave);
|
l.push(key_press_int_leave);
|
||||||
|
|
||||||
auto vm = VM(*device, disk);
|
|
||||||
vm.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char* __asan_default_options(void)
|
extern "C" const char* __asan_default_options(void)
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
using namespace vc5::tools;
|
using namespace vc5::tools;
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
@ -130,7 +131,7 @@ auto Scanner::next() -> Tok
|
|||||||
}
|
}
|
||||||
return tok(TT::Gt, loc);
|
return tok(TT::Gt, loc);
|
||||||
}
|
}
|
||||||
if (test_in("\n()[],:|^+-!")) {
|
if (test_in("\n()[].,:|^+-!")) {
|
||||||
auto ty = static_cast<TT>(current());
|
auto ty = static_cast<TT>(current());
|
||||||
step();
|
step();
|
||||||
return tok(ty, loc);
|
return tok(ty, loc);
|
||||||
@ -140,9 +141,21 @@ auto Scanner::next() -> Tok
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto Scanner::keywords_kind_assembler = KeywordMap {
|
const Scanner::KeywordMap Scanner::keywords_kind_assembler = {
|
||||||
{ "const", TT::KwConst },
|
{ "const", TT::KwConst },
|
||||||
{ "align", TT::KwAlign },
|
{ "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()
|
void Scanner::step()
|
||||||
@ -209,19 +222,22 @@ auto Scanner::is_assembler() const -> bool
|
|||||||
void Scanner::error(Loc loc, std::string_view message)
|
void Scanner::error(Loc loc, std::string_view message)
|
||||||
{
|
{
|
||||||
m_error_occured = true;
|
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 type = "error"sv;
|
||||||
|
|
||||||
constexpr auto clear = "\x1b[0m"sv;
|
constexpr auto clear = "\x1b[0m"sv;
|
||||||
constexpr auto bold_red = "\x1b[1;31m"sv;
|
constexpr auto bold_red = "\x1b[1;91m"sv;
|
||||||
constexpr auto bold_white = "\x1b[1;37m"sv;
|
constexpr auto bold_white = "\x1b[1;97m"sv;
|
||||||
constexpr auto cyan = "\x1b[0;36m"sv;
|
constexpr auto cyan = "\x1b[0;36m"sv;
|
||||||
constexpr auto gray = "\x1b[0;37m"sv;
|
constexpr auto gray = "\x1b[0;90m"sv;
|
||||||
constexpr auto light_gray = "\x1b[1;30m"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 start = text.find_last_of('\n', idx) + 1;
|
||||||
auto end = text.find_first_of('\n', idx);
|
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"
|
" {8: <{9}}{10}|\n"
|
||||||
" {11}{12}{13}|{14}{15}\n"
|
" {11}{12}{13}|{14}{15}\n"
|
||||||
" {16: <{17}}"
|
" {16: <{17}}"
|
||||||
"{18}|${19: <{20}}{21}^ {22}{23}{24}",
|
"{18}|{19: <{20}}{21}^ {22}{23}{24}\n",
|
||||||
bold_red,
|
bold_red,
|
||||||
type,
|
type,
|
||||||
bold_white,
|
bold_white,
|
||||||
message,
|
message,
|
||||||
cyan,
|
cyan,
|
||||||
"<file>",
|
file,
|
||||||
line,
|
line,
|
||||||
col,
|
col,
|
||||||
"",
|
"",
|
||||||
@ -255,7 +271,7 @@ void Loc::print_error(std::string_view text, std::string_view message) const
|
|||||||
light_gray,
|
light_gray,
|
||||||
linenr_str,
|
linenr_str,
|
||||||
gray,
|
gray,
|
||||||
light_gray,
|
green,
|
||||||
line_text,
|
line_text,
|
||||||
"",
|
"",
|
||||||
linenr_str.size(),
|
linenr_str.size(),
|
||||||
|
|||||||
@ -11,7 +11,9 @@ struct Loc {
|
|||||||
int line;
|
int line;
|
||||||
int col;
|
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 {
|
struct Tok {
|
||||||
@ -25,12 +27,27 @@ struct Tok {
|
|||||||
Str,
|
Str,
|
||||||
KwConst,
|
KwConst,
|
||||||
KwAlign,
|
KwAlign,
|
||||||
|
KwWord,
|
||||||
Newline = '\n',
|
Newline = '\n',
|
||||||
|
KwByte,
|
||||||
|
KwR0,
|
||||||
|
KwR1,
|
||||||
|
KwR2,
|
||||||
|
KwR3,
|
||||||
|
KwR4,
|
||||||
|
KwR5,
|
||||||
|
KwRbp,
|
||||||
|
KwRsp,
|
||||||
|
KwRfl,
|
||||||
|
KwRip,
|
||||||
|
LtLt,
|
||||||
|
GtGt,
|
||||||
LParen = '(',
|
LParen = '(',
|
||||||
RParen = ')',
|
RParen = ')',
|
||||||
LBracket = '[',
|
LBracket = '[',
|
||||||
RBracket = ']',
|
RBracket = ']',
|
||||||
Comma = ',',
|
Comma = ',',
|
||||||
|
Dot = '.',
|
||||||
Colon = ':',
|
Colon = ':',
|
||||||
Pipe = '|',
|
Pipe = '|',
|
||||||
Hat = '^',
|
Hat = '^',
|
||||||
@ -40,8 +57,6 @@ struct Tok {
|
|||||||
Exclam = '!',
|
Exclam = '!',
|
||||||
Lt = '<',
|
Lt = '<',
|
||||||
Gt = '>',
|
Gt = '>',
|
||||||
LtLt,
|
|
||||||
GtGt,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string_view text;
|
std::string_view text;
|
||||||
@ -53,8 +68,10 @@ class Scanner {
|
|||||||
public:
|
public:
|
||||||
enum class Kind { Assembler };
|
enum class Kind { Assembler };
|
||||||
|
|
||||||
explicit Scanner(std::string_view text, Kind kind)
|
explicit Scanner(
|
||||||
: m_text(text)
|
std::string_view filename, std::string_view text, Kind kind)
|
||||||
|
: m_filename(filename)
|
||||||
|
, m_text(text)
|
||||||
, m_keywords(&keywords_kind_assembler)
|
, m_keywords(&keywords_kind_assembler)
|
||||||
, m_kind(kind)
|
, m_kind(kind)
|
||||||
{
|
{
|
||||||
@ -85,6 +102,7 @@ private:
|
|||||||
using KeywordMap = std::unordered_map<std::string_view, Tok::Ty>;
|
using KeywordMap = std::unordered_map<std::string_view, Tok::Ty>;
|
||||||
static const KeywordMap keywords_kind_assembler;
|
static const KeywordMap keywords_kind_assembler;
|
||||||
|
|
||||||
|
std::string_view m_filename;
|
||||||
std::string_view m_text;
|
std::string_view m_text;
|
||||||
const KeywordMap* m_keywords;
|
const KeywordMap* m_keywords;
|
||||||
size_t m_idx = 0;
|
size_t m_idx = 0;
|
||||||
|
|||||||
2
vim/ftdetect/vc5asm.vim
Normal file
2
vim/ftdetect/vc5asm.vim
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
autocmd BufNewFile,BufRead *.vc5asm setfiletype vc5asm
|
||||||
|
|
||||||
2
vim/ftplugin/vc5asm.vim
Normal file
2
vim/ftplugin/vc5asm.vim
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
setlocal commentstring=;%s
|
||||||
|
|
||||||
37
vim/syntax/vc5asm.vim
Normal file
37
vim/syntax/vc5asm.vim
Normal 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"
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user