From 53e4babbe34e32ab4f138b1d82b54e39ad03e1b6 Mon Sep 17 00:00:00 2001 From: FeralChild64 Date: Mon, 9 Sep 2019 17:46:52 +0200 Subject: [PATCH] updates to the binning tool --- Makefile | 12 +- src/build_segment.cc | 1060 ++++++++++++++++++++++-------------------- 2 files changed, 569 insertions(+), 503 deletions(-) diff --git a/Makefile b/Makefile index 66226086..5f770905 100644 --- a/Makefile +++ b/Makefile @@ -84,9 +84,9 @@ build/chargen.rom: $(TOOL_PNGPREPARE) assets/8x8font.png # Dependencies - BASIC and KERNAL -build/target_generic/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_generic) $(foreach dir,$(SRCDIR_BASIC_generic),$(wildcard $(dir)/*.s)) build/,generated/gen_packed_messages.s -build/target_mega65/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_mega65) $(foreach dir,$(SRCDIR_BASIC_mega65),$(wildcard $(dir)/*.s)) build/,generated/gen_packed_messages.s -build/target_ultimate64/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_ultimate64) $(foreach dir,$(SRCDIR_BASIC_ultimate64),$(wildcard $(dir)/*.s)) build/,generated/gen_packed_messages.s +build/target_generic/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_generic) $(foreach dir,$(SRCDIR_BASIC_generic),$(wildcard $(dir)/*.s)) build/,generated/packed_messages.s +build/target_mega65/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_mega65) $(foreach dir,$(SRCDIR_BASIC_mega65),$(wildcard $(dir)/*.s)) build/,generated/packed_messages.s +build/target_ultimate64/OUTB.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_BASIC_ultimate64) $(foreach dir,$(SRCDIR_BASIC_ultimate64),$(wildcard $(dir)/*.s)) build/,generated/packed_messages.s build/target_generic/OUTK.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_KERNAL_generic) $(foreach dir,$(SRCDIR_KERNAL_generic),$(wildcard $(dir)/*.s)) build/target_mega65/OUTK.BIN: $(TOOL_ASSEMBLER) $(TOOL_BUILD_SEGMENT) $(SRCDIR_KERNAL_mega65) $(foreach dir,$(SRCDIR_KERNAL_mega65),$(wildcard $(dir)/*.s)) @@ -106,15 +106,15 @@ build/basic_ultimate64.rom: build/target_ultimate64/newrom # Rules - BASIC and KERNAL -build/,generated/gen_packed_messages.s: $(TOOL_COMPRESS_TEXT) +build/,generated/packed_messages.s: $(TOOL_COMPRESS_TEXT) @mkdir -p build/,generated - $(TOOL_COMPRESS_TEXT) > build/,generated/gen_packed_messages.s + $(TOOL_COMPRESS_TEXT) > build/,generated/packed_messages.s .PRECIOUS: build/target_%/OUTB.BIN build/target_%/OUTB.BIN: @mkdir -p build/target_$* @rm -f $@* build/target_$*/BASIC* - @$(TOOL_BUILD_SEGMENT) -s BASIC -i BASIC-$* -o OUTB.BIN -t build/target_$* -l a000 -h e4d2 $(SRCDIR_BASIC_$*) build/,generated + @$(TOOL_BUILD_SEGMENT) -s BASIC -i BASIC-$* -o OUTB.BIN -t build/target_$* -l a000 -h e4d2 $(SRCDIR_BASIC_$*) build/,generated/packed_messages.s .PRECIOUS: build/target_%/OUTK.BIN build/target_%/OUTK.BIN: diff --git a/src/build_segment.cc b/src/build_segment.cc index 3d658171..26667b1d 100644 --- a/src/build_segment.cc +++ b/src/build_segment.cc @@ -4,8 +4,11 @@ // #include +#include +#include #include #include +#include #include #include @@ -17,13 +20,14 @@ #include #include -#if defined(WIN32) || defined(_WIN32) - #define DIR_SEPARATOR "\\" -#else - #define DIR_SEPARATOR "/" -#endif +#if defined(WIN32) || defined(_WIN32) + #define DIR_SEPARATOR "\\" +#else + #define DIR_SEPARATOR "/" +#endif const std::string ASM_CMD = "java -jar assembler/KickAss.jar "; + const std::string LAB_OUT_START = "__routine_START_"; const std::string LAB_OUT_END = "__routine_END_"; const std::string LAB_IN_START = ".label __routine_START_"; @@ -42,53 +46,53 @@ std::string CMD_segInfo = "(unnamed)"; int CMD_loAddress = 0xC000; int CMD_hiAddress = 0xCFFF; -std::list CMD_dirList; +std::list CMD_inList; // // Common helper functions // -void bailOut() +void ERROR() { - exit(-1); + exit(-1); } -void bailOut(const std::string &message) +void ERROR(const std::string &message) { - std::cout << std::endl << "FATAL: " << message << std::endl << std::endl; - exit(-1); + std::cout << "\n" << "ERROR: " << message << "\n\n"; + exit(-1); } void printUsage() { - std::cout << std::endl << - "usage: build_segment [-o ] [-d ] [-t ]" << std::endl << - " [-l ] [-h ]" << std::endl << - " [-s ] [-i ] " << - std::endl << std::endl; + std::cout << "\n" << + "usage: build_segment [-o ] [-d ] [-t ]" << "\n" << + " [-l ] [-h ]" << "\n" << + " [-s ] [-i ] " << + "\n\n"; } void printBannerLineTop() { - std::cout << std::endl << std::endl << std::endl << BANNER_LINE << std::endl; + std::cout << "\n\n\n" << BANNER_LINE << "\n"; } void printBannerLineBottom() { - std::cout << BANNER_LINE << std::endl << std::endl; + std::cout << BANNER_LINE << "\n\n"; } void printBannerCollectAnalyse() { - printBannerLineTop(); - std::cout << "// Segment '" << CMD_segInfo << "' - collecting and analysing routines" << std::endl; - printBannerLineBottom(); + printBannerLineTop(); + std::cout << "// Segment '" << CMD_segInfo << "' - collecting and analysing routines" << "\n"; + printBannerLineBottom(); } void printBannerBinCompile() { - printBannerLineTop(); - std::cout << "// Segment '" << CMD_segInfo << "' - binning and compiling the assembly" << std::endl; + printBannerLineTop(); + std::cout << "// Segment '" << CMD_segInfo << "' - binning and compiling the assembly" << "\n"; printBannerLineBottom(); } @@ -96,16 +100,34 @@ void printBannerBinCompile() // Class definitions // +class DualStream +{ +public: + DualStream(std::ostream& str1, std::ostream& str2) : str1(str1), str2(str2) {} + + template DualStream &operator<<(const T& x) + { + str1 << x; + str2 << x; + + return *this; + } + +private: + std::ostream& str1; + std::ostream& str2; +}; + class SourceFile { public: - SourceFile(const std::string &fileName, const std::string &dirName); + SourceFile(const std::string &fileName, const std::string &dirName); - std::string fileName; - std::string dirName; + std::string fileName; + std::string dirName; bool floating; - std::vector content; + std::vector content; std::string label; int startAddr; // for fixed (non-floating) routines only @@ -119,35 +141,38 @@ class BinningProblem { public: - BinningProblem() {}; - BinningProblem(int loAddress, int hiAddress); + BinningProblem() {}; + BinningProblem(int loAddress, int hiAddress); - bool isSolved() const; + bool isSolved() const; - void addToProblem(SourceFile *routine); - void fillGap(int gapAddress, const std::list &routines); - void performObviousSteps(); - void removeUselessGaps(); - void sortFloatingRoutinesBySize(); + void addToProblem(SourceFile *routine); + void fillGap(DualStream &logOutput, int gapAddress, const std::list &routines); + void performObviousSteps(DualStream &logOutput); + void removeUselessGaps(DualStream &logOutput); + void sortFloatingRoutinesBySize(); - std::map fixedRoutines; // routines with location already fixed - std::map gaps; // gaps by address - std::vector floatingRoutines; // routines not allocated to any address yet; always keep them sorted! + std::map fixedRoutines; // routines with location already fixed + std::map gaps; // gaps by address + std::vector floatingRoutines; // routines not allocated to any address yet; always keep them sorted! }; class Solver { public: - Solver(BinningProblem &problem) : problem(problem) {} + Solver(BinningProblem &problem) : problem(problem), logOutput(logFile, std::cout) {} - void run(); + void run(); - int selectGapToFill(); - void findPartialSolution(int gapSize, std::list &partialSolution); + int selectGapToFill(); + void findPartialSolution(int gapSize, std::list &partialSolution); private: BinningProblem &problem; + std::ofstream logFile; + + DualStream logOutput; }; // @@ -166,119 +191,147 @@ BinningProblem GLOBAL_binningProblem; void parseCommandLine(int argc, char **argv) { - int opt; + int opt; - // Retrieve command line options + // Retrieve command line options while ((opt = getopt(argc, argv, "o:d:t:l:h:s:i:")) != -1) { switch(opt) { - case 'o': CMD_outFile = optarg; break; - case 't': CMD_tmpDir = optarg; break; - case 's': CMD_segName = optarg; break; - case 'i': CMD_segInfo = optarg; break; - case 'l': CMD_loAddress = strtol(optarg, nullptr ,16); break; - case 'h': CMD_hiAddress = strtol(optarg, nullptr ,16); break; - default: printUsage(); bailOut(); + case 'o': CMD_outFile = optarg; break; + case 't': CMD_tmpDir = optarg; break; + case 's': CMD_segName = optarg; break; + case 'i': CMD_segInfo = optarg; break; + case 'l': CMD_loAddress = strtol(optarg, nullptr ,16); break; + case 'h': CMD_hiAddress = strtol(optarg, nullptr ,16); break; + default: printUsage(); ERROR(); } } - // Retrieve directory list + // Retrieve file/directory list for (int idx = optind; idx < argc; idx++) { - CMD_dirList.push_back(argv[idx]); + CMD_inList.push_back(argv[idx]); } - if (CMD_dirList.empty()) { printUsage(); bailOut("empty directory list"); } + if (CMD_inList.empty()) { printUsage(); ERROR("empty directory/file list"); } } void readSourceFiles() { - for (const auto &dirName : CMD_dirList) - { - DIR *dirHandle = opendir(dirName.c_str()); - if (!dirHandle) bailOut(std::string("unable to open directory '") + dirName + "'"); + struct stat statBuf; + for (const auto &objName : CMD_inList) + { + if (stat(objName.c_str(), &statBuf) < 0) + { + ERROR(std::string("can't get information about '") + objName + "'"); + } - struct dirent *dirEntry; + if (S_ISREG(statBuf.st_mode)) + { + // This is a regular file + + char *tmp1 = strdup(objName.c_str()); + char *tmp2 = strdup(objName.c_str()); + + std::string dirName = dirname(tmp1); + std::string fileName = basename(tmp2); + + free(tmp1); + free(tmp2); + + GLOBAL_sourceFiles.push_back(SourceFile(fileName, dirName)); + GLOBAL_maxFileNameLen = std::max(GLOBAL_maxFileNameLen, fileName.length()); + continue; + } + + // This should be a directory + + DIR *dirHandle = opendir(objName.c_str()); + if (!dirHandle) ERROR(std::string("unable to open directory '") + objName + "'"); + + struct dirent *dirEntry; while ((dirEntry = readdir(dirHandle)) != nullptr) { - const std::string fileName = dirEntry->d_name; + const std::string fileName = dirEntry->d_name; // Filter-out files which are not assembler files, temporary, etc. - if (fileName.length() < 3) continue; + if (fileName.length() < 3) continue; if (fileName.front() == '#') continue; if (fileName.front() == '~') continue; if (fileName.substr(fileName.length() - 2) != ".s") continue; // Add file to the global list - GLOBAL_sourceFiles.push_back(SourceFile(fileName, dirName)); + GLOBAL_sourceFiles.push_back(SourceFile(fileName, objName)); GLOBAL_maxFileNameLen = std::max(GLOBAL_maxFileNameLen, fileName.length()); } closedir(dirHandle); - } + } - if (GLOBAL_sourceFiles.empty()) bailOut("no source files found"); + if (GLOBAL_sourceFiles.empty()) ERROR("no source files found"); - // Sort the file list by name, to provide deterministic results + // Sort the file list by name, to provide deterministic results - auto compare = [](const SourceFile &a, const SourceFile &b) -> bool { return a.fileName.compare(b.fileName) < 0; }; - GLOBAL_sourceFiles.sort(compare); + auto compare = [](const SourceFile &a, const SourceFile &b) -> bool { return a.fileName.compare(b.fileName) < 0; }; + GLOBAL_sourceFiles.sort(compare); } void checkInputFileLabels() { - std::set usedLabels; - for (const auto &sourceFile : GLOBAL_sourceFiles) - { - if (usedLabels.count(sourceFile.label) != 0) - { - bailOut(std::string("input file '") + sourceFile.fileName + "' has a name too similar to another one"); - } + std::set usedLabels; + for (const auto &sourceFile : GLOBAL_sourceFiles) + { + if (usedLabels.count(sourceFile.label) != 0) + { + ERROR(std::string("input file '") + sourceFile.fileName + "' has a name too similar to another one"); + } - usedLabels.insert(sourceFile.label); - } + usedLabels.insert(sourceFile.label); + } } void calcRoutineSizes() { - const std::string nameBase = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_sizetest"; + const std::string nameBase = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_sizetest"; const std::string outFileName = nameBase + ".s"; const std::string symFileName = nameBase + ".sym"; - // Remove old files + // Remove old files - unlink(outFileName.c_str()); - unlink(symFileName.c_str()); + unlink(outFileName.c_str()); + unlink(symFileName.c_str()); - // Write test file to determine routine sizes + // Write test file to determine routine sizes std::ofstream outFile(outFileName, std::fstream::out | std::fstream::trunc); - if (!outFile.good()) bailOut(std::string("can't open temporary file '") + outFileName + "'"); + if (!outFile.good()) ERROR(std::string("can't open temporary file '") + outFileName + "'"); // Start at $100, so that no local data gets accesses using ZP addressing modes // during this pass, which would otherwise upset things later - outFile << std::endl << ".segment " << CMD_segName << " [start=$100, min=$100, max=$FFFF]" << std::endl; + outFile << "\n" << ".segment " << CMD_segName << " [start=$100, min=$100, max=$FFFF]" << "\n"; + outFile << "#define BUILD_" << CMD_segName << "\n"; for (const auto &sourceFile : GLOBAL_sourceFiles) { - outFile << std::endl << std::endl << std::endl << std::endl; - outFile << "// Source file: " << sourceFile.fileName << std::endl << std::endl; - outFile << LAB_OUT_START << sourceFile.label << ":" << std::endl << std::endl; + outFile << "\n\n\n\n"; + outFile << "/* Source file: " << sourceFile.fileName << " */" << "\n\n"; + outFile << ".memblock \"" << sourceFile.fileName << "\"" << "\n"; + outFile << LAB_OUT_START << sourceFile.label << ":" << "\n\n"; - outFile << std::string(sourceFile.content.begin(), sourceFile.content.end()); + outFile << std::string(sourceFile.content.begin(), sourceFile.content.end()); - outFile << std::endl << std::endl; - outFile << LAB_OUT_END << sourceFile.label << ":" << std::endl; + outFile << "\n\n"; + outFile << LAB_OUT_END << sourceFile.label << ":" << "\n"; } - if (!outFile.good()) bailOut(std::string("error writing temporary file '") + outFileName + "'"); + if (!outFile.good()) ERROR(std::string("error writing temporary file '") + outFileName + "'"); outFile.close(); // All written - now launch the assembler @@ -287,112 +340,110 @@ void calcRoutineSizes() std::cout << std::flush; if (0 != system(cmd.c_str())) { - bailOut("assembler running failed"); + ERROR("assembler running failed"); } // Read addresses - std::ifstream symFile; + std::ifstream symFile; symFile.open(symFileName); - if (!symFile.good()) bailOut(std::string("unable to open results file '") + symFileName + "'"); + if (!symFile.good()) ERROR(std::string("unable to open results file '") + symFileName + "'"); - std::string line; - while (std::getline(symFile, line)) - { - bool startLabel; + std::string line; + while (std::getline(symFile, line)) + { + bool startLabel; - if (0 == line.compare(0, LAB_IN_START.size(), LAB_IN_START)) - { - line.erase(0, LAB_IN_START.length()); - startLabel = true; - } - else if (0 == line.compare(0, LAB_IN_END.size(), LAB_IN_END)) - { - line.erase(0, LAB_IN_END.length()); - startLabel = false; - } - else continue; + if (0 == line.compare(0, LAB_IN_START.size(), LAB_IN_START)) + { + line.erase(0, LAB_IN_START.length()); + startLabel = true; + } + else if (0 == line.compare(0, LAB_IN_END.size(), LAB_IN_END)) + { + line.erase(0, LAB_IN_END.length()); + startLabel = false; + } + else continue; - // Decode address, write it into the object + // Decode address, write it into the object - auto eqPos = line.rfind('='); + auto eqPos = line.rfind('='); - auto address = strtol(line.substr(eqPos + 2).c_str(), nullptr ,16); - std::string refLabel = line.substr(0, eqPos); + auto address = strtol(line.substr(eqPos + 2).c_str(), nullptr ,16); + std::string refLabel = line.substr(0, eqPos); - for (auto &sourceFile : GLOBAL_sourceFiles) - { - if (refLabel.compare(sourceFile.label) != 0) continue; - if (startLabel) - { - sourceFile.testAddrStart = address; - } - else - { - sourceFile.testAddrEnd = address; - } - } - } + for (auto &sourceFile : GLOBAL_sourceFiles) + { + if (refLabel.compare(sourceFile.label) != 0) continue; + if (startLabel) + { + sourceFile.testAddrStart = address; + } + else + { + sourceFile.testAddrEnd = address; + } + } + } symFile.close(); // Calculate size of each and every routine for (auto &sourceFile : GLOBAL_sourceFiles) - { - if (sourceFile.testAddrStart <= 0 || sourceFile.testAddrEnd <= 0 || - sourceFile.testAddrStart > sourceFile.testAddrEnd) - { - bailOut(std::string("unable to determine code length in '") + sourceFile.fileName + "'"); - } + { + if (sourceFile.testAddrStart <= 0 || sourceFile.testAddrEnd <= 0 || + sourceFile.testAddrStart > sourceFile.testAddrEnd) + { + ERROR(std::string("unable to determine code length in '") + sourceFile.fileName + "'"); + } - sourceFile.codeLength = sourceFile.testAddrEnd - sourceFile.testAddrStart; - GLOBAL_totalRoutinesSize += sourceFile.codeLength; - } + sourceFile.codeLength = sourceFile.testAddrEnd - sourceFile.testAddrStart; + GLOBAL_totalRoutinesSize += sourceFile.codeLength; + } - // Check whether total code size is sane + // Check whether total code size is sane - if (0 == GLOBAL_totalRoutinesSize) bailOut("total code size is 0"); - if (signed(GLOBAL_totalRoutinesSize) > CMD_hiAddress - CMD_loAddress) - { - bailOut(std::string("total code size is ") + std::to_string(GLOBAL_totalRoutinesSize) + ", too much for this segment!"); - } + if (0 == GLOBAL_totalRoutinesSize) ERROR("total code size is 0"); + if (signed(GLOBAL_totalRoutinesSize) > CMD_hiAddress - CMD_loAddress) + { + ERROR(std::string("total code size is ") + std::to_string(GLOBAL_totalRoutinesSize) + ", too much for this segment!"); + } - // Sort the file list by code size, starting from the smallest + // Sort the file list by code size, starting from the smallest - auto compare = [](const SourceFile &a, const SourceFile &b) -> bool { return a.codeLength < b.codeLength; }; - GLOBAL_sourceFiles.sort(compare); + auto compare = [](const SourceFile &a, const SourceFile &b) -> bool { return a.codeLength < b.codeLength; }; + GLOBAL_sourceFiles.sort(compare); - // Move zero-sized element to a separate list + // Move zero-sized element to a separate list - while (GLOBAL_sourceFiles.front().codeLength == 0) - { - GLOBAL_sourceFiles_noCode.push_back(GLOBAL_sourceFiles.front()); - GLOBAL_sourceFiles.pop_front(); - } + while (GLOBAL_sourceFiles.front().codeLength == 0) + { + GLOBAL_sourceFiles_noCode.push_back(GLOBAL_sourceFiles.front()); + GLOBAL_sourceFiles.pop_front(); + } } void prepareBinningProblem() { - // Prepare the log file + // Prepare the log file - const std::string logFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_binproblem.log"; - unlink(logFileName.c_str()); - std::ofstream logFile(logFileName, std::fstream::out | std::fstream::trunc); - std::string logLine; + const std::string logFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_binproblem.log"; + unlink(logFileName.c_str()); + std::ofstream logFile(logFileName, std::fstream::out | std::fstream::trunc); + DualStream logOutput(logFile, std::cout); - // Print out code length information + // Print out code length information - for (const auto &sourceFile : GLOBAL_sourceFiles) - { - std::string spacing; - spacing.resize(GLOBAL_maxFileNameLen + 4 - sourceFile.fileName.length(), ' '); - std::string floating = sourceFile.floating ? "(floating) " : " "; - logLine = "file: " + floating + sourceFile.fileName + spacing + "size: " + std::to_string(sourceFile.codeLength); + for (const auto &sourceFile : GLOBAL_sourceFiles) + { + std::string spacing; + spacing.resize(GLOBAL_maxFileNameLen + 4 - sourceFile.fileName.length(), ' '); + std::string floating = sourceFile.floating ? "(floating) " : " "; - logFile << logLine << std::endl; - std::cout << logLine << std::endl; - } + logOutput << "file: " << floating << sourceFile.fileName << spacing << "size: " << std::to_string(sourceFile.codeLength) << "\n"; + } // Create the binning problem @@ -400,99 +451,97 @@ void prepareBinningProblem() for (auto &sourceFile : GLOBAL_sourceFiles) { - GLOBAL_binningProblem.addToProblem(&sourceFile); + GLOBAL_binningProblem.addToProblem(&sourceFile); } // Print out some more statistics - logLine = "free space (after floating routines are placed): " + - std::to_string(CMD_hiAddress - CMD_loAddress - GLOBAL_totalRoutinesSize) ; - logFile << std::endl << logLine << std::endl; - std::cout << std::endl << logLine << std::endl; - logLine = "number of floating routines: " + - std::to_string(GLOBAL_binningProblem.floatingRoutines.size()); - logFile << logLine << std::endl; - std::cout << logLine << std::endl; - logLine = "number of gaps for the floating routines: " + - std::to_string(GLOBAL_binningProblem.gaps.size()); - logFile << logLine << std::endl << std::endl; - std::cout << logLine << std::endl << std::endl; + logOutput << "\n" << + "free space (after floating routines are placed): " << + std::to_string(CMD_hiAddress - CMD_loAddress - GLOBAL_totalRoutinesSize) << "\n" << + "number of floating routines: " << + std::to_string(GLOBAL_binningProblem.floatingRoutines.size()) << "\n" << + "number of gaps for the floating routines: " << + std::to_string(GLOBAL_binningProblem.gaps.size()) << "\n" << + "\n"; // Print out the available gaps - logLine = "gap address: $"; for (auto& gap : GLOBAL_binningProblem.gaps) { - std::string gapSize = std::string(" size: ") + std::to_string(gap.second); - logFile << logLine << std::uppercase << std::hex << gap.first << gapSize << std::endl; - std::cout << logLine << std::uppercase << std::hex << gap.first << gapSize << std::endl; + logOutput << "gap address: $" << std::uppercase << std::hex << gap.first << + " size: " << std::to_string(gap.second) << "\n"; } - std::cout << std::endl; + logOutput << "\n"; // Close the log file - if (!logFile.good()) bailOut(std::string("error writing log file '") + logFileName + "'"); - logFile.close(); + if (!logFile.good()) ERROR(std::string("error writing log file '") + logFileName + "'"); + logFile.close(); } void solveBinningProblem() { - std::cout << "trying to solve the routine binning problem" << std::endl << std::endl; + std::cout << "trying to solve the routine binning problem" << "\n\n"; - // Do some routine actions on the binning problem object + // Do some routine actions on the binning problem object - Solver solver(GLOBAL_binningProblem); - solver.run(); + Solver solver(GLOBAL_binningProblem); + solver.run(); - if (!GLOBAL_binningProblem.isSolved()) - { - bailOut("unable to solve the routine binning problem"); - } + if (!GLOBAL_binningProblem.isSolved()) + { + ERROR("unable to solve the routine binning problem"); + } - std::cout << std::endl; + std::cout << "\n"; } void compileSegment() { - // First combine everything into one assembler file + // First combine everything into one assembler file - const std::string outFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_combined.s"; - unlink(outFileName.c_str()); - std::ofstream outFile(outFileName, std::fstream::out | std::fstream::trunc); - if (!outFile.good()) bailOut(std::string("can't open temporary file '") + outFileName + "'"); + const std::string outFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_combined.s"; + unlink(outFileName.c_str()); + std::ofstream outFile(outFileName, std::fstream::out | std::fstream::trunc); + if (!outFile.good()) ERROR(std::string("can't open temporary file '") + outFileName + "'"); - // Write the header + // Write the header - outFile << std::endl << ".segment " << CMD_segName << - " [start=$" << std::hex << CMD_loAddress << - ", min=$" << std::hex << CMD_loAddress << - ", max=$" << std::hex << CMD_hiAddress << - ", outBin=\"" << CMD_outFile << "\", fill]" << - std::endl; + outFile << "\n" << + ".segment " << CMD_segName << + " [start=$" << std::hex << CMD_loAddress << + ", min=$" << std::hex << CMD_loAddress << + ", max=$" << std::hex << CMD_hiAddress << + ", outBin=\"" << CMD_outFile << "\", fill]" << + "\n"; + outFile << "#define BUILD_" << CMD_segName << "\n"; - // Write files which only contain definitions (no routines) + // Write files which only contain definitions (no routines) for (const auto &sourceFile : GLOBAL_sourceFiles_noCode) { - outFile << std::endl << std::endl << std::endl << std::endl; - outFile << "// Source file: " << sourceFile.fileName << std::endl << std::endl; - outFile << std::string(sourceFile.content.begin(), sourceFile.content.end()); - outFile << std::endl; + outFile << "\n\n\n\n"; + outFile << "/* Source file: " << sourceFile.fileName << " */" << "\n\n"; + outFile << ".memblock \"" << sourceFile.fileName << "\"" << "\n"; + outFile << std::string(sourceFile.content.begin(), sourceFile.content.end()); + outFile << "\n"; } // Write remaining files, these should be placed under their proper locations for (const auto &routine : GLOBAL_binningProblem.fixedRoutines) { - outFile << std::endl << std::endl << std::endl << std::endl; - outFile << "// Source file: " << routine.second->fileName << std::endl << std::endl; - outFile << "\t* = $" << std::hex << routine.first << std::endl << std::endl; - outFile << std::string(routine.second->content.begin(), routine.second->content.end()); - outFile << std::endl; + outFile << "\n\n\n\n"; + outFile << "/* Source file: " << routine.second->fileName << " */" << "\n\n"; + outFile << ".memblock \"" << routine.second->fileName << "\"" << "\n"; + outFile << "\t* = $" << std::hex << routine.first << "\n\n"; + outFile << std::string(routine.second->content.begin(), routine.second->content.end()); + outFile << "\n"; } - if (!outFile.good()) bailOut(std::string("error writing temporary file '") + outFileName + "'"); + if (!outFile.good()) ERROR(std::string("error writing temporary file '") + outFileName + "'"); outFile.close(); // All written - now launch the assembler @@ -501,7 +550,7 @@ void compileSegment() std::cout << std::flush; if (0 != system(cmd.c_str())) { - bailOut("assembler running failed"); + ERROR("assembler running failed"); } } @@ -511,21 +560,21 @@ void compileSegment() int main(int argc, char **argv) { - parseCommandLine(argc, argv); + parseCommandLine(argc, argv); - printBannerCollectAnalyse(); + printBannerCollectAnalyse(); - readSourceFiles(); - checkInputFileLabels(); - calcRoutineSizes(); + readSourceFiles(); + checkInputFileLabels(); + calcRoutineSizes(); - printBannerBinCompile(); + printBannerBinCompile(); - prepareBinningProblem(); - solveBinningProblem(); - compileSegment(); + prepareBinningProblem(); + solveBinningProblem(); + compileSegment(); - return 0; + return 0; } // @@ -541,25 +590,25 @@ SourceFile::SourceFile(const std::string &fileName, const std::string &dirName) testAddrStart(-1), testAddrEnd(-1) { - const std::string fileNameWithPath = dirName + DIR_SEPARATOR + fileName; - std::cout << "reading file: " << fileNameWithPath << std::endl; + const std::string fileNameWithPath = dirName + DIR_SEPARATOR + fileName; + std::cout << "reading file: " << fileNameWithPath << "\n"; - // Open the file + // Open the file - std::ifstream inFile; + std::ifstream inFile; inFile.open(fileNameWithPath); - if (!inFile.good()) bailOut("unable to open file"); + if (!inFile.good()) ERROR("unable to open file"); // Read the content inFile.seekg(0, inFile.end); auto fileLength = inFile.tellg(); - if (fileLength == 0) bailOut("file is empty"); + if (fileLength == 0) ERROR("file is empty"); content.resize(fileLength); inFile.seekg(0); inFile.read(&content[0], fileLength); - if (!inFile.good()) bailOut("error reading file content"); + if (!inFile.good()) ERROR("error reading file content"); inFile.close(); @@ -568,20 +617,20 @@ SourceFile::SourceFile(const std::string &fileName, const std::string &dirName) auto isHexDigit = [](char digit) -> bool { - return (digit >= '0' && digit <= '9') || - (digit >= 'a' && digit <= 'f') || - (digit >= 'A' && digit <= 'F');; + return (digit >= '0' && digit <= '9') || + (digit >= 'a' && digit <= 'f') || + (digit >= 'A' && digit <= 'F');; }; floating = !(fileName.length() >= 6 && fileName[4] == '.' && - isHexDigit(fileName[0]) && - isHexDigit(fileName[1]) && - isHexDigit(fileName[2]) && - isHexDigit(fileName[3])); + isHexDigit(fileName[0]) && + isHexDigit(fileName[1]) && + isHexDigit(fileName[2]) && + isHexDigit(fileName[3])); if (!floating) { - startAddr = strtol(fileName.substr(0, 4).c_str(), nullptr ,16); + startAddr = strtol(fileName.substr(0, 4).c_str(), nullptr ,16); } // Generate KickAss compatible label from the file name @@ -598,200 +647,200 @@ SourceFile::SourceFile(const std::string &fileName, const std::string &dirName) BinningProblem::BinningProblem(int loAddress, int hiAddress) { - if (hiAddress < loAddress || hiAddress < 0 || loAddress < 0 || hiAddress > 0xFFFF || loAddress > 0xFFFF) - { - bailOut("invalid lo/hi address"); - } + if (hiAddress < loAddress || hiAddress < 0 || loAddress < 0 || hiAddress > 0xFFFF || loAddress > 0xFFFF) + { + ERROR("invalid lo/hi address"); + } - // Create one large gap (initial) to represent the assembly + // Create one large gap (initial) to represent the assembly - gaps[loAddress] = hiAddress - loAddress + 1; + gaps[loAddress] = hiAddress - loAddress + 1; } bool BinningProblem::isSolved() const { - return floatingRoutines.empty(); + return floatingRoutines.empty(); } void BinningProblem::addToProblem(SourceFile *routine) { - if (routine->floating) - { - floatingRoutines.push_back(routine); - } - else - { - // For the fixed-address routines, we have to find the matching place + if (routine->floating) + { + floatingRoutines.push_back(routine); + } + else + { + // For the fixed-address routines, we have to find the matching place - bool gapFound = false; - for (auto &gap: gaps) - { - if (gap.first > routine->startAddr || gap.first + gap.second - 1 < routine->startAddr) - { - continue; // not a suitable gap - } + bool gapFound = false; + for (auto &gap: gaps) + { + if (gap.first > routine->startAddr || gap.first + gap.second - 1 < routine->startAddr) + { + continue; // not a suitable gap + } - // We have to put the routine into the current gap + // We have to put the routine into the current gap - if (gap.first + gap.second < routine->startAddr + routine->codeLength) - { - bailOut(std::string("fixed address file '") + routine->fileName + "' won't fit in the available gap"); - } + if (gap.first + gap.second < routine->startAddr + routine->codeLength) + { + ERROR(std::string("fixed address file '") + routine->fileName + "' won't fit in the available gap"); + } - gapFound = true; + gapFound = true; - // Put the routine into the gap, possibly removing it or splitting into two + // Put the routine into the gap, possibly removing it or splitting into two - fixedRoutines[routine->startAddr] = routine; + fixedRoutines[routine->startAddr] = routine; - // Calculate possible new gap after the routine + // Calculate possible new gap after the routine - int newGapSize = (gap.first + gap.second) - (routine->startAddr + routine->codeLength); - int newGapStart = (newGapSize <= 0) ? -1 : routine->startAddr + routine->codeLength; + int newGapSize = (gap.first + gap.second) - (routine->startAddr + routine->codeLength); + int newGapStart = (newGapSize <= 0) ? -1 : routine->startAddr + routine->codeLength; - // Remove or shrink the current gap + // Remove or shrink the current gap - if (gap.first == routine->startAddr) - { - gaps.erase(gap.first); - } - else - { - gap.second = routine->startAddr - gap.first; - } + if (gap.first == routine->startAddr) + { + gaps.erase(gap.first); + } + else + { + gap.second = routine->startAddr - gap.first; + } - // Add a new gap + // Add a new gap - if (newGapStart > 0) gaps[newGapStart] = newGapSize; + if (newGapStart > 0) gaps[newGapStart] = newGapSize; break; // iterator is not valid enymore, we have to stop the llop - } + } - if (!gapFound) - { - bailOut(std::string("start address of fixed address file '") + routine->fileName + "' already occupied"); - } - } + if (!gapFound) + { + ERROR(std::string("start address of fixed address file '") + routine->fileName + "' already occupied"); + } + } } -void BinningProblem::fillGap(int gapAddress, const std::list &routines) +void BinningProblem::fillGap(DualStream &logOutput, int gapAddress, const std::list &routines) { - int offset = 0; - std::string spacing; + int offset = 0; + std::string spacing; - for (auto &routine : routines) - { - int targetAddr = gapAddress + offset; + for (auto &routine : routines) + { + int targetAddr = gapAddress + offset; - spacing.resize(GLOBAL_maxFileNameLen + 4 - routine->fileName.length(), ' '); - std::cout << " $" << std::hex << targetAddr << std::dec << ": " << - routine->fileName << spacing << "size: " << routine->codeLength << std::endl; + spacing.resize(GLOBAL_maxFileNameLen + 4 - routine->fileName.length(), ' '); + logOutput << " $" << std::hex << targetAddr << std::dec << ": " << + routine->fileName << spacing << "size: " << routine->codeLength << "\n"; fixedRoutines[targetAddr] = routine; offset += routine->codeLength; - if (offset > gaps[gapAddress]) bailOut(std::string("internal error line ") + std::to_string(__LINE__)); + if (offset > gaps[gapAddress]) ERROR(std::string("internal error line ") + std::to_string(__LINE__)); floatingRoutines.erase(std::remove(floatingRoutines.begin(), floatingRoutines.end(), routine), floatingRoutines.end()); - } + } - // Get rid of the gap, it's useless now + // Get rid of the gap, it's useless now - if (offset == gaps[gapAddress]) + if (offset == gaps[gapAddress]) { - std::cout << "filled to the last byte" << std::endl; + logOutput << "filled to the last byte" << "\n"; } else if (!isSolved()) { - std::cout << "filled in - dropped bytes: " << gaps[gapAddress] << std::endl; + logOutput << "filled in - dropped bytes: " << gaps[gapAddress] << "\n"; } else { - std::cout << "out of routines" << std::endl; + logOutput << "out of routines" << "\n"; } - gaps.erase(gapAddress); + gaps.erase(gapAddress); } -void BinningProblem::performObviousSteps() +void BinningProblem::performObviousSteps(DualStream &logOutput) { - // Get the size of the biggest routine; if there is just one gap which - // can handle it - put the roputine exactly there + // Get the size of the biggest routine; if there is just one gap which + // can handle it - put the roputine exactly there - std::string spacing; + std::string spacing; - bool repeat = true; - while (repeat && !floatingRoutines.empty() && !gaps.empty()) - { - repeat = false; + bool repeat = true; + while (repeat && !floatingRoutines.empty() && !gaps.empty()) + { + repeat = false; - int routineSize = floatingRoutines.back()->codeLength; + int routineSize = floatingRoutines.back()->codeLength; - int gapAddress; - int matchingGaps = 0; - for (auto &gap : gaps) - { - if (gap.second >= routineSize) - { - matchingGaps++; - gapAddress = gap.first; - } - } + int gapAddress; + int matchingGaps = 0; + for (auto &gap : gaps) + { + if (gap.second >= routineSize) + { + matchingGaps++; + gapAddress = gap.first; + } + } - if (matchingGaps == 1) - { - // Routine can only be placed in this particular gap - do so + if (matchingGaps == 1) + { + // Routine can only be placed in this particular gap - do so - gaps[gapAddress] -= routineSize; - int targetAddr = gapAddress + gaps[gapAddress]; - fixedRoutines[targetAddr] = floatingRoutines.back(); + gaps[gapAddress] -= routineSize; + int targetAddr = gapAddress + gaps[gapAddress]; + fixedRoutines[targetAddr] = floatingRoutines.back(); - std::cout << "forced reducing gap $" << std::hex << gapAddress << std::dec << " to size " << - gaps[gapAddress] << std::endl; + logOutput << "forced reducing gap $" << std::hex << gapAddress << std::dec << " to size " << + gaps[gapAddress] << "\n"; - spacing.resize(GLOBAL_maxFileNameLen + 4 - floatingRoutines.back()->fileName.length(), ' '); - std::cout << " $" << std::hex << targetAddr << std::dec << ": " << - floatingRoutines.back()->fileName << spacing << "size: " << - floatingRoutines.back()->codeLength << std::endl; + spacing.resize(GLOBAL_maxFileNameLen + 4 - floatingRoutines.back()->fileName.length(), ' '); + logOutput << " $" << std::hex << targetAddr << std::dec << ": " << + floatingRoutines.back()->fileName << spacing << "size: " << + floatingRoutines.back()->codeLength << "\n"; - floatingRoutines.pop_back(); - if (gaps[gapAddress] == 0) gaps.erase(gapAddress); + floatingRoutines.pop_back(); + if (gaps[gapAddress] == 0) gaps.erase(gapAddress); - repeat = true; - } - } + repeat = true; + } + } } -void BinningProblem::removeUselessGaps() +void BinningProblem::removeUselessGaps(DualStream &logOutput) { - // Get the size of the smallest floating routine, - // remove all the gaps which are smaller in size + // Get the size of the smallest floating routine, + // remove all the gaps which are smaller in size - int minUsefulSize = floatingRoutines[0]->codeLength; + int minUsefulSize = floatingRoutines[0]->codeLength; - bool repeat = true; - while (repeat) - { - repeat = false; - for (auto &gap : gaps) - { - if (gap.second < minUsefulSize) - { - std::cout << "dropping gap: $" << std::hex << gap.first << std::dec << " (size: " << gap.second << ")" << std::endl; - gaps.erase(gap.first); - repeat = true; - break; - } - } - } + bool repeat = true; + while (repeat) + { + repeat = false; + for (auto &gap : gaps) + { + if (gap.second < minUsefulSize) + { + logOutput << "dropping gap: $" << std::hex << gap.first << std::dec << " (size: " << gap.second << ")" << "\n"; + gaps.erase(gap.first); + repeat = true; + break; + } + } + } } void BinningProblem::sortFloatingRoutinesBySize() { - // Sort the unallocated routines by size, starting from the smallest one + // Sort the unallocated routines by size, starting from the smallest one - auto compare = [](const SourceFile *a, const SourceFile *b) -> bool { return a->codeLength < b->codeLength; }; - std::sort (floatingRoutines.begin(), floatingRoutines.end(), compare); + auto compare = [](const SourceFile *a, const SourceFile *b) -> bool { return a->codeLength < b->codeLength; }; + std::sort (floatingRoutines.begin(), floatingRoutines.end(), compare); } // @@ -800,184 +849,201 @@ void BinningProblem::sortFloatingRoutinesBySize() void Solver::run() { - problem.sortFloatingRoutinesBySize(); // just to be extra sure + // Prepare the log file - while (!problem.gaps.empty() && !problem.floatingRoutines.empty()) - { - problem.performObviousSteps(); - problem.removeUselessGaps(); + const std::string logFileName = CMD_tmpDir + DIR_SEPARATOR + CMD_segName + "_solver.log"; + unlink(logFileName.c_str()); + logFile.open(logFileName, std::fstream::out | std::fstream::trunc); - if (problem.gaps.empty() || problem.floatingRoutines.empty()) break; + // Sort the floating routines, just to be extra sure - int gapAddr = selectGapToFill(); + problem.sortFloatingRoutinesBySize(); - std::cout << "selected gap: " << "$" << std::hex << gapAddr << std::dec << " (size: " << problem.gaps[gapAddr] << ")" << std::endl; + // Run the solver until all is done - std::list partialSolution; - findPartialSolution(problem.gaps[gapAddr], partialSolution); - problem.fillGap(gapAddr, partialSolution); - } + while (!problem.gaps.empty() && !problem.floatingRoutines.empty()) + { + problem.performObviousSteps(logOutput); + problem.removeUselessGaps(logOutput); - if (problem.isSolved()) - { - std::cout << std::endl << "all the routines sucessfully placed" << std::endl << std::endl; - } + if (problem.gaps.empty() || problem.floatingRoutines.empty()) break; + + int gapAddr = selectGapToFill(); + + logOutput << "selected gap: $" << std::hex << gapAddr << std::dec << " (size: " << problem.gaps[gapAddr] << ")" << "\n"; + + std::list partialSolution; + findPartialSolution(problem.gaps[gapAddr], partialSolution); + problem.fillGap(logOutput, gapAddr, partialSolution); + } + + // Print out the result + + if (problem.isSolved()) + { + logOutput << "\n" << "all the routines sucessfully placed" << "\n\n"; + } + + // Close the log file + + if (!logFile.good()) ERROR(std::string("error writing log file '") + logFileName + "'"); + logFile.close(); } int Solver::selectGapToFill() { - // Find the smallest gap to fill-in + // Find the smallest gap to fill-in - int gapAddr = -1; - int gapSize = -1; + int gapAddr = -1; + int gapSize = -1; - for (auto &gap : problem.gaps) - { - if (gapSize < 0 || gap.second < gapSize) - { - gapAddr = gap.first; - gapSize = gap.second; - } - } + for (auto &gap : problem.gaps) + { + if (gapSize < 0 || gap.second < gapSize) + { + gapAddr = gap.first; + gapSize = gap.second; + } + } - return gapAddr; + return gapAddr; } int KS(const std::vector &routines, - std::vector> &cacheV, // value cache - std::vector>> &cacheS, // partial solution cache - int n, int C, - std::list &solution) // sequence of decisions for each routine, has to be empty when calling + std::vector> &cacheV, // value cache + std::vector>> &cacheS, // partial solution cache + int n, int C, + std::list &solution) // sequence of decisions for each routine, has to be empty when calling { - // This routine solves a knapsack problem using a dynamic programming + // This routine solves a knapsack problem using a dynamic programming - // XXX Possible efficiency improvement for the future: try to consider more than one gap at once, - // this will need a serious rework of the dynamic cache handling and will probably hurt the performance + // XXX Possible efficiency improvement for the future: try to consider more than one gap at once, + // this will need a serious rework of the dynamic cache handling and will probably hurt the performance - if (n == 0) - { - // No more routines to take the decision... - return 0; - } + if (n == 0) + { + // No more routines to take the decision... + return 0; + } - if (C == 0) - { - // We have no capacity left - decisions for all the routines left should be not to take them - solution.resize(n, false); - return 0; - } + if (C == 0) + { + // We have no capacity left - decisions for all the routines left should be not to take them + solution.resize(n, false); + return 0; + } - auto &cachedV = cacheV[n - 1][C - 1]; - auto &cachedS = cacheS[n - 1][C - 1]; - if (cachedV >= 0) - { - // We already hold the solution in out cache - return it - solution = cachedS; - return cachedV; - } + auto &cachedV = cacheV[n - 1][C - 1]; + auto &cachedS = cacheS[n - 1][C - 1]; + if (cachedV >= 0) + { + // We already hold the solution in out cache - return it + solution = cachedS; + return cachedV; + } - auto &codeSize = routines[n - 1]->codeLength; - if (codeSize > C) - { - // We can't take this routine, it's too large + auto &codeSize = routines[n - 1]->codeLength; + if (codeSize > C) + { + // We can't take this routine, it's too large - cachedV = KS(routines, cacheV, cacheS, n - 1, C, solution); - solution.push_back(false); - cachedS = solution; + cachedV = KS(routines, cacheV, cacheS, n - 1, C, solution); + solution.push_back(false); + cachedS = solution; - return cachedV; - } + return cachedV; + } - // Note: it it seems equally good to take this routine, or not - take it! - // Evaluation starts from the largest routines, and we should prefer to leave - // a larger number of smaller routines - as this gives more possibilities - // while optimizing the usage of further gaps + // Note: it it seems equally good to take this routine, or not - take it! + // Evaluation starts from the largest routines, and we should prefer to leave + // a larger number of smaller routines - as this gives more possibilities + // while optimizing the usage of further gaps - std::list solution1; - int val1 = KS(routines, cacheV, cacheS, n - 1, C - codeSize, solution1) + codeSize; // take + std::list solution1; + int val1 = KS(routines, cacheV, cacheS, n - 1, C - codeSize, solution1) + codeSize; // take - if (val1 == C) - { - // By taking this routine we are filling all the space - contrary to the classic - // knapsack problem, this is an optimal solution for us, don't waste time calculating - // the others + if (val1 == C) + { + // By taking this routine we are filling all the space - contrary to the classic + // knapsack problem, this is an optimal solution for us, don't waste time calculating + // the others - cachedV = val1; - solution = solution1; - solution.push_back(true); - cachedS = solution; - return cachedV; - } + cachedV = val1; + solution = solution1; + solution.push_back(true); + cachedS = solution; + return cachedV; + } - std::list solution2; - int val2 = KS(routines, cacheV, cacheS, n - 1, C, solution2); // don't take + std::list solution2; + int val2 = KS(routines, cacheV, cacheS, n - 1, C, solution2); // don't take - if (val1 >= val2) - { - // It's better to take this particular routine + if (val1 >= val2) + { + // It's better to take this particular routine - cachedV = val1; - solution = solution1; - solution.push_back(true); - } - else - { - // It's better not to take this particular routine + cachedV = val1; + solution = solution1; + solution.push_back(true); + } + else + { + // It's better not to take this particular routine - cachedV = val2; - solution = solution2; - solution.push_back(false); - } + cachedV = val2; + solution = solution2; + solution.push_back(false); + } - cachedS = solution; - return cachedV; + cachedS = solution; + return cachedV; }; void Solver::findPartialSolution(int gapSize, std::list &partialSolution) { - // First filter out available routines, take only the ones not larger than our gap + // First filter out available routines, take only the ones not larger than our gap std::vector routines; - routines.clear(); - for (auto &routine : problem.floatingRoutines) - { - if (routine->codeLength <= gapSize) - { - routines.push_back(routine); - } - else break; // 'floatingRoutines' should be kept sorted - } + routines.clear(); + for (auto &routine : problem.floatingRoutines) + { + if (routine->codeLength <= gapSize) + { + routines.push_back(routine); + } + else break; // 'floatingRoutines' should be kept sorted + } - // Prepare value cache + // Prepare value cache - std::vector> cacheV; - cacheV.resize(routines.size()); - for (auto &cacheRow : cacheV) cacheRow.resize(gapSize, -1); + std::vector> cacheV; + cacheV.resize(routines.size()); + for (auto &cacheRow : cacheV) cacheRow.resize(gapSize, -1); - // Prepare partial solution cache - std::vector>> cacheS; - cacheS.resize(routines.size()); - for (auto &cacheRow : cacheS) cacheRow.resize(gapSize); + // Prepare partial solution cache + std::vector>> cacheS; + cacheS.resize(routines.size()); + for (auto &cacheRow : cacheS) cacheRow.resize(gapSize); - // Calculate solution + // Calculate solution - std::list solution; - KS(routines, cacheV, cacheS, routines.size(), gapSize, solution); + std::list solution; + KS(routines, cacheV, cacheS, routines.size(), gapSize, solution); - if (routines.size() != solution.size()) bailOut(std::string("internal error line ") + std::to_string(__LINE__)); + if (routines.size() != solution.size()) ERROR(std::string("internal error line ") + std::to_string(__LINE__)); - // Return the solution upstream + // Return the solution upstream - while (!routines.empty()) - { - if (solution.back() != false) - { - partialSolution.push_front(routines.back()); - } + while (!routines.empty()) + { + if (solution.back() != false) + { + partialSolution.push_front(routines.back()); + } - routines.pop_back(); - solution.pop_back(); - } + routines.pop_back(); + solution.pop_back(); + } - return; + return; }