add assembler
This commit is contained in:
parent
05793ad4ae
commit
4f6520c470
8
Makefile
8
Makefile
@ -34,7 +34,7 @@ endif
|
||||
|
||||
HEADERS = $(shell find . -name *.h)
|
||||
|
||||
ASM_SOURCES = $(shell find asm/ -name *.c)
|
||||
ASM_SOURCES = $(shell find asm/ -name *.c -not -name main.c)
|
||||
ASM_OBJECTS = $(patsubst %.c,build/%.o,$(ASM_SOURCES))
|
||||
|
||||
VM_SOURCES = $(shell find vm/ -name *.c)
|
||||
@ -43,7 +43,7 @@ VM_OBJECTS = $(patsubst %.c,build/%.o,$(VM_SOURCES))
|
||||
KERN_SOURCES = $(shell find kern/ -name *.c)
|
||||
KERN_OBJECTS = $(patsubst %.c,build/%.o,$(KERN_SOURCES))
|
||||
|
||||
all: bin/vm bin/build_disk_image
|
||||
all: bin/vm bin/build_disk_image bin/asm
|
||||
|
||||
bin/vm: $(VM_OBJECTS) $(ASM_OBJECTS) build/image
|
||||
@mkdir -p $(dir $@)
|
||||
@ -57,6 +57,10 @@ bin/build_disk_image: $(KERN_OBJECTS) $(ASM_OBJECTS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $^ -o $@ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS)
|
||||
|
||||
bin/asm: $(ASM_OBJECTS) build/asm/main.o
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $^ -o $@ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS)
|
||||
|
||||
build/%.o: %.c $(HEADERS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $< -c -o $@ $(C_FLAGS) $(OPTIMIZATION) $(F_FLAGS)
|
||||
|
874
asm/main.c
Normal file
874
asm/main.c
Normal file
@ -0,0 +1,874 @@
|
||||
#include "common/arch.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static inline bool str_includes(const char* str, char ch)
|
||||
{
|
||||
for (size_t i = 0; str[i] != '\0'; ++i) {
|
||||
if (str[i] == ch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline char* asm_strndup(const char* str, size_t len)
|
||||
{
|
||||
char* val = calloc(len + 1, sizeof(char));
|
||||
strncpy(val, str, len);
|
||||
return val;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
} Loc;
|
||||
|
||||
typedef enum {
|
||||
TT_Err,
|
||||
TT_Eof,
|
||||
TT_Ident,
|
||||
TT_Int,
|
||||
TT_Binary,
|
||||
TT_Hex,
|
||||
TT_Newline = '\n',
|
||||
TT_DoubleLt,
|
||||
TT_DoubleGt,
|
||||
TT_Pipe = '|',
|
||||
TT_Hat = '^',
|
||||
TT_Ampersand = '&',
|
||||
TT_Plus = '+',
|
||||
TT_Minus = '-',
|
||||
TT_Asterisk = '*',
|
||||
TT_Slash = '/',
|
||||
TT_Percent = '%',
|
||||
TT_LParen = '(',
|
||||
TT_RParen = ')',
|
||||
TT_LBracket = '[',
|
||||
TT_RBracket = ']',
|
||||
TT_Dot = '.',
|
||||
TT_Comma = ',',
|
||||
TT_Colon = ':',
|
||||
TT_Exclamation = '!',
|
||||
} TokTy;
|
||||
|
||||
typedef struct {
|
||||
TokTy ty;
|
||||
Loc loc;
|
||||
size_t len;
|
||||
} Tok;
|
||||
|
||||
typedef struct {
|
||||
const char* filename;
|
||||
const char* text;
|
||||
size_t text_len;
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
char ch;
|
||||
bool error_occured;
|
||||
} Lexer;
|
||||
|
||||
void lexer_construct(Lexer* lexer, const char* filename, const char* text)
|
||||
{
|
||||
*lexer = (Lexer) {
|
||||
.filename = filename,
|
||||
.text = text,
|
||||
.text_len = strlen(text),
|
||||
.idx = 0,
|
||||
.line = 1,
|
||||
.col = 1,
|
||||
.ch = text[0],
|
||||
.error_occured = false,
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool lexer_done(const Lexer* lexer)
|
||||
{
|
||||
return lexer->idx >= lexer->text_len;
|
||||
}
|
||||
|
||||
static inline void lexer_step(Lexer* lexer)
|
||||
{
|
||||
if (lexer_done(lexer)) {
|
||||
return;
|
||||
}
|
||||
if (lexer->ch == '\n') {
|
||||
lexer->line += 1;
|
||||
lexer->col = 1;
|
||||
} else {
|
||||
lexer->col += 1;
|
||||
}
|
||||
lexer->idx += 1;
|
||||
lexer->ch = lexer->text[lexer->idx];
|
||||
}
|
||||
|
||||
static inline Loc lexer_loc(const Lexer* lexer)
|
||||
{
|
||||
return (Loc) { .idx = lexer->idx, .line = lexer->line, .col = lexer->col };
|
||||
}
|
||||
|
||||
static inline Tok lexer_tok(const Lexer* lexer, TokTy ty, Loc loc)
|
||||
{
|
||||
return (Tok) { .ty = ty, .loc = loc, .len = lexer->idx - loc.idx };
|
||||
}
|
||||
|
||||
static inline void lexer_report(Lexer* lexer, const char* msg, Loc loc)
|
||||
{
|
||||
lexer->error_occured = true;
|
||||
|
||||
size_t line_start = loc.idx;
|
||||
while (line_start > 0 && lexer->text[line_start] != '\n') {
|
||||
line_start -= 1;
|
||||
}
|
||||
if (lexer->text[line_start] == '\n') {
|
||||
line_start += 1;
|
||||
}
|
||||
size_t line_end = loc.idx + 1;
|
||||
while (line_end < lexer->text_len && lexer->text[line_end] != '\n') {
|
||||
line_end += 1;
|
||||
}
|
||||
const char* line = &lexer->text[line_start];
|
||||
int line_len = (int)line_end - (int)line_start;
|
||||
|
||||
fprintf(stderr,
|
||||
"error: %s\n --> %s:%d:%d\n |\n%5d|%.*s\n |%*c^\n",
|
||||
msg,
|
||||
lexer->filename,
|
||||
loc.line,
|
||||
loc.col,
|
||||
loc.line,
|
||||
line_len,
|
||||
line,
|
||||
loc.col - 2,
|
||||
' ');
|
||||
}
|
||||
|
||||
Tok lexer_next(Lexer* lexer)
|
||||
{
|
||||
const char* ident_chars = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
||||
const char* int_chars = "1234567890";
|
||||
const char* hex_chars = "01234567889abcdefABCDEF";
|
||||
|
||||
Loc loc = lexer_loc(lexer);
|
||||
if (lexer_done(lexer)) {
|
||||
return lexer_tok(lexer, TT_Eof, loc);
|
||||
}
|
||||
if (lexer->ch == '\n') {
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, '\n', loc);
|
||||
} else if (str_includes(" \t", lexer->ch)) {
|
||||
while (!lexer_done(lexer) && str_includes(" \t", lexer->ch)) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_next(lexer);
|
||||
} else if (str_includes(ident_chars, lexer->ch)) {
|
||||
while (!lexer_done(lexer)
|
||||
&& (str_includes(ident_chars, lexer->ch)
|
||||
|| str_includes(int_chars, lexer->ch))) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Ident, loc);
|
||||
} else if (str_includes(int_chars, lexer->ch) && lexer->ch != '0') {
|
||||
while (!lexer_done(lexer) && (str_includes(int_chars, lexer->ch))) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Int, loc);
|
||||
} else if (lexer->ch == ';') {
|
||||
while (!lexer_done(lexer) && lexer->ch != '\n') {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_next(lexer);
|
||||
} else if (lexer->ch == '0') {
|
||||
lexer_step(lexer);
|
||||
if (lexer->ch == 'b') {
|
||||
lexer_step(lexer);
|
||||
if (lexer_done(lexer) || !str_includes("01", lexer->ch)) {
|
||||
lexer_report(lexer, "malformed binary literal", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
while (!lexer_done(lexer) && str_includes("01", lexer->ch)) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Binary, loc);
|
||||
} else if (lexer->ch == 'x') {
|
||||
lexer_step(lexer);
|
||||
if (lexer_done(lexer) || !str_includes(hex_chars, lexer->ch)) {
|
||||
lexer_report(lexer, "malformed hex literal", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
while (!lexer_done(lexer) && str_includes(hex_chars, lexer->ch)) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Hex, loc);
|
||||
|
||||
} else {
|
||||
return lexer_tok(lexer, TT_Int, loc);
|
||||
}
|
||||
} else if (lexer->ch == '<') {
|
||||
lexer_step(lexer);
|
||||
if (!lexer_done(lexer) && lexer->ch == '<') {
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_DoubleLt, loc);
|
||||
} else {
|
||||
lexer_report(lexer, "expected '<'", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
} else if (lexer->ch == '>') {
|
||||
lexer_step(lexer);
|
||||
if (!lexer_done(lexer) && lexer->ch == '>') {
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_DoubleGt, loc);
|
||||
} else {
|
||||
lexer_report(lexer, "expected '>'", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
} else if (str_includes("|^&+-*/%()[].,:!", lexer->ch)) {
|
||||
char ch = lexer->ch;
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, (TokTy)ch, loc);
|
||||
} else {
|
||||
lexer_report(lexer, "illegal character '%c'", loc);
|
||||
lexer_step(lexer);
|
||||
return lexer_next(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
// typedef enum {
|
||||
// M_err,
|
||||
// M_d8,
|
||||
// M_d16,
|
||||
// M_nop,
|
||||
// M_hlt,
|
||||
// M_jmp,
|
||||
// M_jmpf,
|
||||
// M_jnz,
|
||||
// M_cmp,
|
||||
// M_mov,
|
||||
// M_in,
|
||||
// M_out,
|
||||
// M_call,
|
||||
// M_callf,
|
||||
// M_ret,
|
||||
// M_retf,
|
||||
// M_lit,
|
||||
// M_int,
|
||||
// M_iret,
|
||||
// } Mnemonic;
|
||||
|
||||
typedef struct PLabel PLabel;
|
||||
|
||||
struct PLabel {
|
||||
PLabel* next;
|
||||
char* ident;
|
||||
Loc loc;
|
||||
bool sub_label;
|
||||
};
|
||||
|
||||
PLabel* plabel_new(PLabel* next, char* ident, bool sub_label, Loc loc)
|
||||
{
|
||||
PLabel* label = malloc(sizeof(PLabel));
|
||||
*label = (PLabel) { next, ident, loc, sub_label };
|
||||
return label;
|
||||
}
|
||||
void plabel_free(PLabel* label)
|
||||
{
|
||||
if (!label) {
|
||||
return;
|
||||
}
|
||||
plabel_free(label->next);
|
||||
free(label->ident);
|
||||
free(label);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
PoTy_Reg,
|
||||
PoTy_Imm,
|
||||
PoTy_Ident,
|
||||
PoTy_SubLabel,
|
||||
PoTy_MemU8,
|
||||
PoTy_MemU16,
|
||||
PoTy_Not,
|
||||
PoTy_Negate,
|
||||
PoTy_Or,
|
||||
PoTy_Xor,
|
||||
PoTy_And,
|
||||
PoTy_Shl,
|
||||
PoTy_Shr,
|
||||
PoTy_Add,
|
||||
PoTy_Sub,
|
||||
PoTy_Mul,
|
||||
PoTy_Div,
|
||||
PoTy_Mod,
|
||||
} POperandTy;
|
||||
|
||||
typedef struct POperand POperand;
|
||||
|
||||
struct POperand {
|
||||
POperandTy ty;
|
||||
Loc loc;
|
||||
union {
|
||||
Reg reg;
|
||||
uint16_t imm;
|
||||
char* ident;
|
||||
POperand* operand;
|
||||
struct {
|
||||
POperand* left;
|
||||
POperand* right;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
POperand* poperand_new_reg(Reg reg, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = PoTy_Reg, .loc = loc, .reg = reg };
|
||||
return operand;
|
||||
}
|
||||
POperand* poperand_new_imm(uint16_t imm, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = PoTy_Imm, .loc = loc, .imm = imm };
|
||||
return operand;
|
||||
}
|
||||
POperand* poperand_new_ident(POperandTy ty, char* ident, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = ty, .loc = loc, .ident = ident };
|
||||
return operand;
|
||||
}
|
||||
POperand* poperand_new_unary(POperandTy ty, POperand* inner, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = ty, .loc = loc, .operand = inner };
|
||||
return operand;
|
||||
}
|
||||
POperand* poperand_new_binary(
|
||||
POperandTy ty, POperand* left, POperand* right, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand
|
||||
= (POperand) { .ty = ty, .loc = loc, .left = left, .right = right };
|
||||
return operand;
|
||||
}
|
||||
void poperand_free(POperand* operand)
|
||||
{
|
||||
switch (operand->ty) {
|
||||
case PoTy_Reg:
|
||||
case PoTy_Imm:
|
||||
break;
|
||||
case PoTy_Ident:
|
||||
case PoTy_SubLabel:
|
||||
free(operand->ident);
|
||||
break;
|
||||
case PoTy_MemU8:
|
||||
case PoTy_MemU16:
|
||||
case PoTy_Not:
|
||||
case PoTy_Negate:
|
||||
poperand_free(operand->operand);
|
||||
break;
|
||||
case PoTy_Or:
|
||||
case PoTy_Xor:
|
||||
case PoTy_And:
|
||||
case PoTy_Shl:
|
||||
case PoTy_Shr:
|
||||
case PoTy_Add:
|
||||
case PoTy_Sub:
|
||||
case PoTy_Mul:
|
||||
case PoTy_Div:
|
||||
case PoTy_Mod:
|
||||
poperand_free(operand->left);
|
||||
poperand_free(operand->right);
|
||||
break;
|
||||
}
|
||||
free(operand);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
PLabel* labels;
|
||||
char* op;
|
||||
Loc loc;
|
||||
size_t ops_size;
|
||||
POperand* ops[];
|
||||
} PLine;
|
||||
|
||||
PLine* pline_new_0(char* op, PLabel* labels, Loc loc)
|
||||
{
|
||||
PLine* line = malloc(sizeof(PLine) + sizeof(POperand*) * 0);
|
||||
*line = (PLine) { .labels = labels, .op = op, .loc = loc, .ops_size = 0 };
|
||||
return line;
|
||||
}
|
||||
PLine* pline_new_1(char* op, PLabel* labels, Loc loc, POperand* op0)
|
||||
{
|
||||
PLine* line = malloc(sizeof(PLine) + sizeof(POperand*) * 1);
|
||||
*line = (PLine) { .labels = labels, .op = op, .loc = loc, .ops_size = 1 };
|
||||
line->ops[0] = op0;
|
||||
return line;
|
||||
}
|
||||
PLine* pline_new_2(
|
||||
char* op, PLabel* labels, Loc loc, POperand* op0, POperand* op1)
|
||||
{
|
||||
PLine* line = malloc(sizeof(PLine) + sizeof(POperand*) * 2);
|
||||
*line = (PLine) { .labels = labels, .op = op, .loc = loc, .ops_size = 2 };
|
||||
line->ops[0] = op0;
|
||||
line->ops[1] = op1;
|
||||
return line;
|
||||
}
|
||||
PLine* pline_new_3(char* op,
|
||||
PLabel* labels,
|
||||
Loc loc,
|
||||
POperand* op0,
|
||||
POperand* op1,
|
||||
POperand* op2)
|
||||
{
|
||||
PLine* line = malloc(sizeof(PLine) + sizeof(POperand*) * 3);
|
||||
*line = (PLine) { .labels = labels, .op = op, .loc = loc, .ops_size = 3 };
|
||||
line->ops[0] = op0;
|
||||
line->ops[1] = op1;
|
||||
line->ops[2] = op2;
|
||||
return line;
|
||||
}
|
||||
void pline_free(PLine* pline)
|
||||
{
|
||||
plabel_free(pline->labels);
|
||||
free(pline->op);
|
||||
for (size_t i = 0; i < pline->ops_size; ++i) {
|
||||
poperand_free(pline->ops[i]);
|
||||
}
|
||||
free(pline);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Lexer lexer;
|
||||
Tok tok;
|
||||
Tok eaten;
|
||||
bool error_occured;
|
||||
} Parser;
|
||||
|
||||
void parser_construct(Parser* parser, const char* filename, const char* text)
|
||||
{
|
||||
Lexer lexer;
|
||||
lexer_construct(&lexer, filename, text);
|
||||
|
||||
*parser = (Parser) {
|
||||
.lexer = lexer,
|
||||
.tok = lexer_next(&lexer),
|
||||
.eaten = (Tok) { 0 },
|
||||
.error_occured = false,
|
||||
};
|
||||
}
|
||||
|
||||
bool parser_done(const Parser* parser)
|
||||
{
|
||||
return parser->tok.ty == TT_Eof;
|
||||
}
|
||||
bool parser_error_occured(const Parser* parser)
|
||||
{
|
||||
return parser->error_occured || parser->lexer.error_occured;
|
||||
}
|
||||
|
||||
static inline void parser_step(Parser* parser)
|
||||
{
|
||||
parser->tok = lexer_next(&parser->lexer);
|
||||
}
|
||||
static inline bool parser_test(const Parser* parser, TokTy ty)
|
||||
{
|
||||
return parser->tok.ty == ty;
|
||||
}
|
||||
static inline bool parser_eat(Parser* parser, TokTy ty)
|
||||
{
|
||||
if (parser_test(parser, ty)) {
|
||||
parser->eaten = parser->tok;
|
||||
parser_step(parser);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static inline char* parser_ident_val(const Parser* parser, Tok tok)
|
||||
{
|
||||
return asm_strndup(&parser->lexer.text[tok.loc.idx], tok.len);
|
||||
}
|
||||
static inline void parser_report(Parser* parser, const char* msg, Loc loc)
|
||||
{
|
||||
parser->error_occured = true;
|
||||
|
||||
size_t line_start = loc.idx;
|
||||
while (line_start > 0 && parser->lexer.text[line_start] != '\n') {
|
||||
line_start -= 1;
|
||||
}
|
||||
if (parser->lexer.text[line_start] == '\n') {
|
||||
line_start += 1;
|
||||
}
|
||||
size_t line_end = loc.idx + 1;
|
||||
while (line_end < parser->lexer.text_len
|
||||
&& parser->lexer.text[line_end] != '\n') {
|
||||
line_end += 1;
|
||||
}
|
||||
const char* line = &parser->lexer.text[line_start];
|
||||
int line_len = (int)line_end - (int)line_start;
|
||||
|
||||
fprintf(stderr,
|
||||
"error: %s\n --> %s:%d:%d\n |\n%5d|%.*s\n |%*c^\n",
|
||||
msg,
|
||||
parser->lexer.filename,
|
||||
loc.line,
|
||||
loc.col,
|
||||
loc.line,
|
||||
line_len,
|
||||
line,
|
||||
loc.col - 2,
|
||||
' ');
|
||||
}
|
||||
|
||||
static inline void parser_skip_newlines(Parser* parser)
|
||||
{
|
||||
while (parser_eat(parser, '\n')) { }
|
||||
}
|
||||
|
||||
static inline PLabel* parser_parse_labels(
|
||||
Parser* parser, char** ident, Loc* ident_loc)
|
||||
{
|
||||
*ident = NULL;
|
||||
PLabel* labels = NULL;
|
||||
while (parser->tok.ty != TT_Eof && *ident == NULL) {
|
||||
parser_skip_newlines(parser);
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, '.')) {
|
||||
if (!parser_eat(parser, TT_Ident)) {
|
||||
parser_report(parser, "expected identifier", parser->tok.loc);
|
||||
plabel_free(labels);
|
||||
return NULL;
|
||||
}
|
||||
char* label_ident = parser_ident_val(parser, parser->eaten);
|
||||
if (!parser_eat(parser, ':')) {
|
||||
parser_report(parser, "expected ':'", parser->tok.loc);
|
||||
plabel_free(labels);
|
||||
free(label_ident);
|
||||
return NULL;
|
||||
}
|
||||
labels = plabel_new(labels, label_ident, true, loc);
|
||||
} else if (parser_eat(parser, TT_Ident)) {
|
||||
*ident = parser_ident_val(parser, parser->eaten);
|
||||
*ident_loc = loc;
|
||||
if (!parser_eat(parser, ':')) {
|
||||
break;
|
||||
}
|
||||
labels = plabel_new(labels, *ident, false, loc);
|
||||
*ident = NULL;
|
||||
} else {
|
||||
parser_report(
|
||||
parser, "expected identifier or ':'", parser->tok.loc);
|
||||
plabel_free(labels);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
static const int parser_binary_prec = 6;
|
||||
static inline POperand* parser_parse_operand_2(Parser* parser, int prec);
|
||||
|
||||
static inline POperand* parser_parse_operand_0(Parser* parser)
|
||||
{
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, TT_Ident)) {
|
||||
char* ident = parser_ident_val(parser, parser->eaten);
|
||||
const char* reg_key[10] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "rbp", "rsp", "rfl", "rcs", "rip"
|
||||
};
|
||||
Reg reg_val[10] = { R0, R1, R2, R3, R4, Rbp, Rsp, Rfl, Rcs, Rip };
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
if (strcmp(reg_key[i], ident) == 0) {
|
||||
free(ident);
|
||||
return poperand_new_reg(reg_val[i], loc);
|
||||
}
|
||||
}
|
||||
return poperand_new_ident(PoTy_Ident, ident, loc);
|
||||
} else if (parser_eat(parser, TT_Int)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint16_t imm = (uint16_t)strtoul(str, NULL, 10);
|
||||
free(str);
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, TT_Binary)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint16_t imm = (uint16_t)strtoul(&str[2], NULL, 2);
|
||||
free(str);
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, TT_Hex)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint16_t imm = (uint16_t)strtoul(&str[2], NULL, 16);
|
||||
free(str);
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, '.')) {
|
||||
if (!parser_eat(parser, TT_Ident)) {
|
||||
parser_report(parser, "expected identifier", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
char* ident = parser_ident_val(parser, parser->eaten);
|
||||
return poperand_new_ident(PoTy_SubLabel, ident, loc);
|
||||
} else if (parser_eat(parser, '(')) {
|
||||
POperand* operand = parser_parse_operand_2(parser, parser_binary_prec);
|
||||
if (!parser_eat(parser, ')')) {
|
||||
parser_report(parser, "expected ')'", parser->tok.loc);
|
||||
poperand_free(operand);
|
||||
return NULL;
|
||||
}
|
||||
return operand;
|
||||
} else {
|
||||
parser_report(parser, "expected operand", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline POperand* parser_parse_operand_1(Parser* parser)
|
||||
{
|
||||
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, '-')) {
|
||||
POperand* operand = parser_parse_operand_1(parser);
|
||||
return poperand_new_unary(PoTy_Negate, operand, loc);
|
||||
} else if (parser_eat(parser, '!')) {
|
||||
POperand* operand = parser_parse_operand_1(parser);
|
||||
return poperand_new_unary(PoTy_Not, operand, loc);
|
||||
} else {
|
||||
return parser_parse_operand_0(parser);
|
||||
}
|
||||
}
|
||||
|
||||
static inline POperand* parser_parse_operand_2(Parser* parser, int prec)
|
||||
{
|
||||
const POperandTy op_tys[] = {
|
||||
PoTy_Or,
|
||||
PoTy_Xor,
|
||||
PoTy_And,
|
||||
PoTy_Shl,
|
||||
PoTy_Shr,
|
||||
PoTy_Add,
|
||||
PoTy_Sub,
|
||||
PoTy_Mul,
|
||||
PoTy_Div,
|
||||
PoTy_Mod,
|
||||
};
|
||||
const TokTy op_tts[] = {
|
||||
'|',
|
||||
'^',
|
||||
'&',
|
||||
TT_DoubleGt,
|
||||
TT_DoubleLt,
|
||||
'+',
|
||||
'-',
|
||||
'*',
|
||||
'/',
|
||||
'%',
|
||||
};
|
||||
const int op_precs[] = { 6, 5, 4, 3, 3, 2, 2, 1, 1, 1 };
|
||||
static_assert(sizeof(op_tys) / sizeof(op_tys[0])
|
||||
== sizeof(op_tts) / sizeof(op_tts[0]),
|
||||
"misaligned");
|
||||
static_assert(sizeof(op_tys) / sizeof(op_tys[0])
|
||||
== sizeof(op_precs) / sizeof(op_precs[0]),
|
||||
"misaligned");
|
||||
|
||||
if (prec == 0) {
|
||||
return parser_parse_operand_1(parser);
|
||||
}
|
||||
POperand* left = parser_parse_operand_2(parser, prec - 1);
|
||||
bool should_continue = true;
|
||||
while (should_continue) {
|
||||
should_continue = false;
|
||||
for (size_t i = 0; i < sizeof(op_tys) / sizeof(op_tys[0]); ++i) {
|
||||
if (prec >= op_precs[i] && parser_eat(parser, op_tts[i])) {
|
||||
POperand* right = parser_parse_operand_2(parser, prec - 1);
|
||||
left = poperand_new_binary(op_tys[0], left, right, left->loc);
|
||||
should_continue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
static inline POperand* parser_parse_operand_3(Parser* parser)
|
||||
{
|
||||
if (!parser_test(parser, TT_Ident)) {
|
||||
return parser_parse_operand_2(parser, parser_binary_prec);
|
||||
}
|
||||
Loc loc = parser->tok.loc;
|
||||
char* ident = parser_ident_val(parser, parser->eaten);
|
||||
if (strcmp(ident, "u8") == 0) {
|
||||
free(ident);
|
||||
if (!parser_eat(parser, '[')) {
|
||||
parser_report(parser, "expected '['", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
POperand* operand = parser_parse_operand_2(parser, parser_binary_prec);
|
||||
if (!parser_eat(parser, ']')) {
|
||||
parser_report(parser, "expected ']'", parser->tok.loc);
|
||||
poperand_free(operand);
|
||||
return NULL;
|
||||
}
|
||||
return poperand_new_unary(PoTy_MemU8, operand, loc);
|
||||
} else if (strcmp(ident, "u16") == 0) {
|
||||
free(ident);
|
||||
if (!parser_eat(parser, '[')) {
|
||||
parser_report(parser, "expected '['", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
POperand* operand = parser_parse_operand_2(parser, parser_binary_prec);
|
||||
if (!parser_eat(parser, ']')) {
|
||||
parser_report(parser, "expected ']'", parser->tok.loc);
|
||||
poperand_free(operand);
|
||||
return NULL;
|
||||
}
|
||||
return poperand_new_unary(PoTy_MemU16, operand, loc);
|
||||
} else {
|
||||
free(ident);
|
||||
return parser_parse_operand_2(parser, parser_binary_prec);
|
||||
}
|
||||
}
|
||||
|
||||
PLine* parser_next(Parser* parser)
|
||||
{
|
||||
char* ident;
|
||||
Loc loc;
|
||||
PLabel* labels = parser_parse_labels(parser, &ident, &loc);
|
||||
|
||||
POperand* ops[3];
|
||||
size_t ops_size = 0;
|
||||
|
||||
if (!parser_test(parser, TT_Eof) && !parser_test(parser, '\n')) {
|
||||
POperand* operand = parser_parse_operand_3(parser);
|
||||
if (!operand) {
|
||||
goto error_free_ops;
|
||||
}
|
||||
ops[ops_size++] = operand;
|
||||
while (!parser_test(parser, TT_Eof) && !parser_test(parser, '\n')
|
||||
&& ops_size < 3) {
|
||||
if (!parser_eat(parser, ',')) {
|
||||
parser_report(parser, "expected ','", parser->tok.loc);
|
||||
goto error_free_ops;
|
||||
}
|
||||
POperand* operand = parser_parse_operand_3(parser);
|
||||
if (!operand) {
|
||||
goto error_free_ops;
|
||||
}
|
||||
ops[ops_size++] = operand;
|
||||
}
|
||||
}
|
||||
if (!parser_eat(parser, '\n') && !parser_test(parser, TT_Eof)) {
|
||||
parser_report(parser, "expected newline", parser->tok.loc);
|
||||
goto error_free_ops;
|
||||
}
|
||||
parser_skip_newlines(parser);
|
||||
|
||||
switch (ops_size) {
|
||||
case 0:
|
||||
return pline_new_0(ident, labels, loc);
|
||||
case 1:
|
||||
return pline_new_1(ident, labels, loc, ops[0]);
|
||||
case 2:
|
||||
return pline_new_2(ident, labels, loc, ops[0], ops[1]);
|
||||
case 3:
|
||||
default:
|
||||
return pline_new_3(ident, labels, loc, ops[0], ops[1], ops[2]);
|
||||
}
|
||||
|
||||
error_free_ops:
|
||||
for (size_t i = 0; i < ops_size; ++i)
|
||||
if (ops[i])
|
||||
poperand_free(ops[i]);
|
||||
plabel_free(labels);
|
||||
free(ident);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* input_file;
|
||||
const char* output_file;
|
||||
} Args;
|
||||
|
||||
static inline Args parse_args(int argc, char** argv);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Args args = parse_args(argc, argv);
|
||||
|
||||
FILE* input_fp = fopen(args.input_file, "r");
|
||||
if (!input_fp) {
|
||||
fprintf(stderr,
|
||||
"error: could not open input file '%s': %s\n",
|
||||
args.input_file,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fseek(input_fp, 0L, SEEK_END);
|
||||
size_t file_size = (size_t)ftell(input_fp);
|
||||
rewind(input_fp);
|
||||
|
||||
char* input_text = calloc(file_size + 1, sizeof(char));
|
||||
size_t bytes_read = fread(input_text, sizeof(char), file_size, input_fp);
|
||||
fclose(input_fp);
|
||||
if (bytes_read != file_size) {
|
||||
fprintf(stderr,
|
||||
"error: could not read input file '%s': %s\n",
|
||||
args.input_file,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
Parser parser;
|
||||
parser_construct(&parser, args.input_file, input_text);
|
||||
|
||||
while (!parser_done(&parser)) {
|
||||
PLine* line = parser_next(&parser);
|
||||
if (!line) {
|
||||
break;
|
||||
}
|
||||
pline_free(line);
|
||||
if (parser_error_occured(&parser)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(input_text);
|
||||
}
|
||||
|
||||
static inline Args parse_args(int argc, char** argv)
|
||||
{
|
||||
const char* input_file = NULL;
|
||||
const char* output_file = NULL;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "-o") == 0) {
|
||||
i += 1;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "error: no filename given to -o\n");
|
||||
exit(1);
|
||||
}
|
||||
output_file = argv[i];
|
||||
} else {
|
||||
if (input_file != NULL) {
|
||||
fprintf(stderr, "error: multiple input files specified\n");
|
||||
exit(1);
|
||||
}
|
||||
input_file = argv[i];
|
||||
}
|
||||
}
|
||||
if (input_file == NULL) {
|
||||
fprintf(stderr, "error: no input file\n");
|
||||
exit(1);
|
||||
}
|
||||
if (output_file == NULL) {
|
||||
output_file = "out.o";
|
||||
}
|
||||
return (Args) {
|
||||
input_file,
|
||||
output_file,
|
||||
};
|
||||
}
|
140
kern/main.asm
Normal file
140
kern/main.asm
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
start:
|
||||
; rsp points *at* the top element
|
||||
mov Rbp, 2048
|
||||
mov rsp, 2048 - 2
|
||||
|
||||
lit interrupt_table
|
||||
or rfl, rfl, 1 << Fl_Int
|
||||
|
||||
or rfl, Rfl, 1 << Fl_Vcd
|
||||
|
||||
mov16 r0, 512
|
||||
mov16 r1, 1
|
||||
int Int_DiskRead
|
||||
|
||||
main_loop:
|
||||
hlt
|
||||
jmp main_loop
|
||||
|
||||
interrupt_table:
|
||||
; size
|
||||
d16 1
|
||||
data keyboard_interrupt
|
||||
nop
|
||||
|
||||
keyboard_interrupt:
|
||||
and rfl, rfl, !(1 << Fl_Int)
|
||||
push rbp
|
||||
mov rbp, Rsp
|
||||
push r0
|
||||
push r1
|
||||
push r2
|
||||
push r3
|
||||
|
||||
in r0, Device_Keyboard
|
||||
|
||||
cmp r0, 44
|
||||
mov r1, rfl
|
||||
and r1, r1, 1 << Fl_Eq
|
||||
jnz r1, .L0
|
||||
|
||||
cmp r0, 42
|
||||
mov16 r1, rfl
|
||||
and r1, r1, 1 << Fl_Eq
|
||||
jnz r1, .L1
|
||||
|
||||
cmp r0, 40
|
||||
mov r1, rfl
|
||||
and r1, r1, 1 << Fl_Eq
|
||||
jnz r1, .L2
|
||||
|
||||
jmp .L3
|
||||
|
||||
.L0:
|
||||
mov R0, 32 ; ' '
|
||||
call put_char
|
||||
jmp .L4
|
||||
|
||||
.L1:
|
||||
mov r1, screen_x
|
||||
cmp r1, 0
|
||||
mov r2, rfl
|
||||
and r2, r2, 1 << Fl_Eq
|
||||
jnz r2, .L4
|
||||
sub r1, r1, 1
|
||||
mov screen_x, R1
|
||||
; mov r0, ' '
|
||||
call put_char
|
||||
mov16 r1, screen_x
|
||||
sub r1, r1, 1
|
||||
mov screen_x, R1
|
||||
jmp .L4
|
||||
|
||||
.L2:
|
||||
mov r1, screen_y
|
||||
add r1, r1, 1
|
||||
mov screen_y, R1
|
||||
mov r1, 0
|
||||
mov screen_x, R1
|
||||
jmp .L4
|
||||
|
||||
.L3:
|
||||
; add r0, r0, 'A' - 4
|
||||
call put_char
|
||||
jmp .L4
|
||||
|
||||
.L4:
|
||||
|
||||
pop r3
|
||||
pop r2
|
||||
pop r1
|
||||
pop r0
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
or rfl, rfl, 1 << Fl_Int
|
||||
iret
|
||||
|
||||
put_char:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
push r1
|
||||
push r2
|
||||
|
||||
mov r2, screen_y
|
||||
mul r2, r2, vcd_width_in_ch
|
||||
mov r1, screen_x
|
||||
add r1, r1, 0x0c00
|
||||
add r1, r1, r2
|
||||
mov r1, r0
|
||||
|
||||
mov r1, screen_x
|
||||
add r1, r1, 1
|
||||
mov screen_x, r1
|
||||
|
||||
cmp r1, vcd_width_in_ch
|
||||
mov r2, rfl
|
||||
and r2, r2, 1 << Fl_Eq
|
||||
jnz r2, .L0
|
||||
jmp .L1
|
||||
|
||||
.L0:
|
||||
mov r1, screen_y
|
||||
add r1, r1, 1
|
||||
mov screen_y, r1
|
||||
mov r1, 0
|
||||
mov screen_x, r1
|
||||
|
||||
.L1:
|
||||
pop r1
|
||||
pop r2
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
screen_x:
|
||||
d16 0
|
||||
screen_y:
|
||||
d16 0
|
||||
|
||||
; vim: syntax=nasm
|
Loading…
x
Reference in New Issue
Block a user