?login_element?

Subversion Repositories NedoOS

Rev

Rev 625 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  
  3.   SjASMPlus Z80 Cross Compiler
  4.  
  5.   This is modified sources of SjASM by Aprisobal - aprisobal@tut.by
  6.  
  7.   Copyright (c) 2006 Sjoerd Mastijn
  8.  
  9.   This software is provided 'as-is', without any express or implied warranty.
  10.   In no event will the authors be held liable for any damages arising from the
  11.   use of this software.
  12.  
  13.   Permission is granted to anyone to use this software for any purpose,
  14.   including commercial applications, and to alter it and redistribute it freely,
  15.   subject to the following restrictions:
  16.  
  17.   1. The origin of this software must not be misrepresented; you must not claim
  18.          that you wrote the original software. If you use this software in a product,
  19.          an acknowledgment in the product documentation would be appreciated but is
  20.          not required.
  21.  
  22.   2. Altered source versions must be plainly marked as such, and must not be
  23.          misrepresented as being the original software.
  24.  
  25.   3. This notice may not be removed or altered from any source distribution.
  26.  
  27. */
  28.  
  29. // tables.cpp
  30.  
  31. #include <assert.h>
  32. #include "sjdefs.h"
  33.  
  34. TextFilePos::TextFilePos(const char* fileNamePtr, uint32_t line) : filename(fileNamePtr), line(line), colBegin(0), colEnd(0) {
  35. }
  36.  
  37. void TextFilePos::newFile(const char* fileNamePtr) {
  38.         filename = fileNamePtr;
  39.         line = colBegin = colEnd = 0;
  40. }
  41.  
  42. // advanceColumns are valid only when true == endsWithColon (else advanceColumns == 0)
  43. // default arguments are basically "next line"
  44. void TextFilePos::nextSegment(bool endsWithColon, size_t advanceColumns) {
  45.         if (endsWithColon && 0 == colEnd) colEnd = 1;   // first segment of "colonized" line (do +1,+1)
  46.         colBegin = colEnd;
  47.         if (colBegin <= 1) ++line;              // first segment of any line, increment also line number
  48.         if (endsWithColon)      colEnd += advanceColumns;
  49.         else                            colEnd = 0;
  50. }
  51.  
  52. char* PreviousIsLabel = nullptr;
  53.  
  54. // since v1.18.0:
  55. // The ignore invalid char after feature disconnected from "setNameSpace" to "ignoreCharAfter"
  56. // (it's for evaluating labels straight from the expressions, without copying them out first)
  57. // The prefix "!" is now recognized as "do not set main label" for following local labels
  58. // since v1.18.3:
  59. // Inside macro prefix "@." will create non-macro local label instead of macro's instance
  60. char* ValidateLabel(const char* naam, bool setNameSpace, bool ignoreCharAfter) {
  61.         if (nullptr == naam) {
  62.                 Error("Invalid labelname");
  63.                 return nullptr;
  64.         }
  65.         if ('!' == *naam) {
  66.                 setNameSpace = false;
  67.                 ++naam;
  68.         }
  69.         // check if local label defined inside macro wants to become non-macro local label
  70.         const bool escMacro = setNameSpace && macrolabp && ('@' == naam[0]) && ('.' == naam[1]);
  71.         if (escMacro) ++naam;                   // such extra "@" is consumed right here and only '.' is left
  72.         // regular single prefix case in other use cases
  73.         const bool global = '@' == *naam;
  74.         const bool local = '.' == *naam;
  75.         if (!isLabelStart(naam)) {              // isLabelStart assures that only single modifier exist
  76.                 if (global || local) ++naam;// single modifier is parsed (even when invalid name)
  77.                 Error("Invalid labelname", naam);
  78.                 return nullptr;
  79.         }
  80.         if (global || local) ++naam;    // single modifier is parsed
  81.         const bool inMacro = !escMacro && local && macrolabp;
  82.         const bool inModule = !inMacro && !global && ModuleName[0];
  83.         // check all chars of label
  84.         const char* np = naam;
  85.         while (islabchar(*np)) ++np;
  86.         if ('[' == *np) return nullptr; // this is DEFARRAY name, do not process it as label (silent exit)
  87.         if (*np && !ignoreCharAfter) {
  88.                 // if this is supposed to be new label, there shoulnd't be anything else after it
  89.                 Error("Invalid labelname", naam);
  90.                 return nullptr;
  91.         }
  92.         // calculate expected length of fully qualified label name
  93.         int labelLen = (np - naam), truncateAt = LABMAX;
  94.         if (LABMAX < labelLen) Error("Label too long", naam, IF_FIRST); // non-fatal error, will truncate it
  95.         if (inMacro) labelLen += 1 + strlen(macrolabp);
  96.         else if (local) labelLen += 1 + strlen(vorlabp);
  97.         if (inModule) labelLen += 1 + strlen(ModuleName);
  98.         // build fully qualified label name (in newly allocated memory buffer, with precise length)
  99.         char* const label = new char[1+labelLen];
  100.         if (nullptr == label) ErrorOOM();
  101.         label[0] = 0;
  102.         if (inModule) {
  103.                 STRCAT(label, labelLen, ModuleName);    STRCAT(label, 2, ".");
  104.         }
  105.         if (inMacro) {
  106.                 STRCAT(label, labelLen, macrolabp);             STRCAT(label, 2, ">");
  107.         } else if (local) {
  108.                 STRCAT(label, labelLen, vorlabp);               STRCAT(label, 2, ".");
  109.         }
  110.         char* lp = label + strlen(label), * newVorlabP = nullptr;
  111.         if (setNameSpace && !local) newVorlabP = lp;    // here will start new non-local label prefix
  112.         while (truncateAt-- && islabchar(*naam)) *lp++ = *naam++;       // add the new label (truncated if needed)
  113.         *lp = 0;
  114.         if (labelLen < lp - label) Error("internal error", nullptr, FATAL);             // should never happen :)
  115.         if (newVorlabP) {
  116.                 free(vorlabp);
  117.                 vorlabp = STRDUP(newVorlabP);
  118.                 if (vorlabp == NULL) ErrorOOM();
  119.         }
  120.         return label;
  121. }
  122.  
  123. static char sldLabelExport[2*LINEMAX];
  124.  
  125. char* ExportLabelToSld(const char* naam, const SLabelTableEntry* label) {
  126.         // does re-parse the original source line again similarly to ValidateLabel
  127.         // but prepares SLD 'L'-type line, with module/main/local comma separated + usage traits info
  128.         assert(nullptr != label);
  129.         assert(isLabelStart(naam));             // this should be called only when ValidateLabel did succeed
  130.         const bool global = '@' == *naam;
  131.         const bool local = '.' == *naam;
  132.         if (global || local) ++naam;    // single modifier is parsed
  133.         const bool inMacro = local && macrolabp;
  134.         const bool inModule = !inMacro && !global && ModuleName[0];
  135.         const bool isStructLabel = (label->traits & (LABEL_IS_STRUCT_D|LABEL_IS_STRUCT_E));
  136.         // build fully qualified SLD info
  137.         sldLabelExport[0] = 0;
  138.         // module part
  139.         if (inModule) STRCAT(sldLabelExport, LINEMAX, ModuleName);
  140.         STRCAT(sldLabelExport, 2, ",");
  141.         // main label part (the `vorlabp` is already the current label, if it was main label)
  142.         // except for structure labels: the inner ones don't "set namespace" == vorlabp, use "naam" then
  143.         // (but only if the main label of structure itself is not local, if it's local, use vorlabp)
  144.         STRCAT(sldLabelExport, LABMAX, isStructLabel && !local ? naam : inMacro ? macrolabp : vorlabp);
  145.         STRCAT(sldLabelExport, 2, ",");
  146.         // local part
  147.         if (local) STRCAT(sldLabelExport, LABMAX, naam);
  148.         // usage traits
  149.         if (label->traits&LABEL_IS_EQU) STRCAT(sldLabelExport, 20, ",+equ");
  150.         if (inMacro) STRCAT(sldLabelExport, 20, ",+macro");
  151.         if (label->traits&LABEL_IS_SMC) STRCAT(sldLabelExport, 20, ",+smc");
  152.         if (Relocation::REGULAR == label->isRelocatable) STRCAT(sldLabelExport, 20, ",+reloc");
  153.         if (Relocation::HIGH == label->isRelocatable) STRCAT(sldLabelExport, 20, ",+reloc_high");
  154.         if (label->used) STRCAT(sldLabelExport, 20, ",+used");
  155.         if (label->traits&LABEL_IS_STRUCT_D) STRCAT(sldLabelExport, 20, ",+struct_def");
  156.         if (label->traits&LABEL_IS_STRUCT_E) STRCAT(sldLabelExport, 20, ",+struct_data");
  157.         return sldLabelExport;
  158. }
  159.  
  160. char* ExportModuleToSld(bool endModule) {
  161.         assert(ModuleName[0]);
  162.         STRNCPY(sldLabelExport, 2*LINEMAX, ModuleName, LINEMAX);
  163.         STRCAT(sldLabelExport, LINEMAX-1, endModule ? ",,,+endmod" : ",,,+module");
  164.         return sldLabelExport;
  165. }
  166.  
  167. static bool getLabel_invalidName = false;
  168.  
  169. // does parse + consume input source at "p" (and stores result into "fullName")
  170. //  ^^^ may report invalid label name error
  171. // does search (and only search) LabelTable for various variants of label based on "fullName"
  172. // No refresh of "used", no inserting into table when not found, no other errrors reported
  173. // Leaves canonical name in "temp" global variable, if this is inside macro
  174. // returns table entry, preferring the one with "page defined", if multiple entries are found
  175. static SLabelTableEntry* SearchLabel(char*& p, bool setUsed, /*out*/ std::unique_ptr<char[]>& fullName) {
  176.         getLabel_invalidName = true;
  177.         fullName.reset(ValidateLabel(p, false, true));
  178.         if (!fullName) return nullptr;
  179.         getLabel_invalidName = false;
  180.         const bool global = '@' == *p;
  181.         const bool local = '.' == *p;
  182.         while (islabchar(*p)) ++p;              // advance pointer beyond the parsed label
  183.         // find the label entry in the label table (for local macro labels it has to try all sub-parts!)
  184.         // then regular full label has to be tried
  185.         // and if it's regular non-local in module, then variant w/o current module has to be tried
  186.         bool inMacro = local && macrolabp;              // not just inside macro, but should be prefixed
  187.         const int modNameLen = strlen(ModuleName);
  188.         const char *findName = fullName.get();
  189.         SLabelTableEntry* undefinedLabelEntry = nullptr;
  190.         SLabelTableEntry* labelEntry = nullptr;
  191.         temp[0] = 0;
  192.         do {
  193.                 labelEntry = LabelTable.Find(findName);
  194.                 if (labelEntry) {
  195.                         if (setUsed && pass < LASTPASS) labelEntry->used = true;
  196.                         if (LABEL_PAGE_UNDEFINED != labelEntry->page) return labelEntry;        // found
  197.                         // if found, but "undefined" one, remember it as fall-back result
  198.                         undefinedLabelEntry = labelEntry;
  199.                         labelEntry = nullptr;
  200.                 }
  201.                 // not found (the defined one, try more variants)
  202.                 if (inMacro) {                          // try outer macro (if there is one)
  203.                         while ('>' != *findName && '.' != *findName) ++findName;
  204.                         // if no more outer macros, try module+non-local prefix with the original local label
  205.                         if ('>' == *findName++) {
  206.                                 inMacro = false;
  207.                                 if (modNameLen) {
  208.                                         #pragma GCC diagnostic push     // disable gcc8 warning about truncation - that's intended behaviour
  209.                                         #if 8 <= __GNUC__
  210.                                                 #pragma GCC diagnostic ignored "-Wstringop-truncation"
  211.                                         #endif
  212.                                         STRCAT(temp, LINEMAX-2, ModuleName); STRCAT(temp, 2, ".");
  213.                                         #pragma GCC diagnostic pop
  214.                                 }
  215.                                 STRCAT(temp, LABMAX-1, vorlabp); STRCAT(temp, 2, ".");
  216.                                 STRCAT(temp, LABMAX-1, findName);
  217.                                 findName = temp;
  218.                         }
  219.                 } else {
  220.                         if (!global && !local && fullName.get() == findName && modNameLen) {
  221.                                 // this still may be global label without current module (but author didn't use "@")
  222.                                 findName = fullName.get() + modNameLen + 1;
  223.                         } else {
  224.                                 findName = nullptr;     // all options exhausted
  225.                         }
  226.                 }
  227.         } while (findName);
  228.         return undefinedLabelEntry;
  229. }
  230.  
  231. static SLabelTableEntry* GetLabel(char*& p) {
  232.         std::unique_ptr<char[]> fullName;
  233.         SLabelTableEntry* labelEntry = SearchLabel(p, true, fullName);
  234.         if (getLabel_invalidName) return nullptr;
  235.         if (!labelEntry || LABEL_PAGE_UNDEFINED == labelEntry->page) {
  236.                 IsLabelNotFound = true;
  237.                 // don't insert labels or report errors during substitution phase
  238.                 if (IsSubstituting) return nullptr;
  239.                 // regular parsing/assembling, track new labels and report "not found" error
  240.                 char* findName = temp[0] ? temp : fullName.get();
  241.                 if (!labelEntry) {
  242.                         LabelTable.Insert(findName, 0, LABEL_IS_UNDEFINED);
  243.                 }
  244.                 Error("Label not found", findName, IF_FIRST);
  245.                 return nullptr;
  246.         } else {
  247.                 return labelEntry;
  248.         }
  249. }
  250.  
  251. bool LabelExist(char*& p, aint& val) {
  252.         std::unique_ptr<char[]> fullName;
  253.         SLabelTableEntry* labelEntry = SearchLabel(p, false, fullName);
  254.         val = (labelEntry && LABEL_PAGE_UNDEFINED != labelEntry->page) ? -1 : 0;
  255.         return !getLabel_invalidName;
  256. }
  257.  
  258. bool GetLabelPage(char*& p, aint& val) {
  259.         SLabelTableEntry* labelEntry = GetLabel(p);
  260.         val = labelEntry ? labelEntry->page : LABEL_PAGE_UNDEFINED;
  261.         // true even when not found, but valid label name (neeed for expression-eval logic)
  262.         return !getLabel_invalidName;
  263. }
  264.  
  265. bool GetLabelValue(char*& p, aint& val) {
  266.         SLabelTableEntry* labelEntry = GetLabel(p);
  267.         if (labelEntry) {
  268.                 val = labelEntry->value;
  269.                 if (Relocation::areLabelsOffset) {
  270.                         switch (labelEntry->isRelocatable) {
  271.                                 case Relocation::REGULAR:       val += Relocation::alternative_offset;                  break;
  272.                                 case Relocation::HIGH:          val += Relocation::alternative_offset >> 8;             break;
  273.                                 default:                                        ;
  274.                         }
  275.                 }
  276.         } else {
  277.                 val = 0;
  278.         }
  279.         // true even when not found, but valid label name (needed for expression-eval logic)
  280.         return !getLabel_invalidName;
  281. }
  282.  
  283. int GetTemporaryLabelValue(char*& op, aint& val, bool requireUnderscore) {
  284.         char* p = op;
  285.         if (SkipBlanks(p) || !isdigit((byte)*p)) return 0;
  286.         char* const numberB = p;
  287.         while (isdigit((byte)*p)) ++p;
  288.         const bool hasUnderscore = ('_' == *p);
  289.         if (requireUnderscore && !hasUnderscore) return 0;
  290.         // convert suffix [bB] => 'b', [fF] => 'f' and ignore underscore
  291.         const char type = (hasUnderscore ? p[1] : p[0]) | 0x20; // should be 'b' or 'f'
  292.         const char following = hasUnderscore ? p[2] : p[1];             // should be non-label char
  293.         if ('b' != type && 'f' != type) return 0;       // local label must have "b" or "f" after number
  294.         if (islabchar(following)) return 0;                     // that suffix didn't end correctly
  295.         // numberB -> p are digits to be parsed as integer
  296.         if (!GetNumericValue_IntBased(op = numberB, p, val, 10)) return 0;
  297.         if ('_' == *op) ++op;
  298.         ++op;
  299.         // ^^ advance main parsing pointer op beyond the local label (here it *is* local label)
  300.         auto label = ('b' == type) ? TemporaryLabelTable.seekBack(val) : TemporaryLabelTable.seekForward(val);
  301.         if (label) {
  302.                 val = label->value;
  303.                 if (requireUnderscore) {                                // part of full expression, do relocation by +offset
  304.                         if (label->isRelocatable && Relocation::areLabelsOffset) {
  305.                                 val += Relocation::alternative_offset;
  306.                         }
  307.                 } else {                                                                // single-label-only in jump/call instructions
  308.                         Relocation::isResultAffected = label->isRelocatable;
  309.                         Relocation::deltaType = label->isRelocatable ? Relocation::REGULAR : Relocation::OFF;
  310.                 }
  311.         } else {
  312.                 if (LASTPASS == pass) Error("Temporary label not found", numberB, SUPPRESS);
  313.                 val = 0L;
  314.         }
  315.         return 1;
  316. }
  317.  
  318. static short getAddressPageNumber(const aint address, bool forceRecalculateByAddress) {
  319.         // everything is "ROM" based when device is NONE
  320.         if (!DeviceID) return LABEL_PAGE_ROM;
  321.         // fast-shortcut for regular labels in current slot (if they fit into it)
  322.         auto slot = Device->GetCurrentSlot();
  323.         assert(Page && slot);
  324.         if (!forceRecalculateByAddress && DISP_NONE == PseudoORG) {
  325.                 if (slot->Address <= address && address < slot->Address + slot->Size) {
  326.                         return Page->Number;
  327.                 }
  328.         }
  329.         // enforce explicit request of fake DISP page
  330.         if (DISP_NONE != PseudoORG && LABEL_PAGE_UNDEFINED != dispPageNum) {
  331.                 return dispPageNum;
  332.         }
  333.         // in other case (implicit DISP, out-of-slot-bounds or forceRecalculateByAddress)
  334.         // track down the page num from current memory mapping
  335.         const short page = Device->GetPageOfA16(address);
  336.         if (LABEL_PAGE_UNDEFINED == page) return LABEL_PAGE_OUT_OF_BOUNDS;
  337.         return page;
  338. }
  339.  
  340. int CLabelTable::Insert(const char* nname, aint nvalue, unsigned traits, short equPageNum) {
  341.         const bool IsUndefined = !!(traits & LABEL_IS_UNDEFINED);
  342.  
  343.         // the EQU/DEFL is relocatable when the expression itself is relocatable
  344.         // the regular label is relocatable when relocation is active
  345.         const Relocation::EType deltaType = \
  346.                         (traits&LABEL_HAS_RELOC_TRAIT) ? \
  347.                                 (traits & LABEL_IS_RELOC ? Relocation::REGULAR : Relocation::OFF) : \
  348.                                 (traits & (LABEL_IS_DEFL|LABEL_IS_EQU)) ? \
  349.                                         Relocation::deltaType : \
  350.                                         Relocation::type && DISP_INSIDE_RELOCATE != PseudoORG ? \
  351.                                                 Relocation::REGULAR : Relocation::OFF;
  352.         // Find label in label table
  353.         symbol_map_t::iterator labelIt = symbols.find(nname);
  354.         if (symbols.end() != labelIt) {
  355.                 //if label already added (as used, or in previous pass), just refresh values
  356.                 auto& label = labelIt->second;
  357.                 if (label.traits&LABEL_IS_KEYWORD) WarningById(W_OPKEYWORD, nname, W_EARLY);
  358.                 bool needsUpdate = label.traits&LABEL_IS_DEFL || label.page == LABEL_PAGE_UNDEFINED || label.updatePass < pass;
  359.                 if (needsUpdate) {
  360.                         label.value = nvalue;
  361.                         if ((traits & LABEL_IS_EQU) && LABEL_PAGE_UNDEFINED != equPageNum) {
  362.                                 label.page = equPageNum;
  363.                         } else {
  364.                                 label.page = getAddressPageNumber(nvalue, traits & (LABEL_IS_DEFL|LABEL_IS_EQU));
  365.                         }
  366.                         label.traits = traits;
  367.                         label.isRelocatable = deltaType;
  368.                         label.updatePass = pass;
  369.                 }
  370.                 return needsUpdate;
  371.         }
  372.         auto& label = symbols[nname];
  373.         label.traits = traits;
  374.         label.updatePass = pass;
  375.         label.value = nvalue;
  376.         label.used = IsUndefined;
  377.         if ((traits & LABEL_IS_EQU) && LABEL_PAGE_UNDEFINED != equPageNum) {
  378.                 label.page = equPageNum;
  379.         } else {
  380.                 label.page = IsUndefined ? LABEL_PAGE_UNDEFINED : getAddressPageNumber(nvalue, traits & (LABEL_IS_DEFL|LABEL_IS_EQU));
  381.         }
  382.         label.isRelocatable = IsUndefined ? Relocation::OFF : deltaType;        // ignore "relocatable" for "undefined"
  383.         return 1;
  384. }
  385.  
  386. int CLabelTable::Update(char* name, aint value) {
  387.         auto labelIt = symbols.find(name);
  388.         if (symbols.end() != labelIt) labelIt->second.value = value;
  389.         return (symbols.end() != labelIt);
  390. }
  391.  
  392. SLabelTableEntry* CLabelTable::Find(const char* name, bool onlyDefined) {
  393.         symbol_map_t::iterator labelIt = symbols.find(name);
  394.         if (symbols.end() == labelIt) return nullptr;
  395.         return (onlyDefined && LABEL_PAGE_UNDEFINED == labelIt->second.page) ? nullptr : &labelIt->second;
  396. }
  397.  
  398. bool CLabelTable::IsUsed(const char* name) {
  399.         auto labelIt = symbols.find(name);
  400.         return (symbols.end() != labelIt) ? labelIt->second.used : false;
  401. }
  402.  
  403. bool CLabelTable::Remove(const char* name) {
  404.         return symbols.erase(name);
  405. }
  406.  
  407. void CLabelTable::RemoveAll() {
  408.         symbols.clear();
  409. }
  410.  
  411. static const std::vector<symbol_map_t::key_type> getDumpOrder(const symbol_map_t& table) {
  412.         std::vector<symbol_map_t::key_type> order;
  413.         order.reserve(table.size());
  414.         for (const auto& it : table) order.emplace_back(it.first);
  415.         if (Options::SortSymbols) {
  416.                 std::sort(
  417.                         order.begin(), order.end(),
  418.                         [&](const symbol_map_t::key_type& a, const symbol_map_t::key_type& b) {
  419.                                 // if case insenstive are same, do case sensitive too!
  420.                                 int caseres = strcasecmp(a.c_str(), b.c_str());
  421.                                 if (0 == caseres) return a < b;
  422.                                 return caseres < 0;
  423.                         }
  424.                 );
  425.         }
  426.         return order;
  427. }
  428.  
  429. void CLabelTable::Dump() {
  430.         FILE* listFile = GetListingFile();
  431.         if (NULL == listFile) return;           // listing file must be already opened here
  432.  
  433.         const auto order = getDumpOrder(symbols);
  434.  
  435.         char line[LINEMAX], *ep;
  436.         fputs("\nValue    Label\n", listFile);
  437.         fputs("------ - -----------------------------------------------------------\n", listFile);
  438.         for (const symbol_map_t::key_type& name: order) {
  439.                 const symbol_map_t::mapped_type& symbol = symbols.at(name);
  440.                 if (LABEL_PAGE_UNDEFINED == symbol.page) continue;
  441.                 ep = line;
  442.                 *(ep) = 0;
  443.                 *(ep++) = '0';
  444.                 *(ep++) = 'x';
  445.                 PrintHexAlt(ep, symbol.value);
  446.                 *(ep++) = ' ';
  447.                 *(ep++) = symbol.used ? ' ' : 'X';
  448.                 *(ep++) = ' ';
  449.                 STRNCPY(ep, LINEMAX, name.c_str(), LINEMAX - (ep - line) - 2);
  450.                 STRNCAT(ep, LINEMAX, "\n", 2);
  451.                 fputs(line, listFile);
  452.         }
  453. }
  454.  
  455. void CLabelTable::DumpForUnreal() {
  456.         char ln[LINEMAX], * ep;
  457.         FILE* FP_UnrealList;
  458.         if (!FOPEN_ISOK(FP_UnrealList, Options::UnrealLabelListFName, "w")) {
  459.                 Error("opening file for write", Options::UnrealLabelListFName, FATAL);
  460.         }
  461.         const int PAGE_MASK = DeviceID ? Device->GetPage(0)->Size - 1 : 0x3FFF;
  462.         const int ADR_MASK = Options::EmitVirtualLabels ? 0xFFFF : PAGE_MASK;
  463.         const auto order = getDumpOrder(symbols);
  464.         for (const symbol_map_t::key_type& name: order) {
  465.                 const symbol_map_t::mapped_type& symbol = symbols.at(name);
  466.                 if (LABEL_PAGE_UNDEFINED == symbol.page) continue;
  467.                 int page = Options::EmitVirtualLabels ? LABEL_PAGE_OUT_OF_BOUNDS : symbol.page;
  468.                 if (!strcmp(DeviceID, "ZXSPECTRUM48") && page < 4) {    //TODO fix this properly?
  469.                         // convert pages {0, 1, 2, 3} of ZX48 into ZX128-like {ROM, 5, 2, 0}
  470.                         // this can be fooled when there were multiple devices used, Label doesn't know into
  471.                         // which device it does belong, so even ZX128 labels will be converted.
  472.                         const int fakeZx128Pages[] = {LABEL_PAGE_ROM, 5, 2, 0};
  473.                         page = fakeZx128Pages[page];
  474.                 }
  475.                 int lvalue = symbol.value & ADR_MASK;
  476.                 ep = ln;
  477.  
  478.                 if (page < LABEL_PAGE_ROM) ep += sprintf(ep, "%02d", page&255);
  479.                 *(ep++) = ':';
  480.                 PrintHexAlt(ep, lvalue);
  481.  
  482.                 *(ep++) = ' ';
  483.                 STRCPY(ep, LINEMAX-(ep-ln), name.c_str());
  484.                 STRCAT(ep, LINEMAX, "\n");
  485.                 fputs(ln, FP_UnrealList);
  486.         }
  487.         fclose(FP_UnrealList);
  488. }
  489.  
  490. void CLabelTable::DumpForCSpect() {
  491.         FILE* file;
  492.         if (!FOPEN_ISOK(file, Options::CSpectMapFName, "w")) {
  493.                 Error("opening file for write", Options::CSpectMapFName, FATAL);
  494.         }
  495.         const int CSD_PAGE_SIZE = Options::CSpectMapPageSize;
  496.         const int CSD_PAGE_MASK = CSD_PAGE_SIZE - 1;
  497.         const auto order = getDumpOrder(symbols);
  498.         for (const symbol_map_t::key_type& name: order) {
  499.                 const symbol_map_t::mapped_type& symbol = symbols.at(name);
  500.                 if (LABEL_PAGE_UNDEFINED == symbol.page) continue;
  501.                 const int labelType =
  502.                         (symbol.traits&LABEL_IS_STRUCT_E) ? 0 :
  503.                         (symbol.traits&LABEL_IS_STRUCT_D) ? 4 :
  504.                         (symbol.traits&LABEL_IS_EQU) ? 1 :
  505.                         (symbol.traits&LABEL_IS_DEFL) ? 2 :
  506.                         (LABEL_PAGE_ROM <= symbol.page) ? 3 : 0;
  507.                 const short page = labelType ? 0 : symbol.page;
  508.                         // TODO:
  509.                         // page == -1 will put regular EQU like "BLUE" out of reach for disassembly window
  510.                         // (otherwise BLUE becomes label for address $C001 with default mapping)
  511.                         // BUT then it would be nice to provide real page data for equ which have them explicit
  512.                         // BUT I can't distinguish explicit/implicit page number, as there's heuristic to use current mapping
  513.                         // instead of using the LABEL_PAGE_OUT_OF_BOUNDS page number...
  514.                         // TODO: figure out when/why the implicit page number heuristic happenned and if you can detect
  515.                         // only explicit page numbers used in EQU, and export only those
  516.  
  517.                 const aint longAddress = (CSD_PAGE_MASK & symbol.value) + page * CSD_PAGE_SIZE;
  518.                 fprintf(file, "%08X %08X %02X ", 0xFFFF & symbol.value, longAddress, labelType);
  519.                 // convert primary+local label to be "@" delimited (not "." delimited)
  520.                 STRCPY(temp, LINEMAX, name.c_str());
  521.                 // look for "primary" label (where the local label starts)
  522.                 char* localLabelStart = strrchr(temp, '.');
  523.                 while (temp < localLabelStart) {        // the dot must be at least second character
  524.                         *localLabelStart = 0;                   // terminate the possible "primary" part
  525.                         if (Find(temp, true)) {
  526.                                 *localLabelStart = '@';
  527.                                 break;
  528.                         }
  529.                         *localLabelStart = '.';                 // "primary" label didn't work, restore dot
  530.                         do {
  531.                                 --localLabelStart;                      // and look for next dot
  532.                         } while (temp < localLabelStart && '.' != *localLabelStart);
  533.                 }
  534.                 // convert whole label to upper-case, as CSpect search is malfunctioning otherwise.
  535.                 char* strToUpper = temp;
  536.                 while ((*strToUpper = (char) toupper((byte)*strToUpper))) { ++strToUpper; }
  537.                 fprintf(file, "%s\n", temp);
  538.         }
  539.         fclose(file);
  540. }
  541.  
  542. void CLabelTable::DumpSymbols() {
  543.         FILE* symfp;
  544.         if (!FOPEN_ISOK(symfp, Options::SymbolListFName, "w")) {
  545.                 Error("opening file for write", Options::SymbolListFName, FATAL);
  546.         }
  547.         const auto order = getDumpOrder(symbols);
  548.         for (const symbol_map_t::key_type& name: order) {
  549.                 const symbol_map_t::mapped_type& symbol = symbols.at(name);
  550.                 if (isdigit((byte)name[0])) continue;
  551.                 if (symbol.traits&LABEL_IS_KEYWORD) continue;
  552.                 WriteLabelEquValue(name.c_str(), symbol.value, symfp);
  553.         }
  554.         fclose(symfp);
  555. }
  556.  
  557. int CFunctionTable::Insert(const char* name_cstr, function_fn_t nfunp) {
  558.         std::string name(name_cstr);
  559.         if (!std::get<1>(functions.emplace(name, nfunp))) return 0;
  560.         for (auto& c : name) c = toupper(c);
  561.         return std::get<1>(functions.emplace(name, nfunp));
  562. }
  563.  
  564. int CFunctionTable::insertd(const char* name, function_fn_t nfunp) {
  565.         if ('.' != name[0]) Error("Directive string must start with dot", NULL, FATAL);
  566.         // insert the non-dot variant first, then dot variant
  567.         return Insert(name+1, nfunp) && Insert(name, nfunp);
  568. }
  569.  
  570. int CFunctionTable::zoek(const char* name) {
  571.         auto it = functions.find(name);
  572.         if (functions.end() == it) return 0;
  573.         (*it->second)();
  574.         return 1;
  575. }
  576.  
  577. TemporaryLabel::TemporaryLabel(aint number, aint address)
  578.         : nummer(number), value(address), isRelocatable(bool(Relocation::type)) {}
  579.  
  580. CTemporaryLabelTable::CTemporaryLabelTable() {
  581.         labels.reserve(128);
  582.         refresh = 0;
  583. }
  584.  
  585. void CTemporaryLabelTable::InitPass() {
  586.         refresh = 0;            // reset refresh pointer for next pass
  587. }
  588.  
  589. bool CTemporaryLabelTable::insertImpl(const aint labelNumber) {
  590.         labels.emplace_back(labelNumber, CurAddress);
  591.         return true;
  592. }
  593.  
  594. bool CTemporaryLabelTable::refreshImpl(const aint labelNumber) {
  595.         if (labels.size() <= refresh || labels.at(refresh).nummer != labelNumber) return false;
  596.         TemporaryLabel & to_r = labels.at(refresh);
  597.         if (to_r.value != CurAddress) Warning("Temporary label has different address");
  598.         to_r.value = CurAddress;
  599.         ++refresh;
  600.         return true;
  601. }
  602.  
  603. bool CTemporaryLabelTable::InsertRefresh(const aint nnummer) {
  604.         return (1 == pass) ? insertImpl(nnummer) : refreshImpl(nnummer);
  605. }
  606.  
  607. const TemporaryLabel* CTemporaryLabelTable::seekForward(const aint labelNumber) const {
  608.         if (1 == pass) return nullptr;                                  // just building tables in first pass, no results yet
  609.         temporary_labels_t::size_type i = refresh;              // refresh already points at first "forward" temporary label
  610.         while (i < labels.size() && labelNumber != labels[i].nummer) ++i;
  611.         return (i < labels.size()) ? &labels[i] : nullptr;
  612. }
  613.  
  614. const TemporaryLabel* CTemporaryLabelTable::seekBack(const aint labelNumber) const {
  615.         if (1 == pass || refresh <= 0) return nullptr;  // just building tables or no temporary label "backward"
  616.         temporary_labels_t::size_type i = refresh;              // after last "backward" temporary label
  617.         while (i--) if (labelNumber == labels[i].nummer) return &labels[i];
  618.         return nullptr;                                                                 // not found
  619. }
  620.  
  621. CStringsList::CStringsList(const char* stringSource, CStringsList* nnext) {
  622.         string = STRDUP(stringSource);
  623.         next = nnext;
  624.         if (!sourcePosStack.empty()) source = sourcePosStack.back();
  625. }
  626.  
  627. CStringsList::~CStringsList() {
  628.         if (string) free(string);
  629.         if (next) delete next;
  630. }
  631.  
  632. bool CStringsList::contains(const CStringsList* strlist, const char* searchString) {
  633.         while (nullptr != strlist) {
  634.                 if (!strcmp(searchString, strlist->string)) return true;
  635.                 strlist = strlist->next;
  636.         }
  637.         return false;
  638. }
  639.  
  640. CDefineTableEntry::CDefineTableEntry(const char* nname, const char* nvalue, CStringsList* nnss, CDefineTableEntry* nnext)
  641.                 : name(NULL), value(NULL) {
  642.         name = STRDUP(nname);
  643.         value = new char[strlen(nvalue) + 1];
  644.         if (NULL == name || NULL == value) ErrorOOM();
  645.         char* s1 = value;
  646.         while (*nvalue && *nvalue != '\n' && *nvalue != '\r') *s1++ = *nvalue++;
  647.         *s1 = 0;
  648.         next = nnext;
  649.         nss = nnss;
  650. }
  651.  
  652. CDefineTableEntry::~CDefineTableEntry() {
  653.         if (name) free(name);
  654.         if (value) delete[] value;
  655.         if (nss) delete nss;
  656.         if (next) delete next;
  657. }
  658.  
  659. void CDefineTableEntry::Replace(const char* nvalue) {
  660.         if (value) delete[] value;
  661.         value = new char[strlen(nvalue) + 1];
  662.         strcpy(value, nvalue);
  663. }
  664.  
  665. CDefineTable::~CDefineTable() {
  666.         for (auto def : defs) if (def) delete def;
  667. }
  668.  
  669. CDefineTable& CDefineTable::operator=(CDefineTable const & defTable) {
  670.         RemoveAll();
  671.         for (CDefineTableEntry* srcDef : defTable.defs) {
  672.                 CDefineTableEntry* srcD = srcDef;
  673.                 while (srcD) {
  674.                         Add(srcD->name, srcD->value, srcD->nss);
  675.                         srcD = srcD->next;
  676.                 }
  677.         }
  678.         return *this;
  679. }
  680.  
  681. void CDefineTable::Init() {
  682.         DefArrayList = NULL;
  683.         for (auto & def : defs) def = NULL;
  684. }
  685.  
  686. void CDefineTable::Add(const char* name, const char* value, CStringsList* nss) {
  687.         if (FindDuplicate(name)) {
  688.                 Error("Duplicate define (replacing old value)", name, PASS03);
  689.         }
  690.         defs[(*name)&127] = new CDefineTableEntry(name, value, nss, defs[(*name)&127]);
  691. }
  692.  
  693. static char defineGet__Counter__Buffer[32] = {};
  694. static char defineGet__Line__Buffer[32] = {};
  695.  
  696. const char* CDefineTable::Get(const char* name) {
  697.         DefArrayList = nullptr;
  698.         if (nullptr == name || 0 == name[0]) return nullptr;
  699.         // the __COUNTER__ and __LINE__ have fully dynamic custom implementation here
  700.         if ('_' == name[1]) {
  701.                 if (!strcmp(name, "__COUNTER__")) {
  702.                         SPRINTF1(defineGet__Counter__Buffer, 30, "%d", PredefinedCounter);
  703.                         ++PredefinedCounter;
  704.                         return defineGet__Counter__Buffer;
  705.                 }
  706.                 if (!strcmp(name, "__LINE__")) {
  707.                         SPRINTF1(defineGet__Line__Buffer, 30, "%d", sourcePosStack.empty() ? 0 : sourcePosStack.back().line);
  708.                         return defineGet__Line__Buffer;
  709.                 }
  710.         }
  711.         CDefineTableEntry* p = defs[(*name)&127];
  712.         while (p && strcmp(name, p->name)) p = p->next;
  713.         if (nullptr == p) return nullptr;
  714.         DefArrayList = p->nss;
  715.         return p->value;
  716. }
  717.  
  718. int CDefineTable::FindDuplicate(const char* name) {
  719.         CDefineTableEntry* p = defs[(*name)&127];
  720.         while (p) {
  721.                 if (!strcmp(name, p->name)) {
  722.                         return 1;
  723.                 }
  724.                 p = p->next;
  725.         }
  726.         return 0;
  727. }
  728.  
  729. int CDefineTable::Replace(const char* name, const char* value) {
  730.         CDefineTableEntry* p = defs[(*name)&127];
  731.         while (p) {
  732.                 if (!strcmp(name, p->name)) {
  733.                         p->Replace(value);
  734.                         return 0;
  735.                 }
  736.                 p = p->next;
  737.         }
  738.         defs[(*name)&127] = new CDefineTableEntry(name, value, 0, defs[(*name)&127]);
  739.         return 1;
  740. }
  741.  
  742. int CDefineTable::Replace(const char* name, const int value) {
  743.         char newIntValue[24];
  744.         SPRINTF1(newIntValue, sizeof(newIntValue), "%d", value);
  745.         return Replace(name, newIntValue);
  746. }
  747.  
  748. int CDefineTable::Remove(const char* name) {
  749.         CDefineTableEntry* p = defs[(*name)&127];
  750.         CDefineTableEntry* p2 = NULL;
  751.         while (p) {
  752.                 if (!strcmp(name, p->name)) {
  753.                         // unchain the particular item
  754.                         if (NULL == p2) defs[(*name)&127] = p->next;
  755.                         else                    p2->next = p->next;
  756.                         p->next = NULL;
  757.                         // delete it
  758.                         delete p;
  759.                         DefArrayList = NULL;            // may be invalid here, so just reset it
  760.                         return 1;
  761.                 }
  762.                 p2 = p;
  763.                 p = p->next;
  764.         }
  765.         return 0;
  766. }
  767.  
  768. void CDefineTable::RemoveAll() {
  769.         DefArrayList = NULL;
  770.         for (auto & def : defs) {
  771.                 if (!def) continue;
  772.                 delete def;
  773.                 def = NULL;
  774.         }
  775. }
  776.  
  777. CMacroDefineTable::CMacroDefineTable() : defs(nullptr) {
  778.         for (auto & usedX : used) usedX = false;
  779. }
  780.  
  781. CMacroDefineTable::~CMacroDefineTable() {
  782.         if (defs) delete defs;
  783. }
  784.  
  785. void CMacroDefineTable::ReInit() {
  786.         if (defs) delete defs;
  787.         defs = nullptr;
  788.         for (auto & usedX : used) usedX = false;
  789. }
  790.  
  791. void CMacroDefineTable::AddMacro(char* naam, char* vervanger) {
  792.         CDefineTableEntry* tmpdefs = new CDefineTableEntry(naam, vervanger, 0, defs);
  793.         defs = tmpdefs;
  794.         used[(*naam)&127] = true;
  795. }
  796.  
  797. CDefineTableEntry* CMacroDefineTable::getdefs() {
  798.         return defs;
  799. }
  800.  
  801. void CMacroDefineTable::setdefs(CDefineTableEntry* const ndefs) {
  802.         if (ndefs == defs) return;                      // the current HEAD of defines is already same as requested one
  803.         // traverse through current HEAD until the requested chain is found, unchain the HEAD from it
  804.         CDefineTableEntry* entry = defs;
  805.         while (entry && ndefs != entry->next) entry = entry->next;
  806.         if (entry) entry->next = nullptr;       // if "ndefs" is chained to current HEAD, unchain
  807.         if (defs) delete defs;                          // release front part of current chain from memory
  808.         defs = ndefs;                                           // the requested chain is new current HEAD
  809. }
  810.  
  811. const char* CMacroDefineTable::getverv(const char* name) const {
  812.         if (nullptr == name) return nullptr;
  813.         if (!used[(*name)&127]) return nullptr;
  814.         const CDefineTableEntry* p = defs;
  815.         while (p && strcmp(name, p->name)) p = p->next;
  816.         return p ? p->value : nullptr;
  817. }
  818.  
  819. int CMacroDefineTable::FindDuplicate(char* name) {
  820.         CDefineTableEntry* p = defs;
  821.         if (!used[(*name)&127]) {
  822.                 return 0;
  823.         }
  824.         while (p) {
  825.                 if (!strcmp(name, p->name)) {
  826.                         return 1;
  827.                 }
  828.                 p = p->next;
  829.         }
  830.         return 0;
  831. }
  832.  
  833. CMacroTableEntry::CMacroTableEntry(char* nnaam, CMacroTableEntry* nnext)
  834.         : naam(nnaam), args(nullptr), body(nullptr), next(nnext) {
  835. }
  836.  
  837. CMacroTableEntry::~CMacroTableEntry() {
  838.         if (naam) free(naam);   // must be of STRDUP origin!
  839.         if (args) delete args;
  840.         if (body) delete body;
  841.         if (next) delete next;
  842. }
  843.  
  844. CMacroTable::CMacroTable() : macs(nullptr) {
  845.         for (auto & usedX : used) usedX = false;
  846. }
  847.  
  848. CMacroTable::~CMacroTable() {
  849.         if (macs) delete macs;
  850. }
  851.  
  852. void CMacroTable::ReInit() {
  853.         if (macs) delete macs;
  854.         macs = nullptr;
  855.         for (auto & usedX : used) usedX = false;
  856. }
  857.  
  858. int CMacroTable::FindDuplicate(const char* naam) {
  859.         CMacroTableEntry* p = macs;
  860.         if (!used[(*naam)&127]) {
  861.                 return 0;
  862.         }
  863.         while (p) {
  864.                 if (!strcmp(naam, p->naam)) {
  865.                         return 1;
  866.                 }
  867.                 p = p->next;
  868.         }
  869.         return 0;
  870. }
  871.  
  872. void CMacroTable::Add(const char* nnaam, char*& p) {
  873.         if (FindDuplicate(nnaam)) {
  874.                 Error("Duplicate macroname", nnaam);return;
  875.         }
  876.         char* macroname = STRDUP(nnaam);
  877.         if (macroname == NULL) ErrorOOM();
  878.         macs = new CMacroTableEntry(macroname, macs);
  879.         used[(*macroname)&127] = true;
  880.         CStringsList* last = nullptr;
  881.         do {
  882.                 char* n = GetID(p);
  883.                 if (!n) {
  884.                         // either EOL when no previous argument, or valid name is required after comma (2nd+ loop)
  885.                         if ((1 == pass) && (last || *p)) Error("Illegal argument name", p, EARLY);
  886.                         SkipToEol(p);
  887.                         break;
  888.                 }
  889.                 if ((1 == pass) && CStringsList::contains(macs->args, n)) {
  890.                         Error("Duplicate argument name", n, EARLY);
  891.                 }
  892.                 CStringsList* argname = new CStringsList(n);
  893.                 if (!macs->args) {
  894.                         macs->args = argname;   // first argument name, make it head of list
  895.                 } else {
  896.                         last->next = argname;
  897.                 }
  898.                 last = argname;
  899.         } while (anyComma(p));
  900.         if ((1 == pass) && *p) {
  901.                 Error("Unexpected", p, EARLY);
  902.         }
  903.         ListFile();
  904.         if (!ReadFileToCStringsList(macs->body, "endm")) {
  905.                 Error("Unexpected end of macro", NULL, EARLY);
  906.         }
  907. }
  908.  
  909. int CMacroTable::Emit(char* naam, char*& p) {
  910.         // search for the desired macro
  911.         if (!used[(*naam)&127]) return 0;
  912.         CMacroTableEntry* m = macs;
  913.         while (m && strcmp(naam, m->naam)) m = m->next;
  914.         if (!m) return 0;
  915.         // macro found, emit it, prepare temporary instance label base
  916.         char* omacrolabp = macrolabp;
  917.         char labnr[LINEMAX], ml[LINEMAX];
  918.         SPRINTF1(labnr, LINEMAX, "%d", macronummer++);
  919.         macrolabp = labnr;
  920.         if (omacrolabp) {
  921.                 STRCAT(macrolabp, LINEMAX-1, "."); STRCAT(macrolabp, LINEMAX-1, omacrolabp);
  922.         } else {
  923.                 MacroDefineTable.ReInit();
  924.         }
  925.         // parse argument values
  926.         CDefineTableEntry* odefs = MacroDefineTable.getdefs();
  927.         CStringsList* a = m->args;
  928.         while (a) {
  929.                 char* n = ml;
  930.                 const bool lastArg = NULL == a->next;
  931.                 if (!GetMacroArgumentValue(p, n) || (!lastArg && !comma(p))) {
  932.                         Error("Not enough arguments for macro", naam, SUPPRESS);
  933.                         macrolabp = omacrolabp;
  934.                         return 1;
  935.                 }
  936.                 MacroDefineTable.AddMacro(a->string, ml);
  937.                 a = a->next;
  938.         }
  939.         SkipBlanks(p);
  940.         if (*p) {
  941.                 Error("Too many arguments for macro", naam, SUPPRESS);
  942.                 macrolabp = omacrolabp;
  943.                 return 1;
  944.         }
  945.         // arguments parsed, emit the macro lines and parse them
  946.         lp = p;
  947.         ListFile();
  948.         ++listmacro;
  949.         CStringsList* olijstp = lijstp;
  950.         lijstp = m->body;
  951.         ++lijst;
  952.         STRCPY(ml, LINEMAX, line);
  953.         sourcePosStack.push_back(TextFilePos());
  954.         while (lijstp) {
  955.                 sourcePosStack.back() = lijstp->source;
  956.                 STRCPY(line, LINEMAX, lijstp->string);
  957.                 substitutedLine = line;         // reset substituted listing
  958.                 eolComment = NULL;                      // reset end of line comment
  959.                 lijstp = lijstp->next;
  960.                 ParseLineSafe();
  961.         }
  962.         sourcePosStack.pop_back();
  963.         ++CompiledCurrentLine;
  964.         STRCPY(line, LINEMAX, ml);
  965.         lijstp = olijstp;
  966.         --lijst;
  967.         MacroDefineTable.setdefs(odefs);
  968.         macrolabp = omacrolabp;
  969.         --listmacro; donotlist = 1;
  970.         return 2;
  971. }
  972.  
  973. CStructureEntry1::CStructureEntry1(char* nnaam, aint noffset) {
  974.         next = 0;
  975.         naam = STRDUP(nnaam);
  976.         if (naam == NULL) ErrorOOM();
  977.         offset = noffset;
  978. }
  979.  
  980. CStructureEntry1::~CStructureEntry1() {
  981.         free(naam);
  982.         if (next) delete next;
  983. }
  984.  
  985. CStructureEntry2::CStructureEntry2(aint noffset, aint nlen, aint ndef, Relocation::EType ndeltatype, EStructureMembers ntype) :
  986.         next(nullptr), text(nullptr), offset(noffset), len(nlen), def(ndef), defDeltaType(ndeltatype), type(ntype)
  987. {
  988. }
  989.  
  990. CStructureEntry2::CStructureEntry2(aint noffset, aint nlen, byte* textData) :
  991.         next(nullptr), text(textData), offset(noffset), len(nlen), def(0), defDeltaType(Relocation::OFF), type(SMEMBTEXT)
  992. {
  993.         assert(1 <= len && len <= TEXT_MAX_SIZE && nullptr != text);
  994. }
  995.  
  996. CStructureEntry2::~CStructureEntry2() {
  997.         if (next) delete next;
  998.         if (text) delete[] text;
  999. }
  1000.  
  1001. // Parses source input for types: BYTE, WORD, DWORD, D24
  1002. aint CStructureEntry2::ParseValue(char* & p) {
  1003.         if (SMEMBBYTE != type && SMEMBWORD != type && SMEMBDWORD != type && SMEMBD24 != type) return def;
  1004.         SkipBlanks(p);
  1005.         aint val = def;
  1006.         bool keepRelocatableFlags = false;      // keep flags from the ParseExpressionNoSyntaxError?
  1007.         // check for unexpected {
  1008.         if ('{' != *p) {
  1009.                 if (!(keepRelocatableFlags = ParseExpressionNoSyntaxError(p, val))) {
  1010.                         val = def;
  1011.                 }
  1012.                 switch (type) {
  1013.                         case SMEMBBYTE:
  1014.                                 check8(val);
  1015.                                 val &= 0xFF;
  1016.                                 break;
  1017.                         case SMEMBWORD:
  1018.                                 check16(val);
  1019.                                 val &= 0xFFFF;
  1020.                                 break;
  1021.                         case SMEMBD24:
  1022.                                 check24(val);
  1023.                                 val &= 0xFFFFFF;
  1024.                                 break;
  1025.                         case SMEMBDWORD:
  1026.                                 break;
  1027.                         default:
  1028.                                 break;
  1029.                 }
  1030.         }
  1031.         if (!Relocation::type) return val;
  1032.         if (SMEMBBYTE == type && Relocation::HIGH == Relocation::type) {
  1033.                 if (!keepRelocatableFlags) {    // override flags, if parse expression was not successful
  1034.                         Relocation::isResultAffected |= bool(defDeltaType);
  1035.                         Relocation::deltaType = defDeltaType;
  1036.                 }
  1037.                 Relocation::resolveRelocationAffected(0, Relocation::HIGH);
  1038.         } else if (SMEMBWORD == type) {
  1039.                 if (!keepRelocatableFlags) {    // override flags, if parse expression was not successful
  1040.                         Relocation::isResultAffected |= bool(defDeltaType);
  1041.                         Relocation::deltaType = defDeltaType;
  1042.                 }
  1043.                 Relocation::resolveRelocationAffected(0);
  1044.         }
  1045.         Relocation::checkAndWarn();
  1046.         return val;
  1047. }
  1048.  
  1049. CStructure::CStructure(const char* nnaam, char* nid, int no, int ngl, CStructure* p) {
  1050.         mnf = mnl = NULL; mbf = mbl = NULL;
  1051.         naam = STRDUP(nnaam);
  1052.         if (naam == NULL) ErrorOOM();
  1053.         id = STRDUP(nid);
  1054.         if (id == NULL) ErrorOOM();
  1055.         next = p; noffset = no; global = ngl;
  1056.         maxAlignment = 0;
  1057. }
  1058.  
  1059. CStructure::~CStructure() {
  1060.         free(naam);
  1061.         free(id);
  1062.         if (mnf) delete mnf;
  1063.         if (mbf) delete mbf;
  1064.         if (next) delete next;
  1065. }
  1066.  
  1067. void CStructure::AddLabel(char* nnaam) {
  1068.         CopyLabel(nnaam, 0);
  1069. }
  1070.  
  1071. void CStructure::AddMember(CStructureEntry2* n) {
  1072.         if (!mbf)       mbf = n;
  1073.         else            mbl->next = n;
  1074.         mbl = n;
  1075.         noffset += n->len;
  1076. }
  1077.  
  1078. void CStructure::CopyLabel(char* nnaam, aint offset) {
  1079.         CStructureEntry1* n = new CStructureEntry1(nnaam, noffset + offset);
  1080.         if (!mnf)       mnf = n;
  1081.         else            mnl->next = n;
  1082.         mnl = n;
  1083. }
  1084.  
  1085. void CStructure::CopyLabels(CStructure* st) {
  1086.         CStructureEntry1* np = st->mnf;
  1087.         if (!np || !PreviousIsLabel) return;
  1088.         char str[LINEMAX];
  1089.         STRCPY(str, LINEMAX-1, PreviousIsLabel);
  1090.         STRCAT(str, LINEMAX-1, ".");
  1091.         char * const stw = str + strlen(str);
  1092.         while (np) {
  1093.                 STRCPY(stw, LINEMAX, np->naam); // overwrite the second part of label
  1094.                 CopyLabel(str, np->offset);
  1095.                 np = np->next;
  1096.         }
  1097. }
  1098.  
  1099. void CStructure::CopyMember(CStructureEntry2* item, aint newDefault, Relocation::EType newDeltaType) {
  1100.         AddMember(new CStructureEntry2(noffset, item->len, newDefault, newDeltaType, item->type));
  1101. }
  1102.  
  1103. void CStructure::CopyMembers(CStructure* st, char*& lp) {
  1104.         aint val;
  1105.         int haakjes = 0;
  1106.         AddMember(new CStructureEntry2(noffset, 0, 0, Relocation::OFF, SMEMBPARENOPEN));
  1107.         SkipBlanks(lp);
  1108.         if (*lp == '{') {
  1109.                 ++haakjes; ++lp;
  1110.         }
  1111.         CStructureEntry2* ip = st->mbf;
  1112.         while (ip || 0 < haakjes) {
  1113.                 Relocation::isResultAffected = false;
  1114.                 // check if inside curly braces block, and input seems to be empty -> fetch next line
  1115.                 if (0 < haakjes && !PrepareNonBlankMultiLine(lp)) break;
  1116.                 if (nullptr == ip) {    // no more struct members expected, looking for closing '}'
  1117.                         assert(0 < haakjes);
  1118.                         if (!need(lp, '}')) break;
  1119.                         --haakjes;
  1120.                         continue;
  1121.                 }
  1122.                 assert(ip);
  1123.                 switch (ip->type) {
  1124.                 case SMEMBBLOCK:
  1125.                         CopyMember(ip, ip->def, Relocation::OFF);
  1126.                         break;
  1127.                 case SMEMBBYTE:
  1128.                 case SMEMBWORD:
  1129.                 case SMEMBD24:
  1130.                 case SMEMBDWORD:
  1131.                         {
  1132.                                 Relocation::EType isRelocatable = Relocation::OFF;
  1133.                                 if (ParseExpressionNoSyntaxError(lp, val)) {
  1134.                                         isRelocatable = (Relocation::isResultAffected && (SMEMBWORD == ip->type || SMEMBBYTE == ip->type))
  1135.                                                                                 ? Relocation::deltaType : Relocation::OFF;
  1136.                                 } else {
  1137.                                         val = ip->def;
  1138.                                         isRelocatable = ip->defDeltaType;
  1139.                                 }
  1140.                                 CopyMember(ip, val, isRelocatable);
  1141.                                 if (SMEMBWORD == ip->type) {
  1142.                                         Relocation::resolveRelocationAffected(INT_MAX); // clear flags + warn when can't be relocated
  1143.                                 } else if (SMEMBBYTE == ip->type) {
  1144.                                         Relocation::resolveRelocationAffected(INT_MAX, Relocation::HIGH);       // clear flags + warn when can't be relocated
  1145.                                 }
  1146.                                 if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(lp);
  1147.                         }
  1148.                         break;
  1149.                 case SMEMBTEXT:
  1150.                         {
  1151.                                 byte* textData = new byte[ip->len]();   // zero initialized for stable binary results
  1152.                                 if (nullptr == textData) ErrorOOM();
  1153.                                 GetStructText(lp, ip->len, textData, ip->text);
  1154.                                 AddMember(new CStructureEntry2(noffset, ip->len, textData));
  1155.                                 if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(lp);
  1156.                         }
  1157.                         break;
  1158.                 case SMEMBPARENOPEN:
  1159.                         SkipBlanks(lp);
  1160.                         if (*lp == '{') {
  1161.                                 ++haakjes; ++lp;
  1162.                         }
  1163.                         CopyMember(ip, 0, Relocation::OFF);
  1164.                         break;
  1165.                 case SMEMBPARENCLOSE:
  1166.                         SkipBlanks(lp);
  1167.                         if (haakjes && *lp == '}') {
  1168.                                 --haakjes; ++lp;
  1169.                                 if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(lp);
  1170.                         }
  1171.                         CopyMember(ip, 0, Relocation::OFF);
  1172.                         break;
  1173.                 default:
  1174.                         Error("internalerror CStructure::CopyMembers", NULL, FATAL);
  1175.                 }
  1176.                 Relocation::checkAndWarn();
  1177.                 ip = ip->next;
  1178.         }
  1179.         if (haakjes) {
  1180.                 Error("closing } missing");
  1181.         }
  1182.         AddMember(new CStructureEntry2(noffset, 0, 0, Relocation::OFF, SMEMBPARENCLOSE));
  1183. }
  1184.  
  1185. static void InsertSingleStructLabel(const bool setNameSpace, char *name, const bool isRelocatable, const aint value, const bool isDefine = true) {
  1186.         char *op = name;
  1187.         std::unique_ptr<char[]> p(ValidateLabel(op, setNameSpace));
  1188.         if (!p) {
  1189.                 Error("Illegal labelname", op, EARLY);
  1190.                 return;
  1191.         }
  1192.         if (pass == LASTPASS) {
  1193.                 aint oval;
  1194.                 if (!GetLabelValue(op, oval)) {
  1195.                         Error("Internal error. ParseLabel()", op, FATAL);
  1196.                 }
  1197.                 if (value != oval) {
  1198.                         Error("Label has different value in pass 2", p.get());
  1199.                 }
  1200.                 if (IsSldExportActive()) {              // SLD (Source Level Debugging) tracing-data logging
  1201.                         SLabelTableEntry* symbol = LabelTable.Find(p.get(), true);
  1202.                         assert(symbol); // should have been already defined before last pass
  1203.                         if (symbol) {
  1204.                                 WriteToSldFile(isDefine ? -1 : symbol->page, value, 'L', ExportLabelToSld(name, symbol));
  1205.                         }
  1206.                 }
  1207.         } else {
  1208.                 Relocation::isResultAffected = isRelocatable;
  1209.                 Relocation::deltaType = isRelocatable ? Relocation::REGULAR : Relocation::OFF;
  1210.                 assert(!isDefine || Relocation::OFF == Relocation::deltaType);  // definition labels are always nonrel
  1211.                 unsigned traits = LABEL_HAS_RELOC_TRAIT \
  1212.                                                 | (isDefine ? LABEL_IS_STRUCT_D : LABEL_IS_STRUCT_E) \
  1213.                                                 | (isRelocatable ? LABEL_IS_RELOC : 0);
  1214.                 if (!LabelTable.Insert(p.get(), value, traits)) Error("Duplicate label", p.get(), EARLY);
  1215.         }
  1216. }
  1217.  
  1218. static void InsertStructSubLabels(const char* mainName, const bool isRelocatable, const CStructureEntry1* members, const aint address = 0, const bool isDefine = true) {
  1219.         char ln[LINEMAX+1];
  1220.         STRCPY(ln, LINEMAX, mainName);
  1221.         char * const lnsubw = ln + strlen(ln);
  1222.         while (members) {
  1223.                 STRCPY(lnsubw, LINEMAX-strlen(ln), members->naam);              // overwrite sub-label part
  1224.                 InsertSingleStructLabel(false, ln, isRelocatable, members->offset + address, isDefine);
  1225.                 members = members->next;
  1226.         }
  1227. }
  1228.  
  1229. void CStructure::deflab() {
  1230.         const size_t moduleNameLength = strlen(ModuleName);
  1231.         char sn[LINEMAX] = { '@', 0 };
  1232.         if (moduleNameLength && (0 == strncmp(id, ModuleName, moduleNameLength)) \
  1233.                 && ('.' == id[moduleNameLength]) && (id[moduleNameLength+1]))
  1234.         {
  1235.                 // looks like the structure name starts with current module name, use non-global way then
  1236.                 STRCPY(sn, LINEMAX-1, id + moduleNameLength + 1);
  1237.         } else {
  1238.                 // the structure name does not match current module, use the global "@id" way to define it
  1239.                 STRCPY(sn+1, LINEMAX-1, id);
  1240.         }
  1241.         InsertSingleStructLabel(true, sn, false, noffset);
  1242.         STRCAT(sn, LINEMAX-1, ".");
  1243.         InsertStructSubLabels(sn, false, mnf);
  1244. }
  1245.  
  1246. void CStructure::emitlab(char* iid, aint address, const bool isRelocatable) {
  1247.         const aint misalignment = maxAlignment ? ((-address) & (maxAlignment - 1)) : 0;
  1248.         if (misalignment) {
  1249.                 // emitting in misaligned position (considering the ALIGN used to define this struct)
  1250.                 char warnTxt[LINEMAX];
  1251.                 SPRINTF3(warnTxt, LINEMAX,
  1252.                                         "Struct %s did use ALIGN %d in definition, but here it is misaligned by %d bytes",
  1253.                                         naam, maxAlignment, misalignment);
  1254.                 Warning(warnTxt);
  1255.         }
  1256.         char sn[LINEMAX] { 0 };
  1257.         STRCPY(sn, LINEMAX-1, iid);
  1258.         InsertSingleStructLabel(true, sn, isRelocatable, address, false);
  1259.         STRCAT(sn, LINEMAX-1, ".");
  1260.         InsertStructSubLabels(sn, isRelocatable, mnf, address, false);
  1261. }
  1262.  
  1263. void CStructure::emitmembs(char*& p) {
  1264.         byte* emitTextBuffer = nullptr;
  1265.         aint val;
  1266.         int haakjes = 0;
  1267.         SkipBlanks(p);
  1268.         if (*p == '{') {
  1269.                 ++haakjes; ++p;
  1270.         }
  1271.         CStructureEntry2* ip = mbf;
  1272.         Relocation::isResultAffected = false;
  1273.         while (ip || 0 < haakjes) {
  1274.                 // check if inside curly braces block, and input seems to be empty -> fetch next line
  1275.                 if (0 < haakjes && !PrepareNonBlankMultiLine(p)) break;
  1276.                 if (nullptr == ip) {    // no more struct members expected, looking for closing '}'
  1277.                         assert(0 < haakjes);
  1278.                         if (!need(p, '}')) break;
  1279.                         --haakjes;
  1280.                         continue;
  1281.                 }
  1282.                 assert(ip);
  1283.                 switch (ip->type) {
  1284.                 case SMEMBBLOCK:
  1285.                         EmitBlock(ip->def != -1 ? ip->def : 0, ip->len, ip->def == -1, 8);
  1286.                         if (8 < ip->len) ListFile();    // "..." elipsis happened in listing, force listing
  1287.                         break;
  1288.                 case SMEMBBYTE:
  1289.                         EmitByte(ip->ParseValue(p));
  1290.                         if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(p);
  1291.                         break;
  1292.                 case SMEMBWORD:
  1293.                         // ParseValue will also add relocation data if needed (so the "ParseValue" name is misleading)
  1294.                         EmitWord(ip->ParseValue(p));
  1295.                         if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(p);
  1296.                         break;
  1297.                 case SMEMBD24:
  1298.                         val = ip->ParseValue(p);
  1299.                         EmitByte(val & 0xFF);
  1300.                         EmitWord((val>>8) & 0xFFFF);
  1301.                         if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(p);
  1302.                         break;
  1303.                 case SMEMBDWORD:
  1304.                         val = ip->ParseValue(p);
  1305.                         EmitWord(val & 0xFFFF);
  1306.                         EmitWord((val>>16) & 0xFFFF);
  1307.                         if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(p);
  1308.                         break;
  1309.                 case SMEMBTEXT:
  1310.                         {
  1311.                                 if (nullptr == emitTextBuffer) {
  1312.                                         emitTextBuffer = new byte[CStructureEntry2::TEXT_MAX_SIZE+2];
  1313.                                         if (nullptr == emitTextBuffer) ErrorOOM();
  1314.                                 }
  1315.                                 memset(emitTextBuffer, 0, ip->len);
  1316.                                 GetStructText(p, ip->len, emitTextBuffer, ip->text);
  1317.                                 for (aint ii = 0; ii < ip->len; ++ii) EmitByte(emitTextBuffer[ii]);
  1318.                         }
  1319.                         if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(p);
  1320.                         break;
  1321.                 case SMEMBPARENOPEN:
  1322.                         SkipBlanks(p);
  1323.                         if (*p == '{') { ++haakjes; ++p; }
  1324.                         break;
  1325.                 case SMEMBPARENCLOSE:
  1326.                         SkipBlanks(p);
  1327.                         if (haakjes && *p == '}') {
  1328.                                 --haakjes; ++p;
  1329.                         }
  1330.                         if (ip->next && SMEMBPARENCLOSE != ip->next->type) anyComma(p);
  1331.                         break;
  1332.                 default:
  1333.                         ErrorInt("Internal Error CStructure::emitmembs", ip->type, FATAL);
  1334.                 }
  1335.                 ip = ip->next;
  1336.         }
  1337.         if (haakjes) {
  1338.                 Error("closing } missing");
  1339.         }
  1340.         if (!SkipBlanks(p)) Error("[STRUCT] Syntax error - too many arguments?");
  1341.         Relocation::checkAndWarn();
  1342.         if (nullptr != emitTextBuffer) delete[] emitTextBuffer;
  1343. }
  1344.  
  1345. CStructureTable::CStructureTable() {
  1346.         for (auto & structPtr : strs) structPtr = nullptr;
  1347. }
  1348.  
  1349. CStructureTable::~CStructureTable() {
  1350.         for (auto structPtr : strs) if (structPtr) delete structPtr;
  1351. }
  1352.  
  1353. void CStructureTable::ReInit() {
  1354.         for (auto & structPtr : strs) {
  1355.                 if (structPtr) delete structPtr;
  1356.                 structPtr = nullptr;
  1357.         }
  1358. }
  1359.  
  1360. CStructure* CStructureTable::Add(char* naam, int no, int gl) {
  1361.         char sn[LINEMAX], * sp;
  1362.         sn[0] = 0;
  1363.         if (!gl && *ModuleName) {
  1364.                 STRCPY(sn, LINEMAX-2, ModuleName);
  1365.                 STRCAT(sn, 2, ".");
  1366.         }
  1367.         STRCAT(sn, LINEMAX-1, naam);
  1368.         sp = sn;
  1369.         if (FindDuplicate(sp)) {
  1370.                 Error("[STRUCT] Structure already exist", naam);
  1371.                 return nullptr;
  1372.         }
  1373.         strs[(*sp)&127] = new CStructure(naam, sp, 0, gl, strs[(*sp)&127]);
  1374.         if (no) {
  1375.                 strs[(*sp)&127]->AddMember(new CStructureEntry2(0, no, -1, Relocation::OFF, SMEMBBLOCK));
  1376.         }
  1377.         return strs[(*sp)&127];
  1378. }
  1379.  
  1380. CStructure* CStructureTable::zoek(const char* naam, int gl) {
  1381.         char sn[LINEMAX], * sp;
  1382.         sn[0] = 0;
  1383.         if (!gl && *ModuleName) {
  1384.                 STRCPY(sn, LINEMAX-2, ModuleName);
  1385.                 STRCAT(sn, 2, ".");
  1386.         }
  1387.         STRCAT(sn, LINEMAX-1, naam);
  1388.         sp = sn;
  1389.         CStructure* p = strs[(*sp)&127];
  1390.         while (p) {
  1391.                 if (!strcmp(sp, p->id)) return p;
  1392.                 p = p->next;
  1393.         }
  1394.         if (gl || ! *ModuleName) return NULL;
  1395.         sp += 1 + strlen(ModuleName); p = strs[(*sp)&127];
  1396.         while (p) {
  1397.                 if (!strcmp(sp, p->id)) return p;
  1398.                 p = p->next;
  1399.         }
  1400.         return NULL;
  1401. }
  1402.  
  1403. int CStructureTable::FindDuplicate(char* naam) {
  1404.         CStructure* p = strs[(*naam)&127];
  1405.         while (p) {
  1406.                 if (!strcmp(naam, p->naam)) return 1;
  1407.                 p = p->next;
  1408.         }
  1409.         return 0;
  1410. }
  1411.  
  1412. aint CStructureTable::ParseDesignedAddress(char* &p) {
  1413.         if (!SkipBlanks(p) && ('=' == *p)) {
  1414.                 char* adrP = ++p;
  1415.                 aint resultAdr;
  1416.                 if (ParseExpressionNoSyntaxError(p, resultAdr)) return resultAdr;
  1417.                 Error("[STRUCT] Syntax error in designed address", adrP, SUPPRESS);
  1418.                 return 0;
  1419.         }
  1420.         return INT_MAX;         // no "designed address" provided, emit structure bytes
  1421. }
  1422.  
  1423. int CStructureTable::Emit(char* naam, char* l, char*& p, int gl) {
  1424.         CStructure* st = zoek(naam, gl);
  1425.         if (!st) return 0;
  1426.         // create new labels corresponding to current/designed address
  1427.         aint address = CStructureTable::ParseDesignedAddress(p);
  1428.         if (l) {
  1429.                 const Relocation::EType relocatable =
  1430.                         (INT_MAX == address) ?
  1431.                                 (Relocation::type ? Relocation::REGULAR : Relocation::OFF)
  1432.                                 : Relocation::isResultAffected ? Relocation::deltaType : Relocation::OFF;
  1433.                 st->emitlab(l, (INT_MAX == address) ? CurAddress : address, relocatable == Relocation::REGULAR);
  1434.         }
  1435.         if (INT_MAX == address) st->emitmembs(p);       // address was not designed, emit also bytes
  1436.         else if (!l) Warning("[STRUCT] designed address without label = no effect");
  1437.         return 1;
  1438. }
  1439.  
  1440. SRepeatStack::SRepeatStack(aint count, CStringsList* condition, CStringsList* firstLine)
  1441.         : RepeatCount(count), RepeatCondition(condition), Lines(firstLine), Pointer(firstLine), IsInWork(false), Level(0)
  1442. {
  1443.         assert(!sourcePosStack.empty());
  1444.         sourcePos = sourcePosStack.back();
  1445. }
  1446.  
  1447. SRepeatStack::~SRepeatStack() {
  1448.         if (RepeatCondition) delete RepeatCondition;
  1449.         if (Lines) delete Lines;
  1450. }
  1451.  
  1452. //eof tables.cpp
  1453.