From 0f32c48e22d8fe705f93ef5891a254a7f2a22b1c Mon Sep 17 00:00:00 2001 From: Curtis F Kaylor Date: Tue, 28 Mar 2017 00:09:18 -0400 Subject: [PATCH] Initial commit of c02.c and test.asm --- c02.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ test.asm | 18 +++ 2 files changed, 473 insertions(+) create mode 100644 c02.c create mode 100644 test.asm diff --git a/c02.c b/c02.c new file mode 100644 index 0000000..dc1d165 --- /dev/null +++ b/c02.c @@ -0,0 +1,455 @@ +/************************************************************** + * C02 Compiler - (C) 2013 Curtis F Kaylor * + * * + * C02 is a modified C-like language designed for the 6502 * + * * + * This Compiler generates crasm compatible assembly language * + * * + **************************************************************/ + + +#include +#include +#include +#include +#include + +#define TRUE -1 +#define FALSE 0 + +#define VARLEN 7 /*Maximum Variable Length*/ +#define MAXVAR 255 /*Maximum Number of Variables*/ + +#define DEBUG(s, val) if (debug) printf(s, val) + +enum data_type {BYTE, CHAR}; +enum term_type {NUMBER, VARIABLE}; + +FILE *src_file; /*Source File (Input)*/ +FILE *out_file; /*Assembler File (Output)*/ +FILE *log_file; /*Log File (Output) */ + +char src_name[255]; /*Source File Name*/ +char out_name[255]; /*Assembler File Name*/ +char log_name[255]; /*Log File Name */ + +int debug; /*Print Debug Info*/ + +int nextChar; /*Next Character of Source File to Process*/ +int curCol, curLine; /*Position in Source Code*/ + +char word[255]; /*Word parsed from source file*/ +char term[255]; /*Term parsed from equation*/ +char operator; /*Arithmetic or Bitwise Operator*/ + +char var_name[MAXVAR+1][VARLEN+1]; /*Variable Name Table*/ +char var_type[MAXVAR+1]; /*Variable Type Table*/ +int var_count; /*Number of Variables Assigned*/ + + + + /* Error - print Source File position and exit */ + void exit_error(int err_no) + { + printf("Line %d Column %d\n", curLine, curCol); + exit(err_no); + } + + /* Error - Print perror() and exit */ + void exit_perror() + { + perror("C02"); + exit_error(errno); + } + + /* Error - print "Expected" message and exit + Args: s - Expected string */ + void expected(char* s) + { + printf("Expected %s\n", s); + exit_error(EXIT_FAILURE); + } + + /* Get Next Character from Source File * + * Uses: src_file - Source File Handle * + * Sets: nextChar - Next Character in Source File */ + void getChar() + { + nextChar = fgetc(src_file); + if (nextChar == '\n') curCol=1; else curCol++; + if (curCol == 1) curLine++; + } + + /* Various tests against nextChar */ + int isAlpha() {return isalpha(nextChar);} + int isAlphaNum() {return isalnum(nextChar);} + int isDigit() {return isdigit(nextChar);} + int isNewLine() {return (nextChar == '\n' || nextChar == '\r');} + int isSpace() {return isspace(nextChar);} + int match(int c) {return (nextChar == c);} + + /* Advance Source File to next printable character */ + void skip_spaces() {while (isSpace()) getChar();} + + /* Advance Source File to end of line */ + void skip_to_eol() {while (!isNewLine()) getChar();} + + /* Initilize Compiler Variables */ + void init() + { + DEBUG(">Initializing Compiler Variables\n",0); + var_count = 0; + curCol = 0; + curLine = 0; + } + + /* Reads next Word in Source File, where * + * a Word is a sequence of AlphaNumeric characters * + * Sets: word - the Word read from the source file */ + get_word() + { + int wordLen = 0; + skip_spaces(); + if (!isAlpha()) expected("Alphabetic Character"); + while (isAlphaNum()) + { + word[wordLen++] = nextChar; + getChar(); + } + word[wordLen] = 0; + } + + /* Reads Decimal number from source file * + * a Decimal is a series of digits (0-9) * + * Returns: integer value of number */ + int get_decimal() + { + int digit; + int number = 0; + skip_spaces(); + if (!isDigit()) expected("Digit"); + while (!isDigit()) + { + digit = nextChar - '0'; + number = number * 10 + digit; + } + return(number); + } + + /* if Word is s then return TRUE else return FALSE*/ + int word_is(char *s) + { + return strcmp(word, s) == 0; + } + + /* if next printable character is c then skip, else generate error */ + void expect_char(char c) + { + skip_spaces(); + if (nextChar == c) { getChar(); return; } + printf("Expected Character '%c' ", c); + exit_error(EXIT_FAILURE); + } + + /* add identifier(s) of type t*/ + void add_idents(int t) + { + DEBUG(">Processing Identifier(s) of type %d\n", t); + skip_spaces(); + if (isAlpha()) + { + get_word(); + DEBUG(">adding Identifier %s\n", word); + strncpy(var_name[var_count], word, VARLEN); + var_type[var_count] = t; + var_count++; + expect_char(';'); + } + } + + /* output a single line of assembly code */ + void asm_line(char *label, char *opcode, char *operand) + { + fprintf(out_file, "%-7s %-3s %s\n", label, opcode, operand); + } + + void parse_term() + { + //todo: differentiate between numbers and variables + //check variable name against variable table + if (!isAlphaNum()) + expected("term"); + get_word(); + strcpy(term, word); + DEBUG(">parsing term '%s'\n", term); + skip_spaces(); + } + + void first_term() + { + parse_term(); + DEBUG(">processing term '%s'\n", term); + asm_line("", "LDA", term); + } + + void parse_operator() + { + if (strchr("+-&|^", nextChar) == NULL) + expected("operator"); + operator = nextChar; + DEBUG(">parsing operator '%c'\n", operator); + getChar(); + skip_spaces(); + } + + void process_operator() + { + DEBUG(">processing operator '%c'\n", operator); + switch(operator) + { + case '+': + asm_line("","CLC", ""); + asm_line("","ADC", term); + break; + case '-': + asm_line("","SEC", ""); + asm_line("","SBC", term); + break; + case '&': + asm_line("","AND", term); + break; + case '|': + asm_line("","ORA", term); + break; + case '^': + asm_line("","EOR", term); + break; + default: + printf("Unrecognized operator '%c'\n", operator); + exit_error(EXIT_FAILURE); + } + } + + void parse_expr() + { + DEBUG(">parsing expression\n", 0); + skip_spaces(); + if (match('-')) + asm_line("", "LDA", "#0"); //handle unary minus + else + first_term(); + while (!match(';')) + { + parse_operator(); + parse_term(); + process_operator(); + } + } + + /* parse and compile assignment */ + void parse_assignment() + { + DEBUG(">parsing assignment", 0); + char var_assign[VARLEN+1]; + strcpy(var_assign, word); //save variable to assign to + getChar(); //skip equals sign + parse_expr(); + asm_line("", "STA", var_assign); + } + + /* parse and compile program statement */ + void parse_statement() + { + DEBUG(">parsing statement\n", 0); + skip_spaces(); + if (match('=')) + parse_assignment(); + else + expected("="); + } + + /* Reads and parses the next Word in Source File */ + parse_word() + { + get_word(); + DEBUG(">Parsing Word '%s'\n", word); + if (word_is("byte")) add_idents(BYTE); + else if (word_is("char")) add_idents(CHAR); + else parse_statement(); + } + + /* Advance Source File to end of comment * + * Recognizes both C and C++ style comments */ + void skip_comment() + { + DEBUG(">Skipping Comment\n", 0); + getChar(); + if (match('/')) //if C style comment + skip_to_eol(); // skip rest of line + else if (match('*')) //if C++ style comment + while (TRUE) // skip to "*/" + { + getChar(); if (!match('*')) continue; + getChar(); if (!match('/')) continue; + getChar(); + break; + //todo: add code to catch unterminated comment + } + else //if neither + expected("/ or *"); // error out + } + + void asm_prolog() + { + fprintf(out_file, ";Program %s\n", src_name); + fprintf(out_file, " CPU 6502\n"); + } + + /* Write Variable Table */ + void asm_vartbl() + { + int i; + for (i=0; iStarting Compilation\n",0); + init(); + asm_prolog(); + getChar(); + while ( TRUE ) + { + if (match(EOF)) break; + if (match('/')) + skip_comment(); + else if (isAlpha()) + parse_word(); + else + { + fprintf(log_file, "%c", nextChar); + getChar(); + } + } + asm_epilog(); + } + + /* Display "Usage" text and exit*/ + void usage() + { + printf("Usage: c02 sourcefile.c02\n"); + exit(EXIT_FAILURE); + } + + /* Parse Command Line Arguments * + * Sets: src_name - Source File Name (from first arg) * + * out_name - Output File Name (from optional second arg) */ + void parse_args(int argc, char *argv[]) + { + DEBUG(">Parsing %d Arguments\n", argc); + if (argc < 2) usage(); //at least one argument is required + strcpy(src_name, argv[1]); //set Source File Name to first arg + DEBUG(">src_name set to '%s'\n", src_name); + if (argc > 2) //if second argument exists + strcpy(out_name, argv[2]); //set Out File Name to second arg + else strcpy(out_name, ""); //else set to null string + DEBUG(">out_name set to '%s'\n", out_name); + } + + /* Open Source File s * + * Uses: src_name - Source File Name * + * Sets: src_file - Source File Handle */ + void open_src_file() + { + DEBUG(">Processing Source File Name '%s'\n", src_name); + if (strrchr(src_name, '.') == NULL) //if no extension + strcat(src_name, ".c02"); // add ".c02" + DEBUG(">opening Source File '%s'\n", src_name); + src_file = fopen(src_name, "r"); //open file + if (src_file == NULL) exit_perror(); + } + + /* Open Output File * + * Uses: out_name - Output File Name * + * Sets: out_file - Output File Handle */ + void open_out_file() + { + DEBUG(">Processing Output File Name '%s'\n", out_name); + if (strlen(out_name) == 0) //if Output File not specified + { + strcpy(out_name, src_name); //copy Source Name to Ouput Name + char *dot = strrchr(out_name, '.'); //find extension + if (dot != NULL) *dot = 0; //and remove it + DEBUG(">set Output File Name to '%s'\n", out_name); + } + if (strrchr(out_name, '.') == NULL) //if no extension + strcat(out_name, ".asm"); // add ".asm" + DEBUG(">opening Output File '%s'\n", out_name); + out_file = fopen(out_name, "w"); //open file + if (out_file == NULL) exit_perror(); + } + + /* Open Log File * + * Uses: src_name - Source File Name * + * Sets: log_file - Log File Handle */ + void open_log_file() + { + strcpy(log_name, src_name); //set Log File Name to Source File Name + char *dot = strrchr(log_name, '.'); //find file extension + if (dot != NULL) *dot = 0; //and remove it + strcat(log_name, ".log"); //add extension ".asm" + DEBUG(">Opening Log File '%s'\n", log_name); + log_file = fopen(log_name, "w"); + if (log_file == NULL) exit_perror(); + } + + /* Print Variable Table to Log File */ + void log_vars() + { + int i; + fprintf(log_file, "\n%-31s %s\n", "Variable", "Type"); + for (i=0; i