diff --git a/crater.c b/crater.c index 2783c86..5279035 100644 --- a/crater.c +++ b/crater.c @@ -33,9 +33,9 @@ int main(int argc, char *argv[]) config->assemble ? "assembler" : "disassembler", config->src_path, config->dst_path); if (config->assemble) - retval = assemble(config->src_path, config->dst_path); + retval = assemble_file(config->src_path, config->dst_path); else - retval = disassemble(config->src_path, config->dst_path); + retval = disassemble_file(config->src_path, config->dst_path); retval = retval ? EXIT_SUCCESS : EXIT_FAILURE; } else { ROM *rom; diff --git a/src/assembler.c b/src/assembler.c index ae5446f..e5d8d12 100644 --- a/src/assembler.c +++ b/src/assembler.c @@ -1,16 +1,180 @@ /* Copyright (C) 2014-2015 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ +#include +#include +#include +#include +#include + #include "assembler.h" +#include "logging.h" + +/* + Deallocate a LineBuffer previously created with read_source_file(). +*/ +static void free_line_buffer(LineBuffer *buffer) +{ + for (size_t i = 0; i < buffer->length; i++) + free(buffer->lines[i].data); + free(buffer->lines); + free(buffer); +} + +/* + Read the contents of the source file at the given path into a line buffer. + + Return the buffer if reading was successful; it must be freed with + free_line_buffer() when done. Return NULL if an error occurred while + reading. A message will be printed to stderr in this case. +*/ +static LineBuffer* read_source_file(const char *path) +{ + FILE* fp; + struct stat st; + + if (!(fp = fopen(path, "r"))) { + ERROR_ERRNO("couldn't open source file") + return NULL; + } + + if (fstat(fileno(fp), &st)) { + fclose(fp); + ERROR_ERRNO("couldn't open source file") + return NULL; + } + if (!(st.st_mode & S_IFREG)) { + fclose(fp); + ERROR("couldn't open source file: %s", st.st_mode & S_IFDIR ? + "Is a directory" : "Is not a regular file") + return NULL; + } + + size_t capacity = 16; + LineBuffer *source = malloc(sizeof(LineBuffer)); + if (!source) + OUT_OF_MEMORY() + + source->length = 0; + source->lines = malloc(sizeof(Line) * capacity); + if (!source->lines) + OUT_OF_MEMORY() + + while (1) { + char *line = NULL; + size_t lcap = 0; + ssize_t len; + + if ((len = getline(&line, &lcap, fp)) < 0) { + if (feof(fp)) + break; + if (errno == ENOMEM) + OUT_OF_MEMORY() + ERROR_ERRNO("couldn't read source file") + free_line_buffer(source); + source = NULL; + break; + } + + if (capacity <= source->length + 1) { + capacity <<= 2; + source->lines = realloc(source->lines, sizeof(Line) * capacity); + if (!source->lines) + OUT_OF_MEMORY() + } + + source->lines[source->length++] = (Line) {line, len}; + if (feof(fp)) { + source->lines[source->length].length--; + break; + } + } + + fclose(fp); + return source; +} + +/* + Write an assembled binary file to the given path. + + Return whether the file was written successfully. On error, a message is + printed to stderr. +*/ +static bool write_binary_file(const char *path, const uint8_t *data, size_t size) +{ + // TODO + return false; +} /* - Assemble the z80 source code at the input path into a binary. + Print an ErrorInfo object returned by assemble() to the given file. + + The same LineBuffer passed to assemble() should be passed to this function. + Passing NULL if it is unavailable will still work, but source code snippets + where errors were noted will not be printed. +*/ +void error_info_print(const ErrorInfo *error_info, FILE *file, const LineBuffer *source) +{ + // TODO +} + +/* + Destroy an ErrorInfo object created by assemble(). +*/ +void error_info_destroy(ErrorInfo *error_info) +{ + if (!error_info) + return; + + // TODO + free(error_info); +} + +/* + Assemble the z80 source code in the source code buffer into binary data. + + If successful, return the size of the assembled binary data and change + *binary_ptr to point to the assembled ROM data buffer. *binary_ptr must be + free()'d when finished. + + If an error occurred, return 0 and update *ei_ptr to point to an ErrorInfo + object which can be shown to the user with error_info_print(). The + ErrorInfo object must be destroyed with error_info_destroy() when finished. + + In either case, only one of *binary_ptr and *ei_ptr is modified. +*/ +size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_ptr) +{ + // TODO + return 0; +} + +/* + Assemble the z80 source code at the input path into a binary file. Return true if the operation was a success and false if it was a failure. Errors are printed to STDOUT; if the operation was successful then nothing is printed. */ -bool assemble(const char* src_path, const char* dst_path) +bool assemble_file(const char *src_path, const char *dst_path) { - return true; + LineBuffer *source = read_source_file(src_path); + if (!source) + return false; + + uint8_t *binary; + ErrorInfo *error_info; + size_t size = assemble(source, &binary, &error_info); + + if (!size) { + error_info_print(error_info, stderr, source); + error_info_destroy(error_info); + free_line_buffer(source); + return false; + } + + bool success = write_binary_file(dst_path, binary, size); + free(binary); + free_line_buffer(source); + return success; } diff --git a/src/assembler.h b/src/assembler.h index 75c6d48..4c55e00 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -4,11 +4,27 @@ #pragma once #include +#include /* Structs */ -// ... +typedef struct { + char *data; + size_t length; +} Line; + +typedef struct { + Line *lines; + size_t length; +} LineBuffer; + +typedef struct { + // +} ErrorInfo; /* Functions */ -bool assemble(const char*, const char*); +void error_info_print(const ErrorInfo*, FILE*, const LineBuffer*); +void error_info_destroy(ErrorInfo*); +size_t assemble(const LineBuffer*, uint8_t**, ErrorInfo**); +bool assemble_file(const char*, const char*); diff --git a/src/disassembler.c b/src/disassembler.c index 2f4fb7f..c75561b 100644 --- a/src/disassembler.c +++ b/src/disassembler.c @@ -10,7 +10,7 @@ Errors are printed to STDOUT; if the operation was successful then nothing is printed. */ -bool disassemble(const char* src_path, const char* dst_path) +bool disassemble_file(const char *src_path, const char *dst_path) { return true; } diff --git a/src/disassembler.h b/src/disassembler.h index 1343e10..d56db3e 100644 --- a/src/disassembler.h +++ b/src/disassembler.h @@ -11,4 +11,4 @@ /* Functions */ -bool disassemble(const char*, const char*); +bool disassemble_file(const char*, const char*);