mirror of
https://github.com/RevCurtisP/C02.git
synced 2024-11-22 01:31:33 +00:00
Initial commit of c02.c and test.asm
This commit is contained in:
commit
0f32c48e22
455
c02.c
Normal file
455
c02.c
Normal file
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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; i<var_count; i++)
|
||||
{
|
||||
asm_line(var_name[i], "DB", "0");
|
||||
}
|
||||
}
|
||||
|
||||
void asm_epilog()
|
||||
{
|
||||
asm_vartbl();
|
||||
}
|
||||
|
||||
/* Compile Source Code*/
|
||||
void compile()
|
||||
{
|
||||
DEBUG(">Starting 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<var_count; i++)
|
||||
{
|
||||
fprintf(log_file, "%-31s %4d\n", var_name[i], var_type[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Close Source File */
|
||||
void close_src_file() { fclose(src_file); }
|
||||
|
||||
/* Close Output File */
|
||||
void close_out_file() { fclose(out_file); }
|
||||
|
||||
/* Close Log File */
|
||||
void close_log_file() { fclose(log_file); }
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
debug = TRUE;
|
||||
|
||||
printf("C02 Compiler (C) 2012 Curtis F Kaylor\n" );
|
||||
|
||||
parse_args(argc, argv);
|
||||
open_src_file();
|
||||
open_out_file();
|
||||
open_log_file();
|
||||
|
||||
compile();
|
||||
|
||||
log_vars();
|
||||
|
||||
close_src_file();
|
||||
close_out_file();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user