Minor changes to `nedasm`. Updated usage and wrapped long line.
[ned1] / nedasm / nedasm.c
/*
* © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "nedasm_structures.h"
#include "nedasm_misc.h"
#include "nedasm_parser.h"
#include "nedasm_codegen.h"
#define VERSION 3
void
print_usage(char ** argv)
{
printf( "NED Assembler v%d (www.subgeniuskitty.com)\n"
"Usage: %s -i <file> -o <file>\n"
" -h Help (prints this message)\n"
" -i <file> Specify file name of assembly code input.\n"
" -o <file> Specify file name for assembled output.\n"
, VERSION, argv[0]
);
}
void
enforce_word_boundary(struct instruction * instructions)
{
uint8_t syllable_counter = 0;
struct instruction * tmp_instructions = instructions;
tmp_instructions = seek_instruction_list_start(tmp_instructions);
while (tmp_instructions != NULL) {
if (tmp_instructions->syllable == WORD) {
if (syllable_counter) insert_NOP_structs_before(tmp_instructions, (5-syllable_counter));
syllable_counter = 0;
} else if (tmp_instructions->syllable == LABEL) {
struct instruction * temp = tmp_instructions;
while (temp->syllable == LABEL) temp = temp->prev;
if (syllable_counter) insert_NOP_structs_after(temp, (5-syllable_counter));
syllable_counter = 0;
} else if (tmp_instructions->syllable != WORD) {
syllable_counter = (syllable_counter + 1) % 5;
}
tmp_instructions = tmp_instructions->next;
}
if (syllable_counter != 0) {
instructions = seek_instruction_list_end(instructions);
insert_NOP_structs_after(instructions, (5-syllable_counter));
}
}
void
assign_addresses(struct instruction * instructions)
{
instructions = seek_instruction_list_start(instructions);
uint32_t offset = 0;
uint8_t increment = 4; /* Bytes per word */
while (instructions != NULL) {
if (instructions->syllable == LABEL) {
instructions = instructions->next;
} else {
if (instructions->syllable == WORD) {
instructions->address = MEM_BEGIN + offset;
instructions = instructions->next;
} else {
for (int i=0; i<5; i++) {
instructions->address = MEM_BEGIN + offset;
instructions = instructions->next;
}
}
offset += increment;
}
}
}
void
resolve_labels(struct instruction * instructions)
{
instructions = seek_instruction_list_start(instructions);
while (instructions != NULL) {
if (instructions->syllable == WORD && instructions->target != NULL) {
struct instruction * temp = seek_instruction_list_start(instructions);
while (temp != NULL) {
if (temp->syllable == LABEL) {
if (strncmp(instructions->target, temp->label, MAX_LABEL_LEN) == 0) {
while ((temp->syllable == LABEL) && (temp->next != NULL)) {
temp = temp->next;
}
if (temp->syllable != LABEL) {
instructions->data = temp->address;
break;
}
}
}
temp = temp->next;
}
if (instructions->data == 0) {
fprintf(stderr,"ERROR: Failed to resolve label %s on line %u.\n",
instructions->target, instructions->linenum);
exit(EXIT_FAILURE);
}
}
instructions = instructions->next;
}
}
void
prune_label_structs(struct instruction * instructions)
{
instructions = seek_instruction_list_start(instructions);
while (instructions != NULL) {
if (instructions->syllable == LABEL) {
instructions = remove_instruction_struct(instructions);
}
instructions = instructions->next;
}
}
void
catch_double_label_declarations(struct instruction * instructions)
{
instructions = seek_instruction_list_start(instructions);
while (instructions != NULL) {
if (instructions->syllable == LABEL) {
struct instruction * tmp_instructions = instructions->next;
while (tmp_instructions != NULL) {
if (tmp_instructions->syllable == LABEL) {
if (strncmp(instructions->label,
tmp_instructions->label, MAX_LABEL_LEN) == 0
) {
fprintf(stderr,
"ERROR: Multiple declarations of label %s on lines %d and %d.\n",
instructions->label, instructions->linenum,
tmp_instructions->linenum
);
exit(EXIT_FAILURE);
}
}
tmp_instructions = tmp_instructions->next;
}
}
instructions = instructions->next;
}
}
int
main(int argc, char ** argv)
{
/*
* Process command line arguments
*/
int c = -1;
FILE * input = NULL;
FILE * output = NULL;
while ((c = getopt(argc,argv,"i:o:h")) != -1) {
switch (c) {
case 'i':
if ((input = fopen(optarg, "r")) == NULL) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
}
break;
case 'o':
/* TODO: Don't create the output file until we know the input was valid. */
if ((output = fopen(optarg, "wx")) == NULL) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
}
break;
case 'h':
print_usage(argv);
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
if (input == NULL || output == NULL) {
fprintf(stderr, "ERROR: Must specify input and output files.\n");
print_usage(argv);
exit(EXIT_FAILURE);
}
/*
* Data structure for intermediate representation.
*/
struct instruction * instructions = create_instruction_struct();
instructions->syllable = NOP;
/*
* Parse input file and generate intermediate representation.
*/
parse_assembly(&instructions, input);
fclose(input);
/*
* Make passes over the intermediate representation to enforce architecture
* rules and prepare for code generation.
*/
catch_double_label_declarations(instructions);
enforce_word_boundary(instructions);
assign_addresses(instructions);
resolve_labels(instructions);
generate_code(instructions, output);
/*
* Cleanup and exit
*/
fclose(output);
exit(EXIT_SUCCESS);
}