#include "nbasic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIXTEENBIT 65536
//---------------------------------------
Token::Token(int num_, int type_, int line_, const char* val, int location_)
{
index = num_;
type = type_;
line = line_;
location = location_;
next = prev = NULL;
int len = strlen(val);
if (len == 0)
{
value = NULL;
return;
}
value = new char[len+1];
memset(value,0,len+1);
strcpy(value, val);
valuelen = len;
/*
if (type == INCBIN)
{
printf("incbin\n");
fflush(stdout);
}
*/
}
Token::~Token()
{
if (value != NULL)
delete value;
if (next != NULL)
delete next;
}
//---------------------------------------
TokenList::TokenList()
{
num_tokens = 0;
first = marker = last = NULL;
}
TokenList::~TokenList()
{
last = NULL;
marker = NULL;
if (first != NULL) delete first; //blows away whole list safely
}
bool TokenList::Match(int start, int type)
{
const Token *t = GetToken(start);
return (t != NULL && t->type == type);
}
bool TokenList::Match(int start, int type0, int type1)
{
return Match(start, type0) && Match(start+1, type1);
}
bool TokenList::Match(int start, int type0, int type1, int type2)
{
return Match(start, type0, type1) && Match(start+2, type2);
}
bool TokenList::Match(int start, int type0, int type1, int type2, int type3)
{
return Match(start, type0, type1, type2) && Match(start+3, type3);
}
void TokenList::AddToken(int type, const char *val, int len, int linenum, int loc)
{
char str[len+1];
memset(str,0,len+1);
strncpy(str,val,len);
Token *t = new Token(num_tokens, type, linenum, str, loc);
num_tokens++;
if(first == NULL)
{
first = t;
last = t;
}
else
{
last->next = t;
t->prev = last;
last = last->next;
}
}
void TokenList::RemoveToken(int n)
{
//set marker to the target token, and get handles on surrounding tokens
if (first == NULL || n < 0 || n >= num_tokens) return;
if (marker == NULL) marker = first;
while(marker->index < n) marker = marker->next;
while(marker->index > n) marker = marker->prev;
Token* tnext = marker->next;
Token* tprev = marker->prev;
//delete the token
if (first == marker) first = tnext; //so we don't lose the whole stream
marker->next = NULL;
marker->prev = NULL;
delete marker;
num_tokens--;
//re-point marker and hook up others
if (tprev != NULL) marker = tprev;
else marker = tnext;
if (tprev !=NULL) tprev->next = tnext;
if (tnext !=NULL) tnext->prev = tprev;
//decrement downstream index nums
Token* temp = tnext;
while (temp != NULL)
{
temp->index--;
temp = temp->next;
}
}
const Token* TokenList::GetToken(int n)
{
if (first == NULL || n < 0 || n >= num_tokens) return NULL;
if (marker == NULL) marker = first;
while(marker->index < n) marker = marker->next;
while(marker->index > n) marker = marker->prev;
return marker;
}
Token* TokenList::GetTokenPointer(int n)
{
if (GetToken(n) == NULL) return NULL;
return marker;
}
void TokenList::SetTokenLocation(int i, int loc)
{
if (GetToken(i) != NULL)
marker->location = loc;
}
bool TokenList::HasToken(const char *value)
{
const Token *t = first;
while(t!=NULL)
{
if (!strcmp(t->value, value)) return true;
t = t->next;
}
return false;
}
int TokenList::NumTokens() const
{
return num_tokens;
/*
int n = 0;
const Token *t = first;
while(t!=NULL)
{
n++;
t = t->next;
}
return n;
*/
}
//---------------------------------------
NBasic::NBasic()
{
verbose = true;
numfiles = 0;
numlines = 0;
num_regexes = 0;
next_autolabel = 1;
}
NBasic::~NBasic()
{
//empty
}
void NBasic::SetVerbosity(bool b)
{
verbose = b;
}
void NBasic::SetMaxFiles(int n)
{
filelines = new int[n];
filenames = new const char*[n];
}
int NBasic::TokenType(int i)
{
if (i<tokens.NumTokens())
return tokens.GetToken(i)->type;
return PAST_END_FILE;
}
const char* NBasic::TokenValue(int i)
{
return tokens.GetToken(i)->value;
}
void NBasic::AddRegEx(const char *exp, int result, bool require_whitespace)
{
if (num_regexes == MAX_REGEXES)
{
printf("ERROR: Max number of regular expressions. Change #define MAX_REGEXES\n");
exit(0);
}
regex_returns[num_regexes] = result;
regexes[num_regexes].Load(exp);
regex_requirewhite[num_regexes] = require_whitespace;
num_regexes++;
}
bool IsStartAsm(const char *s)
{
int i=0;
while(s[i]==' ' || s[i]=='\t') i++;
if (strncmp(&s[i],"asm",3)!=0) return false;
i+=3;
while(s[i]==' ' || s[i]=='\t' || s[i]=='\r' || s[i]=='\n') i++;
if (s[i]==0) return true;
return false;
}
bool IsEndAsm(const char *s)
{
int i=0;
while(s[i]==' ' || s[i]=='\t') i++;
if (strncmp(&s[i],"endasm",6)!=0) return false;
i+=6;
while(s[i]==' ' || s[i]=='\t' || s[i]=='\r' || s[i]=='\n') i++;
if (s[i]==0) return true;
return false;
}
bool IsAsmLine(const char *s, int &start)
{
int i=0;
while(s[i]==' ' || s[i]=='\t') i++;
if (strncmp(&s[i],"asmline",7)!=0) return false;
start = i+7;
return true;
}
bool NBasic::TokenizeLine(const char *line, int linenum)
{
static bool in_asm_block = false;
int len = strlen(line);
if (len == 0) return true; //sure, we can tokenize a blank line!
//special case, single ASM line
int asmstart = 0;
if (IsAsmLine(line,asmstart))
{
tokens.AddToken(ASM, NULL, 0, linenum);
tokens.AddToken(ASMTEXT, &line[asmstart], len-asmstart, linenum);
tokens.AddToken(ENDASM, NULL, 0, linenum);
return true;
}
//special case, entering ASM block
if (IsStartAsm(line))
{
tokens.AddToken(ASM, NULL, 0, linenum);
in_asm_block = true;
return true;
}
//special case, within ASM block
if (in_asm_block)
{
if (IsEndAsm(line))
{
tokens.AddToken(ENDASM, NULL, 0, linenum);
in_asm_block = false;
return true;
}
tokens.AddToken(ASMTEXT, line, len, linenum);
return true;
}
//general case, parse normally
for(int i = 0; i < num_regexes; i++) //try matching against each regex
{
int match = regexes[i].Matches(line);
if (match > -1)
{
//token match! check whitespace requirements
if ((!regex_requirewhite[i]) ||
(regex_requirewhite[i] &&
(
match == len ||
regexes[whitespace_regex].Matches(&line[match])>-1 ||
regexes[newline_regex].Matches(&line[match])>-1
)
))
{
//we're good. add the token, check the rest of the line
tokens.AddToken(regex_returns[i], line, match, linenum);
return TokenizeLine(&line[match], linenum);
}
}
}
return false;
}
void NBasic::TrimWhitespace()
{
//remove WHITESPACE, NEWLINE, and COMMENT tokens that aren't in an asm block
int i = 0;
bool asmblock = false;
while(i < tokens.NumTokens())
{
int type = TokenType(i);
if (asmblock)
{
if (type == ENDASM)
asmblock = false;
i++;
}
else
{
if (type == WHITESPACE || type == NEWLINE || type == COMMENT)
tokens.RemoveToken(i);
else i++;
if (type == ASM)
asmblock = true;
}
}
}
void NBasic::CompactMath()
{
bool done = false;
while(!done)
{
done = true; //set false when no longer true
for(int i = 0; i < tokens.NumTokens();)
{
if (IsMath(i) && tokens.Match(i+1,NUMBER,NUMBER))
{
int type = TokenType(i);
if (type==PLUS || type==MINUS || type==SHIFTLEFT || type==SHIFTRIGHT ||
type == BITAND || type == BITOR || type == BITEOR)
{
done = false;
Token *t = tokens.GetTokenPointer(i);
int one = NumberValue(TokenValue(i+1), t->line);
int two = NumberValue(TokenValue(i+2), t->line);
t->type = NUMBER;
if (t->value != NULL) delete t->value;
t->value = new char[8];
int result = (type==PLUS ? one+two : (type==MINUS ? one-two :
(type==SHIFTLEFT ? one<<two : (type==SHIFTRIGHT ? one>>two :
(type==BITAND?/*BITAND*/one&two:(type==BITOR?one | two:(one ^
two)))))));
sprintf(t->value,"%i\0", (result+SIXTEENBIT)%SIXTEENBIT);
tokens.RemoveToken(i+1);
tokens.RemoveToken(i+1);
i++;
}
else i+=3;
}
else
{
i++;
}
}
}
}
#define FBUFSIZE 1024
void NBasic::AddFile(const char *filename)
{
FILE *f = fopen(filename,"r");
if (!f)
{
fprintf(stderr,"Could not open file %s for reading. Aborting.\n",filename);
exit(0);
}
char buf[FBUFSIZE];
int line = 0;
while(fgets(buf,FBUFSIZE,f) != NULL)
{
line++;
TokenizeLine(buf,line+numlines);
}
fclose(f);
filenames[numfiles] = filename;
filelines[numfiles++] = line;
numlines += line;
if (verbose) printf("Read file %s (%i lines)\n",filename,line); fflush(stdout);
}
char* NBasic::MakeNumber(const char* n, int line)
{
int number = NumberValue(n, line);
char *s = new char[8]; //more than big enough
sprintf(s,"%i\0",number);
return s;
}
int NBasic::NumberValue(const char* n, int line)
{
int number = 0;
if (n[0] == '$')
{
for(int i = 1; i < strlen(n); i++)
number = number*16 +
(n[i]>='0'&&n[i]<='9' ? n[i]-'0' :
(n[i]>='a'&&n[i]<='f' ? n[i]-'a'+10 :
n[i]-'A'+10));
}
else if (n[0]=='%')
{
for(int i = 1; i < strlen(n); i++)
number = number*2+(n[i]=='0' ? 0 : 1);
}
else if (n[0]>='0' && n[0]<='9')
{
for(int i = 0; i < strlen(n); i++)
number = number*10+(n[i]-'0');
}
else CompileError("number",line);
number = number % SIXTEENBIT; //we'll never need anything bigger than that (CPU limitation)
return number;
}
void NBasic::CompileError(const char *s, int line)
{
int whichline = line;
int whichfile = 0;
while (whichline > filelines[whichfile])
{
whichline -= filelines[whichfile];
whichfile++;
}
fprintf(stderr,"\nERROR on line %i of file '%s' compiling %s statement.\n",
whichline,filenames[whichfile],s);
exit(0);
}
void NBasic::Compile()
{
TrimWhitespace();
CompactMath();
AddVariable("nbasic_stack", 0x100, 2, 0x100);
int numtokens = tokens.NumTokens();
int i = 0;
while (i < numtokens)
{
i += Compile(i);
}
}
int NBasic::Compile(int i)
{
const Token* t = tokens.GetToken(i);
int type = t->type;
int line = t->line;
//printf("line %i: %s\n", line, t->value);fflush(stdout);
switch(type)
{
case ENDPROG: //deprecated
if (verbose) printf("Encountered program end at line %i\n",line);
return tokens.NumTokens(); //past end of input. stop compile
case ASM:
return CompileAsm(i, type, line);
case LABEL:
case ENDASM:
case ARRAY:
return CompileIdentifiers(i, type, line);
case PUSH:
case POP:
return CompilePush(i, type, line);
case GOTO:
case GOSUB:
case RETURN:
case RESUME:
return CompileJump(i, type, line);
case COMMENT:
case WHITESPACE:
return 1;
case DATA:
return CompileData(i, type, line);
case SET:
return CompileSet(i, type, line);
case INC:
case DEC:
return CompileIncrement(i, type, line);
case IF:
return CompileConditional(i, type, line);
default:
break;
}
CompileError("",tokens.GetToken(i)->line);
return -1;
}
void NBasic::CreateAutoVariables()
{
int n = asmlist.NumTokens();
for(int i = 0; i < n; i++)
{
const Token *t = asmlist.GetToken(i);
if (t->type==STAp && (t->value[0]<'0' || t->value[0]>'9'))
AddVariable(t->value);
}
}
void NBasic::VarError(const char* name)
{
fprintf(stderr, "Redefinition of variable %s. Aborting.\n", name);
exit(0);
}
void NBasic::VarAllocError(const char* name)
{
fprintf(stderr, "Error allocating memory for variable %s. Aborting.\n", name);
exit(0);
}
void NBasic::OutputVariables(FILE *f)
{
TokenList tempvars;
int numtokens = variables.NumTokens();
//add absolute location vars
for(int i=0; i<numtokens; i++)
{
const Token *t = variables.GetToken(i);
if (t->type == 2)
{
if(tempvars.HasToken(t->value)) VarError(t->value);
tempvars.AddToken(t->type, t->value, t->valuelen, t->line, t->location);
}
}
//add zeropage vars
for(int i=0; i<numtokens; i++)
{
const Token *t = variables.GetToken(i);
if (t->type == 1)
{
if(tempvars.HasToken(t->value)) VarError(t->value);
tempvars.AddToken(t->type, t->value, t->valuelen, t->line, t->location);
}
}
//add auto-allocated pre-sized vars
for(int i=0; i<numtokens; i++)
{
const Token *t = variables.GetToken(i);
if (t->type == 0 && t->line != -1)
{
if(tempvars.HasToken(t->value)) VarError(t->value);
tempvars.AddToken(t->type, t->value, t->valuelen, t->line, t->location);
}
}
//add auto-allocated default vars
for(int i=0; i<numtokens; i++)
{
const Token *t = variables.GetToken(i);
if (t->type == 0 && t->line == -1)
if(!tempvars.HasToken(t->value))
tempvars.AddToken(t->type, t->value, t->valuelen, 1, t->location);
}
//now try to allocate memory for each variable
int numvars = tempvars.NumTokens();
bool memory[SIXTEENBIT]; //true means filled
for(int i=0; i<SIXTEENBIT; i++) memory[i] = (i < 2048 ? false : true);
for(int i=0; i<numvars; i++)
{
const Token *t = tempvars.GetToken(i);
int start = t->location;
int len = t->line;
if(t->type == 2 && len != 0) //absolute
{
for(int j = start; j < start+len; j++)
{
if(memory[j]) VarAllocError(t->value);
memory[j] = true;
}
}
else if (t->type == 1 && len != 0) //zeropage
{
start = 0;
bool found = false;
while(start < 0x100-len && !found)
{
if(!memory[start])
{
found = true;
for(int j = 0; j < len; j++)
if(memory[start+j]) found = false;
}
if (!found) start++;
}
if (!found) VarAllocError(t->value);
for(int j = 0; j < len; j++) memory[start+j] = true;
tempvars.SetTokenLocation(i, start);
}
else if(len != 0) //auto allocate
{
start = 0;
bool found = false;
while(start < 0xffff-len && !found)
{
if(!memory[start])
{
found = true;
for(int j = 0; j < len; j++)
if(memory[start+j]) found = false;
}
if (!found) start++;
}
if (!found) VarAllocError(t->value);
for(int j = 0; j < len; j++) memory[start+j] = true;
tempvars.SetTokenLocation(i, start);
}
}
//actually output them now
for(int i=0; i<numvars; i++)
{
const Token *t = tempvars.GetToken(i);
fprintf(f, "%s = %i\n", t->value, t->location);
}
}
bool NBasic::VariableExists(const char* name)
{
for(int i = 0; i < variables.NumTokens(); i++)
if (!strcmp(name, variables.GetToken(i)->value))
return true;
return false;
}
void NBasic::AddVariable(const char* name, int length, int vartype, int loc)
{
if(vartype==2 && strcmp(name,"nbasic_stack")) //warn user if it conflicts with the stack
{
for(int i=loc; i < loc+length; i++)
if (i>=256 && i<= 511)
{
fprintf(stderr,"Declaration of variable '%s' conflicts with NES stack ($100-$1FF)\n",name);
fprintf(stderr,"Compile Aborted\n");fflush(stdout);
exit(0);
}
}
variables.AddToken(vartype, name, strlen(name), length, loc);
}
int NBasic::CompileIdentifiers(int i, int type, int line)
{
const Token* t;
switch(type)
{
case LABEL:
t = tokens.GetToken(i);
asmlist.AddToken(ASMLABEL, t->value, t->valuelen, line);
return 1;
case ARRAY:
if(tokens.Match(i,ARRAY,VAR,NUMBER))
{
AddVariable(TokenValue(i+1),
NumberValue(TokenValue(i+2),line));
return 3;
}
else if(tokens.Match(i,ARRAY,ZEROPAGE,VAR,NUMBER))
{
AddVariable(TokenValue(i+2),
NumberValue(TokenValue(i+3),line), 1);
return 4;
}
else if(tokens.Match(i+1,/*ARRAY,*/ABSOLUTE,NUMBER,VAR,NUMBER))
{
AddVariable(TokenValue(i+3),
NumberValue(TokenValue(i+4),line),2,
NumberValue(TokenValue(i+2),line));
return 5;
}
CompileError("array declaration",line); //invalid array declaration syntax
case ENDASM:
CompileError("endasm",line); //should never see it here
}
}
int NBasic::CompileAsm(int i, int type, int line)
{
bool done = false;
int added = 1;
while (!done)
{
const Token* t = tokens.GetToken(i + added++);
if (t!=NULL && t->type!=ENDASM)
asmlist.AddToken(ASMTEXT, t->value, t->valuelen, t->line);
else
done = true;
}
return added;
}
int NBasic::CompileJump(int i, int type, int line)
{
const Token* t;
switch(type)
{
case RESUME:
asmlist.AddToken(RTI, "", 0, line);
return 1;
case RETURN:
asmlist.AddToken(RTS, "", 0, line);
return 1;
case GOTO:
case GOSUB:
if (tokens.Match(i+1, VAR))
{
t = tokens.GetToken(i+1);
asmlist.AddToken((type==GOTO ? JMP : JSR),
t->value, t->valuelen, line);
return 2;
}
break;
}
CompileError(type==GOTO ? "goto" : "gosub", line);
}
bool IsHex(char c)
{
if ('0'<=c && c<='9') return true;
if ('a'<=c && c<='f') return true;
if ('A'<=c && c<='F') return true;
return false;
}
int HexValue(char c)
{
if ('0'<=c && c<='9') return c-'0';
if ('a'<=c && c<='f') return c-'a'+10;
if ('A'<=c && c<='F') return c-'A'+10;
return 0;
}
int NBasic::CompileData(int toknum, int type, int line)
{
const Token *t = tokens.GetToken(toknum);
if (t==NULL || t->value==NULL) CompileError("data", line);
const char *s = t->value;
int i = 4, len = strlen(s), numints=0;
if (len > 80) CompileError("data (line too long)", line);
int ints[len];
while (s[i]==' ' || s[i]=='\t') i++; //skip initial whitespace
//value line includes data keyword until (including) \n
bool in_string = false;
bool between_values = false;
while (s[i]!='\n' && s[i]!=0)
{
if (between_values)
{
while (s[i]==' ' || s[i]=='\t') i++; //whitespace
if (s[i++]!=',') CompileError("data",line);
while (s[i]==' ' || s[i]=='\t') i++; //whitespace
between_values = false;
}
else if (!in_string)
{
if (s[i]=='\"')
{
in_string = true;
i++;
}
else if (s[i]=='$') //hexadecimal
{
int temp=0;
i++;
if (!IsHex(s[i])) CompileError("data (hexadecimal)", line);
while (IsHex(s[i]))
temp = temp*16+HexValue(s[i++]);
ints[numints++] = temp;
between_values = true;
}
else if (s[i]=='%') //binary
{
int temp=0;
i++;
if (s[i]!='0' && s[i]!='1') CompileError("data (binary)", line);
while (s[i]=='0' || s[i]=='1')
temp = temp*2 + (s[i]-'0');
ints[numints++] = temp;
between_values = true;
}
else if ('0'<=s[i] && s[i]<='9') //decimal
{
int temp = 0;
while ('0'<=s[i] && s[i]<='9')
temp = temp*10 + s[i++]-'0';
if (temp>255) CompileError("data (value too large)", line);
ints[numints++] = temp;
between_values = true;
}
else between_values = true;
}
else
{
if (s[i]=='\"') //quote
{
in_string = false;
between_values = true;
i++;
}
else if (s[i]=='\\') //backslash
{
ints[numints++] = s[i+1];
i+=2;
}
else ints[numints++] = s[i++];
}
}
//now create the ASM output
int startint = 0;
while (startint < numints)
{
int endint = startint+10;
if (endint > numints) endint = numints;
char outbuf[64];
sprintf(outbuf, " .db ");
for (int i = startint; i < endint; i++)
{
if (i!=startint) sprintf(&outbuf[strlen(outbuf)], ",%i", ints[i]);
else sprintf(&outbuf[strlen(outbuf)], "%i", ints[i]);
}
sprintf(&outbuf[strlen(outbuf)], "\n");
startint += 10;
asmlist.AddToken(ASMTEXT, outbuf, strlen(outbuf), line);
}
return 1;
}
bool NBasic::IsConditional(int token_index)
{
switch(TokenType(token_index))
{
case ISEQUAL:
case NOTEQUAL:
case ISLESS:
case ISGREATER:
case LESSEQ:
case GREATEREQ:
return true;
default:
break;
}
return false;
}
int NBasic::ConditionalOpposite(int cond)
{
switch(cond)
{
case ISEQUAL:
return ISEQUAL;
case NOTEQUAL:
return NOTEQUAL;
case ISLESS:
return ISGREATER;
case ISGREATER:
return ISLESS;
case LESSEQ:
return GREATEREQ;
case GREATEREQ:
return LESSEQ;
}
return -1;
}
int NBasic::CompileConditional(int i, int type, int line)
{
int j = -1; //indicates start of second half of the conditional
int cond = -1; //which conditional
// A cond VAR
// A cond NUM
// X cond math
// Y cond math
if(
(tokens.Match(i+1,X) || tokens.Match(i+1,Y) || tokens.Match(i+1,A)) &&
IsConditional(i+2) &&
IsMath(i+3))
{
int reg = TokenType(i+1); //which register
cond = TokenType(i+2); //which conditional
int type = TokenType(i+3); //which math type
int len = 1;
if (type == VAR)
{
asmlist.AddToken(reg==A?CMPp:(reg==X?CPXp:CPYp), TokenValue(i+3), strlen(TokenValue(i+3)), line);
}
else if (type == NUMBER)
{
char *num = MakeNumber(TokenValue(i+3), line);
asmlist.AddToken(reg==A?CMPi:(reg==X?CPXi:CPYi), num, strlen(num), line);
delete num;
}
else //other math
{
//if(reg==A) CompileError("conditional",line);
len = CompileMath(i+3, type, line);
asmlist.AddToken(STAp,"nbasic_temp",strlen("nbasic_temp"),line);
asmlist.AddToken( reg==X ? CPXp : CPYp, "nbasic_temp", strlen("nbasic_temp"), line);
}
j = len + 3;
}
// VAR cond math (compile math, opposite cond)
// NUM cond math (compile math, opposite cond)
else if((tokens.Match(i+1,VAR) || tokens.Match(i+1,NUMBER)) && IsConditional(i+2) && IsMath(i+3))
{
cond = ConditionalOpposite(TokenType(i+2));
j = CompileMath(i+3, TokenType(i+3), line) + 3;
if (TokenType(i+1) == VAR)
asmlist.AddToken(CMPp, TokenValue(i+1), strlen(TokenValue(i+1)), line);
else
{
char *num = MakeNumber(TokenValue(i+1), line);
asmlist.AddToken(CMPi, num, strlen(num), line);
delete num;
}
}
// math cond VAR (same cond)
// math cond NUM (same cond)
// math cond math
else if (IsConditional(i+1+MathLength(i+1,line)) && IsMath(i+2+MathLength(i+1,line)))
{
int len = CompileMath(i+1, TokenType(i+1), line);
cond = TokenType(i+1+len);
if (TokenType(i+2+len) == VAR)
{
asmlist.AddToken(CMPp, TokenValue(i+2+len), strlen(TokenValue(i+2+len)), line);
j = len + 3;
}
else if (TokenType(i+2+len) == NUMBER)
{
char *num = MakeNumber(TokenValue(i+2+len), line);
asmlist.AddToken(CMPi, num, strlen(num), line);
delete num;
j = len + 3;
}
else
{
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
int two = CompileMath(i+2+len, TokenType(i+2+len), line);
cond = ConditionalOpposite(cond);
j = len + 2 + two;
}
}
if (j==-1) CompileError("conditional", line);
if (tokens.Match(i+j, BRANCHTO, VAR))
{
const char* label = TokenValue(i+j+1);
int len = strlen(label);
switch(cond) // branch on same case
{
case ISEQUAL: asmlist.AddToken(BEQ, label, len, line); break;
case NOTEQUAL: asmlist.AddToken(BNE, label, len, line); break;
case ISLESS: asmlist.AddToken(BMI, label, len, line); break;
case ISGREATER: asmlist.AddToken(BCS, label, len, line); break;
case LESSEQ: asmlist.AddToken(BCC, label, len, line); break;
case GREATEREQ: asmlist.AddToken(BPL, label, len, line); break;
default: CompileError("conditional branchto",line); break;
}
j+=2;
}
else
{
int my_autolabel = next_autolabel++;
char autolabel[24];
sprintf(autolabel,"nbasic_autolabel_%i\0",my_autolabel);
int len = strlen(autolabel);
switch(cond) // branch to autolabel on opposite case
{
case ISEQUAL:
asmlist.AddToken(BNE, autolabel, len, line);
break;
case NOTEQUAL:
asmlist.AddToken(BEQ, autolabel, len, line);
break;
case ISLESS:
asmlist.AddToken(BCS, autolabel, len, line);
break;
case ISGREATER:
asmlist.AddToken(BCC, autolabel, len, line);
asmlist.AddToken(BEQ, autolabel, len, line);
break;
case LESSEQ:
asmlist.AddToken(BCS, autolabel, len, line);
asmlist.AddToken(BEQ, autolabel, len, line);
break;
case GREATEREQ:
asmlist.AddToken(BCC, autolabel, len, line);
break;
default: CompileError("conditional branch",line); break;
}
// compile conditional code (check for THEN)
if (TokenType(i+j)==THEN)
{
j++;
int typeij = TokenType(i+j);
while(typeij!=ENDIF && typeij!=PAST_END_FILE)
{
j += Compile(i+j);
typeij = TokenType(i+j);
}
if (typeij == PAST_END_FILE)
CompileError("if..then without corresponding endif",line);
j++;
}
else
{
j += Compile(i+j);
}
// create autolabel
sprintf(autolabel,"nbasic_autolabel_%i:\0",my_autolabel);
asmlist.AddToken(ASMLABEL, autolabel, strlen(autolabel), line);
}
return j;
}
int NBasic::CompileSet(int i, int type, int line)
{
//=============set basic memory location
if (tokens.Match(i+1,VAR,A))
{
asmlist.AddToken(STAp, TokenValue(i+1), strlen(TokenValue(i+1)), line);
return 3;
}
else if (tokens.Match(i+1,NUMBER,A))
{
char *num = MakeNumber(TokenValue(i+1), line);
asmlist.AddToken(STAp, num, strlen(num), line);
delete num;
return 3;
}
if (tokens.Match(i+1,VAR,X))
{
asmlist.AddToken(TXA,"",0,line);
asmlist.AddToken(STAp, TokenValue(i+1), strlen(TokenValue(i+1)), line);
return 3;
}
else if (tokens.Match(i+1,NUMBER,X))
{
char *num = MakeNumber(TokenValue(i+1), line);
asmlist.AddToken(TXA,"",0,line);
asmlist.AddToken(STAp, num, strlen(num), line);
delete num;
return 3;
}
else if (tokens.Match(i,SET,VAR) && IsMath(i+2))
{
const Token *t = tokens.GetToken(i+2);
int len = CompileMath(i+2, t->type, t->line);
t = tokens.GetToken(i+1);
asmlist.AddToken(STAp, t->value, t->valuelen, line);
return len+2;
}
else if (tokens.Match(i,SET,NUMBER) && IsMath(i+2))
{
const Token *t = tokens.GetToken(i+2);
int len = CompileMath(i+2, t->type, t->line);
char *num = MakeNumber(TokenValue(i+1),line);
asmlist.AddToken(STAp, num, strlen(num), line);
delete num;
return len+2;
}
//================ set the accumulator register
else if (tokens.Match(i,SET,A,X))
{
asmlist.AddToken(TXA, "", 0, line);
return 3;
}
else if (tokens.Match(i,SET,A,Y))
{
asmlist.AddToken(TYA, "", 0, line);
return 3;
}
else if (tokens.Match(i,SET,A) && IsMath(i+2))
{
const Token *t = tokens.GetToken(i+2);
return 2 + CompileMath(i+2, t->type, t->line);
}
//================= set the X register
else if (tokens.Match(i,SET,X,A))
{
asmlist.AddToken(TAX, "", 0, line);
return 3;
}
else if (tokens.Match(i,SET,X,VAR))
{
const Token *t = tokens.GetToken(i+2);
asmlist.AddToken(LDXp, t->value, t->valuelen, t->line);
return 3;
}
else if (tokens.Match(i,SET,X,NUMBER))
{
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(LDXi, num, strlen(num), line);
delete num;
return 3;
}
else if (tokens.Match(i,SET,X) && IsMath(i+2))
{
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(TAX, "", 0, line);
return len + 2;
}
//=================== set the Y register
else if (tokens.Match(i,SET,Y,A))
{
asmlist.AddToken(TAY, "", 0, line);
return 3;
}
else if (tokens.Match(i,SET,Y,VAR))
{
const Token *t = tokens.GetToken(i+2);
asmlist.AddToken(LDYp, t->value, t->valuelen, t->line);
return 3;
}
else if (tokens.Match(i,SET,Y,NUMBER))
{
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(LDYi, num, strlen(num), line);
delete num;
return 3;
}
else if (tokens.Match(i,SET,Y) && IsMath(i+2))
{
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(TAY, "", 0, line);
return len + 2;
}
//=============set simple var array location
else if (tokens.Match(i+1,BRACKETOPEN,VAR,X,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
asmlist.AddToken(STApx, TokenValue(i+2), strlen(TokenValue(i+2)), line);
return len+5;
}
else if (tokens.Match(i+1,BRACKETOPEN,VAR,Y,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
asmlist.AddToken(STApy, TokenValue(i+2), strlen(TokenValue(i+2)), line);
return len+5;
}
else if (tokens.Match(i+1,BRACKETOPEN,VAR,VAR,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
asmlist.AddToken(LDXp, TokenValue(i+3), strlen(TokenValue(i+3)), line);
asmlist.AddToken(STApx, TokenValue(i+2), strlen(TokenValue(i+2)), line);
return len+5;
}
else if (tokens.Match(i+1,BRACKETOPEN,VAR,NUMBER,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
asmlist.AddToken(LDXi, TokenValue(i+3), strlen(TokenValue(i+3)), line);
asmlist.AddToken(STApx, TokenValue(i+2), strlen(TokenValue(i+2)), line);
return len+5;
}
//=============set simple number array location
else if (tokens.Match(i+1,BRACKETOPEN,NUMBER,X,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(STApx, num, strlen(num), line);
delete num;
return len+5;
}
else if (tokens.Match(i+1,BRACKETOPEN,NUMBER,Y,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(STApy, num, strlen(num), line);
delete num;
return len+5;
}
else if (tokens.Match(i+1,BRACKETOPEN,NUMBER,VAR,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(LDXp, TokenValue(i+3), strlen(TokenValue(i+3)), line);
asmlist.AddToken(STApx, num, strlen(num), line);
delete num;
return len+5;
}
else if (tokens.Match(i+1,BRACKETOPEN,NUMBER,NUMBER,BRACKETCLOSE) && IsMath(i+5))
{
int len = CompileMath(i+5, TokenType(i+5), line);
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(LDXi, TokenValue(i+3), strlen(TokenValue(i+3)), line);
asmlist.AddToken(STApx, num, strlen(num), line);
delete num;
return len+5;
}
//=============set complex number array location
// SET BRACKETOPEN VAR [math] BRACKETCLOSE [math]
else if (tokens.Match(i+1,BRACKETOPEN,VAR) && tokens.Match(i+3+MathLength(i+3,line),BRACKETCLOSE))
{
int len1 = CompileMath(i+3, TokenType(i+3), line);
asmlist.AddToken(PHA, "", 0, line);
int len2 = CompileMath(i+4+len1, TokenType(i+4+len1), line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA, "", 0, line);
asmlist.AddToken(TAX, "", 0, line);
asmlist.AddToken(LDAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(STApx, TokenValue(i+2), strlen(TokenValue(i+2)), line);
return 4 + len1 + len2;
}
// SET BRACKETOPEN NUMBER [math] BRACKETCLOSE [math]
else if (tokens.Match(i+1,BRACKETOPEN,NUMBER) && tokens.Match(i+3+MathLength(i+3,line),BRACKETCLOSE))
{
int len1 = CompileMath(i+3, TokenType(i+3), line);
asmlist.AddToken(PHA, "", 0, line);
int len2 = CompileMath(i+4+len1, TokenType(i+4+len1), line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA, "", 0, line);
asmlist.AddToken(TAX, "", 0, line);
asmlist.AddToken(LDAp, "nbasic_temp", strlen("nbasic_temp"), line);
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(STApx, num, strlen(num), line);
delete num;
return 4 + len1 + len2;
}
else
CompileError("'set'", line);
return 0;
}
int NBasic::CompilePush(int i, int type, int line)
{
if (tokens.Match(i,PUSH,A))
{
asmlist.AddToken(PHA, "", 0, line);
return 2;
}
else if (tokens.Match(i,POP,A))
{
asmlist.AddToken(PLA, "", 0, line);
return 2;
}
else if (tokens.Match(i,PUSH,X))
{
asmlist.AddToken(TXA, "", 0, line);
asmlist.AddToken(PHA, "", 0, line);
return 2;
}
else if (tokens.Match(i,POP,X))
{
asmlist.AddToken(PLA, "", 0, line);
asmlist.AddToken(TAX, "", 0, line);
return 2;
}
else if (tokens.Match(i,PUSH,Y))
{
asmlist.AddToken(TYA, "", 0, line);
asmlist.AddToken(PHA, "", 0, line);
return 2;
}
else if (tokens.Match(i,POP,Y))
{
asmlist.AddToken(PLA, "", 0, line);
asmlist.AddToken(TAY, "", 0, line);
return 2;
}
CompileError("Error compiling push/pop statement", line);
return 0;
}
int NBasic::CompileIncrement(int i, int type, int line)
{
if (tokens.Match(i,INC,X))
{
asmlist.AddToken(INX, "", 0, line);
return 2;
}
else if (tokens.Match(i,DEC,X))
{
asmlist.AddToken(DEX, "", 0, line);
return 2;
}
else if (tokens.Match(i,INC,Y))
{
asmlist.AddToken(INY, "", 0, line);
return 2;
}
else if (tokens.Match(i,DEC,Y))
{
asmlist.AddToken(DEY, "", 0, line);
return 2;
}
else if (tokens.Match(i,INC,VAR))
{
const Token *t = tokens.GetToken(i+1);
asmlist.AddToken(INCp, t->value, t->valuelen, t->line);
return 2;
}
else if (tokens.Match(i,DEC,VAR))
{
const Token *t = tokens.GetToken(i+1);
asmlist.AddToken(DECp, t->value, t->valuelen, t->line);
return 2;
}
CompileError("not fully implemented feature 'inc'", line); return 0;
}
bool NBasic::IsMath(int token_index)
{
int type = TokenType(token_index);
switch(type)
{
case VAR:
case NUMBER:
case PLUS:
case MINUS:
case SHIFTLEFT:
case SHIFTRIGHT:
case BRACKETOPEN:
case BITAND:
case BITOR:
case BITEOR:
return true;
default:
return false;
}
return false;
}
int NBasic::MathLength(int i, int line)
{
int one,two;
switch(TokenType(i))
{
case VAR:
case NUMBER:
return 1;
case PLUS:
case MINUS:
case BITAND:
case BITOR:
case BITEOR:
one = MathLength(i+1, line);
two = MathLength(i+1+one, line);
return 1 + one + two;
case SHIFTLEFT:
case SHIFTRIGHT:
one = MathLength(i+1, line);
return one+2;
case BRACKETOPEN:
one = MathLength(i+1, line);
if (TokenType(i+1+one) == BRACKETCLOSE)
return one + 2;
two = MathLength(i+1+one, line);
if (TokenType(i+two+one+1)!=BRACKETCLOSE)
CompileError("Error calculating bracketed math string", line);
return 2 + one + two;
default:
CompileError("Error calculating math string", line);
}
return -1; //should never get here
}
int NBasic::CompileMath(int i, int type, int line)
{
switch(type)
{
case VAR:
{
const Token *t = tokens.GetToken(i);
asmlist.AddToken(LDAp, t->value, t->valuelen, t->line);
return 1;
}
case NUMBER:
{
char *num = MakeNumber(TokenValue(i), line);
asmlist.AddToken(LDAi, num, strlen(num), line);
delete num;
return 1;
}
case BRACKETOPEN:
{
if (tokens.Match(i+1,VAR,X,BRACKETCLOSE)) // [var x]
{
const Token *t = tokens.GetToken(i+1);
asmlist.AddToken(LDApx, t->value, t->valuelen, t->line);
return 4;
}
if (tokens.Match(i+1,VAR,VAR,BRACKETCLOSE)) // [var var]
{
const Token *t = tokens.GetToken(i+1);
const Token *t2 = tokens.GetToken(i+2);
asmlist.AddToken(LDXp, t2->value, t2->valuelen, t2->line);
asmlist.AddToken(LDApx, t->value, t->valuelen, t->line);
return 4;
}
if (tokens.Match(i+1,VAR,NUMBER,BRACKETCLOSE)) // [var num]
{
const Token *t = tokens.GetToken(i+1);
char *num = MakeNumber(TokenValue(i+2),line);
asmlist.AddToken(LDXi, num, strlen(num), line);
asmlist.AddToken(LDApx, t->value, t->valuelen, t->line);
delete num;
return 4;
}
else if(tokens.Match(i+1,VAR) && tokens.Match(i+2+MathLength(i+2,line),BRACKETCLOSE)) //[var math]
{
const Token *t = tokens.GetToken(i+1);
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(TAX, "", 0, line);
asmlist.AddToken(LDApx, t->value, t->valuelen, t->line);
return len + 3;
}
else if (tokens.Match(i+1,NUMBER,BRACKETCLOSE)) // [num]
{
char *num = MakeNumber(TokenValue(i+1),line);
asmlist.AddToken(LDAp,num,strlen(num),line);
delete num;
return 3;
}
else if (tokens.Match(i+1,NUMBER,X,BRACKETCLOSE)) // [num x]
{
char *num = MakeNumber(TokenValue(i+1),line);
asmlist.AddToken(LDApx, num, strlen(num), line);
delete num;
return 4;
}
else if (tokens.Match(i+1,NUMBER,VAR,BRACKETCLOSE)) // [num var]
{
char *num = MakeNumber(TokenValue(i+1),line);
const Token *t = tokens.GetToken(i+2);
asmlist.AddToken(LDXp, t->value, t->valuelen, t->line);
asmlist.AddToken(LDApx, num, strlen(num), line);
delete num;
return 4;
}
else if (tokens.Match(i+1,NUMBER,NUMBER,BRACKETCLOSE)) // [num num]
{
// [$2000 3] is the same as [$2003]
int val = (NumberValue(TokenValue(i+1),line) + NumberValue(TokenValue(i+2),line)) % SIXTEENBIT;
char num[8];
sprintf(num, "%i\0",val);
asmlist.AddToken(LDAp, num, strlen(num), line);
return 4;
}
else if(tokens.Match(i+1,NUMBER) && tokens.Match(i+2+MathLength(i+2,line),BRACKETCLOSE)) //[num math]
{
char *num = MakeNumber(TokenValue(i+1),line);
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(TAX, "", 0, line);
asmlist.AddToken(LDApx, num, strlen(num), line);
delete num;
return len + 3;
}
CompileError("array access", line);
}
case PLUS:
{
if (tokens.Match(i+1,VAR)) // + var math
{
int len = CompileMath(i+2, TokenType(i+2), line);
/*if (len == 1)*/ asmlist.AddToken(CLC, "", 0, line);
const Token *t = tokens.GetToken(i+1);
asmlist.AddToken(ADCp, t->value, t->valuelen, t->line);
return len + 2;
}
else if (tokens.Match(i+1,NUMBER)) // + num math
{
int len = CompileMath(i+2, TokenType(i+2), line);
/*if (len == 1)*/ asmlist.AddToken(CLC, "", 0, line);
char *num = MakeNumber(TokenValue(i+1), line);
asmlist.AddToken(ADCi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1,line), VAR)) // + math var
{
int len = CompileMath(i+1, TokenType(i+1), line);
/*if (len == 1)*/ asmlist.AddToken(CLC, "", 0, line);
const Token *t = tokens.GetToken(i+1+len);
asmlist.AddToken(ADCp, t->value, t->valuelen, t->line);
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1,line), NUMBER)) // + math num
{
int len = CompileMath(i+1, TokenType(i+1), line);
/*if (len == 1)*/ asmlist.AddToken(CLC, "", 0, line);
char *num = MakeNumber(TokenValue(i+1+len), line);
asmlist.AddToken(ADCi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (IsMath(i+1+MathLength(i+1,line)))// + math math
{
int len1 = CompileMath(i+1,TokenType(i+1),line);
asmlist.AddToken(PHA,"",0,line);
int len2 = CompileMath(i+1+len1,TokenType(i+1+len1),line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA,"",0,line);
asmlist.AddToken(CLC,"",0,line);
asmlist.AddToken(ADCp, "nbasic_temp", strlen("nbasic_temp"), line);
return len1 + len2 + 1;
}
CompileError("addition", line);
}
case MINUS:
{
if (tokens.Match(i+1+MathLength(i+1,line), NUMBER)) // - math num
{
int len = CompileMath(i+1,TokenType(i+1),line);
/*if (len == 1)*/ asmlist.AddToken(SEC,"",0,line);
char *num = MakeNumber(TokenValue(i+1+len),line);
asmlist.AddToken(SBCi,num,strlen(num),line);
delete num;
return 2+len;
}
else if (tokens.Match(i+1+MathLength(i+1,line), VAR)) // - math var
{
int len = CompileMath(i+1,TokenType(i+1),line);
/*if (len == 1)*/ asmlist.AddToken(SEC,"",0,line);
asmlist.AddToken(SBCp, TokenValue(i+1+len), strlen(TokenValue(i+1+len)), line);
return 2+len;
}
else if (IsMath(i+1+MathLength(i+1,line))) // - math math
{
int len1 = CompileMath(i+1,TokenType(i+1),line);
asmlist.AddToken(PHA,"",0,line);
int len2 = CompileMath(i+1+len1,TokenType(i+1+len1),line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA,"",0,line);
asmlist.AddToken(SEC,"",0,line);
asmlist.AddToken(SBCp, "nbasic_temp", strlen("nbasic_temp"), line);
return len1 + len2 + 1;
}
CompileError("subtraction", line);
}
case BITAND:
{
if (tokens.Match(i+1, VAR) && IsMath(i+2)) // & var math
{
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(ANDp, TokenValue(i+1), strlen(TokenValue(i+1)), line);
return len + 2;
}
else if (tokens.Match(i+1, NUMBER) && IsMath(i+2)) // & num math
{
char *num = MakeNumber(TokenValue(i+1), line);
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(ANDi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1, line), VAR)) // & math var
{
int len = CompileMath(i+1, TokenType(i+1), line);
asmlist.AddToken(ANDp, TokenValue(i+1+len), strlen(TokenValue(i+1+len)), line);
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1, line), NUMBER)) // & math num
{
int len = CompileMath(i+1, TokenType(i+1), line);
char *num = MakeNumber(TokenValue(i+1+len), line);
asmlist.AddToken(ANDi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (IsMath(i+1+MathLength(i+1,line)))// & math math
{
int len1 = CompileMath(i+1,TokenType(i+1),line);
asmlist.AddToken(PHA,"",0,line);
int len2 = CompileMath(i+1+len1,TokenType(i+1+len1),line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA,"",0,line);
asmlist.AddToken(ANDp, "nbasic_temp", strlen("nbasic_temp"), line);
return len1 + len2 + 1;
}
CompileError("bitwise-and",line);
}
case BITOR:
{
if (tokens.Match(i+1, VAR) && IsMath(i+2)) // | var math
{
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(ANDp, TokenValue(i+1), strlen(TokenValue(i+1)), line);
return len + 2;
}
else if (tokens.Match(i+1, NUMBER) && IsMath(i+2)) // | num math
{
char *num = MakeNumber(TokenValue(i+1), line);
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(ORi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1, line), VAR)) // | math var
{
int len = CompileMath(i+1, TokenType(i+1), line);
asmlist.AddToken(ORp, TokenValue(i+1+len), strlen(TokenValue(i+1+len)), line);
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1, line), NUMBER)) // | math num
{
int len = CompileMath(i+1, TokenType(i+1), line);
char *num = MakeNumber(TokenValue(i+1+len), line);
asmlist.AddToken(ORi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (IsMath(i+1+MathLength(i+1,line)))// | math math
{
int len1 = CompileMath(i+1,TokenType(i+1),line);
asmlist.AddToken(PHA,"",0,line);
int len2 = CompileMath(i+1+len1,TokenType(i+1+len1),line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA,"",0,line);
asmlist.AddToken(ORp, "nbasic_temp", strlen("nbasic_temp"), line);
return len1 + len2 + 1;
}
CompileError("bitwise-or",line);
}
case BITEOR:
{
if (tokens.Match(i+1, VAR) && IsMath(i+2)) // ^ var math
{
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(EORp, TokenValue(i+1), strlen(TokenValue(i+1)), line);
return len + 2;
}
else if (tokens.Match(i+1, NUMBER) && IsMath(i+2)) // ^ num math
{
char *num = MakeNumber(TokenValue(i+1), line);
int len = CompileMath(i+2, TokenType(i+2), line);
asmlist.AddToken(EORi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1, line), VAR)) // ^ math var
{
int len = CompileMath(i+1, TokenType(i+1), line);
asmlist.AddToken(EORp, TokenValue(i+1+len), strlen(TokenValue(i+1+len)), line);
return len + 2;
}
else if (tokens.Match(i+1+MathLength(i+1, line), NUMBER)) // ^ math num
{
int len = CompileMath(i+1, TokenType(i+1), line);
char *num = MakeNumber(TokenValue(i+1+len), line);
asmlist.AddToken(EORi, num, strlen(num), line);
delete num;
return len + 2;
}
else if (IsMath(i+1+MathLength(i+1,line)))// ^ math math
{
int len1 = CompileMath(i+1,TokenType(i+1),line);
asmlist.AddToken(PHA,"",0,line);
int len2 = CompileMath(i+1+len1,TokenType(i+1+len1),line);
asmlist.AddToken(STAp, "nbasic_temp", strlen("nbasic_temp"), line);
asmlist.AddToken(PLA,"",0,line);
asmlist.AddToken(EORp, "nbasic_temp", strlen("nbasic_temp"), line);
return len1 + len2 + 1;
}
CompileError("bitwise-eor",line);
}
case SHIFTLEFT:
if (tokens.Match(i+1+MathLength(i+1, line), NUMBER)) // << math num
{
int len = CompileMath(i+1, TokenType(i+1), line);
int num = NumberValue(TokenValue(i+1+len), line);
asmlist.AddToken(CLC,"",0,line);
for(int j = 0; j < num; j++)
asmlist.AddToken(ASLa,"",0,line);
return len + 2;
}
CompileError("left shift", line);
case SHIFTRIGHT:
if (tokens.Match(i+1+MathLength(i+1, line), NUMBER)) // << math num
{
int len = CompileMath(i+1, TokenType(i+1), line);
int num = NumberValue(TokenValue(i+1+len), line);
asmlist.AddToken(CLC,"",0,line);
for(int j = 0; j < num; j++)
asmlist.AddToken(LSRa,"",0,line);
return len + 2;
}
CompileError("right shift", line);
break;
defualt:
CompileError("arithmetic",line);
}
}
bool NBasic::Output(const char* filename)
{
FILE *f = fopen(filename,"w");
if (!f) return false;
Output(f);
fclose(f);
return true;
}
void NBasic::Output(FILE *f)
{
if (verbose) printf("Beginning compile\n");
CreateAutoVariables();
OutputVariables(f);
int n = asmlist.NumTokens();
const Token* t;
for(int i = 0; i < n; i++)
{
t = asmlist.GetToken(i);
switch(t->type)
{
//addition
case ADCi:
fprintf(f, " adc #%s\n",t->value);
break;
case ADCp:
fprintf(f, " adc %s\n",t->value);
break;
case ADCpx:
fprintf(f, " adc %s,x\n",t->value);
break;
//bitwise and
case ANDi:
fprintf(f, " and #%s\n", t->value);
break;
case ANDp:
fprintf(f, " and %s\n", t->value);
break;
//bitwise or
case ORi:
fprintf(f, " or #%s\n", t->value);
break;
case ORp:
fprintf(f, " or %s\n", t->value);
break;
//bitwise eor
case EORi:
fprintf(f, " eor #%s\n", t->value);
break;
case EORp:
fprintf(f, " eor %s\n", t->value);
break;
//shift left
case ASLa:
fprintf(f, " asl a\n");
break;
//labels
case ASMACCEPTABLE:
case ASMTEXT:
fprintf(f, "%s",t->value);
break;
case ASMLABEL:
fprintf(f, "\n%s\n",t->value);
break;
//branches
case BCC:
fprintf(f, " bcc %s\n",t->value);
break;
case BCS:
fprintf(f, " bcs %s\n",t->value);
break;
case BEQ:
fprintf(f, " beq %s\n",t->value);
break;
case BMI:
fprintf(f, " bmi %s\n",t->value);
break;
case BNE:
fprintf(f, " bne %s\n",t->value);
break;
case BPL:
fprintf(f, " bpl %s\n",t->value);
break;
//clear carry
case CLC:
fprintf(f, " clc\n");
break;
//comparisons
case CMPi:
fprintf(f, " cmp #%s\n",t->value);
break;
case CMPp:
fprintf(f, " cmp %s\n",t->value);
break;
case CPXi:
fprintf(f, " cpx #%s\n",t->value);
break;
case CPXp:
fprintf(f, " cpx %s\n",t->value);
break;
case CPYi:
fprintf(f, " cpy #%s\n",t->value);
break;
case CPYp:
fprintf(f, " cpy %s\n",t->value);
break;
//decrements
case DECp:
fprintf(f, " dec %s\n",t->value);
break;
case DECpx:
fprintf(f, " dec %s,x\n",t->value);
break;
case DEX:
fprintf(f, " dex\n");
break;
case DEY:
fprintf(f, " dey\n");
break;
//increments
case INCp:
fprintf(f, " inc %s\n",t->value);
break;
case INCpx:
fprintf(f, " inc %s,x\n",t->value);
break;
case INX:
fprintf(f, " inx\n");
break;
case INY:
fprintf(f, " iny\n");
break;
//jumps
case JMP:
fprintf(f, " jmp %s\n",t->value);
break;
case JSR:
fprintf(f, " jsr %s\n",t->value);
break;
//loads
case LDAi:
fprintf(f, " lda #%s\n",t->value);
break;
case LDAp:
fprintf(f, " lda %s\n",t->value);
break;
case LDApx:
fprintf(f, " lda %s,x\n",t->value);
break;
case LDApy:
fprintf(f, " lda %s,y\n",t->value);
break;
case LDXi:
fprintf(f, " ldx #%s\n",t->value);
break;
case LDXp:
fprintf(f, " ldx %s\n",t->value);
break;
case LDXpy:
fprintf(f, " ldx %s,y\n",t->value);
break;
case LDYi:
fprintf(f, " ldy #%s\n",t->value);
break;
case LDYp:
fprintf(f, " ldy %s\n",t->value);
break;
case LDYpx:
fprintf(f, " ldy %s,x\n",t->value);
break;
//shift right
case LSRa:
fprintf(f, " lsr a\n");
break;
//stack ops
case PHA:
fprintf(f, " pha\n");
break;
case PLA:
fprintf(f, " pla\n");
break;
//subtraction
case SBCi:
fprintf(f, " sbc #%s\n",t->value);
break;
case SBCp:
fprintf(f, " sbc %s\n",t->value);
break;
case SBCpx:
fprintf(f, " sbc %s,x\n",t->value);
break;
case SEC:
fprintf(f, " sec\n");
break;
//stores
case STAp:
fprintf(f, " sta %s\n",t->value);
break;
case STApx:
fprintf(f, " sta %s,x\n",t->value);
break;
case STApy:
fprintf(f, " sta %s,y\n",t->value);
break;
case STXp:
fprintf(f, " stx %s\n",t->value);
break;
case STXpy:
fprintf(f, " stx %s,y\n",t->value);
break;
case STYp:
fprintf(f, " sty %s\n",t->value);
break;
case STYpx:
fprintf(f, " sty %s,x\n",t->value);
break;
case RTI:
fprintf(f, " rti\n");
break;
case RTS:
fprintf(f, " rts\n");
break;
//transfers
case TAX:
fprintf(f, " tax\n");
break;
case TAY:
fprintf(f, " tay\n");
break;
case TXA:
fprintf(f, " txa\n");
break;
case TYA:
fprintf(f, " tya\n");
break;
//unhandled
default:
fprintf(stderr, "don't know how to handle ASM opcode type %i\n",t->type);
}
}
fprintf(f,"\n");
if (verbose) printf("Compile completed successfully.\n");
}
syntax highlighted by Code2HTML, v. 0.9.1