From 4f6520c47015f14463631fe08b91bb704b2f04ab Mon Sep 17 00:00:00 2001 From: sfja Date: Tue, 1 Apr 2025 05:25:34 +0200 Subject: [PATCH] add assembler --- Makefile | 8 +- asm/main.c | 874 ++++++++++++++++++++++++++++++++++++++++++++++++++ kern/main.asm | 140 ++++++++ 3 files changed, 1020 insertions(+), 2 deletions(-) create mode 100644 asm/main.c create mode 100644 kern/main.asm diff --git a/Makefile b/Makefile index c35b6ab..77a68ad 100644 --- a/Makefile +++ b/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) diff --git a/asm/main.c b/asm/main.c new file mode 100644 index 0000000..452bde1 --- /dev/null +++ b/asm/main.c @@ -0,0 +1,874 @@ +#include "common/arch.h" +#include +#include +#include +#include +#include +#include +#include + +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, + }; +} diff --git a/kern/main.asm b/kern/main.asm new file mode 100644 index 0000000..b5f601b --- /dev/null +++ b/kern/main.asm @@ -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