dbltw/compiler/main.c
2020-05-13 19:08:25 -05:00

1088 lines
26 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "utlist.h"
#include "../vm/vm.h"
// /home/scott/joey/rpgengine/dbltw/test.dbl /home/scott/joey/rpgengine/dbltw/test.bin /home/scott/joey/rpgengine/dbltw/test.lst
#define MAX_KEYWORD_ARGS 8
#define MAX_ARG_SIZE 1024
#define MAX_LINE_SIZE 8192
#define STATE_KEYWORD 1
#define STATE_ARGUMENT 2
#define QUOTE_NONE 1
#define QUOTE_SINGLE 2
#define QUOTE_DOUBLE 3
typedef int (*keywordHandler)(int argc, char *argv[], char *file, int line);
typedef struct {
char *keyword;
int argc; // Negative values represent the minimum required
keywordHandler handler;
} KeywordT;
typedef struct {
char *opcode;
int argc;
int stackPop;
int stackPush;
unsigned char value;
} OpcodeT;
typedef struct {
int16_t value;
int labelLine;
char *labelFile;
char *text;
} StackT;
typedef struct variableT_ {
int16_t index;
unsigned char exported;
char *name;
struct variableT_ *next;
} VariableT;
typedef struct labelT_ {
uint16_t addr;
unsigned char exported;
int line;
char *name;
char *file;
struct labelT_ *next;
} LabelT;
typedef struct defineT_ {
char *name;
char *value;
struct defineT_ *next;
} DefineT;
typedef struct bitstreamT_ {
uint16_t addr;
unsigned char opcode;
StackT *argument;
struct bitstreamT_ *next;
} BitstreamT;
int argIsNumber(char *arg);
int argIsString(char *arg);
int argIsVariable(char *arg);
void emitOpcode(char *opcode);
void emitStackItem(StackT *item);
int16_t findVariable(char *varName);
int kw_add(int argc, char *argv[], char *file, int line);
int kw_call(int argc, char *argv[], char *file, int line);
int kw_define(int argc, char *argv[], char *file, int line);
int kw_do(int argc, char *argv[], char *file, int line);
int kw_end(int argc, char *argv[], char *file, int line);
int kw_export(int argc, char *argv[], char *file, int line);
int kw_goto(int argc, char *argv[], char *file, int line);
int kw_if(int argc, char *argv[], char *file, int line);
int kw_include(int argc, char *argv[], char *file, int line);
int kw_return(int argc, char *argv[], char *file, int line);
int kw_set(int argc, char *argv[], char *file, int line);
int kw_subtract(int argc, char *argv[], char *file, int line);
void op_add(void);
void op_boc(void);
void op_exe(void);
void op_jmp(void);
void op_hlt(void);
void op_pop(char *varName);
void op_puc(StackT *arg);
void op_puv(int16_t varIndex);
void op_sub(void);
int parse(char *sourceIn);
void push(char *arg, unsigned char isLabel, char *file, int line);
void strReplace(char *target, const char *needle, const char *replacement);
static KeywordT keywords[] = {
{ "add", 2, kw_add },
{ "call", 1, kw_call },
{ "define", 2, kw_define },
{ "do", -1, kw_do },
{ "end", 0, kw_end },
{ "export", 2, kw_export },
{ "goto", 1, kw_goto },
{ "if", 4, kw_if },
{ "include", 1, kw_include },
{ "return", 0, kw_return },
{ "set", 2, kw_set },
{ "subtract", 2, kw_subtract }
};
static OpcodeT opcodes[] = { // Note: There is no zero opcode!
{ "ADD", 0, 2, 1, OPCODE_ADD }, // Add
{ "BOC", 0, 4, 0, OPCODE_BOC }, // Branch On Condition
{ "EXE", 0, -1, -1, OPCODE_EXE }, // Execute VM Function (Unknown stack results) ***TODO*** Change to be entirely stack based
{ "JMP", 0, 1, 0, OPCODE_JMP }, // Jump to address
{ "HLT", 0, 0, 0, OPCODE_HLT }, // Halt Execution
{ "POP", 1, 1, 0, OPCODE_POP }, // Pop into Variable
{ "PUC", 1, 0, 1, OPCODE_PUC }, // Push Constant
{ "PUV", 1, 0, 1, OPCODE_PUV }, // Push Variable
{ "SUB", 0, 2, 1, OPCODE_SUB } // Subtract
};
static LabelT *labels;
static DefineT *defines;
static VariableT *variables;
static uint16_t variableCount; // Variables declared
static uint16_t pc; // Program Counter
static int sloc; // Source Lines of Code
static int files; // Files compiled
static int opCount; // Operands emitted
static int labelCount; // Labels found
static BitstreamT *bitstream;
int argIsNumber(char *arg) {
if (((arg[0] >= '0') && (arg[0] <= '9')) || (arg[0] == '-')) {
return 1;
}
return 0;
}
int argIsString(char *arg) {
if ((arg[0] == '\'') || (arg[0] == '"')) {
return 1;
}
return 0;
}
int argIsVariable(char *arg) {
return !argIsNumber(arg) && !argIsString(arg);
}
void emitOpcode(char *opcode) {
BitstreamT *bits;
for (int i=0; i<(int)(sizeof(opcodes)/sizeof(OpcodeT)); i++) {
if (strcmp(opcode, opcodes[i].opcode) == 0) {
bits = (BitstreamT *)malloc(sizeof(BitstreamT));
bits->addr = pc;
bits->opcode = opcodes[i].value;
bits->argument = NULL;
LL_APPEND(bitstream, bits);
pc++; // Opcodes are a single byte
opCount++;
}
}
}
void emitStackItem(StackT *item) {
BitstreamT *bits;
bits = (BitstreamT *)malloc(sizeof(BitstreamT));
bits->addr = pc;
bits->opcode = 0;
bits->argument = (StackT *)malloc(sizeof(StackT));
bits->argument->value = item->value;
if (item->text == NULL) {
bits->argument->text = NULL;
pc++; // Non string values still get a 0 written here
} else {
// Remove enclosing quotes
bits->argument->text = strdup(&item->text[1]);
bits->argument->text[strlen(bits->argument->text) - 1] = 0;
bits->argument->value = (jint16)strlen(bits->argument->text);
// Is this a string or label?
if (item->labelLine >= 0) {
// Label. Just add 1 because it will end up a null string.
pc++;
} else {
// String arguments add length
pc += strlen(bits->argument->text);
}
}
if (item->labelFile == NULL) {
bits->argument->labelFile = NULL;
} else {
bits->argument->labelFile = strdup(item->labelFile);
}
bits->argument->labelLine = item->labelLine;
LL_APPEND(bitstream, bits);
pc += 2; // All arguments have a 16 bit integer value
}
int16_t findVariable(char *varName) {
VariableT *variable;
int16_t found = -1;
LL_FOREACH(variables, variable) {
if (strcmp(variable->name, varName) == 0) {
found = variable->index;
}
}
if (found < 0) {
variable = (VariableT *)malloc(sizeof(VariableT));
variable->exported = 0;
variable->name = strdup(varName);
variable->index = variableCount++;
found = variable->index;
LL_APPEND(variables, variable);
}
return found;
}
int kw_add(int argc, char *argv[], char *file, int line) {
(void)argc;
if (!argIsVariable(argv[0])) {
printf("Keyword 'add' requires first argument to be a variable\n");
return 1;
}
push(argv[1], 0, file, line);
push(argv[0], 0, file, line);
op_add();
op_pop(argv[0]);
return 0;
}
int kw_call(int argc, char *argv[], char *file, int line) {
uint16_t addr;
char tempAddr[8];
char *temp;
unsigned long len;
(void)argc;
// We need to calculate the address of the opcode *after* the JMP.
addr = pc + 9;
/*
addr = pc + 4; // +1 for the JMP opcode, +3 for the value we're pushing.
if (argIsString(argv[0])) {
addr += strlen(argv[0]); // Strings are still quoted at this point so the extra two bytes make up for not adding the length bytes
} else {
addr += 3; // Three bytes for a numeric PUC or a PUV
}
*/
snprintf(tempAddr, 8, "%d", addr);
push(tempAddr, 0, file, line);
// Label (expected to be quoted, but isn't at this point)
len = strlen(argv[0]) + 3;
temp = (char *)malloc(len * sizeof(char));
snprintf(temp, len, "'%s'", argv[0]);
push(temp, 1, file, line);
free(temp);
op_jmp();
return 0;
}
int kw_define(int argc, char *argv[], char *file, int line) {
DefineT *define;
(void)argc;
(void)file;
(void)line;
define = (DefineT *)malloc(sizeof(DefineT));
define->name = strdup(argv[0]);
if (argIsString(argv[1])) {
// Remove Quotes
define->value = strdup(&argv[1][1]);
define->value[strlen(define->value) - 1] = 0;
} else {
define->value = strdup(argv[1]);
}
LL_APPEND(defines, define);
return 0;
}
int kw_do(int argc, char *argv[], char *file, int line) {
char temp[8];
// Arguments for EXE
for (int i=argc; i>0; i--) {
push(argv[i - 1], 0, file, line);
}
// Number of arguments
snprintf(temp, 8, "%d", argc);
push(temp, 0, file, line);
op_exe();
return 0;
}
int kw_end(int argc, char *argv[], char *file, int line) {
(void)argc;
(void)argv;
(void)file;
(void)line;
op_hlt();
return 0;
}
int kw_export(int argc, char *argv[], char *file, int line) {
VariableT *variable;
(void)argc;
if (!argIsVariable(argv[0])) {
printf("Keyword 'export' requires first argument to be a variable\n");
return 1;
}
push(argv[1], 0, file, line);
op_pop(argv[0]);
LL_FOREACH(variables, variable) {
if (strcmp(variable->name, argv[0]) == 0) {
variable->exported = 1;
}
}
return 0;
}
int kw_goto(int argc, char *argv[], char *file, int line) {
char *temp;
unsigned long len;
(void)argc;
// Label (expected to be quoted, but isn't at this point)
len = strlen(argv[0]) + 3;
temp = (char *)malloc(len * sizeof(char));
snprintf(temp, len, "'%s'", argv[0]);
push(temp, 1, file, line);
free(temp);
op_jmp();
return 0;
}
int kw_if(int argc, char *argv[], char *file, int line) {
char *temp;
unsigned long len;
(void)argc;
// Label (expected to be quoted, but isn't at this point)
len = strlen(argv[3]) + 3;
temp = (char *)malloc(len * sizeof(char));
snprintf(temp, len, "'%s'", argv[3]);
push(temp, 1, file, line);
free(temp);
push(argv[2], 0, file, line); // Second argument
switch(argv[1][0]) {
case '=':
push("0", 0, file, line);
break;
case '!':
push("1", 0, file, line);
break;
case '<':
push("2", 0, file, line);
break;
case '>':
push("3", 0, file, line);
break;
default:
printf("Unknown conditional operation '%s'\n", argv[1]);
return 1;
}
push(argv[0], 0, file, line); // First argument
op_boc();
return 0;
}
int kw_include(int argc, char *argv[], char *file, int line) {
(void)argc;
(void)file;
(void)line;
return parse(argv[0]);
}
int kw_return(int argc, char *argv[], char *file, int line) {
(void)argc;
(void)argv;
(void)file;
(void)line;
// The return address *should* be on the stack at this point
op_jmp();
return 0;
}
int kw_set(int argc, char *argv[], char *file, int line) {
(void)argc;
(void)file;
(void)line;
if (!argIsVariable(argv[0])) {
printf("Keyword 'set' requires first argument to be a variable\n");
return 1;
}
push(argv[1], 0, file, line);
op_pop(argv[0]);
return 0;
}
int kw_subtract(int argc, char *argv[], char *file, int line) {
(void)argc;
if (!argIsVariable(argv[0])) {
printf("Keyword 'subtract' requires first argument to be a variable\n");
return 1;
}
push(argv[1], 0, file, line);
push(argv[0], 0, file, line);
op_sub();
op_pop(argv[0]);
return 0;
}
void op_add(void) {
// Add
// Add two stack items and push result
emitOpcode("ADD");
}
void op_boc(void) {
// Branch On Condition
// Use four stack items to determine if we should branch
emitOpcode("BOC");
}
void op_exe(void) {
// Execute
// Calls native function number. Unknown stack state.
emitOpcode("EXE");
}
void op_jmp(void) {
// Jump
// Jumps to address on the stack
emitOpcode("JMP");
}
void op_hlt(void) {
// Halt
emitOpcode("HLT");
}
void op_pop(char *varName) {
// Pop
// Pop top stack value into variable
StackT item;
item.labelLine = -1;
item.labelFile = NULL;
item.text = NULL;
item.value = (int16_t)findVariable(varName);
emitOpcode("POP");
emitStackItem(&item);
}
void op_puc(StackT *arg) {
// Push Constant
// Pushes the constant value of 'arg' to the stack
emitOpcode("PUC");
emitStackItem(arg);
}
void op_puv(int16_t varIndex) {
// Push Variable
// Pushes address of variable to use onto stack
StackT item;
item.labelLine = -1;
item.labelFile = NULL;
item.text = NULL;
item.value = varIndex;
emitOpcode("PUV");
emitStackItem(&item);
}
void op_sub(void) {
// Subtract
// Subtract two stack items and push result
emitOpcode("SUB");
}
int parse(char *sourceIn) {
char line[MAX_LINE_SIZE];
char *token;
int tokenLength;
int state;
char **argv;
int argc;
char quote[MAX_ARG_SIZE];
LabelT *found;
LabelT *label;
DefineT *define;
char buffer[MAX_ARG_SIZE];
int inQuote = QUOTE_NONE;
int keyword = -1;
int lineNumber = 0;
int bailOut = 0;
FILE *in = fopen(sourceIn, "rt");
if (!in) {
printf("Unable to open input file '%s'.\n", sourceIn);
return 1;
}
files++;
argv = (char **)malloc(MAX_KEYWORD_ARGS * sizeof(char *));
for (int i=0; i<MAX_KEYWORD_ARGS; i++) {
argv[i] = (char *)malloc(MAX_ARG_SIZE * sizeof(char));
}
while ((fgets(line, MAX_LINE_SIZE, in) != NULL) && (!bailOut)) {
lineNumber++;
sloc++;
// Handle DEFINEs
LL_FOREACH(defines, define) {
strReplace(line, define->name, define->value);
}
//printf("Line: %s\n", line);
// First token
token = strtok(line, " ");
state = STATE_KEYWORD;
argc = 0;
keyword = -1;
while ((token != NULL) && (!bailOut)) {
tokenLength = (int)strlen(token);
if (tokenLength > 0) {
// Copy token to buffer so we don't screw up strtok().
strcpy(buffer, token);
// Handle Comments
if (buffer[0] == ';') {
break;
}
// Handle EOLs
if ((buffer[tokenLength - 1] == 10) || (buffer[tokenLength - 1] == 13)) {
tokenLength--;
buffer[tokenLength] = 0;
}
// Handle Keywords & Arguments
if (tokenLength > 0) {
// Handle keywords
if (state == STATE_KEYWORD) {
// Handle labels
if (buffer[tokenLength - 1] == ':') {
buffer[tokenLength - 1] = 0;
// Do we know this label already?
found = NULL;
LL_FOREACH(labels, label) {
if (strcmp(buffer, label->name) == 0) {
found = label;
}
}
if (found) {
// Yes. Die.
printf("Label '%s' already declared in '%s' at line %d.\n", buffer, found->file, found->line);
bailOut = 1;
} else {
// Add it.
label = (LabelT *)malloc(sizeof(LabelT));
label->addr = pc;
// Is this marked exported?
if (buffer[0] == '>') {
// Yes!
label->name = strdup(&buffer[1]);
label->exported = 1;
} else {
// No
label->name = strdup(buffer);
label->exported = 0;
}
label->line = lineNumber;
label->file = strdup(sourceIn);
LL_APPEND(labels, label);
labelCount++;
//printf("Label: %s\n", buffer);
}
} else {
// Not a label, search for known keyword
keyword = -2;
for (int i=0; i<(int)(sizeof(keywords)/sizeof(KeywordT)); i++) {
if (strcmp(buffer, keywords[i].keyword) == 0) {
// Found our keyword
keyword = i;
quote[0] = 0;
inQuote = QUOTE_NONE;
state = STATE_ARGUMENT;
//printf("Keyword: %s\n", keywords[i].keyword);
}
}
} // Is a label?
} else {
// Handle argument quoting
if (inQuote == QUOTE_NONE) {
// Are we entering a quoted string?
if ((buffer[0] == '"') || (buffer[0] == '\'')) {
// Does this token also end with quotes? If so, we don't need to enter quote mode.
if (!(
(((buffer[tokenLength - 1] == '"') && (buffer[0] == '"')) ||
((buffer[tokenLength - 1] == '\'') && (buffer[0] == '\''))) &&
(buffer[tokenLength - 2] != '\\')
) || (tokenLength == 1)) {
// Enter quote mode
inQuote = buffer[0] == '"' ? QUOTE_DOUBLE : QUOTE_SINGLE;
}
}
} else {
// Look to leave quote mode
if (
(((buffer[tokenLength - 1] == '"') && (inQuote == QUOTE_DOUBLE)) ||
((buffer[tokenLength - 1] == '\'') && (inQuote == QUOTE_SINGLE))) &&
(buffer[tokenLength - 2] != '\\')
) {
inQuote = QUOTE_NONE;
}
}
if (strlen(quote) > 0) {
strcat(quote, " ");
}
strcat(quote, buffer);
// Can we process this argument yet?
if (inQuote == QUOTE_NONE) {
strcpy(argv[argc], quote);
argc++;
//printf("Argument: [%s]\n", quote);
quote[0] = 0;
}
} // state
} // tokenLength > 0
} // tokenLength > 0
// Next token
token = strtok(NULL, " ");
// If this is the end of the line and we found a keyword, process it.
if ((!token) && (keyword >= 0) && (!bailOut)) {
if (keywords[keyword].argc < 0) {
if (argc < abs(keywords[keyword].argc)) {
printf("Keyword '%s' requires at least %d argument(s)\n", keywords[keyword].keyword, abs(keywords[keyword].argc));
bailOut = 1;
}
} else {
if (argc != keywords[keyword].argc) {
printf("Keyword '%s' requires %d argument(s)\n", keywords[keyword].keyword, keywords[keyword].argc);
bailOut = 1;
}
}
if (!bailOut) {
if (keywords[keyword].handler(argc, argv, sourceIn, lineNumber) != 0) {
bailOut = 1;
}
}
} else {
if (keyword == -2) {
printf("Watchu talkin' bout Willus?\n");
bailOut = 1;
}
}
} // token != NULL
} // read line
if (bailOut) {
printf("Location %d in '%s'\n", lineNumber, sourceIn);
}
for (int i=0; i<MAX_KEYWORD_ARGS; i++) {
free(argv[i]);
}
free(argv);
fclose(in);
return bailOut;
}
void push(char *arg, unsigned char isLabel, char *file, int line) {
StackT item;
if (isLabel) {
item.labelLine = line;
item.labelFile = file;
item.text = arg;
item.value = (int16_t)strlen(arg);
op_puc(&item);
} else {
if (argIsVariable(arg)) {
op_puv(findVariable(arg));
} else {
item.labelLine = -1;
item.labelFile = NULL;
if (argIsNumber(arg)) {
item.text = NULL;
item.value = (int16_t)atoi(arg);
} else {
item.text = arg;
item.value = (int16_t)strlen(arg);
}
op_puc(&item);
}
}
}
void strReplace(char *target, const char *needle, const char *replacement) {
char buffer[MAX_ARG_SIZE] = { 0 };
char *insert_point = &buffer[0];
const char *tmp = target;
size_t needle_len = strlen(needle);
size_t repl_len = strlen(replacement);
while (1) {
const char *p = strstr(tmp, needle);
if (p == NULL) {
strcpy(insert_point, tmp);
break;
}
memcpy(insert_point, tmp, (unsigned)(p - tmp));
insert_point += p - tmp;
memcpy(insert_point, replacement, repl_len);
insert_point += repl_len;
tmp = p + needle_len;
}
strcpy(target, buffer);
}
int main(int argc, char *argv[]) {
char *sourceFile;
char *binFile;
char *listingFile;
int bailOut;
int result;
uint16_t exportedLabels;
uint16_t exportedVariables;
uint16_t count;
uint16_t headerSize;
FILE *listing;
FILE *binary;
LabelT *label;
LabelT *labelS;
DefineT *define;
DefineT *defineS;
VariableT *variable;
VariableT *variableS;
BitstreamT *bits;
BitstreamT *bitsS;
// Get arguments
if (argc != 4) {
printf("Usage: %s source.dbl binary.bin listing.lst\n", argv[0]);
return 1;
}
sourceFile = argv[1];
binFile = argv[2];
listingFile = argv[3];
// Set up compiler
labels = NULL;
defines = NULL;
variables = NULL;
bitstream = NULL;
variableCount = 0;
pc = 0;
bailOut = 0;
result = 0;
sloc = 0;
files = 0;
opCount = 0;
labelCount = 0;
exportedLabels = 0;
exportedVariables = 0;
headerSize = 0;
// First pass, emit opcodes
if (parse(sourceFile) == 0) {
// Second pass, fixup lable references
LL_FOREACH(bitstream, bits) {
if (bits->argument != NULL) {
if (bits->argument->labelFile != NULL) {
labelS = NULL;
LL_FOREACH(labels, label) {
if (strcmp(bits->argument->text, label->name) == 0) {
labelS = label;
}
}
if (labelS) {
//printf("Fixing label '%s' as %04x\n", bits->argument->text, labelS->addr);
bits->argument->value = (int16_t)labelS->addr;
free(bits->argument->text);
bits->argument->text = NULL;
} else {
printf("Unable to locate label '%s' in '%s' at %d\n", bits->argument->text, bits->argument->labelFile, bits->argument->labelLine);
bailOut = 1;
result = 1;
}
}
}
}
} else {
bailOut = 1;
result = 1;
}
// If everything was okay, produce listing and binary
if (bailOut == 0) {
listing = fopen(listingFile, "wt");
if (listing) {
binary = fopen(binFile, "wb");
if (binary) {
// Write header
fputc('D', binary);
fputc('B', binary);
fputc('L', binary);
fputc(0, binary); // Version
// Write exported jump table
for (int pass=0; pass<2; pass++) {
LL_FOREACH(labels, label) {
if (label->exported) {
if (pass == 0) {
// First pass, just count
exportedLabels++;
} else {
// Second pass, write it out
fwrite(&label->addr, sizeof(uint16_t), 1, binary);
fputc((char)strlen(label->name), binary);
fwrite(label->name, sizeof(char), strlen(label->name), binary);
}
}
}
// End of first pass, write out header size
if (pass == 0) {
fwrite(&exportedLabels, sizeof(uint16_t), 1, binary);
}
}
// Write exported variable table
for (int pass=0; pass<2; pass++) {
count = 0;
LL_FOREACH(variables, variable) {
if (variable->exported) {
if (pass == 0) {
// First pass, just count
exportedVariables++;
} else {
// Second pass, write it out
fwrite(&count, sizeof(uint16_t), 1, binary);
fputc((char)strlen(variable->name), binary);
fwrite(variable->name, sizeof(char), strlen(variable->name), binary);
}
}
count++;
}
// End of first pass, write out header size
if (pass == 0) {
fwrite(&exportedVariables, sizeof(uint16_t), 1, binary);
}
}
// Write number of variables used
fwrite(&variableCount, sizeof(uint16_t), 1, binary);
headerSize = (uint16_t)ftell(binary);
// Write program bitstream
LL_FOREACH(bitstream, bits) {
// At this point we should always have a valid opcode
if ((bits->opcode > 0) && (!bailOut)) {
for (int i=0; i<(int)(sizeof(opcodes)/sizeof(OpcodeT)); i++) {
// Find opcode data
if (bits->opcode == opcodes[i].value) {
// Sanity check
if (ftell(binary) - headerSize != bits->addr) {
printf("Bitstream addresses out of sync at %04x\n", (uint16_t)ftell(binary) - headerSize);
bailOut = 1;
result = 1;
break;
}
fprintf(listing, "(%04x) %04x %s (%02x)", (uint16_t)ftell(binary), bits->addr, opcodes[i].opcode, i + 1);
fputc(opcodes[i].value, binary);
// Does this opcode have arguments?
if (opcodes[i].argc > 0) {
// Move on to the argument data
bits = bits->next;
// Should NOT have an opcode
if (bits->opcode > 0) {
printf("Unexpected opcode at address %04x\n", bits->addr);
bailOut = 1;
result = 1;
break;
} else {
// MUST have an argument
if (bits->argument == NULL) {
printf("No argument data at address %04x\n", bits->addr);
bailOut = 1;
result = 1;
break;
} else {
// String data?
if (bits->argument->text != NULL) {
// Yes!
fprintf(listing, " [%s]", bits->argument->text);
fwrite(&bits->argument->value, sizeof(int16_t), 1, binary);
fwrite(bits->argument->text, sizeof(char), strlen(bits->argument->text), binary);
} else {
// Nope.
fprintf(listing, " %04x", bits->argument->value);
fwrite(&bits->argument->value, sizeof(int16_t), 1, binary);
fputc(0, binary);
}
}
}
}
fprintf(listing, "\n");
break;
}
}
} else {
printf("Invalid opcode at address %04xd\n", bits->addr);
result = 1;
}
}
fclose(binary);
} else { // binary != null
printf("Unable to create object file '%s'\n", binFile);
result = 1;
}
fclose(listing);
} else {
printf("Unable to create listing file '%s'\n", listingFile);
result = 1;
}
}
// Stats
if (result == 0) {
printf(" Files Compiled: %d\n", files);
printf(" Lines Compiled: %d\n", sloc);
printf(" Labels declared: %d\n", labelCount);
printf(" Labels Exported: %d\n", exportedLabels);
printf("Variables declared: %d\n", variableCount);
printf("Variables Exported: %d\n", exportedVariables);
printf(" Operands Emitted: %d\n", opCount);
printf(" Bytes Used: %d\n", pc);
}
// Shut down
LL_FOREACH_SAFE(bitstream, bits, bitsS) {
LL_DELETE(bitstream, bits);
if (bits->argument != NULL) {
if (bits->argument->labelFile != NULL) {
free(bits->argument->labelFile);
}
if (bits->argument->text != NULL) {
free(bits->argument->text);
}
free(bits->argument);
}
}
LL_FOREACH_SAFE(variables, variable, variableS) {
LL_DELETE(variables, variable);
free(variable->name);
}
LL_FOREACH_SAFE(defines, define, defineS) {
LL_DELETE(defines, define);
free(define->name);
free(define->value);
}
LL_FOREACH_SAFE(labels, label, labelS) {
LL_DELETE(labels, label);
free(label->name);
free(label->file);
}
return result;
}