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

868 lines
37 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include "memory.h"
#include "cpu.h"
#include "assembler.h"
#include "compiler.h"
#include "linker.h"
namespace Linker
{
std::map<std::string, std::vector<std::string>> _subIncludeFiles;
std::map<int, uint16_t> _internalSubMap;
// TODO: use std::map
std::vector<Compiler::InternalSub> _internalSubs =
{
{0x0000, 0x0000, "romCheck" , "", false, false},
{0x0000, 0x0000, "romExec" , "", false, false},
{0x0000, 0x0000, "romRead" , "", false, false},
{0x0000, 0x0000, "realTimeStub" , "", false, false},
{0x0000, 0x0000, "convertEqOp" , "", false, false},
{0x0000, 0x0000, "convertNeOp" , "", false, false},
{0x0000, 0x0000, "convertLeOp" , "", false, false},
{0x0000, 0x0000, "convertGeOp" , "", false, false},
{0x0000, 0x0000, "convertLtOp" , "", false, false},
{0x0000, 0x0000, "convertGtOp" , "", false, false},
{0x0000, 0x0000, "convert8Arr2d" , "", false, false},
{0x0000, 0x0000, "convert8Arr3d" , "", false, false},
{0x0000, 0x0000, "convert16Arr2d" , "", false, false},
{0x0000, 0x0000, "convert16Arr3d" , "", false, false},
{0x0000, 0x0000, "setRealTimeProc0" , "", false, false},
{0x0000, 0x0000, "setRealTimeProc1" , "", false, false},
{0x0000, 0x0000, "setRealTimeProc2" , "", false, false},
{0x0000, 0x0000, "loadRegs" , "", false, false},
{0x0000, 0x0000, "saveRegs" , "", false, false},
{0x0000, 0x0000, "copyBytes" , "", false, false},
{0x0000, 0x0000, "copyWords" , "", false, false},
{0x0000, 0x0000, "copyLoaderImages" , "", false, false},
{0x0000, 0x0000, "resetVars" , "", false, false},
{0x0000, 0x0000, "absolute" , "", false, false},
{0x0000, 0x0000, "sign" , "", false, false},
{0x0000, 0x0000, "integerMin" , "", false, false},
{0x0000, 0x0000, "integerMax" , "", false, false},
{0x0000, 0x0000, "integerClamp" , "", false, false},
{0x0000, 0x0000, "power16bit" , "", false, false},
{0x0000, 0x0000, "power16bitExt" , "", false, false},
{0x0000, 0x0000, "multiply16bit" , "", false, false},
{0x0000, 0x0000, "multiply16bit_1" , "", false, false},
{0x0000, 0x0000, "divide16bit" , "", false, false},
{0x0000, 0x0000, "divide16bit_1" , "", false, false},
{0x0000, 0x0000, "rand16bit" , "", false, false},
{0x0000, 0x0000, "randMod16bit" , "", false, false},
{0x0000, 0x0000, "shiftLeft4bit" , "", false, false},
{0x0000, 0x0000, "shiftLeft8bit" , "", false, false},
{0x0000, 0x0000, "shiftRight1bit" , "", false, false},
{0x0000, 0x0000, "shiftRight2bit" , "", false, false},
{0x0000, 0x0000, "shiftRight3bit" , "", false, false},
{0x0000, 0x0000, "shiftRight4bit" , "", false, false},
{0x0000, 0x0000, "shiftRight5bit" , "", false, false},
{0x0000, 0x0000, "shiftRight6bit" , "", false, false},
{0x0000, 0x0000, "shiftRight7bit" , "", false, false},
{0x0000, 0x0000, "shiftRight8bit" , "", false, false},
{0x0000, 0x0000, "shiftRightSgn1bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn2bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn3bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn4bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn5bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn6bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn7bit", "", false, false},
{0x0000, 0x0000, "shiftRightSgn8bit", "", false, false},
{0x0000, 0x0000, "getArrayByte" , "", false, false},
{0x0000, 0x0000, "setArrayByte" , "", false, false},
{0x0000, 0x0000, "getArrayInt16" , "", false, false},
{0x0000, 0x0000, "setArrayInt16" , "", false, false},
{0x0000, 0x0000, "getArrayInt16Low" , "", false, false},
{0x0000, 0x0000, "setArrayInt16Low" , "", false, false},
{0x0000, 0x0000, "getArrayInt16High", "", false, false},
{0x0000, 0x0000, "setArrayInt16High", "", false, false},
{0x0000, 0x0000, "readIntVar" , "", false, false},
{0x0000, 0x0000, "readStrVar" , "", false, false},
{0x0000, 0x0000, "gotoNumericLabel" , "", false, false},
{0x0000, 0x0000, "gosubNumericLabel", "", false, false},
{0x0000, 0x0000, "scanlineMode" , "", false, false},
{0x0000, 0x0000, "waitVBlank" , "", false, false},
{0x0000, 0x0000, "waitVBlanks" , "", false, false},
{0x0000, 0x0000, "resetVideoFlags" , "", false, false},
{0x0000, 0x0000, "resetVideoTable" , "", false, false},
{0x0000, 0x0000, "initClearFuncs" , "", false, false},
{0x0000, 0x0000, "clearScreen" , "", false, false},
{0x0000, 0x0000, "clearRect" , "", false, false},
{0x0000, 0x0000, "clearLine" , "", false, false},
{0x0000, 0x0000, "clearVertBlinds" , "", false, false},
{0x0000, 0x0000, "clearCursorRow" , "", false, false},
{0x0000, 0x0000, "readPixel" , "", false, false},
{0x0000, 0x0000, "drawPixel" , "", false, false},
{0x0000, 0x0000, "drawHLine" , "", false, false},
{0x0000, 0x0000, "drawVLine" , "", false, false},
{0x0000, 0x0000, "drawLine" , "", false, false},
{0x0000, 0x0000, "drawLineExt" , "", false, false},
{0x0000, 0x0000, "drawLineLoop" , "", false, false},
{0x0000, 0x0000, "drawLineLoadXY" , "", false, false},
{0x0000, 0x0000, "drawLineSlow" , "", false, false},
{0x0000, 0x0000, "drawLineSlowExt" , "", false, false},
{0x0000, 0x0000, "drawLineSlowLoop" , "", false, false},
{0x0000, 0x0000, "drawLineSlowSwap" , "", false, false},
{0x0000, 0x0000, "drawVTLine" , "", false, false},
{0x0000, 0x0000, "drawVTLineExt" , "", false, false},
{0x0000, 0x0000, "drawVTLineLoop" , "", false, false},
{0x0000, 0x0000, "drawVTLineLoadXY" , "", false, false},
{0x0000, 0x0000, "drawCircle" , "", false, false},
{0x0000, 0x0000, "drawCircleExt1" , "", false, false},
{0x0000, 0x0000, "drawCircleExt2" , "", false, false},
{0x0000, 0x0000, "drawCircleF" , "", false, false},
{0x0000, 0x0000, "drawRect" , "", false, false},
{0x0000, 0x0000, "drawRectF" , "", false, false},
{0x0000, 0x0000, "drawPoly" , "", false, false},
{0x0000, 0x0000, "drawPolyRel" , "", false, false},
{0x0000, 0x0000, "setPolyRelFlipX" , "", false, false},
{0x0000, 0x0000, "setPolyRelFlipY" , "", false, false},
{0x0000, 0x0000, "atLineCursor" , "", false, false},
{0x0000, 0x0000, "drawSprite_" , "", false, false},
{0x0000, 0x0000, "drawSprite" , "", false, false},
{0x0000, 0x0000, "drawSpriteX" , "", false, false},
{0x0000, 0x0000, "drawSpriteY" , "", false, false},
{0x0000, 0x0000, "drawSpriteXY" , "", false, false},
{0x0000, 0x0000, "getSpriteLUT" , "", false, false},
{0x0000, 0x0000, "setMidiStream" , "", false, false},
{0x0000, 0x0000, "resetMidi" , "", false, false},
{0x0000, 0x0000, "playMidi" , "", false, false},
{0x0000, 0x0000, "playMidiVol" , "", false, false},
{0x0000, 0x0000, "midiStartNote" , "", false, false},
{0x0000, 0x0000, "midiGetNote" , "", false, false},
{0x0000, 0x0000, "resetMusic" , "", false, false},
{0x0000, 0x0000, "playMusic" , "", false, false},
{0x0000, 0x0000, "musicGetNote" , "", false, false},
{0x0000, 0x0000, "musicPlayNote" , "", false, false},
{0x0000, 0x0000, "soundAllOff" , "", false, false},
{0x0000, 0x0000, "soundOff" , "", false, false},
{0x0000, 0x0000, "soundOn" , "", false, false},
{0x0000, 0x0000, "soundOnV" , "", false, false},
{0x0000, 0x0000, "soundMod" , "", false, false},
{0x0000, 0x0000, "input" , "", false, false},
{0x0000, 0x0000, "inputExt1" , "", false, false},
{0x0000, 0x0000, "inputExt2" , "", false, false},
{0x0000, 0x0000, "inputCursor" , "", false, false},
{0x0000, 0x0000, "inputKeys" , "", false, false},
{0x0000, 0x0000, "inputIntVar" , "", false, false},
{0x0000, 0x0000, "inputStrVar" , "", false, false},
{0x0000, 0x0000, "inputReturn" , "", false, false},
{0x0000, 0x0000, "inputDelete" , "", false, false},
{0x0000, 0x0000, "inputPrint" , "", false, false},
{0x0000, 0x0000, "inputNewline" , "", false, false},
{0x0000, 0x0000, "printInit" , "", false, false},
{0x0000, 0x0000, "printText" , "", false, false},
{0x0000, 0x0000, "printLeft" , "", false, false},
{0x0000, 0x0000, "printRight" , "", false, false},
{0x0000, 0x0000, "printMid" , "", false, false},
{0x0000, 0x0000, "printLower" , "", false, false},
{0x0000, 0x0000, "printUpper" , "", false, false},
{0x0000, 0x0000, "printDigit" , "", false, false},
{0x0000, 0x0000, "printInt16" , "", false, false},
{0x0000, 0x0000, "printChr" , "", false, false},
{0x0000, 0x0000, "printChar" , "", false, false},
{0x0000, 0x0000, "printClip" , "", false, false},
{0x0000, 0x0000, "printHex" , "", false, false},
{0x0000, 0x0000, "printSpc" , "", false, false},
{0x0000, 0x0000, "atTextCursor" , "", false, false},
{0x0000, 0x0000, "newLineScroll" , "", false, false},
{0x0000, 0x0000, "integerStr" , "", false, false},
{0x0000, 0x0000, "stringChr" , "", false, false},
{0x0000, 0x0000, "stringSpc" , "", false, false},
{0x0000, 0x0000, "stringHex" , "", false, false},
{0x0000, 0x0000, "stringCopy" , "", false, false},
{0x0000, 0x0000, "stringCmp" , "", false, false},
{0x0000, 0x0000, "stringAdd" , "", false, false},
{0x0000, 0x0000, "stringConcat" , "", false, false},
{0x0000, 0x0000, "stringConcatLut" , "", false, false},
{0x0000, 0x0000, "stringLeft" , "", false, false},
{0x0000, 0x0000, "stringRight" , "", false, false},
{0x0000, 0x0000, "stringMid" , "", false, false},
{0x0000, 0x0000, "stringLower" , "", false, false},
{0x0000, 0x0000, "stringUpper" , "", false, false},
{0x0000, 0x0000, "stringDigit" , "", false, false},
{0x0000, 0x0000, "stringInt" , "", false, false},
{0x0000, 0x0000, "tickTime" , "", false, false},
{0x0000, 0x0000, "handleTime" , "", false, false},
{0x0000, 0x0000, "timeDigits" , "", false, false},
{0x0000, 0x0000, "timeString" , "", false, false},
{0x0000, 0x0000, "bcdAdd" , "", false, false},
{0x0000, 0x0000, "bcdSub" , "", false, false},
{0x0000, 0x0000, "bcdInt" , "", false, false},
{0x0000, 0x0000, "bcdDigits" , "", false, false},
{0x0000, 0x0000, "bcdCmp" , "", false, false},
{0x0000, 0x0000, "bcdCmpExt" , "", false, false},
{0x0000, 0x0000, "bcdCpy" , "", false, false},
};
std::vector<std::string> _subIncludesROMv1 =
{
"math.i" ,
"memory.i" ,
"flow_control.i",
"clear_screen.i",
"conv_conds.i" ,
"graphics.i" ,
"audio.i" ,
"input.i" ,
"print_text.i" ,
"string.i" ,
"numeric.i" ,
"time.i" ,
};
std::vector<std::string> _subIncludesROMv2 =
{
"math.i" ,
"memory.i" ,
"flow_control.i" ,
"clear_screen_ROMv2.i",
"conv_conds.i" ,
"graphics_ROMv2.i" ,
"audio.i" ,
"input.i" ,
"print_text_ROMv2.i" ,
"string.i" ,
"numeric.i" ,
"time.i" ,
};
std::vector<std::string> _subIncludesROMv3 =
{
"math.i" ,
"memory.i" ,
"flow_control.i" ,
"clear_screen_ROMv2.i",
"conv_conds.i" ,
"graphics_ROMv2.i" ,
"sprite_ROMv3.i" ,
"audio.i" ,
"input.i" ,
"print_text_ROMv3.i" ,
"string.i" ,
"numeric.i" ,
"time.i" ,
};
std::vector<std::string> _subIncludesROMv4 =
{
"math.i" ,
"memory.i" ,
"flow_control.i" ,
"clear_screen_ROMv2.i",
"conv_conds.i" ,
"graphics_ROMv2.i" ,
"sprite_ROMv3.i" ,
"audio.i" ,
"input.i" ,
"print_text_ROMv3.i" ,
"numeric.i" ,
"time.i" ,
};
std::vector<std::string> _subIncludesROMv5a =
{
"math_ROMv5a.i" ,
"memory_ROMv5a.i" ,
"flow_control_ROMv5a.i",
"clear_screen_ROMv5a.i",
"conv_conds_ROMv5a.i" ,
"graphics_ROMv5a.i" ,
"sprite_ROMv5a.i" ,
"audio_ROMv5a.i" ,
"input_ROMv5a.i" ,
"print_text_ROMv5a.i" ,
"string_ROMv5a.i" ,
"numeric_ROMv5a.i" ,
"time_ROMv5a.i" ,
};
bool getInternalSub(const std::string& name, Compiler::InternalSub& internalSub)
{
for(int i=0; i<int(_internalSubs.size()); i++)
{
if(_internalSubs[i]._name == name)
{
internalSub = _internalSubs[i];
return true;
}
}
return false;
}
bool setInternalSub(const std::string& name, const Compiler::InternalSub& internalSub)
{
for(int i=0; i<int(_internalSubs.size()); i++)
{
if(_internalSubs[i]._name == name)
{
_internalSubs[i] = internalSub;
return true;
}
}
return false;
}
bool setInternalSubToLoad(const std::string& name)
{
for(int i=0; i<int(_internalSubs.size()); i++)
{
if(_internalSubs[i]._name == name)
{
_internalSubs[i]._inUse = true;
return true;
}
}
return false;
}
void resetIncludeFiles(void)
{
_subIncludeFiles.clear();
}
void reset(void)
{
resetIncludeFiles();
_internalSubMap.clear();
for(int i=0; i<int(_internalSubs.size()); i++)
{
_internalSubs[i]._size = 0;
_internalSubs[i]._address = 0;
_internalSubs[i]._includeName = "";
_internalSubs[i]._loaded = false;
_internalSubs[i]._inUse = false;
}
}
bool initialise(void)
{
return true;
}
bool enableFontLinking(void)
{
resetIncludeFiles();
for(int i=0; i<int(_subIncludesROMv3.size()); i++)
{
size_t textPos = _subIncludesROMv3[i].find("text");
if(textPos != std::string::npos)
{
_subIncludesROMv3[i].replace(textPos, 4, "font");
}
}
for(int i=0; i<int(_subIncludesROMv5a.size()); i++)
{
size_t textPos = _subIncludesROMv5a[i].find("text");
if(textPos != std::string::npos)
{
_subIncludesROMv5a[i].replace(textPos, 4, "font");
}
}
return parseIncludes();
}
bool disableFontLinking(void)
{
for(int i=0; i<int(_subIncludesROMv3.size()); i++)
{
size_t textPos = _subIncludesROMv3[i].find("font");
if(textPos != std::string::npos)
{
_subIncludesROMv3[i].replace(textPos, 4, "text");
}
}
for(int i=0; i<int(_subIncludesROMv5a.size()); i++)
{
size_t textPos = _subIncludesROMv5a[i].find("font");
if(textPos != std::string::npos)
{
_subIncludesROMv5a[i].replace(textPos, 4, "text");
}
}
return true;
}
bool findSub(const std::vector<std::string>& tokens, const std::string& subName)
{
for(int i=0; i<int(tokens.size()); i++)
{
if(tokens[i] == subName) return true;
}
return false;
}
int getAsmOpcodeSizeOfIncludeSub(const std::string& includeName, const std::string& subName)
{
if(_subIncludeFiles.find(includeName) == _subIncludeFiles.end())
{
fprintf(stderr, "Linker::getAsmOpcodeSizeOfIncludeSub() : Include file was never loaded : '%s'\n", includeName.c_str());
return -1;
}
// Find sub in include
int numLines = 0;
int vasmSize = 0;
bool buildingSub = false;
for(int i=0; i<int(_subIncludeFiles[includeName].size()); i++)
{
std::vector<std::string> tokens = Expression::tokeniseLine(_subIncludeFiles[includeName][i]);
for(int j=0; j<int(tokens.size()); j++) Expression::stripWhitespace(tokens[j]);
if(!buildingSub && tokens.size() >= 2 && tokens[0] == "%SUB" && tokens[1] == subName)
{
buildingSub = true;
}
else if(buildingSub && tokens.size() >= 1 && tokens[0] == "%ENDS")
{
buildingSub = false;
break;
}
else if(buildingSub)
{
for(int j=0; j<int(tokens.size()); j++)
{
if(tokens[j].find_first_of(";#") != std::string::npos) break;
int size = Assembler::getAsmOpcodeSize(tokens[j]);
vasmSize += (size == 0) ? Compiler::getMacroSize(tokens[j]) : size;
}
}
numLines++;
}
//if(vasmSize) fprintf(stderr, "%s : %s : opcode size : %d\n", filename.c_str(), subName.c_str(), vasmSize);
return vasmSize;
}
bool getIncludeSubSize(const std::string& includeName, int subIndex)
{
uint16_t size = uint16_t(getAsmOpcodeSizeOfIncludeSub(includeName, _internalSubs[subIndex]._name));
if(size)
{
_internalSubs[subIndex]._size = size;
_internalSubs[subIndex]._includeName = includeName;
return true;
}
return false;
}
bool getInternalSubCode(const std::string& includeName, const std::vector<std::string>& includeVarsDone, std::vector<std::string>& code, int subIndex)
{
if(_subIncludeFiles.find(includeName) == _subIncludeFiles.end())
{
fprintf(stderr, "Linker::getInternalSubCode() : Include file was never loaded : '%s'\n", includeName.c_str());
return false;
}
std::string subName = _internalSubs[subIndex]._name;
// Check if include vars already done
bool varsDone = false;
for(int i=0; i<int(includeVarsDone.size()); i++)
{
if(includeVarsDone[i] == includeName)
{
varsDone = true;
break;
}
}
// Find sub in include
int numLines = 0;
bool buildingSub = false;
for(int i=0; i<int(_subIncludeFiles[includeName].size()); i++)
{
std::string line = _subIncludeFiles[includeName][i];
std::vector<std::string> tokens = Expression::tokenise(line, ' ');
for(int j=0; j<int(tokens.size()); j++) Expression::stripWhitespace(tokens[j]);
bool foundSub = (line.find("%SUB") != std::string::npos);
bool foundEnd = (line.find("%ENDS") != std::string::npos);
if(!buildingSub && foundSub)
{
varsDone = true;
if(findSub(tokens, subName)) buildingSub = true;
}
else if(buildingSub && foundEnd)
{
_internalSubs[subIndex]._loaded = true;
buildingSub = false;
return false;
}
else if(buildingSub)
{
code.push_back(line);
for(int j=0; j<int(_internalSubs.size()); j++)
{
if(!_internalSubs[j]._inUse && findSub(tokens, _internalSubs[j]._name))
{
_internalSubs[j]._inUse = true;
return true;
}
}
}
// Save all lines up until the first %SUB
else if(!buildingSub && !varsDone && !foundSub && !foundEnd)
{
code.push_back(line);
}
else if(!buildingSub && !varsDone && line.find_first_not_of(" \n\r\f\t\v") == std::string::npos)
{
code.push_back("\n");
}
numLines++;
}
return false;
}
// Marks internal sub to be loaded later
bool loadInternalSub(int subIndex, bool overwrite=false)
{
if(!overwrite && _internalSubs[subIndex]._address) return false;
_internalSubMap[subIndex] = _internalSubs[subIndex]._size;
_internalSubs[subIndex]._address = 0xFFFF;
_internalSubs[subIndex]._inUse = true;
return true;
}
bool loadInclude(const std::string& filename)
{
// Include file already loaded
if(_subIncludeFiles.find(filename) != _subIncludeFiles.end()) return true;
std::ifstream infile(Assembler::getIncludePath() + "/" + filename);
if(!infile.is_open())
{
fprintf(stderr, "Linker::loadInclude() : Failed to open file : '%s'\n", filename.c_str());
return false;
}
int numLines = 0;
std::vector<std::string> lineTokens;
while(!infile.eof())
{
std::string lineToken;
std::getline(infile, lineToken);
lineTokens.push_back(lineToken);
if(!infile.good() && !infile.eof())
{
fprintf(stderr, "Linker::loadInclude() : Bad lineToken : '%s' : in '%s' : on line %d\n", lineToken.c_str(), filename.c_str(), numLines+1);
return false;
}
numLines++;
}
_subIncludeFiles[filename] = lineTokens;
return true;
}
bool parseIncludes(void)
{
// Load include files into memory
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
for(int i=0; i<int(_subIncludesROMv5a.size()); i++)
{
if(!loadInclude(_subIncludesROMv5a[i])) return false;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv4)
{
for(int i=0; i<int(_subIncludesROMv4.size()); i++)
{
if(!loadInclude(_subIncludesROMv4[i])) return false;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv3)
{
for(int i=0; i<int(_subIncludesROMv3.size()); i++)
{
if(!loadInclude(_subIncludesROMv3[i])) return false;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv2)
{
for(int i=0; i<int(_subIncludesROMv2.size()); i++)
{
if(!loadInclude(_subIncludesROMv2[i])) return false;
}
}
else
{
for(int i=0; i<int(_subIncludesROMv1.size()); i++)
{
if(!loadInclude(_subIncludesROMv1[i])) return false;
}
}
// Parse loaded includes
for(int i=0; i<int(_internalSubs.size()); i++)
{
if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
{
for(int j=0; j<int(_subIncludesROMv5a.size()); j++)
{
if(getIncludeSubSize(_subIncludesROMv5a[j], i)) break;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv4)
{
for(int j=0; j<int(_subIncludesROMv4.size()); j++)
{
if(getIncludeSubSize(_subIncludesROMv4[j], i)) break;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv3)
{
for(int j=0; j<int(_subIncludesROMv3.size()); j++)
{
if(getIncludeSubSize(_subIncludesROMv3[j], i)) break;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv2)
{
for(int j=0; j<int(_subIncludesROMv2.size()); j++)
{
if(getIncludeSubSize(_subIncludesROMv2[j], i)) break;
}
}
else if(Compiler::getCodeRomType() >= Cpu::ROMv1)
{
for(int j=0; j<int(_subIncludesROMv1.size()); j++)
{
if(getIncludeSubSize(_subIncludesROMv1[j], i)) break;
}
}
}
return true;
}
bool linkInternalSubs(void)
{
fprintf(stderr, "\n*******************************************************\n");
fprintf(stderr, "* Linking \n");
fprintf(stderr, "*******************************************************\n");
fprintf(stderr, "* Name : Address : Size \n");
fprintf(stderr, "*******************************************************\n");
Assembler::clearDefines();
for(int i=0; i<int(Compiler::getCodeLines().size()); i++)
{
// Valid BASIC code
if(Compiler::getCodeLines()[i]._code.size() >= 2 && Compiler::getCodeLines()[i]._vasm.size())
{
// Vasm code
for(int j=0; j<int(Compiler::getCodeLines()[i]._vasm.size()); j++)
{
std::vector<std::string> tokens = Expression::tokenise(Compiler::getCodeLines()[i]._vasm[j]._code, ' ');
for(int k=0; k<int(tokens.size()); k++) Expression::stripWhitespace(tokens[k]);
// Handle %define, %if, %else and %endif
if(tokens.size())
{
std::string token = Expression::strUpper(tokens[0]);
if(token == "%DEFINE")
{
// Create define
if(!Assembler::createDefine(Compiler::getCodeLines()[i]._text, tokens, i)) return false;
continue;
}
else if(token == "%IF")
{
if(!Assembler::handleIfDefine(Compiler::getCodeLines()[i]._text, tokens, i)) return false;
continue;
}
else if(token == "%ENDIF")
{
if(!Assembler::handleEndIfDefine(Compiler::getCodeLines()[i]._text, tokens, i)) return false;
continue;
}
else if(token == "%ELSE")
{
if(!Assembler::handleElseDefine(Compiler::getCodeLines()[i]._text, tokens, i)) return false;
continue;
}
}
// If current define exists and is disabled, then skip current line
if(Assembler::isCurrentDefineDisabled()) continue;
// Check for internal subs in code and macros
for(int k=0; k<int(_internalSubs.size()); k++)
{
// Code
if(findSub(tokens, _internalSubs[k]._name))
{
loadInternalSub(k);
}
// Macros, (even nested)
std::string opcode = Compiler::getCodeLines()[i]._vasm[j]._opcode;
if(opcode.size() && opcode[0] == '%')
{
opcode.erase(0, 1);
if(Compiler::findMacroText(opcode, _internalSubs[k]._name))
{
loadInternalSub(k);
}
}
}
}
}
}
return true;
}
void collectInternalRuntime(void)
{
std::vector<std::string> includeVarsDone;
RESTART_COLLECTION:
for(int i=0; i<int(_internalSubs.size()); i++)
{
if(_internalSubs[i]._inUse && !_internalSubs[i]._loaded)
{
Compiler::getRuntime().push_back("\n");
std::vector<std::string> code;
// This is a BASIC compiler, it can't possibly work without at least one GOTO
if(getInternalSubCode(_internalSubs[i]._includeName, includeVarsDone, code, i)) goto RESTART_COLLECTION;
includeVarsDone.push_back(_internalSubs[i]._includeName);
for(int j=0; j<int(code.size()); j++)
{
Compiler::getRuntime().push_back(code[j] + "\n");
}
Compiler::getRuntime().push_back("\n");
}
}
}
void relinkInternalSubs(void)
{
uint16_t runtimeSize = 0;
for(int i=0; i<int(_internalSubs.size()); i++)
{
// Check for internal sub directly
if(_internalSubs[i]._inUse && _internalSubs[i]._loaded && _internalSubs[i]._address == 0x0000)
{
loadInternalSub(i);
}
// Runtime size
if(_internalSubs[i]._inUse && _internalSubs[i]._loaded && _internalSubs[i]._address) runtimeSize += _internalSubs[i]._size;
}
// Copy map to vector
std::vector<SubIndexSize> internalSubs;
std::copy(_internalSubMap.begin(), _internalSubMap.end(), std::back_inserter<std::vector<SubIndexSize>>(internalSubs));
// Sort vector, (prioritise by sub size)
std::sort(internalSubs.begin(), internalSubs.end(), [](const SubIndexSize& a, const SubIndexSize& b)
{
if(a.second != b.second) return (a.second > b.second);
return (a.first < b.first);
});
// Allocate memory for runtime
for(auto it=internalSubs.begin(); it!=internalSubs.end(); ++it)
{
uint16_t address;
if(Memory::getNextCodeAddress(Memory::FitDescending, Compiler::getRuntimeStart(), _internalSubs[it->first]._size, address))
{
Memory::takeFreeRAM(address, _internalSubs[it->first]._size, true);
// Save end of runtime/strings
if(address < Compiler::getRuntimeEnd()) Compiler::setRuntimeEnd(address);
fprintf(stderr, "* %-20s : 0x%04x : %2d bytes\n", _internalSubs[it->first]._name.c_str(), address, _internalSubs[it->first]._size);
_internalSubs[it->first]._address = address;
}
}
fprintf(stderr, "*******************************************************\n");
fprintf(stderr, "* Re-Linking \n");
fprintf(stderr, "*******************************************************\n");
fprintf(stderr, "* Start : End : Size \n");
fprintf(stderr, "*******************************************************\n");
fprintf(stderr, "* 0x%04x : 0x%04x : %5d bytes \n", Compiler::getRuntimeStart() & (Memory::getSizeRAM() - 1), Compiler::getRuntimeEnd(), runtimeSize);
fprintf(stderr, "*******************************************************\n");
}
void outputInternalSubs(void)
{
Compiler::getOutput().push_back("\n");
Compiler::getOutput().push_back(";****************************************************************************************************************************************\n");
Compiler::getOutput().push_back(";****************************************************************************************************************************************\n");
Compiler::getOutput().push_back(";* Internal runtime, DO NOT MODIFY PAST THIS POINT, modifications must be made in the original include files *\n");
Compiler::getOutput().push_back(";****************************************************************************************************************************************\n");
Compiler::getOutput().push_back(";****************************************************************************************************************************************\n");
Compiler::getOutput().push_back("\n");
for(int i=0; i<int(_internalSubs.size()); i++)
{
if(_internalSubs[i]._inUse)
{
Compiler::getOutput().push_back(_internalSubs[i]._name + std::string(LABEL_TRUNC_SIZE - _internalSubs[i]._name.size(), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(_internalSubs[i]._address) + "\n");
}
}
// Zero page call table is not needed when using ROMv5a and higher
if(Compiler::getCodeRomType() < Cpu::ROMv5a)
{
// Convert relational operators
{Compiler::getOutput().push_back("convertEqOpAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convertEqOpAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_CC_OPS + 0) + "\n");}
{Compiler::getOutput().push_back("convertNeOpAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convertNeOpAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_CC_OPS + 2) + "\n");}
{Compiler::getOutput().push_back("convertLeOpAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convertLeOpAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_CC_OPS + 4) + "\n");}
{Compiler::getOutput().push_back("convertGeOpAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convertGeOpAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_CC_OPS + 6) + "\n");}
{Compiler::getOutput().push_back("convertLtOpAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convertLtOpAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_CC_OPS + 8) + "\n");}
{Compiler::getOutput().push_back("convertGtOpAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convertGtOpAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_CC_OPS + 10) + "\n");}
// Get array address
{Compiler::getOutput().push_back("convert8Arr2dAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convert8Arr2dAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_ARRAY + 0) + "\n");}
{Compiler::getOutput().push_back("convert8Arr3dAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convert8Arr3dAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_ARRAY + 2) + "\n");}
{Compiler::getOutput().push_back("convert16Arr2dAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convert16Arr2dAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_ARRAY + 4) + "\n");}
{Compiler::getOutput().push_back("convert16Arr3dAddr" + std::string(LABEL_TRUNC_SIZE - strlen("convert16Arr3dAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(CONVERT_ARRAY + 6) + "\n");}
// Real time proc
{Compiler::getOutput().push_back("realTimeStubAddr" + std::string(LABEL_TRUNC_SIZE - strlen("realTimeStubAddr"), ' ') + "EQU" + std::string(9 - sizeof("EQU"), ' ') + Expression::wordToHexString(REAL_TIME_PROC) + "\n");}
}
Compiler::getOutput().push_back("\n");
for(int i=0; i<int(Compiler::getRuntime().size()); i++) Compiler::getOutput().push_back(Compiler::getRuntime()[i]);
}
}