6784 lines
316 KiB
C++
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, >MidiHdr))
|
|
{
|
|
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;
|
|
}
|
|
} |