aboutsummaryrefslogtreecommitdiff
path: root/tools/create_mads/parser.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2010-11-19 10:23:33 +0000
committerPaul Gilbert2010-11-19 10:23:33 +0000
commit16ee411187608220e936b366bfa8afb5da5bce19 (patch)
tree9a3492f050c95b3f6cb0888551d197d311c84cfe /tools/create_mads/parser.cpp
parent763ddd220361e9764b19ccbe05be80ce5f897d5f (diff)
downloadscummvm-rg350-16ee411187608220e936b366bfa8afb5da5bce19.tar.gz
scummvm-rg350-16ee411187608220e936b366bfa8afb5da5bce19.tar.bz2
scummvm-rg350-16ee411187608220e936b366bfa8afb5da5bce19.zip
TOOLS: Added create_mads tool for MADS/M4 engine
svn-id: r54363
Diffstat (limited to 'tools/create_mads/parser.cpp')
-rw-r--r--tools/create_mads/parser.cpp942
1 files changed, 942 insertions, 0 deletions
diff --git a/tools/create_mads/parser.cpp b/tools/create_mads/parser.cpp
new file mode 100644
index 0000000000..be0d78f988
--- /dev/null
+++ b/tools/create_mads/parser.cpp
@@ -0,0 +1,942 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MAX_SOURCE_LINE_LENGTH 256
+#define MAX_TOKEN_STRING_LENGTH MAX_SOURCE_LINE_LENGTH
+#define MAX_DIGIT_COUNT 20
+#define MAX_SYMBOLS 1024
+#define MAX_SUBROUTINES 1024
+#define MAX_SUBROUTINE_SIZE 4096
+#define MAX_SUBROUTINE_JUMPS 256
+
+#define OPSIZE8 0x40 ///< when this bit is set - the operand size is 8 bits
+#define OPSIZE16 0x80 ///< when this bit is set - the operand size is 16 bits
+#define OPSIZE32 0x00 ///< when no bits are set - the operand size is 32 bits
+
+#define VERSION 1
+
+enum CharCode {
+ LETTER, DIGIT, SPECIAL, EOF_CODE, EOL_CODE
+};
+
+enum TokenCode {
+ NO_TOKEN, WORD, NUMBER, IDENTIFIER, END_OF_FILE, END_OF_LINE,
+ RW_DEFINE, RW_COLON, RW_SUB, RW_END, RW_OPCODE,
+ ERROR
+};
+
+enum LiteralType {
+ INTEGER_LIT
+};
+
+struct Literal {
+ LiteralType type;
+ union {
+ int integer;
+ } value;
+};
+
+struct SymbolEntry {
+ char symbol[MAX_TOKEN_STRING_LENGTH];
+ char value[MAX_TOKEN_STRING_LENGTH];
+};
+
+struct SubEntry {
+ char name[MAX_TOKEN_STRING_LENGTH];
+ int fileOffset;
+};
+
+struct JumpSource {
+ char name[MAX_TOKEN_STRING_LENGTH];
+ int line_number;
+ int offset;
+};
+
+struct JumpDest {
+ char name[MAX_TOKEN_STRING_LENGTH];
+ int offset;
+};
+
+enum Opcodes {
+ OP_HALT = 0, OP_IMM = 1, OP_ZERO = 2, OP_ONE = 3, OP_MINUSONE = 4, OP_STR = 5, OP_DLOAD = 6,
+ OP_DSTORE = 7, OP_PAL = 8, OP_LOAD = 9, OP_GLOAD = 10, OP_STORE = 11, OP_GSTORE = 12,
+ OP_CALL = 13, OP_LIBCALL = 14, OP_RET = 15, OP_ALLOC = 16, OP_JUMP = 17, OP_JMPFALSE = 18,
+ OP_JMPTRUE = 19, OP_EQUAL = 20, OP_LESS = 21, OP_LEQUAL = 22, OP_NEQUAL = 23, OP_GEQUAL = 24,
+ OP_GREAT = 25, OP_PLUS = 26, OP_MINUS = 27, OP_LOR = 28, OP_MULT = 29, OP_DIV = 30,
+ OP_MOD = 31, OP_AND = 32, OP_OR = 33, OP_EOR = 34, OP_LAND = 35, OP_NOT = 36, OP_COMP = 37,
+ OP_NEG = 38, OP_DUP = 39,
+ TOTAL_OPCODES = 40
+};
+
+typedef unsigned char byte;
+
+const unsigned char EOF_CHAR = (unsigned char)255;
+const unsigned char EOL_CHAR = (unsigned char)254;
+
+/*----------------------------------------------------------------------*/
+/* Reserved words tables */
+/*----------------------------------------------------------------------*/
+
+enum OpcodeParamType {OP_NO_PARAM, OP_IMM_PARAM, OP_TRANSFER_PARAM};
+
+struct OpcodeEntry {
+ const char *str;
+ OpcodeParamType paramType;
+};
+
+OpcodeEntry OpcodeList[OP_DUP + 1] = {
+ {"HALT", OP_NO_PARAM}, {"IMM", OP_IMM_PARAM}, {"ZERO", OP_NO_PARAM}, {"ONE", OP_NO_PARAM},
+ {"MINUSONE", OP_NO_PARAM}, {"STR", OP_IMM_PARAM}, {"DLOAD", OP_IMM_PARAM}, {"DSTORE", OP_IMM_PARAM},
+ {"PAL", OP_IMM_PARAM}, {"LOAD", OP_IMM_PARAM}, {"GLOAD", OP_IMM_PARAM}, {"STORE", OP_IMM_PARAM},
+ {"GSTORE", OP_IMM_PARAM}, {"CALL", OP_IMM_PARAM}, {"LIBCALL", OP_IMM_PARAM}, {"RET", OP_NO_PARAM},
+ {"ALLOC", OP_IMM_PARAM}, {"JUMP", OP_TRANSFER_PARAM}, {"JMPFALSE", OP_TRANSFER_PARAM},
+ {"JMPTRUE", OP_TRANSFER_PARAM}, {"EQUAL", OP_NO_PARAM}, {"LESS", OP_NO_PARAM},
+ {"LEQUAL", OP_NO_PARAM}, {"NEQUAL", OP_NO_PARAM}, {"GEQUAL", OP_NO_PARAM},
+ {"GREAT", OP_NO_PARAM}, {"PLUS", OP_NO_PARAM}, {"MINUS", OP_NO_PARAM},
+ {"LOR", OP_NO_PARAM}, {"MULT", OP_NO_PARAM}, {"DIV", OP_IMM_PARAM}, {"MOD", OP_NO_PARAM},
+ {"AND", OP_NO_PARAM}, {"OR", OP_NO_PARAM}, {"EOR", OP_NO_PARAM}, {"LAND", OP_NO_PARAM},
+ {"NOT", OP_NO_PARAM}, {"COMP", OP_NO_PARAM}, {"NEG", OP_NO_PARAM}, {"DUP", OP_NO_PARAM}
+};
+
+
+const char *symbol_strings[] = {"#DEFINE", ":", "SUB", "END"};
+
+/*----------------------------------------------------------------------*/
+/* Globals */
+/*----------------------------------------------------------------------*/
+
+unsigned char ch; // Current input character
+TokenCode token; // code of current token
+Opcodes opcode; // Current instruction opcode
+OpcodeParamType paramType; // Parameter type opcode expects
+Literal literal; // Value of literal
+int buffer_offset; // Char offset into source buffer
+int level = 0; // current nesting level
+int line_number = 0; // current line number
+
+char source_buffer[MAX_SOURCE_LINE_LENGTH]; // Source file buffer
+char token_string[MAX_TOKEN_STRING_LENGTH]; // Token string
+char *bufferp = source_buffer; // Source buffer ptr
+char *tokenp = token_string; // Token string ptr
+
+int digit_count; // Total no. of digits in number
+bool count_error; // Too many digits in number?
+
+FILE *source_file;
+FILE *dest_file;
+CharCode char_table[256];
+
+SymbolEntry symbolTable[MAX_SYMBOLS];
+int symbolCount = 0;
+
+int game_number = 0;
+int language = 0;
+
+int indexSize = 0;
+int fileOffset = 0;
+SubEntry subroutinesTable[MAX_SUBROUTINES];
+int subroutinesCount = 0;
+
+byte subroutineData[MAX_SUBROUTINE_SIZE];
+int subroutineSize = 0;
+
+JumpSource jumpSources[MAX_SUBROUTINE_JUMPS];
+int jumpSourceCount = 0;
+JumpDest jumpDests[MAX_SUBROUTINE_JUMPS];
+int jumpDestCount = 0;
+
+#define char_code(ch) char_table[ch]
+
+void get_char();
+void get_token();
+
+/*----------------------------------------------------------------------*/
+/* Miscellaneous support functions */
+/*----------------------------------------------------------------------*/
+
+void strToUpper(char *string) {
+ while (*string) {
+ *string = toupper(*string);
+ ++string;
+ }
+}
+
+void strToLower(char *string) {
+ while (*string) {
+ *string = tolower(*string);
+ ++string;
+ }
+}
+
+int strToInt(const char *s) {
+ unsigned int tmp;
+
+ if (!*s)
+ // No string at all
+ return 0;
+ else if (toupper(s[strlen(s) - 1]) == 'H')
+ // Hexadecimal string with trailing 'h'
+ sscanf(s, "%xh", &tmp);
+ else if (*s == '$')
+ // Hexadecimal string starting with '$'
+ sscanf(s + 1, "%x", &tmp);
+ else
+ // Standard decimal string
+ return atoi(s);
+
+ return (int)tmp;
+}
+
+/*----------------------------------------------------------------------*/
+/* Initialisation / De-initialisation code */
+/*----------------------------------------------------------------------*/
+
+/**
+ * Open the input file for parsing
+ */
+void open_source_file(const char *name) {
+ if ((source_file = fopen(name, "r")) == NULL) {
+ printf("*** Error: Failed to open source file.\n");
+ exit(0);
+ }
+
+ // Fetch the first character
+ bufferp = "";
+ get_char();
+}
+
+/**
+ * Close the source file
+ */
+void close_source_file() {
+ fclose(source_file);
+}
+
+/**
+ * Initialises the scanner
+ */
+void init_scanner(const char *name) {
+ // Initialise character table
+ int ch;
+ for (ch = 0; ch < 256; ++ch) char_table[ch] = SPECIAL;
+ for (ch = '0'; ch <= '9'; ++ch) char_table[ch] = DIGIT;
+ for (ch = 'A'; ch <= 'Z'; ++ch) char_table[ch] = LETTER;
+ for (ch = 'a'; ch <= 'z'; ++ch) char_table[ch] = LETTER;
+ char_table[EOF_CHAR] = EOF_CODE;
+ char_table[EOL_CHAR] = EOL_CODE;
+ char_table[(int)'$'] = DIGIT; // Needed for hexadecimal number handling
+
+ open_source_file(name);
+}
+
+/**
+ * Shuts down the scanner
+ */
+void quit_scanner() {
+ close_source_file();
+}
+
+/*----------------------------------------------------------------------*/
+/* Output routines */
+/*----------------------------------------------------------------------*/
+
+
+/**
+ * Initialises the output
+ */
+void init_output(const char *destFilename) {
+ dest_file = fopen(destFilename, "wb");
+ if (dest_file == NULL) {
+ printf("Could not open file for writing\n");
+ exit(0);
+ }
+}
+
+/**
+ * Closes the output file
+ */
+void close_output() {
+ fclose(dest_file);
+}
+
+/**
+ * Writes a single byte to the output
+ */
+void write_byte(byte v) {
+ fwrite(&v, 1, 1, dest_file);
+ ++fileOffset;
+}
+
+/**
+ * Writes a word to the output
+ */
+void write_word(int v) {
+ write_byte(v & 0xff);
+ write_byte((v >> 8) & 0xff);
+}
+
+/**
+ * Writes a 32-bit value to the output
+ */
+void write_long(int v) {
+ write_byte(v & 0xff);
+ write_byte((v >> 8) & 0xff);
+ write_byte((v >> 16) & 0xff);
+ write_byte((v >> 24) & 0xff);
+}
+
+/**
+ * Writes a sequence of bytes to the output
+ */
+void write_bytes(byte *v, int len) {
+ fwrite(v, 1, len, dest_file);
+ fileOffset += len;
+}
+
+/**
+ * Writes a repeat sequence of a value to the output
+ */
+void write_byte_seq(byte v, int len) {
+ byte *tempData = (byte *)malloc(len);
+ memset(tempData, v, len);
+ write_bytes(tempData, len);
+ free(tempData);
+}
+
+/**
+ * Writes out the header and allocates space for the symbol table
+ */
+void write_header() {
+ // Write out three bytes - game Id, language Id, and version number
+ if (game_number == 0) {
+ game_number = 1;
+ printf("No game specified, defaulting to Rex Nebular\n");
+ }
+ write_byte(game_number);
+
+ if (language == 0) {
+ language = 1;
+ printf("No language specified, defaulting to English\n");
+ }
+ write_byte(language);
+
+ write_byte(VERSION);
+
+ // Write out space to later come back and store the list of subroutine names and offsets
+ if (indexSize == 0) {
+ indexSize = 4096;
+ printf("No index size specified, defaulting to %d bytes\n", indexSize);
+ }
+ write_byte_seq(0, indexSize - 3);
+
+ fileOffset = indexSize;
+}
+
+/**
+ * Goes back and writes out the subroutine list
+ */
+void write_index() {
+ fseek(dest_file, 3, SEEK_SET);
+
+ int bytesRemaining = indexSize - 3;
+ for (int i = 0; i < subroutinesCount; ++i) {
+ int entrySize = strlen(subroutinesTable[i].name) + 5;
+
+ // Ensure there is enough remaining space
+ if ((bytesRemaining - entrySize) < 0) {
+ printf("Index has exceeded allowable size.\n");
+ token = ERROR;
+ }
+
+ // Write out the name and the file offset
+ write_bytes((byte *)&subroutinesTable[i].name, strlen(subroutinesTable[i].name) + 1);
+ write_long(subroutinesTable[i].fileOffset);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+/* Processing routines */
+/*----------------------------------------------------------------------*/
+
+int symbolFind() {
+ for (int i = 0; i < symbolCount; ++i) {
+ if (!strcmp(symbolTable[i].symbol, token_string))
+ return i;
+ }
+ return -1;
+}
+
+int subIndexOf() {
+ for (int i = 0; i < subroutinesCount; ++i) {
+ if (!strcmp(subroutinesTable[i].name, token_string))
+ return i;
+ }
+ return -1;
+}
+
+int jumpIndexOf(const char *name) {
+ for (int i = 0; i < jumpDestCount; ++i) {
+ if (!strcmp(jumpDests[i].name, name))
+ return i;
+ }
+ return -1;
+}
+
+void handle_define() {
+ // Read the variable name
+ get_token();
+ if (token != IDENTIFIER) {
+ token = ERROR;
+ return;
+ }
+
+ // Make sure it doesn't already exist
+ if (symbolFind() != -1) {
+ printf("Duplicate symbol encountered.\n");
+ token = ERROR;
+ return;
+ }
+
+ // Store the new symbol name
+ strcpy(symbolTable[symbolCount].symbol, token_string);
+
+ // Get the value
+ get_token();
+ if (token == END_OF_LINE) {
+ printf("Unexpected end of line.\n");
+ token = ERROR;
+ }
+ if ((token != NUMBER) && (token != IDENTIFIER)) {
+ printf("Invalid define value.\n");
+ token = ERROR;
+ }
+ if (token == ERROR)
+ return;
+
+ // Handle special symbols
+ if (!strcmp(symbolTable[symbolCount].symbol, "GAME_ID")) {
+ // Specify game number
+ if (!strcmp(token_string, "REX"))
+ game_number = 1;
+ else
+ token = ERROR;
+ } else if (!strcmp(symbolTable[symbolCount].symbol, "LANGUAGE")) {
+ // Specify the language
+ if (!strcmp(token_string, "ENGLISH"))
+ language = 1;
+ else
+ token = ERROR;
+ } else if (!strcmp(symbolTable[symbolCount].symbol, "INDEX_BLOCK_SIZE")) {
+ // Specifying the size of the index
+ indexSize = strToInt(token_string);
+ } else {
+ // Standard symbol - save it's value
+ strcpy(symbolTable[symbolCount].value, token_string);
+ ++symbolCount;
+ }
+
+ if (token == ERROR)
+ return;
+
+ // Ensure the next symbol is the end of line
+ get_token();
+ if (token != END_OF_LINE) {
+ printf("Extraneous information on line.\n");
+ token = ERROR;
+ }
+}
+
+/**
+ * Handles getting a parameter for an opcode
+ */
+void get_parameter() {
+ int nvalue;
+
+ if (token == NUMBER) {
+ literal.value.integer = strToInt(token_string);
+ return;
+ }
+
+ if (token != IDENTIFIER)
+ return;
+
+ nvalue = symbolFind();
+ if (nvalue != -1) {
+ // Found symbol, so get it's numeric value and return
+ token = NUMBER;
+ literal.value.integer = strToInt(symbolTable[nvalue].value);
+ return;
+ }
+
+ // Check if the parameter is the name of an already processed subroutine
+ strToLower(token_string);
+ nvalue = subIndexOf();
+ if (nvalue == -1) {
+ token = ERROR;
+ return;
+ }
+
+ // Store the index (not the offset) of the subroutine to call
+ token = NUMBER;
+ literal.value.integer = nvalue;
+}
+
+#define INC_SUB_PTR if (++subroutineSize == MAX_SUBROUTINE_SIZE) { \
+ printf("Maximum allowable subroutine size exceeded\n"); \
+ token = ERROR; \
+ return; \
+ }
+
+#define WRITE_SUB_BYTE(v) subroutineData[subroutineSize] = (byte)(v)
+
+/**
+ * Handles a single instruction within the sub-routine
+ */
+void handle_instruction() {
+ // Write out the opcode
+ WRITE_SUB_BYTE(opcode);
+ INC_SUB_PTR;
+
+ get_token();
+
+ if (OpcodeList[opcode].paramType == OP_IMM_PARAM) {
+ get_parameter();
+
+ if (token != NUMBER) {
+ printf("Incorrect opcode parameter encountered\n");
+ token = ERROR;
+ return;
+ }
+
+ // Apply the correct opcode size to the previously stored opcode and save the byte(s)
+ if (literal.value.integer <= 0xff) {
+ subroutineData[subroutineSize - 1] |= OPSIZE8;
+ WRITE_SUB_BYTE(literal.value.integer);
+ INC_SUB_PTR;
+ } else if (literal.value.integer <= 0xffff) {
+ subroutineData[subroutineSize - 1] |= OPSIZE16;
+ WRITE_SUB_BYTE(literal.value.integer);
+ INC_SUB_PTR;
+ WRITE_SUB_BYTE(literal.value.integer >> 8);
+ INC_SUB_PTR;
+
+ } else {
+ subroutineData[subroutineSize - 1] |= OPSIZE32;
+ int v = literal.value.integer;
+ for (int i = 0; i < 4; ++i, v >>= 8) {
+ WRITE_SUB_BYTE(v);
+ INC_SUB_PTR;
+ }
+ }
+
+ get_token();
+ } else if (OpcodeList[opcode].paramType == OP_TRANSFER_PARAM) {
+
+ if (token != IDENTIFIER) {
+ printf("Incorrect opcode parameter encountered\n");
+ token = ERROR;
+ return;
+ }
+
+ // Check to see if it's a backward jump to an existing label
+ int idx = jumpIndexOf(token_string);
+ if (idx != -1) {
+ // It's a backwards jump whose destination is already known
+ if (jumpDests[idx].offset < 256) {
+ // 8-bit destination
+ subroutineData[subroutineSize - 1] |= OPSIZE8;
+ subroutineData[subroutineSize] = jumpDests[idx].offset;
+ INC_SUB_PTR;
+ } else {
+ // 16-bit destination
+ subroutineData[subroutineSize - 1] |= OPSIZE16;
+ INC_SUB_PTR;
+ subroutineData[subroutineSize] = jumpDests[idx].offset & 0xff;
+ INC_SUB_PTR;
+ subroutineData[subroutineSize] = (jumpDests[idx].offset >> 8) & 0xff;
+ }
+ } else {
+ // Unknown destination, so save it for later resolving
+ strcpy(jumpSources[jumpSourceCount].name, token_string);
+ jumpSources[jumpSourceCount].line_number = line_number;
+ jumpSources[jumpSourceCount].offset = subroutineSize;
+ if (++jumpSourceCount == MAX_SUBROUTINE_JUMPS) {
+ printf("Maximum allowable jumps size exceeded\n");
+ token = ERROR;
+ return;
+ }
+
+ // Store a 16-bit placeholder
+ subroutineData[subroutineSize - 1] |= OPSIZE16;
+ WRITE_SUB_BYTE(0);
+ INC_SUB_PTR;
+ WRITE_SUB_BYTE(0);
+ INC_SUB_PTR;
+ }
+
+ get_token();
+ }
+
+ if (token != END_OF_LINE)
+ token = ERROR;
+}
+
+/**
+ * Called at the end of the sub-routine, fixes the destination of any forward jump references
+ */
+void fix_subroutine_jumps() {
+ for (int i = 0; i < jumpSourceCount; ++i) {
+ // Scan through the list of transfer destinations within the script
+ int idx = jumpIndexOf(jumpSources[i].name);
+ if (idx == -1) {
+ token = ERROR;
+ line_number = jumpSources[i].line_number;
+ return;
+ }
+
+ // Replace the placeholder bytes with the new destination
+ subroutineData[jumpSources[i].offset] = jumpDests[idx].offset & 0xff;
+ subroutineData[jumpSources[i].offset + 1] = (jumpDests[idx].offset >> 8) & 0xff;
+ }
+}
+
+/**
+ * Handles parsing a sub-routine
+ */
+void handle_sub() {
+ // Get the subroutine name
+ get_token();
+ if (token != IDENTIFIER) {
+ printf("Missing subroutine name.\n");
+ token = ERROR;
+ return;
+ }
+
+ strToLower(token_string);
+ if (subIndexOf() != -1) {
+ printf("Duplicate sub-routine encountered\n");
+ token = ERROR;
+ return;
+ }
+
+ // If this is the first subroutine, start writing out the data
+ if (subroutinesCount == 0)
+ write_header();
+
+ // Save the sub-routine details
+ strcpy(subroutinesTable[subroutinesCount].name, token_string);
+ subroutinesTable[subroutinesCount].fileOffset = fileOffset;
+ if (++subroutinesCount == MAX_SUBROUTINES) {
+ printf("Exceeded maximum allowed subroutine count\n");
+ token = ERROR;
+ return;
+ }
+
+ // Ensure the line end
+ get_token();
+ if (token != END_OF_LINE) {
+ token = ERROR;
+ return;
+ }
+
+ // Initial processing arrays
+ memset(subroutineData, 0, MAX_SUBROUTINE_SIZE);
+ subroutineSize = 0;
+ jumpSourceCount = 0;
+ jumpDestCount = 0;
+
+ // Loop through the lines of the sub-routine
+ while (token != ERROR) {
+ get_token();
+
+ if (token == END_OF_LINE) continue;
+ if (token == RW_OPCODE) {
+ // Handle instructions
+ handle_instruction();
+
+ } else if (token == IDENTIFIER) {
+ // Save identifier, it's hopefully a jump symbol
+ strcpy(jumpDests[jumpDestCount].name, token_string);
+ get_token();
+ if (token != RW_COLON)
+ token = ERROR;
+ else {
+ // Save the jump point
+ jumpDests[jumpDestCount].offset = subroutineSize;
+
+ if (++jumpDestCount == MAX_SUBROUTINE_JUMPS) {
+ printf("Subroutine exceeded maximum allowable jump points\n");
+ token = ERROR;
+ return;
+ }
+
+ // Ensure it's the last value on the line
+ get_token();
+ if (token != END_OF_LINE)
+ token = ERROR;
+ }
+ } else if (token == RW_END) {
+ // End of subroutine reached
+ get_token();
+ if (token != ERROR)
+ fix_subroutine_jumps();
+ write_bytes(&subroutineData[0], subroutineSize);
+ break;
+
+ } else {
+ token = ERROR;
+ printf("Unexpected error\n");
+ }
+ }
+}
+
+/*----------------------------------------------------------------------*/
+/* Character routines */
+/*----------------------------------------------------------------------*/
+
+/**
+ * Read the next line from the source file.
+ */
+bool get_source_line() {
+ if ((fgets(source_buffer, MAX_SOURCE_LINE_LENGTH, source_file)) != NULL) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Set ch to the next character from the source buffer
+ */
+void get_char() {
+ // If at the end of current source line, read another line.
+ // If at end of file, set ch to the EOF character and return
+ if (*bufferp == '\0') {
+ if (!get_source_line()) {
+ ch = EOF_CHAR;
+ return;
+ }
+ bufferp = source_buffer;
+ buffer_offset = 0;
+ ++line_number;
+ ch = EOL_CHAR;
+ return;
+ }
+
+ ch = *bufferp++; // Next character in the buffer
+
+ if ((ch == '\n') || (ch == '\t')) ch = ' ';
+}
+
+/**
+ * Skip past any blanks in the current location in the source buffer.
+ * Set ch to the next nonblank character
+ */
+void skip_blanks() {
+ while (ch == ' ') get_char();
+}
+
+/*----------------------------------------------------------------------*/
+/* Token routines */
+/*----------------------------------------------------------------------*/
+
+bool is_reserved_word() {
+ for (int i = 0; i < 4; ++i) {
+ if (!strcmp(symbol_strings[i], token_string)) {
+ token = (TokenCode)(RW_DEFINE + i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_opcode() {
+ for (int i = 0; i < TOTAL_OPCODES; ++i) {
+ if (!strcmp(OpcodeList[i].str, token_string)) {
+ token = RW_OPCODE;
+ opcode = (Opcodes)i;
+ paramType = OpcodeList[i].paramType;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Extract a word token and set token to IDENTIFIER
+ */
+void get_word() {
+ // Extract the word
+ while ((char_code(ch) == LETTER) || (char_code(ch) == DIGIT) || (ch == '_')) {
+ *tokenp++ = ch;
+ get_char();
+ }
+
+ *tokenp = '\0';
+
+ strToUpper(token_string);
+ token = WORD;
+ if (!is_reserved_word() && !is_opcode()) token = IDENTIFIER;
+}
+
+/**
+ * Extract a number token and set literal to it's value. Set token to NUMBER
+ */
+void get_number() {
+ int nvalue = 0; // Value of number
+ int digit_count = 0; // Total no. of digits in number */
+ bool count_error = false;// Too many digits in number?
+
+ do {
+ *tokenp++ = ch;
+
+ if (++digit_count > MAX_DIGIT_COUNT) {
+ count_error = true;
+ break;
+ }
+
+ get_char();
+ } while ((char_code(ch) == DIGIT) || (toupper(ch) == 'X') || ((toupper(ch) >= 'A') && (toupper(ch) <= 'F')));
+
+ if (count_error) {
+ token = ERROR;
+ return;
+ }
+
+ literal.type = INTEGER_LIT;
+ literal.value.integer = strToInt(token_string);
+ *tokenp = '\0';
+ token = NUMBER;
+}
+
+/**
+ * Extract a special token
+ */
+void get_special() {
+ *tokenp++ = ch;
+ if (ch == ':') {
+ token = RW_COLON;
+ get_char();
+ return;
+ } else if (ch == '/') {
+ *tokenp++ = ch;
+ get_char();
+ if (ch == '/') {
+ // Comment, so read until end of line
+ while ((ch != EOL_CHAR) && (ch != EOF_CHAR))
+ get_char();
+ token = END_OF_LINE;
+ return;
+ }
+ }
+
+ // Extract the rest of the word
+ get_char();
+ while ((char_code(ch) == LETTER) || (char_code(ch) == DIGIT)) {
+ *tokenp++ = ch;
+ get_char();
+ }
+ *tokenp = '\0';
+
+ strToUpper(token_string);
+ if (token_string[0] == '@')
+ token = IDENTIFIER;
+ else if (!is_reserved_word())
+ token = ERROR;
+}
+
+/**
+ * Extract the next token from the source buffer
+ */
+void get_token() {
+ skip_blanks();
+ tokenp = token_string;
+
+ switch (char_code(ch)) {
+ case LETTER: get_word(); break;
+ case DIGIT: get_number(); break;
+ case EOL_CODE: { token = END_OF_LINE; get_char(); break; }
+ case EOF_CODE: token = END_OF_FILE; break;
+ default: get_special(); break;
+ }
+}
+
+/**
+ * Handles processing a line outside of subroutines
+ */
+void process_line() {
+ if ((token == ERROR) || (token == END_OF_FILE)) return;
+
+ switch (token) {
+ case RW_DEFINE:
+ handle_define();
+ break;
+ case RW_SUB:
+ handle_sub();
+ break;
+ case END_OF_LINE:
+ break;
+ default:
+ token = ERROR;
+ break;
+ }
+
+ if (token == END_OF_LINE) {
+ get_token();
+ }
+}
+
+/*----------------------------------------------------------------------*/
+/* Interface methods */
+/*----------------------------------------------------------------------*/
+
+/**
+ * Main compiler method
+ */
+bool Compile(const char *srcFilename, const char *destFilename) {
+ init_scanner(srcFilename);
+ init_output(destFilename);
+
+ get_token();
+ while ((token != END_OF_FILE) && (token != ERROR))
+ process_line();
+
+ if (token != ERROR) {
+ write_index();
+ }
+
+ quit_scanner();
+
+ if (token == ERROR)
+ printf("Error encountered on line %d\n", line_number);
+ else
+ printf("Compilation complete\n");
+ return token != ERROR;
+}