gigatron/rom/Contrib/at67/compiler.cpp
2025-01-28 19:17:01 +03:00

5629 lines
252 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <cmath>
#include <cstring>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include "memory.h"
#include "cpu.h"
#include "loader.h"
#include "assembler.h"
#include "compiler.h"
#include "pragmas.h"
#include "keywords.h"
#include "functions.h"
#include "operators.h"
#include "optimiser.h"
#include "validater.h"
#include "linker.h"
#include "midi.h"
size_t _heapTotalUsage = 0;
size_t _heapAllocations = 0;
void* operator new(size_t size)
{
_heapTotalUsage += size;
_heapAllocations++;
return malloc(size);
}
namespace Compiler
{
const std::vector<std::string> _sysInitNames = {"InitEqOp", "InitNeOp", "InitLeOp", "InitGeOp", "InitLtOp", "InitGtOp", "Init8Array2d", "Init8Array3d", "Init16Array2d", "Init16Array3d", "InitRealTimeStub"};
enum VarResult {VarError=-1, VarNotFound, VarCreated, VarUpdated, VarExists, VarExistsAsConst};
enum StrResult {StrError=-1, StrNotFound, StrCreated};
enum FloatSize {Float16=2, Float32=4};
enum LabelResult {LabelError=-1, LabelNotFound, LabelFound};
uint16_t _vasmPC = USER_CODE_START;
uint16_t _userCodeStart = USER_CODE_START;
uint16_t _tempVarSize = TEMP_VAR_SIZE;
uint16_t _tempVarStart = TEMP_VAR_START;
uint16_t _userVarStart = USER_VAR_START;
uint16_t _userVarsAddr = USER_VAR_START;
uint16_t _runtimeEnd = RUN_TIME_START;
uint16_t _runtimeStart = RUN_TIME_START;
uint16_t _arraysStart = RUN_TIME_START;
uint16_t _stringsStart = RUN_TIME_START;
uint16_t _regWorkArea = 0x0000;
uint16_t _gprintfVarsAddr = 0x0000;
uint16_t _strWorkAreaIdx = 0;
uint16_t _strWorkArea[NUM_STR_WORK_AREAS] = {0x0000, 0x0000};
uint16_t _spritesAddrLutAddress = 0x0000;
uint16_t _spriteStripeChunks = 15;
uint16_t _spriteStripeMinAddress = USER_CODE_START;
Memory::FitType _spriteStripeFitType = Memory::FitDescending;
CodeOptimiseType _codeOptimiseType = CodeSpeed;
Cpu::RomType _codeRomType = Cpu::ROMv3;
std::map<std::string, int> _branchTypes = {{"BRA", 0}, {"BEQ", 1}, {"BNE", 2}, {"BLE", 3}, {"BGE", 4}, {"BLT", 5}, {"BGT", 6}};
bool _codeIsAsm = false;
bool _arrayIndiciesOne = false;
bool _createNumericLabelLut = false;
bool _createTimeData = false;
int _currentLabelIndex = -1;
int _currentCodeLineIndex = 0;
int _nonNumericLabelIndex = -1;
int _jumpFalseUniqueId = 0;
int _codeLineStart = 0;
std::string _codeLineText;
std::string _codeLineModule;
std::string _runtimePath = "./runtime";
std::string _tempVarStartStr;
std::string _nextInternalLabel;
std::vector<Input> _input;
std::vector<std::string> _output;
std::vector<std::string> _runtime;
std::vector<Label> _labels;
std::vector<std::string> _gosubLabels;
std::vector<std::string> _equateLabels;
std::vector<InternalLabel> _internalLabels;
std::vector<InternalLabel> _discardedLabels;
std::vector<CodeLine> _codeLines;
std::vector<ModuleLine> _moduleLines;
std::vector<Constant> _constants;
std::vector<IntegerVar> _integerVars;
std::vector<StringVar> _stringVars;
std::map<std::string, TypeData> _typeDatas;
std::stack<ForNextData> _forNextDataStack;
std::stack<ElseIfData> _elseIfDataStack;
std::stack<WhileWendData> _whileWendDataStack;
std::stack<RepeatUntilData> _repeatUntilDataStack;
std::map<std::string, CallData> _callDataMap;
std::stack<ProcData> _procDataStack;
std::map<std::string, ProcData> _procDataMap;
std::vector<std::string> _macroLines;
std::map<int, MacroNameEntry> _macroNameEntries;
std::map<std::string, MacroIndexEntry> _macroIndexEntries;
std::vector<DefDataByte> _defDataBytes;
std::vector<DefDataWord> _defDataWords;
std::vector<DefDataImage> _defDataImages;
std::vector<DefDataLoaderImageChunk> _defDataLoaderImageChunks;
std::map<int, DefDataMidi> _defDataMidis;
std::map<int, DefDataOpen> _defDataOpens;
std::map<int, DefDataSprite> _defDataSprites;
SpritesAddrLut _spritesAddrLut;
std::map<int, DefDataFont> _defDataFonts;
FontsAddrLut _fontsAddrLut;
std::map<std::string, DefFunction> _defFunctions;
std::vector<std::unique_ptr<DataObject>> _dataObjects;
uint16_t getVasmPC(void) {return _vasmPC;}
uint16_t getUserCodeStart(void) {return _userCodeStart;}
uint16_t getRuntimeEnd(void) {return _runtimeEnd;}
uint16_t getRuntimeStart(void) {return _runtimeStart;}
uint16_t getArraysStart(void) {return _arraysStart;}
uint16_t getStringsStart(void) {return _stringsStart;}
uint16_t getTempVarStart(void) {return _tempVarStart;}
uint16_t getTempVarSize(void) {return _tempVarSize;}
uint16_t getRegWorkArea(void) {return _regWorkArea;}
uint16_t getGprintfVarsAddr(void) {return _gprintfVarsAddr;}
uint16_t getStrWorkArea(void) {return _strWorkArea[_strWorkAreaIdx & 1];}
uint16_t getStrWorkArea(int index) {return _strWorkArea[index & 1];}
uint16_t getSpritesAddrLutAddress(void) {return _spritesAddrLutAddress;}
uint16_t getSpriteStripeChunks(void) {return _spriteStripeChunks;}
uint16_t getSpriteStripeMinAddress(void) {return _spriteStripeMinAddress;}
Memory::FitType getSpriteStripeFitType(void) {return _spriteStripeFitType;}
CodeOptimiseType getCodeOptimiseType(void) {return _codeOptimiseType;}
Cpu::RomType getCodeRomType(void) {return _codeRomType;}
const std::map<std::string, int>& getBranchTypes(void) {return _branchTypes;}
bool getArrayIndiciesOne(void) {return _arrayIndiciesOne;}
bool getCreateTimeData(void) {return _createTimeData;}
int getCurrentLabelIndex(void) {return _currentLabelIndex;}
int getCurrentCodeLineIndex(void) {return _currentCodeLineIndex;}
const std::string& getRuntimePath(void) {return _runtimePath;}
const std::string& getTempVarStartStr(void) {return _tempVarStartStr;}
const std::string& getNextInternalLabel(void) {return _nextInternalLabel;}
int getCodeLineStart(int index) {return (_moduleLines.size() && (index < int(_moduleLines.size()))) ? _moduleLines[index]._index : index;}
void setCodeIsAsm(bool codeIsAsm) {_codeIsAsm = codeIsAsm;}
void setUserCodeStart(uint16_t userCodeStart) {_userCodeStart = userCodeStart;}
void setRuntimeEnd(uint16_t runtimeEnd) {_runtimeEnd = runtimeEnd;}
void setRuntimePath(const std::string& runtimePath) {_runtimePath = runtimePath;}
void setRuntimeStart(uint16_t runtimeStart) {_runtimeStart = runtimeStart;}
void setArraysStart(uint16_t arraysStart) {_arraysStart = arraysStart;}
void setStringsStart(uint16_t stringsStart) {_stringsStart = stringsStart;}
void setTempVarStart(uint16_t tempVarStart) {_tempVarStart = tempVarStart;}
void setTempVarSize(uint16_t tempVarSize) {_tempVarSize = tempVarSize;}
void setRegWorkArea(uint16_t regWorkArea) {_regWorkArea = regWorkArea;}
void setGprintfVarsAddr(uint16_t gprintfVarsAddr) {_gprintfVarsAddr = gprintfVarsAddr;}
void setStrWorkArea(uint16_t strWorkArea, int index) {_strWorkArea[index & 1] = strWorkArea;}
void setSpritesAddrLutAddress(uint16_t spritesAddrLutAddress) {_spritesAddrLutAddress = spritesAddrLutAddress;}
void setSpriteStripeChunks(uint16_t spriteStripeChunks) {_spriteStripeChunks = spriteStripeChunks;}
void setSpriteStripeMinAddress(uint16_t spriteStripeMinAddress) {_spriteStripeMinAddress = spriteStripeMinAddress;}
void setSpriteStripeFitType(Memory::FitType spriteStripeFitType) {_spriteStripeFitType = spriteStripeFitType;}
void setCodeOptimiseType(CodeOptimiseType codeOptimiseType) {_codeOptimiseType = codeOptimiseType;}
void setCodeRomType(Cpu::RomType codeRomType) {_codeRomType = codeRomType;}
void setCreateNumericLabelLut(bool createNumericLabelLut) {_createNumericLabelLut = createNumericLabelLut;}
void setCreateTimeData(bool createTimeData) {_createTimeData = createTimeData;}
void setArrayIndiciesOne(bool arrayIndiciesOne) {_arrayIndiciesOne = arrayIndiciesOne;}
void nextStrWorkArea(void) {_strWorkAreaIdx = (_strWorkAreaIdx + 1) & 1;}
int getNextJumpFalseUniqueId(void) {return _jumpFalseUniqueId++;}
std::vector<Label>& getLabels(void) {return _labels;}
std::vector<Constant>& getConstants(void) {return _constants;}
std::vector<CodeLine>& getCodeLines(void) {return _codeLines;}
std::vector<ModuleLine>& getModuleLines(void) {return _moduleLines;}
std::vector<IntegerVar>& getIntegerVars(void) {return _integerVars;}
std::vector<StringVar>& getStringVars(void) {return _stringVars;}
std::map<std::string, TypeData>& getTypeDatas(void) {return _typeDatas;}
std::vector<InternalLabel>& getInternalLabels(void) {return _internalLabels;}
std::vector<InternalLabel>& getDiscardedLabels(void) {return _discardedLabels;}
std::vector<std::string>& getOutput(void) {return _output;}
std::vector<std::string>& getRuntime(void) {return _runtime;}
std::vector<DefDataByte>& getDefDataBytes(void) {return _defDataBytes;}
std::vector<DefDataWord>& getDefDataWords(void) {return _defDataWords;}
std::vector<DefDataImage>& getDefDataImages(void) {return _defDataImages;}
std::vector<DefDataLoaderImageChunk>& getDefDataLoaderImageChunks(void) {return _defDataLoaderImageChunks;}
std::map<int, DefDataMidi>& getDefDataMidis(void) {return _defDataMidis;}
std::map<int, DefDataOpen>& getDefDataOpens(void) {return _defDataOpens;}
std::map<int, DefDataSprite>& getDefDataSprites(void) {return _defDataSprites;}
SpritesAddrLut& getSpritesAddrLut(void) {return _spritesAddrLut;}
std::map<int, DefDataFont>& getDefDataFonts(void) {return _defDataFonts;}
FontsAddrLut& getFontsAddrLut(void) {return _fontsAddrLut;}
std::map<std::string, DefFunction>& getDefFunctions(void) {return _defFunctions;}
std::vector<std::unique_ptr<DataObject>>& getDataObjects(void) {return _dataObjects;}
std::map<std::string, MacroIndexEntry>& getMacroIndexEntries(void) {return _macroIndexEntries;}
std::stack<ForNextData>& getForNextDataStack(void) {return _forNextDataStack;}
std::stack<ElseIfData>& getElseIfDataStack(void) {return _elseIfDataStack;}
std::stack<WhileWendData>& getWhileWendDataStack(void) {return _whileWendDataStack;}
std::stack<RepeatUntilData>& getRepeatUntilDataStack(void) {return _repeatUntilDataStack;}
std::map<std::string, CallData>& getCallDataMap(void) {return _callDataMap;}
std::stack<ProcData>& getProcDataStack(void) {return _procDataStack;}
std::map<std::string, ProcData>& getProcDataMap(void) {return _procDataMap;}
int getNumNumericLabels(void)
{
int numLabels = 0;
for(int i=0; i<int(_labels.size()); i++)
{
if(_labels[i]._numeric) numLabels++;
}
return numLabels;
}
// Vertical blank interrupt uses 0x30-0x35 for context save/restore, (vPC,vAC,vCpuSelect)
bool moveVblankVars(void)
{
if(_codeRomType < Cpu::ROMv5a) return false;
if(_userVarsAddr < USER_VAR_START + Int16*3)
{
_userVarStart = USER_VAR_START + Int16*3;
_userVarsAddr = _userVarStart;
}
for(int i=0; i<int(_integerVars.size()); i++)
{
if(_integerVars[i]._address == USER_VAR_START ||
_integerVars[i]._address == USER_VAR_START + Int16 ||
_integerVars[i]._address == USER_VAR_START + 2*Int16 )
{
//emitVcpuAsm("LDW", Expression::wordToHexString(_integerVars[i]._address), false);
//emitVcpuAsm("STW", Expression::wordToHexString(_userVarsAddr), false);
_integerVars[i]._address = _userVarsAddr;
_userVarsAddr += Int16;
}
}
return true;
}
// If _nextInternalLabel is already queued, add it to discarded labels so that it is fixed later in outputLabels
void setNextInternalLabel(const std::string& label)
{
if(_nextInternalLabel.size()) _discardedLabels.push_back({_vasmPC, _nextInternalLabel});
_nextInternalLabel = label;
}
void adjustDiscardedLabels(const std::string name, uint16_t address)
{
uint16_t match;
Expression::stringToU16(name.substr(name.size() - 6, 6), match);
for(int i=0; i<int(_discardedLabels.size()); i++)
{
if(_discardedLabels[i]._address == match) _discardedLabels[i]._address = address;
}
}
bool setBuildPath(const std::string& buildpath, const std::string& filepath)
{
if(buildpath.size() == 0) return false;
// Prepend current file path to relative paths
if(buildpath.find(":") == std::string::npos && buildpath[0] != '/')
{
std::string path = filepath;
size_t slash = path.find_last_of("\\/");
path = (slash != std::string::npos) ? path.substr(0, slash) : ".";
std::string includePath = path + "/" + buildpath;
Assembler::setIncludePath(includePath);
}
else
{
Assembler::setIncludePath(buildpath);
}
setRuntimePath(buildpath);
return true;
}
bool initialise(void)
{
return true;
}
bool readInputFile(std::ifstream& infile, const std::string& filename, std::vector<Input>& input, int& numLines)
{
if(!infile.is_open())
{
fprintf(stderr, "Compiler::readInputFile() : Failed to open file '%s'\n", filename.c_str());
return false;
}
// Read input .gbas file
std::string lineToken;
while(!infile.eof())
{
std::getline(infile, lineToken);
Input inp = {true, lineToken};
// Compound statement must span multiple lines
size_t lbra, rbra;
lineToken = Expression::removeCommentsNotInStrings(lineToken);
if(Expression::findMatchingBrackets(lineToken, 0, lbra, rbra, '{'))
{
fprintf(stderr, "Compiler::readInputFile() : '%s:%d' : compound statement must span multiple lines\n", filename.c_str(), numLines+1);
return false;
}
// Append lines into a compound statement
int compoundLength = 0;
if(Expression::findNonStringBracket(lineToken, '{') != lineToken.end())
{
do
{
std::getline(infile, lineToken);
if(infile.eof())
{
fprintf(stderr, "Compiler::readInputFile() : '%s:%d' : missing '}'\n", filename.c_str(), numLines+1);
return false;
}
inp._text += lineToken;
lineToken = Expression::removeCommentsNotInStrings(lineToken);
compoundLength++;
}
while(Expression::findNonStringBracket(lineToken, '}') == lineToken.end());
// Remove compound statement delimiters
Expression::stripChars(inp._text, "{}");
}
// Add empty lines for compound statement to correct line numbers
for(int i=0; i<compoundLength; i++)
{
input.push_back({true, ""});
numLines++;
}
if(!infile.good() && !infile.eof())
{
fprintf(stderr, "Compiler::readInputFile() : '%s:%d' : bad line '%s'\n", filename.c_str(), numLines+1, lineToken.c_str());
return false;
}
input.push_back(inp);
numLines++;
}
return true;
}
bool writeOutputFile(std::ofstream& outfile, const std::string& filename)
{
if(!outfile.is_open())
{
fprintf(stderr, "Compiler::writeOutputFile() : failed to open '%s'\n", filename.c_str());
return false;
}
// Write output .vasm file
for(int i=0; i<int(_output.size()); i++)
{
outfile.write((char *)_output[i].c_str(), _output[i].size());
if(outfile.bad() || outfile.fail())
{
fprintf(stderr, "Compiler::writeOutputFile() : write error in '%s'\n", filename.c_str());
return false;
}
}
return true;
}
int findLabel(const std::string& labelName)
{
for(int i=0; i<int(_labels.size()); i++)
{
if(_labels[i]._name == labelName) return i;
}
return -1;
}
int findLabel(uint16_t address)
{
for(int i=0; i<int(_labels.size()); i++)
{
if(_labels[i]._address == address) return i;
}
return -1;
}
int findInternalLabel(const std::string& labelName)
{
for(int i=0; i<int(_internalLabels.size()); i++)
{
if(_internalLabels[i]._name == labelName) return i;
}
return -1;
}
int findInternalLabel(uint16_t address)
{
for(int i=0; i<int(_internalLabels.size()); i++)
{
if(_internalLabels[i]._address == address) return i;
}
return -1;
}
int findConst(std::string& constName)
{
// Valid chars are alpha, '@' and '#'
constName = Expression::getSubAlpha(constName);
for(int i=0; i<int(_constants.size()); i++)
{
if(_constants[i]._name == constName) return i;
}
return -1;
}
int findVar(std::string& varName, bool subAlpha)
{
// Valid chars are alpha and 'address of'
if(subAlpha) varName = Expression::getSubAlpha(varName);
// Within a PROC/ENDPROC pair
if(getProcDataStack().size() == 1)
{
// Top of stack for current PROC
ProcData procData = getProcDataStack().top();
// Translate to local var names
if(procData._localVarNameMap.find(varName) != procData._localVarNameMap.end())
{
varName = procData._localVarNameMap[varName];
}
}
// Search for var name
for(int i=0; i<int(_integerVars.size()); i++)
{
if(_integerVars[i]._name == varName) return i;
}
return -1;
}
int findVar(std::string& varName, std::string& oldName, bool subAlpha)
{
// Valid chars are alpha and 'address of'
if(subAlpha) varName = Expression::getSubAlpha(varName);
oldName = varName;
// Within a PROC/ENDPROC pair
if(getProcDataStack().size() == 1)
{
// Top of stack for current PROC
ProcData procData = getProcDataStack().top();
// Translate to local var names
if(procData._localVarNameMap.find(varName) != procData._localVarNameMap.end())
{
varName = procData._localVarNameMap[varName];
}
}
// Search for var name
for(int i=0; i<int(_integerVars.size()); i++)
{
if(_integerVars[i]._name == varName) return i;
}
return -1;
}
int findStr(std::string& strName)
{
// Valid chars are alpha and 'address of'
strName = Expression::getSubAlpha(strName);
for(int i=0; i<int(_stringVars.size()); i++)
{
if(_stringVars[i]._name == strName) return i;
}
return -1;
}
void createLabel(uint16_t address, const std::string& name, int codeLineIndex, Label& label, bool numeric, bool addUnderscore, bool pageJump, bool gosub)
{
std::string n = name;
Expression::stripWhitespace(n);
std::string o = (addUnderscore) ? "_" + n : n;
Expression::addString(o, LABEL_TRUNC_SIZE - int(o.size()));
size_t space = o.find_first_of(" ");
if(space == std::string::npos || space >= LABEL_TRUNC_SIZE - 1)
{
o = o.substr(0, LABEL_TRUNC_SIZE);
o[LABEL_TRUNC_SIZE - 1] = ' ';
}
label = {address, n, o, codeLineIndex, numeric, pageJump, gosub};
Expression::stripWhitespace(label._name);
_labels.push_back(label);
_currentLabelIndex = int(_labels.size() - 1);
}
// Global int vars
void createIntVar(const std::string& varName, int16_t data, int16_t init, CodeLine& codeLine, int codeLineIndex, bool containsVars, int& varIndex)
{
// Create var
varIndex = int(_integerVars.size());
codeLine._containsVars = containsVars;
codeLine._varIndex = varIndex;
codeLine._varType = VarInt16;
std::vector<uint16_t> arrSizes;
std::vector<int16_t> arrInits;
std::vector<std::vector<uint16_t>> arrAddrs;
std::vector<uint16_t> arrLut;
IntegerVar integerVar = {data, init, _userVarsAddr, varName, varName, codeLineIndex, VarInt16, Int16, arrSizes, arrInits, arrAddrs, arrLut};
_integerVars.push_back(integerVar);
// Create var output
std::string line = "_" + _integerVars[varIndex]._name;
Expression::addString(line, LABEL_TRUNC_SIZE - int(line.size()));
size_t space = line.find_first_of(" ");
if(space == std::string::npos || space >= LABEL_TRUNC_SIZE - 1)
{
line = line.substr(0, LABEL_TRUNC_SIZE);
line[LABEL_TRUNC_SIZE - 1] = ' ';
}
_integerVars[varIndex]._output = line;
_userVarsAddr += Int16;
if(_userVarsAddr >= USER_VAR_END)
{
_userVarsAddr = _userVarStart;
fprintf(stderr, "Compiler::createIntVar() : '%s:%d' : warning, you have used the maximum number of page zero global variables : %s\n", codeLine._moduleName.c_str(), codeLineIndex, codeLine._text.c_str());
}
}
// Proc/Func local and param int vars
void createProcIntVar(const std::string& varName, int16_t data, int16_t init, CodeLine& codeLine, int codeLineIndex, bool containsVars, uint16_t address, int& varIndex)
{
// Create var
varIndex = int(_integerVars.size());
codeLine._containsVars = containsVars;
codeLine._varIndex = varIndex;
codeLine._varType = VarInt16;
std::vector<uint16_t> arrSizes;
std::vector<int16_t> arrInits;
std::vector<std::vector<uint16_t>> arrAddrs;
std::vector<uint16_t> arrLut;
IntegerVar integerVar = {data, init, address, varName, varName, codeLineIndex, VarInt16, Int16, arrSizes, arrInits, arrAddrs, arrLut};
_integerVars.push_back(integerVar);
// Create var output
std::string line = "_" + _integerVars[varIndex]._name;
Expression::addString(line, LABEL_TRUNC_SIZE - int(line.size()));
size_t space = line.find_first_of(" ");
if(space == std::string::npos || space >= LABEL_TRUNC_SIZE - 1)
{
line = line.substr(0, LABEL_TRUNC_SIZE);
line[LABEL_TRUNC_SIZE - 1] = ' ';
}
_integerVars[varIndex]._output = line;
}
// Global int array vars
void createArrIntVar(const std::string& varName, int16_t data, int16_t init, CodeLine& codeLine, int codeLineIndex, bool containsVars, bool isInit, int& varIndex, VarType varType, int intSize,
uint16_t address, std::vector<uint16_t>& arrSizes, const std::vector<int16_t>& arrInits, std::vector<std::vector<uint16_t>>& arrAddrs, std::vector<uint16_t>& arrLut)
{
// Create var
varIndex = int(_integerVars.size());
codeLine._containsVars = containsVars;
codeLine._varIndex = varIndex;
codeLine._varType = VarInt16;
uint16_t varAddr = (varType == VarInt16) ? _userVarsAddr : address;
IntegerVar integerVar = {data, init, varAddr, varName, varName, codeLineIndex, varType, intSize, arrSizes, arrInits, arrAddrs, arrLut, isInit};
_integerVars.push_back(integerVar);
// Create var output
std::string line = "_" + _integerVars[varIndex]._name;
Expression::addString(line, LABEL_TRUNC_SIZE - int(line.size()));
size_t space = line.find_first_of(" ");
if(space == std::string::npos || space >= LABEL_TRUNC_SIZE - 1)
{
line = line.substr(0, LABEL_TRUNC_SIZE);
line[LABEL_TRUNC_SIZE - 1] = ' ';
}
_integerVars[varIndex]._output = line;
}
VarResult createCodeVar(CodeLine& codeLine, int codeLineIndex, int& varIndex)
{
size_t equals = Expression::findNonStringEquals(codeLine._code) - codeLine._code.begin();
if(codeLine._code.size() < 2 || equals >= codeLine._code.size()) return VarNotFound;
// Check all tokens individually, don't just do a find as a var may exist with a reserved keyword embedded within it
for(int i=0; i<int(codeLine._tokens.size()); i++)
{
std::string token = codeLine._tokens[i];
Expression::stripWhitespace(token);
Expression::strToUpper(token);
// No need to check after the equals
if(token == "=") break;
// Check tokens that are reserved keywords using equals
if(Keywords::getEqualsKeywords().find(token) != Keywords::getEqualsKeywords().end()) return VarNotFound;
}
// Name
std::string varName = codeLine._code.substr(0, equals);
std::vector<std::string> tokens = Expression::tokenise(varName, ' ');
if(tokens.size() > 1) return VarError;
Expression::stripWhitespace(varName);
// Byte write configuration
size_t dot = varName.find('.');
if(dot != std::string::npos)
{
std::string dotName = varName.substr(dot);
varName = varName.substr(0, dot);
Expression::strToUpper(dotName);
if(dotName == ".LO") codeLine._int16Byte = Expression::Int16Low;
if(dotName == ".HI") codeLine._int16Byte = Expression::Int16High;
}
// String variables are not handled here
if(varName.find("$") != std::string::npos) return VarError;
std::string constName = varName;
if(Compiler::findConst(constName) >= 0)
{
fprintf(stderr, "Keywords::createCodeVar() : '%s:%d' : const '%s' already exists : %s\n", codeLine._moduleName.c_str(), codeLineIndex, constName.c_str(), codeLine._text.c_str());
return VarExistsAsConst;
}
// Var already exists?
varIndex = findVar(varName);
if(varIndex != -1)
{
codeLine._containsVars = false;
codeLine._varIndex = varIndex;
codeLine._varType = VarInt16;
return VarExists;
}
createIntVar(varName, 0, 0, codeLine, codeLineIndex, false, varIndex);
return VarCreated;
}
VarResult createCodeStr(CodeLine& codeLine, int codeLineIndex, int& strIndex)
{
// Code and string have minimum size requirements
size_t equals = Expression::findNonStringEquals(codeLine._code) - codeLine._code.begin();
if(codeLine._code.size() < 2 || equals >= codeLine._code.size() - 2 || codeLine._tokens.size() < 2) return VarError;
// Check all tokens individually, don't just do a find as a str name may exist with a reserved keyword embedded within it
for(int i=0; i<int(codeLine._tokens.size()); i++)
{
std::string token = codeLine._tokens[i];
Expression::stripWhitespace(token);
Expression::strToUpper(token);
// Check tokens that are reserved keywords using equals
if(Keywords::getEqualsKeywords().find(token) != Keywords::getEqualsKeywords().end()) return VarError;
}
// String name validation
std::string strName = codeLine._code.substr(0, equals);
Expression::stripWhitespace(strName);
Expression::TokType tokType = Expression::isStrNameValid(strName);
if(tokType == Expression::Invalid) return VarError;
strIndex = findStr(strName);
if(strIndex != -1)
{
codeLine._varIndex = strIndex;
switch(tokType)
{
case Expression::Variable: codeLine._varType = VarStr; break;
case Expression::Array: codeLine._varType = VarStr2; break;
default: return VarError;
}
return VarExists;
}
// String data validation
std::string strData = codeLine._tokens[1];
Expression::stripNonStringWhitespace(strData);
std::string strKeyword = strData;
// Check for string keywords
if(Functions::getStringFunctions().find(Expression::strToUpper(strKeyword)) != Functions::getStringFunctions().end())
{
strData = "";
}
// Check for constant string
else
{
if(Expression::isStringValid(strData))
{
// Strip quotes
strData.erase(0, 1);
strData.erase(strData.size()-1, 1);
}
// Check for assignment of another variable
else
{
if(strData.find("$") == std::string::npos) return VarError;
strData = "";
}
}
// Create string
uint16_t address;
if(getOrCreateString(codeLine, codeLineIndex, strData, strName, address, USER_STR_SIZE, false) == -1) return VarError;
strIndex = int(_stringVars.size()) - 1;
codeLine._varIndex = strIndex;
codeLine._varType = VarStr;
return VarCreated;
}
uint32_t isExpression(std::string& input, int& varIndex, int& constIndex, int& strIndex)
{
uint32_t expressionType = Expression::HasNumbers;
if(input.find_first_of("[]") != std::string::npos) return Expression::IsInvalid;
if(input.find("++") != std::string::npos) return Expression::IsInvalid;
if(input.find("--") != std::string::npos) return Expression::IsInvalid;
// Check for strings
if(input.find("\"") != std::string::npos) expressionType |= Expression::HasStrings;
std::string stripped = Expression::stripStrings(input);
std::vector<std::string> tokens = Expression::tokeniseMulti(stripped, "-+/*%&<>=();, ", false);
// Check for pragmas
for(int i=0; i<int(tokens.size()); i++)
{
std::string token = tokens[i];
Expression::strToUpper(token);
if(Pragmas::getPragmas().find(token) != Pragmas::getPragmas().end())
{
expressionType |= Expression::HasPragmas;
break;
}
}
// Check for keywords
for(int i=0; i<int(tokens.size()); i++)
{
std::string token = tokens[i];
Expression::strToUpper(token);
if(Keywords::getKeywords().find(token) != Keywords::getKeywords().end())
{
expressionType |= Expression::HasKeywords;
break;
}
}
// Check for inbuilt functions and user functions
for(int i=0; i<int(tokens.size()); i++)
{
std::string token = tokens[i];
Expression::strToUpper(token);
if((Functions::getFunctions().find(token) != Functions::getFunctions().end()) || (getDefFunctions().find(token) != getDefFunctions().end()))
{
expressionType |= Expression::HasFunctions;
break;
}
}
// Check for string keywords
for(int i=0; i<int(tokens.size()); i++)
{
std::string token = tokens[i];
Expression::strToUpper(token);
if(Functions::getStringFunctions().find(token) != Functions::getStringFunctions().end())
{
// If first token is a string keyword then use optimised prints, (don't create strings)
if(i == 0) expressionType |= Expression::HasOptimisedPrint;
expressionType |= Expression::HasStringKeywords;
break;
}
}
// Check for consts
for(int i=0; i<int(tokens.size()); i++)
{
constIndex = findConst(tokens[i]);
if(constIndex != -1 && tokens[i][0] != '@') // 'address of' operator returns numbers
{
if(_constants[constIndex]._varType == ConstInt16)
{
expressionType |= Expression::HasIntConsts;
break;
}
if(_constants[constIndex]._varType == ConstStr)
{
expressionType |= Expression::HasStrConsts;
break;
}
}
}
// Check for vars
for(int i=0; i<int(tokens.size()); i++)
{
varIndex = findVar(tokens[i]);
if(varIndex != -1 && tokens[i][0] != '@') // 'address of' operator returns numbers
{
// Array variables are treated as a function call
if(_integerVars[varIndex]._varType == Var1Arr8 || _integerVars[varIndex]._varType == Var2Arr8 || _integerVars[varIndex]._varType == Var3Arr8 ||
_integerVars[varIndex]._varType == Var1Arr16 || _integerVars[varIndex]._varType == Var2Arr16 || _integerVars[varIndex]._varType == Var3Arr16)
{
expressionType |= Expression::HasFunctions;
break;
}
expressionType |= Expression::HasIntVars;
break;
}
}
// Check for string vars
for(int i=0; i<int(tokens.size()); i++)
{
strIndex = findStr(tokens[i]);
if(strIndex != -1 && !_stringVars[strIndex]._constant && tokens[i][0] != '@') // 'address of' operator returns numbers)
{
expressionType |= Expression::HasStrVars;
break;
}
}
// Check for operators for non string expressions
bool isStrExpression = ((expressionType >= Expression::HasStrings) && (expressionType <= Expression::IsStringExpression));
if(!isStrExpression)
{
if(stripped.find_first_of("-+/*%<>=") != std::string::npos) expressionType |= Expression::HasOperators;
std::string mod = stripped;
Expression::strToUpper(mod);
for(int i=0; i<int(Operators::getOperators().size()); i++)
{
if(mod.find(Operators::getOperators()[i]) != std::string::npos) expressionType |= Expression::HasOperators;
}
}
// Check for operators for string expressions
if(isStrExpression)
{
if(stripped.find_first_of("+<>=") != std::string::npos) expressionType |= Expression::HasOperators;
}
return expressionType;
}
void updateIntVar(int16_t data, CodeLine& codeLine, int varIndex, bool containsVars)
{
codeLine._containsVars = containsVars;
codeLine._varIndex = varIndex;
codeLine._varType = VarInt16;
_integerVars[varIndex]._data = data;
}
bool createAsmLine(const std::string& code)
{
std::string line, vasm = code;
CodeLine codeLine = _codeLines[_currentCodeLineIndex];
Expression::trimWhitespace(vasm);
std::vector<std::string> tokens = Expression::tokenise(vasm, ' ', false);
if(tokens.size() < 1)
{
fprintf(stderr, "Compiler::createAsmLine() : '%s:%d' : vASM syntax error, missing opcode : %s\n", codeLine._moduleName.c_str(), _currentCodeLineIndex, codeLine._text.c_str());
return false;
}
std::string opcodeStr = tokens[0];
std::string operandStr = "";
for(int i=1; i<int(tokens.size()); i++) operandStr += tokens[i];
Expression::stripWhitespace(opcodeStr);
Expression::strToUpper(opcodeStr);
Expression::stripWhitespace(operandStr);
int vasmSize = createVcpuAsm(opcodeStr, operandStr, int(_codeLines.size()), line);
if(vasmSize == 0 && opcodeStr != "_BREAKPOINT_")
{
// This is a bit of a hack, but unfortunately needed as createAsmLine is called before the keyword ENDASM can be processed
if(opcodeStr == "ENDASM") return true;
fprintf(stderr, "Compiler::createAsmLine() : '%s:%d' : vASM syntax error, undefined opcode : %s\n", codeLine._moduleName.c_str(), _currentCodeLineIndex, codeLine._text.c_str());
return false;
}
_codeLines[_currentCodeLineIndex]._vasm.push_back({uint16_t(_vasmPC - vasmSize), opcodeStr, operandStr, line, _nextInternalLabel, false, vasmSize});
_codeLines[_currentCodeLineIndex]._vasmSize += vasmSize;
_codeLines[_currentCodeLineIndex]._dontParse = true;
_nextInternalLabel = "";
return true;
}
bool createCodeLine(const std::string& code, int codeLineStart, int labelIndex, int varIndex, Expression::Int16Byte int16Byte, bool vars, CodeLine& codeLine, const std::string& moduleName)
{
// Create expression string
size_t equal = code.find_first_of("=");
std::string expression = (equal != std::string::npos) ? code.substr(equal + 1) : code;
Expression::trimWhitespace(expression);
std::vector<int> onLut;
OnGotoGosubLut onGotoGosubLut = {0x0000, "", onLut};
std::vector<uint16_t> concatStrs;
StrConcatLut strConcatLut = {0x0000, concatStrs};
std::vector<uint16_t> inputVars;
std::vector<uint16_t> inputStrs;
std::vector<uint16_t> inputTypes;
InputLut inputLut = {0x0000, 0x0000, 0x0000, 0x0000, inputVars, inputStrs, inputTypes};
std::vector<VasmLine> vasm;
std::string text = code.substr(codeLineStart, code.size() - (codeLineStart));
Expression::trimWhitespace(text);
std::string codeText = Expression::collapseWhitespaceNotStrings(text);
codeText = Expression::removeCommentsNotInStrings(codeText);
std::vector<size_t> offsets;
std::vector<std::string> tokens = Expression::tokeniseLineOffsets(codeText, " (),=", offsets);
codeLine = {text, codeText, tokens, offsets, vasm, expression, onGotoGosubLut, strConcatLut, inputLut, 0, labelIndex, varIndex, VarInt16, int16Byte, vars, false, false, moduleName};
Expression::operatorReduction(codeLine._expression);
// ASM keyword
if(_codeIsAsm)
{
return createAsmLine(code);
}
return true;
}
void getNextTempVar(void)
{
static int prevCodeLineIndex = -1;
if(_currentCodeLineIndex != prevCodeLineIndex)
{
prevCodeLineIndex = _currentCodeLineIndex;
_tempVarStart = TEMP_VAR_START;
}
else
{
// _tempVarSize bytes of temporary expression variables, defaults to 8 bytes, (4 temporary expression variables)
_tempVarStart += 2;
if(_tempVarStart >= TEMP_VAR_START + _tempVarSize) _tempVarStart = TEMP_VAR_START;
}
_tempVarStartStr = Expression::wordToHexString(_tempVarStart);
}
// Find text in a macro
bool findMacroText(const std::string& macroName, const std::string& text)
{
if(_macroIndexEntries.find(macroName) == _macroIndexEntries.end()) return false;
std::string textU = text;
Expression::strToUpper(textU);
int indexStart = _macroIndexEntries[macroName]._indexStart;
int indexEnd = _macroIndexEntries[macroName]._indexEnd;
for(int i=indexStart+1; i<indexEnd; i++)
{
size_t commentStart = _macroLines[i].find_first_of(";#");
std::string macroLine = (commentStart != std::string::npos) ? _macroLines[i].substr(0, commentStart) : _macroLines[i];
std::vector<std::string> tokens = Expression::tokeniseLine(macroLine);
for(int j=0; j<int(tokens.size()); j++)
{
Expression::stripWhitespace(tokens[j]);
Expression::strToUpper(tokens[j]);
if(tokens[j] == textU) return true;
}
// Check for nested macros
for(int j=0; j<int(tokens.size()); j++)
{
if(findMacroText(tokens[j], text)) return true;
}
}
return false;
}
// Find macro and work out it's vASM byte size
int getMacroSize(const std::string& macroName)
{
if(_macroIndexEntries.find(macroName) == _macroIndexEntries.end()) return 0;
int opcodesSize = 0;
int indexStart = _macroIndexEntries[macroName]._indexStart;
int indexEnd = _macroIndexEntries[macroName]._indexEnd;
for(int i=indexStart+1; i<indexEnd; i++)
{
size_t commentStart = _macroLines[i].find_first_of(";#");
std::string macroLine = (commentStart != std::string::npos) ? _macroLines[i].substr(0, commentStart) : _macroLines[i];
std::vector<std::string> tokens = Expression::tokeniseLine(macroLine);
int opcodeSize = 0;
for(int j=0; j<int(tokens.size()); j++)
{
opcodeSize = Assembler::getAsmOpcodeSize(tokens[j]);
if(opcodeSize)
{
opcodesSize += opcodeSize;
break;
}
}
// Check for nested macros
if(opcodeSize == 0)
{
for(int j=0; j<int(tokens.size()); j++)
{
opcodeSize = getMacroSize(tokens[j]);
if(opcodeSize)
{
opcodesSize += opcodeSize;
break;
}
}
}
}
return opcodesSize;
}
bool initialiseMacros(void)
{
std::string filename = (_codeRomType < Cpu::ROMv5a) ? "/macros.i" : "/macros_ROMv5a.i";
filename = Assembler::getIncludePath() + filename;
std::ifstream infile(filename);
if(!infile.is_open())
{
fprintf(stderr, "Compiler::initialiseMacros() : failed to open file '%s'\n", filename.c_str());
return false;
}
std::string lineToken;
while(!infile.eof())
{
std::getline(infile, lineToken);
_macroLines.push_back(lineToken);
}
// Macro names
int macroIndex = 0;
std::string macroName;
bool foundMacro = false;
for(int i=0; i<int(_macroLines.size()); i++)
{
std::vector<std::string> tokens = Expression::tokeniseLine(_macroLines[i]);
if(!foundMacro && tokens.size() >= 2 && tokens[0] == "%MACRO")
{
macroIndex = i;
macroName = tokens[1];
MacroNameEntry macroNameEntry = {macroName, 0, 0};
_macroNameEntries[macroIndex] = macroNameEntry;
MacroIndexEntry macroIndexEntry = {macroIndex, 0, 0};
_macroIndexEntries[macroName] = macroIndexEntry;
foundMacro = true;
}
else if(foundMacro && tokens.size() >= 1 && tokens[0] == "%ENDM")
{
_macroNameEntries[macroIndex]._indexEnd = i;
_macroIndexEntries[macroName]._indexEnd = i;
foundMacro = false;
}
}
// %MACRO is missing a %ENDM
if(foundMacro)
{
//_macroNameEntries.erase(macroIndex);
//_macroIndexEntries.erase(macroName);
fprintf(stderr, "Compiler::initialiseMacros() : '%s:%d' : %%MACRO '%s' is missing a %%ENDM\n", filename.c_str(), macroIndex, macroName.c_str());
return false;
}
// Calculate macros vASM byte sizes
for(auto it=_macroNameEntries.begin(); it!=_macroNameEntries.end(); ++it)
{
it->second._byteSize = getMacroSize(it->second._name);
_macroIndexEntries[it->second._name]._byteSize = it->second._byteSize;
//macroIndex = _macroIndexEntries[it->second._name]._indexStart;
//fprintf(stderr, "%s %d %d %d %d bytes\n", it->second._name.c_str(), macroIndex, it->second._indexEnd, it->second._byteSize, _macroIndexEntries[it->second._name]._byteSize);
}
return true;
}
bool initialiseCode(void)
{
// Entry point initialisation
Label label;
createLabel(_vasmPC, "_entryPoint_", 0, label, false, false, false, false);
// BASIC INIT
CodeLine codeLine;
if(createCodeLine("INIT", 0, 0, -1, Expression::Int16Both, false, codeLine)) _codeLines.push_back(codeLine);
// Rom check, (always used for versions greater than ROMv1)
if(_codeRomType > Cpu::ROMv1)
{
emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(_codeRomType)), false, 0);
emitVcpuAsm("STW", "romType", false, 0);
emitVcpuAsm("%RomCheck", "", false, 0);
}
// Initialise
emitVcpuAsm("%Initialise", "", false, 0);
// Realtime proc, relational operators and array helpers are CALLS from zero page for efficiency
if(_codeRomType < Cpu::ROMv5a)
{
// Linked only when actually used, the semi-colon is removed by enableSysInitFunc()
for(int i=0; i<int(_sysInitNames.size()); i++)
{
emitVcpuAsm(";%" + _sysInitNames[i], "", false, 0);
}
}
return true;
}
void finaliseCode(void)
{
CodeLine codeLine;
// Add END to code
if(_codeLines.size())
{
bool foundEnd = false;
for(int i=0; i<int(_codeLines[_codeLines.size() - 1]._tokens.size()); i++)
{
std::string token = _codeLines[_codeLines.size() - 1]._tokens[i];
Expression::strToUpper(token);
if(token == "END")
{
foundEnd = true;
break;
}
}
if(!foundEnd)
{
if(createCodeLine("END", 0, -1, -1, Expression::Int16Both, false, codeLine)) _codeLines.push_back(codeLine);
}
}
}
int createVcpuAsm(const std::string& opcodeStr, const std::string& operandStr, int codeLineIdx, std::string& line)
{
UNREFERENCED_PARAM(codeLineIdx);
int vasmSize = 0;
std::string opcode = std::string(opcodeStr);
// Get macro size
if(opcode.size() && opcode[0] == '%')
{
opcode.erase(0, 1);
if(_macroIndexEntries.find(opcode) != _macroIndexEntries.end())
{
vasmSize = _macroIndexEntries[opcode]._byteSize;
}
}
// Get opcode size
else
{
vasmSize = Assembler::getAsmOpcodeSize(opcode);
}
_vasmPC += uint16_t(vasmSize);
//fprintf(stderr, "%s %d %04x\n", opcode.c_str(), vasmSize, _vasmPC);
std::string operand = std::string(operandStr);
line = opcode + std::string(OPCODE_TRUNC_SIZE - opcode.size(), ' ') + operand;
return vasmSize;
}
std::pair<int, int> emitVcpuAsm(const std::string& opcodeStr, const std::string& operandStr, bool nextTempVar, int codeLineIdx, const std::string& internalLabel, bool pageJump)
{
if(codeLineIdx == -1) codeLineIdx = _currentCodeLineIndex;
std::string line;
int vasmSize = createVcpuAsm(opcodeStr, operandStr, codeLineIdx, line);
// NEXT and THEN don't know where the next vasm instruction is, so they use _nextInternalLabel, (which has priority over internalLabel)
std::string intLabel = (_nextInternalLabel.size()) ? _nextInternalLabel : internalLabel;
// Discarded labels are replaced correctly later in outputLabels()
if(_nextInternalLabel.size() && internalLabel.size()) _discardedLabels.push_back({_vasmPC, internalLabel});
_codeLines[codeLineIdx]._vasm.push_back({uint16_t(_vasmPC - vasmSize), opcodeStr, operandStr, line, intLabel, pageJump, vasmSize});
_codeLines[codeLineIdx]._vasmSize += vasmSize;
if(nextTempVar) getNextTempVar();
_nextInternalLabel = "";
// Return current vasm instruction index
return std::make_pair(codeLineIdx, int(_codeLines[codeLineIdx]._vasm.size()) - 1);
}
void emitVcpuPreProcessingCmd(const std::string& cmdStr)
{
_codeLines[0]._vasm.push_back({_userCodeStart, cmdStr, "", cmdStr, "", false, 0});
}
void createVcpuAsmLabel(int codeLineIdxBra, int vcpuAsmBra, int codeLineIdxDst, int vcpuAsmDst, const std::string& label)
{
std::string opcode = _codeLines[codeLineIdxBra]._vasm[vcpuAsmBra]._opcode;
_codeLines[codeLineIdxBra]._vasm[vcpuAsmBra]._code = opcode + std::string(OPCODE_TRUNC_SIZE - opcode.size(), ' ') + label;
_codeLines[codeLineIdxBra]._vasm[vcpuAsmBra]._operand = label;
_codeLines[codeLineIdxDst]._vasm[vcpuAsmDst]._internalLabel = label;
}
// Array1d LDW expression parser
uint32_t parseArray1dVarExpression(int codeLineIndex, std::string& expression, Expression::Numeric& numeric)
{
if(!Expression::parse(expression, codeLineIndex, numeric)) return Expression::IsInvalid;
int varIndex, constIndex, strIndex;
uint32_t expressionType = isExpression(expression, varIndex, constIndex, strIndex);
if(((expressionType & Expression::HasIntVars) && (expressionType & Expression::HasOperators)) || (expressionType & Expression::HasFunctions) ||
(expressionType & Expression::HasKeywords) || (expressionType & Expression::HasStringKeywords))
{
emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(_tempVarStart)), false, codeLineIndex);
}
else if(expressionType & Expression::HasIntVars)
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("LDW", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
default: break;
}
}
return expressionType;
}
bool writeArray1d(CodeLine& codeLine, int codeLineIndex, size_t lbra, size_t rbra, int intSize, uint16_t arrayPtr)
{
// Array index from expression
Expression::Numeric arrIndex;
std::string arrText = codeLine._code.substr(lbra + 1, rbra - (lbra + 1));
uint32_t expressionType = parseArray1dVarExpression(codeLineIndex, arrText, arrIndex);
if(expressionType == Expression::IsInvalid) return false;
// Constant index
if(!(expressionType & Expression::HasIntVars))
{
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr + int16_t(std::lround(arrIndex._value))*uint16_t(intSize)), false, codeLineIndex);
emitVcpuAsm("STW", "memAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
switch(intSize)
{
case Int8:
{
emitVcpuAsm("POKE", "memAddr", false, codeLineIndex);
}
break;
case Int16:
{
switch(codeLine._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("INC", "memAddr", false, codeLineIndex); emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("DOKE", "memAddr", false, codeLineIndex); break;
default: break;
}
}
break;
default: break;
}
}
else
{
emitVcpuAsm("STW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false, codeLineIndex);
switch(intSize)
{
case Int8:
{
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("STW", "memAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
emitVcpuAsm("POKE", "memAddr", false, codeLineIndex);
}
break;
case Int16:
{
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("STW", "memAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
switch(codeLine._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("INC", "memAddr", false, codeLineIndex); emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("DOKE", "memAddr", false, codeLineIndex); break;
default: break;
}
}
break;
default: break;
}
}
return true;
}
// ArrayXd LDW expression parser
uint32_t parseArrayXdVarExpression(int codeLineIndex, std::string& expression, Expression::Numeric& numeric)
{
if(!Expression::parse(expression, codeLineIndex, numeric)) return Expression::IsInvalid;
int varIndex, constIndex, strIndex;
uint32_t expressionType = isExpression(expression, varIndex, constIndex, strIndex);
if(((expressionType & Expression::HasIntVars) && (expressionType & Expression::HasOperators)) || (expressionType & Expression::HasFunctions) ||
(expressionType & Expression::HasKeywords) || (expressionType & Expression::HasStringKeywords))
{
emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(_tempVarStart)), false, codeLineIndex);
}
else if(expressionType & Expression::HasIntVars)
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("LDW", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
default: break;
}
}
else
{
emitVcpuAsm("LDI", std::to_string(uint8_t(std::lround(numeric._value))), false, codeLineIndex);
}
return expressionType;
}
bool writeArray2d(CodeLine& codeLine, int codeLineIndex, size_t lbra, size_t rbra, int intSize, uint16_t arrayPtr)
{
// Array index from expression
std::string arrText = codeLine._code.substr(lbra + 1, rbra - (lbra + 1));
std::vector<std::string> indexTokens = Expression::tokenise(arrText, ',', true);
if(indexTokens.size() != 2)
{
fprintf(stderr, "Compiler::writeArray2d() : '%s:%d' : number of dimensions must be equal to 2, found %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, int(indexTokens.size()), codeLine._text.c_str());
return false;
}
for(int i=0; i<int(indexTokens.size()); i++)
{
Expression::Numeric arrIndex;
std::string indexToken = indexTokens[i];
Expression::stripWhitespace(indexToken);
if(parseArrayXdVarExpression(codeLineIndex, indexToken, arrIndex) == Expression::IsInvalid) return false;
emitVcpuAsm("STW", "memIndex" + std::to_string(i), false, codeLineIndex);
}
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false, codeLineIndex);
switch(intSize)
{
case Int8:
{
(getCodeRomType() >= Cpu::ROMv5a) ? emitVcpuAsm("CALLI", "convert8Arr2d", false, codeLineIndex) : emitVcpuAsm("CALL", "convert8Arr2dAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
emitVcpuAsm("POKE", "memAddr", false, codeLineIndex);
}
break;
case Int16:
{
(getCodeRomType() >= Cpu::ROMv5a) ? emitVcpuAsm("CALLI", "convert16Arr2d", false, codeLineIndex) : emitVcpuAsm("CALL", "convert16Arr2dAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
switch(codeLine._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("INC", "memAddr", false, codeLineIndex); emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("DOKE", "memAddr", false, codeLineIndex); break;
default: break;
}
}
break;
default: break;
}
return true;
}
bool writeArray3d(CodeLine& codeLine, int codeLineIndex, size_t lbra, size_t rbra, int intSize, uint16_t arrayPtr)
{
// Array index from expression
std::string arrText = codeLine._code.substr(lbra + 1, rbra - (lbra + 1));
std::vector<std::string> indexTokens = Expression::tokenise(arrText, ',', true);
if(indexTokens.size() != 3)
{
fprintf(stderr, "Compiler::writeArray3d() : '%s:%d' : number of dimensions must be equal to 3, found %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, int(indexTokens.size()), codeLine._text.c_str());
return false;
}
for(int i=0; i<int(indexTokens.size()); i++)
{
Expression::Numeric arrIndex;
std::string indexToken = indexTokens[i];
Expression::stripWhitespace(indexToken);
if(parseArrayXdVarExpression(codeLineIndex, indexToken, arrIndex) == Expression::IsInvalid) return false;
emitVcpuAsm("STW", "memIndex" + std::to_string(i), false, codeLineIndex);
}
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false, codeLineIndex);
switch(intSize)
{
case Int8:
{
(getCodeRomType() >= Cpu::ROMv5a) ? emitVcpuAsm("CALLI", "convert8Arr3d", false, codeLineIndex) : emitVcpuAsm("CALL", "convert8Arr3dAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
emitVcpuAsm("POKE", "memAddr", false, codeLineIndex);
}
break;
case Int16:
{
(getCodeRomType() >= Cpu::ROMv5a) ? emitVcpuAsm("CALLI", "convert16Arr3d", false, codeLineIndex) : emitVcpuAsm("CALL", "convert16Arr3dAddr", false, codeLineIndex);
emitVcpuAsm("LDW", "memValue", false, codeLineIndex);
switch(codeLine._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("INC", "memAddr", false, codeLineIndex); emitVcpuAsm("POKE", "memAddr", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("DOKE", "memAddr", false, codeLineIndex); break;
default: break;
}
}
break;
default: break;
}
return true;
}
bool writeArrayVar(CodeLine& codeLine, int codeLineIndex, int varIndex)
{
// Array var?
size_t lbra, rbra;
if(!Expression::findMatchingBrackets(codeLine._code, 0, lbra, rbra)) return false;
size_t equals = codeLine._code.find("=");
if(equals == std::string::npos || equals < rbra) return false;
// Previous expression result
emitVcpuAsm("STW", "memValue", false, codeLineIndex);
int intSize = _integerVars[varIndex]._intSize;
uint16_t arrayPtr = _integerVars[varIndex]._address;
Compiler::VarType varType = _integerVars[varIndex]._varType;
switch(varType)
{
case VarType::Var1Arr8:
case VarType::Var1Arr16: writeArray1d(codeLine, codeLineIndex, lbra, rbra, intSize, arrayPtr); break;
case VarType::Var2Arr8:
case VarType::Var2Arr16: writeArray2d(codeLine, codeLineIndex, lbra, rbra, intSize, arrayPtr); break;
case VarType::Var3Arr8:
case VarType::Var3Arr16: writeArray3d(codeLine, codeLineIndex, lbra, rbra, intSize, arrayPtr); break;
default: break;
}
return true;
}
void writeArrayVarNoAssign(CodeLine& codeLine, int codeLineIndex, int varIndex)
{
// Array var? (expects lbra and rbra to have previously been validated!)
size_t lbra, rbra;
Expression::findMatchingBrackets(codeLine._code, 0, lbra, rbra);
// Previous expression result
emitVcpuAsm("STW", "memValue", false, codeLineIndex);
int intSize = _integerVars[varIndex]._intSize;
uint16_t arrayPtr = _integerVars[varIndex]._address;
VarType varType = _integerVars[varIndex]._varType;
switch(varType)
{
case VarType::Var1Arr8:
case VarType::Var1Arr16: writeArray1d(codeLine, codeLineIndex, lbra, rbra, intSize, arrayPtr); break;
case VarType::Var2Arr8:
case VarType::Var2Arr16: writeArray2d(codeLine, codeLineIndex, lbra, rbra, intSize, arrayPtr); break;
case VarType::Var3Arr8:
case VarType::Var3Arr16: writeArray3d(codeLine, codeLineIndex, lbra, rbra, intSize, arrayPtr); break;
default: break;
}
}
bool writeArrayStr(CodeLine& codeLine, int codeLineIndex, Expression::Numeric& numeric, int strIndex, uint16_t srcAddr)
{
// Array var?
size_t lbra, rbra;
if(!Expression::findMatchingBrackets(codeLine._code, 0, lbra, rbra)) return false;
size_t equals = codeLine._code.find("=");
if(equals == std::string::npos || equals < rbra) return false;
uint16_t arrayPtr = _stringVars[strIndex]._address;
std::string arrText = codeLine._code.substr(lbra + 1, rbra - (lbra + 1));
// Array index from expression
Expression::Numeric arrIndex;
uint32_t expressionType = parseArray1dVarExpression(codeLineIndex, arrText, arrIndex);
if(expressionType == Expression::IsInvalid) return false;
// Constant index
if(!(expressionType & Expression::HasIntVars))
{
emitStringAddress(numeric, srcAddr);
emitVcpuAsm("STW", "strSrcAddr", false, codeLineIndex);
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr + int16_t(std::lround(arrIndex._value))*2), false, codeLineIndex);
}
// Variable index generated by parseArray1dVarExpression()
else
{
emitVcpuAsm("STW", "memIndex0", false, codeLineIndex);
emitStringAddress(numeric, srcAddr);
emitVcpuAsm("STW", "strSrcAddr", false, codeLineIndex);
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false, codeLineIndex);
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
}
emitVcpuAsm("DEEK", "", false, codeLineIndex);
emitVcpuAsm("%StringCopy", "", false, codeLineIndex);
return true;
}
bool writeArrayStrNoAssign(std::string& arrText, int codeLineIndex, int strIndex)
{
uint16_t arrayPtr = _stringVars[strIndex]._address;
// Array index from expression
Expression::Numeric arrIndex;
uint32_t expressionType = parseArray1dVarExpression(codeLineIndex, arrText, arrIndex);
if(expressionType == Expression::IsInvalid) return false;
// Constant index
if(!(expressionType & Expression::HasIntVars))
{
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr + int16_t(std::lround(arrIndex._value))*2), false, codeLineIndex);
}
// Variable index generated by parseArray1dVarExpression()
else
{
emitVcpuAsm("STW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false, codeLineIndex);
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
emitVcpuAsm("ADDW", "memIndex0", false, codeLineIndex);
}
emitVcpuAsm("DEEK", "", false, codeLineIndex);
return true;
}
void handleInt16Byte(const std::string& opcode, const std::string& operand, Expression::Numeric& numeric, bool nextTempVar)
{
switch(numeric._int16Byte)
{
case Expression::Int16Both:
{
emitVcpuAsm(opcode, operand, nextTempVar);
return;
}
break;
case Expression::Int16Low:
{
if(opcode == "SUBW")
{
emitVcpuAsm("STW", "register14", false);
emitVcpuAsm("LD", operand, false);
emitVcpuAsm("STW", "register15", false);
emitVcpuAsm("LDW", "register14", false);
emitVcpuAsm("SUBW", "register15", nextTempVar);
return;
}
else
{
emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(getTempVarStart())), false);
emitVcpuAsm("LD", operand, false);
emitVcpuAsm(opcode, Expression::byteToHexString(uint8_t(getTempVarStart())), nextTempVar);
return;
}
}
break;
case Expression::Int16High:
{
if(opcode == "SUBW")
{
emitVcpuAsm("STW", "register14", false);
emitVcpuAsm("LD", operand + " + 1", false);
emitVcpuAsm("STW", "register15", false);
emitVcpuAsm("LDW", "register14", false);
emitVcpuAsm("SUBW", "register15", nextTempVar);
return;
}
else
{
emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(getTempVarStart())), false);
emitVcpuAsm("LD", operand + " + 1", false);
emitVcpuAsm(opcode, Expression::byteToHexString(uint8_t(getTempVarStart())), nextTempVar);
return;
}
}
break;
default: break;
}
}
bool emitVcpuAsmUserVar(const std::string& opcodeStr, Expression::Numeric& numeric, bool nextTempVar)
{
std::string opcode = std::string(opcodeStr);
std::string varName = std::string(numeric._name);
int varIndex = findVar(varName);
if(varIndex == -1)
{
fprintf(stderr, "Compiler::emitVcpuAsmUserVar() : couldn't find variable name '%s'\n", varName.c_str());
return false;
}
std::string operand = "_" + _integerVars[varIndex]._name;
if(opcode == "LDW")
{
switch(numeric._int16Byte)
{
case Expression::Int16Both: opcode = "LDW"; break;
case Expression::Int16Low: opcode = "LD"; break;
case Expression::Int16High: opcode = "LD"; operand += " + 1"; break;
default: break;
}
emitVcpuAsm(opcode, operand, nextTempVar);
return true;
}
else if(opcode == "STW")
{
switch(numeric._int16Byte)
{
case Expression::Int16Both: opcode = "STW"; break;
case Expression::Int16Low: opcode = "ST"; break;
case Expression::Int16High: opcode = "ST"; operand += " + 1"; break;
default: break;
}
emitVcpuAsm(opcode, operand, nextTempVar);
return true;
}
// ADDW, SUBW, ANDW, ORW, XORW
handleInt16Byte(opcode, operand, numeric, nextTempVar);
return true;
}
// Static expression parser
OperandType parseStaticExpression(int codeLineIndex, std::string& expression, std::string& operand, Expression::Numeric& numeric)
{
if(!Expression::parse(expression, codeLineIndex, numeric)) return OperandInvalid;
int varIndex, constIndex, strIndex;
uint32_t expressionType = isExpression(expression, varIndex, constIndex, strIndex);
if(((expressionType & Expression::HasIntVars) && (expressionType & Expression::HasOperators)) ||
((expressionType & Expression::HasStrVars) && (expressionType & Expression::HasOperators)) ||
(expressionType & Expression::HasKeywords) || (expressionType & Expression::HasStringKeywords) || (expressionType & Expression::HasFunctions))
{
operand = Expression::byteToHexString(uint8_t(_tempVarStart));
return OperandTemp;
}
else if(expressionType & Expression::HasIntVars)
{
operand = _integerVars[varIndex]._name;
return OperandVar;
}
operand = std::to_string(int16_t(std::lround(numeric._value)));
return OperandConst;
}
// LDW expression parser
uint32_t parseExpression(int codeLineIndex, std::string& expression, Expression::Numeric& numeric)
{
if(!Expression::parse(expression, codeLineIndex, numeric)) return Expression::IsInvalid;
int varIndex, constIndex, strIndex;
uint32_t expressionType = isExpression(expression, varIndex, constIndex, strIndex);
if(((expressionType & Expression::HasIntVars) && (expressionType & Expression::HasOperators)) ||
((expressionType & Expression::HasStrVars) && (expressionType & Expression::HasOperators)) ||
(expressionType & Expression::HasKeywords) || (expressionType & Expression::HasStringKeywords) || (expressionType & Expression::HasFunctions))
{
emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(_tempVarStart)), false, codeLineIndex);
}
else if(expressionType & Expression::HasIntVars)
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("LDW", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
default: break;
}
}
else
{
int16_t value = int16_t(std::lround(numeric._value));
(value >= 0 && value <= 255) ? emitVcpuAsm("LDI", std::to_string(value), false, codeLineIndex) : emitVcpuAsm("LDWI", std::to_string(value), false, codeLineIndex);
}
return expressionType;
}
// Handle expression, (use this when expression has already been parsed)
uint32_t handleExpression(int codeLineIndex, std::string& expression, Expression::Numeric numeric)
{
int varIndex, constIndex, strIndex;
uint32_t expressionType = isExpression(expression, varIndex, constIndex, strIndex);
if(((expressionType & Expression::HasIntVars) && (expressionType & Expression::HasOperators)) ||
((expressionType & Expression::HasStrVars) && (expressionType & Expression::HasOperators)) ||
(expressionType & Expression::HasKeywords) || (expressionType & Expression::HasStringKeywords) || (expressionType & Expression::HasFunctions))
{
emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(_tempVarStart)), false, codeLineIndex);
}
else if(expressionType & Expression::HasIntVars)
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("LD", "_" + _integerVars[varIndex]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("LDW", "_" + _integerVars[varIndex]._name, false, codeLineIndex); break;
default: break;
}
}
else if(expressionType & Expression::HasStrVars)
{
emitVcpuAsm("LDWI", Expression::wordToHexString(_stringVars[strIndex]._address), false, codeLineIndex);
}
else
{
int16_t value = int16_t(std::lround(numeric._value));
(value >= 0 && value <= 255) ? emitVcpuAsm("LDI", std::to_string(value), false, codeLineIndex) : emitVcpuAsm("LDWI", std::to_string(value), false, codeLineIndex);
}
return expressionType;
}
bool isGosubLabel(const std::string& label)
{
for(int i=0; i<int(_gosubLabels.size()); i++)
{
if(_gosubLabels[i] == label) return true;
}
return false;
}
bool checkForGosubLabel(const std::string& moduleName, const std::string& code, int lineNumber)
{
std::vector<std::string> tokens = Expression::tokeniseLine(code, " :=");
for(int i=0; i<int(tokens.size()); i++)
{
if(Expression::strToUpper(tokens[i]) == "GOSUB")
{
if(i+1 >= int(tokens.size()))
{
fprintf(stderr, "Compiler::checkForGosubLabel() : '%s:%d' : missing label after GOSUB in '%s'\n", moduleName.c_str(), lineNumber + 1, code.c_str());
return false;
}
_gosubLabels.push_back(tokens[i+1]);
}
}
return true;
}
LabelResult checkForLabel(const std::string& moduleName, std::string& code, int lineNumber)
{
Label label;
CodeLine codeLine;
std::string gosubOpcode = "";
// Numeric labels
if(code.size() > 1 && isdigit((unsigned char)code[0]))
{
size_t space = code.find_first_of(" \n\r\f\t\v,");
if(space == std::string::npos) space = code.size() - 1;
// Force space between line numbers and line
for(size_t i=1; i<space; i++)
{
if(!isdigit((unsigned char)code[i]) && code[i] != ':' && code[i] != '!')
{
space = i;
code.insert(i, " ");
break;
}
}
if(code.size() - (space + 1) <= 2)
{
fprintf(stderr, "Compiler::checkForLabel() : '%s:%d' : line number cannot exist on its own in '%s'\n", moduleName.c_str(), lineNumber + 1, code.c_str());
return LabelError;
}
if(code[0] == '0')
{
fprintf(stderr, "Compiler::checkForLabel() : '%s:%d' : line number cannot be zero or start with zero in '%s'\n", moduleName.c_str(), lineNumber + 1, code.c_str());
return LabelError;
}
// Create label
bool numeric = false;
bool foundGosub = false;
std::string labelName = code.substr(0, space);
size_t colon = labelName.find(':');
size_t exclamation = labelName.find('!');
if(colon != std::string::npos)
{
numeric = true;
foundGosub = true;
labelName.erase(colon, 1);
}
else if(exclamation != std::string::npos)
{
numeric = true;
foundGosub = false;
labelName.erase(exclamation, 1);
}
else
{
foundGosub = isGosubLabel(labelName);
}
createLabel(_vasmPC, labelName, int(_codeLines.size()), label, numeric, true, false, foundGosub);
if(createCodeLine(code, int(space + 1), _currentLabelIndex, -1, Expression::Int16Both, false, codeLine, moduleName)) _codeLines.push_back(codeLine);
return LabelFound;
}
// Text label
size_t colon = code.find_first_of(":");
if(colon != std::string::npos)
{
std::string labelName = code.substr(0, colon);
if(Expression::isLabNameValid(labelName))
{
bool validCode = false;
// Create label
bool foundGosub = isGosubLabel(labelName);
createLabel(_vasmPC, labelName, int(_codeLines.size()), label, false, true, false, foundGosub);
// Check for label with code
if(code.size() > colon + 1)
{
std::string labelCode = code.substr(colon + 1);
Expression::stripWhitespace(labelCode);
if(labelCode.size() > 2 && createCodeLine(code, int(colon + 1), _currentLabelIndex, -1, Expression::Int16Both, false, codeLine, moduleName))
{
validCode = true;
}
}
// Check for label without code, (create dummy codeLine for labels without code, so that codeLine count remains consistant)
if(!validCode)
{
_nonNumericLabelIndex = _currentLabelIndex;
createCodeLine("'" + labelName + ":", 0, -1, -1, Expression::Int16Both, false, codeLine, moduleName);
}
_codeLines.push_back(codeLine);
return LabelFound;
}
}
// Non label code, (except if previous line had a non numeric label without code)
if(createCodeLine(code, 0, _nonNumericLabelIndex, -1, Expression::Int16Both, false, codeLine, moduleName))
{
// Don't reset _nonNumericLabelIndex until it has been assigned to a valid line
if(codeLine._code.size() >= 2) _nonNumericLabelIndex = -1;
_codeLines.push_back(codeLine);
}
return LabelNotFound;
}
bool parsePragmas(std::vector<Input>& input, int numLines)
{
// Parse each line of input for pragmas, (pragmas are case sensitive)
for(int j=0; j<numLines; j++)
{
std::string inputText = input[j]._text;
inputText = Expression::removeCommentsNotInStrings(inputText);
Pragmas::PragmaResult pragmaResult = Pragmas::handlePragmas(inputText, j);
switch(pragmaResult)
{
case Pragmas::PragmaFound: input[j]._parse = false; break;
case Pragmas::PragmaError: return false; break;
default: break;
}
}
_vasmPC = _userCodeStart;
return true;
}
bool loadModule(const std::string& moduleName, int codeLineIndex, std::vector<Input>& input, int& numLines)
{
std::string moduleFile = moduleName;
// Strip non string whitespace and quotes
Expression::stripNonStringWhitespace(moduleFile);
if(!Expression::isStringValid(moduleFile))
{
fprintf(stderr, "Compiler::loadModule() : %s is not a valid module string, use 'MODULE \"<module name>\"'\n", moduleName.c_str());
return false;
}
// Remove quotes
moduleFile.erase(0, 1);
moduleFile.erase(moduleFile.size()-1, 1);
// Prepend source code path
std::string path = Loader::getFilePath();
size_t slash = path.find_last_of("\\/");
path = (slash != std::string::npos) ? path.substr(0, slash) : ".";
std::string modulePath = path + "/" + moduleFile;
// Open module
std::ifstream infile(modulePath);
if(!infile.is_open())
{
fprintf(stderr, "Compiler::loadModule() : failed to open module %s\n", moduleName.c_str());
return false;
}
//fprintf(stderr, "Keywords::MODULE() : module path = %s\n", modulePath.c_str());
// Extract module name from module filename
slash = moduleFile.find_last_of("\\/");
if(slash != std::string::npos) moduleFile = moduleFile.substr(slash + 1);
size_t period = moduleFile.find_last_of(".");
if(period != std::string::npos) moduleFile = moduleFile.substr(0, period);
//fprintf(stderr, "Keywords::MODULE() : module name = %s\n : %d", moduleFile.c_str(), int(Compiler::getCodeLines().size()));
for(int i=0; i<int(_moduleLines.size()); i++)
{
if(moduleFile == _moduleLines[i]._name)
{
fprintf(stderr, "Compiler::loadModule() : module %s already loaded\n", moduleName.c_str());
return false;
}
}
// Read module
int inpLines = 0;
std::vector<Compiler::Input> inp;
if(!Compiler::readInputFile(infile, modulePath, inp, inpLines)) return false;
// Erase module line name and insert module names
auto itModule = _moduleLines.erase(_moduleLines.begin() + codeLineIndex);
std::vector<ModuleLine> moduleLines(inp.size());
for(int i=0; i<int(moduleLines.size()); i++)
{
moduleLines[i] = {i, moduleFile};
}
_moduleLines.insert(itModule, moduleLines.begin(), moduleLines.end());
// Erase module line and insert module
auto itInput = input.erase(input.begin() + codeLineIndex);
input.insert(itInput, inp.begin(), inp.end());
numLines = int(input.size());
return true;
}
bool parseModules(std::vector<Input>& input, int& numLines)
{
// Parse each line of input for modules
for(int j=0; j<numLines; j++)
{
std::string inputText = input[j]._text;
inputText = Expression::removeCommentsNotInStrings(inputText);
std::vector<std::string> tokens = Expression::tokenise(inputText, ' ');
if(tokens.size() == 0) continue;
Expression::strToUpper(tokens[0]);
Expression::stripWhitespace(tokens[0]);
if(tokens[0] == "MODULE")
{
if(tokens.size() < 2 || tokens.size() > 2)
{
fprintf(stderr, "Compiler::parseModules() : '%s:%d' : syntax error, use 'MODULE \"<module name>\"'\n", input[j]._text.c_str(), j);
return false;
}
// TODO: clean this up with iterators
if(!loadModule(tokens[1], j, input, numLines)) return false;
j--;
}
}
return true;
}
// Uncomments sys init funcs as they are used, (effectively inserting them into the code)
// vASM and label addresses need to be fixed, this is done at the end of parseCode()
void enableSysInitFunc(const std::string& sysInitFunc)
{
for(int i=0; i<int(_codeLines[0]._vasm.size()); i++)
{
if(_codeLines[0]._vasm[i]._opcode == std::string(";%" + sysInitFunc))
{
Expression::replaceText(_codeLines[0]._vasm[i]._opcode, ";%", "");
Expression::replaceText(_codeLines[0]._vasm[i]._code, ";%", "");
break;
}
}
}
// Parse labels and generate codeLines
bool parseLabels(std::vector<Input>& input, int numLines)
{
// GOSUB labels
for(int i=0; i<numLines; i++)
{
if(!input[i]._parse) continue;
if(!checkForGosubLabel(_moduleLines[i]._name, input[i]._text, i)) return false;
}
// All labels and code lines
for(int i=0; i<numLines; i++)
{
if(!input[i]._parse)
{
// Create dummy codeLine for pragmas, so that codeLine count remains consistant
CodeLine codeLine;
if(createCodeLine("", 0, -1, -1, Expression::Int16Both, false, codeLine, MODULE_MAIN)) _codeLines.push_back(codeLine);
continue;
}
switch(checkForLabel(_moduleLines[i]._name, input[i]._text, i))
{
case LabelFound: break;
case LabelNotFound: break;
case LabelError: return false;
default: break;
}
}
// Add END statement
finaliseCode();
return true;
}
// Get or create string
int getOrCreateString(CodeLine& codeLine, int codeLineIndex, const std::string& str, std::string& name, uint16_t& address, uint8_t maxSize, bool constString, VarType varType)
{
int index = -1, strLength = int(str.size());
// Don't count escape char '\'
int escCount = 0;
for(int i=0; i<strLength; i++)
{
if(str[i] == '\\') escCount++;
}
strLength -= escCount;
// Reuse const string if possible
if(constString)
{
for(int j=0; j<int(_stringVars.size()); j++)
{
if(_stringVars[j]._constant && _stringVars[j]._text == str)
{
index = j;
break;
}
}
if(index != -1)
{
name = _stringVars[index]._name;
address = _stringVars[index]._address;
}
else
{
// Allocate RAM for string + length and delimiter bytes
if(!Memory::getFreeRAM(Memory::FitDescending, strLength + 2, USER_CODE_START, _stringsStart, address))
{
fprintf(stderr, "Compiler::getOrCreateString() : '%s:%d' : not enough RAM for string %s='%s' of size %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, name.c_str(), str.c_str(), strLength + 2, codeLine._text.c_str());
return -1;
}
std::vector<uint16_t> arrAddrs;
std::vector<std::string> arrInits;
name = "str_" + Expression::wordToHexString(address);
StringVar stringVar = {uint8_t(strLength), uint8_t(strLength), address, str, name, "_" + name + std::string(LABEL_TRUNC_SIZE - name.size() - 1, ' '), varType, -1, true, arrInits, arrAddrs};
_stringVars.push_back(stringVar);
index = int(_stringVars.size()) - 1;
}
}
// Variable strings
else
{
// Allocate RAM for string + length and delimiter bytes
if(!Memory::getFreeRAM(Memory::FitDescending, maxSize + 2, USER_CODE_START, _stringsStart, address))
{
fprintf(stderr, "Compiler::getOrCreateString() : '%s:%d' : not enough RAM for string %s='%s' of size %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, name.c_str(), str.c_str(), maxSize + 2, codeLine._text.c_str());
return -1;
}
std::vector<uint16_t> arrAddrs;
std::vector<std::string> arrInits;
StringVar stringVar = {uint8_t(strLength), maxSize, address, str, name, "_" + name + std::string(LABEL_TRUNC_SIZE - name.size() - 1, ' '), varType, -1, false, arrInits, arrAddrs};
_stringVars.push_back(stringVar);
index = int(_stringVars.size()) - 1;
}
return index;
}
// Get or create constant string
uint16_t getOrCreateConstString(const std::string& input, int& index)
{
std::string output = input;
std::string name;
uint16_t address;
index = getOrCreateString(_codeLines[_currentCodeLineIndex], _currentCodeLineIndex, output, name, address);
return address;
}
// Get or create constant string from int
uint16_t getOrCreateConstString(ConstStrType constStrType, int16_t input, int& index)
{
char output[16] = "";
switch(constStrType)
{
case StrChar: sprintf(output, "%c", uint8_t(input) & 0x7F); break;
case StrHex: sprintf(output, "%04X", uint16_t(input)); break;
default: break;
}
std::string name;
uint16_t address;
index = getOrCreateString(_codeLines[_currentCodeLineIndex], _currentCodeLineIndex, std::string(output), name, address);
return address;
}
// Get or create constant sub-string
uint16_t getOrCreateConstString(ConstStrType constStrType, const std::string& input, int8_t length, uint8_t offset, int& index)
{
std::string output;
switch(constStrType)
{
case StrLeft: output = input.substr(0, length); break;
case StrRight: output = input.substr(input.size() - length); break;
case StrMid: output = input.substr(offset, length); break;
case StrLower: output = Expression::strLower(input); break;
case StrUpper: output = Expression::strUpper(input); break;
default: break;
}
std::string name;
uint16_t address;
index = getOrCreateString(_codeLines[_currentCodeLineIndex], _currentCodeLineIndex, output, name, address);
return address;
}
// Create an array of strings
int createStringArray(CodeLine& codeLine, int codeLineIndex, const std::string& name, uint8_t size, bool isInit, std::vector<std::string>& arrInits, std::vector<uint16_t>& arrAddrs)
{
int index = -1;
if(size > USER_STR_SIZE)
{
fprintf(stderr, "Compiler::createStringArray() : '%s:%d' : length %d of string is larger than maximum of %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, size, USER_STR_SIZE, codeLine._text.c_str());
return -1;
}
bool constArray = (size == 0);
if(constArray && arrInits.size() != arrAddrs.size())
{
fprintf(stderr, "Compiler::createStringArray() : '%s:%d' : CONST array initialisers missing, found %d, expecting %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, int(arrInits.size()), int(arrAddrs.size()), codeLine._text.c_str());
return -1;
}
// Allocate RAM for array of strings
for(int i=0; i<int(arrAddrs.size()); i++)
{
uint8_t strSize = uint8_t(((constArray) ? arrInits[i].size() : size));
if(!Memory::getFreeRAM(Memory::FitDescending, strSize + 2, USER_CODE_START, _arraysStart, arrAddrs[i]))
{
fprintf(stderr, "Compiler::createStringArray() : '%s:%d' : not enough RAM for string %s of size %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, name.c_str(), strSize + 2, codeLine._text.c_str());
return -1;
}
}
// Array of pointers to strings
uint16_t address = 0x0000;
int arraySize = int(arrAddrs.size())*2;
if(!Memory::getFreeRAM(Memory::FitDescending, arraySize, USER_CODE_START, _arraysStart, address, false)) // arrays do not need to be contained within pages
{
fprintf(stderr, "Keywords::createStringArray() : '%s:%d' : not enough RAM for int array of size %d : %s\n", codeLine._moduleName.c_str(), codeLineIndex, arraySize, codeLine._text.c_str());
return -1;
}
// TODO: Fix this, so that _constant can be used in string arrays properly
// Max string size = 0 so compiler can differentiate between const and non const string arrays, (can't set _constant to true!)
const std::string text(size, ' ');
uint8_t maxSize = (constArray) ? 0 : USER_STR_SIZE;
StringVar stringVar = {size, maxSize, address, text, name, "_" + name + std::string(LABEL_TRUNC_SIZE - name.size() - 1, ' '), VarStr2, -1, false, arrInits, arrAddrs, isInit};
_stringVars.push_back(stringVar);
index = int(_stringVars.size()) - 1;
return index;
}
void getOrCreateString(const Expression::Numeric& numeric, std::string& name, uint16_t& addr, int& index)
{
switch(numeric._varType)
{
case Expression::String:
{
getOrCreateConstString(numeric._text, index);
name = getStringVars()[index]._name;
addr = getStringVars()[index]._address;
}
break;
case Expression::StrVar:
{
name = getStringVars()[index]._name;
addr = getStringVars()[index]._address;
}
break;
case Expression::Constant:
{
name = getConstants()[index]._name;
addr = getConstants()[index]._address;
}
break;
default: break;
}
}
void emitStringAddress(const Expression::Numeric& numeric, uint16_t address)
{
if(numeric._varType == Expression::Str2Var || numeric._varType == Expression::TmpStrAddr)
{
emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(getTempVarStart())), false);
}
else
{
emitVcpuAsm("LDWI", Expression::wordToHexString(address), false);
}
}
// ********************************************************************************************
// Recursive Descent Parser helpers
// ********************************************************************************************
char peek(bool skipSpaces)
{
// Skipping spaces can attach hex numbers to variables, keywords, etc
while(skipSpaces && Expression::peek() == ' ')
{
if(!Expression::advance(1)) return 0;
}
return Expression::peek();
}
char get(bool skipSpaces)
{
// Skipping spaces can attach hex numbers to variables, keywords, etc
while(skipSpaces && Expression::peek() == ' ')
{
if(!Expression::advance(1)) return 0;
}
return Expression::get();
}
bool find(char chr)
{
if(peek(true) == chr)
{
get(true);
return true;
}
return false;
}
void setCurrentCodeLine(void)
{
// Module line, Pragma parsing happens before any code has been parsed, so _codeLines[] may be empty
_codeLineStart = getCodeLineStart(_currentCodeLineIndex);
_codeLineText = (int(_codeLines.size()) > _currentCodeLineIndex) ? _codeLines[_currentCodeLineIndex]._code : "PRAGMA";
_codeLineModule = (int(_moduleLines.size()) > _currentCodeLineIndex) ? _moduleLines[_currentCodeLineIndex]._name : "MAIN";
}
bool number(double& value)
{
char uchr;
bool isDouble = false;
std::string valueStr;
uchr = char(toupper((unsigned char)peek(true)));
valueStr.push_back(uchr); get(true);
uchr = char(toupper((unsigned char)peek(true)));
if((uchr >= '0' && uchr <= '9') || uchr == 'X' || uchr == 'H' || uchr == 'B' || uchr == 'O' || uchr == 'Q' || uchr == '.')
{
if(uchr == '.') isDouble = true;
valueStr.push_back(uchr); get(true);
uchr = char(toupper((unsigned char)peek(true)));
while(uchr && ((uchr >= '0' && uchr <= '9') || (uchr >= 'A' && uchr <= 'F') || uchr == '.'))
{
if(uchr == '.')
{
// Check for multiple periods
if(isDouble) return false;
isDouble = true;
}
// Don't skip spaces here, as hex numbers can become attached to variables, keywords, etc
valueStr.push_back(get(false));
uchr = char(toupper((unsigned char)peek(false)));
}
}
if(!isDouble)
{
int16_t ivalue;
bool success = Expression::stringToI16(valueStr, ivalue);
value = double(ivalue);
return success;
}
Expression::stringToDouble(valueStr, value);
return true;
}
Expression::Numeric getString(void)
{
// First quote
get(true);
// Don't skip spaces within string, skip escaped quotes
char prev = 0;
std::string text;
while(peek(false) && ((prev == '\\' && peek(false) == '"') || peek(false) != '"'))
{
prev = peek(false);
text += get(false);
}
if(!peek(true) || peek(true) != '"')
{
fprintf(stderr, "Compiler::getString() : '%s:%d' : syntax error in string '%s' : '%s'\n", _codeLineModule.c_str(), _codeLineStart, text.c_str(), _codeLineText.c_str());
return Expression::Numeric();
}
// Last quote
get(true);
return Expression::Numeric(0, -1, true, false, false, Expression::String, Expression::BooleanCC, Expression::Int16Both, std::string(""), text);
}
Expression::Numeric addressOf(void)
{
size_t lbra, rbra;
std::string params;
int indices[MAX_ARRAY_DIMS] = {0};
std::string varName = Expression::getExpression();
if(varName.size() && !isalpha(varName[0]))
{
fprintf(stderr, "Compiler::sizeOf() : '%s:%d' : syntax error : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
// Parse index params if they exist for arrays, (they must evaluate to literals)
std::vector<std::string> indexTokens;
size_t varEnd = varName.find_first_of("-+/*%&<>=();,."); // TODO: this needs to be done in as betterer way, repeated multiple times throughout the code
if(Expression::findMatchingBrackets(varName, 0, lbra, rbra) && lbra == varEnd)
{
params = varName.substr(lbra + 1, rbra - (lbra + 1));
indexTokens = Expression::tokenise(params, ',', true);
if(indexTokens.size() < 1 || indexTokens.size() > MAX_ARRAY_DIMS)
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : wrong number of indicies : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
// Save expression string, (parseExpression() destroys current expression string)
std::string expression = Expression::getExpression();
// Save output's nested count, (parseExpression() can modify output's nested count)
int nestedCount = Expression::getOutputNumeric()._nestedCount;
// Only literal params are valid, use ADDR() for variable params
for(int i=0; i<int(indexTokens.size()); i++)
{
std::string token = indexTokens[i];
Expression::stripWhitespace(token);
std::string operand;
Expression::Numeric numeric; // = expression(); // TODO: Find out why expression() can't be called instead of parseExpression()
if(parseStaticExpression(_currentCodeLineIndex, token, operand, numeric) == OperandInvalid) return Expression::Numeric();
if(numeric._varType != Expression::Number)
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : indicies must be literal expressions : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
// Order of indices is [0]:k [1]:j [2]:i : func(k, j, i)
indices[i + MAX_ARRAY_DIMS - indexTokens.size()] = int16_t(std::lround(numeric._value));
if(indices[i + MAX_ARRAY_DIMS - indexTokens.size()] < 0)
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : indicies must be >= 0\n : %s", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
}
// Restore output's nested count
Expression::getOutputNumeric()._nestedCount = nestedCount;
// Restore expression string
Expression::setExpression(expression);
}
int varIndex = -1;
std::string oldName;
if(indexTokens.size())
{
oldName = varName.substr(0, rbra+1);
varIndex = findVar(varName);
}
else
{
if(varName.back() == ')') varName.erase(varName.size()-1);
varIndex = findVar(varName, oldName);
}
int strIndex = findStr(varName);
int labIndex = findLabel(varName);
int constIndex = findConst(varName);
// Use oldName instead of varName for advancing, as varName gets name mangled by findVar()
Expression::advance(oldName.size());
// Int vars and int arrays
uint16_t address = 0x0000;
if(varIndex != -1)
{
int numIndices = int(_integerVars[varIndex]._arrSizes.size());
for(int i=0; i<numIndices; i++)
{
int dim = _integerVars[varIndex]._arrSizes[i];
if(indices[i] > dim - 1)
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : index %d:%d greater than array dimension %d:%d : %s\n", _codeLineModule.c_str(), _codeLineStart, i, indices[i], i, dim, _codeLineText.c_str());
return Expression::Numeric();
}
}
switch(_integerVars[varIndex]._varType)
{
case Var1Arr8:
case Var1Arr16: address = _integerVars[varIndex]._address + uint16_t(indices[2] * _integerVars[varIndex]._intSize); break;
// Returns address of first element in first dimension of array, but array dimensions are NOT guaranteed to be sequential in memory
case Var2Arr8:
case Var2Arr16: address = _integerVars[varIndex]._arrAddrs[0][indices[1]] + uint16_t(indices[2] * _integerVars[varIndex]._intSize); break;
// Returns address of first element in first dimension of array, but array dimensions are NOT guaranteed to be sequential in memory
case Var3Arr8:
case Var3Arr16: address = _integerVars[varIndex]._arrAddrs[indices[0]][indices[1]] + uint16_t(indices[2] * _integerVars[varIndex]._intSize); break;
default: address = _integerVars[varIndex]._address; break;
}
}
// Strings and string arrays
else if(strIndex != -1)
{
switch(_stringVars[strIndex]._varType)
{
// Returns address of a string or an address of a char in a string, (including length and delimiter bytes)
case VarStr:
{
// Order of indices is [0]:k [1]:j [2]:i : func(k, j, i)
switch(indexTokens.size())
{
case 0: address = _stringVars[strIndex]._address; break;
case 1: address = _stringVars[strIndex]._address + uint16_t(indices[2]); break;
default:
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : too many indices for string array '%s(%s) : %s'\n", _codeLineModule.c_str(), _codeLineStart, _stringVars[strIndex]._name.c_str(), params.c_str(),
_codeLineText.c_str());
return Expression::Numeric();
}
break;
}
}
break;
// Returns an address of a string in an array or an address of a char in a string in an array, (including length and delimiter bytes)
case VarStr2:
{
// Order of indices is [0]:k [1]:j [2]:i : func(k, j, i)
switch(indexTokens.size())
{
case 0: address = _stringVars[strIndex]._arrAddrs[0]; break;
case 1: address = _stringVars[strIndex]._arrAddrs[indices[2]]; break;
case 2: address = _stringVars[strIndex]._arrAddrs[indices[1]] + uint16_t(indices[2]); break;
default:
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : too many indices for string array '%s(%s) : %s'\n", _codeLineModule.c_str(), _codeLineStart, _stringVars[strIndex]._name.c_str(), params.c_str(),
_codeLineText.c_str());
return Expression::Numeric();
}
break;
}
}
break;
default: break;
}
}
// labels
else if(labIndex != -1)
{
address = _labels[labIndex]._address;
}
// Constants
else if(constIndex != -1)
{
address = _constants[constIndex]._address;
}
else
{
fprintf(stderr, "Compiler::addressOf() : '%s:%d' : syntax error : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
bool relocatable = (labIndex > 0) ? true : false;
return Expression::Numeric(address, -1, true, false, relocatable, Expression::Number, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
}
Expression::Numeric sizeOf(void)
{
std::string varName = Expression::getExpression();
if(varName.size() && !isalpha(varName[0]))
{
fprintf(stderr, "Compiler::sizeOf() : '%s:%d' : syntax error : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
if(varName.back() == ')') varName.erase(varName.size()-1);
std::string oldName;
int varIndex = findVar(varName, oldName);
int strIndex = findStr(varName);
int constIndex = findConst(varName);
// Use oldName instead of varName for advancing, as varName gets name mangled by findVar() for local variables
Expression::advance(oldName.size());
// Int and int arrays
uint16_t size = 0;
if(varIndex != -1)
{
size = uint16_t(_integerVars[varIndex]._intSize);
switch(_integerVars[varIndex]._varType)
{
case Var1Arr8:
case Var1Arr16: size *= _integerVars[varIndex]._arrSizes[2]; break;
case Var2Arr8:
case Var2Arr16: size *= _integerVars[varIndex]._arrSizes[1] * _integerVars[varIndex]._arrSizes[2]; break;
case Var3Arr8:
case Var3Arr16: size *= _integerVars[varIndex]._arrSizes[0] * _integerVars[varIndex]._arrSizes[1] * _integerVars[varIndex]._arrSizes[2]; break;
default: break;
}
}
// String and string arrays
else if(strIndex != -1)
{
switch(_stringVars[strIndex]._varType)
{
// Returns allocated size of string, including length and delimiter bytes
case VarStr: size = uint16_t(_stringVars[strIndex]._maxSize + 2); break;
// Returns total allocated size of all strings in array, including length and delimiter bytes
case VarStr2:
{
for(int i=0; i<int(_stringVars[strIndex]._arrAddrs.size()); i++)
{
// Const string array
if(_stringVars[strIndex]._maxSize == 0)
{
size += uint16_t(_stringVars[strIndex]._arrInits[i].size() + 2);
}
// String array
else
{
size += _stringVars[strIndex]._maxSize + 2;
}
}
}
break;
default: break;
}
}
// Constants
else if(constIndex != -1)
{
size = _constants[constIndex]._size;
}
else
{
fprintf(stderr, "Compiler::sizeOf() : '%s:%d' : syntax error : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return Expression::Numeric();
}
return Expression::Numeric(size, -1, true, false, false, Expression::Number, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
}
bool userFunc(const std::string& name)
{
size_t lbra, rbra;
std::string funcText = Expression::getExpression();
if(!Expression::findMatchingBrackets(funcText, 0, lbra, rbra))
{
fprintf(stderr, "Compiler::userFunc() : '%s:%d' : parenthesis error : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return false;
}
funcText = funcText.substr(lbra + 1, rbra - (lbra + 1));
std::vector<std::string> params = Expression::tokenise(funcText, ',', true);
if(params.size() == 0)
{
fprintf(stderr, "Compiler::userFunc() : '%s:%d' : syntax error, need at least one parameter : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
return false;
}
int paramsSize = int(getDefFunctions()[name]._params.size());
if(paramsSize != int(params.size()))
{
fprintf(stderr, "Compiler::userFunc() : '%s:%d' : syntax error, wrong number of parameters, expecting %d : %s\n", _codeLineModule.c_str(), _codeLineStart, paramsSize, _codeLineText.c_str());
return false;
}
std::string func = getDefFunctions()[name]._function;
for(int i=0; i<int(params.size()); i++)
{
Expression::stripWhitespace(params[i]);
Expression::replaceText(func, getDefFunctions()[name]._params[i], params[i]);
}
// Substitute function and re-create expression, (factor() then parses the new expression string)
intptr_t offset = Expression::getExpression() - (char *)Expression::getExpressionToParseString().c_str();
Expression::replaceText(Expression::getExpressionToParseString(), funcText, func, offset);
Expression::setExpression(Expression::getExpressionToParseString(), offset);
return true;
}
Expression::Numeric factor(int16_t defaultValue, bool returnAddress=false)
{
double value = 0;
Expression::Numeric numeric;
setCurrentCodeLine();
// Fast boolean conditions, (condition must be enclosed within paranthesis. e.g. '&(a), &(a XOR b), etc')
if(Expression::find(" &("))
{
Expression::getOutputNumeric()._nestedCount++;
numeric = expression(returnAddress); if(!numeric._isValid) return numeric;
numeric._ccType = Expression::FastCC;
// Parameters
while(peek(true) && peek(true) != ')')
{
if(get(true) == ',') numeric._params.push_back(expression());
}
Expression::getOutputNumeric()._nestedCount--;
if(peek(true) != ')')
{
fprintf(stderr, "Compiler::factor() : '%s:%d' : found '%c' expecting ')'\n", Expression::getExpressionToParse(), _codeLineStart, peek(true));
numeric = Expression::Numeric();
}
get(true);
// Label gets filled in later by addLabelToJumpCC(), (optimiser removes extra LDW/STW pairs)
Operators::createSingleOp("LDW", numeric);
emitVcpuAsm("BEQ", "", false);
Operators::createSingleOp("STW", numeric);
}
else if(peek(true) == '(')
{
Expression::getOutputNumeric()._nestedCount++;
get(true);
numeric = expression(returnAddress); if(!numeric._isValid) return numeric;
// Parameters
while(peek(true) && peek(true) != ')')
{
if(get(true) == ',') numeric._params.push_back(expression());
}
Expression::getOutputNumeric()._nestedCount--;
if(peek(true) != ')')
{
fprintf(stderr, "Compiler::factor() : '%s:%d' : found '%c' expecting ')'\n", Expression::getExpressionToParse(), _codeLineStart, peek(true));
numeric = Expression::Numeric();
}
get(true);
}
// Numeric literals
else if((peek(true) >= '0' && peek(true) <= '9') || peek(true) == '&')
{
if(number(value))
{
numeric = Expression::Numeric(value, -1, true, false, false, Expression::Number, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
}
else
{
fprintf(stderr, "Compiler::factor() : '%s:%d' : syntax error in number literal : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
numeric = Expression::Numeric();
}
}
// Char literals
else if(peek(true) == '\'')
{
get(true);
char chr = 0;
bool validChar = false;
if((chr = peek(false)) != 0) // don't skip spaces as char literal can be a space
{
if(chr >= 32)
{
get(false); // don't skip spaces as char literal can be a space
if(peek(false) == '\'') // don't skip spaces as char literal must be a single char
{
validChar = true;
numeric = Expression::Numeric(double(chr), -1, true, false, false, Expression::Number, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
get(false); // don't skip spaces as char literal must be a single char
}
}
}
if(!validChar)
{
fprintf(stderr, "Compiler::factor() : '%s:%d' : syntax error in character literal : %s\n", _codeLineModule.c_str(), _codeLineStart, _codeLineText.c_str());
numeric = Expression::Numeric();
}
}
// Strings
else if(peek(true) == '"')
{
// Handles quotes internally
numeric = getString();
}
// 'Address of' operator
else if(peek(true) == '@')
{
get(true); numeric = addressOf();
}
// 'Size of' operator
else if(peek(true) == '#')
{
get(true); numeric = sizeOf();
}
// Unary operators
else if(peek(true) == '+')
{
get(true); numeric = factor(0); numeric = Operators::POS(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(peek(true) == '-')
{
get(true); numeric = factor(0); numeric = Operators::NEG(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::find("NOT "))
{
numeric = factor(0); numeric = Operators::NOT(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
// Functions with no parameters
else if(Expression::find("TIME$"))
{
numeric = Functions::TIME$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
// Functions
else if(Expression::findFunc("PEEK"))
{
numeric = factor(0); numeric = Functions::PEEK(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("DEEK"))
{
numeric = factor(0); numeric = Functions::DEEK(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("USR"))
{
numeric = factor(0); numeric = Functions::USR(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("RND"))
{
numeric = factor(0); numeric = Functions::RND(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("URND"))
{
numeric = factor(0); numeric = Functions::URND(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LEN"))
{
// Functions::IARR() needs to return an address rather than a value for LEN()
numeric = factor(0, true); numeric = Functions::LEN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("GET"))
{
numeric = factor(0); numeric = Functions::GET(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ABS"))
{
numeric = factor(0); numeric = Functions::ABS(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("SGN"))
{
numeric = factor(0); numeric = Functions::SGN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ASC"))
{
numeric = factor(0); numeric = Functions::ASC(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("STRCMP"))
{
numeric = factor(0); numeric = Functions::STRCMP(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("BCDCMP"))
{
numeric = factor(0); numeric = Functions::BCDCMP(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("VAL"))
{
numeric = factor(0); numeric = Functions::VAL(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LUP"))
{
numeric = factor(0); numeric = Functions::LUP(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ADDR"))
{
// Functions::IARR() needs to return an address rather than a value for ADDR()
numeric = factor(0, true); numeric = Functions::ADDR(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("POINT"))
{
numeric = factor(0); numeric = Functions::POINT(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("MIN"))
{
numeric = factor(0); numeric = Functions::MIN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("MAX"))
{
numeric = factor(0); numeric = Functions::MAX(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("CLAMP"))
{
numeric = factor(0); numeric = Functions::CLAMP(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("CHR$"))
{
numeric = factor(0); numeric = Functions::CHR$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("SPC$"))
{
numeric = factor(0); numeric = Functions::SPC$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("STR$"))
{
numeric = factor(0); numeric = Functions::STR$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("STRING$"))
{
numeric = factor(0); numeric = Functions::STRING$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("HEX$"))
{
numeric = factor(0); numeric = Functions::HEX$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LEFT$"))
{
numeric = factor(0); numeric = Functions::LEFT$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("RIGHT$"))
{
numeric = factor(0); numeric = Functions::RIGHT$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("MID$"))
{
numeric = factor(0); numeric = Functions::MID$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LOWER$"))
{
numeric = factor(0); numeric = Functions::LOWER$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("UPPER$"))
{
numeric = factor(0); numeric = Functions::UPPER$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("STRCAT$"))
{
numeric = factor(0); numeric = Functions::STRCAT$(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("CEIL"))
{
numeric = factor(0); numeric = Operators::CEIL(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("FLOOR"))
{
numeric = factor(0); numeric = Operators::FLOOR(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("POW"))
{
numeric = factor(0); numeric = Operators::POWF(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("SQRT"))
{
numeric = factor(0); numeric = Operators::SQRT(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("EXP2"))
{
numeric = factor(0); numeric = Operators::EXP2(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("EXP"))
{
numeric = factor(0); numeric = Operators::EXP(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LOG10"))
{
numeric = factor(0); numeric = Operators::LOG10(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LOG2"))
{
numeric = factor(0); numeric = Operators::LOG2(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("LOG"))
{
numeric = factor(0); numeric = Operators::LOG(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("SIN"))
{
numeric = factor(0); numeric = Operators::SIN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("COS"))
{
numeric = factor(0); numeric = Operators::COS(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("TAN"))
{
numeric = factor(0); numeric = Operators::TAN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ASIN"))
{
numeric = factor(0); numeric = Operators::ASIN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ACOS"))
{
numeric = factor(0); numeric = Operators::ACOS(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ATAN2"))
{
numeric = factor(0); numeric = Operators::ATAN2(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("ATAN"))
{
numeric = factor(0); numeric = Operators::ATAN(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("RAND"))
{
numeric = factor(0); numeric = Operators::RAND(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("REV16"))
{
numeric = factor(0); numeric = Operators::REV16(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("REV8"))
{
numeric = factor(0); numeric = Operators::REV8(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else if(Expression::findFunc("REV4"))
{
numeric = factor(0); numeric = Operators::REV4(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
else
{
// User functions, (DEF FN), names are NOT case sensitive, (like inbuilt functions)
for(auto it=getDefFunctions().begin(); it!=getDefFunctions().end(); ++it)
{
std::string name = it->first;
if(Expression::findFunc(name))
{
if(!userFunc(name)) return numeric;
return factor(0);
}
}
switch(peek(true))
{
// Reached end of expression
case 0: numeric = Expression::Numeric(defaultValue, -1, false, false, false, Expression::Number, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string("")); break;
default:
{
// Variables
bool foundParams = false;
std::string varName = Expression::getExpression();
if(varName.back() == ')') varName.erase(varName.size()-1);
if(varName.find('(') != std::string::npos) foundParams = true;
std::string oldName;
int varIndex = findVar(varName, oldName);
int strIndex = findStr(varName);
int constIndex = findConst(varName);
if(varIndex != -1)
{
Expression::Int16Byte int16Byte = Expression::Int16Both;
// Use oldName instead of varName for advancing, as varName gets name mangled by findVar() for local variables
Expression::advance(oldName.size());
// Arrays
if(_integerVars[varIndex]._varType == Var1Arr8 || _integerVars[varIndex]._varType == Var2Arr8 || _integerVars[varIndex]._varType == Var3Arr8 ||
_integerVars[varIndex]._varType == Var1Arr16 || _integerVars[varIndex]._varType == Var2Arr16 || _integerVars[varIndex]._varType == Var3Arr16)
{
Expression::VarType varType = Expression::Arr1Var16;
switch(_integerVars[varIndex]._varType)
{
case Var1Arr8: varType = Expression::Arr1Var8; break;
case Var2Arr8: varType = Expression::Arr2Var8; break;
case Var3Arr8: varType = Expression::Arr3Var8; break;
case Var1Arr16: varType = Expression::Arr1Var16; break;
case Var2Arr16: varType = Expression::Arr2Var16; break;
case Var3Arr16: varType = Expression::Arr3Var16; break;
default: break;
}
// Array numeric
numeric = Expression::Numeric(defaultValue, int16_t(varIndex), true, false, false, varType, Expression::BooleanCC, Expression::Int16Both, varName, std::string(""));
numeric._returnAddress = returnAddress;
// Array index parameters, (commands like LEN expect no array indices)
if(foundParams)
{
Expression::Numeric param = factor(0);
numeric._params.push_back(param);
for(int i=0; i<int(param._params.size()); i++)
{
numeric._params.push_back(param._params[i]);
}
// Read both, low or high bytes, (DEEK <X>, PEEK <X>, PEEK <X+1>)
if(Expression::find(".LO")) int16Byte = Expression::Int16Low;
if(Expression::find(".HI")) int16Byte = Expression::Int16High;
numeric._int16Byte = int16Byte;
numeric = Functions::IARR(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
}
// Vars
else
{
// Read both, low or high bytes, (LDW <X>, LD <X>, LD <X+1>)
if(Expression::find(".LO")) int16Byte = Expression::Int16Low;
if(Expression::find(".HI")) int16Byte = Expression::Int16High;
// Numeric is now passed back to compiler, (rather than just numeric._value), so make sure all fields are valid
numeric = Expression::Numeric(defaultValue, int16_t(varIndex), true, false, false, Expression::IntVar16, Expression::BooleanCC, int16Byte, varName, std::string(""));
}
}
// Strings
else if(strIndex != -1)
{
Expression::advance(varName.size());
// Arrays
if(_stringVars[strIndex]._varType == VarStr2)
{
// String array numeric
numeric = Expression::Numeric(defaultValue, int16_t(strIndex), true, false, false, Expression::Str2Var, Expression::BooleanCC, Expression::Int16Both, varName, _stringVars[strIndex]._text);
// Array index parameters, (commands like LEN expect no array indices)
if(foundParams)
{
Expression::Numeric param = factor(0);
numeric._params.push_back(param);
for(int i=0; i<int(param._params.size()); i++)
{
numeric._params.push_back(param._params[i]);
}
numeric = Functions::SARR(numeric, _codeLineModule, _codeLineText, _codeLineStart);
}
}
// Vars
else
{
// Numeric is now passed back to compiler, (rather than just numeric._value), so make sure all fields are valid
numeric = Expression::Numeric(defaultValue, int16_t(strIndex), true, false, false, Expression::StrVar, Expression::BooleanCC, Expression::Int16Both, varName, _stringVars[strIndex]._text);
}
}
// Constants
else if(constIndex != -1)
{
Expression::advance(varName.size());
switch(_constants[constIndex]._varType)
{
// Numeric is now passed back to compiler, (rather than just numeric._value), so make sure all fields are valid
case ConstInt16:
{
numeric = Expression::Numeric(_constants[constIndex]._data, int16_t(constIndex), true, false, false, Expression::Number, Expression::BooleanCC, Expression::Int16Both, varName, std::string(""));
}
break;
case ConstStr:
{
numeric = Expression::Numeric(defaultValue, int16_t(constIndex), true, false, false, Expression::Constant, Expression::BooleanCC, Expression::Int16Both, varName, _constants[constIndex]._text);
}
break;
default: break;
}
}
// Unknown symbol
else
{
numeric = Expression::Numeric(defaultValue, -1, false, false, false, Expression::Number, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
if(varName.size())
{
fprintf(stderr, "\nCompiler::factor() : '%s:%d' : found an unknown symbol '%s' : %s\n", _codeLineModule.c_str(), _codeLineStart, varName.c_str(), _codeLineText.c_str());
}
else
{
Expression::advance(-1);
fprintf(stderr, "\nCompiler::factor() : '%s:%d' : found an unknown symbol '%s' : %s\n", _codeLineModule.c_str(), _codeLineStart, Expression::getExpression(), _codeLineText.c_str());
Expression::advance(1);
}
}
}
break;
}
}
return numeric;
}
Expression::Numeric term(bool returnAddress=false)
{
Expression::Numeric numeric, result = factor(0, returnAddress);
if(!result._isValid) return result;
for(;;)
{
if(Expression::find("**")) {numeric = factor(0, returnAddress); if(!numeric._isValid) return numeric; result = Operators::POW(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('*')) {numeric = factor(0, returnAddress); if(!numeric._isValid) return numeric; result = Operators::MUL(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('/')) {numeric = factor(0, returnAddress); if(!numeric._isValid) return numeric; result = Operators::DIV(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('%')) {numeric = factor(0, returnAddress); if(!numeric._isValid) return numeric; result = Operators::MOD(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("MOD")) {numeric = factor(0, returnAddress); if(!numeric._isValid) return numeric; result = Operators::MOD(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else return result;
}
}
Expression::Numeric expr(bool returnAddress=false)
{
Expression::Numeric numeric, result = term(returnAddress);
if(!result._isValid) return result;
for(;;)
{
// Toggle string work area between lhs and rhs for '+', (no need to check that parameters are strings)
if(find('+')) {numeric = term(returnAddress); if(!numeric._isValid) return numeric; result = Operators::ADD(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('-')) {numeric = term(returnAddress); if(!numeric._isValid) return numeric; result = Operators::SUB(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else return result;
}
}
Expression::Numeric logical(bool returnAddress=false)
{
Expression::Numeric numeric, result = expr(returnAddress);
if(!result._isValid) return result;
for(;;)
{
// Boolean conditionals
if(Expression::find("==")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::EQ(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('=')) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::EQ(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("<>")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::NE(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("<=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LE(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find(">=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::GE(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('<')) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LT(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(find('>')) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::GT(result, numeric, Expression::BooleanCC, _codeLineModule, _codeLineText, _codeLineStart);}
// Normal conditionals
else if(Expression::find("&==")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::EQ(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::EQ(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&<>")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::NE(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&<=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LE(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&>=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::GE(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&<")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LT(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&>")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::GT(result, numeric, Expression::NormalCC, _codeLineModule, _codeLineText, _codeLineStart);}
// Fast conditionals
else if(Expression::find("&&==")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::EQ(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&&=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::EQ(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&&<>")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::NE(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&&<=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LE(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&&>=")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::GE(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&&<")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LT(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("&&>")) {numeric = expr(returnAddress); if(!numeric._isValid) return numeric; result = Operators::GT(result, numeric, Expression::FastCC, _codeLineModule, _codeLineText, _codeLineStart);}
else return result;
}
}
Expression::Numeric expression(bool returnAddress)
{
Expression::Numeric numeric, result = logical(returnAddress);
if(!result._isValid) return result;
for(;;)
{
if(Expression::find("AND")) {numeric = logical(returnAddress); if(!numeric._isValid) return numeric; result = Operators::AND(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("XOR")) {numeric = logical(returnAddress); if(!numeric._isValid) return numeric; result = Operators::XOR(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("OR")) {numeric = logical(returnAddress); if(!numeric._isValid) return numeric; result = Operators::OR(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("LSL")) {numeric = logical(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LSL(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("LSR")) {numeric = logical(returnAddress); if(!numeric._isValid) return numeric; result = Operators::LSR(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else if(Expression::find("ASR")) {numeric = logical(returnAddress); if(!numeric._isValid) return numeric; result = Operators::ASR(result, numeric, _codeLineModule, _codeLineText, _codeLineStart);}
else return result;
}
}
uint16_t getStringAddress(Expression::Numeric& numeric)
{
uint16_t srcAddr = 0x0000;
if(numeric._varType == Expression::StrAddr)
{
srcAddr = uint16_t(std::lround(numeric._value));
}
// String assignment from temporary
else if(numeric._varType == Expression::TmpStrVar)
{
srcAddr = Compiler::getStrWorkArea();
}
// String assignment from literal
else if(numeric._index == -1 && numeric._varType == Expression::String)
{
int index;
getOrCreateConstString(numeric._text, index);
if(index != -1) srcAddr = getStringVars()[index]._address;
}
// String assignment from var or const
else if(numeric._index != -1)
{
switch(numeric._varType)
{
case Expression::Constant: srcAddr = getConstants()[numeric._index]._address; break;
case Expression::StrVar: srcAddr = getStringVars()[numeric._index]._address; break;
default: break;
}
}
return srcAddr;
}
uint16_t getStringAddress(std::string& strText)
{
uint16_t srcAddr = 0x0000;
if(Expression::isStrNameValid(strText))
{
int index = findStr(strText);
if(index != -1)
{
srcAddr = getStringVars()[index]._address;
}
else
{
index = findConst(strText);
if(index != -1)
{
srcAddr = getConstants()[index]._address;
}
}
}
else if(Expression::isStringValid(strText))
{
// Strip quotes
strText.erase(0, 1);
strText.erase(strText.size()-1, 1);
int index;
getOrCreateConstString(strText, index);
if(index != -1) srcAddr = getStringVars()[index]._address;
}
return srcAddr;
}
StrResult assignString(CodeLine& codeLine, int codeLineIndex, int codeLineStart, Expression::Numeric& numeric, uint32_t expressionType)
{
if(codeLine._text.size() < 2) return StrNotFound;
int dstIndex = codeLine._varIndex;
if(dstIndex == -1)
{
fprintf(stderr, "Compiler::assignString() : '%s:%d' : syntax error : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return StrError;
}
bool isStrExpression = ((expressionType >= Expression::HasStrings) && (expressionType <= Expression::IsStringExpression));
if(isStrExpression || numeric._varType == Expression::StrAddr || numeric._varType == Expression::TmpStrAddr || numeric._varType == Expression::TmpStrVar || numeric._varType == Expression::Str2Var)
{
// String assignment, from var or const or literal
uint16_t srcAddr = getStringAddress(numeric);
if(srcAddr == 0x0000 && numeric._varType != Expression::Str2Var && numeric._varType != Expression::TmpStrAddr)
{
fprintf(stderr, "Compiler::assignString() : '%s:%d' : Syntax error : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return StrError;
}
// String assignment
if(Expression::getOutputNumeric()._varType == Expression::StrVar)
{
// No need to copy if src and dst are equal
uint16_t dstAddr = _stringVars[dstIndex]._address;
if(srcAddr != dstAddr)
{
emitStringAddress(numeric, srcAddr);
emitVcpuAsm("STW", "strSrcAddr", false, codeLineIndex);
emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false, codeLineIndex);
emitVcpuAsm("%StringCopy", "", false, codeLineIndex);
}
}
// String array assignment
else if(Expression::getOutputNumeric()._varType == Expression::Str2Var)
{
if(!writeArrayStr(codeLine, codeLineIndex, numeric, dstIndex, srcAddr))
{
fprintf(stderr, "Compiler::assignString() : '%s:%d' : syntax error : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return StrError;
}
}
return StrCreated;
}
return StrNotFound;
}
bool assignInt(CodeLine& codeLine, int codeLineIndex, Expression::Numeric& numeric, uint32_t expressionType, int varIndexRhs)
{
// Update result variable
if(codeLine._varIndex != -1)
{
// Expression that contains one or more vars
bool containsVars = (expressionType & Expression::HasIntVars) || (expressionType & Expression::HasStrVars) || (expressionType & Expression::HasStrConsts);
updateIntVar(int16_t(std::lround(numeric._value)), codeLine, codeLine._varIndex, containsVars);
}
// TODO: only works with Int16, fix for all var types
// Variable assignment
if(codeLine._varIndex != -1)
{
// Assignment with a var expression
if(codeLine._containsVars)
{
// Try and optimise LDW away if possible
if(varIndexRhs >= 0 && _integerVars[varIndexRhs]._varType != Var1Arr16 && !(expressionType & Expression::HasOperators) && !(expressionType & Expression::HasFunctions))
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("LD", "_" + _integerVars[varIndexRhs]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("LD", "_" + _integerVars[varIndexRhs]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("LDW", "_" + _integerVars[varIndexRhs]._name, false, codeLineIndex); break;
default: break;
}
}
if(_integerVars[codeLine._varIndex]._varType == Var1Arr8 || _integerVars[codeLine._varIndex]._varType == Var2Arr8 || _integerVars[codeLine._varIndex]._varType == Var3Arr8 ||
_integerVars[codeLine._varIndex]._varType == Var1Arr16 || _integerVars[codeLine._varIndex]._varType == Var2Arr16 || _integerVars[codeLine._varIndex]._varType == Var3Arr16)
{
if(!writeArrayVar(codeLine, codeLineIndex, codeLine._varIndex)) return false;
}
else
{
switch(codeLine._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("ST", "_" + _integerVars[codeLine._varIndex]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("ST", "_" + _integerVars[codeLine._varIndex]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("STW", "_" + _integerVars[codeLine._varIndex]._name, false, codeLineIndex); break;
default: break;
}
}
}
// Standard assignment
else
{
// Skip for functions unless function parameter was a literal
if(!(expressionType & Expression::HasFunctions) || numeric._varType == Expression::Number)
{
// 8bit constants
if(_integerVars[codeLine._varIndex]._data >=0 && _integerVars[codeLine._varIndex]._data <= 255)
{
emitVcpuAsm("LDI", std::to_string(_integerVars[codeLine._varIndex]._data), false, codeLineIndex);
}
// 16bit constants
else
{
emitVcpuAsm("LDWI", std::to_string(_integerVars[codeLine._varIndex]._data), false, codeLineIndex);
}
}
if(_integerVars[codeLine._varIndex]._varType == Var1Arr8 || _integerVars[codeLine._varIndex]._varType == Var2Arr8 || _integerVars[codeLine._varIndex]._varType == Var3Arr8 ||
_integerVars[codeLine._varIndex]._varType == Var1Arr16 || _integerVars[codeLine._varIndex]._varType == Var2Arr16 || _integerVars[codeLine._varIndex]._varType == Var3Arr16)
{
if(!writeArrayVar(codeLine, codeLineIndex, codeLine._varIndex)) return false;
}
else
{
switch(codeLine._int16Byte)
{
case Expression::Int16Low: emitVcpuAsm("ST", "_" + _integerVars[codeLine._varIndex]._name, false, codeLineIndex); break;
case Expression::Int16High: emitVcpuAsm("ST", "_" + _integerVars[codeLine._varIndex]._name + " + 1", false, codeLineIndex); break;
case Expression::Int16Both: emitVcpuAsm("STW", "_" + _integerVars[codeLine._varIndex]._name, false, codeLineIndex); break;
default: break;
}
}
}
}
return true;
}
StatementResult createVasmCode(CodeLine& codeLine, int codeLineIndex, int codeLineStart)
{
// Check for subroutine start, make sure PUSH is emitted only once, even for multi-statement lines, (codeLine is a local copy of each statement within a multi-statement codeLine)
if(!_codeLines[_currentCodeLineIndex]._pushEmitted && codeLine._labelIndex >= 0 && _labels[codeLine._labelIndex]._gosub)
{
_codeLines[_currentCodeLineIndex]._pushEmitted = true;
emitVcpuAsm("PUSH", "", false, codeLineIndex);
}
// Specific parsing requirements for most keywords, (*NOT* functions), some keywords like IF will also parse multi-statements; token[0] has to always be a valid keyword
bool isTokenZeroValid = false;
for(int i=0; i<int(codeLine._tokens.size()); i++)
{
Keywords::KeywordFuncResult result;
Keywords::KeywordResult keywordResult = Keywords::handleKeywords(codeLine, codeLine._tokens[i], codeLineIndex, i, result);
if(keywordResult == Keywords::KeywordError) return StatementError;
// Search for keyword, if found return it's statement type result
std::string token = codeLine._tokens[i];
Expression::strToUpper(token);
if(Keywords::getKeywords().find(token) != Keywords::getKeywords().end())
{
if(i == 0) isTokenZeroValid = true;
if(i > 0 && !isTokenZeroValid)
{
fprintf(stderr, "Compiler::createVasmCode() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._tokens[0].c_str(), codeLine._text.c_str());
return StatementError;
}
return Keywords::getKeywords()[token]._result;
}
}
int varIndexRhs = -1, constIndexRhs = -1, strIndexRhs = -1;
uint32_t expressionType = isExpression(codeLine._expression, varIndexRhs, constIndexRhs, strIndexRhs);
if(expressionType == Expression::IsInvalid) return StatementError;
// Parse expression, handles ints, strings, arrays, operators and functions
bool isStringVar = false;
Expression::Numeric numeric;
if(codeLine._varIndex != -1)
{
std::string name;
Expression::VarType varType = Expression::Number;
switch(codeLine._varType)
{
case VarInt16: varType = Expression::IntVar16; name = _integerVars[codeLine._varIndex]._name; break;
case Var1Arr16: varType = Expression::Arr1Var16; name = _integerVars[codeLine._varIndex]._name; break;
case Var2Arr16: varType = Expression::Arr2Var16; name = _integerVars[codeLine._varIndex]._name; break;
case Var3Arr16: varType = Expression::Arr3Var16; name = _integerVars[codeLine._varIndex]._name; break;
case Var1Arr8: varType = Expression::Arr1Var8; name = _integerVars[codeLine._varIndex]._name; break;
case Var2Arr8: varType = Expression::Arr2Var8; name = _integerVars[codeLine._varIndex]._name; break;
case Var3Arr8: varType = Expression::Arr3Var8; name = _integerVars[codeLine._varIndex]._name; break;
case VarStr: varType = Expression::StrVar; name = _stringVars[codeLine._varIndex]._name; isStringVar = true; break;
case VarStr2: varType = Expression::Str2Var; name = _stringVars[codeLine._varIndex]._name; isStringVar = true; break;
default: break;
}
// Output variable, (functions can access this variable within parse())
numeric = Expression::Numeric(0, int16_t(codeLine._varIndex), true, false, false, varType, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
}
if(!Expression::parse(codeLine._expression, codeLineIndex, numeric))
{
//fprintf(stderr, "Compiler::createVasmCode() : '%s:%d' : syntax error in '%s'\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression());
return StatementError;
}
// String assignment
StrResult stringResult = assignString(codeLine, codeLineIndex, codeLineStart, numeric, expressionType);
if(stringResult == StrCreated || isStringVar) return StringStatementParsed;
if(stringResult == StrError) return StatementError;
// Int assignment
if(!assignInt(codeLine, codeLineIndex, numeric, expressionType, varIndexRhs)) return StatementError;
return StatementExpression;
}
// Very simple check, if the programmer goes out of his way to write gnarly code, e.g. ()()()()()()(((var((()()))))), then this will be accepted
bool checkMatchingBrackets(const std::string& statement)
{
int bracketCount = 0;
for(int i=0; i<int(statement.size()); i++)
{
switch(statement[i])
{
case '(': bracketCount++; break;
case ')': bracketCount--; break;
default: break;
}
// Too many closing brackets
if(bracketCount < 0) return false;
}
// Brackets don't match
if(bracketCount != 0) return false;
return true;
}
StatementResult parseMultiStatements(const std::string& code, int codeLineIndex, int codeLineStart, int& varIndex, int& strIndex)
{
// Make a local copy, otherwise original tokens are destroyed
CodeLine codeLine = _codeLines[codeLineIndex];
// Tokenise and parse multi-statement lines
StatementResult statementResult = StatementSuccess;
std::vector<std::string> tokens = Expression::tokenise(code, ':', false);
for(int j=0; j<int(tokens.size()); j++)
{
REDO_STATEMENT:
Functions::restart();
Keywords::restart();
createCodeLine(tokens[j], 0, codeLine._labelIndex, -1, Expression::Int16Both, false, codeLine, codeLine._moduleName);
// Check statement matching brackets
if(!checkMatchingBrackets(tokens[j]))
{
fprintf(stderr, "Compiler::parseMultiStatements() : '%s:%d' : syntax error, brackets do not match : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
statementResult = StatementError;
break;
}
if(_codeLines[codeLineIndex]._dontParse) return StatementSuccess;
// Skip empty lines
if(codeLine._tokens.size() == 0) continue;
// Create vars
if(createCodeVar(codeLine, codeLineIndex, varIndex) == VarExistsAsConst) return StatementError;
createCodeStr(codeLine, codeLineIndex, strIndex);
// Create vasm
statementResult = createVasmCode(codeLine, codeLineIndex, codeLineStart);
if(statementResult == StatementError) break;
// Some commands, (such as FN), modify the BASIC source and cause a re-evaluation
if(statementResult == RedoStatementParse) goto REDO_STATEMENT;
// Some commands, (such as IF), process multi-statements themselves
if(statementResult == MultiStatementParsed) break;
}
return statementResult;
}
void addLabelToJumpCC(std::vector<VasmLine>& vasm, const std::string& label)
{
for(int i=0; i<int(vasm.size()); i++)
{
if(vasm[i]._code.substr(0, sizeof("Jump")-1) == "Jump" || _branchTypes.find(vasm[i]._code.substr(0, 3)) != _branchTypes.end())
{
vasm[i]._code += label;
return;
}
}
}
void addLabelToJump(std::vector<VasmLine>& vasm, const std::string& label)
{
for(int i=0; i<int(vasm.size()); i++)
{
if(_codeRomType >= Cpu::ROMv5a)
{
if(vasm[i]._code.find("CALLI_JUMP") != std::string::npos)
{
vasm[i]._opcode = "CALLI";
vasm[i]._code = "CALLI" + std::string(OPCODE_TRUNC_SIZE - (sizeof("CALLI")-1), ' ') + label;
return;
}
}
else
{
if(vasm[i]._code.find("BRA_JUMP") != std::string::npos)
{
vasm[i]._opcode = "BRA";
vasm[i]._code = "BRA" + std::string(OPCODE_TRUNC_SIZE - (sizeof("BRA")-1), ' ') + label;
return;
}
else if(vasm[i]._code.find("LDWI_JUMP") != std::string::npos)
{
vasm[i]._opcode = "LDWI";
vasm[i]._code = "LDWI" + std::string(OPCODE_TRUNC_SIZE - (sizeof("LDWI")-1), ' ') + label;
return;
}
}
}
}
bool parseCode(void)
{
// Parse code creating vars and vasm code, (BASIC code lines were created in ParseLabels())
int varIndex, strIndex;
for(int i=0; i<int(_codeLines.size()); i++)
{
_currentCodeLineIndex = i;
int codeLineStart = getCodeLineStart(_currentCodeLineIndex);
// First line of BASIC code is always a dummy INIT line, ignore it
if(i > 0 && _codeLines[i]._code.size() >= 2)
{
// Adjust label address
if(_codeLines[i]._labelIndex >= 0) _labels[_codeLines[i]._labelIndex]._address = _vasmPC;
// Multi-statements
StatementResult statementResult = parseMultiStatements(_codeLines[i]._code, i, codeLineStart, varIndex, strIndex);
if(statementResult == StatementError) return false;
}
}
// Check for inserted sys init funcs and adjust vASM and label addresses
for(int i=0; i<int(_codeLines[0]._vasm.size()); i++)
{
for(int j=0; j<int(_sysInitNames.size()); j++)
{
if(_codeLines[0]._vasm[i]._opcode == _sysInitNames[j])
{
uint16_t address = _codeLines[0]._vasm[i]._address;
Validater::adjustLabelAddresses(address, SYS_INIT_FUNC_LEN);
Validater::adjustVasmAddresses(0, address, SYS_INIT_FUNC_LEN);
_codeLines[0]._vasm[i]._address = address;
break;
}
}
}
return true;
}
void outputReservedWords(void)
{
std::string line = "_startAddress_ ";
Expression::addString(line, LABEL_TRUNC_SIZE - int(line.size()));
line += "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_userCodeStart) + "\n";
_output.push_back(line);
}
void outputLabels(void)
{
std::string line;
_output.push_back("; Labels\n");
// BASIC labels
for(int i=0; i<int(_labels.size()); i++)
{
std::string addrStr = Expression::wordToHexString(_labels[i]._address);
_output.push_back(_labels[i]._output + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + addrStr + "\n");
_equateLabels.push_back(_labels[i]._name);
}
// Internal labels
for(int i=0; i<int(_codeLines.size()); i++)
{
for(int j=0; j<int(_codeLines[i]._vasm.size()); j++)
{
uint16_t address = _codeLines[i]._vasm[j]._address;
std::string internalLabel = _codeLines[i]._vasm[j]._internalLabel;
if(internalLabel.size())
{
// Warn about duplicate internal labels, (should not happen)
if(findInternalLabel(internalLabel) > -1)
{
fprintf(stderr, "\nCompiler::outputLabels() : warning duplicate internal label '%s' at '0x%04x' on line '%d'\n\n", internalLabel.c_str(), address, i);
continue;
}
// Skip duplicate internal labels, (can happen when mixing ASM code with BASIC code)
bool foundDuplicate = false;
for(int k=0; k<int(_labels.size()); k++)
{
if(internalLabel.substr(1) == _labels[k]._name) // && address == _labels[k]._address)
{
fprintf(stderr, "\nCompiler::outputLabels() : warning duplicate internal label, (if mixing ASM with BASIC then safe to ignore), '%s' at '0x%04x' on line '%d'\n\n",
internalLabel.c_str(), address, i);
foundDuplicate = true;
break;
}
}
if(foundDuplicate) continue;
std::string addrStr = Expression::wordToHexString(_codeLines[i]._vasm[j]._address);
_output.push_back(internalLabel + std::string(LABEL_TRUNC_SIZE - internalLabel.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + addrStr + "\n");
_internalLabels.push_back({_codeLines[i]._vasm[j]._address, internalLabel});
_equateLabels.push_back(internalLabel);
}
}
}
// Check for label conflicts
for(int i=0; i<int(_codeLines.size()); i++)
{
for(int j=0; j<int(_codeLines[i]._vasm.size()); j++)
{
// BASIC label conflict
for(int k=0; k<int(_internalLabels.size()); k++)
{
int labelIndex = findLabel(_internalLabels[k]._address);
if(labelIndex >= 0)
{
std::string basicLabel = _labels[labelIndex]._output;
Expression::stripWhitespace(basicLabel);
Expression::replaceText(_codeLines[i]._vasm[j]._code, _internalLabels[k]._name, basicLabel);
// BASIC labels override internal labels
if(_codeLines[i]._vasm[j]._address == _labels[labelIndex]._address)
{
_codeLines[i]._vasm[j]._internalLabel = basicLabel;
}
}
// Discarded internal label
for(int l=0; l<int(_discardedLabels.size()); l++)
{
// Match on unique address embedded within names or the real address
std::string internalName = _internalLabels[k]._name.substr(_internalLabels[k]._name.size() - 4, 4);
std::string discardedName = _discardedLabels[l]._name.substr(_discardedLabels[l]._name.size() - 4, 4);
if(internalName == discardedName || _internalLabels[k]._address == _discardedLabels[l]._address)
{
Expression::replaceText(_codeLines[i]._vasm[j]._code, _discardedLabels[l]._name, _internalLabels[k]._name);
// Check local var names for matches with discarded labels from procs and update if necessary
for(int m=0; m<int(_integerVars.size()); m++)
{
std::string replacedText = _discardedLabels[l]._name.substr(1); // ignore leading '_'
std::string replaceText = _internalLabels[k]._name.substr(1); // ignore leading '_'
if(_integerVars[m]._name.find(replacedText) != std::string::npos)
{
// Regenerate local var name and local var output
Expression::replaceText(_integerVars[m]._name, replacedText, replaceText);
_integerVars[m]._output = "_" + _integerVars[m]._name;
Expression::addString(_integerVars[m]._output, LABEL_TRUNC_SIZE - int(_integerVars[m]._output.size()));
}
}
}
}
}
}
}
_output.push_back("\n");
}
void outputConsts(void)
{
_output.push_back("; Constants\n");
for(int i=0; i<int(_constants.size()); i++)
{
int16_t data = _constants[i]._data;
std::string name = _constants[i]._name;
std::string internalName = _constants[i]._internalName;
VarType constType = _constants[i]._varType;
switch(constType)
{
case ConstInt16:
{
_output.push_back(internalName + std::string(LABEL_TRUNC_SIZE - internalName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(data) + "\n");
}
break;
case ConstStr:
{
}
break;
default: break;
}
}
_output.push_back("\n");
}
void outputVars(void)
{
_output.push_back("; Global Variables\n");
for(int varIndex=0; varIndex<int(_integerVars.size()); varIndex++)
{
if(_integerVars[varIndex]._address >= INT_VAR_START) continue;
switch(_integerVars[varIndex]._varType)
{
case VarInt16:
{
std::string address = Expression::wordToHexString(_integerVars[varIndex]._address);
_output.push_back(_integerVars[varIndex]._output + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + address + "\n");
}
break;
default: break;
}
}
_output.push_back("\n");
_output.push_back("; Local Variables\n");
for(int varIndex=0; varIndex<int(_integerVars.size()); varIndex++)
{
if(_integerVars[varIndex]._address < INT_VAR_START) continue;
switch(_integerVars[varIndex]._varType)
{
case VarInt16:
{
std::string address = Expression::wordToHexString(_integerVars[varIndex]._address);
_output.push_back(_integerVars[varIndex]._output + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + address + "\n");
}
break;
default: break;
}
}
_output.push_back("\n");
}
void outputArrs(void)
{
_output.push_back("; Arrays\n");
for(int varIndex=0; varIndex<int(_integerVars.size()); varIndex++)
{
switch(_integerVars[varIndex]._varType)
{
case Var1Arr8:
{
std::string arrName = "_" + _integerVars[varIndex]._name + "_array";
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][0]) + "\n");
// Don't output DB statements for an uninitialised array
if(_integerVars[varIndex]._arrInit)
{
std::string dbString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// I array values
for(int i=0; i<_integerVars[varIndex]._arrSizes[2]; i++)
{
// Single initialisation value
if(_integerVars[varIndex]._arrInits.size() == 0)
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._init)) + " ";
}
// Multiple initialisation values
else
{
// Number of initialisation values may be smaller than array size
if(i < int(_integerVars[varIndex]._arrInits.size()))
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._arrInits[i])) + " ";
}
// Use default initialisation value for the rest of the array
else
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._init)) + " ";
}
}
}
_output.push_back(dbString + "\n");
}
}
break;
case Var1Arr16:
{
std::string arrName = "_" + _integerVars[varIndex]._name + "_array";
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][0]) + "\n");
// Don't output DW statements for an uninitialised array
if(_integerVars[varIndex]._arrInit)
{
std::string dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// I array values
for(int i=0; i<_integerVars[varIndex]._arrSizes[2]; i++)
{
// Single initialisation value
if(_integerVars[varIndex]._arrInits.size() == 0)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._init) + " ";
}
// Multiple initialisation values
else
{
// Number of initialisation values may be smaller than array size
if(i < int(_integerVars[varIndex]._arrInits.size()))
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrInits[i]) + " ";
}
// Use default initialisation value for the rest of the array
else
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._init) + " ";
}
}
}
_output.push_back(dwString + "\n");
}
}
break;
case Var2Arr8:
{
std::string arrName = "_" + _integerVars[varIndex]._name + "_array";
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._address) + "\n");
std::string dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// J array pointers
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][j]) + " ";
}
_output.push_back(dwString + "\n");
// J arrays
int initIndex = 0;
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
arrName = "_" + _integerVars[varIndex]._name + "_" + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][j]);
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][j]) + "\n");
// Don't output DB statements for an uninitialised array
if(_integerVars[varIndex]._arrInit)
{
std::string dbString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// I array values
for(int i=0; i<_integerVars[varIndex]._arrSizes[2]; i++)
{
// Single initialisation value
if(_integerVars[varIndex]._arrInits.size() == 0)
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._init)) + " ";
}
// Multiple initialisation values
else
{
// Number of initialisation values may be smaller than array size
if(initIndex < int(_integerVars[varIndex]._arrInits.size()))
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._arrInits[initIndex++])) + " ";
}
// Use default initialisation value for the rest of the array
else
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._init)) + " ";
}
}
}
_output.push_back(dbString + "\n");
}
}
}
break;
case Var2Arr16:
{
std::string arrName = "_" + _integerVars[varIndex]._name + "_array";
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._address) + "\n");
std::string dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// J array pointers
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][j]) + " ";
}
_output.push_back(dwString + "\n");
// J arrays
int initIndex = 0;
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
arrName = "_" + _integerVars[varIndex]._name + "_" + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][j]);
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[0][j]) + "\n");
// Don't output DW statements for an uninitialised array
if(_integerVars[varIndex]._arrInit)
{
dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// I array values
for(int i=0; i<_integerVars[varIndex]._arrSizes[2]; i++)
{
// Single initialisation value
if(_integerVars[varIndex]._arrInits.size() == 0)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._init) + " ";
}
// Multiple initialisation values
else
{
// Number of initialisation values may be smaller than array size
if(initIndex < int(_integerVars[varIndex]._arrInits.size()))
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrInits[initIndex++]) + " ";
}
// Use default initialisation value for the rest of the array
else
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._init) + " ";
}
}
}
_output.push_back(dwString + "\n");
}
}
}
break;
case Var3Arr8:
{
std::string arrName = "_" + _integerVars[varIndex]._name + "_array";
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._address) + "\n");
std::string dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// K array pointers
for(int k=0; k<_integerVars[varIndex]._arrSizes[0]; k++)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrLut[k]) + " ";
}
_output.push_back(dwString + "\n");
// K * J array pointers
for(int k=0; k<_integerVars[varIndex]._arrSizes[0]; k++)
{
arrName = "_" + _integerVars[varIndex]._name + "_lut_" + std::to_string(k);
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrLut[k]) + "\n");
dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// J array pointers
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[k][j]) + " ";
}
_output.push_back(dwString + "\n");
}
// K * J arrays
int initIndex = 0;
for(int k=0; k<_integerVars[varIndex]._arrSizes[0]; k++)
{
// J arrays
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
arrName = "_" + _integerVars[varIndex]._name + "_" + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[k][j]);
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[k][j]) + "\n");
// Don't output DB statements for an uninitialised array
if(_integerVars[varIndex]._arrInit)
{
std::string dbString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// I array values
for(int i=0; i<_integerVars[varIndex]._arrSizes[2]; i++)
{
// Single initialisation value
if(_integerVars[varIndex]._arrInits.size() == 0)
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._init)) + " ";
}
// Multiple initialisation values
else
{
// Number of initialisation values may be smaller than array size
if(initIndex < int(_integerVars[varIndex]._arrInits.size()))
{
dbString += Expression::byteToHexString(uint8_t(_integerVars[varIndex]._arrInits[initIndex++])) + " ";
}
// Use default initialisation value for the rest of the array
else
{
dbString += Expression::wordToHexString(uint8_t(_integerVars[varIndex]._init)) + " ";
}
}
}
_output.push_back(dbString + "\n");
}
}
}
}
break;
case Var3Arr16:
{
std::string arrName = "_" + _integerVars[varIndex]._name + "_array";
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._address) + "\n");
std::string dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// K array pointers
for(int k=0; k<_integerVars[varIndex]._arrSizes[0]; k++)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrLut[k]) + " ";
}
_output.push_back(dwString + "\n");
// K * J array pointers
for(int k=0; k<_integerVars[varIndex]._arrSizes[0]; k++)
{
arrName = "_" + _integerVars[varIndex]._name + "_lut_" + std::to_string(k);
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrLut[k]) + "\n");
dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// J array pointers
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[k][j]) + " ";
}
_output.push_back(dwString + "\n");
}
// K * J arrays
int initIndex = 0;
for(int k=0; k<_integerVars[varIndex]._arrSizes[0]; k++)
{
// J arrays
for(int j=0; j<_integerVars[varIndex]._arrSizes[1]; j++)
{
arrName = "_" + _integerVars[varIndex]._name + "_" + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[k][j]);
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_integerVars[varIndex]._arrAddrs[k][j]) + "\n");
// Don't output DW statements for an uninitialised array
if(_integerVars[varIndex]._arrInit)
{
dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// I array values
for(int i=0; i<_integerVars[varIndex]._arrSizes[2]; i++)
{
// Single initialisation value
if(_integerVars[varIndex]._arrInits.size() == 0)
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._init) + " ";
}
// Multiple initialisation values
else
{
// Number of initialisation values may be smaller than array size
if(initIndex < int(_integerVars[varIndex]._arrInits.size()))
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._arrInits[initIndex++]) + " ";
}
// Use default initialisation value for the rest of the array
else
{
dwString += Expression::wordToHexString(_integerVars[varIndex]._init) + " ";
}
}
}
_output.push_back(dwString + "\n");
}
}
}
}
break;
default: break;
}
}
_output.push_back("\n");
}
bool sanitiseString(const std::string& input, std::string& output, int& length)
{
output.clear();
int numQuotes = 0;
for(int i=0; i<int(input.size()); i++)
{
// Valid ASCII and not escape sequence
if(input[i] != '\\') output.push_back(input[i]);
// Escape single quotes
if(input[i] == '\'')
{
numQuotes++;
output.insert(output.end() - 1, '\\');
}
}
if(int(output.size()) > USER_STR_SIZE + numQuotes)
{
fprintf(stderr, "Expression::sanitiseString() : string '%s' of size '%d' is larger than '%d' chars\n", output.c_str(), int(output.size()), USER_STR_SIZE);
return false;
}
// Reduce real length of string by number of inserted escapes, (assembler removes escapes)
length = int(output.size()) - numQuotes;
return true;
}
bool outputStrs(void)
{
_output.push_back("; Strings\n");
// User strings
for(int i=0; i<int(_stringVars.size()); i++)
{
// Normal strings
if(_stringVars[i]._varType == VarStr && !_stringVars[i]._constant)
{
int len = 0;
std::string str;
if(!sanitiseString(_stringVars[i]._text, str, len)) return false;
_output.push_back(_stringVars[i]._output + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_stringVars[i]._address) + "\n");
_output.push_back(_stringVars[i]._output + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ') + std::to_string(len) + " '" + str + "' 0\n");
}
// Array of strings
else if(_stringVars[i]._varType == VarStr2)
{
std::string arrName = "_" + _stringVars[i]._name;
_output.push_back(arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_stringVars[i]._address) + "\n");
std::string dwString = arrName + std::string(LABEL_TRUNC_SIZE - arrName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// J array pointers
for(int j=0; j<int(_stringVars[i]._arrAddrs.size()); j++)
{
dwString += Expression::wordToHexString(_stringVars[i]._arrAddrs[j]) + " ";
}
_output.push_back(dwString + "\n");
// J strings
std::string defaultStr = (_stringVars[i]._arrInits.size()) ? _stringVars[i]._arrInits.back() : _stringVars[i]._text;
for(int j=0; j<int(_stringVars[i]._arrAddrs.size()); j++)
{
std::string strName = "_" + _stringVars[i]._name + "_" + Expression::wordToHexString(_stringVars[i]._arrAddrs[j]);
_output.push_back(strName + std::string(LABEL_TRUNC_SIZE - strName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_stringVars[i]._arrAddrs[j]) + "\n");
// Don't output DW statements for an uninitialised array
if(_stringVars[i]._arrInit)
{
int len = 0;
std::string str;
std::string initStr = (j < int(_stringVars[i]._arrInits.size())) ? _stringVars[i]._arrInits[j] : defaultStr;
if(!sanitiseString(initStr, str, len)) return false;
_output.push_back(strName + std::string(LABEL_TRUNC_SIZE - strName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ') + std::to_string(len) + " '" + str + "' 0\n");
}
}
}
}
_output.push_back("\n");
_output.push_back("; Constant Strings\n");
// Constant strings
for(int i=0; i<int(_stringVars.size()); i++)
{
// Normal strings
if(_stringVars[i]._varType == VarStr && _stringVars[i]._constant)
{
int len = 0;
std::string str;
if(!sanitiseString(_stringVars[i]._text, str, len)) return false;
_output.push_back(_stringVars[i]._output + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_stringVars[i]._address) + "\n");
_output.push_back(_stringVars[i]._output + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ') + std::to_string(len) + " '" + str + "' 0\n");
}
}
_output.push_back("\n");
return true;
}
bool outputDATA(void)
{
_output.push_back("; Data\n");
// Output DATA fields
for(int i=0; i<int(_dataObjects.size()); i++)
{
DataObject* pObject = _dataObjects[i].get();
switch(pObject->_dataType)
{
case DataInteger:
{
DataInt* pData = (DataInt*)pObject;
int16_t var = pData->_data;
int size = 2;
if(!Memory::getFreeRAM(Memory::FitDescending, size, USER_CODE_START, _runtimeStart, pData->_address, true))
{
fprintf(stderr, "Compiler::outputDATA() : not enough RAM for data of size %d\n", size);
return false;
}
std::string defName = "_data_" + Expression::wordToHexString(pData->_address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(pData->_address) + "\n");
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ') + std::to_string(var) + "\n");
}
break;
case DataString:
{
DataStr* pData = (DataStr*)pObject;
std::string str = pData->_data;
int size = int(str.size()) + 2;
if(!Memory::getFreeRAM(Memory::FitDescending, size, USER_CODE_START, _runtimeStart, pData->_address, true))
{
fprintf(stderr, "Compiler::outputDATA() : not enough RAM for data of size %d\n", size);
return false;
}
std::string defName = "_data_" + Expression::wordToHexString(pData->_address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(pData->_address) + "\n");
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ') + std::to_string(size-2) + " '" + str + "' 0\n");
}
break;
default: break;
}
}
// Output DATA address LUT
if(_dataObjects.size())
{
uint16_t address = 0x0000;
int size = int(_dataObjects.size()) * 2;
if(!Memory::getFreeRAM(Memory::FitDescending, size, USER_CODE_START, _runtimeStart, address, false))
{
fprintf(stderr, "Compiler::outputDATA() : not enough RAM for data LUT of size %d\n", size);
return false;
}
std::string defName = "_data_";
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
std::string dwString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<int(_dataObjects.size()); i++)
{
DataObject* pObject = _dataObjects[i].get();
dwString += Expression::wordToHexString(pObject->_address) + " ";
}
_output.push_back(dwString + "\n");
// Output DATA index
address = 0x0000;
size = 2;
if(!Memory::getFreeRAM(Memory::FitDescending, size, USER_CODE_START, _runtimeStart, address, false))
{
fprintf(stderr, "Compiler::outputDATA() : not enough RAM for data LUT of size %d\n", size);
return false;
}
defName = "_dataIndex_";
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ') + "0\n");
}
_output.push_back("\n");
return true;
}
bool outputTIME(void)
{
_output.push_back("; Time\n");
// Time array and string
if(_createTimeData)
{
int timeArraySize = 3;
uint16_t timeArrayAddress;
if(!Memory::getFreeRAM(Memory::FitDescending, timeArraySize, USER_CODE_START, _runtimeStart, timeArrayAddress))
{
fprintf(stderr, "Compiler::outputTIME() : not enough RAM for time array of size %d\n", timeArraySize);
return false;
}
std::string defName = "_timeArray_";
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(timeArrayAddress) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
_output.push_back(dbString + "00 00 00\n");
int timeStringSize = 10;
uint16_t timeStringAddress;
if(!Memory::getFreeRAM(Memory::FitDescending, timeStringSize, USER_CODE_START, _runtimeStart, timeStringAddress))
{
fprintf(stderr, "Compiler::outputTIME() : not enough RAM for time string of size %d\n", timeStringSize);
return false;
}
defName = "_timeString_";
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(timeStringAddress) + "\n");
dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
_output.push_back(dbString + std::to_string(timeStringSize - 2) + " '00:00:00' 0\n");
}
_output.push_back("\n");
return true;
}
bool outputMidiDef(int midiId, uint16_t startAddr, uint16_t jmpAddr, int size, int& dataIndex)
{
std::string defName = "def_midis_" + Expression::wordToHexString(startAddr);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(startAddr) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// Output a midi segment
for(int i=0; i<size; i++)
{
//dbString += std::to_string(_defDataMidis[midiId]._data[dataIndex++]) + " ";
dbString += Expression::byteToHexString(_defDataMidis[midiId]._data[dataIndex++]) + " ";
}
//dbString += std::to_string(MIDI_CMD_JMP_SEG) + " " + std::to_string(jmpAddr & 0x00FF) + " " + std::to_string((jmpAddr >>8) & 0x00FF);
dbString += Expression::byteToHexString(MIDI_CMD_JMP_SEG) + " " + Expression::byteToHexString(jmpAddr & 0x00FF) + " " + Expression::byteToHexString((jmpAddr >>8) & 0x00FF);
_output.push_back(dbString + "\n");
return true;
}
bool outputSpriteDef(int spriteId, int numStripeChunks, uint16_t address, int& dataIndex)
{
std::string defName = "def_sprites_" + Expression::wordToHexString(address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// Output a stripe of chunks
for(int i=0; i<numStripeChunks; i++)
{
for(int j=0; j<SPRITE_CHUNK_SIZE; j++)
{
dbString += std::to_string(_defDataSprites[spriteId]._data[dataIndex++]) + " ";
}
}
// Output stripe delimiter
dbString += std::to_string(_defDataSprites[spriteId]._data[dataIndex++]) + " ";
_output.push_back(dbString + "\n");
return true;
}
bool outputDefs(void)
{
// Create def byte data
_output.push_back("; Define Bytes\n");
for(int i=0; i<int(_defDataBytes.size()); i++)
{
std::string defName = "def_bytes_" + Expression::wordToHexString(_defDataBytes[i]._address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_defDataBytes[i]._address) + "\n");
std::string db = (_defDataBytes[i]._offset != 0) ? "DB(" + std::to_string(_defDataBytes[i]._offset) + ")" : "DB";
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + db + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<int(_defDataBytes[i]._data.size()); j++)
{
dbString += std::to_string(_defDataBytes[i]._data[j]) + " ";
}
_output.push_back(dbString + "\n");
}
_output.push_back("\n");
// Create def word data
_output.push_back("; Define Words\n");
for(int i=0; i<int(_defDataWords.size()); i++)
{
std::string defName = "def_words_" + Expression::wordToHexString(_defDataWords[i]._address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_defDataWords[i]._address) + "\n");
std::string dw = (_defDataWords[i]._offset != 0) ? "DW(" + std::to_string(_defDataWords[i]._offset) + ")" : "DW";
std::string dwString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + dw + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<int(_defDataWords[i]._data.size()); j++)
{
dwString += std::to_string(_defDataWords[i]._data[j]) + " ";
}
_output.push_back(dwString + "\n");
}
_output.push_back("\n");
// Create def image data
_output.push_back("; Define Images\n");
for(int i=0; i<int(_defDataImages.size()); i++)
{
uint16_t stride = _defDataImages[i]._stride;
uint16_t address = _defDataImages[i]._address;
uint16_t width = _defDataImages[i]._width;
// For each scanline of image data
for(int j=0; j<_defDataImages[i]._height; j++)
{
// 'Loader.gcl' is resident at these addresses when loading *.gt1 files, this onscreen memory can only be overwritten AFTER the loading process has finished
// DefDataLoaderImageChunk offscreen memory chunks are generated within Keywords::Load() for each of these scanlines
if((address >= LOADER_SCANLINE0_START && address<= LOADER_SCANLINE0_END) || (address >= LOADER_SCANLINE1_START && address<= LOADER_SCANLINE1_END) ||
(address >= LOADER_SCANLINE2_START && address<= LOADER_SCANLINE2_END))
{
}
else
{
std::string defName = "def_images_" + Expression::wordToHexString(address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int k=0; k<width; k++)
{
dbString += std::to_string(_defDataImages[i]._data[j * width + k]) + " ";
}
_output.push_back(dbString + "\n");
}
// Next destination row
address += stride;
}
}
_output.push_back("\n");
// Create def loader image chunk data
_output.push_back("; Define Loader Image Chunks\n");
for(int i=0; i<int(_defDataLoaderImageChunks.size()); i++)
{
uint16_t srcAddr = _defDataLoaderImageChunks[i]._lutEntry._srcAddr;
uint8_t length = _defDataLoaderImageChunks[i]._lutEntry._length;
std::string defName = "def_loader_image_chunks_" + Expression::wordToHexString(srcAddr);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(srcAddr) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<length; j++)
{
dbString += std::to_string(_defDataLoaderImageChunks[i]._data[j]) + " ";
}
_output.push_back(dbString + "\n");
}
_output.push_back("\n");
// Create def midi data
_output.push_back("; Define Midis\n");
for(auto it=_defDataMidis.begin(); it!=_defDataMidis.end(); ++it)
{
int dataIndex = 0;
int midiId = it->first;
//bool volume = it->second._volume;
int numSegments = int(it->second._segmentAddrs.size());
for(int i=0; i<numSegments; i++)
{
uint16_t segSize = it->second._segmentSizes[i];
uint16_t segAddr = it->second._segmentAddrs[i];
uint16_t jmpAddr = (i == numSegments-1) ? it->second._segmentAddrs[0] : it->second._segmentAddrs[i+1];
outputMidiDef(midiId, segAddr, jmpAddr, segSize, dataIndex);
}
}
_output.push_back("\n");
// Create def sprite data
_output.push_back("; Define Sprites\n");
for(auto it=_defDataSprites.begin(); it!=_defDataSprites.end(); ++it)
{
int spriteId = it->first;
// Skip invalid sprite
if(it->second._stripeAddrs.size() == 0)
{
fprintf(stderr, "Compiler::outputDefs() : Wwrning sprite %d is missing stripe addresses\n", spriteId);
continue;
}
int dataIndex = 0;
uint16_t numColumns = it->second._numColumns;
uint16_t numStripesPerCol = it->second._numStripesPerCol;
uint16_t numStripeChunks = it->second._numStripeChunks;
uint16_t remStripeChunks = it->second._remStripeChunks;
uint16_t isInstanced = it->second._isInstanced;
if(!isInstanced)
{
// For each column of sprite data
for(int j=0; j<numColumns; j++)
{
// Multiple stripes per column
if(numStripesPerCol > 1)
{
for(int k=0; k<numStripesPerCol-1; k++)
{
uint16_t address = it->second._stripeAddrs[j*numStripesPerCol*2 + k*2];
outputSpriteDef(spriteId, numStripeChunks, address, dataIndex);
}
uint16_t address = it->second._stripeAddrs[j*numStripesPerCol*2 + (numStripesPerCol-1)*2];
outputSpriteDef(spriteId, remStripeChunks, address, dataIndex);
}
// Single stripe per column
else
{
uint16_t address = it->second._stripeAddrs[j*2];
outputSpriteDef(spriteId, numStripeChunks, address, dataIndex);
}
}
}
}
_output.push_back("\n");
// Create def font data
_output.push_back("; Define Fonts\n");
if(_defDataFonts.size() > 0)
{
int fontIdSize = 1;
uint16_t fontIdAddress;
if(!Memory::getFreeRAM(Memory::FitDescending, fontIdSize, USER_CODE_START, _runtimeStart, fontIdAddress))
{
fprintf(stderr, "Compiler::outputDefs() : not enough RAM for font id var of size %d\n", fontIdSize);
return false;
}
std::string defName = "_fontId_";
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(fontIdAddress) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
_output.push_back(dbString + "0\n");
}
for(auto it=_defDataFonts.begin(); it!=_defDataFonts.end(); ++it)
{
int fontId = it->first;
// Skip invalid font
if(it->second._mapping.size() == 0)
{
fprintf(stderr, "Compiler::outputDefs() : warning font %d is missing mapping table\n", fontId);
continue;
}
// Font mapping table, (null means 32 to 127 of the ASCII set is represented, so a mapping table is not required)
uint16_t address = it->second._mapAddr;
if(address)
{
int mapSize = int(it->second._mapping.size());
std::string defName = "def_map_" + Expression::wordToHexString(address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<mapSize; i++)
{
uint8_t mapData = it->second._mapping[i];
dbString += std::to_string(mapData) + " ";
}
_output.push_back(dbString + "\n");
}
// For each char of font data
int numChars = int(it->second._data.size());
for(int i=0; i<numChars; i++)
{
address = it->second._charAddrs[i];
std::string defName = "def_char_" + Expression::wordToHexString(address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
// Output each char
std::vector<uint8_t>& charData = it->second._data[i];
for(int j=0; j<int(charData.size()); j++)
{
dbString += std::to_string(charData[j]) + " ";
}
_output.push_back(dbString + "\n");
}
// Baseline for each char, (shared by all chars in one font)
address = it->second._baseAddr;
uint16_t fgbgColour = it->second._fgbgColour;
std::string defName = "def_baseline_" + Expression::wordToHexString(address);
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(address) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<6; i++) dbString += Expression::byteToHexString(fgbgColour & 0x00FF) + " ";
dbString += "255";
_output.push_back(dbString + "\n");
}
_output.push_back("\n");
return true;
}
bool outputLuts(void)
{
_output.push_back("; Lookup Tables\n");
// Numeric labels LUT
if(_createNumericLabelLut)
{
std::vector<uint16_t> numericLabels;
std::vector<uint16_t> numericAddresses;
for(int i=0; i<int(_labels.size()); i++)
{
if(_labels[i]._numeric)
{
uint16_t numericLabel;
if(!Expression::stringToU16(_labels[i]._name, numericLabel))
{
fprintf(stderr, "Compiler::outputLuts() : bad numeric label %s : %04x\n", _labels[i]._name.c_str(), numericLabel);
return false;
}
numericLabels.push_back(numericLabel);
numericAddresses.push_back(_labels[i]._address);
}
}
if(numericLabels.size() == 0)
{
fprintf(stderr, "Compiler::outputLuts() : There is a mismatch in Numeric labels for one or more GOTO <var> and GOSUB <var>\n");
fprintf(stderr, " Correct your line numbers and add appropriate trailers, ':' for GOTO <var> and '!' for GOSUB <var>\n");
return false;
}
else
{
// Create numeric labels LUT, (delimited by 0)
int lutSize = int(numericLabels.size())*2;
uint16_t lutAddress;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize + 2, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for numeric labels LUT of size %d\n", lutSize + 2);
return false;
}
std::string lutName = "_lut_numericLabs";
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<int(numericLabels.size()); i++)
{
dwString += std::to_string(numericLabels[i]) + " ";
}
_output.push_back(dwString + "0x0000\n");
// Create numeric addresses LUT, (same size as above, but no delimiter)
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for numeric addresses LUT of size %d\n", lutSize);
return false;
}
lutName = "_lut_numericAddrs";
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<int(numericAddresses.size()); i++)
{
dwString += Expression::wordToHexString(numericAddresses[i]) + " ";
}
_output.push_back(dwString + "\n");
}
}
// ON GOTO GOSUB label LUTs
for(int i=0; i<int(_codeLines.size()); i++)
{
// Output LUT if it exists
int lutSize = int(_codeLines[i]._onGotoGosubLut._lut.size());
uint16_t lutAddress = _codeLines[i]._onGotoGosubLut._address;
std::string lutName = _codeLines[i]._onGotoGosubLut._name;
if(lutSize)
{
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<lutSize; j++)
{
int index = _codeLines[i]._onGotoGosubLut._lut[j];
if(index == -1) fprintf(stderr, "Compiler::outputLuts() : warning label index is invalid for LUT entry %d\n", j);
uint16_t labelAddress = _labels[index]._address;
dwString += Expression::wordToHexString(labelAddress) + " ";
}
_output.push_back(dwString + "\n");
}
}
// String concatenation LUTs
for(int i=0; i<int(_codeLines.size()); i++)
{
// Output LUT if it exists
int lutSize = int(_codeLines[i]._strConcatLut._lut.size());
uint16_t lutAddress = _codeLines[i]._strConcatLut._address;
std::string lutName = "_concat_" + Expression::wordToHexString(lutAddress);
if(lutSize)
{
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<lutSize; j++)
{
uint16_t address = _codeLines[i]._strConcatLut._lut[j];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
}
// INPUT LUTs
for(int i=0; i<int(_codeLines.size()); i++)
{
// Output varsLUT if it exists
int varsSize = int(_codeLines[i]._inputLut._varsLut.size());
uint16_t varsAddr = _codeLines[i]._inputLut._varsAddr;
std::string varsName = "_inputVars_" + Expression::wordToHexString(varsAddr);
if(varsSize)
{
_output.push_back(varsName + std::string(LABEL_TRUNC_SIZE - varsName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(varsAddr) + "\n");
std::string dwString = varsName + std::string(LABEL_TRUNC_SIZE - varsName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<varsSize; j++)
{
uint16_t address = _codeLines[i]._inputLut._varsLut[j];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
// Output strsLUT if it exists
int strsSize = int(_codeLines[i]._inputLut._strsLut.size());
uint16_t strsAddr = _codeLines[i]._inputLut._strsAddr;
std::string strsName = "_inputStrs_" + Expression::wordToHexString(strsAddr);
if(strsSize)
{
_output.push_back(strsName + std::string(LABEL_TRUNC_SIZE - strsName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(strsAddr) + "\n");
std::string dwString = strsName + std::string(LABEL_TRUNC_SIZE - strsName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<strsSize; j++)
{
uint16_t address = _codeLines[i]._inputLut._strsLut[j];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
// Output typesLUT if it exists
int typesSize = int(_codeLines[i]._inputLut._typesLut.size());
uint16_t typesAddr = _codeLines[i]._inputLut._typesAddr;
std::string typesName = "_inputTypes_" + Expression::wordToHexString(typesAddr);
if(typesSize)
{
_output.push_back(typesName + std::string(LABEL_TRUNC_SIZE - typesName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(typesAddr) + "\n");
std::string dwString = typesName + std::string(LABEL_TRUNC_SIZE - typesName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<typesSize; j++)
{
uint16_t type = _codeLines[i]._inputLut._typesLut[j];
dwString += Expression::wordToHexString(type) + " ";
}
_output.push_back(dwString + "\n");
}
// Output INPUT LUT
if(varsSize && strsSize && typesSize && _codeLines[i]._inputLut._address)
{
uint16_t lutAddress = _codeLines[i]._inputLut._address;
std::string name = "_input_" + Expression::wordToHexString(lutAddress);
_output.push_back(name + std::string(LABEL_TRUNC_SIZE - name.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = name + std::string(LABEL_TRUNC_SIZE - name.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
dwString += Expression::wordToHexString(varsAddr) + " ";
dwString += Expression::wordToHexString(strsAddr) + " ";
dwString += Expression::wordToHexString(typesAddr) + " ";
_output.push_back(dwString + "\n");
}
}
// Create loader image chunk LUT
if(_defDataLoaderImageChunks.size())
{
// Allocate RAM for loader images LUT and delimiter
uint16_t lutAddress;
uint16_t lutSize = uint16_t(_defDataLoaderImageChunks.size() * DefDataLoaderImageChunkLutEntrySize) + 2;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputDefs() : not enough RAM for loader image LUT of size %d\n", lutSize);
return false;
}
// Loader images LUT delimited by 0
std::string defName = "_loader_image_chunksLut";
_output.push_back(defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dbString = defName + std::string(LABEL_TRUNC_SIZE - defName.size(), ' ') + "DB" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<int(_defDataLoaderImageChunks.size()); i++)
{
uint16_t srcAddr = _defDataLoaderImageChunks[i]._lutEntry._srcAddr;
uint16_t dstAddr = _defDataLoaderImageChunks[i]._lutEntry._dstAddr;
uint8_t length = _defDataLoaderImageChunks[i]._lutEntry._length;
dbString += std::to_string(srcAddr & 0x00FF) + " ";
dbString += std::to_string((srcAddr & 0xFF00) >>8) + " ";
dbString += std::to_string(dstAddr & 0x00FF) + " ";
dbString += std::to_string((dstAddr & 0xFF00) >>8) + " ";
dbString += std::to_string(length) + " ";
}
_output.push_back(dbString + "0 0\n");
}
// MIDIS LUT
if(_defDataMidis.size())
{
// Allocate RAM for midis LUT
uint16_t lutAddress;
int lutSize = int(_defDataMidis.size()) * 2;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for midis LUT of size %d\n", lutSize);
return false;
}
std::string lutName = "_midisLut_";
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(auto it=_defDataMidis.begin(); it!=_defDataMidis.end(); ++it)
{
uint16_t address = it->second._segmentAddrs[0];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
// SPRITE ADDRESS LUTs
for(auto it=_defDataSprites.begin(); it!=_defDataSprites.end(); ++it)
{
int spriteId = it->first;
// Skip invalid sprite
if(it->second._stripeAddrs.size() == 0)
{
fprintf(stderr, "Compiler::outputLuts() : warning sprite %d is missing stripe addresses\n", spriteId);
continue;
}
// Allocate RAM for sprite addresses/offsets and delimiter
uint16_t lutAddress;
int lutSize = int(it->second._stripeAddrs.size())*2 + 2;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for sprite %d address LUT of size %d\n", spriteId, lutSize);
return false;
}
_spritesAddrLut._spriteAddrs.push_back(lutAddress);
std::string lutName = "_spriteLut_" + Expression::wordToHexString(lutAddress);
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int j=0; j<int(it->second._stripeAddrs.size()); j++)
{
uint16_t address = it->second._stripeAddrs[j];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "0x0000\n");
}
// SPRITES LUT
if(_spritesAddrLut._spriteAddrs.size())
{
// Allocate RAM for sprites LUT
if(_spritesAddrLutAddress == 0x0000)
{
int lutSize = int(_spritesAddrLut._spriteAddrs.size()) * 2;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, _spritesAddrLutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for sprites LUT of size %d\n", lutSize);
return false;
}
}
_spritesAddrLut._address = _spritesAddrLutAddress;
std::string lutName = "_spritesLut_";
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(_spritesAddrLutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<int(_spritesAddrLut._spriteAddrs.size()); i++)
{
uint16_t address = _spritesAddrLut._spriteAddrs[i];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
// FONT ADDRESS LUTs
for(auto it=_defDataFonts.begin(); it!=_defDataFonts.end(); ++it)
{
int fontId = it->first;
// Skip invalid font
if(it->second._charAddrs.size() == 0)
{
fprintf(stderr, "Compiler::outputLuts() : warning font %d is missing char addresses\n", fontId);
continue;
}
// Allocate memory for font chars + map address + baseline address
uint16_t lutAddress;
int lutSize = int(it->second._charAddrs.size())*2 + 4;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for font %d address LUT of size %d\n", fontId, lutSize);
return false;
}
_fontsAddrLut._fontAddrs.push_back(lutAddress);
std::string lutName = "_fontLut_" + Expression::wordToHexString(lutAddress);
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
// Mapping table, (mapping address is null when full ASCII set, 32 to 127, is represented)
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
uint16_t address = it->second._mapAddr;
dwString += Expression::wordToHexString(address) + " ";
// Font baseline address, baseline pixels shared by every char in a font
address = it->second._baseAddr;
dwString += Expression::wordToHexString(address) + " ";
// Characters
for(int j=0; j<int(it->second._charAddrs.size()); j++)
{
address = it->second._charAddrs[j];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
// FONTS LUT
if(_fontsAddrLut._fontAddrs.size())
{
// Allocate RAM for fonts addresses
uint16_t lutAddress;
int lutSize = int(_fontsAddrLut._fontAddrs.size()) * 2;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize, USER_CODE_START, _runtimeStart, lutAddress))
{
fprintf(stderr, "Compiler::outputLuts() : not enough RAM for fonts LUT of size %d\n", lutSize);
return false;
}
_fontsAddrLut._address = lutAddress;
std::string lutName = "_fontsLut_";
_output.push_back(lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(lutAddress) + "\n");
std::string dwString = lutName + std::string(LABEL_TRUNC_SIZE - lutName.size(), ' ') + "DW" + std::string(OPCODE_TRUNC_SIZE - 2, ' ');
for(int i=0; i<int(_fontsAddrLut._fontAddrs.size()); i++)
{
uint16_t address = _fontsAddrLut._fontAddrs[i];
dwString += Expression::wordToHexString(address) + " ";
}
_output.push_back(dwString + "\n");
}
_output.push_back("\n");
return true;
}
void outputInternalEquates(void)
{
_output.push_back("\n");
_output.push_back("; Internal Variables\n");
_output.push_back("serialRawPrev" + std::string(LABEL_TRUNC_SIZE - strlen("serialRawPrev"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(SER_RAW_PREV) + "\n");
_output.push_back("register0" + std::string(LABEL_TRUNC_SIZE - strlen("register0"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(INT_VAR_START) + "\n");
_output.push_back("register1" + std::string(LABEL_TRUNC_SIZE - strlen("register1"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x02\n");
_output.push_back("register2" + std::string(LABEL_TRUNC_SIZE - strlen("register2"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x04\n");
_output.push_back("register3" + std::string(LABEL_TRUNC_SIZE - strlen("register3"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x06\n");
_output.push_back("register4" + std::string(LABEL_TRUNC_SIZE - strlen("register4"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x08\n");
_output.push_back("register5" + std::string(LABEL_TRUNC_SIZE - strlen("register5"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x0A\n");
_output.push_back("register6" + std::string(LABEL_TRUNC_SIZE - strlen("register6"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x0C\n");
_output.push_back("register7" + std::string(LABEL_TRUNC_SIZE - strlen("register7"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x0E\n");
_output.push_back("register8" + std::string(LABEL_TRUNC_SIZE - strlen("register8"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x10\n");
_output.push_back("register9" + std::string(LABEL_TRUNC_SIZE - strlen("register9"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x12\n");
_output.push_back("register10" + std::string(LABEL_TRUNC_SIZE - strlen("register10"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x14\n");
_output.push_back("register11" + std::string(LABEL_TRUNC_SIZE - strlen("register11"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x16\n");
_output.push_back("register12" + std::string(LABEL_TRUNC_SIZE - strlen("register12"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x18\n");
_output.push_back("register13" + std::string(LABEL_TRUNC_SIZE - strlen("register13"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x1A\n");
_output.push_back("register14" + std::string(LABEL_TRUNC_SIZE - strlen("register14"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x1C\n");
_output.push_back("register15" + std::string(LABEL_TRUNC_SIZE - strlen("register15"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x1E\n");
_output.push_back("fgbgColour" + std::string(LABEL_TRUNC_SIZE - strlen("fgbgColour"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x20\n");
_output.push_back("cursorXY" + std::string(LABEL_TRUNC_SIZE - strlen("cursorXY"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x22\n");
_output.push_back("midiStream" + std::string(LABEL_TRUNC_SIZE - strlen("midiStream"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x24\n");
_output.push_back("midiDelay" + std::string(LABEL_TRUNC_SIZE - strlen("midiDelay"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x26\n");
_output.push_back("miscFlags" + std::string(LABEL_TRUNC_SIZE - strlen("miscFlags"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x28\n");
_output.push_back("timerTick" + std::string(LABEL_TRUNC_SIZE - strlen("timerTick"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x2A\n");
_output.push_back("timerPrev" + std::string(LABEL_TRUNC_SIZE - strlen("timerPrev"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + "register0 + 0x2C\n");
_output.push_back("\n");
_output.push_back("; Internal Constants\n");
_output.push_back("ENABLE_SCROLL_BIT" + std::string(LABEL_TRUNC_SIZE - strlen("ENABLE_SCROLL_BIT"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(ENABLE_SCROLL_BIT) + "\n");
_output.push_back("ON_BOTTOM_ROW_BIT" + std::string(LABEL_TRUNC_SIZE - strlen("ON_BOTTOM_ROW_BIT"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(ON_BOTTOM_ROW_BIT) + "\n");
_output.push_back("DISABLE_CLIP_BIT" + std::string(LABEL_TRUNC_SIZE - strlen("DISABLE_CLIP_BIT"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(DISABLE_CLIP_BIT) + "\n");
_output.push_back("ENABLE_SCROLL_MSK" + std::string(LABEL_TRUNC_SIZE - strlen("ENABLE_SCROLL_MSK"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(ENABLE_SCROLL_MSK) + "\n");
_output.push_back("ON_BOTTOM_ROW_MSK" + std::string(LABEL_TRUNC_SIZE - strlen("ON_BOTTOM_ROW_MSK"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(ON_BOTTOM_ROW_MSK) + "\n");
_output.push_back("DISABLE_CLIP_MSK" + std::string(LABEL_TRUNC_SIZE - strlen("DISABLE_CLIP_MSK"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(DISABLE_CLIP_MSK) + "\n");
_output.push_back("\n");
_output.push_back("; Internal Buffers\n");
_output.push_back("regsWorkArea" + std::string(LABEL_TRUNC_SIZE - strlen("regsWorkArea"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(getRegWorkArea()) + "\n");
_output.push_back("textWorkArea" + std::string(LABEL_TRUNC_SIZE - strlen("textWorkArea"), ' ') + "EQU" + std::string(OPCODE_TRUNC_SIZE - 3, ' ') + Expression::wordToHexString(getStrWorkArea()) + "\n");
_output.push_back("\n");
}
void outputIncludes(void)
{
_output.push_back("; Includes\n");
_output.push_back("%includePath" + std::string(LABEL_TRUNC_SIZE - strlen("%includePath"), ' ') + "\"" + getRuntimePath() + "\"\n");
_output.push_back("%include" + std::string(LABEL_TRUNC_SIZE - strlen("%include"), ' ') + "util.i\n");
_output.push_back("%include" + std::string(LABEL_TRUNC_SIZE - strlen("%include"), ' ') + "gigatron.i\n");
if(_codeRomType < Cpu::ROMv5a)
{
_output.push_back("%include" + std::string(LABEL_TRUNC_SIZE - strlen("%include"), ' ') + "macros.i\n");
}
else
{
_output.push_back("%include" + std::string(LABEL_TRUNC_SIZE - strlen("%include"), ' ') + "macros_ROMv5a.i\n");
}
_output.push_back("\n");
}
void outputCode(void)
{
std::string line;
_output.push_back("; Code\n");
for(int i=0; i<int(_codeLines.size()); i++)
{
int labelIndex = _codeLines[i]._labelIndex;
// Valid BASIC code
if(_codeLines[i]._code.size() >= 2 && _codeLines[i]._vasm.size())
{
// BASIC Label, (may not be owned by vasm line 0 as PAGE JUMPS may move labels)
std::string basicLabel = (labelIndex >= 0) ? _labels[labelIndex]._output : "";
// Vasm code
for(int j=0; j<int(_codeLines[i]._vasm.size()); j++)
{
// Skip sys init funcs that are commented out, (when not used)
if(_codeLines[i]._vasm[j]._opcode[0] == ';') continue;
uint16_t vasmAddress = _codeLines[i]._vasm[j]._address;
std::string vasmCode = _codeLines[i]._vasm[j]._code;
std::string vasmLabel = _codeLines[i]._vasm[j]._internalLabel;
// BASIC label has priority over internal label
bool useBasicLabel = (labelIndex >=0 && _labels[labelIndex]._address == vasmAddress);
std::string label = (useBasicLabel) ? basicLabel : vasmLabel + std::string(LABEL_TRUNC_SIZE - vasmLabel.size(), ' ');
if(j == 0)
{
line = (label.size()) ? label + vasmCode : std::string(LABEL_TRUNC_SIZE, ' ') + vasmCode;
}
else
{
// Assembler preprocessing commands
if(vasmCode[0] == '%')
{
line += "\n" + vasmCode;
}
// vASM labels and code
else
{
line += (label.size()) ? "\n" + label + std::string(LABEL_TRUNC_SIZE - label.size(), ' ') + vasmCode : "\n" + std::string(LABEL_TRUNC_SIZE, ' ') + vasmCode;
}
}
}
#define TAB_SPACE_LENGTH 4
#define COMMENT_PADDING (TAB_SPACE_LENGTH*24)
// Commented BASIC code, (assumes any tabs are 4 spaces)
int lineLength = Expression::tabbedStringLength(line, TAB_SPACE_LENGTH);
line.append(COMMENT_PADDING - (lineLength % COMMENT_PADDING), ' ');
//fprintf(stderr, "%d\n", lineLength + COMMENT_PADDING - (lineLength % COMMENT_PADDING));
// Line spacing for parsed code and non parsed code is different
bool dontParse = (i+1 < int(_codeLines.size())) ? _codeLines[i+1]._dontParse : false;
std::string newLine = (_codeLines[i]._dontParse && dontParse) ? "\n" : "\n\n";
line += "; " + _codeLines[i]._text + newLine;
_output.push_back(line);
}
}
_output.push_back("\n");
}
void discardUnusedLabels(void)
{
for(int k=0; k<int(_equateLabels.size()); k++)
{
bool foundLabel = false;
for(int i=0; i<int(_codeLines.size()); i++)
{
// Valid BASIC code
if(_codeLines[i]._code.size() >= 2 && _codeLines[i]._vasm.size())
{
// Vasm code
for(int j=0; j<int(_codeLines[i]._vasm.size()); j++)
{
if(_codeLines[i]._vasm[j]._code.find(_equateLabels[k]) != std::string::npos)
{
foundLabel = true;
break;
}
}
}
}
// Found a potential unused label to discard
if(!foundLabel)
{
// Can only discard internal labels
bool foundInternal = (_equateLabels[k].find("_if_") != std::string::npos || _equateLabels[k].find("_else_") != std::string::npos || _equateLabels[k].find("_elseif_") != std::string::npos ||
_equateLabels[k].find("_endif_") != std::string::npos || _equateLabels[k].find("_while_") != std::string::npos || _equateLabels[k].find("_wend_") != std::string::npos ||
_equateLabels[k].find("_repeat_") != std::string::npos || _equateLabels[k].find("_next_") != std::string::npos || _equateLabels[k].find("_page_") != std::string::npos);
for(int l=0; l<int(_output.size()); l++)
{
// Find unused internal label in output and discard it
if(foundInternal && _output[l].find(_equateLabels[k]) != std::string::npos)
{
_output.erase(_output.begin() + l);
break;
}
}
}
}
}
void clearCompiler(void)
{
_heapTotalUsage = 0;
_heapAllocations = 0;
_vasmPC = USER_CODE_START;
_userCodeStart = USER_CODE_START;
_tempVarSize = TEMP_VAR_SIZE;
_tempVarStart = TEMP_VAR_START;
_userVarStart = USER_VAR_START;
_userVarsAddr = USER_VAR_START;
_runtimeEnd = RUN_TIME_START;
_runtimeStart = RUN_TIME_START;
_arraysStart = RUN_TIME_START;
_stringsStart = RUN_TIME_START;
_regWorkArea = 0x0000;
_gprintfVarsAddr = 0x0000;
_strWorkArea[0] = 0x0000;
_strWorkArea[1] = 0x0000;
_strWorkAreaIdx = 0;
_spritesAddrLutAddress = 0x0000;
_spriteStripeChunks = 15;
_spriteStripeMinAddress = USER_CODE_START;
_codeOptimiseType = CodeSpeed;
_codeRomType = Cpu::ROMv3;
_codeIsAsm = false;
_arrayIndiciesOne = false;
_createNumericLabelLut = false;
_createTimeData = false;
_currentLabelIndex = -1;
_currentCodeLineIndex = 0;
_nonNumericLabelIndex = -1;
_jumpFalseUniqueId = 0;
_runtimePath = ".";
_tempVarStartStr = "";
_nextInternalLabel = "";
_input.clear();
_output.clear();
_runtime.clear();
_labels.clear();
_gosubLabels.clear();
_equateLabels.clear();
_internalLabels.clear();
_discardedLabels.clear();
_codeLines.clear();
_moduleLines.clear();
_constants.clear();
_integerVars.clear();
_stringVars.clear();
_typeDatas.clear();
_macroLines.clear();
_macroNameEntries.clear();
_macroIndexEntries.clear();
_defDataBytes.clear();
_defDataWords.clear();
_defDataImages.clear();
_defDataLoaderImageChunks.clear();
_defDataMidis.clear();
_defDataOpens.clear();
_defDataSprites.clear();
_defDataFonts.clear();
_defFunctions.clear();
_dataObjects.clear();
_spritesAddrLut._address = 0x0000;
_spritesAddrLut._spriteAddrs.clear();
_fontsAddrLut._address = 0x0000;
_fontsAddrLut._fontAddrs.clear();
Linker::reset();
Linker::disableFontLinking();
Memory::initialise();
Operators::initialise();
Keywords::reset();
Expression::setExprFunc(expression);
while(!_forNextDataStack.empty()) _forNextDataStack.pop();
while(!_elseIfDataStack.empty()) _elseIfDataStack.pop();
while(!_whileWendDataStack.empty()) _whileWendDataStack.pop();
while(!_repeatUntilDataStack.empty()) _repeatUntilDataStack.pop();
_callDataMap.clear();
_procDataMap.clear();
while(!_procDataStack.empty()) _procDataStack.pop();
// Allocate default string work area, (for string functions like LEFT$, MID$, etc), the +2 is for the length and delimiter bytes
Memory::getFreeRAM(Memory::FitDescending, USER_STR_SIZE + 2, USER_CODE_START, _stringsStart, _strWorkArea[0]);
Memory::getFreeRAM(Memory::FitDescending, USER_STR_SIZE + 2, USER_CODE_START, _stringsStart, _strWorkArea[1]);
}
bool compile(const std::string& inputFilename, const std::string& outputFilename)
{
Assembler::clearAssembler();
clearCompiler();
// Read .gbas file
int numLines = 0;
std::ifstream infile(inputFilename);
if(!readInputFile(infile, inputFilename, _input, numLines)) return false;
for(int i=0; i<int(_input.size()); i++) _moduleLines.push_back({i, MODULE_MAIN});
fprintf(stderr, "\n\n*******************************************************\n");
fprintf(stderr, "* Compiling file '%s'\n", inputFilename.c_str());
fprintf(stderr, "*******************************************************\n");
// Pragmas
if(!parsePragmas(_input, numLines)) return false;
// Relies on _codeRomType_, so make sure _codeRomType_ is already initialised
if(!initialiseMacros()) return false;
// Initialise
if(!initialiseCode()) return false;
// Modules
if(!parseModules(_input, numLines)) return false;
//#define DEBUG_DATA
#ifdef DEBUG_DATA
std::ofstream ofile0("input.txt", std::ios::out);
for(int i=0; i<int(_input.size()); i++)
{
ofile0 << _input[i]._parse << " : " << _input[i]._text << std::endl;
}
std::ofstream ofile1("module.txt", std::ios::out);
for(int i=0; i<int(_moduleLines.size()); i++)
{
ofile1 << _moduleLines[i]._index << " : " << _moduleLines[i]._name << std::endl;
}
#endif
// Labels
if(!parseLabels(_input, numLines)) return false;
// Includes
if(!Linker::parseIncludes()) return false;
// Code
if(!parseCode()) return false;
#ifdef DEBUG_DATA
std::ofstream ofile2("code.txt", std::ios::out);
for(int i=0; i<int(_codeLines.size()); i++)
{
ofile2 << _codeLines[i]._module << " : " << _codeLines[i]._code << std::endl;
for(int j=0; j<int(_codeLines[i]._vasm.size()); j++)
{
ofile2 << std::hex << _codeLines[i]._vasm[j]._address << " : " << _codeLines[i]._vasm[j]._opcode << " " << _codeLines[i]._vasm[j]._operand << std::endl;
}
}
#endif
// Optimise
if(!Optimiser::optimiseCode()) return false;
// Check for code relocations
if(!Validater::checkForRelocations()) return false;
// Check keywords that form statement blocks
if(!Validater::checkStatementBlocks()) return false;
// Check PROC's, FUNC's and CALL's
if(!Validater::checkCallProcFuncData()) return false;
// Only link runtime subroutines that are referenced
if(!Linker::linkInternalSubs()) return false;
#ifndef STAND_ALONE
// GBAS gprintf's need to be converted to vCPU gprintf's after all relocation/linking has finished
if(!Keywords::convertGprintGbasToGprintfAsm()) return false;
#endif
// Output
outputReservedWords();
outputInternalEquates();
outputIncludes();
outputLabels();
outputConsts();
outputVars();
outputArrs();
if(!outputStrs()) return false;
if(!outputDATA()) return false;
if(!outputTIME()) return false;
if(!outputDefs()) return false;
if(!outputLuts()) return false;
outputCode();
// Discard
discardUnusedLabels();
// Re-linking is needed here as collectInternalRuntime() can find new subs that need to be linked
Linker::collectInternalRuntime();
Linker::relinkInternalSubs();
Linker::outputInternalSubs();
if(!Validater::checkBranchLabels()) return false;
//Memory::printFreeRamList(Memory::SizeDescending);
// Write .vasm file
std::ofstream outfile(outputFilename, std::ios::binary | std::ios::out);
if(!writeOutputFile(outfile, outputFilename)) return false;
//fprintf(stderr, "\nHeap allocations %llu, total heap usage %llu\n", _heapAllocations, _heapTotalUsage);
return true;
}
}