2398 lines
103 KiB
C++
2398 lines
103 KiB
C++
#include <ctime>
|
|
#include <random>
|
|
#include <numeric>
|
|
#include <algorithm>
|
|
|
|
#include "memory.h"
|
|
#include "cpu.h"
|
|
#include "functions.h"
|
|
#include "operators.h"
|
|
|
|
|
|
namespace Functions
|
|
{
|
|
int _nestedCount = -1;
|
|
|
|
double _umin = 0.0;
|
|
double _umax = 0.0;
|
|
double _ulen = 0.0;
|
|
double _ustp = 1.0;
|
|
uint16_t _uidx = 0;
|
|
std::vector<int16_t> _uvalues;
|
|
|
|
std::mt19937_64 _randGenerator;
|
|
|
|
std::map<std::string, std::string> _functions;
|
|
std::map<std::string, std::string> _stringFunctions;
|
|
|
|
|
|
std::map<std::string, std::string>& getFunctions(void) {return _functions; }
|
|
std::map<std::string, std::string>& getStringFunctions(void) {return _stringFunctions;}
|
|
|
|
|
|
void restart(void)
|
|
{
|
|
_nestedCount = -1;
|
|
|
|
_umin = _umax = _ulen = 0.0;
|
|
_ustp = 1.0;
|
|
_uidx = 0;
|
|
_uvalues.clear();
|
|
}
|
|
|
|
bool initialise(void)
|
|
{
|
|
restart();
|
|
|
|
// Functions
|
|
_functions["IARR" ] = "IARR";
|
|
_functions["SARR" ] = "SARR";
|
|
_functions["PEEK" ] = "PEEK";
|
|
_functions["DEEK" ] = "DEEK";
|
|
_functions["USR" ] = "USR";
|
|
_functions["RND" ] = "RND";
|
|
_functions["URND" ] = "URND";
|
|
_functions["LEN" ] = "LEN";
|
|
_functions["GET" ] = "GET";
|
|
_functions["ABS" ] = "ABS";
|
|
_functions["SGN" ] = "SGN";
|
|
_functions["ASC" ] = "ASC";
|
|
_functions["STRCMP"] = "STRCMP";
|
|
_functions["BCDCMP"] = "BCDCMP";
|
|
_functions["VAL" ] = "VAL";
|
|
_functions["LUP" ] = "LUP";
|
|
_functions["ADDR" ] = "ADDR";
|
|
_functions["POINT" ] = "POINT";
|
|
_functions["MIN" ] = "MIN";
|
|
_functions["MAX" ] = "MAX";
|
|
_functions["CLAMP" ] = "CLAMP";
|
|
|
|
// String functions
|
|
_stringFunctions["CHR$" ] = "CHR$";
|
|
_stringFunctions["SPC$" ] = "SPC$";
|
|
_stringFunctions["STR$" ] = "STR$";
|
|
_stringFunctions["STRING$"] = "STRING$";
|
|
_stringFunctions["TIME$" ] = "TIME$";
|
|
_stringFunctions["HEX$" ] = "HEX$";
|
|
_stringFunctions["LEFT$" ] = "LEFT$";
|
|
_stringFunctions["RIGHT$" ] = "RIGHT$";
|
|
_stringFunctions["MID$" ] = "MID$";
|
|
_stringFunctions["LOWER$" ] = "LOWER$";
|
|
_stringFunctions["UPPER$" ] = "UPPER$";
|
|
_stringFunctions["STRCAT$"] = "STRCAT$";
|
|
|
|
uint64_t timeSeed = time(NULL);
|
|
std::seed_seq seedSequence{uint32_t(timeSeed & 0xffffffff), uint32_t(timeSeed>>32)};
|
|
_randGenerator.seed(seedSequence);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void handleConstantString(const Expression::Numeric& numeric, Compiler::ConstStrType constStrType, std::string& name, int& index)
|
|
{
|
|
switch(constStrType)
|
|
{
|
|
case Compiler::StrLeft:
|
|
case Compiler::StrRight:
|
|
{
|
|
uint8_t length = uint8_t(std::lround(numeric._params[0]._value));
|
|
Compiler::getOrCreateConstString(constStrType, numeric._text, length, 0, index);
|
|
}
|
|
break;
|
|
|
|
case Compiler::StrMid:
|
|
{
|
|
uint8_t offset = uint8_t(std::lround(numeric._params[0]._value));
|
|
uint8_t length = uint8_t(std::lround(numeric._params[1]._value));
|
|
Compiler::getOrCreateConstString(constStrType, numeric._text, length, offset, index);
|
|
}
|
|
break;
|
|
|
|
case Compiler::StrLower:
|
|
case Compiler::StrUpper:
|
|
{
|
|
Compiler::getOrCreateConstString(constStrType, numeric._text, 0, 0, index);
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
name = Compiler::getStringVars()[index]._name;
|
|
uint16_t srcAddr = Compiler::getStringVars()[index]._address;
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr), false);
|
|
Compiler::emitVcpuAsm("%PrintAcString", "", false);
|
|
}
|
|
else
|
|
{
|
|
uint16_t dstAddr = Compiler::getStringVars()[Expression::getOutputNumeric()._index]._address;
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr), false);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringCopy", "", false);
|
|
}
|
|
}
|
|
|
|
void handleStringParameter(Expression::Numeric& param)
|
|
{
|
|
// Literals
|
|
if(param._varType == Expression::Number)
|
|
{
|
|
// 8bit
|
|
if(param._value >=0 && param._value <= 255)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(int16_t(std::lround(param._value))), false);
|
|
}
|
|
// 16bit
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", std::to_string(int16_t(std::lround(param._value))), false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
Operators::handleSingleOp("LDW", param);
|
|
}
|
|
|
|
// Does a function contain nested functions as parameters
|
|
bool isFuncNested(void)
|
|
{
|
|
if(Expression::getOutputNumeric()._nestedCount == _nestedCount) return false;
|
|
|
|
bool codeInit = (_nestedCount == -1);
|
|
_nestedCount = Expression::getOutputNumeric()._nestedCount;
|
|
if(codeInit) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ********************************************************************************************
|
|
// Functions
|
|
// ********************************************************************************************
|
|
void opcodeARR(Expression::Numeric& param)
|
|
{
|
|
// Can't call Operators::handleSingleOp() here, so special case it
|
|
switch(param._varType)
|
|
{
|
|
// Temporary variable address
|
|
case Expression::TmpVar:
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(std::lround(param._value))), false);
|
|
}
|
|
break;
|
|
|
|
// User variable
|
|
case Expression::IntVar16:
|
|
{
|
|
Compiler::emitVcpuAsmUserVar("LDW", param, false);
|
|
}
|
|
break;
|
|
|
|
// Literal or constant
|
|
case Expression::Number:
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(uint8_t(std::lround(param._value))), false);
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
Expression::Numeric IARR(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::IARR() : '%s:%d' : %s cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
|
|
int intSize = Compiler::getIntegerVars()[numeric._index]._intSize;
|
|
uint16_t arrayPtr = Compiler::getIntegerVars()[numeric._index]._address;
|
|
|
|
// Literal array index, (only optimise for 1d arrays)
|
|
if(numeric._varType == Expression::Arr1Var8 && numeric._params.size() == 1 && numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
std::string operand = Expression::wordToHexString(arrayPtr + uint16_t(numeric._params[0]._value*intSize));
|
|
Compiler::emitVcpuAsm("LDWI", operand, false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
else if(numeric._varType == Expression::Arr1Var16 && numeric._params.size() == 1 && numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
std::string operand = Expression::wordToHexString(arrayPtr + uint16_t(numeric._params[0]._value*intSize));
|
|
|
|
// Handle .LO and .HI
|
|
switch(numeric._int16Byte)
|
|
{
|
|
case Expression::Int16Low: Compiler::emitVcpuAsm("LDWI", operand, false); Compiler::emitVcpuAsm("PEEK", "", false); break;
|
|
case Expression::Int16High: Compiler::emitVcpuAsm("LDWI", operand + " + 1", false); Compiler::emitVcpuAsm("PEEK", "", false); break;
|
|
case Expression::Int16Both: Compiler::emitVcpuAsm("LDWI", operand, false); Compiler::emitVcpuAsm("DEEK", "", false); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
// Variable array index or 2d/3d array
|
|
else
|
|
{
|
|
size_t numDims = 0;
|
|
if(numeric._varType >= Expression::Arr1Var8 && numeric._varType <= Expression::Arr3Var8)
|
|
{
|
|
numDims = numeric._varType - Expression::Arr1Var8 + 1;
|
|
}
|
|
else if(numeric._varType >= Expression::Arr1Var16 && numeric._varType <= Expression::Arr3Var16)
|
|
{
|
|
numDims = numeric._varType - Expression::Arr1Var16 + 1;
|
|
}
|
|
if(numDims != numeric._params.size())
|
|
{
|
|
fprintf(stderr, "Functions::IARR() : '%s:%d' : %s() expects %d dimension/s, found %d : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), int(numDims), int(numeric._params.size()), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Generate array indices
|
|
for(size_t i=0; i<numeric._params.size(); i++)
|
|
{
|
|
Expression::Numeric param = numeric._params[i];
|
|
opcodeARR(param);
|
|
Compiler::emitVcpuAsm("STW", "memIndex" + std::to_string(i), false);
|
|
}
|
|
|
|
// Handle 1d/2d/3d arrays
|
|
switch(numeric._varType)
|
|
{
|
|
case Expression::Arr1Var8:
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
Compiler::emitVcpuAsm("ADDW", "memIndex0", false);
|
|
}
|
|
break;
|
|
|
|
case Expression::Arr2Var8:
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
(Compiler::getCodeRomType() >= Cpu::ROMv5a) ? Compiler::emitVcpuAsm("CALLI", "convert8Arr2d", false) : Compiler::emitVcpuAsm("CALL", "convert8Arr2dAddr", false);
|
|
}
|
|
break;
|
|
|
|
case Expression::Arr3Var8:
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
(Compiler::getCodeRomType() >= Cpu::ROMv5a) ? Compiler::emitVcpuAsm("CALLI", "convert8Arr3d", false) : Compiler::emitVcpuAsm("CALL", "convert8Arr3dAddr", false);
|
|
}
|
|
break;
|
|
|
|
case Expression::Arr1Var16:
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
Compiler::emitVcpuAsm("ADDW", "memIndex0", false);
|
|
Compiler::emitVcpuAsm("ADDW", "memIndex0", false);
|
|
}
|
|
break;
|
|
|
|
case Expression::Arr2Var16:
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
(Compiler::getCodeRomType() >= Cpu::ROMv5a) ? Compiler::emitVcpuAsm("CALLI", "convert16Arr2d", false) : Compiler::emitVcpuAsm("CALL", "convert16Arr2dAddr", false);
|
|
}
|
|
break;
|
|
|
|
case Expression::Arr3Var16:
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
(Compiler::getCodeRomType() >= Cpu::ROMv5a) ? Compiler::emitVcpuAsm("CALLI", "convert16Arr3d", false) : Compiler::emitVcpuAsm("CALL", "convert16Arr3dAddr", false);
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// Functions like LEN() and ADDR() require IARR() to return the address rather than the value
|
|
if(!numeric._returnAddress)
|
|
{
|
|
// Bytes
|
|
if(numeric._varType >= Expression::Arr1Var8 && numeric._varType <= Expression::Arr3Var8)
|
|
{
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
// Words, handle .LO and .HI
|
|
else
|
|
{
|
|
switch(numeric._int16Byte)
|
|
{
|
|
case Expression::Int16Low: Compiler::emitVcpuAsm("PEEK", "", false); break;
|
|
case Expression::Int16High: Compiler::emitVcpuAsm("ADDI", "1", false); Compiler::emitVcpuAsm("PEEK", "", false); break;
|
|
case Expression::Int16Both: Compiler::emitVcpuAsm("DEEK", "", false); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric SARR(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::SARR() : '%s:%d' : %s cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::SARR() : '%s:%d' : %s() expects 1 dimension, found %d : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), int(numeric._params.size()), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// String addresses cannot be combined using boolean expressions, so no need to increment temporary var
|
|
//Compiler::getNextTempVar();
|
|
|
|
uint16_t arrayPtr = Compiler::getStringVars()[numeric._index]._address;
|
|
|
|
// Literal array index
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
std::string operand = Expression::wordToHexString(arrayPtr + uint16_t(numeric._params[0]._value)*2);
|
|
Compiler::emitVcpuAsm("LDWI", operand, false);
|
|
Compiler::emitVcpuAsm("DEEK", "", false);
|
|
}
|
|
// Variable array index
|
|
else
|
|
{
|
|
Expression::Numeric param = numeric._params[0];
|
|
opcodeARR(param);
|
|
Compiler::emitVcpuAsm("STW", "memIndex0", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(arrayPtr), false);
|
|
Compiler::emitVcpuAsm("ADDW", "memIndex0", false);
|
|
Compiler::emitVcpuAsm("ADDW", "memIndex0", false);
|
|
Compiler::emitVcpuAsm("DEEK", "", false);
|
|
}
|
|
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
numeric._varType = Expression::Str2Var;
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric PEEK(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::PEEK() : '%s:%d' : PEEK() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
// Optimise for page 0
|
|
if(numeric._value >= 0 && numeric._value <= 255)
|
|
{
|
|
Compiler::emitVcpuAsm("LD", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
Operators::changeToTmpVar(numeric);
|
|
return numeric;
|
|
}
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
}
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric DEEK(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::PEEK() : '%s:%d' : PEEK() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
// Optimise for page 0
|
|
if(numeric._value >= 0 && numeric._value <= 255)
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
Operators::changeToTmpVar(numeric);
|
|
return numeric;
|
|
}
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
}
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("DEEK", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric USR(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::USR() : '%s:%d' : USR() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
|
|
{
|
|
(numeric._value >= 0 && numeric._value <= 255) ? Compiler::emitVcpuAsm("CALLI", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false) :
|
|
Compiler::emitVcpuAsm("CALLI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
}
|
|
else
|
|
{
|
|
(numeric._value >= 0 && numeric._value <= 255) ? Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false) :
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
}
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
|
|
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
|
|
{
|
|
Operators::handleSingleOp("CALLI", numeric);
|
|
}
|
|
else
|
|
{
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
|
|
}
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric RND(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
UNREFERENCED_PARAM(moduleName);
|
|
UNREFERENCED_PARAM(codeLineText);
|
|
UNREFERENCED_PARAM(codeLineStart);
|
|
|
|
bool useMod = true;
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
// No code needed for static initialisation
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
if(numeric._value == 0)
|
|
{
|
|
std::uniform_int_distribution<uint16_t> distribution(0, 0xFFFF);
|
|
numeric._value = distribution(_randGenerator);
|
|
}
|
|
else
|
|
{
|
|
std::uniform_int_distribution<uint16_t> distribution(0, uint16_t(numeric._value));
|
|
numeric._value = distribution(_randGenerator);
|
|
}
|
|
|
|
return numeric;
|
|
}
|
|
|
|
// RND(0) skips the MOD call and allows you to filter the output manually
|
|
if(numeric._value == 0)
|
|
{
|
|
useMod = false;
|
|
}
|
|
else
|
|
{
|
|
(numeric._value > 0 && numeric._value <= 255) ? Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false) :
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
}
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
if(useMod)
|
|
{
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("%RandMod", "", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("%Rand", "", false);
|
|
}
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric URND(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
UNREFERENCED_PARAM(codeLineStart);
|
|
|
|
if(!Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : URND only works in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 3)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : URND expects 4 parameters, found %d : %s\n", moduleName.c_str(), codeLineStart, int(numeric._params.size()), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._varType != Expression::Number || numeric._params[0]._varType != Expression::Number || numeric._params[1]._varType != Expression::Number || numeric._params[2]._varType != Expression::Number)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : URND expects 4 literal parameters, 'URND(<min>, <max>, <len>, <step>) : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Initialise unique random number generator
|
|
if(numeric._value != _umin || numeric._params[0]._value != _umax || numeric._params[1]._value != _ulen || numeric._params[2]._value != _ustp)
|
|
{
|
|
_umin = _umax = _ulen = 0.0;
|
|
_ustp = 1.0;
|
|
|
|
if(abs(numeric._params[0]._value - numeric._value) < numeric._params[1]._value)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : range is smaller than length : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params[0]._value <= numeric._value)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : maximum must be greater than minimum : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params[1]._value <= 0.0 || std::lround(numeric._params[1]._value) > 0xFFFF)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : 0x0000 < length < 0x10000 : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params[2]._value == 0.0)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : step must not be equal to zero : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
_umin = numeric._value;
|
|
_umax = numeric._params[0]._value;
|
|
_ulen = numeric._params[1]._value;
|
|
_ustp = numeric._params[2]._value;
|
|
_uidx = 0;
|
|
|
|
uint16_t range = uint16_t((abs(std::lround(_umax) - std::lround(_umin))) / std::lround(abs(_ustp))) + 1;
|
|
if(range == 0)
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : step size is too large for range : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
_uvalues.resize(range);
|
|
|
|
for(int i=0; i<range; i++) _uvalues[i] = int16_t(std::lround(_umin)) + int16_t(i*abs(_ustp));
|
|
//std::iota(_uvalues.begin(), _uvalues.end(), int16_t(std::lround(_umin)));
|
|
std::shuffle(_uvalues.begin(), _uvalues.end(), _randGenerator);
|
|
}
|
|
|
|
if(_uidx >= uint16_t(_uvalues.size()))
|
|
{
|
|
fprintf(stderr, "Functions::URND() : '%s:%d' : length is greater than range : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
numeric._value = _uvalues[_uidx++];
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric LEN(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
fprintf(stderr, "Functions::LEN() : '%s:%d' : parameter can't be a literal : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 0)
|
|
{
|
|
fprintf(stderr, "Functions::LEN() : '%s:%d' : LEN expects 1 parameter, found %d : %s\n", moduleName.c_str(), codeLineStart, int(numeric._params.size()), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Handle non variables
|
|
if(numeric._index == -1)
|
|
{
|
|
switch(numeric._varType)
|
|
{
|
|
// Get or create constant string
|
|
case Expression::String:
|
|
{
|
|
int index;
|
|
Compiler::getOrCreateConstString(numeric._text, index);
|
|
numeric._index = int16_t(index);
|
|
numeric._varType = Expression::StrVar;
|
|
}
|
|
break;
|
|
|
|
// Needs to pass through
|
|
case Expression::TmpStrVar:
|
|
{
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
fprintf(stderr, "Functions::LEN() : '%s:%d' : couldn't find variable name '%s' : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
}
|
|
}
|
|
|
|
int length = 0;
|
|
switch(numeric._varType)
|
|
{
|
|
case Expression::IntVar16:
|
|
case Expression::Arr1Var8:
|
|
case Expression::Arr2Var8:
|
|
case Expression::Arr3Var8:
|
|
case Expression::Arr1Var16:
|
|
case Expression::Arr2Var16:
|
|
case Expression::TmpVar:
|
|
case Expression::Arr3Var16: length = Compiler::getIntegerVars()[numeric._index]._intSize; break;
|
|
case Expression::Constant: length = Compiler::getConstants()[numeric._index]._size; break;
|
|
case Expression::StrVar: length = Compiler::getStringVars()[numeric._index]._size; break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// No code needed for static initialisation
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
numeric._value = length;
|
|
return numeric;
|
|
}
|
|
|
|
// String vars
|
|
if(numeric._varType == Expression::StrVar && !Compiler::getStringVars()[numeric._index]._constant)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStringVars()[numeric._index]._address), false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
// String arrays
|
|
else if(numeric._varType == Expression::Str2Var)
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
// Temp string vars
|
|
else if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStrWorkArea()), false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
// Ints, int arrays and constants
|
|
else
|
|
{
|
|
// Generate code to save result into a tmp var
|
|
(length <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(length), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(length), false);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric GET(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::GET() : '%s:%d' : GET() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::String)
|
|
{
|
|
std::string sysVarName = numeric._text;
|
|
Expression::strToUpper(sysVarName);
|
|
if(sysVarName == "ROM_READ_DIR" && numeric._params.size() == 1)
|
|
{
|
|
// Literal constant
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._params[0]._value))), false);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric._params[0]);
|
|
Compiler::emitVcpuAsm("%RomRead", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params[0]._params.clear();
|
|
return numeric._params[0];
|
|
}
|
|
else if(sysVarName == "SPRITE_LUT" && numeric._params.size() == 1)
|
|
{
|
|
// Literal constant
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._params[0]._value))), false);
|
|
}
|
|
|
|
// Look up sprite lut from sprites lut using a sprite index, (handleSingleOp LDW is skipped if above was a constant literal)
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "spriteId", false);
|
|
Compiler::emitVcpuAsm("%GetSpriteLUT", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params[0]._params.clear();
|
|
return numeric._params[0];
|
|
}
|
|
else if(sysVarName == "MIDI_NOTE" && numeric._params.size() == 1)
|
|
{
|
|
// Literal constant
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._params[0]._value))), false);
|
|
}
|
|
|
|
// Look up a ROM note using a midi index, (handleSingleOp LDW is skipped if above was a constant literal)
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "musicNote", false);
|
|
Compiler::emitVcpuAsm("%GetMidiNote", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params[0]._params.clear();
|
|
return numeric._params[0];
|
|
}
|
|
else if(sysVarName == "MUSIC_NOTE" && numeric._params.size() == 1)
|
|
{
|
|
// Literal constant
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._params[0]._value))), false);
|
|
}
|
|
|
|
// Look up a ROM note using a note index, (handleSingleOp LDW is skipped if above was a constant literal)
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "musicNote", false);
|
|
Compiler::emitVcpuAsm("%GetMusicNote", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params[0]._params.clear();
|
|
return numeric._params[0];
|
|
}
|
|
else if(numeric._params.size() == 0)
|
|
{
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
|
|
if(sysVarName == "TIME_MODE")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "handleT_mode + 1", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
if(sysVarName == "TIME_EPOCH")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "handleT_epoch + 1", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
else if(sysVarName == "TIME_S")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "_timeArray_ + 0", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
else if(sysVarName == "TIME_M")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "_timeArray_ + 1", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
else if(sysVarName == "TIME_H")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "_timeArray_ + 2", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
else if(sysVarName == "TIMER")
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", "timerTick", false);
|
|
}
|
|
else if(sysVarName == "TIMER_PREV")
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", "timerPrev", false);
|
|
}
|
|
else if(sysVarName == "VBLANK_PROC")
|
|
{
|
|
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
|
|
{
|
|
std::string romTypeStr;
|
|
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
|
|
fprintf(stderr, "Functions::GET() : '%s:%d' : version error, 'SET VBLANK_PROC' requires ROMv5a or higher, you are trying to link against '%s' : %s\n", moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(VBLANK_PROC), false);
|
|
Compiler::emitVcpuAsm("DEEK", "", false);
|
|
}
|
|
}
|
|
else if(sysVarName == "VBLANK_FREQ")
|
|
{
|
|
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
|
|
{
|
|
std::string romTypeStr;
|
|
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
|
|
fprintf(stderr, "Functions::GET() : '%s:%d' : version error, 'SET VBLANK_FREQ' requires ROMv5a or higher, you are trying to link against '%s' : %s\n", moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
// (256 - n) = vblank interrupt frequency, where n = 1 to 255
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "realTS_rti + 2", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
Compiler::emitVcpuAsm("STW", "register0", false);
|
|
Compiler::emitVcpuAsm("LDWI", "256", false);
|
|
Compiler::emitVcpuAsm("SUBW", "register0", false);
|
|
}
|
|
}
|
|
else if(sysVarName == "CURSOR_X")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "cursorXY", false);
|
|
}
|
|
else if(sysVarName == "CURSOR_Y")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "cursorXY + 1", false);
|
|
}
|
|
else if(sysVarName == "CURSOR_XY")
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", "cursorXY", false);
|
|
}
|
|
else if(sysVarName == "FG_COLOUR")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "fgbgColour + 1", false);
|
|
}
|
|
else if(sysVarName == "BG_COLOUR")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "fgbgColour", false);
|
|
}
|
|
else if(sysVarName == "FGBG_COLOUR")
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", "fgbgColour", false);
|
|
}
|
|
else if(sysVarName == "MIDI_STREAM")
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", "midiStream", false);
|
|
}
|
|
else if(sysVarName == "LED_TEMPO")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_ledTempo", false);
|
|
}
|
|
else if(sysVarName == "LED_STATE")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_ledState", false);
|
|
}
|
|
else if(sysVarName == "SOUND_TIMER")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_soundTimer", false);
|
|
}
|
|
else if(sysVarName == "CHANNEL_MASK")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_channelMask", false);
|
|
Compiler::emitVcpuAsm("ANDI", "0x03", false);
|
|
}
|
|
else if(sysVarName == "ROM_TYPE")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_romType", false);
|
|
Compiler::emitVcpuAsm("ANDI", "0xFC", false);
|
|
}
|
|
else if(sysVarName == "VSP")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_vSP", false);
|
|
}
|
|
else if(sysVarName == "VLR")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_vLR", false);
|
|
}
|
|
else if(sysVarName == "VAC")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_vAC", false);
|
|
}
|
|
else if(sysVarName == "VPC")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_vPC", false);
|
|
}
|
|
else if(sysVarName == "XOUT_MASK")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_xoutMask", false);
|
|
}
|
|
else if(sysVarName == "BUTTON_STATE")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_buttonState", false);
|
|
}
|
|
else if(sysVarName == "SERIAL_RAW")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_serialRaw", false);
|
|
}
|
|
else if(sysVarName == "FRAME_COUNT")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_frameCount", false);
|
|
}
|
|
else if(sysVarName == "VIDEO_Y")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_videoY", false);
|
|
}
|
|
else if(sysVarName == "RAND2")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_rand2", false);
|
|
}
|
|
else if(sysVarName == "RAND1")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_rand1", false);
|
|
}
|
|
else if(sysVarName == "RAND0")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_rand0", false);
|
|
}
|
|
else if(sysVarName == "MEM_SIZE")
|
|
{
|
|
Compiler::emitVcpuAsm("LD", "giga_memSize", false);
|
|
}
|
|
else if(sysVarName == "Y_RES")
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", "giga_yres", false);
|
|
}
|
|
else if(sysVarName == "X_RES")
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", "giga_xres", false);
|
|
}
|
|
else if(sysVarName == "SND_CHN4")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_soundChan4", false);
|
|
}
|
|
else if(sysVarName == "SND_CHN3")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_soundChan3", false);
|
|
}
|
|
else if(sysVarName == "SND_CHN2")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_soundChan2", false);
|
|
}
|
|
else if(sysVarName == "SND_CHN1")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_soundChan1", false);
|
|
}
|
|
else if(sysVarName == "V_TOP")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_videoTop", false);
|
|
}
|
|
else if(sysVarName == "V_TABLE")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_videoTable", false);
|
|
}
|
|
else if(sysVarName == "V_RAM")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_vram", false);
|
|
}
|
|
else if(sysVarName == "ROM_NOTES")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_notesTable", false);
|
|
}
|
|
else if(sysVarName == "ROM_TEXT82")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_text82", false);
|
|
}
|
|
else if(sysVarName == "ROM_TEXT32")
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", "giga_text32", false);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Functions::GET() : '%s:%d' : system variable name '%s' does not exist : %s\n", moduleName.c_str(), codeLineStart, numeric._text.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
}
|
|
}
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric ABS(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::ABS() : '%s:%d' : ABS() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType != Expression::String && numeric._varType != Expression::StrVar && numeric._varType != Expression::TmpStrVar)
|
|
{
|
|
Compiler::getNextTempVar();
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
numeric._value = abs(numeric._value);
|
|
(numeric._value >= 0 && numeric._value <= 255) ? Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false) :
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("%Absolute", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
}
|
|
}
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric SGN(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::SGN() : '%s:%d' : SGN() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType != Expression::String && numeric._varType != Expression::StrVar && numeric._varType != Expression::TmpStrVar)
|
|
{
|
|
Compiler::getNextTempVar();
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
numeric._value = Expression::sgn(numeric._value);
|
|
(numeric._value >= 0 && numeric._value <= 255) ? Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false) :
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("%Sign", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
}
|
|
}
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric ASC(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
fprintf(stderr, "Functions::ASC() : '%s:%d' : parameter can't be a literal : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
|
|
// Handle non variables
|
|
if(numeric._index == -1)
|
|
{
|
|
switch(numeric._varType)
|
|
{
|
|
// Get or create constant string
|
|
case Expression::String:
|
|
{
|
|
int index;
|
|
Compiler::getOrCreateConstString(numeric._text, index);
|
|
numeric._index = int16_t(index);
|
|
numeric._varType = Expression::StrVar;
|
|
}
|
|
break;
|
|
|
|
case Expression::TmpStrVar:
|
|
{
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
fprintf(stderr, "Functions::ASC() : '%s:%d' : couldn't find variable name '%s' : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t ascii = 0;
|
|
switch(numeric._varType)
|
|
{
|
|
case Expression::StrVar: ascii = Compiler::getStringVars()[numeric._index]._text[0]; break;
|
|
case Expression::Constant: ascii = Compiler::getConstants()[numeric._index]._text[0]; break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// No code needed for static initialisation
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
numeric._value = ascii;
|
|
return numeric;
|
|
}
|
|
|
|
// Variables
|
|
if(numeric._varType == Expression::StrVar && !Compiler::getStringVars()[numeric._index]._constant)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStringVars()[numeric._index]._address) + " + 1", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
else if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStrWorkArea()) + " + 1", false);
|
|
Compiler::emitVcpuAsm("PEEK", "", false);
|
|
}
|
|
// Constants
|
|
else
|
|
{
|
|
// Generate code to save result into a tmp var
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(ascii), false);
|
|
}
|
|
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric STRCMP(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::STRCMP() : '%s:%d' : STRCMP() requires two string parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal strings, (optimised case)
|
|
if(numeric._varType == Expression::String && numeric._params[0]._varType == Expression::String)
|
|
{
|
|
// No code needed for static initialisation
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
numeric._varType = Expression::Number;
|
|
numeric._value = uint8_t(numeric._text == numeric._params[0]._text) - 1;
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
// Generate code to save result into a tmp var
|
|
else
|
|
{
|
|
int result = int(numeric._text == numeric._params[0]._text) - 1;
|
|
(result >= 0 && result <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(result), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(result), false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get addresses of strings to be compared
|
|
uint16_t srcAddr0 = 0x0000, srcAddr1 = 0x0000;
|
|
if(numeric._varType == Expression::TmpStrVar) srcAddr0 = Compiler::getStrWorkArea(0);
|
|
if(numeric._params[0]._varType == Expression::TmpStrVar) srcAddr1 = Compiler::getStrWorkArea(0);
|
|
if(numeric._varType == Expression::TmpStrVar && numeric._params[0]._varType == Expression::TmpStrVar)
|
|
{
|
|
// If both params are temp strs then swap them so they compare correctly
|
|
srcAddr1 = Compiler::getStrWorkArea(1);
|
|
std::swap(srcAddr0, srcAddr1);
|
|
}
|
|
|
|
if(!srcAddr0)
|
|
{
|
|
std::string name;
|
|
int index = int(numeric._index);
|
|
Compiler::getOrCreateString(numeric, name, srcAddr0, index);
|
|
}
|
|
if(!srcAddr1)
|
|
{
|
|
std::string name;
|
|
int index = int(numeric._params[0]._index);
|
|
Compiler::getOrCreateString(numeric._params[0], name, srcAddr1, index);
|
|
}
|
|
|
|
// By definition this must be a match
|
|
if(srcAddr0 == srcAddr1)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", "1", false);
|
|
}
|
|
// Compare strings, -1, 0, 1, (smaller, equal, bigger)
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr0), false);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr1), false);
|
|
Compiler::emitVcpuAsm("%StringCmp", "", false);
|
|
Compiler::emitVcpuAsm("SUBI", "1", false); // convert 0, 1, 2 to -1, 0, 1
|
|
}
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric BCDCMP(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::BCDCMP() : '%s:%d' : BCDCMP() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 2)
|
|
{
|
|
fprintf(stderr, "Functions::BCDCMP() : '%s:%d' : BCDCMP() requires three string parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Get addresses and length of bcd values to be compared
|
|
uint16_t srcAddr0 = uint16_t(numeric._value);
|
|
uint16_t srcAddr1 = uint16_t(numeric._params[0]._value);
|
|
uint16_t length = uint16_t(numeric._params[1]._value);
|
|
|
|
// Compare bcd values, (addresses MUST point to msd)
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr0), false);
|
|
Compiler::emitVcpuAsm("STW", "bcdSrcAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr1), false);
|
|
Compiler::emitVcpuAsm("STW", "bcdDstAddr", false);
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(length), false);
|
|
Compiler::emitVcpuAsm("%BcdCmp", "", false);
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric VAL(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._params.size() != 0)
|
|
{
|
|
fprintf(stderr, "Functions::VAL() : '%s:%d' : VAL() requires only one string parameter : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal strings, (optimised case)
|
|
if(numeric._varType == Expression::String)
|
|
{
|
|
int16_t val = 0;
|
|
Expression::stringToI16(numeric._text, val);
|
|
|
|
// No code needed for static initialisation
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
numeric._varType = Expression::Number;
|
|
numeric._value = val;
|
|
return numeric;
|
|
}
|
|
// Generate code to save result into a tmp var
|
|
else
|
|
{
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get addresses of src string
|
|
std::string name;
|
|
uint16_t srcAddr;
|
|
int index = int(numeric._index);
|
|
Compiler::getOrCreateString(numeric, name, srcAddr, index);
|
|
|
|
// StringVal expects srcAddr to point past the string's length byte
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(srcAddr + 1), false);
|
|
Compiler::emitVcpuAsm("%IntegerStr", "", false);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric LUP(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::LUP() : '%s:%d' : LUP(<address>, <offset>) cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::LUP() : '%s:%d' : LUP(<address>, <offset>) missing offset : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._params[0]._varType != Expression::Number)
|
|
{
|
|
fprintf(stderr, "Functions::LUP() : '%s:%d' : LUP(<address>, <offset>) offset is not a constant literal : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
std::string offset = Expression::byteToHexString(uint8_t(std::lround(numeric._params[0]._value)));
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(uint16_t(std::lround(numeric._value))), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("LUP", offset, false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric ADDR(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
fprintf(stderr, "Functions::ADDR() : '%s:%d' : parameter can't be a literal : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 0)
|
|
{
|
|
fprintf(stderr, "Functions::ADDR() : '%s:%d' : expects 1 parameter, found %d : %s\n", moduleName.c_str(), codeLineStart, int(numeric._params.size()), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Handle non variables
|
|
if(numeric._index == -1)
|
|
{
|
|
switch(numeric._varType)
|
|
{
|
|
// Get or create constant string
|
|
case Expression::String:
|
|
{
|
|
int index;
|
|
Compiler::getOrCreateConstString(numeric._text, index);
|
|
numeric._index = int16_t(index);
|
|
numeric._varType = Expression::StrVar;
|
|
}
|
|
break;
|
|
|
|
// Needs to pass through
|
|
case Expression::TmpStrVar:
|
|
{
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
fprintf(stderr, "Functions::ADDR() : '%s:%d' : couldn't find variable name '%s' : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint16_t address = 0x0000;
|
|
switch(numeric._varType)
|
|
{
|
|
case Expression::IntVar16:
|
|
case Expression::Arr1Var8:
|
|
case Expression::Arr1Var16: address = Compiler::getIntegerVars()[numeric._index]._address; break;
|
|
case Expression::Constant: address = Compiler::getConstants()[numeric._index]._address; break;
|
|
case Expression::StrVar: address = Compiler::getStringVars()[numeric._index]._address; break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// No code needed for static initialisation
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
switch(numeric._varType)
|
|
{
|
|
case Expression::TmpVar:
|
|
case Expression::Str2Var:
|
|
{
|
|
fprintf(stderr, "Functions::ADDR() : '%s:%d' : can't statically initialise from multi-dimensional array '%s' : %s\n", moduleName.c_str(), codeLineStart, numeric._name.c_str(), codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
default: break;
|
|
}
|
|
|
|
numeric._value = address;
|
|
return numeric;
|
|
}
|
|
|
|
// String vars
|
|
if(numeric._varType == Expression::StrVar && !Compiler::getStringVars()[numeric._index]._constant)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStringVars()[numeric._index]._address), false);
|
|
}
|
|
// Multi-dimensional arrays, (array of strings, Str2Var, is treated as a 2D array of bytes)
|
|
else if(numeric._varType == Expression::TmpVar || numeric._varType == Expression::Str2Var)
|
|
{
|
|
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
}
|
|
// Temp string vars
|
|
else if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStrWorkArea()), false);
|
|
}
|
|
// Ints, int arrays and constants
|
|
else
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(address), false);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric POINT(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::POINT() : '%s:%d' : POINT() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::POINT() : '%s:%d' : syntax error, 'POINT(x, y)' requires two parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("ST", "readPixel_xy", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("ST", "readPixel_xy", false);
|
|
}
|
|
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(std::lround(numeric._params[0]._value))), false);
|
|
Compiler::emitVcpuAsm("ST", "readPixel_xy + 1", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric._params[0]);
|
|
Compiler::emitVcpuAsm("ST", "readPixel_xy + 1", false);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("%ReadPixel", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric MIN(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::MIN() : '%s:%d' : syntax error, 'MIN(x, y)' requires two parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._varType == Expression::Number && numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
numeric._value = std::min(numeric._value, numeric._params[0]._value);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
Compiler::emitVcpuAsm("STW", "intSrcA", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("STW", "intSrcA", false);
|
|
}
|
|
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._params[0]._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric._params[0]);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("%IntMin", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric MAX(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::MAX() : '%s:%d' : syntax error, 'MAX(x, y)' requires two parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._varType == Expression::Number && numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
numeric._value = std::max(numeric._value, numeric._params[0]._value);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
Compiler::emitVcpuAsm("STW", "intSrcA", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("STW", "intSrcA", false);
|
|
}
|
|
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._params[0]._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric._params[0]);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("%IntMax", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric CLAMP(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(numeric._params.size() != 2)
|
|
{
|
|
fprintf(stderr, "Functions::CLAMP() : '%s:%d' : syntax error, 'CLAMP(x, a, b)' requires three parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._varType == Expression::Number && numeric._params[0]._varType == Expression::Number && numeric._params[1]._varType == Expression::Number)
|
|
{
|
|
numeric._value = std::min(std::max(numeric._value, numeric._params[0]._value), numeric._params[1]._value);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
Compiler::emitVcpuAsm("STW", "intSrcX", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric);
|
|
Compiler::emitVcpuAsm("STW", "intSrcX", false);
|
|
}
|
|
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._params[0]._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
Compiler::emitVcpuAsm("STW", "intSrcA", false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "intSrcA", false);
|
|
}
|
|
|
|
if(numeric._params[1]._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._params[1]._value));
|
|
(val >= 0 && val <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(val), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(val), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric._params[1]);
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("%IntClamp", "", false);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric CHR$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::CHR$() : '%s:%d' : CHR$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
// Print CHR string, (without wasting memory)
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(int16_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("%PrintAcChr", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create CHR string
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(int16_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("STW", "strChr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringChr", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("%PrintAcChr", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create CHR string
|
|
Compiler::emitVcpuAsm("STW", "strChr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringChr", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Expression::Numeric SPC$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::SPC$() : '%s:%d' : SPC$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
uint8_t len = uint8_t(std::lround(numeric._value));
|
|
if(len < 1 || len > 94)
|
|
{
|
|
fprintf(stderr, "Functions::SPC$() : '%s:%d' : syntax error, 'SPC$(n)', if 'n' is a literal, it MUST be <1-94> : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(len), false);
|
|
Compiler::emitVcpuAsm("%PrintSpc", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create SPC string
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(len), false);
|
|
Compiler::emitVcpuAsm("STW", "strLen", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringSpc", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("%PrintSpc", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create SPC string
|
|
Compiler::emitVcpuAsm("STW", "strLen", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringSpc", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Expression::Numeric STR$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::STR$() : '%s:%d' : STR$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
// Print STR string, (without wasting memory)
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create STR string
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("STW", "strInteger", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringInt", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create STR string
|
|
Compiler::emitVcpuAsm("STW", "strInteger", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringInt", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Expression::Numeric STRING$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::STRING$() : '%s:%d' : STRING$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
// Print STR string, (without wasting memory)
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(uint16_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("%PrintAcString", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Point to STR address
|
|
return Expression::Numeric(numeric._value, uint16_t(-1), true, false, false, Expression::StrAddr, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("%PrintAcString", "", false);
|
|
return numeric;
|
|
}
|
|
|
|
Operators::changeToTmpVar(numeric);
|
|
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
|
|
numeric._varType = Expression::TmpStrAddr;
|
|
|
|
return numeric;
|
|
}
|
|
|
|
Expression::Numeric TIME$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::TIME$() : '%s:%d' : TIME$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Function with no parameters, so isValid needs to be explicitly set
|
|
numeric._isValid = true;
|
|
|
|
// Generate new time string
|
|
Compiler::emitVcpuAsm("%TimeString", "", false);
|
|
|
|
// Print it directly if able
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("%PrintString", "_timeString_", false);
|
|
return numeric;
|
|
}
|
|
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
Compiler::emitVcpuAsm("LDWI", "_timeString_", false);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringCopy", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Expression::Numeric HEX$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::HEX$() : '%s:%d' : HEX$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::HEX$() : '%s:%d' : syntax error, 'HEX$(x, n)' requires two parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
if(numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
int16_t val = int16_t(std::lround(numeric._params[0]._value));
|
|
if(val < 1 || val > 4)
|
|
{
|
|
fprintf(stderr, "Functions::HEX$() : '%s:%d' : syntax error, 'HEX$(x, n)', if 'n' is a literal, it MUST be <1-4> : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
Compiler::emitVcpuAsm("LDI", std::to_string(val), false);
|
|
}
|
|
else
|
|
{
|
|
Operators::createSingleOp("LDW", numeric._params[0]);
|
|
}
|
|
Compiler::emitVcpuAsm("ST", "textLen", false);
|
|
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
if(numeric._varType == Expression::Number)
|
|
{
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(uint16_t(std::lround(numeric._value))), false);
|
|
Compiler::emitVcpuAsm("STW", "textHex", false);
|
|
|
|
// Print HEX string, (without wasting memory)
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("%PrintHex", "", false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
// Create HEX string
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringHex", "", false);
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Compiler::getNextTempVar();
|
|
Operators::handleSingleOp("LDW", numeric);
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitVcpuAsm("STW", "textHex", false);
|
|
Compiler::emitVcpuAsm("%PrintHex", "", false);
|
|
numeric._params.clear();
|
|
return numeric;
|
|
}
|
|
|
|
// Create HEX string
|
|
Compiler::emitVcpuAsm("STW", "strHex", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringHex", "", false);
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, std::string(""), std::string(""));
|
|
}
|
|
|
|
Expression::Numeric LEFT$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::LEFT$() : '%s:%d' : LEFT$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::LEFT$() : '%s:%d' : syntax error, 'LEFT$(s$, n)' requires two parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal string and parameter, (optimised case)
|
|
if(numeric._varType == Expression::String && numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
int index;
|
|
std::string name;
|
|
handleConstantString(numeric, Compiler::StrLeft, name, index);
|
|
return Expression::Numeric(0, uint16_t(index), true, false, false, Expression::StrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
// Non optimised case
|
|
std::string name;
|
|
uint16_t srcAddr;
|
|
int index = int(numeric._index);
|
|
|
|
// String input can be literal, const, var and temp
|
|
if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
// Second parameter can never be a temp string
|
|
srcAddr = Compiler::getStrWorkArea();
|
|
}
|
|
else
|
|
{
|
|
Compiler::getOrCreateString(numeric, name, srcAddr, index);
|
|
}
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "textStr", false);
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "textLen", false);
|
|
Compiler::emitVcpuAsm("%PrintAcLeft", "", false);
|
|
}
|
|
else
|
|
{
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
// Optimise STW/LDW
|
|
if(numeric._params[0]._varType == Expression::TmpVar)
|
|
{
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "strDstLen", false);
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
}
|
|
else
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "strDstLen", false);
|
|
}
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringLeft", "", false);
|
|
}
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
Expression::Numeric RIGHT$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::RIGHT$() : '%s:%d' : RIGHT$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 1)
|
|
{
|
|
fprintf(stderr, "Functions::RIGHT$() : '%s:%d' : syntax error, 'RIGHT$(s$, n)' requires two parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal string and parameter, (optimised case)
|
|
if(numeric._varType == Expression::String && numeric._params[0]._varType == Expression::Number)
|
|
{
|
|
int index;
|
|
std::string name;
|
|
handleConstantString(numeric, Compiler::StrRight, name, index);
|
|
return Expression::Numeric(0, uint16_t(index), true, false, false, Expression::StrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
// Non optimised case
|
|
std::string name;
|
|
uint16_t srcAddr;
|
|
int index = int(numeric._index);
|
|
|
|
// String input can be literal, const, var and temp
|
|
if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
srcAddr = Compiler::getStrWorkArea();
|
|
}
|
|
else
|
|
{
|
|
Compiler::getOrCreateString(numeric, name, srcAddr, index);
|
|
}
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "textStr", false);
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "textLen", false);
|
|
Compiler::emitVcpuAsm("%PrintAcRight", "", false);
|
|
}
|
|
else
|
|
{
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
// Optimise STW/LDW
|
|
if(numeric._params[0]._varType == Expression::TmpVar)
|
|
{
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "strDstLen", false);
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
}
|
|
else
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "strDstLen", false);
|
|
}
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringRight", "", false);
|
|
}
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
Expression::Numeric MID$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::MID$() : '%s:%d' : MID$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 2)
|
|
{
|
|
fprintf(stderr, "Functions::MID$() : '%s:%d' : syntax error, 'MID$(s$, i, n)' requires three parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal string and parameters, (optimised case)
|
|
if(numeric._varType == Expression::String && numeric._params[0]._varType == Expression::Number && numeric._params[1]._varType == Expression::Number)
|
|
{
|
|
int index;
|
|
std::string name;
|
|
handleConstantString(numeric, Compiler::StrMid, name, index);
|
|
return Expression::Numeric(0, uint16_t(index), true, false, false, Expression::StrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
// Non optimised case
|
|
std::string name;
|
|
uint16_t srcAddr;
|
|
int index = int(numeric._index);
|
|
|
|
// String input can be literal, const, var and temp
|
|
if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
srcAddr = Compiler::getStrWorkArea();
|
|
}
|
|
else
|
|
{
|
|
Compiler::getOrCreateString(numeric, name, srcAddr, index);
|
|
}
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "textStr", false);
|
|
handleStringParameter(numeric._params[1]);
|
|
Compiler::emitVcpuAsm("STW", "textLen", false);
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "textOfs", false);
|
|
Compiler::emitVcpuAsm("%PrintAcMid", "", false);
|
|
}
|
|
else
|
|
{
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
handleStringParameter(numeric._params[1]);
|
|
Compiler::emitVcpuAsm("STW", "strDstLen", false);
|
|
handleStringParameter(numeric._params[0]);
|
|
Compiler::emitVcpuAsm("STW", "strOffset", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringMid", "", false);
|
|
}
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
Expression::Numeric LOWER$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::LOWER$() : '%s:%d' : LOWER$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 0)
|
|
{
|
|
fprintf(stderr, "Functions::LOWER$() : '%s:%d' : syntax error, 'LOWER$()' requires one string parameter : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal string and parameter, (optimised case)
|
|
if(numeric._varType == Expression::String)
|
|
{
|
|
int index;
|
|
std::string name;
|
|
handleConstantString(numeric, Compiler::StrLower, name, index);
|
|
return Expression::Numeric(0, uint16_t(index), true, false, false, Expression::StrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
// Non optimised case
|
|
std::string name;
|
|
uint16_t srcAddr;
|
|
int index = int(numeric._index);
|
|
|
|
// String input can be literal, const, var and temp
|
|
if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
srcAddr = Compiler::getStrWorkArea();
|
|
}
|
|
else
|
|
{
|
|
Compiler::getOrCreateString(numeric, name, srcAddr, index);
|
|
}
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "textStr", false);
|
|
Compiler::emitVcpuAsm("%PrintAcLower", "", false);
|
|
}
|
|
else
|
|
{
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringLower", "", false);
|
|
}
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
Expression::Numeric UPPER$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::UPPER$() : '%s:%d' : UPPER$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() != 0)
|
|
{
|
|
fprintf(stderr, "Functions::UPPER$() : '%s:%d' : syntax error, 'UPPER$()' requires one string parameter : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
|
|
// Literal string and parameter, (optimised case)
|
|
if(numeric._varType == Expression::String)
|
|
{
|
|
int index;
|
|
std::string name;
|
|
handleConstantString(numeric, Compiler::StrUpper, name, index);
|
|
return Expression::Numeric(0, uint16_t(index), true, false, false, Expression::StrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
// Non optimised case
|
|
std::string name;
|
|
uint16_t srcAddr;
|
|
int index = int(numeric._index);
|
|
|
|
// String input can be literal, const, var and temp
|
|
if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
srcAddr = Compiler::getStrWorkArea();
|
|
}
|
|
else
|
|
{
|
|
Compiler::getOrCreateString(numeric, name, srcAddr, index);
|
|
}
|
|
|
|
if(Expression::getEnableOptimisedPrint() && Expression::getOutputNumeric()._nestedCount == 0)
|
|
{
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "textStr", false);
|
|
Compiler::emitVcpuAsm("%PrintAcUpper", "", false);
|
|
}
|
|
else
|
|
{
|
|
// Create a new temporary string
|
|
if(!isFuncNested()) Compiler::nextStrWorkArea();
|
|
uint16_t dstAddr = Compiler::getStrWorkArea();
|
|
|
|
Compiler::emitStringAddress(numeric, srcAddr);
|
|
Compiler::emitVcpuAsm("STW", "strSrcAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(dstAddr), false);
|
|
Compiler::emitVcpuAsm("%StringUpper", "", false);
|
|
}
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
|
|
Expression::Numeric STRCAT$(Expression::Numeric& numeric, const std::string& moduleName, const std::string& codeLineText, int codeLineStart)
|
|
{
|
|
if(Expression::getOutputNumeric()._staticInit)
|
|
{
|
|
fprintf(stderr, "Functions::STRCAT$() : '%s:%d' : STRCAT$() cannot be used in static initialisation : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(Expression::getEnableOptimisedPrint())
|
|
{
|
|
fprintf(stderr, "Functions::STRCAT$() : '%s:%d' : syntax error, STRCAT$() cannot be used in PRINT statements : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._params.size() == 0)
|
|
{
|
|
fprintf(stderr, "Functions::STRCAT$() : '%s:%d' : syntax error, STRCAT$() requires at least two string parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
if(numeric._varType == Expression::TmpStrVar)
|
|
{
|
|
fprintf(stderr, "Functions::STRCAT$() : '%s:%d' : syntax error, STRCAT$() requires string literals or string variables as ALL parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
for(int i=0; i<int(numeric._params.size()); i++)
|
|
{
|
|
if(numeric._params[i]._varType == Expression::TmpStrVar)
|
|
{
|
|
fprintf(stderr, "Functions::STRCAT$() : '%s:%d' : syntax error, STRCAT$() requires string literals or string variables as ALL parameters : %s\n", moduleName.c_str(), codeLineStart, codeLineText.c_str());
|
|
numeric._isValid = false;
|
|
return numeric;
|
|
}
|
|
}
|
|
|
|
// Source string addresses, (extra 0x0000 delimiter used by VASM runtime)
|
|
std::string name;
|
|
int index = int(numeric._index);
|
|
std::vector<uint16_t> strAddrs(numeric._params.size() + 2, 0x0000);
|
|
Compiler::getOrCreateString(numeric, name, strAddrs[0], index);
|
|
for(int i=0; i<int(numeric._params.size()); i++)
|
|
{
|
|
index = int(numeric._params[i]._index);
|
|
Compiler::getOrCreateString(numeric._params[i], name, strAddrs[i + 1], index);
|
|
}
|
|
|
|
// Source string addresses LUT
|
|
uint16_t lutAddress;
|
|
if(!Memory::getFreeRAM(Memory::FitDescending, int(strAddrs.size()*2), USER_CODE_START, Compiler::getStringsStart(), lutAddress))
|
|
{
|
|
fprintf(stderr, "Functions::STRCAT$() : '%s:%d' : not enough RAM for string concatenation LUT of size %d : %s\n", moduleName.c_str(), codeLineStart, int(strAddrs.size()), codeLineText.c_str());
|
|
return false;
|
|
}
|
|
Compiler::getCodeLines()[Compiler::getCurrentCodeLineIndex()]._strConcatLut = {lutAddress, strAddrs};
|
|
|
|
// Concatenate multiple source strings to string work area
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(lutAddress), false);
|
|
Compiler::emitVcpuAsm("STW", "strLutAddr", false);
|
|
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStrWorkArea()), false);
|
|
Compiler::emitVcpuAsm("%StringConcatLut", "", false);
|
|
|
|
return Expression::Numeric(0, uint16_t(-1), true, false, false, Expression::TmpStrVar, Expression::BooleanCC, Expression::Int16Both, name, std::string(""));
|
|
}
|
|
} |