868 lines
37 KiB
C++
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]);
|
|
}
|
|
} |