5629 lines
252 KiB
C++
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;
|
|
}
|
|
}
|