diff --git a/asm/asm.c b/asm/asm.c index a8dba25..015860a 100644 --- a/asm/asm.c +++ b/asm/asm.c @@ -401,6 +401,54 @@ uint16_t assemble_line(uint16_t* out, const Line* line) out[ip++] = op1; break; } + case LineTy_Out_Reg_Reg: { + uint16_t op1 = line->op1.reg; + uint16_t op2 = line->op2.reg; + + uint32_t ins = Op_In; + add_op1_reg(&ins, op1); + ins |= (op2 & 0x7u) << 13; + + out[ip++] = (uint16_t)ins; + break; + } + case LineTy_Out_Reg_Imm: { + uint16_t op1 = line->op1.reg; + uint16_t op2 = line->op2.imm; + + uint32_t ins = Op_In; + add_op1_reg(&ins, op1); + ins |= 1 << 7; + + out[ip++] = (uint16_t)ins; + out[ip++] = op2; + break; + } + case LineTy_Out_Imm_Reg: { + uint16_t op1 = line->op1.imm; + uint16_t op2 = line->op2.reg; + + uint32_t ins = Op_In; + set_is_imm(&ins); + ins |= (op2 & 0x7u) << 13; + + out[ip++] = (uint16_t)ins; + out[ip++] = op1; + break; + } + case LineTy_Out_Imm_Imm: { + uint16_t op1 = line->op1.imm; + uint16_t op2 = line->op2.imm; + + uint32_t ins = Op_In; + set_is_imm(&ins); + ins |= 1 << 7; + + out[ip++] = (uint16_t)ins; + out[ip++] = op1; + out[ip++] = op2; + break; + } case LineTy_Call_Reg: { uint16_t op1 = line->op1.reg; diff --git a/asm/asm.h b/asm/asm.h index 5b516b2..167a4c2 100644 --- a/asm/asm.h +++ b/asm/asm.h @@ -44,6 +44,10 @@ typedef enum { LineTy_Mov16_MemLabel_Reg, LineTy_In_Reg, LineTy_In_Imm, + LineTy_Out_Reg_Reg, + LineTy_Out_Reg_Imm, + LineTy_Out_Imm_Reg, + LineTy_Out_Imm_Imm, LineTy_Call_Reg, LineTy_Call_Imm, LineTy_Call_Label, @@ -114,6 +118,7 @@ typedef struct { uint16_t offset; } Line; +uint16_t assemble_line(uint16_t* out, const Line* line); uint16_t assemble_lines_with_labels( uint16_t* out, const Line* lines, size_t lines_size); @@ -139,8 +144,8 @@ Line s_mov8_r_r(Reg dst_reg, Reg op2_reg); Line s_mov8_r_i(Reg dst_reg, uint16_t op2_imm); Line s_mov8_r_mr(Reg dst_reg, Reg op2_reg, uint16_t op2_offset); Line s_mov8_r_mi(Reg dst_reg, uint16_t op2_imm); -Line s_mov8_mr_r(Reg dst_reg, Reg op2_reg); -Line s_mov8_mr_i(Reg dst_reg, uint16_t op2_imm); +Line s_mov8_mr_r(Reg dst_reg, uint16_t dst_offset, Reg op2_reg); +Line s_mov8_mr_i(Reg dst_reg, uint16_t dst_offset, uint16_t op2_imm); Line s_mov8_mi_r(uint16_t dst_imm, Reg op2_reg); Line s_mov8_mi_i(uint16_t dst_imm, uint16_t op2_imm); Line s_mov16_r_r(Reg dst_reg, Reg op2_reg); @@ -156,6 +161,10 @@ Line s_mov16_mi_i(uint16_t dst_imm, uint16_t op2_imm); Line s_mov16_ml_r(int dst_label, Reg op2_reg); Line s_in_r(Reg dst_reg, Reg op1_reg); Line s_in_i(Reg dst_reg, uint16_t op1_imm); +Line s_out_r_r(Reg op1_reg, Reg op2_reg); +Line s_out_r_i(Reg op1_reg, uint16_t op2_imm); +Line s_out_i_r(uint16_t op1_imm, Reg op2_reg); +Line s_out_i_i(uint16_t op1_imm, uint16_t op2_imm); Line s_call_r(Reg op1_reg); Line s_call_i(uint16_t op1_imm); Line s_call_l(int op1_label); diff --git a/asm/ctors.c b/asm/ctors.c index 399888e..4d47b8a 100644 --- a/asm/ctors.c +++ b/asm/ctors.c @@ -166,20 +166,22 @@ Line s_mov8_r_mi(Reg dst_reg, uint16_t op2_imm) .op2 = (Ex) { .imm = op2_imm }, }; } -Line s_mov8_mr_r(Reg dst_reg, Reg op2_reg) +Line s_mov8_mr_r(Reg dst_reg, uint16_t dst_offset, Reg op2_reg) { return (Line) { .ty = LineTy_Mov8_MemReg_Reg, .dst = (Ex) { .reg = (uint16_t)dst_reg }, .op2 = (Ex) { .reg = (uint16_t)op2_reg }, + .offset = dst_offset, }; } -Line s_mov8_mr_i(Reg dst_reg, uint16_t op2_imm) +Line s_mov8_mr_i(Reg dst_reg, uint16_t dst_offset, uint16_t op2_imm) { return (Line) { .ty = LineTy_Mov8_MemReg_Imm, .dst = (Ex) { .reg = (uint16_t)dst_reg }, .op2 = (Ex) { .imm = op2_imm }, + .offset = dst_offset, }; } Line s_mov8_mi_r(uint16_t dst_imm, Reg op2_reg) @@ -305,6 +307,38 @@ Line s_in_i(Reg dst_reg, uint16_t op1_imm) .op1 = (Ex) { .imm = op1_imm }, }; } +Line s_out_r_r(Reg op1_reg, Reg op2_reg) +{ + return (Line) { + .ty = LineTy_Out_Reg_Reg, + .op1 = (Ex) { .reg = (uint16_t)op1_reg }, + .op2 = (Ex) { .reg = (uint16_t)op2_reg }, + }; +} +Line s_out_r_i(Reg op1_reg, uint16_t op2_imm) +{ + return (Line) { + .ty = LineTy_Out_Reg_Imm, + .op1 = (Ex) { .reg = (uint16_t)op1_reg }, + .op2 = (Ex) { .imm = op2_imm }, + }; +} +Line s_out_i_r(uint16_t op1_imm, Reg op2_reg) +{ + return (Line) { + .ty = LineTy_Out_Imm_Reg, + .op1 = (Ex) { .imm = op1_imm }, + .op2 = (Ex) { .reg = (uint16_t)op2_reg }, + }; +} +Line s_out_i_i(uint16_t op1_imm, uint16_t op2_imm) +{ + return (Line) { + .ty = LineTy_Out_Imm_Imm, + .op1 = (Ex) { .imm = op1_imm }, + .op2 = (Ex) { .imm = op2_imm }, + }; +} Line s_call_r(Reg op1_reg) { return (Line) { diff --git a/asm/main.c b/asm/main.c index 72d6597..6e0d987 100644 --- a/asm/main.c +++ b/asm/main.c @@ -1,3 +1,4 @@ +#include "asm/asm.h" #include "common/arch.h" #include #include @@ -17,6 +18,14 @@ static inline bool str_includes(const char* str, char ch) return false; } +static inline char* asm_strdup(const char* str) +{ + size_t len = strlen(str); + char* val = calloc(len + 1, sizeof(char)); + strncpy(val, str, len); + return val; +} + static inline char* asm_strndup(const char* str, size_t len) { char* val = calloc(len + 1, sizeof(char)); @@ -30,15 +39,17 @@ typedef struct { int col; } Loc; -#define REPORTF_INNER(FMT, ...) \ - (fprintf(stderr, "error: " FMT "\n", __VA_ARGS__)) -#define REPORTF(...) REPORTF_INNER(__VA_ARGS__) +#define REPORTF_ERROR(FMT, ...) \ + (fprintf( \ + stderr, "\x1b[1;91merror\x1b[1;97m: " FMT "\x1b[0m\n", __VA_ARGS__)) +#define REPORTF_INFO(FMT, ...) \ + (fprintf(stderr, "\x1b[1;96minfo\x1b[1;97m: " FMT "\x1b[0m\n", __VA_ARGS__)) +#define REPORTF_WARNING(FMT, ...) \ + (fprintf( \ + stderr, "\x1b[1;93mwarning\x1b[1;97m: " FMT "\x1b[0m\n", __VA_ARGS__)) -static inline void report_with_loc(const char* filename, - const char* text, - size_t text_len, - const char* msg, - Loc loc) +static inline void print_report_loc( + const char* filename, const char* text, size_t text_len, Loc loc) { size_t line_start = loc.idx; while (line_start > 0 && text[line_start] != '\n') { @@ -54,9 +65,10 @@ static inline void report_with_loc(const char* filename, const char* line = &text[line_start]; int line_len = (int)line_end - (int)line_start; - REPORTF("%s", msg); fprintf(stderr, - " --> %s:%d:%d\n |\n%5d|%.*s\n |%*c^\n", + " \x1b[96m--> ./%s:%d:%d\n " + "\x1b[37m|\n\x1b[96m%5d\x1b[37m|%.*s\n " + "|%*c\x1b[1;91m^\x1b[0m\n", filename, loc.line, loc.col, @@ -67,6 +79,23 @@ static inline void report_with_loc(const char* filename, ' '); } +typedef struct { + const char* filename; + const char* text; + size_t text_len; +} Reporter; + +void reporter_print_loc(Reporter* rep, Loc loc) +{ + print_report_loc(rep->filename, rep->text, rep->text_len, loc); +} + +void reporter_error_with_loc(Reporter* rep, const char* msg, Loc loc) +{ + REPORTF_ERROR("%s", msg); + reporter_print_loc(rep, loc); +} + typedef enum { TT_Err, TT_Eof, @@ -173,7 +202,8 @@ static inline int lexer_skip_literal_char(Lexer* lexer) static inline void lexer_report(Lexer* lexer, const char* msg, Loc loc) { lexer->error_occured = true; - report_with_loc(lexer->filename, lexer->text, lexer->text_len, msg, loc); + REPORTF_ERROR("%s", msg); + print_report_loc(lexer->filename, lexer->text, lexer->text_len, loc); } Tok lexer_next(Lexer* lexer) @@ -287,28 +317,6 @@ Tok lexer_next(Lexer* 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 { @@ -340,8 +348,8 @@ typedef enum { PoTy_Ident, PoTy_SubLabel, PoTy_Str, - PoTy_MemU8, - PoTy_MemU16, + PoTy_Mem8, + PoTy_Mem16, PoTy_Not, PoTy_Negate, PoTy_Or, @@ -364,7 +372,10 @@ struct POperand { union { Reg reg; uint16_t imm; - char* str; + struct { + char* str; + size_t str_len; + }; POperand* operand; struct { POperand* left; @@ -385,10 +396,15 @@ POperand* poperand_new_imm(uint16_t imm, Loc loc) *operand = (POperand) { .ty = PoTy_Imm, .loc = loc, .imm = imm }; return operand; } -POperand* poperand_new_str(POperandTy ty, char* str, Loc loc) +POperand* poperand_new_str(POperandTy ty, char* str, size_t str_len, Loc loc) { POperand* operand = malloc(sizeof(POperand)); - *operand = (POperand) { .ty = ty, .loc = loc, .str = str }; + *operand = (POperand) { + .ty = ty, + .loc = loc, + .str = str, + .str_len = str_len, + }; return operand; } POperand* poperand_new_unary(POperandTy ty, POperand* inner, Loc loc) @@ -416,8 +432,8 @@ void poperand_free(POperand* operand) case PoTy_Str: free(operand->str); break; - case PoTy_MemU8: - case PoTy_MemU16: + case PoTy_Mem8: + case PoTy_Mem16: case PoTy_Not: case PoTy_Negate: poperand_free(operand->operand); @@ -525,10 +541,10 @@ static inline char* parser_ident_val(const Parser* parser, Tok tok) static inline void parser_report(Parser* parser, const char* msg, Loc loc) { parser->error_occured = true; - report_with_loc(parser->lexer.filename, + REPORTF_ERROR("%s", msg); + print_report_loc(parser->lexer.filename, parser->lexer.text, parser->lexer.text_len, - msg, loc); } @@ -613,21 +629,42 @@ static inline POperand* parser_parse_operand_0(Parser* parser) return poperand_new_reg(reg_val[i], loc); } } - return poperand_new_str(PoTy_Ident, ident, loc); + return poperand_new_str(PoTy_Ident, ident, parser->eaten.len, 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); + uint64_t val = strtoull(str, NULL, 10); free(str); + if (val > 0xffff) { + parser_report(parser, + "integers larger than 65536 not supported", + parser->tok.loc); + return NULL; + } + uint16_t imm = (uint16_t)val; 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); + uint64_t val = strtoull(&str[2], NULL, 2); free(str); + if (val > 0xffff) { + parser_report(parser, + "integers larger than 65536 not supported", + parser->tok.loc); + return NULL; + } + uint16_t imm = (uint16_t)val; 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); + uint64_t val = strtoull(&str[2], NULL, 16); free(str); + if (val > 0xffff) { + parser_report(parser, + "integers larger than 65536 not supported", + parser->tok.loc); + return NULL; + } + uint16_t imm = (uint16_t)val; return poperand_new_imm(imm, loc); } else if (parser_eat(parser, TT_Char)) { char* str = parser_ident_val(parser, parser->eaten); @@ -643,14 +680,14 @@ static inline POperand* parser_parse_operand_0(Parser* parser) str[i] = literal_char_val(&lit[i]); } free(lit); - return poperand_new_str(PoTy_Str, str, loc); + return poperand_new_str(PoTy_Str, str, str_len, 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_str(PoTy_SubLabel, ident, loc); + return poperand_new_str(PoTy_SubLabel, ident, parser->eaten.len, loc); } else if (parser_eat(parser, '(')) { POperand* operand = parser_parse_operand_2(parser, parser_binary_prec); if (!parser_eat(parser, ')')) { @@ -686,8 +723,8 @@ static inline POperand* parser_parse_operand_2(Parser* parser, int prec) PoTy_Or, PoTy_Xor, PoTy_And, - PoTy_Shl, PoTy_Shr, + PoTy_Shl, PoTy_Add, PoTy_Sub, PoTy_Mul, @@ -724,7 +761,7 @@ static inline POperand* parser_parse_operand_2(Parser* parser, int prec) 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); + left = poperand_new_binary(op_tys[i], left, right, left->loc); should_continue = true; break; } @@ -737,7 +774,7 @@ static inline POperand* parser_parse_operand_3(Parser* parser) { Loc loc = parser->tok.loc; if (parser_eat(parser, TT_LBracket)) { - parser_report(parser, "expected 'u8' or 'u16'", loc); + parser_report(parser, "expected 'u8' or 'u16' before '['", loc); return NULL; } if (!parser_test(parser, TT_Ident)) { @@ -757,7 +794,7 @@ static inline POperand* parser_parse_operand_3(Parser* parser) poperand_free(operand); return NULL; } - return poperand_new_unary(PoTy_MemU8, operand, loc); + return poperand_new_unary(PoTy_Mem8, operand, loc); } else if (strcmp(ident, "u16") == 0) { free(ident); parser_step(parser); @@ -771,13 +808,20 @@ static inline POperand* parser_parse_operand_3(Parser* parser) poperand_free(operand); return NULL; } - return poperand_new_unary(PoTy_MemU16, operand, loc); + return poperand_new_unary(PoTy_Mem16, operand, loc); } else { free(ident); return parser_parse_operand_2(parser, parser_binary_prec); } } +static inline void parser_skip_to_next_line(Parser* parser) +{ + while (!parser_done(parser) && !parser_eat(parser, TT_Newline)) { + parser_step(parser); + } +} + PLine* parser_next(Parser* parser) { char* ident; @@ -792,6 +836,7 @@ PLine* parser_next(Parser* parser) if (!parser_test(parser, TT_Eof) && !parser_test(parser, '\n')) { POperand* operand = parser_parse_operand_3(parser); if (!operand) { + parser_skip_to_next_line(parser); goto error_free_ops; } ops[ops_size++] = operand; @@ -801,14 +846,17 @@ PLine* parser_next(Parser* parser) parser_report(parser, "exceeded maximum number of operands (64)", parser->tok.loc); + parser_skip_to_next_line(parser); goto error_free_ops; } if (!parser_eat(parser, ',')) { parser_report(parser, "expected ','", parser->tok.loc); + parser_skip_to_next_line(parser); goto error_free_ops; } POperand* operand = parser_parse_operand_3(parser); if (!operand) { + parser_skip_to_next_line(parser); goto error_free_ops; } ops[ops_size++] = operand; @@ -834,6 +882,1119 @@ error_free_ops: return NULL; } +typedef enum { + IdentResolTy_None, + IdentResolTy_Label, + IdentResolTy_SubLabel, +} IdentResolTy; + +typedef struct IdentResol IdentResol; +struct IdentResol { + char* ident; + Loc loc; + const IdentResol* parent; + IdentResolTy ty; + union { + uint16_t ip; + }; +}; + +void ident_resol_destroy(IdentResol* resol) +{ + switch (resol->ty) { + case IdentResolTy_None: + break; + case IdentResolTy_Label: + case IdentResolTy_SubLabel: + free(resol->ident); + break; + } +} + +typedef struct IdentResolver IdentResolver; + +struct IdentResolver { + IdentResol* resols; + size_t resols_capacity; + size_t resols_size; + const IdentResol* current_parent; +}; + +void ident_resolver_construct(IdentResolver* resolver) +{ + size_t capacity = 512; + *resolver = (IdentResolver) { + .resols = malloc(sizeof(IdentResol) * capacity), + .resols_capacity = capacity, + .resols_size = 0, + }; +} + +void ident_resolver_destroy(IdentResolver* resolver) +{ + for (size_t i = 0; i < resolver->resols_size; ++i) { + ident_resol_destroy(&resolver->resols[i]); + } + free(resolver->resols); +} + +static inline size_t ident_resolver_first_empty(IdentResolver* resolver) +{ + size_t i = 0; + for (; i < resolver->resols_size; ++i) { + if (resolver->resols[i].ty == IdentResolTy_None) { + break; + } + } + if (i >= resolver->resols_size) { + if (resolver->resols_size + 1 > resolver->resols_capacity) { + resolver->resols_capacity *= 2; + resolver->resols = realloc(resolver->resols, + sizeof(IdentResol) * resolver->resols_capacity); + } + resolver->resols_size += 1; + } + return i; +} + +void ident_resolver_define_label( + IdentResolver* resolver, char* ident, Loc loc, uint16_t asm_ip) +{ + size_t i = ident_resolver_first_empty(resolver); + resolver->resols[i] = (IdentResol) { + .ident = ident, + .loc = loc, + .ty = IdentResolTy_Label, + .ip = asm_ip * 2, + }; + resolver->current_parent = &resolver->resols[i]; +} + +void ident_resolver_define_sublabel( + IdentResolver* resolver, char* ident, Loc loc, uint16_t asm_ip) +{ + size_t i = ident_resolver_first_empty(resolver); + resolver->resols[i] = (IdentResol) { + .ident = ident, + .loc = loc, + .parent = resolver->current_parent, + .ty = IdentResolTy_SubLabel, + .ip = asm_ip * 2, + }; +} + +const IdentResol* ident_resolver_resolve( + const IdentResolver* resolver, const char* ident) +{ + for (size_t i = resolver->resols_size; i > 0; --i) { + IdentResol* re = &resolver->resols[i - 1]; + if (re->ty != IdentResolTy_None && strcmp(re->ident, ident) == 0 + && (re->ty != IdentResolTy_SubLabel + || re->parent == resolver->current_parent)) { + return re; + } + } + return NULL; +} + +static inline int define_labels( + IdentResolver* resolver, PLabel* label, uint16_t asm_ip, Reporter* rep) +{ + if (label == NULL) + return 0; + int res = define_labels(resolver, label->next, asm_ip, rep); + const IdentResol* existing = ident_resolver_resolve(resolver, label->ident); + if (existing != NULL) { + REPORTF_ERROR("redefinition of identifier '%s'", label->ident); + reporter_print_loc(rep, label->loc); + REPORTF_INFO("original definition of '%s'", label->ident); + reporter_print_loc(rep, existing->loc); + return 1; + } + if (label->sub_label) { + ident_resolver_define_sublabel( + resolver, asm_strdup(label->ident), label->loc, asm_ip); + } else { + ident_resolver_define_label( + resolver, asm_strdup(label->ident), label->loc, asm_ip); + } + return res; +} + +static inline void use_labels(IdentResolver* resolver, PLabel* label) +{ + if (label == NULL) + return; + use_labels(resolver, label->next); + const IdentResol* existing = ident_resolver_resolve(resolver, label->ident); + if (existing->ty == IdentResolTy_Label) { + resolver->current_parent = existing; + } +} + +typedef enum { + EoTy_Err, + EoTy_Reg, + EoTy_Imm, + EoTy_Str, + EoTy_Mem8Reg, + EoTy_Mem8Imm, + EoTy_MemU16Reg, + EoTy_MemU16Imm, +} EvaledOperandTy; + +typedef struct { + EvaledOperandTy ty; + union { + Reg reg; + uint16_t imm; + }; + uint16_t offset; +} EvaledOperand; + +typedef struct { + IdentResolver* re; + Reporter* rep; + bool unresolve_is_error; +} OperandEvaluator; + +static inline uint16_t eval_poperandty_unary(POperandTy ty, uint16_t operand) +{ + switch (ty) { + case PoTy_Not: + return ~operand; + case PoTy_Negate: + return (uint16_t)-(int16_t)operand; + default: + return 0; + } +} + +static inline uint16_t eval_poperandty_binary( + POperandTy ty, uint16_t left, uint16_t right) +{ + switch (ty) { + case PoTy_Or: + return left | right; + case PoTy_Xor: + return left ^ right; + case PoTy_And: + return left & right; + case PoTy_Shl: + return (uint16_t)(left << right); + case PoTy_Shr: + return (uint16_t)(left >> right); + case PoTy_Add: + return (uint16_t)((int16_t)left + (int16_t)right); + case PoTy_Sub: + return (uint16_t)((int16_t)left - (int16_t)right); + case PoTy_Mul: + return (uint16_t)((int16_t)left * (int16_t)right); + case PoTy_Div: + return (uint16_t)((int16_t)left / (int16_t)right); + case PoTy_Mod: + return (uint16_t)((int16_t)left % (int16_t)right); + default: + return 0; + } +} + +EvaledOperand eval_operand_to_imm( + OperandEvaluator* evaluator, POperand* operand) +{ + switch (operand->ty) { + case PoTy_Str: + REPORTF_ERROR("%s", "strings cannot be part of expressions"); + reporter_print_loc(evaluator->rep, operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + case PoTy_Mem8: + case PoTy_Mem16: + REPORTF_ERROR("%s", "indirections cannot be part of expressions"); + reporter_print_loc(evaluator->rep, operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + case PoTy_Reg: + REPORTF_ERROR("%s", "registers cannot be part of expressions"); + reporter_print_loc(evaluator->rep, operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + case PoTy_Imm: + return (EvaledOperand) { .ty = EoTy_Imm, .imm = operand->imm }; + case PoTy_Ident: + case PoTy_SubLabel: { + const IdentResol* re + = ident_resolver_resolve(evaluator->re, operand->str); + if (re == NULL) { + if (!evaluator->unresolve_is_error) { + return (EvaledOperand) { .ty = EoTy_Imm, .imm = 0 }; + } + REPORTF_ERROR("undefined identifier '%s'", operand->str); + reporter_print_loc(evaluator->rep, operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + } + switch (re->ty) { + case IdentResolTy_None: + break; + case IdentResolTy_Label: + case IdentResolTy_SubLabel: + return (EvaledOperand) { .ty = EoTy_Imm, .imm = re->ip }; + } + fprintf(stderr, "unreachable\n"); + exit(1); + } + case PoTy_Not: + case PoTy_Negate: { + EvaledOperand inner + = eval_operand_to_imm(evaluator, operand->operand); + if (inner.ty == EoTy_Err) { + return inner; + } else if (inner.ty != EoTy_Imm) { + REPORTF_ERROR("%s", "operand cannot be used in expressions"); + reporter_print_loc(evaluator->rep, operand->operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + } + return (EvaledOperand) { + .ty = EoTy_Imm, + .imm = eval_poperandty_unary(operand->ty, inner.imm), + }; + } + 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: { + EvaledOperand left = eval_operand_to_imm(evaluator, operand->left); + if (left.ty == EoTy_Err) { + return left; + } else if (left.ty != EoTy_Imm) { + REPORTF_ERROR("%s", "operand cannot be used in expressions"); + reporter_print_loc(evaluator->rep, operand->left->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + } + EvaledOperand right + = eval_operand_to_imm(evaluator, operand->right); + if (right.ty == EoTy_Err) { + return right; + } else if (right.ty != EoTy_Imm) { + REPORTF_ERROR("%s", "operand cannot be used in expressions"); + reporter_print_loc(evaluator->rep, operand->right->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + } + return (EvaledOperand) { + .ty = EoTy_Imm, + .imm = eval_poperandty_binary(operand->ty, left.imm, right.imm), + }; + } + } + fprintf(stderr, "unreachable\n"); + exit(1); +} + +EvaledOperand eval_operand_indirection_expr( + OperandEvaluator* evaluator, POperand* operand) +{ + switch (operand->ty) { + case PoTy_Reg: + return (EvaledOperand) { + .ty = EoTy_Mem8Reg, + .reg = operand->reg, + .offset = 0, + }; + case PoTy_Str: + REPORTF_ERROR("%s", "strings cannot be part of indirections"); + reporter_print_loc(evaluator->rep, operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + case PoTy_Mem8: + case PoTy_Mem16: + REPORTF_ERROR("%s", "indirections cannot be part of indirections"); + reporter_print_loc(evaluator->rep, operand->loc); + return (EvaledOperand) { .ty = EoTy_Err }; + case PoTy_Imm: + case PoTy_Ident: + case PoTy_SubLabel: + case PoTy_Not: + case PoTy_Negate: + case PoTy_Or: + case PoTy_Xor: + case PoTy_And: + case PoTy_Shl: + case PoTy_Shr: + case PoTy_Mul: + case PoTy_Div: + case PoTy_Mod: { + EvaledOperand evaled = eval_operand_to_imm(evaluator, operand); + if (evaled.ty == EoTy_Err) { + return evaled; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .imm = evaled.imm, + }; + } + case PoTy_Add: { + if (operand->left->ty == PoTy_Reg) { + EvaledOperand right + = eval_operand_to_imm(evaluator, operand->right); + if (right.ty == EoTy_Err) { + return right; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .reg = operand->left->reg, + .offset = right.imm, + }; + } else if (operand->right->ty == PoTy_Reg) { + EvaledOperand left + = eval_operand_to_imm(evaluator, operand->left); + if (left.ty == EoTy_Err) { + return left; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .reg = operand->right->reg, + .offset = left.imm, + }; + } else { + EvaledOperand evaled = eval_operand_to_imm(evaluator, operand); + if (evaled.ty == EoTy_Err) { + return evaled; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .imm = evaled.imm, + }; + } + break; + } + case PoTy_Sub: { + if (operand->left->ty == PoTy_Reg) { + EvaledOperand right + = eval_operand_to_imm(evaluator, operand->right); + if (right.ty == EoTy_Err) { + return right; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .reg = operand->left->reg, + .offset = (uint16_t)-(int16_t)right.imm, + }; + } else if (operand->right->ty == PoTy_Reg) { + EvaledOperand left + = eval_operand_to_imm(evaluator, operand->left); + if (left.ty == EoTy_Err) { + return left; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .reg = operand->right->reg, + .offset = (uint16_t)-(int16_t)left.imm, + }; + } else { + EvaledOperand evaled = eval_operand_to_imm(evaluator, operand); + if (evaled.ty == EoTy_Err) { + return evaled; + } + return (EvaledOperand) { + .ty = EoTy_Mem8Imm, + .imm = evaled.imm, + }; + } + break; + } + } + fprintf(stderr, "unreachable\n"); + exit(1); +} + +EvaledOperand eval_operand(OperandEvaluator* evaluator, POperand* operand) +{ + switch (operand->ty) { + case PoTy_Str: + return (EvaledOperand) { .ty = EoTy_Str }; + case PoTy_Mem8: + return eval_operand_indirection_expr(evaluator, operand->operand); + case PoTy_Mem16: { + EvaledOperand evaled + = eval_operand_indirection_expr(evaluator, operand->operand); + switch (evaled.ty) { + case EoTy_Mem8Reg: + evaled.ty = EoTy_MemU16Reg; + break; + case EoTy_Mem8Imm: + evaled.ty = EoTy_MemU16Imm; + break; + default: + break; + } + return evaled; + } + case PoTy_Reg: + return (EvaledOperand) { .ty = EoTy_Reg, .reg = operand->reg }; + case PoTy_Imm: + case PoTy_Ident: + case PoTy_SubLabel: + case PoTy_Not: + case PoTy_Negate: + 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: + return eval_operand_to_imm(evaluator, operand); + } + fprintf(stderr, "unreachable\n"); + exit(1); +} + +typedef enum { + // clang-format off + 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, M_or, M_xor, M_and, M_shl, M_rshl, + M_shr, M_rshr, M_add, M_sub, M_rsub, M_mul, + M_imul, M_div, M_idiv, M_rdiv, M_ridiv, M_mod, + M_rmod, + // clang-format on +} Mnemonic; + +const char* mnemonic_str[] = { + // clang-format off + "err", "d8", "d16", "nop", "hlt", "jmp", + "jmpf", "jnz", "cmp", "mov", "in", "out", + "call", "callf", "ret", "retf", "lit", "int", + "iret", "or", "xor", "and", "shl", "rshl", + "shr", "rshr", "add", "sub", "rsub", "mul", + "imul", "div", "idiv", "rdiv", "ridiv", "mod", + "rmod", + // clang-format on +}; + +static inline uint16_t pline_assemble(OperandEvaluator* evaluator, + uint16_t* object, + const PLine* line, + Reporter* rep) +{ + +#define CHECK_OPERAND(OP) \ + do { \ + if ((OP).ty == EoTy_Err) { \ + return 0; \ + } \ + } while (0) + +#define ASSEMBLE_ONE(RVAL) \ + do { \ + Line l = (RVAL); \ + return assemble_line(object, &l); \ + } while (0) + + size_t mnemonics_amount = sizeof(mnemonic_str) / sizeof(mnemonic_str[0]); + Mnemonic m = M_err; + for (size_t i = 0; i < mnemonics_amount; ++i) { + if (strcmp(mnemonic_str[i], line->op) == 0) { + m = (Mnemonic)i; + break; + } + } + switch (m) { + case M_err: { + REPORTF_ERROR("unrecognized mnemonic '%s'", line->op); + reporter_print_loc(rep, line->loc); + return 0; + } + case M_d8: { + if (line->ops_size > 64) { + reporter_error_with_loc( + rep, "too many operands (max is 64)", line->loc); + return 0; + } + size_t buffer_capacity = 128; + uint8_t* buffer = malloc(sizeof(uint8_t) * buffer_capacity); + size_t buffer_size = 0; + for (size_t i = 0; i < line->ops_size; ++i) { + EvaledOperand val = eval_operand(evaluator, line->ops[i]); + CHECK_OPERAND(val); + switch (val.ty) { + case EoTy_Imm: + buffer[buffer_size++] = (uint8_t)val.imm; + break; + case EoTy_Str: { + for (size_t si = 0; si < line->ops[i]->str_len; ++si) { + buffer[buffer_size++] + = (uint8_t)line->ops[i]->str[si]; + } + break; + } + default: + reporter_error_with_loc( + rep, "invalid operand", line->ops[i]->loc); + return 0; + } + } + uint16_t ip_diff = 0; + for (size_t i = 0; i < buffer_size; i += 2) { + uint16_t data = 0; + // XXX: little endian + data |= buffer[i]; + if (i + 1 < buffer_size) { + data |= (uint16_t)((uint16_t)buffer[i] << 8); + } + Line l = s_data_i(data); + ip_diff += assemble_line(object, &l); + } + return ip_diff; + } + case M_d16: { + if (line->ops_size > 32) { + reporter_error_with_loc( + rep, "too many operands (max is 32)", line->loc); + return 0; + } + uint16_t ip_diff = 0; + for (size_t i = 0; i < line->ops_size; ++i) { + EvaledOperand val = eval_operand(evaluator, line->ops[i]); + CHECK_OPERAND(val); + switch (val.ty) { + case EoTy_Imm: { + Line l = s_data_i(val.imm); + ip_diff += assemble_line(object, &l); + break; + } + default: + reporter_error_with_loc( + rep, "invalid operand", line->ops[i]->loc); + return 0; + } + } + return ip_diff; + } + case M_nop: + if (line->ops_size == 0) + ASSEMBLE_ONE(s_nop()); + break; + case M_hlt: + if (line->ops_size == 0) + ASSEMBLE_ONE(s_hlt()); + break; + case M_jmp: + if (line->ops_size == 1) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + if (op1.ty == EoTy_Reg) + ASSEMBLE_ONE(s_jmp_r(op1.reg)); + if (op1.ty == EoTy_Imm) + ASSEMBLE_ONE(s_jmp_i(op1.imm)); + } + break; + case M_jmpf: + if (line->ops_size == 2) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op2); + if (op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_jmpf_r_r(op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_jmpf_r_i(op1.reg, op2.imm)); + } + if (op1.ty == EoTy_Imm) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_jmpf_i_r(op1.imm, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_jmpf_i_i(op1.imm, op2.imm)); + } + } + break; + case M_jnz: + if (line->ops_size == 2) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op2); + if (op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_jnz_r(op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_jnz_i(op1.reg, op2.imm)); + } + } + break; + case M_cmp: + if (line->ops_size == 2) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op2); + if (op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_cmp_r(op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_cmp_i(op1.reg, op2.imm)); + } + } + break; + case M_mov: + if (line->ops_size == 2) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op2); + if (op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_mov16_r_r(op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_mov16_r_i(op1.reg, op2.imm)); + if (op2.ty == EoTy_Mem8Reg) + ASSEMBLE_ONE(s_mov8_r_mr(op1.reg, op2.reg, op2.offset)); + if (op2.ty == EoTy_Mem8Imm) + ASSEMBLE_ONE(s_mov8_r_mi(op1.reg, op2.imm)); + if (op2.ty == EoTy_MemU16Reg) + ASSEMBLE_ONE( + s_mov16_r_mr(op1.reg, op2.reg, op2.offset)); + if (op2.ty == EoTy_MemU16Imm) { + ASSEMBLE_ONE(s_mov16_r_mi(op1.reg, op2.imm)); + } + } + if (op1.ty == EoTy_Mem8Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_mov8_mr_r(op1.reg, op1.offset, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_mov8_mr_i(op1.reg, op1.offset, op2.imm)); + } + if (op1.ty == EoTy_Mem8Imm) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_mov8_mi_r(op1.imm, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_mov8_mi_i(op1.imm, op2.imm)); + } + if (op1.ty == EoTy_MemU16Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE( + s_mov16_mr_r(op1.reg, op1.offset, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE( + s_mov16_mr_i(op1.reg, op1.offset, op2.imm)); + } + if (op1.ty == EoTy_MemU16Imm) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_mov16_mi_r(op1.imm, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_mov16_mi_i(op1.imm, op2.imm)); + } + } + break; + case M_in: + if (line->ops_size == 2) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + if (dst.ty == EoTy_Reg) { + if (op1.ty == EoTy_Reg) + ASSEMBLE_ONE(s_in_r(dst.reg, op1.reg)); + if (op1.ty == EoTy_Imm) + ASSEMBLE_ONE(s_in_i(dst.reg, op1.imm)); + } + } + break; + case M_out: + if (line->ops_size == 2) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op2); + if (op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_out_r_r(op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_out_r_i(op1.reg, op2.imm)); + } + if (op1.ty == EoTy_Imm) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_out_i_r(op1.imm, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_out_i_i(op1.imm, op2.imm)); + } + } + break; + case M_call: + if (line->ops_size == 1) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + if (op1.ty == EoTy_Reg) + ASSEMBLE_ONE(s_call_r(op1.reg)); + if (op1.ty == EoTy_Imm) + ASSEMBLE_ONE(s_call_i(op1.imm)); + } + break; + case M_callf: + if (line->ops_size == 2) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op2); + if (op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_callf_r_r(op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_callf_r_i(op1.reg, op2.imm)); + } + if (op1.ty == EoTy_Imm) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_callf_i_r(op1.imm, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_callf_i_i(op1.imm, op2.imm)); + } + } + break; + case M_ret: + if (line->ops_size == 0) { + ASSEMBLE_ONE(s_ret()); + } + break; + case M_retf: + if (line->ops_size == 0) { + ASSEMBLE_ONE(s_retf()); + } + break; + case M_lit: + if (line->ops_size == 1) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + if (op1.ty == EoTy_Reg) + ASSEMBLE_ONE(s_lit_r(op1.reg)); + if (op1.ty == EoTy_Imm) + ASSEMBLE_ONE(s_lit_i(op1.imm)); + } + break; + case M_int: + if (line->ops_size == 1) { + EvaledOperand op1 = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(op1); + if (op1.ty == EoTy_Imm) { + if (op1.imm > 0xff) { + reporter_error_with_loc( + rep, "interrupt id exceeds 1 byte", line->loc); + return 0; + } + ASSEMBLE_ONE(s_int((uint8_t)op1.imm)); + } + } + break; + case M_iret: + if (line->ops_size == 0) { + ASSEMBLE_ONE(s_iret()); + } + break; + case M_or: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_or_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) { + ASSEMBLE_ONE(s_or_i(dst.reg, op1.reg, op2.imm)); + } + } + } + break; + case M_xor: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_xor_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_xor_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_and: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_and_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_and_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_shl: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_shl_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_shl_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_rshl: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_rshl_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_rshl_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_shr: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_shr_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_shr_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_rshr: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_rshr_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_rshr_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_add: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_add_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_add_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_sub: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_sub_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_sub_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_rsub: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_rsub_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_rsub_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_mul: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_mul_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_mul_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_imul: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_imul_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_imul_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_div: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_div_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_div_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_idiv: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_idiv_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_idiv_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_rdiv: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_rdiv_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_rdiv_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_ridiv: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_ridiv_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_ridiv_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_mod: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_mod_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_mod_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + case M_rmod: + if (line->ops_size == 3) { + EvaledOperand dst = eval_operand(evaluator, line->ops[0]); + CHECK_OPERAND(dst); + EvaledOperand op1 = eval_operand(evaluator, line->ops[1]); + CHECK_OPERAND(op1); + EvaledOperand op2 = eval_operand(evaluator, line->ops[2]); + CHECK_OPERAND(op2); + if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) { + if (op2.ty == EoTy_Reg) + ASSEMBLE_ONE(s_rmod_r(dst.reg, op1.reg, op2.reg)); + if (op2.ty == EoTy_Imm) + ASSEMBLE_ONE(s_rmod_i(dst.reg, op1.reg, op2.imm)); + } + } + break; + break; + break; + } + reporter_error_with_loc(rep, "malformed instruction", line->loc); + return 0; + +#undef CHECK_OPERAND +#undef ASSEMBLE_ONE +} + typedef struct { const char* input_file; const char* output_file; @@ -843,11 +2004,13 @@ static inline Args parse_args(int argc, char** argv); int main(int argc, char** argv) { + int res = 0; + Args args = parse_args(argc, argv); FILE* input_fp = fopen(args.input_file, "r"); if (!input_fp) { - REPORTF("could not open input file '%s': %s", + REPORTF_ERROR("could not open input file '%s': %s", args.input_file, strerror(errno)); return -1; @@ -860,27 +2023,121 @@ int main(int argc, char** argv) size_t bytes_read = fread(input_text, sizeof(char), file_size, input_fp); fclose(input_fp); if (bytes_read != file_size) { - REPORTF("could not read input file'%s': %s", + REPORTF_ERROR("could not read input file '%s': %s", args.input_file, strerror(errno)); return -1; } + bool errors_occured = false; + Parser parser; parser_construct(&parser, args.input_file, input_text); + Reporter rep = { + .filename = parser.lexer.filename, + .text = parser.lexer.text, + .text_len = parser.lexer.text_len, + }; + + size_t lines_capacity = 1024; + PLine** lines = malloc(sizeof(PLine*) * lines_capacity); + size_t lines_size = 0; + while (!parser_done(&parser)) { + if (lines_size + 1 > lines_capacity) { + lines_capacity += 2; + lines = realloc(lines, sizeof(PLine*) * lines_capacity); + } PLine* line = parser_next(&parser); if (!line) { - break; - } - pline_free(line); - if (parser_error_occured(&parser)) { - break; + continue; } + lines[lines_size++] = line; } + errors_occured &= parser_error_occured(&parser); + + IdentResolver resolver; + ident_resolver_construct(&resolver); + + OperandEvaluator evaluator = { + .re = &resolver, + .rep = &rep, + .unresolve_is_error = false, + }; + + size_t chunk_capacity = 64; + uint16_t* chunk = malloc(sizeof(uint16_t) * chunk_capacity); + + uint16_t ip = 0; + for (size_t i = 0; i < lines_size; ++i) { + int res = define_labels(&resolver, lines[i]->labels, ip, &rep); + if (res != 0) + errors_occured = true; + ip += pline_assemble(&evaluator, chunk, lines[i], &rep); + if (ip == 0) + errors_occured = true; + } + + if (errors_occured) { + fprintf(stderr, "nothing written. stopping...\n"); + res = -1; + goto leave_free_chunk; + } + + evaluator.unresolve_is_error = true; + + FILE* output_fp = fopen(args.output_file, "wb"); + if (!output_fp) { + REPORTF_ERROR("could not open output file '%s': %s", + args.output_file, + strerror(errno)); + res = -1; + goto leave_free_chunk; + } + + size_t total_bytes_written = 0; + for (size_t i = 0; i < lines_size; ++i) { + use_labels(&resolver, lines[i]->labels); + + size_t size = pline_assemble(&evaluator, chunk, lines[i], &rep); + if (size == 0) { + errors_occured = true; + } + + if (errors_occured) + continue; + + size_t bytes_written = fwrite(chunk, sizeof(uint16_t), size, output_fp); + total_bytes_written += bytes_written; + if (bytes_written != size) { + REPORTF_ERROR("could not write to output file '%s': %s", + args.output_file, + strerror(errno)); + errors_occured = true; + } + } + fclose(output_fp); + + if (errors_occured) { + fprintf( + stderr, "%ld bytes written. stopping...\n", total_bytes_written); + res = -1; + goto leave_free_chunk; + } + + res = 0; +leave_free_chunk: + free(chunk); + // leave_free_lines: + for (size_t i = 0; i < lines_size; ++i) { + pline_free(lines[i]); + } + free(lines); free(input_text); + ident_resolver_destroy(&resolver); + return res; } static inline Args parse_args(int argc, char** argv) @@ -891,20 +2148,20 @@ static inline Args parse_args(int argc, char** argv) if (strcmp(argv[i], "-o") == 0) { i += 1; if (i >= argc) { - REPORTF("%s", "no filename given to -o"); + REPORTF_ERROR("%s", "no filename given to -o"); exit(1); } output_file = argv[i]; } else { if (input_file != NULL) { - REPORTF("%s", "multiple input files specified"); + REPORTF_ERROR("%s", "multiple input files specified"); exit(1); } input_file = argv[i]; } } if (input_file == NULL) { - REPORTF("%s", "no input file"); + REPORTF_ERROR("%s", "no input file"); exit(1); } if (output_file == NULL) { diff --git a/kern/main.asm b/kern/main.asm index 338d777..bd02b8e 100644 --- a/kern/main.asm +++ b/kern/main.asm @@ -5,13 +5,13 @@ start: mov rsp, 2048 - 2 lit interrupt_table - or rfl, rfl, 1 << Fl_Int + or rfl, rfl, 1 << 5 ; Fl_Int - or rfl, Rfl, 1 << Fl_Vcd + or rfl, rfl, 1 << 6 ; Fl_Vcd - mov16 r0, 512 - mov16 r1, 1 - int Int_DiskRead + mov r0, 512 + mov r1, 1 + int 0 ; Int_DiskRead main_loop: hlt @@ -20,15 +20,15 @@ main_loop: interrupt_table: ; size d16 1 - data keyboard_interrupt + d16 keyboard_interrupt nop keyboard_interrupt: - and rfl, rfl, !(1 << Fl_Int) + and rfl, rfl, !(1 << 5) ; Fl_Int ; push rbp add rsp, rsp, 2 mov u16 [rsp], rbp - mov rbp, Rsp + mov rbp, rsp ; push r0 add rsp, rsp, 2 mov u16 [rsp], r0 @@ -42,51 +42,51 @@ keyboard_interrupt: add rsp, rsp, 2 mov u16 [rsp], r3 - in r0, Device_Keyboard + in r0, 0 ; Device_Keyboard cmp r0, 44 mov r1, rfl - and r1, r1, 1 << Fl_Eq + and r1, r1, 1 << 1 ; Fl_Eq jnz r1, .L0 cmp r0, 42 - mov16 r1, rfl - and r1, r1, 1 << Fl_Eq + mov r1, rfl + and r1, r1, 1 << 1 ; Fl_Eq jnz r1, .L1 cmp r0, 40 mov r1, rfl - and r1, r1, 1 << Fl_Eq + and r1, r1, 1 << 1 ; Fl_Eq jnz r1, .L2 jmp .L3 .L0: - mov R0, ' ' + mov r0, ' ' call put_char jmp .L4 .L1: - mov r1, screen_x + mov r1, u16 [screen_x] cmp r1, 0 mov r2, rfl - and r2, r2, 1 << Fl_Eq + and r2, r2, 1 << 1 ; Fl_Eq jnz r2, .L4 sub r1, r1, 1 - mov screen_x, R1 - ; mov r0, ' ' + mov u16 [screen_x], r1 + mov r0, ' ' call put_char - mov16 r1, screen_x + mov r1, u16 [screen_x] sub r1, r1, 1 - mov screen_x, R1 + mov u16 [screen_x], r1 jmp .L4 .L2: - mov r1, screen_y + mov r1, u16 [screen_y] add r1, r1, 1 - mov screen_y, R1 + mov u16 [screen_y], r1 mov r1, 0 - mov screen_x, R1 + mov u16 [screen_x], r1 jmp .L4 .L3: @@ -112,7 +112,7 @@ keyboard_interrupt: ; pop rbp mov rbp, u16 [rsp] sub rsp, rsp, 2 - or rfl, rfl, 1 << Fl_Int + or rfl, rfl, 1 << 5 ; Fl_Int iret put_char: @@ -127,29 +127,29 @@ put_char: add rsp, rsp, 2 mov u16 [rsp], r2 - mov r2, screen_y - mul r2, r2, vcd_width_in_ch - mov r1, screen_x + mov r2, u16 [screen_y] + mul r2, r2, 40 ; vcd_width_in_ch + mov r1, u16 [screen_x] add r1, r1, 0x0c00 add r1, r1, r2 - mov r1, r0 + mov u8 [r1], r0 - mov r1, screen_x + mov r1, u16 [screen_x] add r1, r1, 1 - mov screen_x, r1 + mov u16 [screen_x], r1 - cmp r1, vcd_width_in_ch + cmp r1, 40 ; vcd_width_in_ch mov r2, rfl - and r2, r2, 1 << Fl_Eq + and r2, r2, 1 << 1 ; Fl_Eq jnz r2, .L0 jmp .L1 .L0: - mov r1, screen_y + mov r1, u16 [screen_y] add r1, r1, 1 - mov screen_y, r1 + mov u16 [screen_y], r1 mov r1, 0 - mov screen_x, r1 + mov u16 [screen_x], r1 .L1: ; pop r1 diff --git a/kern/main.c b/kern/main.c index ece9893..0bb4f27 100644 --- a/kern/main.c +++ b/kern/main.c @@ -162,7 +162,7 @@ void write_program(FILE* fp) s_mov16_r_ml(R1, screen_x), s_add_i(R1, R1, 0x0c00), s_add_r(R1, R1, R2), - s_mov8_mr_r(R1, R0), + s_mov8_mr_r(R1, 0, R0), s_mov16_r_ml(R1, screen_x), s_add_i(R1, R1, 1), diff --git a/vm/main.c b/vm/main.c index f192a77..01fa12c 100644 --- a/vm/main.c +++ b/vm/main.c @@ -413,8 +413,16 @@ __attribute__((unused)) static inline void dump_program(uint16_t* program) } } -int main(void) +typedef struct { + const char* disk_file; +} Args; + +static inline Args parse_args(int argc, char** argv); + +int main(int argc, char** argv) { + Args args = parse_args(argc, argv); + int res; int label_ids = 0; @@ -425,10 +433,12 @@ int main(void) exit(1); } - FILE* fp = fopen("build/image", "rb"); + FILE* fp = fopen(args.disk_file, "rb"); if (!fp) { - fprintf( - stderr, "error: could not open build/image: %s\n", strerror(errno)); + fprintf(stderr, + "error: could not open %s: %s\n", + args.disk_file, + strerror(errno)); exit(1); } @@ -441,6 +451,28 @@ int main(void) sdldevice_destroy(&io_device); } +static inline Args parse_args(int argc, char** argv) +{ + const char* disk_file = "build/image"; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--disk-file") == 0) { + if (i + 1 >= argc) { + fprintf( + stderr, "error: expected filename after '%s'\n", argv[i]); + exit(1); + } + disk_file = argv[i + 1]; + i += 1; + } else { + fprintf(stderr, "error: unrecognized argument '%s'\n", argv[i]); + exit(1); + } + } + return (Args) { + disk_file, + }; +} + const char* __asan_default_options(void) { return "detect_leaks=0"; diff --git a/vm/vm.c b/vm/vm.c index a5270b2..5a5827f 100644 --- a/vm/vm.c +++ b/vm/vm.c @@ -222,9 +222,9 @@ void vm_start(Drive* boot_drive, IODevice* io_device) break; } case Op_Out: { - uint16_t op1 = ins_op2(vm, ins); uint16_t device_id = ins_op1_or_imm(vm, ins); - handle_device_write(vm, op1, device_id); + uint16_t op2 = ins_reg_val_or_imm(vm, ins, 7, 13, 0x7); + handle_device_write(vm, op2, device_id); break; } case Op_Call: {