1088 lines
26 KiB
C
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;
|
|
}
|