Compare commits
No commits in common. "c9d2fad4c84b9d5dbb9a86f0e8ba43648da5f7c9" and "d1c491c7c0f1ee7a0f3dd3a133d34ee962acf93a" have entirely different histories.
c9d2fad4c8
...
d1c491c7c0
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
MAKEFLAGS += -j16
|
MAKEFLAGS += -j16
|
||||||
|
|
||||||
CXXFLAGS := -std=c++23 -Wall -Wextra -pedantic-errors -fsanitize=address
|
CXXFLAGS := -std=c++23 -Wall -Wextra -pedantic-errors -fsanitize=address,undefined
|
||||||
LDFLAGS :=
|
LDFLAGS :=
|
||||||
|
|
||||||
CXXFLAGS += $(shell pkgconf sdl2 --cflags)
|
CXXFLAGS += $(shell pkgconf sdl2 --cflags)
|
||||||
|
|||||||
@ -13,7 +13,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using namespace vc5::tools;
|
using namespace vc5::tools;
|
||||||
@ -23,9 +22,6 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
using TT = Tok::Ty;
|
using TT = Tok::Ty;
|
||||||
|
|
||||||
using EO = EvaledOperand;
|
|
||||||
using EOT = EO::Ty;
|
|
||||||
|
|
||||||
auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path)
|
auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path)
|
||||||
-> std::expected<void, std::string>
|
-> std::expected<void, std::string>
|
||||||
{
|
{
|
||||||
@ -53,8 +49,6 @@ auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path)
|
|||||||
|
|
||||||
auto assembler = Assembler(text, builder);
|
auto assembler = Assembler(text, builder);
|
||||||
|
|
||||||
auto lines = std::vector<Line>();
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
auto ins = parser.parse_ins();
|
auto ins = parser.parse_ins();
|
||||||
if (not ins) {
|
if (not ins) {
|
||||||
@ -81,49 +75,24 @@ auto vc5::tools::assemble_file(fs::path input_path, fs::path output_path)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Assembler::assemble_file(std::string_view text)
|
void Assembler::assemble_ins(Ins& ins)
|
||||||
-> std::expected<std::vector<uint8_t>, std::string>
|
|
||||||
{
|
{
|
||||||
auto parser = Parser(text);
|
if (ins.labels) {
|
||||||
|
for (const auto& label : *ins.labels) {
|
||||||
auto program = std::vector<uint8_t>(65536);
|
if (label.is_local) {
|
||||||
auto builder = Builder(program.data());
|
m_local_labels[label.ident] = m_builder->ip();
|
||||||
|
} else {
|
||||||
auto assembler = Assembler(text, builder);
|
m_local_labels.clear();
|
||||||
|
m_global_labels[label.ident] = m_builder->ip();
|
||||||
auto lines = std::vector<Line>();
|
}
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto ins = parser.parse_ins();
|
|
||||||
if (not ins) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not parser.ok()) {
|
assemble_line(ins);
|
||||||
return std::unexpected("parsing failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return { std::move(program) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Assembler::assemble_define_labels(const std::vector<Label>& labels)
|
void Assembler::assemble_line(Ins& ins)
|
||||||
{
|
{
|
||||||
for (const auto& label : labels) {
|
|
||||||
if (label.is_local) {
|
|
||||||
m_local_labels[label.ident] = m_builder->ip();
|
|
||||||
} else {
|
|
||||||
m_local_labels.clear();
|
|
||||||
m_global_labels[label.ident] = m_builder->ip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Assembler::assemble_line(const Line& ins)
|
|
||||||
{
|
|
||||||
if (ins.ident == "db") {
|
|
||||||
for (const auto& arg : ins.args) { }
|
|
||||||
}
|
|
||||||
if (ins.ident == "mov") {
|
if (ins.ident == "mov") {
|
||||||
if (arg_count_wrong(ins, 2))
|
if (arg_count_wrong(ins, 2))
|
||||||
return;
|
return;
|
||||||
@ -133,88 +102,7 @@ void Assembler::assemble_line(const Line& ins)
|
|||||||
std::format("instruction '{}' not supported/implemented", ins.ident));
|
std::format("instruction '{}' not supported/implemented", ins.ident));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Assembler::eval_operand(const Expr& expr) -> std::unique_ptr<EvaledOperand>
|
bool Assembler::arg_count_wrong(Ins& ins, size_t count)
|
||||||
{
|
|
||||||
auto loc = expr.loc;
|
|
||||||
switch (expr.ty) {
|
|
||||||
case Expr::Ty::Ident:
|
|
||||||
case Expr::Ty::SubLabel:
|
|
||||||
case Expr::Ty::Reg:
|
|
||||||
return std::make_unique<EO>(loc, EOT::Reg, expr.as_reg());
|
|
||||||
case Expr::Ty::Int:
|
|
||||||
case Expr::Ty::Str:
|
|
||||||
return std::make_unique<EO>(loc, EOT::Str, expr.as_str());
|
|
||||||
case Expr::Ty::Mem:
|
|
||||||
case Expr::Ty::MemByte:
|
|
||||||
case Expr::Ty::MemWord:
|
|
||||||
case Expr::Ty::Negate:
|
|
||||||
case Expr::Ty::Not:
|
|
||||||
case Expr::Ty::Or:
|
|
||||||
case Expr::Ty::Xor:
|
|
||||||
case Expr::Ty::And:
|
|
||||||
case Expr::Ty::Add:
|
|
||||||
case Expr::Ty::Sub:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Assembler::eval_operand_mem(const Expr& expr)
|
|
||||||
-> std::unique_ptr<EvaledOperand>
|
|
||||||
{
|
|
||||||
switch (expr.ty) {
|
|
||||||
case Expr::Ty::Ident:
|
|
||||||
case Expr::Ty::SubLabel:
|
|
||||||
case Expr::Ty::Reg:
|
|
||||||
case Expr::Ty::Int:
|
|
||||||
case Expr::Ty::Str:
|
|
||||||
case Expr::Ty::Mem:
|
|
||||||
case Expr::Ty::MemByte:
|
|
||||||
case Expr::Ty::MemWord:
|
|
||||||
case Expr::Ty::Negate:
|
|
||||||
case Expr::Ty::Not:
|
|
||||||
case Expr::Ty::Or:
|
|
||||||
case Expr::Ty::Xor:
|
|
||||||
case Expr::Ty::And:
|
|
||||||
case Expr::Ty::Add:
|
|
||||||
case Expr::Ty::Sub:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Assembler::eval_operand_to_imm(const Expr& expr)
|
|
||||||
-> std::unique_ptr<EvaledOperand>
|
|
||||||
{
|
|
||||||
switch (expr.ty) {
|
|
||||||
case Expr::Ty::Ident:
|
|
||||||
case Expr::Ty::SubLabel:
|
|
||||||
case Expr::Ty::Reg:
|
|
||||||
error(expr.loc, "registers cannot be part of an expression");
|
|
||||||
case Expr::Ty::Int:
|
|
||||||
return std::make_unique<EO>(
|
|
||||||
expr.loc, EOT::Imm, static_cast<uint16_t>(expr.as_int()));
|
|
||||||
case Expr::Ty::Str:
|
|
||||||
error(expr.loc, "strings cannot be part of an expression");
|
|
||||||
return nullptr;
|
|
||||||
case Expr::Ty::Mem:
|
|
||||||
case Expr::Ty::MemByte:
|
|
||||||
case Expr::Ty::MemWord:
|
|
||||||
error(expr.loc, "indirections cannot be part of an expression");
|
|
||||||
return nullptr;
|
|
||||||
case Expr::Ty::Negate:
|
|
||||||
case Expr::Ty::Not:
|
|
||||||
case Expr::Ty::Or:
|
|
||||||
case Expr::Ty::Xor:
|
|
||||||
case Expr::Ty::And:
|
|
||||||
case Expr::Ty::Add:
|
|
||||||
case Expr::Ty::Sub:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
||||||
@ -229,11 +117,11 @@ void Assembler::error(Loc loc, std::string_view message)
|
|||||||
loc.print_error(m_text, message);
|
loc.print_error(m_text, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Parser::parse_ins() -> std::unique_ptr<Line>
|
auto Parser::parse_ins() -> std::unique_ptr<Ins>
|
||||||
{
|
{
|
||||||
auto loc = current_loc();
|
auto loc = current_loc();
|
||||||
|
|
||||||
auto labels = Line::Labels(nullptr);
|
auto labels = Ins::Labels(nullptr);
|
||||||
|
|
||||||
auto ident = std::string_view();
|
auto ident = std::string_view();
|
||||||
|
|
||||||
@ -270,7 +158,7 @@ auto Parser::parse_ins() -> std::unique_ptr<Line>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto args = Line::Args();
|
auto args = Ins::Args();
|
||||||
|
|
||||||
auto first = true;
|
auto first = true;
|
||||||
while (not test(TT::Eof) and not eat('\n')) {
|
while (not test(TT::Eof) and not eat('\n')) {
|
||||||
@ -291,7 +179,7 @@ auto Parser::parse_ins() -> std::unique_ptr<Line>
|
|||||||
error(current_loc(), "expected line ending");
|
error(current_loc(), "expected line ending");
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<Line>(
|
return std::make_unique<Ins>(
|
||||||
loc, std::move(labels), ident, std::move(args));
|
loc, std::move(labels), ident, std::move(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,9 +238,7 @@ auto Parser::parse_prefix() -> std::unique_ptr<Expr>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
static const auto reg_idents = std::unordered_map<std::string_view, vc5::Reg> {
|
||||||
|
|
||||||
const auto reg_idents = std::unordered_map<std::string_view, vc5::Reg> {
|
|
||||||
{ "R0", vc5::Reg::R0 },
|
{ "R0", vc5::Reg::R0 },
|
||||||
{ "R1", vc5::Reg::R1 },
|
{ "R1", vc5::Reg::R1 },
|
||||||
{ "R2", vc5::Reg::R2 },
|
{ "R2", vc5::Reg::R2 },
|
||||||
@ -365,8 +251,6 @@ const auto reg_idents = std::unordered_map<std::string_view, vc5::Reg> {
|
|||||||
{ "Rip", vc5::Reg::Rip },
|
{ "Rip", vc5::Reg::Rip },
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
||||||
{
|
{
|
||||||
auto loc = current_loc();
|
auto loc = current_loc();
|
||||||
@ -379,47 +263,21 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
|||||||
} else {
|
} else {
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::Ident, ident);
|
return std::make_unique<Expr>(loc, Expr::Ty::Ident, ident);
|
||||||
}
|
}
|
||||||
} else if (eat('.')) {
|
|
||||||
if (!test(TT::Ident)) {
|
|
||||||
error(current_loc(), "expected ')'");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto value
|
|
||||||
= std::string_view(m_tok.text.data() - 1, m_tok.text.size() + 1);
|
|
||||||
} else if (test(TT::Int)) {
|
} else if (test(TT::Int)) {
|
||||||
auto text = std::string(m_tok.text);
|
auto text = std::string(m_tok.text);
|
||||||
step();
|
|
||||||
auto value = std::strtol(text.c_str(), nullptr, 10);
|
auto value = std::strtol(text.c_str(), nullptr, 10);
|
||||||
return std::make_unique<Expr>(
|
return std::make_unique<Expr>(
|
||||||
loc, Expr::Ty::Int, static_cast<int>(value));
|
loc, Expr::Ty::Int, static_cast<int>(value));
|
||||||
} else if (test(TT::Bin)) {
|
} else if (test(TT::Bin)) {
|
||||||
auto text = std::string(m_tok.text);
|
auto text = std::string(m_tok.text);
|
||||||
step();
|
|
||||||
auto value = std::strtol(&text[2], nullptr, 2);
|
auto value = std::strtol(&text[2], nullptr, 2);
|
||||||
return std::make_unique<Expr>(
|
return std::make_unique<Expr>(
|
||||||
loc, Expr::Ty::Int, static_cast<int>(value));
|
loc, Expr::Ty::Int, static_cast<int>(value));
|
||||||
} else if (test(TT::Hex)) {
|
} else if (test(TT::Hex)) {
|
||||||
auto text = std::string(m_tok.text);
|
auto text = std::string(m_tok.text);
|
||||||
step();
|
|
||||||
auto value = std::strtol(&text[2], nullptr, 16);
|
auto value = std::strtol(&text[2], nullptr, 16);
|
||||||
return std::make_unique<Expr>(
|
return std::make_unique<Expr>(
|
||||||
loc, Expr::Ty::Int, static_cast<int>(value));
|
loc, Expr::Ty::Int, static_cast<int>(value));
|
||||||
} else if (test(TT::Char)) {
|
|
||||||
auto text = std::string(m_tok.text);
|
|
||||||
step();
|
|
||||||
|
|
||||||
int value = text.at(1) == '\\' ? unescape_escape_char(text.at(2))
|
|
||||||
: text.at(1);
|
|
||||||
|
|
||||||
return std::make_unique<Expr>(
|
|
||||||
loc, Expr::Ty::Int, static_cast<int>(value));
|
|
||||||
} else if (test(TT::Str)) {
|
|
||||||
auto text = std::string(m_tok.text);
|
|
||||||
step();
|
|
||||||
|
|
||||||
auto value = unescape_string(text.substr(1, text.size() - 2));
|
|
||||||
|
|
||||||
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 eat(')')) {
|
if (not eat(')')) {
|
||||||
@ -433,31 +291,8 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
|||||||
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>(
|
||||||
} else if (test(TT::Ident) and m_tok.text == "byte") {
|
loc, Expr::Ty::Indirection, std::move(expr));
|
||||||
step();
|
|
||||||
if (not eat('[')) {
|
|
||||||
error(current_loc(), "expected '['");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto expr = parse_expr();
|
|
||||||
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();
|
|
||||||
if (not eat('[')) {
|
|
||||||
error(current_loc(), "expected '['");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto expr = parse_expr();
|
|
||||||
if (not eat(']')) {
|
|
||||||
error(current_loc(), "expected ']'");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return std::make_unique<Expr>(loc, Expr::Ty::MemWord, std::move(expr));
|
|
||||||
} else {
|
} else {
|
||||||
error(current_loc(), "expected expression");
|
error(current_loc(), "expected expression");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -465,11 +300,6 @@ auto Parser::parse_operand() -> std::unique_ptr<Expr>
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Parser::eat(int ty) -> bool
|
auto Parser::eat(int ty) -> bool
|
||||||
{
|
|
||||||
return eat(static_cast<Tok::Ty>(ty));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Parser::eat(Tok::Ty ty) -> bool
|
|
||||||
{
|
{
|
||||||
if (test(ty)) {
|
if (test(ty)) {
|
||||||
step();
|
step();
|
||||||
@ -484,11 +314,6 @@ void Parser::step()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Parser::test(int ty) const -> bool
|
auto Parser::test(int ty) const -> bool
|
||||||
{
|
|
||||||
return m_tok.ty == static_cast<Tok::Ty>(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Parser::test(Tok::Ty ty) const -> bool
|
|
||||||
{
|
{
|
||||||
return m_tok.ty == ty;
|
return m_tok.ty == ty;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,13 +28,9 @@ namespace asmer {
|
|||||||
struct Expr {
|
struct Expr {
|
||||||
enum class Ty {
|
enum class Ty {
|
||||||
Ident,
|
Ident,
|
||||||
SubLabel,
|
|
||||||
Reg,
|
Reg,
|
||||||
Int,
|
Int,
|
||||||
Str,
|
Indirection,
|
||||||
Mem,
|
|
||||||
MemByte,
|
|
||||||
MemWord,
|
|
||||||
Negate,
|
Negate,
|
||||||
Not,
|
Not,
|
||||||
Or,
|
Or,
|
||||||
@ -46,40 +42,22 @@ namespace asmer {
|
|||||||
|
|
||||||
using Ptr = std::unique_ptr<Expr>;
|
using Ptr = std::unique_ptr<Expr>;
|
||||||
using Binary = std::tuple<Ptr, Ptr>;
|
using Binary = std::tuple<Ptr, Ptr>;
|
||||||
using Data = std::
|
using Data = std::variant<std::string_view, Reg, int, Ptr, Binary>;
|
||||||
variant<std::string_view, std::string, Reg, int, Ptr, Binary>;
|
|
||||||
|
|
||||||
auto as_ident() const -> const std::string_view&
|
// clang-format off
|
||||||
{
|
auto as_ident() -> std::string_view& { return std::get<std::string_view>(data); }
|
||||||
return std::get<std::string_view>(data);
|
auto as_reg() -> Reg& { return std::get<Reg>(data); }
|
||||||
}
|
auto as_int() -> int& { return std::get<int>(data); }
|
||||||
auto as_str() const -> const std::string&
|
auto as_unary() -> Ptr& { return std::get<Ptr>(data); }
|
||||||
{
|
auto as_binary() -> Binary& { return std::get<Binary>(data); }
|
||||||
return std::get<std::string>(data);
|
// clang-format on
|
||||||
}
|
|
||||||
auto as_reg() const -> Reg
|
|
||||||
{
|
|
||||||
return std::get<Reg>(data);
|
|
||||||
}
|
|
||||||
auto as_int() const -> int
|
|
||||||
{
|
|
||||||
return std::get<int>(data);
|
|
||||||
}
|
|
||||||
auto as_unary() const -> const Ptr&
|
|
||||||
{
|
|
||||||
return std::get<Ptr>(data);
|
|
||||||
}
|
|
||||||
auto as_binary() const -> const Binary&
|
|
||||||
{
|
|
||||||
return std::get<Binary>(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loc loc;
|
Loc loc;
|
||||||
Ty ty;
|
Ty ty;
|
||||||
Data data;
|
Data data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Line {
|
struct Ins {
|
||||||
using Labels = std::unique_ptr<std::vector<Label>>;
|
using Labels = std::unique_ptr<std::vector<Label>>;
|
||||||
using Args = std::vector<std::unique_ptr<Expr>>;
|
using Args = std::vector<std::unique_ptr<Expr>>;
|
||||||
|
|
||||||
@ -89,18 +67,6 @@ namespace asmer {
|
|||||||
Args args;
|
Args args;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Const {
|
|
||||||
Loc loc;
|
|
||||||
std::string_view ident;
|
|
||||||
std::unique_ptr<Expr> expr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Align {
|
|
||||||
Loc loc;
|
|
||||||
std::string_view ident;
|
|
||||||
std::unique_ptr<Expr> expr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
explicit Parser(std::string_view text)
|
explicit Parser(std::string_view text)
|
||||||
@ -110,7 +76,7 @@ namespace asmer {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parse_ins() -> std::unique_ptr<Line>;
|
auto parse_ins() -> std::unique_ptr<Ins>;
|
||||||
|
|
||||||
auto ok() const -> bool
|
auto ok() const -> bool
|
||||||
{
|
{
|
||||||
@ -124,11 +90,9 @@ namespace asmer {
|
|||||||
auto parse_operand() -> std::unique_ptr<Expr>;
|
auto parse_operand() -> std::unique_ptr<Expr>;
|
||||||
|
|
||||||
auto eat(int ty) -> bool;
|
auto eat(int ty) -> bool;
|
||||||
auto eat(Tok::Ty ty) -> bool;
|
|
||||||
void step();
|
void step();
|
||||||
|
|
||||||
auto test(int ty) const -> bool;
|
auto test(int ty) const -> bool;
|
||||||
auto test(Tok::Ty ty) const -> bool;
|
|
||||||
auto current_loc() const -> Loc;
|
auto current_loc() const -> Loc;
|
||||||
|
|
||||||
void error(Loc loc, std::string_view message);
|
void error(Loc loc, std::string_view message);
|
||||||
@ -139,31 +103,6 @@ namespace asmer {
|
|||||||
bool m_error_occured = false;
|
bool m_error_occured = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EvaledOperand {
|
|
||||||
enum class Ty {
|
|
||||||
Reg,
|
|
||||||
Imm,
|
|
||||||
Str,
|
|
||||||
MemByteImm,
|
|
||||||
MemByteReg,
|
|
||||||
MemWordImm,
|
|
||||||
MemWordReg,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto as_reg() -> Reg&
|
|
||||||
{
|
|
||||||
return std::get<Reg>(data);
|
|
||||||
}
|
|
||||||
auto as_imm() -> uint16_t&
|
|
||||||
{
|
|
||||||
return std::get<uint16_t>(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loc loc;
|
|
||||||
Ty ty;
|
|
||||||
std::variant<Reg, uint16_t, std::string> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Assembler {
|
class Assembler {
|
||||||
public:
|
public:
|
||||||
explicit Assembler(std::string_view text, Builder& builder)
|
explicit Assembler(std::string_view text, Builder& builder)
|
||||||
@ -172,11 +111,7 @@ namespace asmer {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
auto assemble_file(std::string_view text)
|
void assemble_ins(Ins& ins);
|
||||||
-> std::expected<std::vector<uint8_t>, std::string>;
|
|
||||||
|
|
||||||
void assemble_define_labels(const std::vector<Label>& labels);
|
|
||||||
void assemble_line(const Line& line);
|
|
||||||
|
|
||||||
auto ok() const -> bool
|
auto ok() const -> bool
|
||||||
{
|
{
|
||||||
@ -184,14 +119,10 @@ namespace asmer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto eval_operand(const Expr& expr) -> std::unique_ptr<EvaledOperand>;
|
void assemble_line(Ins& ins);
|
||||||
auto eval_operand_mem(const Expr& expr)
|
|
||||||
-> std::unique_ptr<EvaledOperand>;
|
|
||||||
auto eval_operand_to_imm(const Expr& expr)
|
|
||||||
-> std::unique_ptr<EvaledOperand>;
|
|
||||||
|
|
||||||
/// true means fail
|
/// true means fail
|
||||||
bool arg_count_wrong(const Line& ins, size_t count);
|
bool arg_count_wrong(Ins& ins, size_t count);
|
||||||
|
|
||||||
void error(Loc loc, std::string_view message);
|
void error(Loc loc, std::string_view message);
|
||||||
|
|
||||||
|
|||||||
@ -71,26 +71,13 @@ public:
|
|||||||
m_ip = ip;
|
m_ip = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(uint16_t v)
|
inline void push(uint16_t v)
|
||||||
{
|
{
|
||||||
m_data[m_ip] = v >> 8;
|
m_data[m_ip] = v >> 8;
|
||||||
m_data[m_ip + 1] = v & 0xff;
|
m_data[m_ip + 1] = v & 0xff;
|
||||||
m_ip += 2;
|
m_ip += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_byte(uint8_t v)
|
|
||||||
{
|
|
||||||
m_data[m_ip] = v;
|
|
||||||
m_ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void align_word()
|
|
||||||
{
|
|
||||||
if (m_ip & 1) {
|
|
||||||
m_ip += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void binary_reg(Reg dst, Reg op1, Reg op2, Op op);
|
void binary_reg(Reg dst, Reg op1, Reg op2, Op op);
|
||||||
void binary_imm(Reg dst, Reg op1, uint16_t op2, Op op);
|
void binary_imm(Reg dst, Reg op1, uint16_t op2, Op op);
|
||||||
|
|||||||
109
src/scanner.cpp
109
src/scanner.cpp
@ -1,20 +1,16 @@
|
|||||||
#include "scanner.hpp"
|
#include "scanner.hpp"
|
||||||
#include <cassert>
|
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
using namespace vc5::tools;
|
using namespace vc5::tools;
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
using TT = Tok::Ty;
|
|
||||||
|
|
||||||
auto Scanner::next() -> Tok
|
auto Scanner::next() -> Tok
|
||||||
{
|
{
|
||||||
auto loc = this->current_loc();
|
auto loc = this->current_loc();
|
||||||
if (done()) {
|
if (done()) {
|
||||||
return tok(TT::Eof, loc);
|
return tok(Tok::Eof, loc);
|
||||||
}
|
}
|
||||||
if (test_in(" \t\r")) {
|
if (test_in(" \t\r")) {
|
||||||
while (test_in(" \t\r")) {
|
while (test_in(" \t\r")) {
|
||||||
@ -34,24 +30,13 @@ auto Scanner::next() -> Tok
|
|||||||
or test_range('a', 'z')) {
|
or test_range('a', 'z')) {
|
||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
|
return tok(Tok::Ident, loc);
|
||||||
auto ident_tok = tok(TT::Ident, loc);
|
|
||||||
|
|
||||||
static const auto keywords = std::unordered_map<std::string_view, TT> {
|
|
||||||
{ "const", TT::KwConst },
|
|
||||||
};
|
|
||||||
|
|
||||||
if (keywords.contains(ident_tok.text)) {
|
|
||||||
return tok(keywords.at(ident_tok.text), loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ident_tok;
|
|
||||||
}
|
}
|
||||||
if (test_range('1', '9')) {
|
if (test_range('1', '9')) {
|
||||||
while (test_range('0', '9')) {
|
while (test_range('0', '9')) {
|
||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
return tok(TT::Int, loc);
|
return tok(Tok::Int, loc);
|
||||||
}
|
}
|
||||||
if (test('0')) {
|
if (test('0')) {
|
||||||
step();
|
step();
|
||||||
@ -60,66 +45,20 @@ auto Scanner::next() -> Tok
|
|||||||
while (test_in("01")) {
|
while (test_in("01")) {
|
||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
return tok(TT::Bin, loc);
|
return tok(Tok::Bin, loc);
|
||||||
} else if (test('x')) {
|
} else if (test('x')) {
|
||||||
step();
|
step();
|
||||||
while (test_range('0', '9') or test_range('a', 'f')
|
while (test_range('0', '9') or test_range('a', 'f')
|
||||||
or test_range('A', 'F')) {
|
or test_range('A', 'F')) {
|
||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
return tok(TT::Hex, loc);
|
return tok(Tok::Hex, loc);
|
||||||
} else {
|
} else {
|
||||||
return tok(TT::Int, loc);
|
return tok(Tok::Int, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (test('\'')) {
|
|
||||||
step();
|
|
||||||
if (done()) {
|
|
||||||
error(loc, "malformed literal");
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
if (test('\\')) {
|
|
||||||
step();
|
|
||||||
if (done()) {
|
|
||||||
error(loc, "malformed literal");
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
step();
|
|
||||||
} else {
|
|
||||||
if (test('\'')) {
|
|
||||||
error(loc, "malformed literal");
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
step();
|
|
||||||
}
|
|
||||||
if (done() or not test('\'')) {
|
|
||||||
error(loc, "malformed literal");
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
step();
|
|
||||||
return tok(TT::Char, loc);
|
|
||||||
}
|
|
||||||
if (test('"')) {
|
|
||||||
step();
|
|
||||||
while (not done() and not test('"')) {
|
|
||||||
if (test('\\')) {
|
|
||||||
step();
|
|
||||||
if (done()) {
|
|
||||||
error(loc, "malformed literal");
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
step();
|
|
||||||
}
|
|
||||||
if (done() or not test('"')) {
|
|
||||||
error(loc, "malformed literal");
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
step();
|
|
||||||
return tok(TT::Str, loc);
|
|
||||||
}
|
|
||||||
if (test_in("\n()[],:|^+-!")) {
|
if (test_in("\n()[],:|^+-!")) {
|
||||||
auto ty = static_cast<TT>(current());
|
auto ty = static_cast<Tok::Ty>(current());
|
||||||
step();
|
step();
|
||||||
return tok(ty, loc);
|
return tok(ty, loc);
|
||||||
}
|
}
|
||||||
@ -144,7 +83,7 @@ void Scanner::step()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Scanner::tok(TT ty, Loc loc) const -> Tok
|
auto Scanner::tok(Tok::Ty ty, Loc loc) const -> Tok
|
||||||
{
|
{
|
||||||
return Tok { m_text.substr(loc.idx, m_idx - loc.idx), loc, ty };
|
return Tok { m_text.substr(loc.idx, m_idx - loc.idx), loc, ty };
|
||||||
}
|
}
|
||||||
@ -245,35 +184,3 @@ void Loc::print_error(std::string_view text, std::string_view message) const
|
|||||||
message,
|
message,
|
||||||
clear);
|
clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto vc5::tools::unescape_escape_char(char ch) -> char
|
|
||||||
{
|
|
||||||
switch (ch) {
|
|
||||||
case 'n':
|
|
||||||
return '\n';
|
|
||||||
case 'r':
|
|
||||||
return '\r';
|
|
||||||
case 't':
|
|
||||||
return '\t';
|
|
||||||
case '0':
|
|
||||||
return '\0';
|
|
||||||
default:
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto vc5::tools::unescape_string(std::string_view value) -> std::string
|
|
||||||
{
|
|
||||||
auto result = std::string();
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < value.size()) {
|
|
||||||
if (value[0] == '\\') {
|
|
||||||
i += 1;
|
|
||||||
result += unescape_escape_char(value[i]);
|
|
||||||
} else {
|
|
||||||
result += value[i];
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -14,15 +14,12 @@ struct Loc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Tok {
|
struct Tok {
|
||||||
enum class Ty {
|
enum Ty {
|
||||||
Eof,
|
Eof,
|
||||||
Ident,
|
Ident,
|
||||||
Int,
|
Int,
|
||||||
Hex,
|
Hex,
|
||||||
Bin,
|
Bin,
|
||||||
Char,
|
|
||||||
Str,
|
|
||||||
KwConst,
|
|
||||||
Newline = '\n',
|
Newline = '\n',
|
||||||
LParen = '(',
|
LParen = '(',
|
||||||
RParen = ')',
|
RParen = ')',
|
||||||
@ -77,7 +74,4 @@ private:
|
|||||||
bool m_error_occured = false;
|
bool m_error_occured = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto unescape_escape_char(char ch) -> char;
|
|
||||||
auto unescape_string(std::string_view value) -> std::string;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user