mirror of
https://github.com/RevCurtisP/C02.git
synced 2024-11-25 21:33:44 +00:00
455 lines
11 KiB
C
455 lines
11 KiB
C
/*************************************
|
|
* C02 Input File Parsing Routines *
|
|
*************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "common.h"
|
|
#include "files.h"
|
|
#include "parse.h"
|
|
|
|
/* Various tests against nxtchr */
|
|
int match(char c) {return TF(nxtchr == c);}
|
|
int inbtwn(char mn, char mx) {return TF(nxtchr >= mn && nxtchr <= mx);}
|
|
int isprnt() {return isprint(nxtchr);}
|
|
int isalph() {return isalpha(nxtchr);}
|
|
int isanum() {return isalnum(nxtchr);}
|
|
int isdec() {return inbtwn('0', '9');}
|
|
int ishexd() {return TF(isdec() || inbtwn('A', 'Z'));}
|
|
int isbin() {return inbtwn('0', '1');}
|
|
int isnl() {return TF(match('\n') || match('\r'));}
|
|
int isspc() {return isspace(nxtchr);}
|
|
int isnpre() {return TF(isdec() || match('$') || match('%'));}
|
|
int isapos() {return match('\'');}
|
|
int isbpre() {return TF(isnpre() || isapos());}
|
|
int ishash() {return match('#');}
|
|
int iscpre() {return TF(isbpre() || ishash());}
|
|
int isvpre() {return TF(isalph() || iscpre());}
|
|
int isoper() {return TF(strchr("+-&|^", nxtchr));}
|
|
int ispopr() {return TF(strchr("+-<>", nxtchr));}
|
|
int isxpre() {return TF(isvpre() || match('-'));}
|
|
|
|
/* if Word is s then return TRUE else return FALSE*/
|
|
int wordis(char *s)
|
|
{
|
|
return strcmp(word, s) == 0;
|
|
}
|
|
|
|
/* Get Next Character from Current Input File *
|
|
* Uses: inpfil - Input File Handle *
|
|
* Sets: nxtchr - Next Character in Source File */
|
|
char getnxt()
|
|
{
|
|
int i;
|
|
int wascr = match('\r');
|
|
char c = nxtchr;
|
|
//if (nxtwrd[nxtptr]) //If nxtwrd is set
|
|
// nxtchr = nxtwrd[nxtptr++]; // Pop First Character from nxtwrd
|
|
//else
|
|
nxtchr = fgetc(inpfil);
|
|
nxtupc = toupper(nxtchr);
|
|
if (wascr && match('\n')) return c; //Skip NL after CR
|
|
if (isnl()) curcol=1; else curcol++;
|
|
if (curcol == 1) curlin++;
|
|
return c;
|
|
}
|
|
|
|
/* Advance Input File to next printable character */
|
|
void skpspc()
|
|
{
|
|
//DEBUG("Skipping Spaces\n", 0);
|
|
if (isspc()) CCMNT(' ');
|
|
while (isspc())
|
|
getnxt();
|
|
}
|
|
|
|
/* Check if the next printable character is c *
|
|
* and advance past it if it is *
|
|
* Returns TRUE is character is found, *
|
|
* otherwise FALSE */
|
|
int look(char c)
|
|
{
|
|
int found;
|
|
skpspc();
|
|
found = match(c);
|
|
if (found) {
|
|
skpchr();
|
|
CCMNT(c);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/* if next printable character is c then skip, else generate error */
|
|
void expect(char c)
|
|
{
|
|
if (c == 0) return;
|
|
if (look(c)) return;
|
|
else {
|
|
printf("Expected Character '%c', but found '%c'\n", c, nxtchr);
|
|
exterr(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Advance Input File to next printable character */
|
|
void skpchr() {char skip = getnxt();}
|
|
|
|
/* Advance Input File to end of line */
|
|
void skpeol() {while (!isnl()) getnxt();}
|
|
|
|
/* Advance Source File to end of comment *
|
|
* Recognizes both C and C++ style comments */
|
|
void skpcmt()
|
|
{
|
|
DEBUG("Skipping Comment\n", 0);
|
|
skpchr(); //skip initial /
|
|
if (match('/')) //if C style comment
|
|
skpeol(); // skip rest of line
|
|
else if (match('*')) //if C++ style comment
|
|
while (TRUE) // skip to */
|
|
{
|
|
skpchr();
|
|
if (!match('*')) continue;
|
|
while (match('*')) skpchr();
|
|
if (!match('/')) continue;
|
|
skpchr();
|
|
break;
|
|
//todo: add code to catch unterminated comment
|
|
}
|
|
else //if neither
|
|
expctd("/ or *"); // error out
|
|
}
|
|
|
|
/* Reads next Word in current Input File, where *
|
|
* a Word is a sequence of AlphaNumeric characters *
|
|
* Sets: word - the Word read from the source file */
|
|
void getwrd()
|
|
{
|
|
int wrdlen = 0;
|
|
skpspc();
|
|
if (!isalph()) expctd("Alphabetic Character");
|
|
while (isanum())
|
|
{
|
|
word[wrdlen++] = toupper(getnxt());
|
|
}
|
|
word[wrdlen] = 0;
|
|
}
|
|
|
|
char escape(char c)
|
|
{
|
|
DEBUG("Escaping character '%c'\n", c);
|
|
switch (c) {
|
|
case 'r': return 0x0d;
|
|
default: return c;
|
|
}
|
|
}
|
|
|
|
/* Get String */
|
|
|
|
void getstr() {
|
|
char strdel, tmpchr;
|
|
int wrdlen = 0, escnxt = FALSE;
|
|
DEBUG("Parsing string\n", 0);
|
|
strdel = getnxt(); //Get String Delimiter
|
|
CCMNT(strdel);
|
|
while(match(strdel) == escnxt) {
|
|
CCMNT(nxtchr);
|
|
if (escnxt) {
|
|
word[wrdlen++] = escape(getnxt());
|
|
escnxt = FALSE;
|
|
}
|
|
else {
|
|
if (match('\\'))
|
|
escnxt = TRUE;
|
|
else
|
|
word[wrdlen++] = nxtchr;
|
|
skpchr();
|
|
}
|
|
}
|
|
skpchr(); //Skip End Delimiter
|
|
CCMNT(strdel);
|
|
word[wrdlen++] = 0;
|
|
}
|
|
|
|
/* Read Binary number from input file *
|
|
* a Binary is a series of binary digits (0 or 1) *
|
|
* prefixed with '%' *
|
|
* Sets: word - binary number including leading '%' *
|
|
* Returns: integer value of number */
|
|
int prsbin()
|
|
{
|
|
int wrdlen = 0;
|
|
int digit;
|
|
int number = 0;
|
|
if (!match('%'))
|
|
expctd("hexadecimal number");
|
|
word[wrdlen++] = nxtchr;
|
|
getnxt();
|
|
while (isbin()) {
|
|
word[wrdlen++] = nxtchr;
|
|
digit = nxtchr - '0';
|
|
number = number * 2 + digit;
|
|
skpchr();
|
|
}
|
|
word[wrdlen] = 0;
|
|
return (number);
|
|
}
|
|
|
|
/* Read Decimal number from input file *
|
|
* a Decimal is a series of digits (0-9) *
|
|
* Sets: word - number without leading 0's *
|
|
* Returns: integer value of number */
|
|
int prsdec()
|
|
{
|
|
int wrdlen = 0;
|
|
int digit;
|
|
int number = 0;
|
|
if (!isdec()) expctd("Digit");
|
|
while (isdec())
|
|
{
|
|
if (!match('0') || number != 0) //skip leading zeros
|
|
word[wrdlen++] = nxtchr;
|
|
digit = getnxt() - '0';
|
|
number = number * 10 + digit;
|
|
}
|
|
if (number == 0) word[wrdlen++] = '0';
|
|
word[wrdlen] = 0;
|
|
return(number);
|
|
}
|
|
|
|
/* Reads Hexadecimal number from input file *
|
|
* Sets: word - Hex number including leading '$' *
|
|
* Returns: integer value of number */
|
|
int prshex()
|
|
{
|
|
int wrdlen = 0;
|
|
int digit;
|
|
int number = 0;
|
|
DEBUG("Parsing hexadecimal constant\n", 0);
|
|
if (!match('$'))
|
|
expctd("hexadecimal number");
|
|
word[wrdlen++] = getnxt();
|
|
while (ishexd()) {
|
|
DEBUG("Found hex digit '%c'\n", nxtchr);
|
|
word[wrdlen++] = nxtchr;
|
|
if (isdec())
|
|
digit = nxtchr - '0';
|
|
else
|
|
digit = nxtchr - 'A' + 10;
|
|
number = number * 16 + digit;
|
|
skpchr();
|
|
}
|
|
word[wrdlen] = 0;
|
|
return (number);
|
|
}
|
|
|
|
/* Reads Character constant from input file *
|
|
* Sets: word - Character constant including *
|
|
* single quotes *
|
|
* Returns: ASCII value of constant */
|
|
int prschr()
|
|
{
|
|
int wrdlen = 0;
|
|
char c;
|
|
DEBUG("Parsing character constant\n", 0);
|
|
expect('\'');
|
|
word[wrdlen++] = '\'';
|
|
if (match('\\'))
|
|
word[wrdlen++] = getnxt();
|
|
c = getnxt();
|
|
DEBUG("Extracted character %c\n", c);
|
|
word[wrdlen++] = c;
|
|
expect('\'');
|
|
word[wrdlen++] = '\'';
|
|
word[wrdlen] = 0;
|
|
return (int)c;
|
|
}
|
|
|
|
/* Parse numeric value *
|
|
* Args: maxval - maximum allowed value *
|
|
* Sets: value - parsed number (as string) *
|
|
* Returns: parsed number */
|
|
int prsnum(int maxval)
|
|
{
|
|
int number;
|
|
skpspc();
|
|
if (!isbpre()) expctd("constant value");
|
|
switch(nxtchr) {
|
|
case '%':
|
|
number = prsbin();
|
|
break;
|
|
case '$':
|
|
number = prshex();
|
|
break;
|
|
case '\'':
|
|
number = prschr();
|
|
break;
|
|
default:
|
|
number = prsdec();
|
|
}
|
|
DEBUG("Parsed number '%s'\n", word);
|
|
DEBUG("with value '%d'\n", number);
|
|
|
|
if (number > maxval) {
|
|
ERROR("Out of bounds constant '%d';\n", number, EXIT_FAILURE);
|
|
}
|
|
if (maxval > 255)
|
|
sprintf(value, "$%04X", number);
|
|
else
|
|
sprintf(value, "$%02X", number);
|
|
return number;
|
|
}
|
|
|
|
int prsbyt()
|
|
{
|
|
return prsnum(0xFF);
|
|
}
|
|
|
|
void fnddef(char *name)
|
|
{
|
|
DEBUG("Looking up defined constant '%s'\n", word);
|
|
for (defidx=0; defidx<defcnt; defidx++) {
|
|
if (strcmp(defnam[defidx], name) == 0)
|
|
return;
|
|
}
|
|
defidx = -1;
|
|
}
|
|
|
|
int prsdef()
|
|
{
|
|
expect('#');
|
|
getwrd(); //Get Constant Name
|
|
fnddef(word);
|
|
if (defidx < 0) {
|
|
ERROR("Undefined constant '%s'\n", word, EXIT_FAILURE);
|
|
}
|
|
strcpy(value, word);
|
|
return defval[defidx];
|
|
}
|
|
|
|
/* Parse numeric constant *
|
|
* Args: maxval - maximum allowed value *
|
|
* Sets: cnstnt - the constant (as an integer) *
|
|
* value - the constant (as a string) *
|
|
* valtyp - value type (CONSTANT) */
|
|
void prscon()
|
|
{
|
|
skpspc();
|
|
if (ishash())
|
|
cnstnt = prsdef();
|
|
else
|
|
cnstnt = prsbyt();
|
|
valtyp = CONSTANT;
|
|
ACMNT(word);
|
|
strcpy(value, "#");
|
|
strcat(value, word);
|
|
DEBUG("Generated constant '%s'\n", value);
|
|
}
|
|
|
|
/* Get Value Type */
|
|
int gettyp()
|
|
{
|
|
if (match('(')) return FUNCTION;
|
|
else if (match('[')) return ARRAY;
|
|
else return VARIABLE;
|
|
}
|
|
|
|
/* Parse next word as variable or function name *
|
|
* Args: alwreg - Allow Register Names *
|
|
* Sets: value - Identifier Name *
|
|
* valtyp - Identifier Type */
|
|
void prsvar(int alwreg)
|
|
{
|
|
getwrd();
|
|
valtyp = gettyp();
|
|
if (valtyp != FUNCTION) chksym(alwreg, word);
|
|
strcpy(value, word);
|
|
DEBUG("Parsed variable '%s'\n", value);
|
|
ACMNT(word);
|
|
}
|
|
|
|
/* Parse arithmetic or bitwise operator */
|
|
void prsopr()
|
|
{
|
|
if (!isoper)
|
|
expctd("Arithmetic or bitwise operator");
|
|
oper = getnxt();
|
|
DEBUG("Parsed operator '%c'\n", oper);
|
|
CCMNT(oper);
|
|
skpspc();
|
|
}
|
|
|
|
void prcpst(char* name)
|
|
{
|
|
DEBUG("Processing post operation '%c'\n", oper);
|
|
switch(oper)
|
|
{
|
|
case '+':
|
|
if (strcmp(name, "X")==0)
|
|
asmlin("INX", "");
|
|
else if (strcmp(name, "Y")==0)
|
|
asmlin("INY", "");
|
|
else if (strcmp(name, "A")==0)
|
|
poperr(); //65C02 supports implicit INC, 6502 does not
|
|
else
|
|
asmlin("INC", name);
|
|
break;
|
|
case '-':
|
|
if (strcmp(name, "X")==0)
|
|
asmlin("DEX", "");
|
|
else if (strcmp(name, "Y")==0)
|
|
asmlin("DEY", "");
|
|
else if (strcmp(name, "A")==0)
|
|
poperr(); //65C02 supports implicit DEC, 6502 does not
|
|
else
|
|
asmlin("DEC", name);
|
|
break;
|
|
case '<':
|
|
if (strcmp(name, "X")==0)
|
|
poperr(); //Index Register Shift not Supported
|
|
else if (strcmp(name, "Y")==0)
|
|
poperr(); //Index Register Shift not Supported
|
|
else if (strcmp(name, "A")==0)
|
|
asmlin("ASL", "");
|
|
else
|
|
asmlin("ASL", name);
|
|
break;
|
|
case '>':
|
|
if (strcmp(name, "X")==0)
|
|
poperr(); //Index Register Shift not Supported
|
|
else if (strcmp(name, "Y")==0)
|
|
poperr(); //Index Register Shift not Supported
|
|
else if (strcmp(name, "A")==0)
|
|
asmlin("LSR", "");
|
|
else
|
|
asmlin("LSR", name);
|
|
break;
|
|
default:
|
|
printf("Unrecognized post operator '%c'\n", oper);
|
|
exterr(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Parse Post Operator */
|
|
int prspst(char trmntr, char* name) {
|
|
oper = getnxt();
|
|
CCMNT(oper);
|
|
DEBUG("Checking for post operation '%c'\n", oper);
|
|
if (nxtchr == oper) {
|
|
skpchr();
|
|
CCMNT(oper);
|
|
expect(trmntr);
|
|
prcpst(name); //Process Post-Op
|
|
oper = 0;
|
|
}
|
|
else {
|
|
DEBUG("Not a post operation\n", 0);
|
|
}
|
|
return oper;
|
|
}
|
|
|
|
|