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

6784 lines
316 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <cmath>
#include <ctime>
#include <fstream>
#include <algorithm>
#include <random>
#include "memory.h"
#include "cpu.h"
#include "loader.h"
#include "image.h"
#include "assembler.h"
#include "keywords.h"
#include "functions.h"
#include "operators.h"
#include "linker.h"
#include "midi.h"
#define FONT_WIDTH 6
#define FONT_HEIGHT 8
#define MAPPING_SIZE 96
namespace Keywords
{
enum LoadUsage {LoadType=0, LoadWave, LoadMidi, LoadImage, LoadSprite, LoadFont};
enum InitTypes {InitTime, InitMidi, InitMidiV, InitUser};
enum MidiTypes {MidiNone, Midi, MidiV, MidiId, MidiIdV};
struct Gprintf
{
int codeLineIndex = 0;
Assembler::Gprintf _gprintfAsm;
};
bool _constDimStrArray = false;
int _numNumericGotosGosubs = 0;
MidiTypes _midiType = MidiNone;
std::string _userRoutine;
std::map<std::string, Keyword> _keywords;
std::map<std::string, std::string> _equalsKeywords;
std::map<std::string, Keyword>& getKeywords(void) {return _keywords; }
std::map<std::string, std::string>& getEqualsKeywords(void) {return _equalsKeywords;}
std::vector<Gprintf> _gprintfs;
void reset(void)
{
_midiType = MidiNone;
_userRoutine = "";
_gprintfs.clear();
}
void restart(void)
{
_constDimStrArray = false;
_numNumericGotosGosubs = 0;
}
bool initialise(void)
{
restart();
// Equals keywords
_equalsKeywords["CONST" ] = "CONST";
_equalsKeywords["DIM" ] = "DIM";
_equalsKeywords["DEF" ] = "DEF";
_equalsKeywords["FOR" ] = "FOR";
_equalsKeywords["IF" ] = "IF";
_equalsKeywords["ELSEIF"] = "ELSEIF";
_equalsKeywords["WHILE" ] = "WHILE";
_equalsKeywords["UNTIL" ] = "UNTIL";
// Keywords
_keywords["END" ] = {"END", END, Compiler::SingleStatementParsed};
_keywords["INC" ] = {"INC", INC, Compiler::SingleStatementParsed};
_keywords["DEC" ] = {"DEC", DEC, Compiler::SingleStatementParsed};
_keywords["ON" ] = {"ON", ON, Compiler::SingleStatementParsed};
_keywords["GOTO" ] = {"GOTO", GOTO, Compiler::SingleStatementParsed};
_keywords["GOSUB" ] = {"GOSUB", GOSUB, Compiler::SingleStatementParsed};
_keywords["RETURN" ] = {"RETURN", RETURN, Compiler::SingleStatementParsed};
_keywords["RET" ] = {"RET", RET, Compiler::SingleStatementParsed};
_keywords["CLS" ] = {"CLS", CLS, Compiler::SingleStatementParsed};
_keywords["?" ] = {"?", PRINT, Compiler::SingleStatementParsed};
_keywords["PRINT" ] = {"PRINT", PRINT, Compiler::SingleStatementParsed};
_keywords["INPUT" ] = {"INPUT", INPUT, Compiler::SingleStatementParsed};
_keywords["FOR" ] = {"FOR", FOR, Compiler::SingleStatementParsed};
_keywords["NEXT" ] = {"NEXT", NEXT, Compiler::SingleStatementParsed};
_keywords["IF" ] = {"IF", IF, Compiler::MultiStatementParsed };
_keywords["ELSEIF" ] = {"ELSEIF", ELSEIF, Compiler::SingleStatementParsed};
_keywords["ELSE" ] = {"ELSE", ELSE, Compiler::SingleStatementParsed};
_keywords["ENDIF" ] = {"ENDIF", ENDIF, Compiler::SingleStatementParsed};
_keywords["WHILE" ] = {"WHILE", WHILE, Compiler::SingleStatementParsed};
_keywords["WEND" ] = {"WEND", WEND, Compiler::SingleStatementParsed};
_keywords["REPEAT" ] = {"REPEAT", REPEAT, Compiler::SingleStatementParsed};
_keywords["UNTIL" ] = {"UNTIL", UNTIL, Compiler::SingleStatementParsed};
_keywords["FOREVER" ] = {"FOREVER", FOREVER, Compiler::SingleStatementParsed};
_keywords["&FOREVER"] = {"&FOREVER", FOREVER, Compiler::SingleStatementParsed};
_keywords["AS" ] = {"AS", AS, Compiler::SingleStatementParsed};
_keywords["TYPE" ] = {"TYPE", TYPE, Compiler::SingleStatementParsed};
_keywords["CALL" ] = {"CALL", CALL, Compiler::SingleStatementParsed};
_keywords["PROC" ] = {"PROC", PROC, Compiler::SingleStatementParsed};
_keywords["ENDPROC" ] = {"ENDPROC", ENDPROC, Compiler::SingleStatementParsed};
_keywords["LOCAL" ] = {"LOCAL", LOCAL, Compiler::SingleStatementParsed};
_keywords["CONST" ] = {"CONST", CONST, Compiler::SingleStatementParsed};
_keywords["DIM" ] = {"DIM", DIM, Compiler::SingleStatementParsed};
_keywords["DEF" ] = {"DEF", DEF, Compiler::SingleStatementParsed};
_keywords["DATA" ] = {"DATA", DATA, Compiler::SingleStatementParsed};
_keywords["READ" ] = {"READ", READ, Compiler::SingleStatementParsed};
_keywords["RESTORE" ] = {"RESTORE", RESTORE, Compiler::SingleStatementParsed};
_keywords["ALLOC" ] = {"ALLOC", ALLOC, Compiler::SingleStatementParsed};
_keywords["FREE" ] = {"FREE", FREE, Compiler::SingleStatementParsed};
_keywords["AT" ] = {"AT", AT, Compiler::SingleStatementParsed};
_keywords["PUT" ] = {"PUT", PUT, Compiler::SingleStatementParsed};
_keywords["MODE" ] = {"MODE", MODE, Compiler::SingleStatementParsed};
_keywords["WAIT" ] = {"WAIT", WAIT, Compiler::SingleStatementParsed};
_keywords["PSET" ] = {"PSET", PSET, Compiler::SingleStatementParsed};
_keywords["LINE" ] = {"LINE", LINE, Compiler::SingleStatementParsed};
_keywords["HLINE" ] = {"HLINE", HLINE, Compiler::SingleStatementParsed};
_keywords["VLINE" ] = {"VLINE", VLINE, Compiler::SingleStatementParsed};
_keywords["CIRCLE" ] = {"CIRCLE", CIRCLE, Compiler::SingleStatementParsed};
_keywords["CIRCLEF" ] = {"CIRCLEF", CIRCLEF, Compiler::SingleStatementParsed};
_keywords["RECT" ] = {"RECT", RECT, Compiler::SingleStatementParsed};
_keywords["RECTF" ] = {"RECTF", RECTF, Compiler::SingleStatementParsed};
_keywords["POLY" ] = {"POLY", POLY, Compiler::SingleStatementParsed};
_keywords["POLYR" ] = {"POLYR", POLYR, Compiler::SingleStatementParsed};
_keywords["TCLIP" ] = {"TCLIP", TCLIP, Compiler::SingleStatementParsed};
_keywords["SCROLL" ] = {"SCROLL", SCROLL, Compiler::SingleStatementParsed};
_keywords["POKE" ] = {"POKE", POKE, Compiler::SingleStatementParsed};
_keywords["DOKE" ] = {"DOKE", DOKE, Compiler::SingleStatementParsed};
_keywords["INIT" ] = {"INIT", INIT, Compiler::SingleStatementParsed};
_keywords["TICK" ] = {"TICK", TICK, Compiler::SingleStatementParsed};
_keywords["PLAY" ] = {"PLAY", PLAY, Compiler::SingleStatementParsed};
_keywords["LOAD" ] = {"LOAD", LOAD, Compiler::SingleStatementParsed};
_keywords["SPRITE" ] = {"SPRITE", SPRITE, Compiler::SingleStatementParsed};
_keywords["SOUND" ] = {"SOUND", SOUND, Compiler::SingleStatementParsed};
_keywords["SET" ] = {"SET", SET, Compiler::SingleStatementParsed};
_keywords["ASM" ] = {"ASM", ASM, Compiler::SingleStatementParsed};
_keywords["ENDASM" ] = {"ENDASM", ENDASM, Compiler::SingleStatementParsed};
_keywords["BCDADD" ] = {"BCDADD", BCDADD, Compiler::SingleStatementParsed};
_keywords["BCDSUB" ] = {"BCDSUB", BCDSUB, Compiler::SingleStatementParsed};
_keywords["BCDINT" ] = {"BCDINT", BCDINT, Compiler::SingleStatementParsed};
_keywords["BCDCPY" ] = {"BCDCPY", BCDCPY, Compiler::SingleStatementParsed};
_keywords["GPRINTF" ] = {"GPRINTF", GPRINTF, Compiler::SingleStatementParsed};
_keywords["EXEC" ] = {"EXEC", EXEC, Compiler::SingleStatementParsed};
_keywords["OPEN" ] = {"OPEN", OPEN, Compiler::SingleStatementParsed};
return true;
}
bool findKeyword(std::string code, const std::string& keyword, size_t& foundPos)
{
Expression::strToUpper(code);
foundPos = code.find(keyword);
if(foundPos != std::string::npos)
{
foundPos += keyword.size();
return true;
}
return false;
}
KeywordResult handleKeywords(Compiler::CodeLine& codeLine, const std::string& keyword, int codeLineIndex, int tokenIndex, KeywordFuncResult& result)
{
size_t foundPos;
std::string key = keyword;
Expression::strToUpper(key);
if(_keywords.find(key) == _keywords.end()) return KeywordNotFound;
// Handle keyword in code line
if(findKeyword(key, _keywords[key]._name, foundPos) && _keywords[key]._func)
{
// Line index taking into account modules
int codeLineStart = Compiler::getCodeLineStart(codeLineIndex);
// Keyword
bool success = _keywords[key]._func(codeLine, codeLineIndex, codeLineStart, tokenIndex, foundPos, result);
return (!success) ? KeywordError : KeywordFound;
}
return KeywordFound;
}
#ifndef STAND_ALONE
bool addGprintf(const std::string& lineToken, const std::string& formatText, const std::vector<std::string>& variables, uint16_t address, int codeLineIndex)
{
std::vector<Assembler::Gprintf::Var> vars;
std::vector<std::string> subs;
Assembler::parseGprintfFormat(formatText, variables, vars, subs);
Gprintf gprintf = {codeLineIndex, {Compiler::getVasmPC(), codeLineIndex, lineToken, formatText, vars, subs}};
for(int i=0; i<int(gprintf._gprintfAsm._vars.size()); i++)
{
gprintf._gprintfAsm._vars[i]._indirection = 2;
gprintf._gprintfAsm._vars[i]._data = uint16_t(address + i*2);
}
_gprintfs.push_back(gprintf);
return true;
}
bool convertGprintGbasToGprintfAsm(void)
{
for(int i=0; i<int(_gprintfs.size()); i++)
{
int codeLineIndex = _gprintfs[i].codeLineIndex;
const Compiler::CodeLine& codeLine = Compiler::getCodeLines()[codeLineIndex];
const Compiler::VasmLine& vasmLine = codeLine._vasm.back();
Assembler::Gprintf& gprintfAsm = _gprintfs[i]._gprintfAsm;
uint16_t address = uint16_t(vasmLine._address);
gprintfAsm._address = address;
if(!Assembler::addGprintf(gprintfAsm, address))
{
fprintf(stderr, "Keywords::fixGprintfAddresses() : '%s:%d' : Assembler::addGprintf() at '0x%04x' already exists : %s\n", codeLine._moduleName.c_str(), codeLineIndex, address, codeLine._text.c_str());
return false;
}
}
return true;
}
#endif
// ********************************************************************************************
// Keywords
// ********************************************************************************************
bool END(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
//std::string labelName = "_end_" + Expression::wordToHexString(Compiler::getVasmPC());
//Compiler::emitVcpuAsm("BRA", labelName, false, codeLineIndex, labelName);
Compiler::emitVcpuAsm("HALT", "", false, codeLineIndex);
return true;
}
bool INC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
// Operand must be an integer var
std::string varToken = codeLine._code.substr(foundPos);
Expression::stripWhitespace(varToken);
// Byte write configuration
Expression::Int16Byte int16Byte = Expression::Int16Both;
size_t dot = varToken.find('.');
if(dot != std::string::npos)
{
std::string dotName = varToken.substr(dot);
varToken = varToken.substr(0, dot);
Expression::strToUpper(dotName);
if(dotName == ".LO") int16Byte = Expression::Int16Low;
if(dotName == ".HI") int16Byte = Expression::Int16High;
}
int varIndex = Compiler::findVar(varToken, false);
if(varIndex < 0)
{
fprintf(stderr, "Keywords::INC() : '%s:%d' : syntax error, integer variable '%s' not found : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
switch(int16Byte)
{
case Expression::Int16Low: Compiler::emitVcpuAsm("INC", "_" + Compiler::getIntegerVars()[varIndex]._name, false); break;
case Expression::Int16High: Compiler::emitVcpuAsm("INC", "_" + Compiler::getIntegerVars()[varIndex]._name + " + 1", false); break;
case Expression::Int16Both: Compiler::emitVcpuAsm("INC", "_" + Compiler::getIntegerVars()[varIndex]._name, false); break;
default: break;
}
return true;
}
bool DEC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
// Operand must be an integer var
std::string varToken = codeLine._code.substr(foundPos);
Expression::stripWhitespace(varToken);
int varIndex = Compiler::findVar(varToken, false);
if(varIndex < 0)
{
fprintf(stderr, "Keywords::DEC() : '%s:%d' : syntax error, integer variable '%s' not found : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDW", "_" + Compiler::getIntegerVars()[varIndex]._name, false);
Compiler::emitVcpuAsm("SUBI", "1", false);
Compiler::emitVcpuAsm("STW", "_" + Compiler::getIntegerVars()[varIndex]._name, false);
return true;
}
bool ON(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::string code = codeLine._code;
Expression::strToUpper(code);
size_t gotoOffset = code.find("GOTO");
size_t gosubOffset = code.find("GOSUB");
if(gotoOffset == std::string::npos && gosubOffset == std::string::npos)
{
fprintf(stderr, "Keywords::ON() : '%s:%d' : syntax error : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
size_t gSize = (gotoOffset != std::string::npos) ? 4 : 5;
size_t gOffset = (gotoOffset != std::string::npos) ? gotoOffset : gosubOffset;
// Parse ON field
Expression::Numeric onValue;
std::string onToken = codeLine._code.substr(foundPos, gOffset - (foundPos + 1));
if(Compiler::parseExpression(codeLineIndex, onToken, onValue) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::ON() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, onToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "register0", false);
// Parse labels
std::vector<size_t> gOffsets;
std::vector<std::string> gTokens = Expression::tokeniseOffsets(codeLine._code.substr(gOffset + gSize), ',', gOffsets, false);
if(gTokens.size() < 1)
{
fprintf(stderr, "Keywords::ON() : '%s:%d' : syntax error, must have at least one label after GOTO/GOSUB : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Create on goto/gosub label LUT
Compiler::getCodeLines()[codeLineIndex]._onGotoGosubLut._lut.clear();
for(int i=0; i<int(gTokens.size()); i++)
{
std::string gLabel = gTokens[i];
Expression::stripWhitespace(gLabel);
// Optimised gosub has no PUSH, (i.e. leaf function, VBI handlers, ASM code, etc)
bool usePush = true;
if(gLabel[0] == '&')
{
usePush = false;
gLabel.erase(0, 1);
}
int labelIndex = Compiler::findLabel(gLabel);
if(labelIndex == -1)
{
fprintf(stderr, "Keywords::ON() : '%s:%d' : invalid label %s in slot %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, gLabel.c_str(), i, codeLine._text.c_str());
Compiler::getCodeLines()[codeLineIndex]._onGotoGosubLut._lut.clear();
return false;
}
// Only ON GOSUB needs a PUSH, (emitted in createVasmCode())
if(gosubOffset != std::string::npos) Compiler::getLabels()[labelIndex]._gosub = usePush;
// Create lookup table out of label addresses
Compiler::getCodeLines()[codeLineIndex]._onGotoGosubLut._lut.push_back(labelIndex);
}
// Allocate giga memory for LUT
int size = int(gTokens.size()) * 2;
uint16_t address;
if(!Memory::getFreeRAM(Memory::FitDescending, size, USER_CODE_START, Compiler::getRuntimeStart(), address))
{
fprintf(stderr, "Keywords::ON() : '%s:%d' : not enough RAM for onGotoGosub LUT of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, size, codeLine._text.c_str());
return false;
}
Compiler::getCodeLines()[codeLineIndex]._onGotoGosubLut._address = address;
Compiler::getCodeLines()[codeLineIndex]._onGotoGosubLut._name = "_lut_onAddrs_" + Expression::wordToHexString(address);
Compiler::emitVcpuAsm("ADDW", "register0", false);
Compiler::emitVcpuAsm("STW", "register0", false);
Compiler::emitVcpuAsm("LDWI", Compiler::getCodeLines()[codeLineIndex]._onGotoGosubLut._name, false);
Compiler::emitVcpuAsm("ADDW", "register0", false);
if(Compiler::getArrayIndiciesOne())
{
Compiler::emitVcpuAsm("SUBI", "2", false); // enable this to start at 1 instead of 0
}
Compiler::emitVcpuAsm("DEEK", "", false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
return true;
}
bool GOTO(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Parse labels
std::vector<size_t> gotoOffsets;
std::vector<std::string> gotoTokens = Expression::tokeniseOffsets(codeLine._code.substr(foundPos), ',', gotoOffsets, false);
if(gotoTokens.size() < 1 || gotoTokens.size() > 2)
{
fprintf(stderr, "Keywords::GOTO() : '%s:%d' : syntax error, must have one or two parameters, e.g. 'GOTO 200' or 'GOTO k+1,default' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Parse GOTO field
Expression::Numeric gotoValue;
std::string gotoToken = gotoTokens[0];
Expression::stripWhitespace(gotoToken);
bool useBRA = false;
if(gotoToken[0] == '&')
{
useBRA = true;
gotoToken.erase(0, 1);
}
int labelIndex = Compiler::findLabel(gotoToken);
if(labelIndex == -1)
{
if(Expression::isNumber(gotoToken))
{
fprintf(stderr, "Keywords::GOTO() : '%s:%d' : numeric label '%s' does not exist : %s\n", codeLine._moduleName.c_str(), codeLineStart, gotoToken.c_str(), codeLine._text.c_str());
return false;
}
if(++_numNumericGotosGosubs > Compiler::getNumNumericLabels())
{
fprintf(stderr, "Keywords::GOTO() : '%s:%d' : numeric label '%s' does not exist : %s\n", codeLine._moduleName.c_str(), codeLineStart, gotoToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::setCreateNumericLabelLut(true);
if(Compiler::parseExpression(codeLineIndex, gotoToken, gotoValue) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::GOTO() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, gotoToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "numericLabel", false);
// Default label exists
if(gotoTokens.size() == 2)
{
std::string defaultToken = gotoTokens[1];
Expression::stripWhitespace(defaultToken);
labelIndex = Compiler::findLabel(defaultToken);
if(labelIndex == -1)
{
fprintf(stderr, "Keywords::GOTO() : '%s:%d' : default label does not exist : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDWI", "_" + Compiler::getLabels()[labelIndex]._name, false);
}
// No default label
else
{
Compiler::emitVcpuAsm("LDI", "0", false);
}
Compiler::emitVcpuAsm("STW", "defaultLabel", false);
// Call gotoNumericLabel
Compiler::emitVcpuAsm("%GotoNumeric", "", false);
return true;
}
// Within same page, (validation check on same page branch may fail after outputCode(), user will be warned)
if(useBRA)
{
Compiler::emitVcpuAsm("BRA", "_" + gotoToken, false);
}
// Long jump
else
{
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("CALLI", "_" + gotoToken, false);
}
else
{
Compiler::emitVcpuAsm("LDWI", "_" + gotoToken, false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
}
return true;
}
bool GOSUB(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Parse labels
std::vector<size_t> gosubOffsets;
std::vector<std::string> gosubTokens = Expression::tokeniseOffsets(codeLine._code.substr(foundPos), ',', gosubOffsets, false);
if(gosubTokens.size() < 1 || gosubTokens.size() > 2)
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : syntax error, must have one or two parameters, e.g. 'GOSUB <label>' or 'GOSUB <expression>, <default label>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(gosubTokens[0].size() == 0)
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : syntax error, invalid label : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Parse GOSUB field
Expression::Numeric gosubValue;
std::string gosubToken = gosubTokens[0];
Expression::stripWhitespace(gosubToken);
// Optimised gosub has no PUSH, (i.e. leaf function, VBI handlers, ASM code, etc)
bool usePush = true;
if(gosubToken[0] == '&')
{
usePush = false;
gosubToken.erase(0, 1);
}
int labelIndex = Compiler::findLabel(gosubToken);
if(labelIndex == -1)
{
if(Expression::isNumber(gosubToken))
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : numeric label '%s' does not exist : %s\n", codeLine._moduleName.c_str(), codeLineStart, gosubToken.c_str(), codeLine._text.c_str());
return false;
}
if(++_numNumericGotosGosubs > Compiler::getNumNumericLabels())
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : numeric label '%s' does not exist : %s\n", codeLine._moduleName.c_str(), codeLineStart, gosubToken.c_str(), codeLine._text.c_str());
return false;
}
if(!usePush)
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : can't use optimised GOSUB with numeric labels : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::setCreateNumericLabelLut(true);
if(Compiler::parseExpression(codeLineIndex, gosubToken, gosubValue) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, gosubToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "numericLabel", false);
// Default label exists
if(gosubTokens.size() == 2)
{
std::string defaultToken = gosubTokens[1];
Expression::stripWhitespace(defaultToken);
labelIndex = Compiler::findLabel(defaultToken);
if(labelIndex == -1)
{
fprintf(stderr, "Keywords::GOSUB() : '%s:%d' : default label does not exist : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::getLabels()[labelIndex]._gosub = true;
Compiler::emitVcpuAsm("LDWI", "_" + Compiler::getLabels()[labelIndex]._name, false);
}
// No default label
else
{
Compiler::emitVcpuAsm("LDI", "0", false);
}
Compiler::emitVcpuAsm("STW", "defaultLabel", false);
// Call gosubNumericLabel
Compiler::emitVcpuAsm("%GosubNumeric", "", false);
return true;
}
// CALL label
Compiler::getLabels()[labelIndex]._gosub = usePush;
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("CALLI", "_" + gosubToken, false);
}
else
{
Compiler::emitVcpuAsm("LDWI", "_" + gosubToken, false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
return true;
}
bool RETURN(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
// Use a macro instead of separate "POP" and "RET", otherwise page jumps could be inserted in between the "POP" and "RET" causing mayhem and havoc
Compiler::emitVcpuAsm("%Return", "", false);
return true;
}
bool RET(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
Compiler::emitVcpuAsm("RET", "", false);
return true;
}
bool CLS(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() > 3)
{
fprintf(stderr, "Keywords::CLS() : '%s:%d' : syntax error, expected 'CLS INIT' or 'CLS <address>, <optional width>, <optional height>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric param;
if(tokens.size() == 1 && tokens[0].size())
{
std::string token = tokens[0];
Expression::strToUpper(token);
Expression::stripWhitespace(token);
if(token == "INIT")
{
Compiler::emitVcpuAsm("%ResetVideoTable", "", false);
}
else
{
if(Compiler::parseExpression(codeLineIndex, tokens[0], param) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::CLS() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
if(param._varType == Expression::Number && uint16_t(std::lround(param._value)) < DEFAULT_EXEC_ADDRESS)
{
fprintf(stderr, "Keywords::CLS() : '%s:%d' : address field must be above &h%04x, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, DEFAULT_EXEC_ADDRESS, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "clsAddress", false);
Compiler::emitVcpuAsm("%ClearScreen", "", false);
}
}
else if(tokens.size() > 1)
{
if(Compiler::parseExpression(codeLineIndex, tokens[0], param) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::CLS() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "clrAddress", false);
if(Compiler::parseExpression(codeLineIndex, tokens[1], param) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::CLS() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[1].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "clrWidth", false); // runtime uses clrWidth in arithmetic, so make sure all of it is valid
if(tokens.size() == 2)
{
Compiler::emitVcpuAsm("LDI", "120", false);
}
else
{
if(Compiler::parseExpression(codeLineIndex, tokens[2], param) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::CLS() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[2].c_str(), codeLine._text.c_str());
return false;
}
}
Compiler::emitVcpuAsm("STW", "clrLines", false); // runtime uses clrLines in arithmetic, so make sure all of it is valid
Compiler::emitVcpuAsm("%ClearRect", "", false);
}
else
{
Compiler::emitVcpuAsm("%ClearVertBlinds", "", false);
}
return true;
}
bool PRINT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineStart);
// Parse print tokens
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ';', false, false);
RESTART_PRINT:
for(int i=0; i<int(tokens.size()); i++)
{
if(tokens[i].size() == 0 || Expression::hasOnlyWhiteSpace(tokens[i]))
{
tokens.erase(tokens.begin() + i);
goto RESTART_PRINT;
}
Expression::Numeric numeric;
int varIndex = -1, constIndex = -1, strIndex = -1;
uint32_t expressionType = Compiler::isExpression(tokens[i], varIndex, constIndex, strIndex);
#if 1
if((expressionType & Expression::HasStringKeywords) && (expressionType & Expression::HasOptimisedPrint))
{
// Prints text on the fly without creating strings
Expression::setEnableOptimisedPrint(true);
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
Expression::setEnableOptimisedPrint(false);
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Expression::setEnableOptimisedPrint(false);
}
#else
// TODO: Fix this, (checks for syntax errors)
if((expressionType & Expression::HasStringKeywords))
{
if(expressionType & Expression::HasOptimisedPrint)
{
// Prints text on the fly without creating strings
Expression::setEnableOptimisedPrint(true);
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
Expression::setEnableOptimisedPrint(false);
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Expression::setEnableOptimisedPrint(false);
}
// Leading chars before a string function
else
{
//fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
//return false;
}
}
#endif
// Arrays are handled as functions
else if(expressionType & Expression::HasFunctions)
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(numeric._varType == Expression::Number)
{
Compiler::emitVcpuAsm("%PrintInt16", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
}
else
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
}
}
else if((expressionType & Expression::HasStrVars) && (expressionType & Expression::HasOperators))
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(numeric._varType == Expression::Number)
{
Compiler::emitVcpuAsm("%PrintInt16", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
}
else if(numeric._varType == Expression::Str2Var)
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcString", "", false);
}
else if(numeric._varType == Expression::TmpStrVar)
{
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(Compiler::getStrWorkArea()), false);
Compiler::emitVcpuAsm("%PrintAcString", "", false);
}
else
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
}
}
else if((expressionType & Expression::HasIntVars) && (expressionType & Expression::HasOperators))
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(numeric._varType == Expression::Number)
{
Compiler::emitVcpuAsm("%PrintInt16", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
}
else
{
if(numeric._varType != Expression::Str2Var)
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
}
// String array with variable index
else
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcString", "", false);
}
}
}
else if(expressionType & Expression::HasIntVars)
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(varIndex >= 0)
{
if(numeric._varType != Expression::Str2Var)
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: Compiler::emitVcpuAsm("LD", "_" + Compiler::getIntegerVars()[varIndex]._name, false); break;
case Expression::Int16High: Compiler::emitVcpuAsm("LD", "_" + Compiler::getIntegerVars()[varIndex]._name + " + 1", false); break;
case Expression::Int16Both: Compiler::emitVcpuAsm("LDW", "_" + Compiler::getIntegerVars()[varIndex]._name, false); break;
default: break;
}
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
}
// String array with variable index
else
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcString", "", false);
}
}
else
{
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
}
}
else if(expressionType & Expression::HasStrVars)
{
if(strIndex >= 0)
{
if(Compiler::getStringVars()[strIndex]._varType != Compiler::VarStr2)
{
std::string strName = Compiler::getStringVars()[strIndex]._name;
Compiler::emitVcpuAsm("%PrintString", "_" + strName, false);
}
// String array with literal index
else
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcString", "", false);
}
}
}
else if(expressionType & Expression::HasKeywords)
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
Compiler::emitVcpuAsm("%PrintAcInt16", "", false);
}
else if(expressionType & Expression::HasOperators)
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%PrintInt16", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
}
else if(expressionType & Expression::HasStrings)
{
size_t lquote = tokens[i].find_first_of("\"");
size_t rquote = tokens[i].find_last_of("\"");
#if 1
// TODO: Test this thoroughly
if(lquote > 0)
{
// If there are leading chars that are not whitespace, then syntax error
for(size_t j=0; j<lquote; j++)
{
if(!isspace(tokens[i][j]))
{
std::string error = tokens[i].substr(0, lquote);
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, error.c_str(), codeLine._text.c_str());
return false;
}
}
}
if(rquote < tokens[i].size() - 1)
{
// If there are trailing chars left over and they are not whitespace, then syntax error
for(size_t j=rquote+1; j<tokens[i].size(); j++)
{
if(!isspace(tokens[i][j]))
{
std::string error = tokens[i].substr(rquote + 1);
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, error.c_str(), codeLine._text.c_str());
return false;
}
}
}
#endif
if(lquote != std::string::npos && rquote != std::string::npos)
{
if(rquote == lquote + 1) continue; // skip empty strings
std::string str = tokens[i].substr(lquote + 1, rquote - (lquote + 1));
// Create string
std::string name;
uint16_t address;
if(Compiler::getOrCreateString(codeLine, codeLineIndex, str, name, address) == -1) return false;
// Print string
Compiler::emitVcpuAsm("%PrintString", "_" + name, false);
}
}
else if(expressionType == Expression::HasStrConsts && constIndex > -1)
{
// Print constant string
std::string internalName = Compiler::getConstants()[constIndex]._internalName;
Compiler::emitVcpuAsm("%PrintString", "_" + internalName, false);
}
else if(expressionType == Expression::HasIntConsts && constIndex > -1)
{
// Print constant int
int16_t data = Compiler::getConstants()[constIndex]._data;
Compiler::emitVcpuAsm("%PrintInt16", Expression::wordToHexString(data), false);
}
else if(expressionType == Expression::HasNumbers)
{
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::PRINT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%PrintInt16", Expression::wordToHexString(int16_t(std::lround(numeric._value))), false);
}
}
// New line
if(codeLine._code[codeLine._code.size() - 1] != ';' && codeLine._code[codeLine._code.size() - 1] != ',')
{
Compiler::emitVcpuAsm("%NewLine", "", false);
}
return true;
}
bool INPUT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Tokenise string and vars
std::vector<std::string> strings;
std::string text = codeLine._code.substr(foundPos);
Expression::stripNonStringWhitespace(text);
std::vector<std::string> tokens = Expression::tokenise(text, ',', false, false);
std::string code = Expression::stripStrings(text, strings, true);
std::vector<std::string> varTokens = Expression::tokenise(code, ',', false, false);
if(varTokens.size() < 1 || (strings.size() > varTokens.size() + 1))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : syntax error, use 'INPUT <heading>, <int/str var0>, <prompt0>, ... <int/str varN>, <promptN>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Print heading string
bool foundHeadingString = false;
if(tokens.size() && Expression::isStringValid(tokens[0]))
{
size_t lquote = tokens[0].find_first_of("\"");
size_t rquote = tokens[0].find_last_of("\"");
if(lquote != std::string::npos && rquote != std::string::npos)
{
// Skip empty strings
if(rquote > lquote + 1)
{
std::string str = tokens[0].substr(lquote + 1, rquote - (lquote + 1));
// Create string
std::string name;
uint16_t address;
if(Compiler::getOrCreateString(codeLine, codeLineIndex, str, name, address) == -1) return false;
// Print string
Compiler::emitVcpuAsm("%PrintString", "_" + name, false);
foundHeadingString = true;
}
}
}
// INPUT vars/strs/types LUTs, (extra 0x0000 delimiter used by VASM runtime)
std::vector<uint16_t> varsLut(varTokens.size());
std::vector<uint16_t> strsLut(varTokens.size());
std::vector<uint16_t> typesLut(varTokens.size() + 1, 0x0000);
// Loop through vars
for(int i=0; i<int(varTokens.size()); i++)
{
// Int var exists
bool isStrVar = false;
int intVar = Compiler::findVar(varTokens[i]);
if(intVar >= 0)
{
varsLut[i] = Compiler::getIntegerVars()[intVar]._address;
typesLut[i] = Compiler::VarInt16;
continue;
}
// Str var exists
else
{
if(Expression::isStrNameValid(varTokens[i]))
{
isStrVar = true;
int strIndex = Compiler::findStr(varTokens[i]);
if(strIndex >= 0)
{
varsLut[i] = Compiler::getStringVars()[strIndex]._address;
typesLut[i] = Compiler::VarStr;
continue;
}
}
}
// Create int var
int varIndex = 0;
if(!isStrVar)
{
Compiler::createIntVar(varTokens[i], 0, 0, codeLine, codeLineIndex, false, varIndex);
if(varIndex == -1)
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : couldn't create integer var '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varTokens[i].c_str(), codeLine._text.c_str());
return false;
}
varsLut[i] = Compiler::getIntegerVars()[varIndex]._address;
typesLut[i] = Compiler::VarInt16;
}
// Create str var
else
{
uint16_t address;
varIndex = getOrCreateString(codeLine, codeLineIndex, "", varTokens[i], address, USER_STR_SIZE, false);
if(varIndex == -1)
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : couldn't create string var '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varTokens[i].c_str(), codeLine._text.c_str());
return false;
}
varsLut[i] = Compiler::getStringVars()[varIndex]._address;
typesLut[i] = Compiler::VarStr;
}
}
// Loop through strs
for(int i=0; i<int(varTokens.size()); i++)
{
// Create string
std::string name;
uint16_t address;
int index = (foundHeadingString) ? i + 1 : i;
std::string str = (index < int(strings.size())) ? strings[index] : "\"?\";;";
size_t fquote = str.find_first_of('"');
size_t lquote = str.find_last_of('"');
// Semicolons
if(str.size() > lquote + 1 && str[lquote + 1] != ';') typesLut[i] |= 0x40;
if(str.size() > lquote + 1 && str[lquote + 1] == ';') str.erase(lquote + 1, 1);
if(str.back() != ';') typesLut[i] |= 0x80;
if(str.back() == ';') str.erase(str.size() - 1, 1);
// Text length field
uint8_t length = USER_STR_SIZE;
if(str.size() > lquote + 1 && isdigit((unsigned char)str[lquote + 1]))
{
std::string field = str.substr(lquote + 1);
if(!Expression::stringToU8(field, length))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : syntax error in text size field of string '%s' of INPUT statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, str.c_str(), codeLine._text.c_str());
return false;
}
if(length > USER_STR_SIZE)
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : text size field > %d of string '%s' of INPUT statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, USER_STR_SIZE, str.c_str(), codeLine._text.c_str());
return false;
}
str.erase(lquote + 1, field.size());
}
typesLut[i] |= (length + 1) << 8; // increment length as INPUT VASM code counts cursor
// Remove quotes, (remove last quote first)
str.erase(lquote, 1);
str.erase(fquote, 1);
if(Compiler::getOrCreateString(codeLine, codeLineIndex, str, name, address) == -1) return false;
strsLut[i] = address;
}
// Allocate memory for register work area if it hasn't been allocated already
if(Compiler::getRegWorkArea() == 0x0000)
{
uint16_t regAddr;
if(!Memory::getFreeRAM(Memory::FitDescending, REG_WORK_SIZE, USER_CODE_START, Compiler::getRuntimeStart(), regAddr))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : not enough RAM for register work area of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, REG_WORK_SIZE, codeLine._text.c_str());
return false;
}
Compiler::setRegWorkArea(regAddr);
}
// INPUT LUTs
const int lutSize = 3;
uint16_t lutAddr, varsAddr, strsAddr, typesAddr;
if(!Memory::getFreeRAM(Memory::FitDescending, lutSize*2, USER_CODE_START, Compiler::getRuntimeStart(), lutAddr))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : not enough RAM for INPUT LUT of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, lutSize*2, codeLine._text.c_str());
return false;
}
if(!Memory::getFreeRAM(Memory::FitDescending, int(varsLut.size()*2), USER_CODE_START, Compiler::getRuntimeStart(), varsAddr))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : not enough RAM for INPUT Vars LUT of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(varsLut.size()*2), codeLine._text.c_str());
return false;
}
if(!Memory::getFreeRAM(Memory::FitDescending, int(strsLut.size()*2), USER_CODE_START, Compiler::getRuntimeStart(), strsAddr))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : not enough RAM for INPUT Strings LUT of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(strsLut.size()*2), codeLine._text.c_str());
return false;
}
if(!Memory::getFreeRAM(Memory::FitDescending, int(typesLut.size()*2), USER_CODE_START, Compiler::getRuntimeStart(), typesAddr))
{
fprintf(stderr, "Keywords::INPUT() : '%s:%d' : not enough RAM for INPUT Var Types LUT of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(typesLut.size()*2), codeLine._text.c_str());
return false;
}
Compiler::getCodeLines()[codeLineIndex]._inputLut = {lutAddr, varsAddr, strsAddr, typesAddr, varsLut, strsLut, typesLut}; // save LUT in global codeLine not local copy
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(lutAddr), false);
Compiler::emitVcpuAsm("%Input", "", false);
return true;
}
bool FOR(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
bool optimise = true;
bool varStart = false;
int varIndex, constIndex, strIndex;
uint32_t expressionType;
// Parse first line of FOR loop
std::string code = codeLine._code;
Expression::strToUpper(code);
size_t equals, to, step;
if((equals = code.find("=")) == std::string::npos)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error, missing '=' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// TO uses INC/ADD, UPTO uses ADD, DOWNTO uses DEC/SUB; &TO/&UPTO/&DOWNTO are optimised BRA versions
Compiler::ForNextType type = Compiler::AutoTo;
type = (code.find("UPTO") != std::string::npos) ? Compiler::UpTo : type;
type = (code.find("DOWNTO") != std::string::npos) ? Compiler::DownTo : type;
bool farJump = (code.find("&TO") == std::string::npos) && (code.find("&UPTO") == std::string::npos) && (code.find("&DOWNTO") == std::string::npos);
if((to = code.find("TO")) == std::string::npos)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error, missing 'TO' or 'DOWNTO' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
step = code.find("STEP");
// Maximum of 4 nested loops
if(Compiler::getForNextDataStack().size() == MAX_NESTED_LOOPS)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error, maximum nested loops is 4 : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Nested loops temporary variables
uint16_t offset = uint16_t(Compiler::getForNextDataStack().size()) * LOOP_VARS_SIZE;
uint16_t varEnd = LOOP_VAR_START + offset;
uint16_t varStep = LOOP_VAR_START + offset + sizeof(uint16_t);
// Adjust 'to' based on length of TO keyword
int16_t loopStart = 0;
int toOffset = (farJump) ? 0 : 0 - sizeof('&');
toOffset = (type == Compiler::UpTo) ? toOffset - (sizeof("UP")-1) : toOffset;
toOffset = (type == Compiler::DownTo) ? toOffset - (sizeof("DOWN")-1) : toOffset;
// Loop start
std::string startToken = codeLine._code.substr(equals + sizeof('='), to - (equals + sizeof('=')) + toOffset);
expressionType = Compiler::isExpression(startToken, varIndex, constIndex, strIndex);
if((expressionType & Expression::HasIntVars) || (expressionType & Expression::HasKeywords) || (expressionType & Expression::HasFunctions)) varStart = true;
// Var counter, (create or update if being reused)
std::string var = codeLine._code.substr(foundPos, equals - foundPos);
Expression::stripWhitespace(var);
int varCounter = Compiler::findVar(var);
(varCounter < 0) ? Compiler::createIntVar(var, loopStart, 0, codeLine, codeLineIndex, false, varCounter) : Compiler::updateIntVar(loopStart, codeLine, varCounter, false);
// Loop end
int16_t loopEnd = 0;
size_t end = (step == std::string::npos) ? codeLine._code.size() : step;
std::string endToken = codeLine._code.substr(to + sizeof("TO")-1, end - (to + sizeof("TO")-1));
expressionType = Compiler::isExpression(endToken, varIndex, constIndex, strIndex);
if((expressionType & Expression::HasIntVars) || (expressionType & Expression::HasKeywords) || (expressionType & Expression::HasFunctions)) optimise = false;
// Loop step
int16_t loopStep = 1;
std::string stepToken;
if(step != std::string::npos)
{
end = codeLine._code.size();
stepToken = codeLine._code.substr(step + sizeof("STEP")-1, end - (step + sizeof("STEP")-1));
expressionType = Compiler::isExpression(stepToken, varIndex, constIndex, strIndex);
if((expressionType & Expression::HasIntVars) || (expressionType & Expression::HasKeywords) || (expressionType & Expression::HasFunctions)) optimise = false;
}
Expression::Numeric startNumeric, endNumeric, stepNumeric;
if(optimise)
{
// Parse start
if(!Expression::parse(startToken, codeLineIndex, startNumeric))
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in '%s'\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression());
return false;
}
loopStart = int16_t(std::lround(startNumeric._value));
// Parse end
if(!Expression::parse(endToken, codeLineIndex, endNumeric))
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in '%s'\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression());
return false;
}
loopEnd = int16_t(std::lround(endNumeric._value));
// Parse step
if(stepToken.size())
{
if(!Expression::parse(stepToken, codeLineIndex, stepNumeric))
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in '%s'\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression());
return false;
}
loopStep = int16_t(std::lround(stepNumeric._value));
if(loopStep < 1 || loopStep > 255) optimise = false;
}
// Variable start
if(optimise && varStart && endNumeric._isValid && loopEnd >= 0 && loopEnd <= 255)
{
if(Compiler::parseExpression(codeLineIndex, startToken, startNumeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, startToken.c_str(), codeLine._text.c_str());
return false;
}
loopStart = int16_t(std::lround(startNumeric._value));
Compiler::emitVcpuAsm("STW", "_" + Compiler::getIntegerVars()[varCounter]._name, false);
}
// 8bit constants
else if(optimise && startNumeric._isValid && loopStart >= 0 && loopStart <= 255 && endNumeric._isValid && loopEnd >= 0 && loopEnd <= 255)
{
Compiler::emitVcpuAsm("LDI", std::to_string(loopStart), false);
Compiler::emitVcpuAsm("STW", "_" + Compiler::getIntegerVars()[varCounter]._name, false);
}
// 16bit constants require variables
else
{
optimise = false;
(loopStart >= 0 && loopStart <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(loopStart), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(loopStart), false);
Compiler::emitVcpuAsm("STW", "_" + Compiler::getIntegerVars()[varCounter]._name, false);
(loopEnd >= 0 && loopEnd <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(loopEnd), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(loopEnd), false);
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(varEnd)), false);
(loopStep >= 0 && loopStep <= 255) ? Compiler::emitVcpuAsm("LDI", std::to_string(loopStep), false) : Compiler::emitVcpuAsm("LDWI", std::to_string(loopStep), false);
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(varStep)), false);
}
}
else
{
// Parse start
if(Compiler::parseExpression(codeLineIndex, startToken, startNumeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, startToken.c_str(), codeLine._text.c_str());
return false;
}
loopStart = int16_t(std::lround(startNumeric._value));
Compiler::emitVcpuAsm("STW", "_" + Compiler::getIntegerVars()[varCounter]._name, false);
// Parse end
if(Compiler::parseExpression(codeLineIndex, endToken, endNumeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, endToken.c_str(), codeLine._text.c_str());
return false;
}
loopEnd = int16_t(std::lround(endNumeric._value));
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(varEnd)), false);
// Parse step
if(stepToken.size())
{
if(Compiler::parseExpression(codeLineIndex, stepToken, stepNumeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::FOR() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, stepToken.c_str(), codeLine._text.c_str());
return false;
}
loopStep = int16_t(std::lround(stepNumeric._value));
}
else
{
loopStep = 1;
Compiler::emitVcpuAsm("LDI", std::to_string(loopStep), false);
}
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(varStep)), false);
}
// Label and stack
Compiler::setNextInternalLabel("_next_" + Expression::wordToHexString(Compiler::getVasmPC()));
Compiler::getForNextDataStack().push({varCounter, Compiler::getNextInternalLabel(), loopEnd, loopStep, varEnd, varStep, type, farJump, optimise, codeLineIndex});
return true;
}
bool NEXT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
if(codeLine._tokens.size() != 2)
{
fprintf(stderr, "Keywords::NEXT() : '%s:%d' : syntax error, wrong number of tokens : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string var = codeLine._code.substr(foundPos);
int varIndex = Compiler::findVar(codeLine._tokens[1]);
if(varIndex < 0)
{
fprintf(stderr, "Keywords::NEXT() : '%s:%d' : syntax error, bad var : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Pop stack for this nested loop
if(Compiler::getForNextDataStack().empty())
{
fprintf(stderr, "Keywords::NEXT() : '%s:%d' : syntax error, missing FOR statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::ForNextData forNextData = Compiler::getForNextDataStack().top();
Compiler::getForNextDataStack().pop();
if(varIndex != forNextData._varIndex)
{
fprintf(stderr, "Keywords::NEXT() : '%s:%d' : syntax error, wrong var : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string varName = Compiler::getIntegerVars()[varIndex]._name;
std::string labName = forNextData._labelName;
int16_t loopEnd = forNextData._loopEnd;
int16_t loopStep = forNextData._loopStep;
uint16_t varEnd = forNextData._varEnd;
uint16_t varStep = forNextData._varStep;
Compiler::ForNextType type = forNextData._type;
bool farJump = forNextData._farJump;
bool optimise = forNextData._optimise;
std::string forNextCmd;
if(optimise)
{
if(abs(loopStep) == 1)
{
switch(type)
{
case Compiler::AutoTo:
{
// Can't use INC when counting upto 0 or 255
if(loopEnd == 0 || loopEnd == 255)
{
forNextCmd = (farJump) ? "%ForNextFarAdd" : "%ForNextAdd";
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName + " " + std::to_string(loopEnd) + " " + std::to_string(abs(loopStep)), false);
}
else
{
forNextCmd = (farJump) ? "%ForNextFarInc" : "%ForNextInc";
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName + " " + std::to_string(loopEnd), false);
}
}
break;
case Compiler::UpTo:
{
forNextCmd = (farJump) ? "%ForNextFarAdd" : "%ForNextAdd";
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName + " " + std::to_string(loopEnd) + " " + std::to_string(abs(loopStep)), false);
}
break;
case Compiler::DownTo:
{
// Decrement to 0, (if only we had a DJNZ instruction)
if(loopEnd == 0)
{
forNextCmd = (farJump) ? "%ForNextFarDecZero" : "%ForNextDecZero";
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName, false);
}
else
{
forNextCmd = (farJump) ? "%ForNextFarDec" : "%ForNextDec";
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName + " " + std::to_string(loopEnd), false);
}
}
break;
default: break;
}
}
// Additive/subtractive step
else
{
switch(type)
{
case Compiler::AutoTo:
case Compiler::UpTo: forNextCmd = (farJump) ? "%ForNextFarAdd" : "%ForNextAdd"; break;
case Compiler::DownTo: forNextCmd = (farJump) ? "%ForNextFarSub" : "%ForNextSub"; break;
default: break;
}
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName + " " + std::to_string(loopEnd) + " " + std::to_string(abs(loopStep)), false);
}
}
// Positive/negative variable step
else
{
switch(type)
{
case Compiler::AutoTo:
case Compiler::UpTo: forNextCmd = (farJump) ? "%ForNextFarVarAdd" : "%ForNextVarAdd"; break;
case Compiler::DownTo: forNextCmd = (farJump) ? "%ForNextFarVarSub" : "%ForNextVarSub"; break;
default: break;
}
Compiler::emitVcpuAsm(forNextCmd, "_" + varName + " " + labName + " " + Expression::byteToHexString(uint8_t(varEnd)) + " " + Expression::byteToHexString(uint8_t(varStep)), false);
}
return true;
}
bool IF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
bool ifElseEndif = false;
// IF
std::string code = Compiler::getCodeLines()[codeLineIndex]._code;
Expression::strToUpper(code);
size_t offsetIF = code.find("IF");
// THEN
code = codeLine._code;
Expression::strToUpper(code);
size_t offsetTHEN = code.find("THEN");
if(offsetTHEN == std::string::npos) ifElseEndif = true;
// Condition
Expression::Numeric condition;
std::string conditionToken = codeLine._code.substr(foundPos, offsetTHEN - foundPos);
if(Compiler::parseExpression(codeLineIndex, conditionToken, condition) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::IF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, conditionToken.c_str(), codeLine._text.c_str());
return false;
}
if(condition._ccType == Expression::BooleanCC) Compiler::emitVcpuAsm("%JumpFalse", "", false); // Boolean condition requires this extra check
int jmpIndex = int(Compiler::getCodeLines()[codeLineIndex]._vasm.size()) - 1;
// Bail early as we assume this is an IF ELSE ENDIF block
if(ifElseEndif)
{
std::stack<Compiler::EndIfData> endIfData; // stores endif label jumps per if/endif pair
Compiler::getElseIfDataStack().push({jmpIndex, "", codeLineIndex, Compiler::IfBlock, condition._ccType, endIfData});
return true;
}
// Action
std::string actionToken = Compiler::getCodeLines()[codeLineIndex]._code.substr(offsetIF + offsetTHEN + 4);
if(actionToken.size() == 0)
{
fprintf(stderr, "Keywords::IF() : '%s:%d' : syntax error, missing action in 'IF THEN <action>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::trimWhitespace(actionToken);
std::string actionText = Expression::collapseWhitespaceNotStrings(actionToken);
// Short circuit GOTO if action is a literal constant or a label
uint16_t res = 0;
if(Expression::stringToU16(actionText, res) || Compiler::findLabel(actionText) != -1)
{
actionText = "GOTO " + actionText;
}
// Multi-statements
int varIndex, strIndex;
if(Compiler::parseMultiStatements(actionText, codeLineIndex, codeLineStart, varIndex, strIndex) == Compiler::StatementError) return false;
// Create label on next line of vasm code
Compiler::setNextInternalLabel("_else_" + Expression::wordToHexString(Compiler::getVasmPC()));
std::string nextInternalLabel = Compiler::getNextInternalLabel() + " " + std::to_string(Compiler::getNextJumpFalseUniqueId());
// Update if's jump to this new label
Compiler::VasmLine* vasm = &Compiler::getCodeLines()[codeLineIndex]._vasm[jmpIndex];
switch(condition._ccType)
{
case Expression::BooleanCC: vasm->_code = "JumpFalse" + std::string(OPCODE_TRUNC_SIZE - (sizeof("JumpFalse")-1), ' ') + nextInternalLabel; break;
case Expression::NormalCC: addLabelToJumpCC(Compiler::getCodeLines()[codeLineIndex]._vasm, nextInternalLabel); break;
case Expression::FastCC: addLabelToJumpCC(Compiler::getCodeLines()[codeLineIndex]._vasm, Compiler::getNextInternalLabel()); break;
default: break;
}
return true;
}
bool ELSEIF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Check stack for this IF ELSE ENDIF block
if(Compiler::getElseIfDataStack().empty())
{
fprintf(stderr, "Keywords::ELSEIF() : '%s:%d' : syntax error, missing IF statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::ElseIfData elseIfData = Compiler::getElseIfDataStack().top();
int jmpIndex = elseIfData._jmpIndex;
int codeIndex = elseIfData._codeLineIndex;
Expression::CCType ccType = elseIfData._ccType;
std::stack<Compiler::EndIfData> endIfData = elseIfData._endIfData; // add to the list of endif label jumps per if/endif pair
Compiler::getElseIfDataStack().pop();
if(elseIfData._ifElseEndType != Compiler::IfBlock && elseIfData._ifElseEndType != Compiler::ElseIfBlock)
{
fprintf(stderr, "Keywords::ELSEIF() : '%s:%d' : syntax error, ELSEIF follows IF or ELSEIF : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Jump to endif for previous BASIC line
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("CALLI", "CALLI_JUMP", false, codeLineIndex - 1);
endIfData.push({int(Compiler::getCodeLines()[codeLineIndex - 1]._vasm.size()) - 1, codeLineIndex - 1, ccType});
}
else
{
// Validator checks for invalid page jumps
if(ccType == Expression::FastCC)
{
Compiler::emitVcpuAsm("BRA", "BRA_JUMP", false, codeLineIndex - 1);
endIfData.push({int(Compiler::getCodeLines()[codeLineIndex - 1]._vasm.size()) - 1, codeLineIndex - 1, ccType});
}
else
{
Compiler::emitVcpuAsm("LDWI", "LDWI_JUMP", false, codeLineIndex - 1);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false, codeLineIndex - 1);
endIfData.push({int(Compiler::getCodeLines()[codeLineIndex - 1]._vasm.size()) - 2, codeLineIndex - 1, ccType});
}
}
// Create label on next line of vasm code
Compiler::setNextInternalLabel("_elseif_" + Expression::wordToHexString(Compiler::getVasmPC()));
std::string nextInternalLabel = Compiler::getNextInternalLabel() + " " + std::to_string(Compiler::getNextJumpFalseUniqueId());
// Update if's jump to this new label
Compiler::VasmLine* vasm = &Compiler::getCodeLines()[codeIndex]._vasm[jmpIndex];
switch(ccType)
{
case Expression::BooleanCC: vasm->_code = "JumpFalse" + std::string(OPCODE_TRUNC_SIZE - (sizeof("JumpFalse")-1), ' ') + nextInternalLabel; break;
case Expression::NormalCC: addLabelToJumpCC(Compiler::getCodeLines()[codeIndex]._vasm, nextInternalLabel); break;
case Expression::FastCC: addLabelToJumpCC(Compiler::getCodeLines()[codeIndex]._vasm, Compiler::getNextInternalLabel()); break;
default: break;
}
// Condition
Expression::Numeric condition;
std::string conditionToken = codeLine._code.substr(foundPos);
if(Compiler::parseExpression(codeLineIndex, conditionToken, condition) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::ELSEIF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, conditionToken.c_str(), codeLine._text.c_str());
return false;
}
if(condition._ccType == Expression::BooleanCC) Compiler::emitVcpuAsm("%JumpFalse", "", false); // Boolean condition requires this extra check
jmpIndex = int(Compiler::getCodeLines()[codeLineIndex]._vasm.size()) - 1;
Compiler::getElseIfDataStack().push({jmpIndex, "", codeLineIndex, Compiler::ElseIfBlock, condition._ccType, endIfData});
return true;
}
bool ELSE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
if(codeLine._tokens.size() != 1)
{
fprintf(stderr, "Keywords::ELSE() : '%s:%d' : syntax error, wrong number of tokens : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Check stack for this IF ELSE ENDIF block
if(Compiler::getElseIfDataStack().empty())
{
fprintf(stderr, "Keywords::ELSE() : '%s:%d' : syntax error, missing IF statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::ElseIfData elseIfData = Compiler::getElseIfDataStack().top();
int jmpIndex = elseIfData._jmpIndex;
int codeIndex = elseIfData._codeLineIndex;
Expression::CCType ccType = elseIfData._ccType;
std::stack<Compiler::EndIfData> endIfData = elseIfData._endIfData; // add to the list of endif label jumps per if/endif pair
Compiler::getElseIfDataStack().pop();
// Jump to endif for previous BASIC line
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("CALLI", "CALLI_JUMP", false, codeLineIndex - 1);
endIfData.push({int(Compiler::getCodeLines()[codeLineIndex - 1]._vasm.size()) - 1, codeLineIndex - 1, ccType});
}
else
{
// Validator checks for invalid page jumps
if(ccType == Expression::FastCC)
{
Compiler::emitVcpuAsm("BRA", "BRA_JUMP", false, codeLineIndex - 1);
endIfData.push({int(Compiler::getCodeLines()[codeLineIndex - 1]._vasm.size()) - 1, codeLineIndex - 1, ccType});
}
else
{
Compiler::emitVcpuAsm("LDWI", "LDWI_JUMP", false, codeLineIndex - 1);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false, codeLineIndex - 1);
endIfData.push({int(Compiler::getCodeLines()[codeLineIndex - 1]._vasm.size()) - 2, codeLineIndex - 1, ccType});
}
}
// Create label on next line of vasm code
Compiler::setNextInternalLabel("_else_" + Expression::wordToHexString(Compiler::getVasmPC()));
std::string nextInternalLabel = Compiler::getNextInternalLabel() + " " + std::to_string(Compiler::getNextJumpFalseUniqueId());
// Update if's or elseif's jump to this new label
Compiler::VasmLine* vasm = &Compiler::getCodeLines()[codeIndex]._vasm[jmpIndex];
switch(ccType)
{
case Expression::BooleanCC: vasm->_code = "JumpFalse" + std::string(OPCODE_TRUNC_SIZE - (sizeof("JumpFalse")-1), ' ') + nextInternalLabel; break;
case Expression::NormalCC: addLabelToJumpCC(Compiler::getCodeLines()[codeIndex]._vasm, nextInternalLabel); break;
case Expression::FastCC: addLabelToJumpCC(Compiler::getCodeLines()[codeIndex]._vasm, Compiler::getNextInternalLabel()); break;
default: break;
}
Compiler::getElseIfDataStack().push({jmpIndex, nextInternalLabel, codeIndex, Compiler::ElseBlock, ccType, endIfData});
return true;
}
bool ENDIF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
if(codeLine._tokens.size() != 1)
{
fprintf(stderr, "Keywords::ENDIF() : '%s:%d' : syntax error, wrong number of tokens : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Check stack for this IF ELSE ENDIF block
if(Compiler::getElseIfDataStack().empty())
{
fprintf(stderr, "Keywords::ENDIF() : '%s:%d' : syntax error, missing IF statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::ElseIfData elseIfData = Compiler::getElseIfDataStack().top();
int jmpIndex = elseIfData._jmpIndex;
int codeIndex = elseIfData._codeLineIndex;
Compiler::IfElseEndType ifElseEndType = elseIfData._ifElseEndType;
Expression::CCType ccType = elseIfData._ccType;
std::stack<Compiler::EndIfData>& endIfData = elseIfData._endIfData; // get the list of complete endif label jumps per if/endif pair
Compiler::getElseIfDataStack().pop();
// Create label on next line of vasm code
Compiler::setNextInternalLabel("_endif_" + Expression::wordToHexString(Compiler::getVasmPC()));
std::string nextInternalLabel = Compiler::getNextInternalLabel() + " " + std::to_string(Compiler::getNextJumpFalseUniqueId());
// Update if's/elseif's jump to this new label
if(ifElseEndType == Compiler::IfBlock || ifElseEndType == Compiler::ElseIfBlock)
{
Compiler::VasmLine* vasm = &Compiler::getCodeLines()[codeIndex]._vasm[jmpIndex];
switch(ccType)
{
case Expression::BooleanCC: vasm->_code = "JumpFalse" + std::string(OPCODE_TRUNC_SIZE - (sizeof("JumpFalse")-1), ' ') + nextInternalLabel; break;
case Expression::NormalCC: addLabelToJumpCC(Compiler::getCodeLines()[codeIndex]._vasm, nextInternalLabel); break;
case Expression::FastCC: addLabelToJumpCC(Compiler::getCodeLines()[codeIndex]._vasm, Compiler::getNextInternalLabel()); break;
default: break;
}
}
// Traverse entire list of endif label jumps per if/endif pair and update elseif's and/or else's jumps to endif label
while(!endIfData.empty())
{
codeIndex = endIfData.top()._codeLineIndex;
jmpIndex = endIfData.top()._jmpIndex;
ccType = endIfData.top()._ccType;
Compiler::VasmLine* vasm = &Compiler::getCodeLines()[codeIndex]._vasm[jmpIndex];
switch(ccType)
{
case Expression::BooleanCC:
{
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
vasm->_code = "CALLI" + std::string(OPCODE_TRUNC_SIZE - (sizeof("CALLI")-1), ' ') + Compiler::getNextInternalLabel();
}
else
{
vasm->_code = "LDWI" + std::string(OPCODE_TRUNC_SIZE - (sizeof("LDWI")-1), ' ') + Compiler::getNextInternalLabel();
}
}
break;
case Expression::NormalCC: addLabelToJump(Compiler::getCodeLines()[codeIndex]._vasm, Compiler::getNextInternalLabel()); break;
case Expression::FastCC: addLabelToJump(Compiler::getCodeLines()[codeIndex]._vasm, Compiler::getNextInternalLabel()); break;
default: break;
}
endIfData.pop();
}
return true;
}
bool WHILE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineStart);
Compiler::setNextInternalLabel("_while_" + Expression::wordToHexString(Compiler::getVasmPC()));
Compiler::getWhileWendDataStack().push({0, Compiler::getNextInternalLabel(), codeLineIndex, Expression::BooleanCC});
// Condition
Expression::Numeric condition;
std::string conditionToken = codeLine._code.substr(foundPos);
if(Compiler::parseExpression(codeLineIndex, conditionToken, condition) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::WHILE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, conditionToken.c_str(), codeLine._text.c_str());
return false;
}
if(condition._ccType == Expression::BooleanCC) Compiler::emitVcpuAsm("%JumpFalse", "", false); // Boolean condition requires this extra check
Compiler::getWhileWendDataStack().top()._jmpIndex = int(Compiler::getCodeLines()[codeLineIndex]._vasm.size()) - 1;
Compiler::getWhileWendDataStack().top()._ccType = condition._ccType;
return true;
}
bool WEND(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
// Pop stack for this WHILE loop
if(Compiler::getWhileWendDataStack().empty())
{
fprintf(stderr, "Keywords::WEND() : '%s:%d' : syntax error, missing WHILE statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::WhileWendData whileWendData = Compiler::getWhileWendDataStack().top();
Compiler::getWhileWendDataStack().pop();
// Branch to WHILE and check condition again
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("CALLI", whileWendData._labelName, false);
}
else
{
// There are no checks to see if this BRA's destination is in the same page, programmer discretion required when using this feature
if(whileWendData._ccType == Expression::FastCC)
{
Compiler::emitVcpuAsm("BRA", whileWendData._labelName, false);
}
else
{
Compiler::emitVcpuAsm("LDWI", whileWendData._labelName, false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
}
// Branch if condition false to instruction after WEND
Compiler::setNextInternalLabel("_wend_" + Expression::wordToHexString(Compiler::getVasmPC()));
Compiler::VasmLine* vasm = &Compiler::getCodeLines()[whileWendData._codeLineIndex]._vasm[whileWendData._jmpIndex];
switch(whileWendData._ccType)
{
case Expression::BooleanCC:
{
vasm->_code = "JumpFalse" + std::string(OPCODE_TRUNC_SIZE - (sizeof("JumpFalse")-1), ' ') + Compiler::getNextInternalLabel() + " " +
std::to_string(Compiler::getNextJumpFalseUniqueId());
}
break;
case Expression::NormalCC:
{
addLabelToJumpCC(Compiler::getCodeLines()[whileWendData._codeLineIndex]._vasm, Compiler::getNextInternalLabel() + " " + std::to_string(Compiler::getNextJumpFalseUniqueId()));
}
break;
case Expression::FastCC:
{
addLabelToJumpCC(Compiler::getCodeLines()[whileWendData._codeLineIndex]._vasm, Compiler::getNextInternalLabel());
}
break;
default: break;
}
return true;
}
bool REPEAT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
Compiler::setNextInternalLabel("_repeat_" + Expression::wordToHexString(Compiler::getVasmPC()));
Compiler::getRepeatUntilDataStack().push({Compiler::getNextInternalLabel(), codeLineIndex});
return true;
}
bool UNTIL(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Pop stack for this REPEAT loop
if(Compiler::getRepeatUntilDataStack().empty())
{
fprintf(stderr, "Keywords::UNTIL() : '%s:%d' : syntax error, missing REPEAT statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::RepeatUntilData repeatUntilData = Compiler::getRepeatUntilDataStack().top();
Compiler::getRepeatUntilDataStack().pop();
// Condition
Expression::Numeric condition;
std::string conditionToken = codeLine._code.substr(foundPos);
if(Compiler::parseExpression(codeLineIndex, conditionToken, condition) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::UNTIL() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, conditionToken.c_str(), codeLine._text.c_str());
return false;
}
// Branch if condition false to instruction after REPEAT
switch(condition._ccType)
{
case Expression::BooleanCC: Compiler::emitVcpuAsm("%JumpFalse", repeatUntilData._labelName + " " + std::to_string(Compiler::getNextJumpFalseUniqueId()), false); break;
case Expression::NormalCC: addLabelToJumpCC(Compiler::getCodeLines()[codeLineIndex]._vasm, repeatUntilData._labelName + " " + std::to_string(Compiler::getNextJumpFalseUniqueId())); break;
case Expression::FastCC: addLabelToJumpCC(Compiler::getCodeLines()[codeLineIndex]._vasm, repeatUntilData._labelName); break;
default: break;
}
return true;
}
bool FOREVER(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(codeLineIndex);
// Pop stack for this REPEAT loop
if(Compiler::getRepeatUntilDataStack().empty())
{
fprintf(stderr, "Keywords::FOREVER() : '%s:%d' : syntax error, missing REPEAT statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::RepeatUntilData repeatUntilData = Compiler::getRepeatUntilDataStack().top();
Compiler::getRepeatUntilDataStack().pop();
std::string gotoLabel = repeatUntilData._labelName;
// Check for optimised branch
bool useBRA = (codeLine._tokens[tokenIndex][0] == '&') ? true : false;
// Within same page, (validation check on same page branch may fail after outputCode(), user will be warned)
if(useBRA)
{
Compiler::emitVcpuAsm("BRA", repeatUntilData._labelName, false);
}
// Long jump
else
{
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("CALLI", repeatUntilData._labelName, false);
}
else
{
Compiler::emitVcpuAsm("LDWI", repeatUntilData._labelName, false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
}
return true;
}
bool AS(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLineIndex);
UNREFERENCED_PARAM(codeLine);
return true;
}
bool typeHelper(const std::string& input, Compiler::CodeLine& codeLine, int codeLineStart, std::string& name, uint16_t& address, Compiler::TypeVarType& varType, int& paramNum)
{
name = input;
address = 0x0000;
Expression::stripWhitespace(name);
std::string token = input;
Expression::strToUpper(token);
size_t dimPos = token.find("DIM ");
if(dimPos == std::string::npos) return true;
size_t lbra, rbra;
token = input.substr(dimPos + 3);
Expression::stripWhitespace(token);
if(Expression::findMatchingBrackets(token, 0, lbra, rbra, '(', name, paramNum))
{
// Byte
if(varType == Compiler::Byte && (paramNum >= 1 && paramNum <= 3))
{
varType = Compiler::TypeVarType(Compiler::ArrayB + paramNum - 1);
}
// Word
if(varType == Compiler::Word && (paramNum >= 1 && paramNum <= 3))
{
varType = Compiler::TypeVarType(Compiler::ArrayW + paramNum - 1);
}
// String
else if(varType == Compiler::String && paramNum == 1)
{
varType = Compiler::ArrayS;
}
else
{
fprintf(stderr, "Keywords::TYPE() : '%s:%d' : syntax error, 'TYPE' var array does not have the correct number of dimensions : %s'\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
}
return true;
}
bool TYPE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ' ', true);
if(tokens.size() < 3 || tokens[1] != "=")
{
fprintf(stderr, "Keywords::TYPE() : '%s:%d' : syntax error, 'TYPE' requires the following format 'TYPE <NAME> = <varType1>, ... <varTypeN>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Type name
std::string typeName = tokens[0];
Expression::stripWhitespace(typeName);
std::map<std::string, Compiler::TypeData>& typeDatas = Compiler::getTypeDatas();
if(typeDatas.find(typeName) != typeDatas.end())
{
fprintf(stderr, "Keywords::TYPE() : '%s:%d' : type '%s' already exists : %s\n", codeLine._moduleName.c_str(), codeLineStart, typeName.c_str(), codeLine._text.c_str());
return false;
}
// Variables
size_t equals = codeLine._code.find_first_of('=');
std::string vars = codeLine._code.substr(equals + 1);
tokens = Expression::tokenise(vars, ',', true);
if(tokens.size() == 0)
{
fprintf(stderr, "Keywords::TYPE() : '%s:%d' : missing variable types, 'TYPE' requires the following format 'TYPE <NAME> = <varType1>, ... <varTypeN>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Type data
Compiler::TypeData typeData;
for(int i=0; i<int(tokens.size()); i++)
{
int paramNum = 0;
std::string varName;
uint16_t varAddr = 0x0000;
Compiler::TypeVarType varType;
// Byte
if(tokens[i].find('%') != std::string::npos)
{
varType = Compiler::Byte;
if(!typeHelper(tokens[i], codeLine, codeLineStart, varName, varAddr, varType, paramNum)) return false;
}
// String
else if(tokens[i].find('$') != std::string::npos)
{
varType = Compiler::String;
if(!typeHelper(tokens[i], codeLine, codeLineStart, varName, varAddr, varType, paramNum)) return false;
}
// Word
else
{
varType = Compiler::Word;
if(!typeHelper(tokens[i], codeLine, codeLineStart, varName, varAddr, varType, paramNum)) return false;
}
if(typeData._vars.find(varName) != typeData._vars.end())
{
fprintf(stderr, "Keywords::TYPE() : '%s:%d' : var '%s' already exists : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
typeData._vars[varName] = {varAddr, varType};
}
typeDatas[typeName] = typeData;
//// Constant string array
//std::string token = tokens[0];
//Expression::strToUpper(token);
//size_t dimPos = token.find("DIM ");
//if(dimPos != std::string::npos)
//{
// size_t strPos = token.find("$", dimPos + 3);
// if(strPos != std::string::npos)
// {
// size_t lbra, rbra;
// if(Expression::findMatchingBrackets(token, strPos + 1, lbra, rbra))
// {
// _constDimStrArray = true;
// return DIM(codeLine, codeLineIndex, codeLineStart, tokenIndex, foundPos + dimPos + 3, result);
// }
// }
//}
return true;
}
bool callHelper(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, std::string& token, uint16_t localVarsAddr)
{
UNREFERENCED_PARAM(codeLine);
// If valid expression
Expression::Numeric numeric;
if(!Expression::parse(token, codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::CALL() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(numeric._varType == Expression::Number)
{
int16_t value = int16_t(std::lround(numeric._value));
(value >= 0 && value <= 255) ? Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(uint8_t(value)), false) : Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(value), false);
}
else if(numeric._varType == Expression::IntVar16)
{
switch(numeric._int16Byte)
{
case Expression::Int16Low: Compiler::emitVcpuAsm("LD", "_" + Compiler::getIntegerVars()[numeric._index]._name, false); break;
case Expression::Int16High: Compiler::emitVcpuAsm("LD", "_" + Compiler::getIntegerVars()[numeric._index]._name + " + 1", false); break;
case Expression::Int16Both: Compiler::emitVcpuAsm("LDW", "_" + Compiler::getIntegerVars()[numeric._index]._name, false); break;
default: break;
}
}
else
{
Compiler::emitVcpuAsm("LDW", Expression::byteToHexString(uint8_t(Compiler::getTempVarStart())), false);
}
Compiler::emitVcpuAsm("STW", Expression::byteToHexString(uint8_t(localVarsAddr)), false);
return true;
}
bool CALL(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> callTokens = Expression::tokenise(codeLine._code.substr(foundPos + 1), ',', true);
if(callTokens.size() == 0)
{
fprintf(stderr, "Keywords::CALL() : '%s:%d' : syntax error, 'CALL' requires a 'NAME' and optional parameters, 'CALL <NAME>, <param0, param1, ... paramN>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Proc name
Expression::stripWhitespace(callTokens[0]);
std::string procName = callTokens[0];
// Params
int numParams = int(callTokens.size()) - 1;
uint16_t localVarsAddr = LOCAL_VAR_START;
if(callTokens.size() > 1)
{
for(int i=1; i<int(callTokens.size()); i++)
{
if(localVarsAddr >= TEMP_VAR_START)
{
fprintf(stderr, "Keywords::CALL() : '%s:%d' : syntax error, maximum number of parameters exceeded : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(!callHelper(codeLine, codeLineIndex, codeLineStart, callTokens[i], localVarsAddr))
{
return false;
}
localVarsAddr += 2;
}
}
// Save for later validation
Compiler::CallData callData = {numParams, codeLineIndex, procName};
Compiler::getCallDataMap()[procName] = callData;
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("LDWI", "_" + procName, false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
else
{
Compiler::emitVcpuAsm("CALLI", "_" + procName, false);
}
return true;
}
bool PROC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> procTokens = Expression::tokenise(codeLine._code.substr(foundPos + 1), ',', true);
if(procTokens.size() == 0)
{
fprintf(stderr, "Keywords::PROC() : '%s:%d' : syntax error, 'PROC' requires a 'NAME' and optional parameters, 'PROC <NAME>, <param0, param1, ... paramN>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(!Compiler::getProcDataStack().empty())
{
fprintf(stderr, "Keywords::PROC() : '%s:%d' : syntax error, 'PROC' can NOT be nested : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::ProcData procData;
uint16_t localVarsAddr = LOCAL_VAR_START;
// Proc name
Expression::stripWhitespace(procTokens[0]);
procData._name = procTokens[0];
procData._codeLineIndex = codeLineIndex;
procData._numParams = int(procTokens.size()) - 1;
// Params
if(procTokens.size() > 1)
{
for(int i=1; i<int(procTokens.size()); i++)
{
if(localVarsAddr >= TEMP_VAR_START)
{
fprintf(stderr, "Keywords::PROC() : '%s:%d' : syntax error, maximum number of parameters exceeded : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::stripWhitespace(procTokens[i]);
if(Expression::isVarNameValid(procTokens[i]) == Expression::Invalid)
{
fprintf(stderr, "Keywords::PROC() : '%s:%d' : syntax error, parameter types can only be integer : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
int localVarIndex = -1;
std::string localVarName = procData._name + "_" + procTokens[i];
Compiler::createProcIntVar(localVarName, 0, 0, codeLine, codeLineIndex, false, localVarsAddr, localVarIndex);
if(localVarIndex == -1)
{
fprintf(stderr, "Keywords::PROC() : '%s:%d' : can't create local integer var '%s'\n", codeLine._moduleName.c_str(), codeLineStart, localVarName.c_str());
return false;
}
// Accessing variables within a PROC requires a translation from local var name to source var name
procData._localVarNameMap[procTokens[i]] = localVarName;
localVarsAddr += 2;
}
}
Compiler::getProcDataStack().push(procData);
Compiler::getProcDataMap()[procData._name] = procData;
Compiler::emitVcpuAsm("PUSH", "", false, codeLineIndex, "_" + procData._name);
return true;
}
bool ENDPROC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
if(Compiler::getProcDataStack().empty())
{
fprintf(stderr, "Keywords::ENDPROC() : '%s:%d' : syntax error, missing PROC statement : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(Compiler::getProcDataStack().size() != 1)
{
fprintf(stderr, "Keywords::ENDPROC() : '%s:%d' : syntax error, 'PROC' can NOT be nested : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Pop stack for current PROC
Compiler::getProcDataStack().pop();
Compiler::emitVcpuAsm("%Return", "", false);
return true;
}
bool LOCAL(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> localTokens = Expression::tokenise(codeLine._code.substr(foundPos + 1), ',', true);
if(localTokens.size() < 1)
{
fprintf(stderr, "Keywords::LOCAL() : '%s:%d' : syntax error, 'LOCAL' requires at least one '<VAR>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(Compiler::getProcDataStack().empty())
{
fprintf(stderr, "Keywords::LOCAL() : '%s:%d' : syntax error, 'LOCAL' can only be used within a 'PROC/ENDPROC' pair : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(Compiler::getProcDataStack().size() != 1)
{
fprintf(stderr, "Keywords::LOCAL() : '%s:%d' : syntax error, 'LOCAL' can NOT be used in nested 'PROC's' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::ProcData procData = Compiler::getProcDataStack().top();
procData._numLocals = int(localTokens.size());
uint16_t localVarsAddr = LOCAL_VAR_START + uint16_t(procData._numParams)*2;
// Local vars
for(int i=0; i<int(localTokens.size()); i++)
{
if(localVarsAddr >= TEMP_VAR_START)
{
fprintf(stderr, "Keywords::LOCAL() : '%s:%d' : error, maximum number of local vars exceeded : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::stripWhitespace(localTokens[i]);
if(Expression::isVarNameValid(localTokens[i]) == Expression::Invalid)
{
fprintf(stderr, "Keywords::LOCAL() : '%s:%d' : syntax error, local var types can only be integer : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
int localVarIndex = -1;
std::string localVarName = procData._name + "_" + localTokens[i];
Compiler::createProcIntVar(localVarName, 0, 0, codeLine, codeLineIndex, false, localVarsAddr, localVarIndex);
if(localVarIndex == -1)
{
fprintf(stderr, "Keywords::LOCAL() : '%s:%d' : couldn't create local integer var '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, localVarName.c_str(), codeLine._text.c_str());
return false;
}
// Accessing variables within a PROC requires a translation from local var name to source var name
procData._localVarNameMap[localTokens[i]] = localVarName;
localVarsAddr += 2;
}
Compiler::getProcDataStack().top() = procData;
Compiler::getProcDataMap()[procData._name] = procData;
return true;
}
bool CONST(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), '=', true);
if(tokens.size() != 2)
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error, use CONST a=50 or CONST a$=\"doggy\" or const dim arr$(2) = \"One\", \"Two\", \"Three\" : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Constant string array, (only string arrays can be constant, byte and integer arrays are excluded)
std::string token = tokens[0];
Expression::strToUpper(token);
size_t dimPos = token.find("DIM ");
if(dimPos != std::string::npos)
{
size_t strPos = token.find("$", dimPos + 3);
if(strPos != std::string::npos)
{
size_t lbra, rbra;
if(Expression::findMatchingBrackets(token, strPos + 1, lbra, rbra))
{
_constDimStrArray = true;
return DIM(codeLine, codeLineIndex, codeLineStart, tokenIndex, foundPos + dimPos + 3, result);
}
}
}
// Variable string array
Expression::stripWhitespace(tokens[0]);
if(Expression::isVarNameValid(tokens[0]) == Expression::Invalid && Expression::isStrNameValid(tokens[0]) == Expression::Invalid)
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error, name MUST contain only alphanumerics and '$' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// String
if(Expression::isStrNameValid(tokens[0]) == Expression::Variable)
{
// Strip whitespace
Expression::stripNonStringWhitespace(tokens[1]);
if(Expression::isStringValid(tokens[1]))
{
uint16_t address;
std::string internalName;
// Strip quotes
tokens[1].erase(0, 1);
tokens[1].erase(tokens[1].size()-1, 1);
// Don't count escape char '\'
int escCount = 0;
int strLength = int(tokens[1].size());
for(int i=0; i<strLength; i++)
{
if(tokens[1][i] == '\\') escCount++;
}
strLength -= escCount;
Compiler::getOrCreateString(codeLine, codeLineIndex, tokens[1], internalName, address);
Compiler::getConstants().push_back({uint8_t(strLength), 0, address, tokens[1], tokens[0], internalName, Compiler::ConstStr});
}
// String keyword
else
{
size_t lbra, rbra;
if(!Expression::findMatchingBrackets(tokens[1], 0, lbra, rbra))
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error, invalid string or keyword : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string funcToken = tokens[1].substr(0, lbra);
std::string paramToken = tokens[1].substr(lbra + 1, rbra - (lbra + 1));
Expression::strToUpper(funcToken);
if(Functions::getStringFunctions().find(funcToken) == Functions::getStringFunctions().end())
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error, invalid string or keyword : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
int16_t param;
if(!Expression::stringToI16(paramToken, param))
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error, keyword param must be a constant number : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Create constant string
int index;
uint8_t length = 0;
uint16_t address = 0x0000;
if(funcToken == "CHR$") {length = 1; address = Compiler::getOrCreateConstString(Compiler::StrChar, param, index);}
else if(funcToken == "HEX$") {length = 4; address = Compiler::getOrCreateConstString(Compiler::StrHex, param, index);}
// Create constant
if(address)
{
std::string internalName = Compiler::getStringVars().back()._name;
Compiler::getConstants().push_back({length, 0, address, Compiler::getStringVars().back()._text, tokens[0], internalName, Compiler::ConstStr});
}
}
}
// Integer
else
{
Expression::Numeric numeric(true); // true = allow static init
if(!Expression::parse(tokens[1], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(tokens[1].size() == 0 || !numeric._isValid || numeric._varType == Expression::TmpVar || numeric._varType == Expression::IntVar16)
{
fprintf(stderr, "Keywords::CONST() : '%s:%d' : syntax error, invalid constant expression : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::getConstants().push_back({2, int16_t(std::lround(numeric._value)), 0x0000, "", tokens[0], "_" + tokens[0], Compiler::ConstInt16});
}
return true;
}
bool initDIM(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, std::string& varName, int arrSizeTotal, int16_t& intInit, std::vector<int16_t>& intInits, bool& isInit)
{
std::string constName = varName;
if(Compiler::findConst(constName) >= 0)
{
fprintf(stderr, "Keywords::initDIM() : '%s:%d' : const '%s' already exists : %s\n", codeLine._moduleName.c_str(), codeLineStart, varName.c_str(), codeLine._text.c_str());
return false;
}
if(Compiler::findVar(varName) >= 0)
{
fprintf(stderr, "Keywords::initDIM() : '%s:%d' : var '%s' already exists : %s\n", codeLine._moduleName.c_str(), codeLineStart, varName.c_str(), codeLine._text.c_str());
return false;
}
// Optional array int init values
isInit = false;
size_t equalsPos = codeLine._code.find("=");
if(equalsPos != std::string::npos)
{
std::string initText = codeLine._code.substr(equalsPos + 1);
Expression::stripWhitespace(initText);
std::vector<std::string> initTokens = Expression::tokenise(initText, ',', true);
if(initTokens.size() == 0)
{
fprintf(stderr, "Keywords::initDIM() : '%s:%d' : initial value must be a constant, found '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, initText.c_str(), codeLine._text.c_str());
return false;
}
else if(initTokens.size() == 1)
{
std::string operand;
Expression::Numeric numeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, initTokens[0], operand, numeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::initDIM() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, initTokens[0].c_str(), codeLine._text.c_str());
return false;
}
intInit = int16_t(std::lround(numeric._value));
}
else if(int(initTokens.size()) > arrSizeTotal)
{
fprintf(stderr, "Keywords::initDIM() : '%s:%d' : too many initialisation values for size of array, found %d for a size of %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(initTokens.size()), arrSizeTotal, codeLine._text.c_str());
return false;
}
// Multiple initialisation values, (if there are less init values than array size, then array is padded with last init value)
std::string operand;
intInits.resize(initTokens.size());
std::vector<Expression::Numeric> funcParams(initTokens.size(), Expression::Numeric(true)); // true = allow static init
for(int i=0; i<int(initTokens.size()); i++)
{
if(Compiler::parseStaticExpression(codeLineIndex, initTokens[i], operand, funcParams[i]) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::initDIM() : '%s:%d' : bad initialiser %s at index %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), i, codeLine._text.c_str());
return false;
}
intInits[i] = int16_t(std::lround(funcParams[i]._value));
}
intInit = intInits.back();
isInit = true;
}
return true;
}
bool allocDIM(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, uint16_t& address, Compiler::VarType& varType, std::vector<uint16_t>& arrLut, std::vector<uint16_t>& arrSizes, std::vector<std::vector<uint16_t>>& arrAddrs)
{
UNREFERENCED_PARAM(codeLineIndex);
int intSize = 0;
switch(varType)
{
case Compiler::Var1Arr8: intSize = 1; break;
case Compiler::Var1Arr16: intSize = 2; break;
default: fprintf(stderr, "Keywords::allocDIM() : '%s:%d' : unknown var type : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
break;
}
// Allocate memory for k * j * i of intSize byte values
int iSizeBytes = arrSizes[2] * intSize;
for(int k=0; k<arrSizes[0]; k++)
{
for(int j=0; j<arrSizes[1]; j++)
{
if(!Memory::getFreeRAM(Memory::FitDescending, iSizeBytes, USER_CODE_START, Compiler::getArraysStart(), arrAddrs[k][j], false)) // arrays do not need to be contained within pages
{
fprintf(stderr, "Keywords::allocDIM() : '%s:%d' : not enough RAM for int array of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, iSizeBytes, codeLine._text.c_str());
return false;
}
}
}
// 1D array
if(arrSizes[0] == 1 && arrSizes[1] == 1)
{
address = arrAddrs[0][0];
varType = (varType == Compiler::Var1Arr8) ? Compiler::Var1Arr8 : Compiler::Var1Arr16;
}
// 2D array
else if(arrSizes[0] == 1)
{
int jSizeBytes = arrSizes[1] * 2;
if(!Memory::getFreeRAM(Memory::FitDescending, jSizeBytes, USER_CODE_START, Compiler::getArraysStart(), address, false)) // arrays do not need to be contained within pages
{
fprintf(stderr, "Keywords::allocDIM() : '%s:%d' : not enough RAM for int array of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, jSizeBytes, codeLine._text.c_str());
return false;
}
address = address;
varType = (varType == Compiler::Var1Arr8) ? Compiler::Var2Arr8 : Compiler::Var2Arr16;
// Enable system internal sub intialiser and mark system internal sub to be loaded, (init functions are not needed for ROMv5a and higher as CALLI is able to directly CALL them)
if(intSize == 1)
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a) Compiler::enableSysInitFunc("Init8Array2d");
Linker::setInternalSubToLoad("convert8Arr2d");
}
else if(intSize == 2)
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a) Compiler::enableSysInitFunc("Init16Array2d");
Linker::setInternalSubToLoad("convert16Arr2d");
}
}
// 3D array
else
{
int jSizeBytes = arrSizes[1] * 2;
for(int k=0; k<arrSizes[0]; k++)
{
if(!Memory::getFreeRAM(Memory::FitDescending, jSizeBytes, USER_CODE_START, Compiler::getArraysStart(), arrLut[k], false)) // arrays do not need to be contained within pages
{
fprintf(stderr, "Keywords::allocDIM() : '%s:%d' : not enough RAM for int array of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, jSizeBytes, codeLine._text.c_str());
return false;
}
}
int kSizeBytes = arrSizes[2] * 2;
if(!Memory::getFreeRAM(Memory::FitDescending, kSizeBytes, USER_CODE_START, Compiler::getArraysStart(), address, false)) // arrays do not need to be contained within pages
{
fprintf(stderr, "Keywords::allocDIM() : '%s:%d' : not enough RAM for int array of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, kSizeBytes, codeLine._text.c_str());
return false;
}
address = address;
varType = (varType == Compiler::Var1Arr8) ? Compiler::Var3Arr8 : Compiler::Var3Arr16;
// Enable system internal sub intialiser and mark system internal sub to be loaded, (init functions are not needed for ROMv5a and higher as CALLI is able to directly CALL them)
if(intSize == 1)
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a) Compiler::enableSysInitFunc("Init8Array3d");
Linker::setInternalSubToLoad("convert8Arr3d");
}
else if(intSize == 2)
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a) Compiler::enableSysInitFunc("Init16Array3d");
Linker::setInternalSubToLoad("convert16Arr3d");
}
}
return true;
}
bool DIM(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
size_t lbra, rbra;
if(!Expression::findMatchingBrackets(codeLine._code, foundPos, lbra, rbra))
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : syntax error in DIM statement, must be DIM <var>(<n1>, <optional n2>) : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Dimensions
std::vector<uint16_t> arrSizes;
std::vector<std::string> sizeTokens = Expression::tokenise(codeLine._code.substr(lbra + 1, rbra - (lbra + 1)), ',', true);
if(sizeTokens.size() > MAX_ARRAY_DIMS)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : maximum of %d dimensions, found %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, MAX_ARRAY_DIMS, int(sizeTokens.size()), codeLine._text.c_str());
return false;
}
int arrSizeTotal = 1;
for(int i=0; i<int(sizeTokens.size()); i++)
{
int varIndex = -1, constIndex = -1, strIndex = -1;
std::string sizeToken = sizeTokens[i];
Expression::stripWhitespace(sizeToken);
uint32_t expressionType = Compiler::isExpression(sizeToken, varIndex, constIndex, strIndex);
// Constant dimension
if((expressionType & Expression::HasIntConsts) && constIndex > -1)
{
std::string operand;
Expression::Numeric numeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, sizeToken, operand, numeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, sizeToken.c_str(), codeLine._text.c_str());
return false;
}
int16_t data = int16_t(std::lround(numeric._value));
arrSizes.push_back(data);
}
// Literal dimension
else
{
uint16_t arrSize = 0;
if(!Expression::stringToU16(sizeToken, arrSize) || arrSize <= 0)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : array dimensions must be a positive constant, found '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, sizeToken.c_str(), codeLine._text.c_str());
return false;
}
arrSizes.push_back(arrSize);
}
// Most BASIC's declared 0 to n elements, hence size = n + 1
if(!Compiler::getArrayIndiciesOne())
{
arrSizes.back()++;
}
arrSizeTotal *= arrSizes.back();
}
std::string varName = codeLine._code.substr(foundPos, lbra - foundPos);
Expression::stripWhitespace(varName);
int16_t intInit = 0;
bool isIntInit = false;
std::vector<int16_t> intInits;
std::vector<std::string> strInits;
Compiler::VarType varType = Compiler::Var1Arr16;
// Str array
bool isStrInit = false;
if(Expression::isStrNameValid(varName))
{
if(Compiler::findStr(varName) >= 0)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : str %s already exists : %s\n", codeLine._moduleName.c_str(), codeLineStart, varName.c_str(), codeLine._text.c_str());
return false;
}
if(arrSizes.size() != 1)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : require 1 string dimension, found %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(arrSizes.size()), codeLine._text.c_str());
return false;
}
// String array 2nd dimension is always USER_STR_SIZE, (actual arrSizes data structure looks like this, [n][USER_STR_SIZE])
arrSizes.push_back(USER_STR_SIZE);
// Optional array str init values
size_t equalsPos = codeLine._code.find("=");
if(equalsPos != std::string::npos)
{
std::string initText = codeLine._code.substr(equalsPos + 1);
Expression::stripNonStringWhitespace(initText);
std::vector<std::string> initTokens = Expression::tokenise(initText, ',', true);
if(initTokens.size() == 0)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : initial value must be a string, found '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, initText.c_str(), codeLine._text.c_str());
return false;
}
else if(int(initTokens.size()) > arrSizes[0])
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : too many initialisation strings for size of array, found %d for a size of %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(initTokens.size()), arrSizes[0], codeLine._text.c_str());
return false;
}
// Multiple initialisation values, (if there are less strings than array size, then array is padded with last string)
for(int i=0; i<int(initTokens.size()); i++)
{
if(initTokens[i].size() - 2 > USER_STR_SIZE)
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : initialisation string '%s' is larger than %d chars : %s\n", codeLine._moduleName.c_str(), codeLineStart, initTokens[i].c_str(), USER_STR_SIZE, codeLine._text.c_str());
return false;
}
if(!Expression::isStringValid(initTokens[i]))
{
fprintf(stderr, "Keywords::DIM() : '%s:%d' : invalid string initialiser, found '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, initTokens[i].c_str(), codeLine._text.c_str());
return false;
}
// Strip quotes
initTokens[i].erase(0, 1);
initTokens[i].erase(initTokens[i].size() - 1, 1);
strInits.push_back(initTokens[i]);
}
isStrInit = true;
}
varType = Compiler::VarStr2;
}
// Int8 array
else if(varName.back() == '%')
{
// % is only used within DIM keyword
varName.erase(varName.size()-1, 1);
if(!initDIM(codeLine, codeLineIndex, codeLineStart, varName, arrSizeTotal, intInit, intInits, isIntInit)) return false;
varType = Compiler::Var1Arr8;
}
// Int16 array
else
{
if(!initDIM(codeLine, codeLineIndex, codeLineStart, varName, arrSizeTotal, intInit, intInits, isIntInit)) return false;
varType = Compiler::Var1Arr16;
}
// Represent 1 or 2 dimensional arrays as a 3 dimensional array with dimensions of 1 for the missing dimensions
while(arrSizes.size() != 3)
{
arrSizes.insert(arrSizes.begin(), 1);
}
// Array lut
std::vector<uint16_t> arrLut;
arrLut.resize(arrSizes[0]);
// Array addresses
std::vector<std::vector<uint16_t>> arrAddrs;
arrAddrs.resize(arrSizes[0]);
for(int i=0; i<arrSizes[0]; i++)
{
arrAddrs[i].resize(arrSizes[1]);
}
uint16_t address = 0x0000;
switch(varType)
{
// Str arrays
case Compiler::VarStr2:
{
std::vector<uint16_t> strAddrs;
strAddrs.resize(arrSizes[1]);
if(_constDimStrArray)
{
// Constant array of strings
_constDimStrArray = false;
if(Compiler::createStringArray(codeLine, codeLineIndex, varName, 0, isStrInit, strInits, strAddrs) == -1) return false;
}
else
{
// Variable array of strings
if(Compiler::createStringArray(codeLine, codeLineIndex, varName, USER_STR_SIZE, isStrInit, strInits, strAddrs) == -1) return false;
}
}
break;
// Int8 arrays, (allocDIM returns var type based on arrSizes)
case Compiler::Var1Arr8:
{
int intIndex = -1;
if(!allocDIM(codeLine, codeLineIndex, codeLineStart, address, varType, arrLut, arrSizes, arrAddrs)) return false;
Compiler::createArrIntVar(varName, 0, intInit, codeLine, codeLineIndex, false, isIntInit, intIndex, varType, Compiler::Int8, address, arrSizes, intInits, arrAddrs, arrLut);
}
break;
// Int16 arrays, (allocDIM returns var type based on arrSizes)
case Compiler::Var1Arr16:
{
int intIndex = -1;
if(!allocDIM(codeLine, codeLineIndex, codeLineStart, address, varType, arrLut, arrSizes, arrAddrs)) return false;
Compiler::createArrIntVar(varName, 0, intInit, codeLine, codeLineIndex, false, isIntInit, intIndex, varType, Compiler::Int16, address, arrSizes, intInits, arrAddrs, arrLut);
}
break;
default: fprintf(stderr, "Keywords::DIM() : '%s:%d' : unknown array type %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, varType, codeLine._text.c_str());
return false;
}
return true;
}
// Not used, implemented as a function, (Compiler::userFunc())
bool FUNC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
size_t lbra, rbra;
std::string fnText = codeLine._expression;
Expression::strToUpper(fnText);
size_t fnPos = fnText.find("FUNC");
std::string funcText = codeLine._expression.substr(fnPos);
if(!Expression::findMatchingBrackets(funcText, 0, lbra, rbra))
{
fprintf(stderr, "Keywords::FUNC() : '%s:%d' : syntax error, invalid parenthesis in FN : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
funcText = funcText.substr(0, rbra + 1);
// Name
std::string name = funcText.substr(sizeof("FUNC")-1, lbra - (sizeof("FUNC")-1));
Expression::stripWhitespace(name);
if(Compiler::getDefFunctions().find(name) == Compiler::getDefFunctions().end())
{
fprintf(stderr, "Keywords::FUNC() : '%s:%d' : syntax error, FN %s can't be found : %s\n", codeLine._moduleName.c_str(), codeLineStart, name.c_str(), codeLine._text.c_str());
return false;
}
int varIndex = Compiler::findVar(name);
if(varIndex >= 0)
{
fprintf(stderr, "Keywords::FUNC() : '%s:%d' : syntax error, name collision with var %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, name.c_str(), codeLine._text.c_str());
return false;
}
// Params
std::vector<std::string> params = Expression::tokenise(funcText.substr(lbra + 1, rbra - (lbra + 1)), ',', true);
if(params.size() == 0)
{
fprintf(stderr, "Keywords::FUNC() : '%s:%d' : syntax error, need at least one parameter : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
int paramsSize = int(Compiler::getDefFunctions()[name]._params.size());
if(paramsSize != int(params.size()))
{
fprintf(stderr, "Keywords::FUNC() : '%s:%d' : syntax error, wrong number of parameters, expecting %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, paramsSize, codeLine._text.c_str());
return false;
}
std::string func = Compiler::getDefFunctions()[name]._function;
for(int i=0; i<int(params.size()); i++)
{
Expression::stripWhitespace(params[i]);
Expression::replaceText(func, Compiler::getDefFunctions()[name]._params[i], params[i]);
}
// Replace DEF FN with FUNC
Expression::replaceText(codeLine._code, funcText, func);
Expression::replaceText(codeLine._text, funcText, func);
Expression::replaceText(codeLine._expression, funcText, func);
Expression::replaceText(Compiler::getCodeLines()[codeLineIndex]._code, funcText, func);
Expression::replaceText(Compiler::getCodeLines()[codeLineIndex]._text, funcText, func);
Expression::replaceText(Compiler::getCodeLines()[codeLineIndex]._expression, funcText, func);
std::vector<size_t> offsets;
std::vector<std::string> tokens = Expression::tokeniseLineOffsets(Compiler::getCodeLines()[codeLineIndex]._code, " (),=", offsets);
codeLine._tokens = tokens;
codeLine._offsets = offsets;
Compiler::getCodeLines()[codeLineIndex]._tokens = tokens;
Compiler::getCodeLines()[codeLineIndex]._offsets = offsets;
return true;
}
bool createDEFFN(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, std::string& defFunc)
{
UNREFERENCED_PARAM(codeLineIndex);
size_t lbra, rbra;
if(!Expression::findMatchingBrackets(defFunc, 0, lbra, rbra))
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, invalid parenthesis : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Name
if(lbra == 0)
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, missing name : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string name = defFunc.substr(0, lbra);
Expression::stripWhitespace(name);
Expression::strToUpper(name);
int varIndex = Compiler::findVar(name);
if(varIndex >= 0)
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, name collision with var %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, name.c_str(), codeLine._text.c_str());
return false;
}
// Function
size_t equalsPos = defFunc.find("=", rbra + 1);
if(equalsPos == std::string::npos)
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, missing equals sign : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string function = defFunc.substr(equalsPos + 1);
Expression::trimWhitespace(function);
// Params
std::vector<std::string> params = Expression::tokenise(defFunc.substr(lbra + 1, rbra - (lbra + 1)), ',', true);
if(params.size() == 0)
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, need at least one parameter : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
for(int i=0; i<int(params.size()); i++)
{
Expression::stripWhitespace(params[i]);
if(function.find(params[i]) == std::string::npos)
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, parameter %s missing from function %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, params[i].c_str(), function.c_str(), codeLine._text.c_str());
return false;
}
}
Compiler::DefFunction defFunction = {name, function, params};
if(Compiler::getDefFunctions().find(name) != Compiler::getDefFunctions().end())
{
fprintf(stderr, "Keywords::functionDEF() : '%s:%d' : syntax error, DEF FN %s has been defined more than once : %s\n", codeLine._moduleName.c_str(), codeLineStart, name.c_str(), codeLine._text.c_str());
return false;
}
Compiler::getDefFunctions()[name] = defFunction;
return true;
}
bool DEF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::string defText = codeLine._code.substr(foundPos);
std::string fnText = defText;
Expression::strToUpper(fnText);
// FUNC
size_t defPos = std::string::npos;
if((defPos = fnText.find("FN")) != std::string::npos)
{
std::string defFunc = defText.substr(defPos + sizeof("FN") - 1);
return createDEFFN(codeLine, codeLineIndex, codeLineStart, defFunc);
}
// Equals
size_t equalsPos = codeLine._code.find("=");
if(equalsPos == std::string::npos)
{
// Integer var definition list
std::vector<std::string> varTokens = Expression::tokenise(defText, ',', true);
if(varTokens.size())
{
std::map<std::string, int> varMap;
for(int i=0; i<int(varTokens.size()); i++)
{
// Check for valid var name
Expression::stripWhitespace(varTokens[i]);
if(Expression::isVarNameValid(varTokens[i]) == Expression::Invalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in variable definition '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varTokens[i].c_str(), codeLine._text.c_str());
return false;
}
// Check for duplicate vars
if(varMap.find(varTokens[i]) != varMap.end())
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : duplicate variable definition '%s : %s'\n", codeLine._moduleName.c_str(), codeLineStart, varTokens[i].c_str(), codeLine._text.c_str());
return false;
}
// Create var, (no vASM code, i.e. uninitialised)
varMap[varTokens[i]] = i;
int varIndex = -1;
Compiler::createIntVar(varTokens[i], 0, 0, codeLine, codeLineIndex, false, varIndex);
}
return true;
}
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error, missing equals sign : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Address field
size_t typePos, lbra, rbra;
uint16_t address = 0;
bool foundAddress = false;
bool foundLutGenerator = false;
std::string addrText, operand;
std::vector<std::string> addrTokens;
Expression::Numeric addrNumeric(true); // true = allow static init
if(Expression::findMatchingBrackets(codeLine._code, foundPos, lbra, rbra))
{
// Check for LUT generator
addrText = codeLine._code.substr(lbra + 1, rbra - (lbra + 1));
addrTokens = Expression::tokenise(addrText, ',', true);
if(addrTokens.size() >= 4)
{
foundLutGenerator = true;
}
// Parse address field
if(addrTokens.size() == 0)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : address field does not exist, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrText.c_str(), codeLine._text.c_str());
return false;
}
if(Compiler::parseStaticExpression(codeLineIndex, addrTokens[0], operand, addrNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrTokens[0].c_str(), codeLine._text.c_str());
return false;
}
address = uint16_t(std::lround(addrNumeric._value));
typePos = lbra;
foundAddress = true;
}
else
{
typePos = equalsPos;
}
// Type field
std::string typeText = codeLine._code.substr(foundPos, typePos - foundPos);
Expression::stripWhitespace(typeText);
Expression::strToUpper(typeText);
if(typeText != "BYTE" && typeText != "WORD")
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : type field must be either BYTE or WORD, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, typeText.c_str(), codeLine._text.c_str());
return false;
}
// Address offset field
uint16_t addrOffset = 0;
if(addrTokens.size() == 2 && !foundLutGenerator)
{
Expression::Numeric offsetNumeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, addrTokens[1], operand, offsetNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrTokens[1].c_str(), codeLine._text.c_str());
return false;
}
addrOffset = uint16_t(std::lround(offsetNumeric._value));
}
// ************************************************************************************************************
// LUT generator
if(foundLutGenerator)
{
if(addrTokens.size() < 4 || addrTokens.size() > 6)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : LUT generator must have 4 to 6 parameters, '(ADDR, <VAR>, START, STOP, SIZE, <OFFSET>)', (<VAR> and <OFFSET> are optional), found %d : %s\n",
codeLine._moduleName.c_str(), codeLineStart, int(addrTokens.size()), codeLine._text.c_str());
return false;
}
for(int i=0; i<int(addrTokens.size()); i++) Expression::stripWhitespace(addrTokens[i]);
std::string lutGenerator = codeLine._code.substr(equalsPos + 1);
Expression::stripWhitespace(lutGenerator);
if(lutGenerator.size() == 0)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : LUT generator '%s' is invalid : %s\n", codeLine._moduleName.c_str(), codeLineStart, lutGenerator.c_str(), codeLine._text.c_str());
return false;
}
// Parse LUT generator variable
bool foundVar = false;
std::string lutGenVar;
size_t varPos = 0;
std::vector<size_t> varPositions;
if(addrTokens.size() >= 5)
{
lutGenVar = addrTokens[1];
if(Expression::isVarNameValid(lutGenVar) == Expression::Variable)
{
foundVar = true;
bool foundVarFirstTime = false;
for(;;)
{
varPos = lutGenerator.find(lutGenVar, varPos);
if(varPos == std::string::npos)
{
if(!foundVarFirstTime)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : LUT generator variable '%s' invalid : %s\n", codeLine._moduleName.c_str(), codeLineStart, lutGenVar.c_str(), codeLine._text.c_str());
return false;
}
// Found all occurenced of LUT generator variable
break;
}
lutGenerator.erase(varPos, lutGenVar.size());
varPositions.push_back(varPos);
foundVarFirstTime = true;
varPos++;
}
}
}
// Parse LUT generator parameters
int paramsOffset = (foundVar) ? 2 : 1;
std::vector<Expression::Numeric> lutGenParams = {Expression::Numeric(true), Expression::Numeric(true), Expression::Numeric(true)}; // true = allow static init
for(int i=0; i<int(lutGenParams.size()); i++)
{
if(Compiler::parseStaticExpression(codeLineIndex, addrTokens[i + paramsOffset], operand, lutGenParams[i]) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrTokens[i + paramsOffset].c_str(), codeLine._text.c_str());
return false;
}
}
if(lutGenParams[2]._value <= 0.0)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : LUT size must be greater than zero, found '%d' : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(lutGenParams[2]._value), codeLine._text.c_str());
return false;
}
// Parse LUT generator address offset, if it exists
if(addrTokens.size() == 6 || (addrTokens.size() == 5 && !foundVar))
{
Expression::Numeric offsetNumeric;
if(Compiler::parseStaticExpression(codeLineIndex, addrTokens[3 + paramsOffset], operand, offsetNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrTokens[3 + paramsOffset].c_str(), codeLine._text.c_str());
return false;
}
addrOffset = uint16_t(std::lround(offsetNumeric._value));
}
// Evaluate LUT generator
double start = lutGenParams[0]._value;
double end = lutGenParams[1]._value;
double count = fabs(lutGenParams[2]._value);
double step = (end - start) / count;
std::vector<int16_t> lutGenData;
for(double d=start; d<end; d+=step)
{
std::string var;
if(foundVar)
{
// Insert substitute values into var's positions
var = std::to_string(d);
for(int i=0; i<int(varPositions.size()); i++)
{
lutGenerator.insert(varPositions[i] + i*var.size(), var);
}
}
Expression::Numeric lutGenResult = Expression::Numeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, lutGenerator, operand, lutGenResult) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, lutGenerator.c_str(), codeLine._text.c_str());
return false;
}
lutGenData.push_back(int16_t(std::lround(lutGenResult._value)));
if(foundVar)
{
// Erase every occurence of substitute values
for(int i=0; i<int(varPositions.size()); i++)
{
lutGenerator.erase(varPositions[i], var.size());
}
}
}
// Allows for byte allocating and setting of interlaced memory, (audio memory is not allocated, but can still be set)
if(typeText == "BYTE")
{
uint16_t addr = address;
std::vector<uint8_t> dataBytes(int(count), 0);
for(int i=0; i<int(dataBytes.size()); i++)
{
dataBytes[i] = uint8_t(lutGenData[i]);
if(addrOffset != 0)
{
if(addr < RAM_AUDIO_START || addr > RAM_AUDIO_END)
{
if(!Memory::takeFreeRAM(addr, 1)) return false;
addr += addrOffset;
}
}
}
if(addrOffset == 0)
{
if(!Memory::takeFreeRAM(address, int(dataBytes.size()))) return false;
}
Compiler::getDefDataBytes().push_back({address, addrOffset, dataBytes});
}
// Allows for word allocating and setting of interlaced memory, (audio memory is not allocated, but can still be set)
else if(typeText == "WORD")
{
uint16_t addr = address;
std::vector<int16_t> dataWords(int(count), 0);
for(int i=0; i<int(dataWords.size()); i++)
{
dataWords[i] = int16_t(lutGenData[i]);
if(addrOffset != 0)
{
if(addr < RAM_AUDIO_START || addr > RAM_AUDIO_END)
{
if(!Memory::takeFreeRAM(addr, 2)) return false;
addr += addrOffset * 2;
}
}
}
if(addrOffset == 0)
{
if(!Memory::takeFreeRAM(address, int(dataWords.size()) * 2)) return false;
}
Compiler::getDefDataWords().push_back({address, addrOffset, dataWords});
}
return true;
}
// ************************************************************************************************************
// Data fields
std::vector<std::string> dataTokens = Expression::tokenise(codeLine._code.substr(equalsPos + 1), ',', true);
if(dataTokens.size() == 0)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error, require at least one data field : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// BYTE data
if(typeText == "BYTE")
{
// Parse byte fields
std::vector<uint8_t> dataBytes;
for(int i=0; i<int(dataTokens.size()); i++)
{
Expression::Numeric numeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, dataTokens[i], operand, numeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, dataTokens[i].c_str(), codeLine._text.c_str());
return false;
}
dataBytes.push_back(uint8_t(std::lround(numeric._value)));
}
// If no new address is found, then it is recalculated
if(!foundAddress)
{
addrOffset = Compiler::getDefDataBytes().back()._offset;
uint16_t offset = (addrOffset != 0) ? uint16_t(Compiler::getDefDataBytes().back()._data.size()) * addrOffset : uint16_t(Compiler::getDefDataBytes().back()._data.size());
address = Compiler::getDefDataBytes().back()._address + offset;
}
// No address offset so take memory as one chunk
if(addrOffset == 0)
{
if(address >= DEFAULT_EXEC_ADDRESS && !Memory::takeFreeRAM(address, int(dataBytes.size())))
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : memory error, byte chunk allocation at '0x%04x of size '%d' failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, address, int(dataBytes.size()), codeLine._text.c_str());
return false;
}
}
// Valid address offset, so take memory as individual bytes
else
{
uint16_t addr = address;
for(int i=0; i<int(dataBytes.size()); i++)
{
if(addr < RAM_AUDIO_START || addr > RAM_AUDIO_END)
{
if(!Memory::takeFreeRAM(addr, 1))
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : memory error, byte allocation at '0x%04x of size '1' failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, address, codeLine._text.c_str());
return false;
}
addr += addrOffset;
}
}
}
Compiler::getDefDataBytes().push_back({address, addrOffset, dataBytes});
}
// WORD data
else if(typeText == "WORD")
{
// Parse word fields
std::vector<int16_t> dataWords;
for(int i=0; i<int(dataTokens.size()); i++)
{
Expression::Numeric numeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, dataTokens[i], operand, numeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, dataTokens[i].c_str(), codeLine._text.c_str());
return false;
}
dataWords.push_back(int16_t(std::lround(numeric._value)));
}
// If no new address is found, then it is recalculated
if(!foundAddress)
{
addrOffset = Compiler::getDefDataWords().back()._offset;
uint16_t offset = (addrOffset != 0) ? uint16_t(Compiler::getDefDataWords().back()._data.size()) * addrOffset * 2 : uint16_t(Compiler::getDefDataWords().back()._data.size()) * 2;
address = Compiler::getDefDataWords().back()._address + offset;
}
// No address offset so take memory as one chunk
if(addrOffset == 0)
{
if(address >= DEFAULT_EXEC_ADDRESS && !Memory::takeFreeRAM(address, int(dataWords.size()) * 2))
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : memory error, word chunk allocation at '0x%04x of size '%d' failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, address, int(dataWords.size()) * 2, codeLine._text.c_str());
return false;
}
}
// Valid address offset, so take memory as individual words
else
{
uint16_t addr = address;
for(int i=0; i<int(dataWords.size()); i++)
{
if(addr < RAM_AUDIO_START || addr > RAM_AUDIO_END)
{
if(!Memory::takeFreeRAM(addr, 2))
{
fprintf(stderr, "Keywords::DEF() : '%s:%d' : memory error, word allocation at '0x%04x of size '2' failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, address, codeLine._text.c_str());
return false;
}
addr += addrOffset * 2;
}
}
}
Compiler::getDefDataWords().push_back({address, addrOffset, dataWords});
}
return true;
}
bool DATA(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Data fields
std::vector<std::string> dataTokens = Expression::tokenise(codeLine._code.substr(foundPos + 1), ',', true);
if(dataTokens.size() == 0)
{
fprintf(stderr, "Keywords::DATA() : '%s:%d' : syntax error, require at least one data field : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Parse data
std::string operand;
for(int i=0; i<int(dataTokens.size()); i++)
{
std::string dataToken = dataTokens[i];
Expression::stripNonStringWhitespace(dataToken);
if(Expression::isStringValid(dataToken))
{
// Strip quotes
dataToken.erase(0, 1);
dataToken.erase(dataToken.size()-1, 1);
// Add str to list
std::unique_ptr<Compiler::DataObject> pObject = std::make_unique<Compiler::DataStr>(dataToken);
Compiler::getDataObjects().push_back(std::move(pObject));
}
else
{
// Parse and add int to list, (ints can be constants, complex expressions or functions that return statics, hence the parsing)
Expression::Numeric numeric(true); // true = allow static init
if(Compiler::parseStaticExpression(codeLineIndex, dataTokens[i], operand, numeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DATA() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, dataTokens[i].c_str(), codeLine._text.c_str());
return false;
}
int16_t data = int16_t(std::lround(numeric._value));
std::unique_ptr<Compiler::DataObject> pObject = std::make_unique<Compiler::DataInt>(data);
Compiler::getDataObjects().push_back(std::move(pObject));
}
}
return true;
}
bool READ(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1)
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : syntax error, use 'READ <var0, var1, var2...varN>', requires at least one variable : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Search for variables
for(int i=0; i<int(tokens.size()); i++)
{
std::string varToken = tokens[i];
Expression::stripWhitespace(varToken);
int varIndex = Compiler::findVar(varToken);
int strIndex = Compiler::findStr(varToken);
size_t lbra, rbra;
std::string arrText;
Compiler::VarType varType = Compiler::VarInt16;
if(varToken.find("$") != std::string::npos) varType = Compiler::VarStr;
if(Expression::findMatchingBrackets(tokens[i], 0, lbra, rbra))
{
arrText = tokens[i].substr(lbra + 1, rbra - (lbra + 1));
switch(varType)
{
case Compiler::VarInt16: varType = Compiler::Var1Arr16; break;
case Compiler::VarStr: varType = Compiler::VarStr2; break;
default:
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : syntax error, only single dimensional arrays are supported in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
break;
}
}
// Int array must have already been dimensioned
if(varIndex == -1 && varType == Compiler::Var1Arr16)
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : integer array is not dimensioned in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
// Str array must have already been dimensioned
if(strIndex == -1 && varType == Compiler::VarStr2)
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : string array is not dimensioned in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
// Create int var
if(varIndex == -1 && varType == Compiler::VarInt16)
{
Compiler::createIntVar(varToken, 0, 0, codeLine, codeLineIndex, false, varIndex);
if(varIndex == -1)
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : couldn't create integer var '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
}
// Create str var
else if(strIndex == -1 && varType == Compiler::VarStr)
{
uint16_t address;
strIndex = getOrCreateString(codeLine, codeLineIndex, "", varToken, address, USER_STR_SIZE, false);
if(strIndex == -1)
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : couldn't create string var '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
}
// Copy read into int var
if(varIndex >= 0)
{
Compiler::emitVcpuAsm("%ReadIntVar", "", false);
Compiler::emitVcpuAsm("DEEK", "", false);
if(varType == Compiler::VarInt16)
{
Compiler::emitVcpuAsm("STW", "_" + Compiler::getIntegerVars()[varIndex]._name, false);
}
else if(varType == Compiler::Var1Arr16)
{
Compiler::writeArrayVarNoAssign(codeLine, codeLineIndex, varIndex);
}
else
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : integer var '%s' is invalid : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
}
// Copy read into str var
else if(strIndex >= 0)
{
if(varType == Compiler::VarStr)
{
Compiler::emitVcpuAsm("LDWI", "_" + Compiler::getStringVars()[strIndex]._name, false);
Compiler::emitVcpuAsm("STW", "strDstAddr", false);
Compiler::emitVcpuAsm("%ReadStrVar", "", false);
}
else if(varType == Compiler::VarStr2)
{
Compiler::writeArrayStrNoAssign(arrText, codeLineIndex, strIndex);
Compiler::emitVcpuAsm("STW", "strDstAddr", false);
Compiler::emitVcpuAsm("%ReadStrVar", "", false);
}
else
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : string var '%s' is invalid : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
}
else
{
fprintf(stderr, "Keywords::READ() : '%s:%d' : var '%s' is invalid : %s\n", codeLine._moduleName.c_str(), codeLineStart, varToken.c_str(), codeLine._text.c_str());
return false;
}
}
return true;
}
bool RESTORE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() > 1)
{
fprintf(stderr, "Keywords::RESTORE() : '%s:%d' : syntax error, use 'RESTORE <optional index>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDWI", "_dataIndex_", false);
Compiler::emitVcpuAsm("STW", "memAddr", false);
if(tokens.size() == 1)
{
Expression::Numeric numeric;
if(Compiler::parseExpression(codeLineIndex, tokens[0], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::RESTORE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
}
else
{
Compiler::emitVcpuAsm("LDI", "0", false);
}
Compiler::emitVcpuAsm("DOKE", "memAddr", false);
return true;
}
bool ALLOC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 && tokens.size() > 4)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : syntax error, use 'ALLOC <address>, <optional size>, <optional count>, <optional offset=0x0100>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
int count = 1;
uint16_t address, end, size = 0x0000, offset = 0x0100;
std::string addrOperand, sizeOperand, countOperand, offsetOperand;
Expression::Numeric addrNumeric(true), sizeNumeric(true), countNumeric(true), offsetNumeric(true);
if(Compiler::parseStaticExpression(codeLineIndex, tokens[0], addrOperand, addrNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
if(tokens.size() >= 2)
{
if(Compiler::parseStaticExpression(codeLineIndex, tokens[1], sizeOperand, sizeNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[1].c_str(), codeLine._text.c_str());
return false;
}
size = uint16_t(std::lround(sizeNumeric._value));
}
if(tokens.size() >= 3)
{
if(Compiler::parseStaticExpression(codeLineIndex, tokens[2], countOperand, countNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[2].c_str(), codeLine._text.c_str());
return false;
}
count = std::lround(countNumeric._value);
}
if(tokens.size() >= 4)
{
if(Compiler::parseStaticExpression(codeLineIndex, tokens[3], offsetOperand, offsetNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[3].c_str(), codeLine._text.c_str());
return false;
}
offset = uint16_t(std::lround(offsetNumeric._value));
if(count == 0 || offset == 0)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : count and offset must both be non zero, found %d and 0x%04x : %s\n", codeLine._moduleName.c_str(), codeLineStart, count, offset, codeLine._text.c_str());
return false;
}
}
address = uint16_t(std::lround(addrNumeric._value));
if(address < DEFAULT_EXEC_ADDRESS)
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : address field must be above &h%04x, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, DEFAULT_EXEC_ADDRESS, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
end = (size == 0) ? 0xFFFF : address + size;
for(int i=0; i<count; i++)
{
//fprintf(stderr, "0x%04x 0x%04x %d\n", address, end, end - address);
for(uint16_t j=address; j<end; j++)
{
if(!Memory::takeFreeRAM(j, 1, false))
{
fprintf(stderr, "Keywords::ALLOC() : '%s:%d' : trying to allocate already allocated memory at 0x%04x : %s\n", codeLine._moduleName.c_str(), codeLineStart, j, codeLine._text.c_str());
return false;
}
}
address += offset;
end += offset;
}
//Memory::printFreeRamList(Memory::NoSort);
return true;
}
bool FREE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 2)
{
fprintf(stderr, "Keywords::FREE() : '%s:%d' : syntax error, use 'FREE <address>, <size> or FREE STRINGWORKAREA' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Free string work area
if(tokens.size() == 1)
{
std::string token = Expression::strUpper(tokens[0]);
Expression::stripWhitespace(token);
if(token != "STRINGWORKAREA")
{
fprintf(stderr, "Keywords::FREE() : '%s:%d' : syntax error, expecting 'STRINGWORKAREA', found '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, token.c_str(), codeLine._text.c_str());
return false;
}
for(int i=0; i<NUM_STR_WORK_AREAS; i++)
{
if(!Memory::giveFreeRAM(Compiler::getStrWorkArea(i), USER_STR_SIZE + 2))
{
fprintf(stderr, "Keywords::FREE() : '%s:%d' : RAM at '0x%04x' is already free : %s\n", codeLine._moduleName.c_str(), codeLineStart, Compiler::getStrWorkArea(i), codeLine._text.c_str());
return false;
}
}
return true;
}
Expression::Numeric addrNumeric(true), sizeNumeric(true); // true = allow static init
std::string addrOperand, sizeOperand;
if(Compiler::parseStaticExpression(codeLineIndex, tokens[0], addrOperand, addrNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::FREE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
if(Compiler::parseStaticExpression(codeLineIndex, tokens[1], sizeOperand, sizeNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::FREE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[1].c_str(), codeLine._text.c_str());
return false;
}
uint16_t address = uint16_t(std::lround(addrNumeric._value));
uint16_t size = uint16_t(std::lround(sizeNumeric._value));
//Memory::printFreeRamList(Memory::NoSort);
if(!Memory::giveFreeRAM(address, size))
{
fprintf(stderr, "Keywords::FREE() : '%s:%d' : RAM at '0x%04x' is already free : %s\n", codeLine._moduleName.c_str(), codeLineStart, address, codeLine._text.c_str());
return false;
}
//Memory::printFreeRamList(Memory::NoSort);
return true;
}
bool AT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 2)
{
fprintf(stderr, "Keywords::AT() : '%s:%d' : syntax error, use 'AT <x>' or 'AT <x>, <y>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric numeric;
if(Compiler::parseExpression(codeLineIndex, tokens[0], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::AT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "cursorXY", false);
if(tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, tokens[1], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::AT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[1].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "cursorXY + 1", false);
}
Compiler::emitVcpuAsm("%AtTextCursor", "", false);
return true;
}
bool PUT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 1)
{
fprintf(stderr, "Keywords::PUT() : '%s:%d' : syntax error, use 'PUT <ascii>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric numeric;
if(Compiler::parseExpression(codeLineIndex, tokens[0], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::PUT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%PrintAcChr", "", false);
return true;
}
bool MODE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
if(Compiler::getCodeRomType() < Cpu::ROMv2)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
fprintf(stderr, "Keywords::MODE() : '%s:%d' : version error, 'MODE' requires ROMv2 or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 1)
{
fprintf(stderr, "Keywords::MODE() : '%s:%d' : syntax error, use 'MODE <0 - 3>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric numeric;
if(Compiler::parseExpression(codeLineIndex, tokens[0], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::MODE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "graphicsMode", false);
Compiler::emitVcpuAsm("%ScanlineMode", "", false);
return true;
}
bool WAIT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() > 1)
{
fprintf(stderr, "Keywords::WAIT() : '%s:%d' : syntax error, use 'WAIT <optional vblank count>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(tokens.size() == 0)
{
Compiler::emitVcpuAsm("%WaitVBlank", "", false);
return true;
}
Expression::Numeric numeric;
if(Compiler::parseExpression(codeLineIndex, tokens[0], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::WAIT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "waitVBlankNum", false);
Compiler::emitVcpuAsm("%WaitVBlanks", "", false);
return true;
}
bool PSET(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 2 || tokens.size() > 3)
{
fprintf(stderr, "Keywords::PSET() : '%s:%d' : syntax error, use 'PSET <x>, <y>' or 'PSET <x>, <y>, <colour>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric numeric;
if(Compiler::parseExpression(codeLineIndex, tokens[0], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::PSET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "drawPixel_xy", false);
if(Compiler::parseExpression(codeLineIndex, tokens[1], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::PSET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[1].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "drawPixel_xy + 1", false);
if(tokens.size() == 3)
{
if(Compiler::parseExpression(codeLineIndex, tokens[2], numeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::PSET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[2].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "fgbgColour + 1", false);
}
Compiler::emitVcpuAsm("%DrawPixel", "", false);
return true;
}
bool LINE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 2 && tokens.size() != 4)
{
fprintf(stderr, "Keywords::LINE() : '%s:%d' : syntax error, use 'LINE <x>, <y>' or 'LINE <x1>, <y1>, <x2>, <y2>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(tokens.size() == 2)
{
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::LINE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawLine_x2", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawLine_y2", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%AtLineCursor", "", false);
Compiler::emitVcpuAsm("%DrawVTLine", "", false);
}
else
{
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::LINE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawLine_x1", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawLine_y1", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawLine_x2", false); break;
case 3: Compiler::emitVcpuAsm("STW", "drawLine_y2", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawLine", "", false);
}
return true;
}
bool HLINE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::HLINE() : '%s:%d' : syntax error, use 'HLINE <x1>, <y>, <x2>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::HLINE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawHLine_x1", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawHLine_y1", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawHLine_x2", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawHLine", "", false);
return true;
}
bool VLINE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::VLINE() : '%s:%d' : syntax error, use 'VLINE <x>, <y1>, <y2>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::VLINE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawVLine_x1", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawVLine_y1", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawVLine_y2", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawVLine", "", false);
return true;
}
bool CIRCLE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::CIRCLE() : '%s:%d' : syntax error, use 'CIRCLE <x>, <y>, <radius>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::CIRCLE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawCircle_cx", false); break;
case 1: Compiler::emitVcpuAsm("ADDI", "8", false); Compiler::emitVcpuAsm("STW", "drawCircle_cy", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawCircle_r", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawCircle", "", false);
return true;
}
bool CIRCLEF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::CIRCLEF() : '%s:%d' : syntax error, use 'CIRCLEF <x>, <y>, <radius>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::CIRCLEF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawCircleF_cx", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawCircleF_cy", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawCircleF_r", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawCircleF", "", false);
return true;
}
bool RECT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 4)
{
fprintf(stderr, "Keywords::RECT() : '%s:%d' : syntax error, use 'RECT <x1>, <y1>, <x2>, <y2>'' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::RECT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawRect_x1", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawRect_y1", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawRect_x2", false); break;
case 3: Compiler::emitVcpuAsm("STW", "drawRect_y2", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawRect", "", false);
return true;
}
bool RECTF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 4)
{
fprintf(stderr, "Keywords::RECTF() : '%s:%d' : syntax error, use 'RECTF <x1>, <y1>, <x2>, <y2>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<Expression::Numeric> params = {Expression::Numeric(), Expression::Numeric(), Expression::Numeric(), Expression::Numeric()};
for(int i=0; i<int(tokens.size()); i++)
{
if(Compiler::parseExpression(codeLineIndex, tokens[i], params[i]) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::RECTF() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
switch(i)
{
case 0: Compiler::emitVcpuAsm("STW", "drawRectF_x1", false); break;
case 1: Compiler::emitVcpuAsm("STW", "drawRectF_y1", false); break;
case 2: Compiler::emitVcpuAsm("STW", "drawRectF_x2", false); break;
case 3: Compiler::emitVcpuAsm("STW", "drawRectF_y2", false); break;
default: break;
}
}
Compiler::emitVcpuAsm("%DrawRectF", "", false);
return true;
}
bool POLY(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 1)
{
fprintf(stderr, "Keywords::POLY() : '%s:%d' : syntax error, use 'POLY <coords address>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric param;
if(Compiler::parseExpression(codeLineIndex, tokens[0], param) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::POLY() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "drawPoly_addr", false);
Compiler::emitVcpuAsm("%DrawPoly", "", false);
return true;
}
bool POLYR(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 2)
{
fprintf(stderr, "Keywords::POLYR() : '%s:%d' : syntax error, use 'POLYR <coords address> <optional FLIPX/FLIPY/FLIPXY>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Flip type
if(tokens.size() == 2)
{
std::string flipToken = tokens[1];
Expression::stripWhitespace(flipToken);
Expression::strToUpper(flipToken);
if(flipToken != "FLIPX" && flipToken != "FLIPY" && flipToken != "FLIPXY")
{
fprintf(stderr, "Keywords::POLYR() : '%s:%d' : syntax error, use one of the correct flip types, 'POLY <coords address> <optional FLIPX/FLIPY/FLIPXY>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// SUBW mode
Compiler::emitVcpuAsm("LDI", "0xB8", false);
Compiler::emitVcpuAsm("ST", "drawPoly_mode", false);
// FlipX
if(flipToken == "FLIPX")
{
Compiler::emitVcpuAsm("%SetPolyRelFlipX", "", false);
}
// FlipY
else if(flipToken == "FLIPY")
{
Compiler::emitVcpuAsm("%SetPolyRelFlipY", "", false);
}
// FlipXY
else
{
Compiler::emitVcpuAsm("%SetPolyRelFlipX", "", false);
Compiler::emitVcpuAsm("%SetPolyRelFlipY", "", false);
}
}
Expression::Numeric param;
if(Compiler::parseExpression(codeLineIndex, tokens[0], param) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::POLYR() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[0].c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "drawPoly_addr", false);
Compiler::emitVcpuAsm("%DrawPolyRel", "", false);
return true;
}
bool TCLIP(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ' ', false);
if(tokens.size() != 1)
{
fprintf(stderr, "Keywords::TCLIP() : '%s:%d' : syntax error, use 'TCLIP ON' or 'TCLIP OFF' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string tclipToken = Expression::strToUpper(tokens[0]);
Expression::stripWhitespace(tclipToken);
if(tclipToken != "ON" && tclipToken != "OFF")
{
fprintf(stderr, "Keywords::TCLIP() : '%s:%d' : syntax error, use 'TCLIP ON' or 'TCLIP OFF' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(tclipToken == "ON")
{
Compiler::emitVcpuAsm("LDWI", "0xFFFB", false);
Compiler::emitVcpuAsm("ANDW", "miscFlags", false);
}
else
{
Compiler::emitVcpuAsm("LDWI", "0x0004", false);
Compiler::emitVcpuAsm("ORW", "miscFlags", false);
}
Compiler::emitVcpuAsm("STW", "miscFlags", false);
return true;
}
bool SCROLL(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ' ', false);
if(tokens.size() != 1)
{
fprintf(stderr, "Keywords::SCROLL() : '%s:%d' : syntax error, use 'SCROLL ON' or 'SCROLL OFF' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::string scrollToken = Expression::strToUpper(tokens[0]);
Expression::stripWhitespace(scrollToken);
if(scrollToken != "ON" && scrollToken != "OFF")
{
fprintf(stderr, "Keywords::SCROLL() : '%s:%d' : syntax error, use 'SCROLL ON' or 'SCROLL OFF'' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(scrollToken == "ON")
{
Compiler::emitVcpuAsm("LDWI", "0x0001", false);
Compiler::emitVcpuAsm("ORW", "miscFlags", false);
}
else
{
Compiler::emitVcpuAsm("LDWI", "0xFFFE", false);
Compiler::emitVcpuAsm("ANDW", "miscFlags", false);
}
Compiler::emitVcpuAsm("STW", "miscFlags", false);
return true;
}
bool POKE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 2)
{
fprintf(stderr, "Keywords::POKE() : '%s:%d' : syntax error, use 'POKE <address>, <value>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<std::string> operands = {"", ""};
std::vector<Expression::Numeric> numerics = {Expression::Numeric(), Expression::Numeric()};
std::vector<Compiler::OperandType> operandTypes {Compiler::OperandConst, Compiler::OperandConst};
for(int i=0; i<int(tokens.size()); i++)
{
operandTypes[i] = Compiler::parseStaticExpression(codeLineIndex, tokens[i], operands[i], numerics[i]);
if(operandTypes[i] == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::POKE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
}
std::string opcode, operand;
switch(numerics[1]._int16Byte)
{
case Expression::Int16Low: opcode = "LD"; operand = "_" + operands[1]; break;
case Expression::Int16High: opcode = "LD"; operand = "_" + operands[1] + " + 1"; break;
case Expression::Int16Both: opcode = "LDW"; operand = "_" + operands[1]; break;
default: break;
}
if((operandTypes[0] == Compiler::OperandVar || operandTypes[0] == Compiler::OperandTemp) && (operandTypes[1] == Compiler::OperandVar ||
operandTypes[1] == Compiler::OperandTemp))
{
(operandTypes[1] == Compiler::OperandVar) ? Compiler::emitVcpuAsm(opcode, operand, false, codeLineIndex) : Compiler::emitVcpuAsm("LDW", operands[1], false);
(operandTypes[0] == Compiler::OperandVar) ? Compiler::emitVcpuAsm("POKE", "_" + operands[0], false, codeLineIndex) : Compiler::emitVcpuAsm("POKE", operands[0], false);
}
else if((operandTypes[0] == Compiler::OperandVar || operandTypes[0] == Compiler::OperandTemp) && operandTypes[1] == Compiler::OperandConst)
{
Compiler::emitVcpuAsm("LDI", operands[1], false);
(operandTypes[0] == Compiler::OperandVar) ? Compiler::emitVcpuAsm("POKE", "_" + operands[0], false, codeLineIndex) : Compiler::emitVcpuAsm("POKE", operands[0], false);
}
else if(operandTypes[0] == Compiler::OperandConst && (operandTypes[1] == Compiler::OperandVar || operandTypes[1] == Compiler::OperandTemp))
{
uint16_t addr;
if(Expression::stringToU16(operands[0], addr) && addr < 0x0100)
{
(operandTypes[1] == Compiler::OperandVar) ? Compiler::emitVcpuAsm(opcode, operand, false, codeLineIndex) : Compiler::emitVcpuAsm("LDW", operands[1], false);
Compiler::emitVcpuAsm("ST", operands[0], false);
}
else
{
Compiler::emitVcpuAsm("LDWI", operands[0], false);
Compiler::emitVcpuAsm("STW", "register0", false);
(operandTypes[1] == Compiler::OperandVar) ? Compiler::emitVcpuAsm(opcode, operand, false, codeLineIndex) : Compiler::emitVcpuAsm("LDW", operands[1], false);
Compiler::emitVcpuAsm("POKE", "register0", false);
}
}
else
{
// Optimise for page 0
uint16_t addr;
if(Expression::stringToU16(operands[0], addr) && addr < 0x0100)
{
Compiler::emitVcpuAsm("LDI", operands[1], false);
Compiler::emitVcpuAsm("ST", operands[0], false);
}
// All other pages
else
{
Compiler::emitVcpuAsm("LDWI", operands[0], false);
Compiler::emitVcpuAsm("STW", "register0", false);
Compiler::emitVcpuAsm("LDI", operands[1], false);
Compiler::emitVcpuAsm("POKE", "register0", false);
}
}
return true;
}
bool DOKE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 2)
{
fprintf(stderr, "Keywords::DOKE() : '%s:%d' : syntax error, use 'DOKE <address>, <value>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
std::vector<std::string> operands = {"", ""};
std::vector<Expression::Numeric> numerics = {Expression::Numeric(), Expression::Numeric()};
std::vector<Compiler::OperandType> operandTypes {Compiler::OperandConst, Compiler::OperandConst};
for(int i=0; i<int(tokens.size()); i++)
{
operandTypes[i] = Compiler::parseStaticExpression(codeLineIndex, tokens[i], operands[i], numerics[i]);
if(operandTypes[i] == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::DOKE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[i].c_str(), codeLine._text.c_str());
return false;
}
}
std::string opcode, operand;
switch(numerics[1]._int16Byte)
{
case Expression::Int16Low: opcode = "LD"; operand = "_" + operands[1]; break;
case Expression::Int16High: opcode = "LD"; operand = "_" + operands[1] + " + 1"; break;
case Expression::Int16Both: opcode = "LDW"; operand = "_" + operands[1]; break;
default: break;
}
if((operandTypes[0] == Compiler::OperandVar || operandTypes[0] == Compiler::OperandTemp) && (operandTypes[1] == Compiler::OperandVar ||
operandTypes[1] == Compiler::OperandTemp))
{
(operandTypes[1] == Compiler::OperandVar) ? Compiler::emitVcpuAsm(opcode, operand, false, codeLineIndex) : Compiler::emitVcpuAsm("LDW", "" + operands[1], false);
(operandTypes[0] == Compiler::OperandVar) ? Compiler::emitVcpuAsm("DOKE", "_" + operands[0], false, codeLineIndex) : Compiler::emitVcpuAsm("DOKE", "" + operands[0], false);
}
else if((operandTypes[0] == Compiler::OperandVar || operandTypes[0] == Compiler::OperandTemp) && operandTypes[1] == Compiler::OperandConst)
{
Compiler::emitVcpuAsm("LDWI", operands[1], false);
(operandTypes[0] == Compiler::OperandVar) ? Compiler::emitVcpuAsm("DOKE", "_" + operands[0], false, codeLineIndex) : Compiler::emitVcpuAsm("DOKE", "" + operands[0], false);
}
else if(operandTypes[0] == Compiler::OperandConst && (operandTypes[1] == Compiler::OperandVar || operandTypes[1] == Compiler::OperandTemp))
{
uint16_t addr;
if(Expression::stringToU16(operands[0], addr) && addr < 0x0100)
{
(operandTypes[1] == Compiler::OperandVar) ? Compiler::emitVcpuAsm(opcode, operand, false, codeLineIndex) : Compiler::emitVcpuAsm("LDW", "" + operands[1], false);
Compiler::emitVcpuAsm("STW", operands[0], false);
}
else
{
Compiler::emitVcpuAsm("LDWI", operands[0], false);
Compiler::emitVcpuAsm("STW", "register0", false);
(operandTypes[1] == Compiler::OperandVar) ? Compiler::emitVcpuAsm(opcode, operand, false, codeLineIndex) : Compiler::emitVcpuAsm("LDW", "" + operands[1], false);
Compiler::emitVcpuAsm("DOKE", "register0", false);
}
}
else
{
// Optimise for page 0
uint16_t addr;
if(Expression::stringToU16(operands[0], addr) && addr < 0x0100)
{
Compiler::emitVcpuAsm("LDWI", operands[1], false);
Compiler::emitVcpuAsm("STW", operands[0], false);
}
// All other pages
else
{
Compiler::emitVcpuAsm("LDWI", operands[0], false);
Compiler::emitVcpuAsm("STW", "register0", false);
Compiler::emitVcpuAsm("LDWI", operands[1], false);
Compiler::emitVcpuAsm("DOKE", "register0", false);
}
}
return true;
}
void timeInit(void)
{
// Init time proc
Compiler::setCreateTimeData(true);
// ROM's 1 to 4
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("LDI", "0", false);
Compiler::emitVcpuAsm("STW", "timerTick", false);
Compiler::emitVcpuAsm("LDI", "giga_frameCount", false);
Compiler::emitVcpuAsm("STW", "timerPrev", false);
}
// ROMv5a or higher
else
{
Compiler::emitVcpuAsm("LDI", "0", false);
Compiler::emitVcpuAsm("STW", "timerTick", false);
Compiler::emitVcpuAsm("STW", "timerPrev", false);
}
}
void midiInit(void)
{
_midiType = Midi;
}
void midivInit(void)
{
_midiType = MidiV;
}
void userInit(const std::string& label)
{
_userRoutine = label;
}
void timeAddr(int index)
{
Compiler::emitVcpuAsm("LDWI", "tickTime", false);
Compiler::emitVcpuAsm("STW", "realTimeProc" + std::to_string(index), false);
}
void midiAddr(int index)
{
Compiler::emitVcpuAsm("LDWI", "playMidi", false);
Compiler::emitVcpuAsm("STW", "realTimeProc" + std::to_string(index), false);
}
void midivAddr(int index)
{
Compiler::emitVcpuAsm("LDWI", "playMidiVol", false);
Compiler::emitVcpuAsm("STW", "realTimeProc" + std::to_string(index), false);
}
void userAddr(const std::string& label, int index)
{
Compiler::emitVcpuAsm("LDWI", "_" + label, false);
Compiler::emitVcpuAsm("STW", "realTimeProc" + std::to_string(index), false);
}
void usageINIT(Compiler::CodeLine& codeLine, int codeLineStart)
{
fprintf(stderr, "Keywords::INIT() : '%s:%d' : syntax error, use 'INIT VARS, <optional var address>, TIME, MIDI/MIDIV, <user proc>, NOUPDATE : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
}
bool INIT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
static std::map<std::string, InitTypes> initTypesMap = {{"TIME", InitTime}, {"MIDI", InitMidi}, {"MIDIV", InitMidiV}};
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 4)
{
usageINIT(codeLine, codeLineStart);
return false;
}
if(tokens.size() == 1 || Compiler::getCodeRomType() > Cpu::ROMv4)
{
Expression::stripWhitespace(tokens[0]);
std::string token = tokens[0];
Expression::strToUpper(token);
if(token == "NOUPDATE")
{
fprintf(stderr, "Keywords::INIT() : '%s:%d' : syntax error, 'NOUPDATE' must be used with 'INIT TIME, MIDI/MIDIV, <user proc>' and only on ROMv4 or lower : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
}
// Search for init types and labels
bool noUpdate = false;
bool resetVars = false;
std::string varsAddr;
std::vector<InitTypes> initTypes;
for(int i=0; i<int(tokens.size()); i++)
{
Expression::stripWhitespace(tokens[i]);
std::string token = tokens[i];
Expression::strToUpper(token);
// Init type not found, so search for label
if(initTypesMap.find(token) == initTypesMap.end())
{
if(token == "NOUPDATE")
{
noUpdate = true;
continue;
}
size_t varsPos = token.find("VARS");
if(varsPos != std::string::npos)
{
// Check for starting var address
size_t addrPos = varsPos + sizeof("VARS")-1;
if(addrPos < tokens[i].size())
{
varsAddr = tokens[i].substr(addrPos);
}
resetVars = true;
continue;
}
int labIndex = Compiler::findLabel(tokens[i]);
if(labIndex == -1)
{
usageINIT(codeLine, codeLineStart);
return false;
}
else
{
initTypes.push_back(InitUser);
}
}
else
{
if(initTypesMap[token] == InitMidi || initTypesMap[token] == InitMidiV)
{
if(_midiType != MidiNone)
{
fprintf(stderr, "Keywords::INIT() : '%s:%d' : syntax error, can only init one instance of MIDI or MIDIV, use 'INIT TIME, MIDI/MIDIV, <user proc> : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
}
initTypes.push_back(initTypesMap[token]);
}
}
// Proc and addr init
for(int i=0; i<int(initTypes.size()); i++)
{
switch(initTypes[i])
{
case InitTime: timeInit(); timeAddr(i); break;
case InitMidi: midiInit(); midiAddr(i); break;
case InitMidiV: midivInit(); midivAddr(i); break;
case InitUser: userInit(tokens[i]); userAddr(tokens[i], i); break;
default: break;
}
}
if(resetVars)
{
if(varsAddr.size())
{
Expression::Numeric numeric(true); // true = allow static init
if(!Expression::parse(varsAddr, codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::INIT() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDI", Expression::byteToHexString(int8_t(std::lround(numeric._value))), false);
}
else
{
Compiler::emitVcpuAsm("LDI", "giga_User", false);
}
Compiler::emitVcpuAsm("STW", "varAddress", false);
Compiler::emitVcpuAsm("%ResetVars", "", false);
}
if(initTypes.size())
{
// ROM's 1 to 4, (time sliced code)
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
// If 'NOUPDATE" is specified then user must call tick himself
if(!noUpdate)
{
Compiler::enableSysInitFunc("InitRealTimeStub");
Linker::setInternalSubToLoad("realTimeStub");
Compiler::emitVcpuPreProcessingCmd("%define TIME_SLICING");
Compiler::emitVcpuAsm("LDWI", "setRealTimeProc" + std::to_string(initTypes.size() - 1), false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
}
// ROMv5a and higher, (vertical blank interrupt)
else
{
// Vertical blank interrupt uses 0x30-0x33 for vPC and vAC save/restore, so we must move any variables found there
Compiler::moveVblankVars();
// Build chain of vertical blank interrupt handlers, (up to 3)
Compiler::emitVcpuAsm("CALLI", "setRealTimeProc" + std::to_string(initTypes.size() - 1), false);
// Start vertical blank interrupt
Compiler::emitVcpuAsm("LDI", "0xFF" , false);
Compiler::emitVcpuAsm("ST", "giga_frameCount", false);
Compiler::emitVcpuAsm("LDWI", "giga_vblankProc", false);
Compiler::emitVcpuAsm("STW", "register0" , false);
Compiler::emitVcpuAsm("LDWI", "realTimeStub" , false);
Compiler::emitVcpuAsm("DOKE", "register0" , false);
}
}
return true;
}
void usageTICK(Compiler::CodeLine& codeLine, int codeLineStart)
{
fprintf(stderr, "Keywords::INIT() : '%s:%d' : syntax error, use one or more of, 'TICK TIME, MIDI, MIDIV, USER' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
}
bool TICK(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
if(Compiler::getCodeRomType() > Cpu::ROMv4)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
fprintf(stderr, "Keywords::TICK() : '%s:%d' : version error, 'TICK' requires ROMv4 or lower, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 4)
{
usageTICK(codeLine, codeLineStart);
return false;
}
std::map<std::string, InitTypes> initTypesMap;
for(int i=0; i<int(tokens.size()); i++)
{
std::string token = Expression::strToUpper(tokens[i]);
Expression::stripWhitespace(token);
if(initTypesMap.find(token) == initTypesMap.end())
{
if(token == "TIME")
{
if(!Compiler::getCreateTimeData())
{
fprintf(stderr, "Keywords::TICK() : '%s:%d' : TIME not initialised using INIT : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
initTypesMap[token] = InitTime;
Compiler::emitVcpuAsm("%TickTime", "", false);
}
else if(token == "MIDI")
{
if(_midiType != Midi)
{
fprintf(stderr, "Keywords::TICK() : '%s:%d' : MIDI not initialised using INIT : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
initTypesMap[token] = InitMidi;
Compiler::emitVcpuAsm("%TickMidi", "", false);
}
else if(token == "MIDIV")
{
if(_midiType != MidiV)
{
fprintf(stderr, "Keywords::TICK() : '%s:%d' : MIDIV not initialised using INIT : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
initTypesMap[token] = InitMidiV;
Compiler::emitVcpuAsm("%TickMidiV", "", false);
}
else if(token == "USER")
{
if(_userRoutine == "")
{
fprintf(stderr, "Keywords::TICK() : '%s:%d' : USER routine not initialised using INIT : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
initTypesMap[token] = InitUser;
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("LDWI", "_" + _userRoutine, false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
else
{
Compiler::emitVcpuAsm("CALLI", "_" + _userRoutine, false);
}
}
else
{
usageTICK(codeLine, codeLineStart);
return false;
}
}
else
{
usageTICK(codeLine, codeLineStart);
return false;
}
}
return true;
}
void usagePLAY(Compiler::CodeLine& codeLine, int codeLineStart)
{
fprintf(stderr, "Keywords::PLAY() : '%s:%d' : syntax error, use 'PLAY <TYPE>, <id/address>, <optional waveType>', where <TYPE> = 'MIDI', 'MIDID', 'MIDIV', 'MIDIDV' or 'MUSIC' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
}
bool PLAY(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 2 || tokens.size() > 3)
{
usagePLAY(codeLine, codeLineStart);
return false;
}
// Default wave type
if(tokens.size() == 2)
{
Compiler::emitVcpuAsm("LDI", "2", false);
Compiler::emitVcpuAsm("ST", "waveType + 1", false);
}
// Midi wave type, (optional)
else if(tokens.size() == 3)
{
std::string waveTypeToken = tokens[2];
Expression::Numeric waveTypeNumeric;
if(Compiler::parseExpression(codeLineIndex, waveTypeToken, waveTypeNumeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::PLAY() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, waveTypeToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "waveType + 1", false);
}
// Midi stream address
std::string addressToken = tokens[1];
Expression::Numeric addressNumeric;
if(Compiler::parseExpression(codeLineIndex, tokens[1], addressNumeric) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::PLAY() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, tokens[1].c_str(), codeLine._text.c_str());
return false;
}
std::string midiToken = Expression::strToUpper(tokens[0]);
Expression::stripWhitespace(midiToken);
if(midiToken == "MIDI")
{
Compiler::emitVcpuAsm("%PlayMidi", "", false);
return true;
}
if(midiToken == "MIDID")
{
Compiler::emitVcpuAsm("%SetMidiStream", "", false);
Compiler::emitVcpuAsm("%PlayMidi", "", false);
return true;
}
if(midiToken == "MIDIV")
{
Compiler::emitVcpuAsm("%PlayMidiV", "", false);
return true;
}
if(midiToken == "MIDIDV")
{
Compiler::emitVcpuAsm("%SetMidiStream", "", false);
Compiler::emitVcpuAsm("%PlayMidiV", "", false);
return true;
}
if(midiToken == "MUSIC")
{
Compiler::emitVcpuAsm("%PlayMusic", "", false);
return true;
}
usagePLAY(codeLine, codeLineStart);
return false;
}
void loadUsage(int msgType, Compiler::CodeLine& codeLine, int codeLineStart)
{
switch(msgType)
{
case LoadType: fprintf(stderr, "Keywords::LOAD() : '%s:%d' : syntax error, use 'LOAD <TYPE>, <filename>, where <TYPE> = 'IMAGE', 'SPRITE', 'FONT', 'MIDI', 'WAVE' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case LoadWave: fprintf(stderr, "Keywords::LOAD() : '%s:%d' : syntax error, use 'LOAD WAVE, <filename>, <optional address>, <optional address offset>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case LoadMidi: fprintf(stderr, "Keywords::LOAD() : '%s:%d' : syntax error, use 'LOAD MIDI, <filename>, <id>, <optional loop count 1<->255, 0=forever>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case LoadImage: fprintf(stderr, "Keywords::LOAD() : '%s:%d' : syntax error, use 'LOAD IMAGE, <filename>, <optional address>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case LoadSprite: fprintf(stderr, "Keywords::LOAD() : '%s:%d' : syntax error, use 'LOAD SPRITE, <filename>, <id>, <optional flip>, <optional overlap>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case LoadFont: fprintf(stderr, "Keywords::LOAD() : '%s:%d' : syntax error, use 'LOAD FONT, <filename>, <id>, <optional 16 bit bg:fg colours>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
default: break;
}
}
/********************************************************************************************************************************/
/* Load Wave */
/********************************************************************************************************************************/
bool loadWave(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, const std::vector<std::string>& tokens)
{
if(tokens.size() < 2 || tokens.size() > 4)
{
loadUsage(LoadWave, codeLine, codeLineStart);
return false;
}
std::string filename = tokens[1];
Expression::stripWhitespace(filename);
std::string ext = filename;
Expression::strToUpper(ext);
if(ext.find(".GTWAV") != std::string::npos)
{
std::string filepath = Loader::getFilePath();
size_t slash = filepath.find_last_of("\\/");
filepath = (slash != std::string::npos) ? filepath.substr(0, slash) : ".";
filename = filepath + "/" + filename;
// Parse optional address
uint16_t address = RAM_AUDIO_START;
if(tokens.size() >= 3)
{
std::string addrToken = tokens[2];
Expression::Numeric addrNumeric;
std::string addrOperand;
if(Compiler::parseStaticExpression(codeLineIndex, addrToken, addrOperand, addrNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadWave() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrToken.c_str(), codeLine._text.c_str());
return false;
}
address = uint16_t(std::lround(addrNumeric._value));
if(address < DEFAULT_EXEC_ADDRESS)
{
loadUsage(LoadWave, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadWave() : '%s:%d' : address field must be above &h%04x, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, DEFAULT_EXEC_ADDRESS, addrToken.c_str(), codeLine._text.c_str());
return false;
}
}
// Parse optional address
uint16_t addrOffset = 0;
if(tokens.size() == 4)
{
std::string offsetToken = tokens[3];
Expression::Numeric offsetNumeric;
std::string offsetOperand;
if(Compiler::parseStaticExpression(codeLineIndex, offsetToken, offsetOperand, offsetNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadWave() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, offsetToken.c_str(), codeLine._text.c_str());
return false;
}
addrOffset = uint16_t(std::lround(offsetNumeric._value));
}
// Load wave file
std::vector<uint8_t> dataBytes(64);
std::ifstream infile(filename, std::ios::binary | std::ios::in);
if(!infile.is_open())
{
loadUsage(LoadWave, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadWave() : '%s:%d' : failed to open file '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, filename.c_str(), codeLine._text.c_str());
return false;
}
infile.read((char *)&dataBytes[0], 64);
if(infile.eof() || infile.bad() || infile.fail())
{
loadUsage(LoadWave, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadWave() : '%s:%d' : failed to read file '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, filename.c_str(), codeLine._text.c_str());
return false;
}
uint16_t addr = address;
for(int i=0; i<int(dataBytes.size()); i++)
{
if(addrOffset != 0)
{
if(addr < RAM_AUDIO_START || addr > RAM_AUDIO_END)
{
if(!Memory::takeFreeRAM(addr, 1)) return false;
addr += addrOffset;
}
}
}
if(addrOffset == 0)
{
if(!Memory::takeFreeRAM(address, int(dataBytes.size()))) return false;
}
Compiler::getDefDataBytes().push_back({address, addrOffset, dataBytes});
}
return true;
}
/********************************************************************************************************************************/
/* Load Midi */
/********************************************************************************************************************************/
bool loadMidi(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, const std::vector<std::string>& tokens)
{
if(tokens.size() < 3 || tokens.size() > 4)
{
loadUsage(LoadMidi, codeLine, codeLineStart);
return false;
}
std::string filename = tokens[1];
Expression::stripWhitespace(filename);
std::string ext = filename;
Expression::strToUpper(ext);
if(ext.find(".GTMID") != std::string::npos)
{
std::string filepath = Loader::getFilePath();
size_t slash = filepath.find_last_of("\\/");
filepath = (slash != std::string::npos) ? filepath.substr(0, slash) : ".";
filename = filepath + "/" + filename;
// Unique midi ID
std::string idToken = tokens[2];
Expression::Numeric idNumeric;
std::string idOperand;
if(Compiler::parseStaticExpression(codeLineIndex, idToken, idOperand, idNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadMidi() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, idToken.c_str(), codeLine._text.c_str());
return false;
}
int midiId = int(std::lround(idNumeric._value));
if(Compiler::getDefDataMidis().find(midiId) != Compiler::getDefDataMidis().end())
{
loadUsage(LoadMidi, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadMidi() : '%s:%d' : MIDI id %d not unique : %s\n", codeLine._moduleName.c_str(), codeLineStart, midiId, codeLine._text.c_str());
return false;
}
// Parse optional loop count
uint16_t loops = 0;
if(tokens.size() == 4)
{
std::string loopsToken = tokens[2];
Expression::Numeric loopsNumeric;
std::string loopsOperand;
if(Compiler::parseStaticExpression(codeLineIndex, loopsToken, loopsOperand, loopsNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadMidi() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, loopsToken.c_str(), codeLine._text.c_str());
return false;
}
loops = uint16_t(std::lround(loopsNumeric._value));
if(loops > 255)
{
loadUsage(LoadMidi, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadMidi() : '%s:%d' : loops field must be between 0 and 255, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, loopsToken.c_str(), codeLine._text.c_str());
return false;
}
}
// Load gtMID file
int midiSize = 0;
GtMidiHdr gtMidiHdr;
std::vector<uint8_t> midiBuffer(MIDI_MAX_BUFFER_SIZE);
if(!Midi::loadFile(filename, midiBuffer.data(), midiSize, &gtMidiHdr))
{
loadUsage(LoadMidi, codeLine, codeLineStart);
return false;
}
midiBuffer.resize(midiSize);
// Allocate memory for midi segments
int byteIndex = 0;
int segmentCount = 0;
std::vector<uint16_t> segSizes;
std::vector<uint16_t> segAddrs;
bool hasVolume = bool(gtMidiHdr._hasVolume);
while(midiSize)
{
uint16_t size = 0;
uint16_t address = 0;
if(!Memory::getFreeRAM(Memory::FitDescending, USER_CODE_START, Compiler::getRuntimeStart(), MIDI_MIN_SEGMENT_SIZE, address, uint16_t(midiSize + MIDI_CMD_JMP_SEG_SIZE), size, false))
{
loadUsage(LoadMidi, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadMidi() : '%s:%d' : getting MIDI memory for segment %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, segmentCount, codeLine._text.c_str());
return false;
}
byteIndex += size;
// Pad for jump segment command and adjust for start notes, start note can be either 3 bytes, (0x90, nn, vv) or 2 bytes, (0x90, nn), depending on hasVolume
if(hasVolume && (midiBuffer[byteIndex - 5] & 0xF0) == MIDI_CMD_START_NOTE) {byteIndex -= 2; size -= 2; Memory::giveFreeRAM(address + size-2, 2);} // landed on volume, (vv)
else if((midiBuffer[byteIndex - 4] & 0xF0) == MIDI_CMD_START_NOTE) {byteIndex -= 1; size -= 1; Memory::giveFreeRAM(address + size-1, 1);} // landed on note, (nn)
{byteIndex -= 3; size -= 3; } // jump segment, (0xD0, lo, hi)
midiSize -= size;
segSizes.push_back(size);
segAddrs.push_back(address);
segmentCount++;
}
Compiler::getDefDataMidis()[midiId] = {midiId, hasVolume, uint8_t(loops), midiBuffer, segSizes, segAddrs};
}
return true;
}
/********************************************************************************************************************************/
/* Load Image */
/********************************************************************************************************************************/
bool loadImageChunk(Compiler::CodeLine& codeLine, int codeLineStart, const std::vector<uint8_t>& data, int row, uint16_t width, uint16_t address, uint8_t chunkSize, uint16_t& chunkOffset, uint16_t& chunkAddr)
{
if(!Memory::getFreeRAM(Memory::FitDescending, chunkSize, USER_CODE_START, Compiler::getRuntimeStart(), chunkAddr))
{
loadUsage(LoadImage, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadImageChunk() : '%s:%d' : allocating RAM for offscreen pixel chunk on row %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, row, codeLine._text.c_str());
return false;
}
std::vector<uint8_t> chunkData;
for(int j=0; j<chunkSize; j++) chunkData.push_back(data[row*width + chunkOffset + j]);
// Output loader image chunks copy code
if(Compiler::getDefDataLoaderImageChunks().size() == 0)
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
Compiler::emitVcpuAsm("LDWI", "copyLoaderImages", false);
Compiler::emitVcpuAsm("CALL", "giga_vAC", false);
}
else
{
Compiler::emitVcpuAsm("CALLI", "copyLoaderImages", false);
}
}
Compiler::DefDataLoaderImageChunk defDataLoaderImageChunk = {chunkAddr, uint16_t(address + chunkOffset), chunkSize, chunkData};
Compiler::getDefDataLoaderImageChunks().push_back(defDataLoaderImageChunk);
chunkOffset += chunkSize;
return true;
}
bool loadImage(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, const std::vector<std::string>& tokens, const std::string& filename, const Image::TgaFile& tgaFile, const Image::GtRgbFile& gtRgbFile)
{
const uint16_t stride = 256;
if(tokens.size() < 2 || tokens.size() > 3)
{
loadUsage(LoadImage, codeLine, codeLineStart);
return false;
}
// Parse optional address
uint16_t address = RAM_VIDEO_START;
if(tokens.size() == 3)
{
std::string addrToken = tokens[2];
Expression::Numeric addrNumeric;
std::string addrOperand;
if(Compiler::parseStaticExpression(codeLineIndex, addrToken, addrOperand, addrNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadImage() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, addrToken.c_str(), codeLine._text.c_str());
return false;
}
address = uint16_t(std::lround(addrNumeric._value));
if(address < DEFAULT_EXEC_ADDRESS)
{
loadUsage(LoadImage, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadImage() : '%s:%d' : address field must be above &h%04x, found %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, DEFAULT_EXEC_ADDRESS, addrToken.c_str(), codeLine._text.c_str());
return false;
}
}
if(gtRgbFile._header._width > stride || gtRgbFile._header._width + (address & 0x00FF) > stride)
{
loadUsage(LoadImage, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadImage() : '%s:%d' : image width %d + starting address 0x%04x overflow, for %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, gtRgbFile._header._width, address, filename.c_str(), codeLine._text.c_str());
return false;
}
Compiler::DefDataImage defDataImage = {address, tgaFile._header._width, tgaFile._header._height, stride, gtRgbFile._data};
Compiler::getDefDataImages().push_back(defDataImage);
int size = gtRgbFile._header._width;
for(int y=0; y<gtRgbFile._header._height; y++)
{
// Take offscreen memory from compiler for images wider than visible screen resolution, or images in offscreen memory
if(address >= RAM_VIDEO_START && address <= RUN_TIME_START)
{
size = gtRgbFile._header._width + (address & 0x00FF) - RAM_SCANLINE_SIZE;
if(size > 0)
{
if(!Memory::takeFreeRAM((address & 0xFF00) + RAM_SCANLINE_SIZE, size))
{
loadUsage(LoadImage, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadImage() : '%s:%d' : allocating RAM for pixel row %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, y, codeLine._text.c_str());
return false;
}
}
}
// 'Loader.gcl' is resident at these addresses when loading *.gt1 files, therefore you can overwrite these locations only AFTER the loading process has finished
// Split up image scanlines into offscreen chunks and load the offscreen chunks into the correct onscreen memory locations after 'Loader.gcl' is done
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))
{
uint16_t chunkOffset = 0x0000, chunkAddr = 0x0000;
for(int i=0; i<gtRgbFile._header._width / RAM_SEGMENTS_SIZE; i++)
{
loadImageChunk(codeLine, codeLineStart, gtRgbFile._data, y, gtRgbFile._header._width, address, RAM_SEGMENTS_SIZE, chunkOffset, chunkAddr);
}
loadImageChunk(codeLine, codeLineStart, gtRgbFile._data, y, gtRgbFile._header._width, address, gtRgbFile._header._width % RAM_SEGMENTS_SIZE, chunkOffset, chunkAddr);
}
// Next destination row
address += stride;
}
return true;
}
/********************************************************************************************************************************/
/* Load Sprite */
/********************************************************************************************************************************/
bool loadSprite(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, const std::vector<std::string>& tokens, const std::string& filename, const Image::TgaFile& tgaFile, const Image::GtRgbFile& gtRgbFile)
{
if(Compiler::getCodeRomType() < Cpu::ROMv3)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : version error, 'LOAD SPRITE' requires ROMv3 or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
if(tokens.size() < 3 || tokens.size() > 5)
{
loadUsage(LoadSprite, codeLine, codeLineStart);
return false;
}
if(gtRgbFile._header._width % SPRITE_CHUNK_SIZE != 0)
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : sprite width not a multiple of %d, (%d x %d), for %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, SPRITE_CHUNK_SIZE, gtRgbFile._header._width, gtRgbFile._header._height, filename.c_str(), codeLine._text.c_str());
return false;
}
if(tokens.size() < 3)
{
loadUsage(LoadSprite, codeLine, codeLineStart);
return false;
}
// Unique sprite ID
std::string idToken = tokens[2];
Expression::Numeric idNumeric;
std::string idOperand;
if(Compiler::parseStaticExpression(codeLineIndex, idToken, idOperand, idNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, idToken.c_str(), codeLine._text.c_str());
return false;
}
int spriteId = int(std::lround(idNumeric._value));
if(Compiler::getDefDataSprites().find(spriteId) != Compiler::getDefDataSprites().end())
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : sprite id %d not unique : %s\n", codeLine._moduleName.c_str(), codeLineStart, spriteId, codeLine._text.c_str());
return false;
}
// Parse optional sprite flip
Compiler::SpriteFlipType flipType = Compiler::NoFlip;
if(tokens.size() >= 4)
{
std::string flipToken = tokens[3];
Expression::stripWhitespace(flipToken);
Expression::strToUpper(flipToken);
if(flipToken == "NOFLIP") flipType = Compiler::NoFlip;
else if(flipToken == "FLIPX") flipType = Compiler::FlipX;
else if(flipToken == "FLIPY") flipType = Compiler::FlipY;
else if(flipToken == "FLIPXY") flipType = Compiler::FlipXY;
else
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : unknown sprite flip type, %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, flipToken.c_str(), codeLine._text.c_str());
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : must use one of 'NOFLIP', 'FLIPX', 'FLIPY', 'FLIPXY' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
}
// Parse optional sprite overlap, (for last column)
uint16_t overlap = 0;
if(tokens.size() == 5)
{
std::string overlapToken = tokens[4];
Expression::Numeric overlapNumeric;
std::string overlapOperand;
if(Compiler::parseStaticExpression(codeLineIndex, overlapToken, overlapOperand, overlapNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, overlapToken.c_str(), codeLine._text.c_str());
return false;
}
overlap = uint16_t(std::lround(overlapNumeric._value));
}
// Build sprite data from image data
uint16_t numColumns = gtRgbFile._header._width / SPRITE_CHUNK_SIZE;
uint16_t remStripeChunks = gtRgbFile._header._height % Compiler::getSpriteStripeChunks();
uint16_t numStripesPerCol = gtRgbFile._header._height / Compiler::getSpriteStripeChunks() + int(remStripeChunks > 0);
uint16_t numStripeChunks = (numStripesPerCol == 1) ? gtRgbFile._header._height : Compiler::getSpriteStripeChunks();
std::vector<uint16_t> stripeAddrs;
std::vector<uint8_t> spriteData;
if(numColumns == 1 && overlap)
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : can't have a non zero overlap with a single column sprite : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Search sprite image for instancing
int parentInstance = 0;
bool isInstanced = false;
for(auto it=Compiler::getDefDataSprites().begin(); it!=Compiler::getDefDataSprites().end(); ++it)
{
if(it->second._filename == filename)
{
spriteData = it->second._data;
parentInstance = it->first;
isInstanced = true;
break;
}
}
// Allocate sprite memory
int addrIndex = 0;
uint16_t address = 0x0000;
for(int i=0; i<numColumns; i++)
{
// One stripe per column
if(numStripesPerCol == 1)
{
if(isInstanced)
{
address = Compiler::getDefDataSprites()[parentInstance]._stripeAddrs[addrIndex];
addrIndex += 2;
}
else
{
if(!Memory::getFreeRAM(Compiler::getSpriteStripeFitType(), numStripeChunks*SPRITE_CHUNK_SIZE + 1, Compiler::getSpriteStripeMinAddress(), Compiler::getRuntimeStart(), address))
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : getting sprite memory for stripe %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(stripeAddrs.size()/2 + 1), codeLine._text.c_str());
return false;
}
}
stripeAddrs.push_back(address);
// Destination offsets
switch(flipType)
{
case Compiler::NoFlip: stripeAddrs.push_back(uint16_t(0 + i*6));
if(i == numColumns - 1) stripeAddrs.back() -= overlap; // push last column closer to all other columns
break;
case Compiler::FlipX: stripeAddrs.push_back(uint16_t(0 + (numColumns-1-i)*6));
if(i != numColumns - 1) stripeAddrs.back() -= overlap; // push all other columns closer to last column
break;
case Compiler::FlipY: stripeAddrs.push_back(uint16_t((numStripeChunks-1)*256 + i*6));
if(i == numColumns - 1) stripeAddrs.back() -= overlap; // push last column closer to all other columns
break;
case Compiler::FlipXY: stripeAddrs.push_back(uint16_t((numStripeChunks-1)*256 + (numColumns-1-i)*6));
if(i != numColumns - 1) stripeAddrs.back() -= overlap; // push all other columns closer to last column
break;
default: break;
}
// Copy sprite data and delimiter
for(int j=0; j<numStripeChunks; j++)
{
for(int k=0; k<SPRITE_CHUNK_SIZE; k++)
{
spriteData.push_back(gtRgbFile._data[i*SPRITE_CHUNK_SIZE + j*SPRITE_CHUNK_SIZE*numColumns + k]);
}
}
spriteData.push_back(uint8_t(-gtRgbFile._header._height));
}
// Multiple stripes per column
else
{
// MAX_SPRITE_CHUNKS_PER_STRIPE stripes
for(int j=0; j<numStripesPerCol-1; j++)
{
if(isInstanced)
{
address = Compiler::getDefDataSprites()[parentInstance]._stripeAddrs[addrIndex];
addrIndex += 2;
}
else
{
if(!Memory::getFreeRAM(Compiler::getSpriteStripeFitType(), numStripeChunks*SPRITE_CHUNK_SIZE + 1, Compiler::getSpriteStripeMinAddress(), Compiler::getRuntimeStart(), address))
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : getting sprite memory failed for stripe %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(stripeAddrs.size()/2 + 1), codeLine._text.c_str());
return false;
}
}
stripeAddrs.push_back(address);
// Destination offsets
switch(flipType)
{
case Compiler::NoFlip: stripeAddrs.push_back(uint16_t(j*numStripeChunks*256 + i*6));
if(i == numColumns - 1) stripeAddrs.back() -= overlap; // push last column closer to all other columns
break;
case Compiler::FlipX: stripeAddrs.push_back(uint16_t(j*numStripeChunks*256 + (numColumns-1-i)*6));
if(i != numColumns - 1) stripeAddrs.back() -= overlap; // push all other columns closer to last column
break;
case Compiler::FlipY: stripeAddrs.push_back(uint16_t(((numStripesPerCol-1-j)*numStripeChunks+remStripeChunks-1)*256 + i*6));
if(i == numColumns - 1) stripeAddrs.back() -= overlap; // push last column closer to all other columns
break;
case Compiler::FlipXY: stripeAddrs.push_back(uint16_t(((numStripesPerCol-1-j)*numStripeChunks+remStripeChunks-1)*256 + (numColumns-1-i)*6));
if(i != numColumns - 1) stripeAddrs.back() -= overlap; // push all other columns closer to last column
break;
default: break;
}
// Copy sprite data and delimiter
for(int k=0; k<numStripeChunks; k++)
{
for(int l=0; l<SPRITE_CHUNK_SIZE; l++)
{
spriteData.push_back(gtRgbFile._data[i*SPRITE_CHUNK_SIZE + j*numStripeChunks*SPRITE_CHUNK_SIZE*numColumns + k*SPRITE_CHUNK_SIZE*numColumns + l]);
}
}
spriteData.push_back(255);
}
// Remainder stripe
if(isInstanced)
{
address = Compiler::getDefDataSprites()[parentInstance]._stripeAddrs[addrIndex];
addrIndex += 2;
}
else
{
if(!Memory::getFreeRAM(Compiler::getSpriteStripeFitType(), remStripeChunks*SPRITE_CHUNK_SIZE + 1, Compiler::getSpriteStripeMinAddress(), Compiler::getRuntimeStart(), address))
{
loadUsage(LoadSprite, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadSprite() : '%s:%d' : getting sprite memory failed for stripe %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(stripeAddrs.size()/2 + 1), codeLine._text.c_str());
return false;
}
}
stripeAddrs.push_back(address);
// Destination offsets
switch(flipType)
{
case Compiler::NoFlip: stripeAddrs.push_back(uint16_t((numStripesPerCol-1)*numStripeChunks*256 + i*6));
if(i == numColumns - 1) stripeAddrs.back() -= overlap; // push last column closer to all other columns
break;
case Compiler::FlipX: stripeAddrs.push_back(uint16_t((numStripesPerCol-1)*numStripeChunks*256 + (numColumns-1-i)*6));
if(i != numColumns - 1) stripeAddrs.back() -= overlap; // push all other columns closer to last column
break;
case Compiler::FlipY: stripeAddrs.push_back(uint16_t((remStripeChunks-1)*256 + i*6));
if(i == numColumns - 1) stripeAddrs.back() -= overlap; // push last column closer to all other columns
break;
case Compiler::FlipXY: stripeAddrs.push_back(uint16_t((remStripeChunks-1)*256 + (numColumns-1-i)*6));
if(i != numColumns - 1) stripeAddrs.back() -= overlap; // push all other columns closer to last column
break;
default: break;
}
// Copy sprite data and delimiter
for(int j=0; j<remStripeChunks; j++)
{
for(int k=0; k<SPRITE_CHUNK_SIZE; k++)
{
spriteData.push_back(gtRgbFile._data[i*SPRITE_CHUNK_SIZE + (numStripesPerCol-1)*numStripeChunks*SPRITE_CHUNK_SIZE*numColumns + j*SPRITE_CHUNK_SIZE*numColumns + k]);
}
}
spriteData.push_back(255);
}
}
Compiler::getDefDataSprites()[spriteId] = {spriteId, filename, tgaFile._header._width, tgaFile._header._height, numColumns, numStripesPerCol, numStripeChunks, remStripeChunks, stripeAddrs, spriteData, flipType, isInstanced};
return true;
}
/********************************************************************************************************************************/
/* Load font */
/********************************************************************************************************************************/
bool loadFont(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, const std::vector<std::string>& tokens, const std::string& filename, const Image::TgaFile& tgaFile, const Image::GtRgbFile& gtRgbFile)
{
if(Compiler::getCodeRomType() < Cpu::ROMv3)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : version error, 'LOAD FONT' requires ROMv3 or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
if(tokens.size() < 3 || tokens.size() > 4)
{
loadUsage(LoadFont, codeLine, codeLineStart);
return false;
}
// Unique font ID
std::string idToken = tokens[2];
Expression::Numeric idNumeric;
std::string idOperand;
if(Compiler::parseStaticExpression(codeLineIndex, idToken, idOperand, idNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, idToken.c_str(), codeLine._text.c_str());
return false;
}
int fontId = int(std::lround(idNumeric._value));
if(Compiler::getDefDataFonts().find(fontId) != Compiler::getDefDataFonts().end())
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : font id %d not unique : %s\n", codeLine._moduleName.c_str(), codeLineStart, fontId, codeLine._text.c_str());
return false;
}
// Foreground/background colours
uint16_t fgbgColour = 0x0000;
if(tokens.size() == 4)
{
std::string fgbgToken = tokens[3];
Expression::Numeric fgbgNumeric;
std::string fgbgOperand;
if(Compiler::parseStaticExpression(codeLineIndex, fgbgToken, fgbgOperand, fgbgNumeric) == Compiler::OperandInvalid)
{
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, fgbgToken.c_str(), codeLine._text.c_str());
return false;
}
fgbgColour = uint16_t(std::lround(fgbgNumeric._value));
}
// Width
if(gtRgbFile._header._width % FONT_WIDTH != 0)
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : font width %d is not a multiple of %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, gtRgbFile._header._width, FONT_WIDTH, codeLine._text.c_str());
return false;
}
// Height
if(gtRgbFile._header._height % FONT_HEIGHT != 0)
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : font height %d is not a multiple of %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, gtRgbFile._header._height, FONT_HEIGHT, codeLine._text.c_str());
return false;
}
// Load font mapping file
bool foundMapFile = true;
size_t nameSuffix = filename.find_last_of(".");
std::string filenameMap = filename.substr(0, nameSuffix) + ".map";
std::ifstream infile(filenameMap, std::ios::in);
if(!infile.is_open())
{
foundMapFile = false;
}
// Parse font mapping file
int maxIndex = -1;
uint16_t mapAddr = 0x0000;
std::vector<uint8_t> mapping(MAPPING_SIZE);
if(foundMapFile)
{
int ascii, index, line = 0;
while(!infile.eof())
{
infile >> ascii >> index;
if(index > maxIndex) maxIndex = index;
if(!infile.good() && !infile.eof())
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : error in mapping file %s on line %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, filenameMap.c_str(), line + 1, codeLine._text.c_str());
return false;
}
if(line >= MAPPING_SIZE) break;
mapping[line++] = uint8_t(index);
}
if(line != MAPPING_SIZE)
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : warning, found an incorrect number of map entries %d for file %s, should be %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, line - 1, filenameMap.c_str(), MAPPING_SIZE, codeLine._text.c_str());
return false;
}
if(!Memory::getFreeRAM(Memory::FitDescending, MAPPING_SIZE, USER_CODE_START, Compiler::getRuntimeStart(), mapAddr))
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : getting mapping memory for map size of %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, MAPPING_SIZE, codeLine._text.c_str());
return false;
}
}
// 8th line is implemented as a separate sprite call, to save memory and allow for more efficient memory packing
const int kCharHeight = FONT_HEIGHT-1;
// Copy font data and create delimiter
std::vector<uint8_t> charData;
std::vector<uint16_t> charAddrs;
std::vector<std::vector<uint8_t>> fontData;
for(int j=0; j<tgaFile._header._height; j+=FONT_HEIGHT)
{
for(int i=0; i<tgaFile._header._width; i+=FONT_WIDTH)
{
for(int l=0; l<kCharHeight; l++)
{
for(int k=0; k<FONT_WIDTH; k++)
{
uint8_t pixel = gtRgbFile._data[j*tgaFile._header._width + i + l*tgaFile._header._width + k];
if(fgbgColour)
{
if(pixel == 0x00) pixel = fgbgColour & 0x00FF;
if(pixel == 0x3F) pixel = fgbgColour >> 8;
}
charData.push_back(pixel);
}
}
charData.push_back(uint8_t(-(kCharHeight)));
fontData.push_back(charData);
charData.clear();
uint16_t address = 0x0000;
if(!Memory::getFreeRAM(Memory::FitDescending, (kCharHeight)*FONT_WIDTH + 1, USER_CODE_START, Compiler::getRuntimeStart(), address))
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : getting font memory for char %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(fontData.size() - 1), codeLine._text.c_str());
return false;
}
charAddrs.push_back(address);
}
}
if(foundMapFile && maxIndex + 1 != int(fontData.size()))
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : font mapping table does not match font data, found a mapping count of %d and a chars count of %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, maxIndex + 1, int(fontData.size()), codeLine._text.c_str());
return false;
}
// Create baseline for all chars in each font
uint16_t baseAddr = 0x0000;
if(!Memory::getFreeRAM(Memory::FitDescending, FONT_WIDTH + 1, USER_CODE_START, Compiler::getRuntimeStart(), baseAddr))
{
loadUsage(LoadFont, codeLine, codeLineStart);
fprintf(stderr, "Keywords::loadFont() : '%s:%d' : getting font memory for char %d failed : %s\n", codeLine._moduleName.c_str(), codeLineStart, int(fontData.size() - 1), codeLine._text.c_str());
return false;
}
Compiler::getDefDataFonts()[fontId] = {fontId, filenameMap, tgaFile._header._width, tgaFile._header._height, charAddrs, fontData, mapAddr, mapping, baseAddr, fgbgColour};
Linker::enableFontLinking();
return true;
}
bool LOAD(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 2 || tokens.size() > 5)
{
loadUsage(LoadType, codeLine, codeLineStart);
return false;
}
// Type
Expression::strToUpper(tokens[0]);
Expression::stripWhitespace(tokens[0]);
// Load WAVE
if(tokens[0] == "WAVE")
{
return loadWave(codeLine, codeLineIndex, codeLineStart, tokens);
}
// Load Midi
if(tokens[0] == "MIDI")
{
return loadMidi(codeLine, codeLineIndex, codeLineStart, tokens);
}
// Load Image, Sprite and Font
if(tokens[0] == "IMAGE" || tokens[0] == "SPRITE" || tokens[0] == "FONT")
{
std::string filename = tokens[1];
Expression::stripWhitespace(filename);
std::string ext = filename;
Expression::strToUpper(ext);
if(ext.find(".TGA") != std::string::npos)
{
std::string filepath = Loader::getFilePath();
size_t slash = filepath.find_last_of("\\/");
filepath = (slash != std::string::npos) ? filepath.substr(0, slash) : ".";
filename = filepath + "/" + filename;
Image::TgaFile tgaFile;
//fprintf(stderr, "\nKeywords::LOAD() : %s\n", filename.c_str());
if(!Image::loadTgaFile(filename, tgaFile))
{
fprintf(stderr, "Keywords::LOAD() : '%s:%d' : file '%s' failed to load : %s\n", codeLine._moduleName.c_str(), codeLineStart, filename.c_str(), codeLine._text.c_str());
return false;
}
// Load image/sprite/font
std::vector<uint8_t> data;
std::vector<uint16_t> optional;
Image::GtRgbFile gtRgbFile{GTRGB_IDENTIFIER, Image::GT_RGB_222, tgaFile._header._width, tgaFile._header._height, data, optional};
Image::convertRGB8toRGB2(tgaFile._data, gtRgbFile._data, tgaFile._header._width, tgaFile._header._height, tgaFile._imageOrigin);
// Image
if(tokens[0] == "IMAGE")
{
return loadImage(codeLine, codeLineIndex, codeLineStart, tokens, filename, tgaFile, gtRgbFile);
}
// Sprite
if(tokens[0] == "SPRITE")
{
return loadSprite(codeLine, codeLineIndex, codeLineStart, tokens, filename, tgaFile, gtRgbFile);
}
// Font
if(tokens[0] == "FONT")
{
return loadFont(codeLine, codeLineIndex, codeLineStart, tokens, filename, tgaFile, gtRgbFile);
}
}
}
loadUsage(LoadType, codeLine, codeLineStart);
return false;
}
bool SPRITE(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
if(Compiler::getCodeRomType() < Cpu::ROMv3)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
fprintf(stderr, "Keywords::SPRITE() : '%s:%d' : version error, 'SPRITE' requires ROMv3 or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 4)
{
fprintf(stderr, "Keywords::SPRITE() : '%s:%d' : syntax error, use 'SPRITE <NOFLIP/FLIPX/FLIPY/FLIPXY>, <id>, <x pos>, <y pos>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Flip type
static std::map<std::string, Compiler::SpriteFlipType> flipType = {{"NOFLIP", Compiler::NoFlip}, {"FLIPX", Compiler::FlipX}, {"FLIPY", Compiler::FlipY}, {"FLIPXY", Compiler::FlipXY}};
std::string flipToken = tokens[0];
Expression::stripWhitespace(flipToken);
Expression::strToUpper(flipToken);
if(flipType.find(flipToken) == flipType.end())
{
fprintf(stderr, "Keywords::SPRITE() : '%s:%d' : syntax error, use one of the correct flip types, 'SPRITE <NOFLIP/FLIPX/FLIPY/FLIPXY>, <id>, <x pos>, <y pos>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Sprite identifier
std::string idToken = tokens[1];
Expression::Numeric idParam;
if(Compiler::parseExpression(codeLineIndex, idToken, idParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SPRITE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, idToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "spriteId", false);
// Sprite X position
std::string xposToken = tokens[2];
Expression::Numeric xposParam;
if(Compiler::parseExpression(codeLineIndex, xposToken, xposParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SPRITE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, xposToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "spriteXY", false);
// Sprite Y position
std::string yposToken = tokens[3];
Expression::Numeric yposParam;
if(Compiler::parseExpression(codeLineIndex, yposToken, yposParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SPRITE() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, yposToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ADDI", "8", false);
Compiler::emitVcpuAsm("ST", "spriteXY + 1", false);
// Draw sprite
switch(flipType[flipToken])
{
case Compiler::NoFlip: Compiler::emitVcpuAsm("%DrawSprite", "", false); break;
case Compiler::FlipX: Compiler::emitVcpuAsm("%DrawSpriteX", "", false); break;
case Compiler::FlipY: Compiler::emitVcpuAsm("%DrawSpriteY", "", false); break;
case Compiler::FlipXY: Compiler::emitVcpuAsm("%DrawSpriteXY", "", false); break;
}
return true;
}
void usageSOUND(int msgType, Compiler::CodeLine& codeLine, int codeLineStart)
{
switch(msgType)
{
case 0: fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error, use 'SOUND <TYPE>, <params>, where <TYPE> = 'ON', 'MOD' or 'OFF' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case 1: fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error, use 'SOUND ON, <channel>, <frequency>, <optional volume>, <optional waveform>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case 2: fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error, use 'SOUND MOD, <channel>, <wavX>, <optional wavA>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
case 3: fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error, use 'SOUND OFF, <optional channel>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str()); break;
default: break;
}
}
bool SOUND(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 5)
{
usageSOUND(0, codeLine, codeLineStart);
return false;
}
// Sound state
std::string stateToken = tokens[0];
Expression::stripWhitespace(stateToken);
Expression::strToUpper(stateToken);
// Sound channel, (has to be between 1 and 4, saves an ADD/INC, no checking done)
if(tokens.size() >= 2)
{
std::string chanToken = tokens[1];
Expression::Numeric chanParam;
if(Compiler::parseExpression(codeLineIndex, chanToken, chanParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, chanToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "sndChannel + 1", false);
}
// Sound channels on
if(stateToken == "ON")
{
if(tokens.size() < 3)
{
usageSOUND(1, codeLine, codeLineStart);
return false;
}
std::string freqToken = tokens[2];
Expression::Numeric freqParam;
if(Compiler::parseExpression(codeLineIndex, freqToken, freqParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, freqToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "sndFrequency", false);
if(tokens.size() == 3)
{
Compiler::emitVcpuAsm("%SoundOn", "", false);
return true;
}
std::string volToken = tokens[3];
Expression::Numeric volParam;
if(Compiler::parseExpression(codeLineIndex, volToken, volParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, volToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "sndVolume", false);
if(tokens.size() == 4)
{
Compiler::emitVcpuAsm("LDI", "2", false);
Compiler::emitVcpuAsm("STW", "sndWaveType", false);
Compiler::emitVcpuAsm("%SoundOnV", "", false);
return true;
}
std::string wavToken = tokens[4];
Expression::Numeric wavParam;
if(Compiler::parseExpression(codeLineIndex, wavToken, wavParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, wavToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "sndWaveType", false);
Compiler::emitVcpuAsm("%SoundOnV", "", false);
return true;
}
// Sound channels modulation
if(stateToken == "MOD")
{
if(tokens.size() < 3 || tokens.size() > 4)
{
usageSOUND(2, codeLine, codeLineStart);
return false;
}
std::string waveXToken = tokens[2];
Expression::Numeric waveXParam;
if(Compiler::parseExpression(codeLineIndex, waveXToken, waveXParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, waveXToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "sndWaveType + 1", false);
if(tokens.size() == 4)
{
std::string waveAToken = tokens[3];
Expression::Numeric waveAParam;
if(Compiler::parseExpression(codeLineIndex, waveAToken, waveAParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SOUND() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, waveAToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "sndWaveType", false);
}
// Reset waveA
else
{
Compiler::emitVcpuAsm("LDI", "0", false);
Compiler::emitVcpuAsm("ST", "sndWaveType", false);
}
Compiler::emitVcpuAsm("%SoundMod", "", false);
return true;
}
// Sound channels off
if(stateToken == "OFF")
{
if(tokens.size() > 2)
{
usageSOUND(3, codeLine, codeLineStart);
return false;
}
// All sound channels off
if(tokens.size() == 1)
{
Compiler::emitVcpuAsm("%SoundAllOff", "", false);
return true;
}
// Single channel off
else
{
Compiler::emitVcpuAsm("%SoundOff", "", false);
return true;
}
}
usageSOUND(0, codeLine, codeLineStart);
return false;
}
void usageSET(Compiler::CodeLine& codeLine, int codeLineStart)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error, use 'SET <VAR NAME>, <PARAM>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
}
bool SET(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 3)
{
usageSET(codeLine, codeLineStart);
return false;
}
// System variable to set
std::string sysVarName = tokens[0];
Expression::stripWhitespace(sysVarName);
Expression::strToUpper(sysVarName);
// First parameter after system var name
std::string token1;
Expression::Numeric param1;
if(tokens.size() >= 2) token1 = tokens[1];
// Second parameter after system var name
std::string token2;
Expression::Numeric param2;
if(tokens.size() >= 3) token2 = tokens[2];
// Font id variable
if(sysVarName == "FONT_ID" && tokens.size() == 2)
{
if(Compiler::getCodeRomType() < Cpu::ROMv3)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
fprintf(stderr, "Keywords::SET() : '%s:%d' : version error, 'SET FONTID' requires ROMv3 or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDWI", "_fontId_", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "TIME_MODE")
{
Compiler::emitVcpuAsm("LDWI", "handleT_mode + 1", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "TIME_EPOCH")
{
Compiler::emitVcpuAsm("LDWI", "handleT_epoch + 1", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "TIME_S")
{
Compiler::emitVcpuAsm("LDWI", "_timeArray_ + 0", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "TIME_M")
{
Compiler::emitVcpuAsm("LDWI", "_timeArray_ + 1", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "TIME_H")
{
Compiler::emitVcpuAsm("LDWI", "_timeArray_ + 2", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "TIMER")
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "timerTick", false);
return true;
}
else if(sysVarName == "VBLANK_PROC")
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
fprintf(stderr, "Keywords::SET() : '%s:%d' : version error, 'SET VBLANK_PROC' requires ROMv5a or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(VBLANK_PROC), false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("DOKE", "register0", false);
return true;
}
else if(sysVarName == "VBLANK_FREQ")
{
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
std::string romTypeStr;
getRomTypeStr(Compiler::getCodeRomType(), romTypeStr);
fprintf(stderr, "Keywords::SET() : '%s:%d' : version error, 'SET VBLANK_FREQ' requires ROMv5a or higher, you are trying to link against '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, romTypeStr.c_str(), codeLine._text.c_str());
return false;
}
#if 0
// TODO: Fix this
// (256 - n) = vblank interrupt frequency, where n = 1 to 255
Compiler::emitVcpuAsm("LDWI", "realTS_rti + 2", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "register1", false);
Compiler::emitVcpuAsm("LDWI", "256", false);
Compiler::emitVcpuAsm("SUBW", "register1", false);
Compiler::emitVcpuAsm("POKE", "register0", false);
#endif
return true;
}
else if(sysVarName == "CURSOR_X" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "cursorXY", false);
return true;
}
else if(sysVarName == "CURSOR_Y" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "cursorXY + 1", false);
return true;
}
else if(sysVarName == "CURSOR_XY" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "cursorXY", false);
return true;
}
else if(sysVarName == "FG_COLOUR" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "fgbgColour + 1", false);
return true;
}
else if(sysVarName == "BG_COLOUR" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "fgbgColour", false);
return true;
}
else if(sysVarName == "FGBG_COLOUR" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "fgbgColour", false);
return true;
}
else if(sysVarName == "MIDI_STREAM" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "midiStream", false);
return true;
}
else if(sysVarName == "VIDEO_TOP" && tokens.size() == 2)
{
Compiler::emitVcpuAsm("LDWI", "giga_videoTop", false);
Compiler::emitVcpuAsm("STW", "register0", false);
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("POKE", "register0", false);
return true;
}
else if(sysVarName == "LED_TEMPO" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_ledTempo", false);
return true;
}
else if(sysVarName == "LED_STATE" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_ledState", false);
return true;
}
else if(sysVarName == "SOUND_TIMER" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_soundTimer", false);
return true;
}
else if(sysVarName == "CHANNEL_MASK" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_channelMask", false);
return true;
}
else if(sysVarName == "XOUT_MASK" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_xoutMask", false);
return true;
}
else if(sysVarName == "BUTTON_STATE" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_buttonState", false);
return true;
}
else if(sysVarName == "FRAME_COUNT" && tokens.size() == 2)
{
if(Compiler::parseExpression(codeLineIndex, token1, param1) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::SET() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, token1.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("ST", "giga_frameCount", false);
return true;
}
usageSET(codeLine, codeLineStart);
return false;
}
bool ASM(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
// If ASM is attached to a label, propagate it to the generated vCPU code, (if sub/proc/func already has a PUSH, then label is already valid, so ignore)
if(codeLine._labelIndex > -1 && !Compiler::getLabels()[codeLine._labelIndex]._gosub)
{
Compiler::setNextInternalLabel("_" + Compiler::getLabels()[codeLine._labelIndex]._name);
}
Compiler::setCodeIsAsm(true);
return true;
}
bool ENDASM(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
Compiler::setCodeIsAsm(false);
return true;
}
bool BCDADD(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::BCDADD() : '%s:%d' : syntax error, use 'BCDADD <src bcd address>, <dst bcd address>, <length>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// BCD src address
std::string srcToken = tokens[0];
Expression::Numeric srcParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, srcToken, srcParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDADD() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, srcToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdSrcAddr", false);
// BCD dst address
std::string dstToken = tokens[1];
Expression::Numeric dstParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, dstToken, dstParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDADD() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, dstToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdDstAddr", false);
// BCD length
std::string lenToken = tokens[2];
Expression::Numeric lenParam;
if(Compiler::parseExpression(codeLineIndex, lenToken, lenParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDADD() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, lenToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%BcdAdd", "", false);
return true;
}
bool BCDSUB(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::BCDSUB() : '%s:%d' : syntax error, use 'BCDSUB <src bcd address>, <dst bcd address>, <length>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// BCD src address
std::string srcToken = tokens[0];
Expression::Numeric srcParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, srcToken, srcParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDSUB() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, srcToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdSrcAddr", false);
// BCD dst address
std::string dstToken = tokens[1];
Expression::Numeric dstParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, dstToken, dstParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDSUB() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, dstToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdDstAddr", false);
// BCD length
std::string lenToken = tokens[2];
Expression::Numeric lenParam;
if(Compiler::parseExpression(codeLineIndex, lenToken, lenParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDSUB() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, lenToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%BcdSub", "", false);
return true;
}
bool BCDINT(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 2)
{
fprintf(stderr, "Keywords::BCDINT() : '%s:%d' : syntax error, use 'BCDINT <dst bcd address>, <int>' bcd value MUST contain at least 5 digits : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// BCD dst address
std::string srcToken = tokens[0];
Expression::Numeric srcParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, srcToken, srcParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDINT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, srcToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdDstAddr", false);
// Integer value, must be +ve, max value 42767, (32767 + 10000 because of how vASM sub Numeric::bcdInt works)
std::string intToken = tokens[1];
Expression::Numeric intParam;
if(Compiler::parseExpression(codeLineIndex, intToken, intParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDINT() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, intToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%BcdInt", "", false);
return true;
}
bool BCDCPY(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 3)
{
fprintf(stderr, "Keywords::BCDCPY() : '%s:%d' : syntax error, use 'BCDCPY <src bcd address>, <dst bcd address>, <length>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// BCD src address
std::string srcToken = tokens[0];
Expression::Numeric srcParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, srcToken, srcParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDCPY() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, srcToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdSrcAddr", false);
// BCD dst address
std::string dstToken = tokens[1];
Expression::Numeric dstParam(true); // true = allow static init
if(Compiler::parseExpression(codeLineIndex, dstToken, dstParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDCPY() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, dstToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "bcdDstAddr", false);
// BCD length
std::string lenToken = tokens[2];
Expression::Numeric lenParam;
if(Compiler::parseExpression(codeLineIndex, lenToken, lenParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::BCDCPY() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, lenToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("%BcdCpy", "", false);
return true;
}
bool GPRINTF(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
// Produces vCPU code and allocates Gigatron memory only when compiling on the emulator
#ifdef STAND_ALONE
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(foundPos);
UNREFERENCED_PARAM(tokenIndex);
UNREFERENCED_PARAM(codeLineIndex);
UNREFERENCED_PARAM(codeLineStart);
UNREFERENCED_PARAM(codeLine);
#else
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
// Allocate memory for gprintf variable's addresses if it hasn't been allocated already
uint16_t varsAddr = 0x0000;
if(Compiler::getGprintfVarsAddr() == 0x0000)
{
if(!Memory::getFreeRAM(Memory::FitDescending, GPRINT_VAR_ADDRS*2, USER_CODE_START, Compiler::getRuntimeStart(), varsAddr))
{
fprintf(stderr, "Keywords::GPRINTF() : '%s:%d' : not enough RAM for variables LUT of size %d : %s\n", codeLine._moduleName.c_str(), codeLineStart, GPRINT_VAR_ADDRS*2, codeLine._text.c_str());
return false;
}
Compiler::setGprintfVarsAddr(varsAddr);
}
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',');
if(tokens.size() < 2)
{
fprintf(stderr, "Keywords::GPRINTF() : '%s:%d' : syntax error, use 'GPRINTF \"<format string>\", <var1>, ... <varN>' : %s\n", codeLine._text.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
if(tokens.size() > GPRINT_VAR_ADDRS)
{
fprintf(stderr, "Keywords::GPRINTF() : '%s:%d' : maximum number of vars is '%d', found '%d' vars : %s\n", codeLine._moduleName.c_str(), codeLineStart, GPRINT_VAR_ADDRS, int(tokens.size()), codeLine._text.c_str());
return false;
}
// Format string
std::string formatStr = tokens[0];
Expression::stripNonStringWhitespace(formatStr);
if(formatStr[0] != '\"' || formatStr.back() != '\"')
{
fprintf(stderr, "Keywords::GPRINTF() : '%s:%d' : syntax error in string format, use 'GPRINTF \"<format string>\", <var1>, ... <varN>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Vars
Expression::Numeric numeric;
std::vector<std::string> variables;
for(int i=1; i<int(tokens.size()); i++)
{
uint16_t address = varsAddr + uint16_t((i-1)*2);
if(i == 1)
{
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(address), false);
Compiler::emitVcpuAsm("STW", "memDstAddr", false);
}
else
{
Compiler::emitVcpuAsm("INC", "memDstAddr", false);
Compiler::emitVcpuAsm("INC", "memDstAddr", false);
}
// Convert GBAS format to ASM format
variables.push_back("*" + Expression::wordToHexString(address));
if(!Expression::parse(tokens[i], codeLineIndex, numeric))
{
fprintf(stderr, "Keywords::GPRINTF() : '%s:%d' : syntax error in '%s' : %s\n", codeLine._moduleName.c_str(), codeLineStart, Expression::getExpression(), codeLine._text.c_str());
return false;
}
if(numeric._varType == Expression::Number || numeric._varType == Expression::IntVar16 || numeric._varType == Expression::StrVar)
{
Compiler::handleExpression(codeLineIndex, tokens[i], numeric);
}
// An extra doke as a dummy instruction so that gprintf is called at the correct address
Compiler::emitVcpuAsm("DOKE", "memDstAddr", false);
Compiler::emitVcpuAsm("DOKE", "memDstAddr", false);
}
return addGprintf(codeLine._code.substr(foundPos), formatStr, variables, Compiler::getGprintfVarsAddr(), codeLineIndex);
#endif
#ifdef STAND_ALONE
return true;
#endif
}
bool EXEC(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() < 1 || tokens.size() > 2)
{
fprintf(stderr, "Keywords::EXEC() : '%s:%d' : syntax error, expected 'EXEC <rom address>, <optional ram address>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// ROM address to load from
std::string romToken = tokens[0];
Expression::Numeric romParam;
if(Compiler::parseExpression(codeLineIndex, romToken, romParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::EXEC() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, romToken.c_str(), codeLine._text.c_str());
return false;
}
Compiler::emitVcpuAsm("STW", "giga_sysArg0", false);
// RAM address to execute at
if(tokens.size() == 2)
{
std::string ramToken = tokens[1];
Expression::Numeric ramParam;
if(Compiler::parseExpression(codeLineIndex, ramToken, ramParam) == Expression::IsInvalid)
{
fprintf(stderr, "Keywords::EXEC() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, ramToken.c_str(), codeLine._text.c_str());
return false;
}
}
else
{
// Default execute address
Compiler::emitVcpuAsm("LDWI", Expression::wordToHexString(DEFAULT_EXEC_ADDRESS), false);
}
// SYS_Exec_88
Compiler::emitVcpuAsm("%RomExec", "", false);
return true;
}
void openUsage(Compiler::CodeLine& codeLine, int codeLineStart)
{
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : usage, 'OPEN #<id>, <path>, <file>, <mode>', where mode is one of 'r', 'w', 'a', 'r+', 'w+', 'a+' : %s\n", codeLine._moduleName.c_str(),
codeLineStart, codeLine._text.c_str());
}
bool OPEN(Compiler::CodeLine& codeLine, int codeLineIndex, int codeLineStart, int tokenIndex, size_t foundPos, KeywordFuncResult& result)
{
UNREFERENCED_PARAM(result);
UNREFERENCED_PARAM(tokenIndex);
std::vector<std::string> tokens = Expression::tokenise(codeLine._code.substr(foundPos), ',', false);
if(tokens.size() != 4)
{
openUsage(codeLine, codeLineStart);
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : syntax error, wrong number of parameters : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// file id
std::string hashToken = tokens[0];
Expression::stripWhitespace(hashToken);
if(hashToken[0] != '#')
{
openUsage(codeLine, codeLineStart);
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : syntax error, missing '#' in '#<id>' : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Expression::Numeric idNumeric;
std::string idOperand;
std::string idToken = hashToken.substr(1);
if(Compiler::parseStaticExpression(codeLineIndex, idToken, idOperand, idNumeric) == Compiler::OperandInvalid)
{
openUsage(codeLine, codeLineStart);
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : syntax error in %s : %s\n", codeLine._moduleName.c_str(), codeLineStart, idToken.c_str(), codeLine._text.c_str());
return false;
}
int openId = int(std::lround(idNumeric._value));
if(Compiler::getDefDataOpens().find(openId) != Compiler::getDefDataOpens().end())
{
openUsage(codeLine, codeLineStart);
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : #%d is not unique : %s\n", codeLine._moduleName.c_str(), codeLineStart, openId, codeLine._text.c_str());
return false;
}
// File path
std::string pathToken = tokens[1];
Expression::stripWhitespace(pathToken);
if(pathToken == "") pathToken = "/";
// File name
std::string fileToken = tokens[2];
Expression::stripWhitespace(fileToken);
if(fileToken == "")
{
openUsage(codeLine, codeLineStart);
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : syntax error, <file> is empty : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
// Open mode
Compiler::DefDataOpen::OpenMode openMode;
std::string modeToken = tokens[3];
Expression::stripWhitespace(modeToken);
Expression::strToUpper(modeToken);
if(modeToken == "R") openMode = Compiler::DefDataOpen::OpenRead;
else if(modeToken == "W") openMode = Compiler::DefDataOpen::OpenWrite;
else if(modeToken == "A") openMode = Compiler::DefDataOpen::OpenAppend;
else if(modeToken == "R+") openMode = Compiler::DefDataOpen::OpenUpdateRW;
else if(modeToken == "W+") openMode = Compiler::DefDataOpen::OpenCreateRW;
else if(modeToken == "A+") openMode = Compiler::DefDataOpen::OpenAppendR;
else
{
openUsage(codeLine, codeLineStart);
fprintf(stderr, "Keywords::OPEN() : '%s:%d' : syntax error, <mode> is expecting one of 'r', 'w', 'a', 'r+', 'w+', 'a+', : %s\n", codeLine._moduleName.c_str(), codeLineStart, codeLine._text.c_str());
return false;
}
Compiler::getDefDataOpens()[openId] = {openId, pathToken, fileToken, openMode};
return true;
}
}