vc3/asm/main.c
2025-04-02 18:39:19 +02:00

260 lines
7.2 KiB
C

#include "asm/report.h"
#include "assemble.h"
#include "eval.h"
#include "parse.h"
#include "resolve.h"
#include "str.h"
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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 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)
{
int res = 0;
Args args = parse_args(argc, argv);
FILE* input_fp = fopen(args.input_file, "r");
if (!input_fp) {
REPORTF_ERROR("could not open input file '%s': %s",
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) {
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,
};
IdentResolver resolver;
ident_resolver_construct(&resolver);
OperandEvaluator evaluator = {
.re = &resolver,
.rep = &rep,
.second_pass = false,
};
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);
}
PStmt* stmt = parser_next_stmt(&parser);
if (!stmt) {
continue;
}
switch (stmt->ty) {
case PStmtTy_Line:
lines[lines_size++] = stmt->line;
// NOTE: Shallow free.
free(stmt);
break;
case PStmtTy_Global:
pstmt_free(stmt);
break;
case PStmtTy_Extern:
pstmt_free(stmt);
break;
case PStmtTy_Const: {
const IdentResol* existing
= ident_resolver_resolve(&resolver, stmt->ident);
if (existing != NULL) {
REPORTF_ERROR("redefinition of constant '%s'", stmt->ident);
reporter_print_loc(&rep, stmt->loc);
pstmt_free(stmt);
continue;
}
EvaledOperand evaled
= eval_operand_to_imm(&evaluator, stmt->value);
if (evaled.ty == EoTy_Err) {
pstmt_free(stmt);
continue;
}
ident_resolver_define_const(
&resolver, asm_strdup(stmt->ident), stmt->loc, evaled.imm);
pstmt_free(stmt);
break;
}
}
}
errors_occured &= parser_error_occured(&parser);
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;
uint16_t size = pline_assemble(&evaluator, chunk, lines[i], &rep);
if (size == 0)
errors_occured = true;
ip += size;
}
if (errors_occured) {
fprintf(stderr, "nothing written. stopping...\n");
res = -1;
goto leave_free_chunk;
}
evaluator.second_pass = 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)
{
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) {
REPORTF_ERROR("%s", "no filename given to -o");
exit(1);
}
output_file = argv[i];
} else {
if (input_file != NULL) {
REPORTF_ERROR("%s", "multiple input files specified");
exit(1);
}
input_file = argv[i];
}
}
if (input_file == NULL) {
REPORTF_ERROR("%s", "no input file");
exit(1);
}
if (output_file == NULL) {
output_file = "out.o";
}
return (Args) {
input_file,
output_file,
};
}