?login_element?

Subversion Repositories NedoOS

Rev

Rev 554 | 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. // direct.cpp
  30.  
  31. #include "sjdefs.h"
  32.  
  33. CFunctionTable DirectivesTable;
  34. CFunctionTable DirectivesTable_dup;
  35.  
  36. int ParseDirective(bool beginningOfLine)
  37. {
  38.         char* olp = lp;
  39.  
  40.         // if END/.END directive is at the beginning of line = ignore them (even with "--dirbol")
  41.         if (beginningOfLine && (cmphstr(lp, "end") || cmphstr(lp, ".end"))) {
  42.                 lp = olp;
  43.                 return 0;
  44.         }
  45.  
  46.         bp = lp;
  47.         char* n;
  48.         aint val;
  49.         if (!(n = getinstr(lp))) {      // will also reject any instruction followed by colon char (label)
  50.                 lp = olp;
  51.                 return 0;
  52.         }
  53.  
  54.         if (DirectivesTable.zoek(n)) return 1;
  55.  
  56.         // Only "." repeat directive remains, but that one can't start at beginning of line (without --dirbol)
  57.         const bool isDigitDot = ('.' == *n) && isdigit((byte)n[1]);
  58.         const bool isExprDot = ('.' == *n) && (0 == n[1]) && ('(' == *lp);
  59.         if ((beginningOfLine && !Options::syx.IsPseudoOpBOF) || (!isDigitDot && !isExprDot)) {
  60.                 lp = olp;               // alone "." must be followed by digit, or math expression in parentheses
  61.                 return 0;               // otherwise just return
  62.         }
  63.  
  64.         // parse repeat-count either from n+1 (digits) or lp (parentheses) (if syntax is valid)
  65.         if ((isDigitDot && !White(*lp)) || !ParseExpression(isDigitDot ? ++n : ++lp, val) || (isExprDot && ')' != *lp++)) {
  66.                 lp = olp; Error("Dot-repeater must be followed by number or parentheses", olp, SUPPRESS);
  67.                 return 0;
  68.         }
  69.         if (val < 1) {
  70.                 lp = olp; ErrorInt(".N must be positive integer", val, SUPPRESS);
  71.                 return 0;
  72.         }
  73.  
  74.         // preserve original line buffer, and also the line to be repeated (at `lp`)
  75.         char* ml = STRDUP(line);
  76.         SkipBlanks();
  77.         char* pp = STRDUP(lp);
  78.         // create new copy of eolComment because original "line" content will be destroyed
  79.         char* eolCommCopy = eolComment ? STRDUP(eolComment) : nullptr;
  80.         eolComment = eolCommCopy;
  81.         if (NULL == ml || NULL == pp) ErrorOOM();
  82.         ++listmacro;
  83.         do {
  84.                 line[0] = ' ';
  85.                 STRCPY(line+1, LINEMAX-1, pp);  // reset `line` to the content which should be repeated
  86.                 ParseLineSafe();                        // and parse it
  87.                 eolComment = NULL;                      // switch OFF EOL-comment after first line
  88.         } while (--val);
  89.         // restore everything
  90.         STRCPY(line, LINEMAX, ml);
  91.         --listmacro;
  92.         donotlist = 1;
  93.         if (eolCommCopy) free(eolCommCopy);
  94.         free(pp);
  95.         free(ml);
  96.         // make lp point at \0, as the repeating line was processed fully
  97.         lp = sline;
  98.         *sline = 0;
  99.         return 1;
  100. }
  101.  
  102. int ParseDirective_REPT() {
  103.         char* olp = bp = lp, * n;
  104.         if ((n = getinstr(lp)) && DirectivesTable_dup.zoek(n)) return 1;
  105.         lp = olp;
  106.         return 0;
  107. }
  108.  
  109.  
  110. static void getBytesWithCheck(int add = 0, int dc = 0, bool dz = false) {
  111.         check8(add); add &= 255;
  112.         int dirDx[130];
  113.         if (GetBytes(lp, dirDx, add, dc)) {
  114.                 EmitBytes(dirDx);
  115.                 if (dz) EmitByte(0);
  116.         } else {
  117.                 Error("no arguments");
  118.         }
  119. }
  120.  
  121. static void dirBYTE() {
  122.         getBytesWithCheck();
  123. }
  124.  
  125. static void dirDC() {
  126.         getBytesWithCheck(0, 1);
  127. }
  128.  
  129. static void dirDZ() {
  130.         getBytesWithCheck(0, 0, true);
  131. }
  132.  
  133. static void dirABYTE() {
  134.         aint add;
  135.         if (ParseExpressionNoSyntaxError(lp, add)) {
  136.                 Relocation::checkAndWarn();
  137.                 getBytesWithCheck(add);
  138.         } else {
  139.                 Error("ABYTE <offset> <bytes>: parsing <offset> failed", bp, SUPPRESS);
  140.         }
  141. }
  142.  
  143. static void dirABYTEC() {
  144.         aint add;
  145.         if (ParseExpressionNoSyntaxError(lp, add)) {
  146.                 Relocation::checkAndWarn();
  147.                 getBytesWithCheck(add, 1);
  148.         } else {
  149.                 Error("ABYTEC <offset> <bytes>: parsing <offset> failed", bp, SUPPRESS);
  150.         }
  151. }
  152.  
  153. static void dirABYTEZ() {
  154.         aint add;
  155.         if (ParseExpressionNoSyntaxError(lp, add)) {
  156.                 Relocation::checkAndWarn();
  157.                 getBytesWithCheck(add, 0, true);
  158.         } else {
  159.                 Error("ABYTEZ <offset> <bytes>: parsing <offset> failed", bp, SUPPRESS);
  160.         }
  161. }
  162.  
  163. static void dirWORD() {
  164.         aint val;
  165.         int teller = 0, e[130];
  166.         do {
  167.                 // reset alternate result flag in ParseExpression part of code
  168.                 Relocation::isResultAffected = false;
  169.                 if (SkipBlanks()) {
  170.                         Error("Expression expected", NULL, SUPPRESS);
  171.                 } else if (ParseExpressionNoSyntaxError(lp, val)) {
  172.                         check16(val);
  173.                         e[teller] = val & 65535;
  174.                         Relocation::resolveRelocationAffected(teller * 2);
  175.                         ++teller;
  176.                 } else {
  177.                         Error("[DW/DEFW/WORD] Syntax error", lp, SUPPRESS);
  178.                         break;
  179.                 }
  180.         } while (comma(lp) && teller < 128);
  181.         e[teller] = -1;
  182.         if (teller == 128 && *lp) Error("Over 128 values in DW/DEFW/WORD. Values over", lp, SUPPRESS);
  183.         if (teller) EmitWords(e);
  184.         else            Error("DW/DEFW/WORD with no arguments");
  185. }
  186.  
  187. static void dirDWORD() {
  188.         aint val;
  189.         int teller = 0, e[130 * 2];
  190.         do {
  191.                 if (SkipBlanks()) {
  192.                         Error("Expression expected", NULL, SUPPRESS);
  193.                 } else if (ParseExpressionNoSyntaxError(lp, val)) {
  194.                         e[teller * 2] = val & 65535; e[teller * 2 + 1] = (val >> 16) & 0xFFFF; ++teller;
  195.                 } else {
  196.                         Error("[DWORD] Syntax error", lp, SUPPRESS);
  197.                         break;
  198.                 }
  199.         } while (comma(lp) && teller < 128);
  200.         e[teller * 2] = -1;
  201.         if (teller == 128 && *lp) Error("Over 128 values in DWORD. Values over", lp, SUPPRESS);
  202.         if (teller) EmitWords(e);
  203.         else            Error("DWORD with no arguments");
  204. }
  205.  
  206. static void dirD24() {
  207.         aint val;
  208.         int teller = 0, e[130 * 3];
  209.         do {
  210.                 if (SkipBlanks()) {
  211.                         Error("Expression expected", NULL, SUPPRESS);
  212.                 } else if (ParseExpressionNoSyntaxError(lp, val)) {
  213.                         check24(val);
  214.                         e[teller++] = val & 255; e[teller++] = (val >> 8) & 255; e[teller++] = (val >> 16) & 255;
  215.                 } else {
  216.                         Error("[D24] Syntax error", lp, SUPPRESS);
  217.                         break;
  218.                 }
  219.         } while (comma(lp) && teller < 128*3);
  220.         e[teller] = -1;
  221.         if (teller == 128*3 && *lp) Error("Over 128 values in D24. Values over", lp, SUPPRESS);
  222.         if (teller) EmitBytes(e);
  223.         else            Error("D24 with no arguments");
  224. }
  225.  
  226. static void dirDG() {
  227.         int dirDx[130];
  228.         if (GetBits(lp, dirDx)) {
  229.                 EmitBytes(dirDx);
  230.         } else {
  231.                 Error("no arguments");
  232.         }
  233. }
  234.  
  235. static void dirDH() {
  236.         int dirDx[130];
  237.         if (GetBytesHexaText(lp, dirDx)) {
  238.                 EmitBytes(dirDx);
  239.         } else {
  240.                 Error("no arguments");
  241.         }
  242. }
  243.  
  244. static void dirBLOCK() {
  245.         aint teller,val = 0;
  246.         if (ParseExpressionNoSyntaxError(lp, teller)) {
  247.                 if (teller < 0) {
  248.                         Warning("Negative BLOCK?");
  249.                 }
  250.                 if (comma(lp)) {
  251.                         if (ParseExpression(lp, val)) check8(val);
  252.                 }
  253.                 EmitBlock(val, teller);
  254.         } else {
  255.                 Error("[BLOCK] Syntax Error in <length>", lp, SUPPRESS);
  256.         }
  257. }
  258.  
  259. bool dirPageImpl(const char* const dirName, int pageNumber) {
  260.         if (!Device) return false;
  261.         if (pageNumber < 0 || Device->PagesCount <= pageNumber) {
  262.                 char buf[LINEMAX];
  263.                 SPRINTF2(buf, LINEMAX, "[%s] Page number must be in range 0..%d", dirName, Device->PagesCount - 1);
  264.                 ErrorInt(buf, pageNumber);
  265.                 return false;
  266.         }
  267.         Device->GetCurrentSlot()->Page = Device->GetPage(pageNumber);
  268.         Device->CheckPage(CDevice::CHECK_RESET);
  269.         return true;
  270. }
  271.  
  272. static void dirPageImpl(const char* const dirName) {
  273.         aint pageNum;
  274.         if (ParseExpressionNoSyntaxError(lp, pageNum)) {
  275.                 dirPageImpl(dirName, pageNum);
  276.         } else {
  277.                 Error("Syntax error in <page_number>", lp, SUPPRESS);
  278.         }
  279. }
  280.  
  281. static void dirORG() {
  282.         aint val;
  283.         if (!ParseExpressionNoSyntaxError(lp, val)) {
  284.                 Error("[ORG] Syntax error in <address>", lp, SUPPRESS);
  285.                 return;
  286.         }
  287.         // crop (with warning) address in device or non-longptr mode to 16bit address range
  288.         if ((DeviceID || !Options::IsLongPtr) && !check16u(val)) val &= 0xFFFF;
  289.         CurAddress = val;
  290.         if (DISP_NONE != PseudoORG) WarningById(W_DISPLACED_ORG);
  291.         if (!DeviceID) return;
  292.         if (!comma(lp)) {
  293.                 Device->CheckPage(CDevice::CHECK_RESET);
  294.                 return;
  295.         }
  296.         // emit warning when current slot does not cover address used for ORG
  297.         auto slot = Device->GetCurrentSlot();
  298.         if ((CurAddress < slot->Address || slot->Address + slot->Size <= CurAddress)) {
  299.                 char warnTxt[LINEMAX];
  300.                 SPRINTF4(warnTxt, LINEMAX,
  301.                                         "address 0x%04X vs slot %d range 0x%04X..0x%04X",
  302.                                         CurAddress, Device->GetCurrentSlotNum(), slot->Address, slot->Address + slot->Size - 1);
  303.                 WarningById(W_ORG_PAGE, warnTxt);
  304.         }
  305.         dirPageImpl("ORG");
  306. }
  307.  
  308. static void dirDISP() {
  309.         if (DISP_NONE != PseudoORG) {
  310.                 Warning("[DISP] displacement inside another displacement block, ignoring it.");
  311.                 SkipToEol(lp);
  312.                 return;
  313.         }
  314.         aint valAdr, valPageNum;
  315.         // parse+validate values first, don't even switch into DISP mode in case of any error
  316.         Relocation::isResultAffected = false;
  317.         if (!ParseExpressionNoSyntaxError(lp, valAdr)) {
  318.                 Error("[DISP] Syntax error in <address>", lp, SUPPRESS);
  319.                 return;
  320.         }
  321.         // the expression of the DISP shouldn't be affected by relocation (even when starting inside relocation block)
  322.         if (Relocation::checkAndWarn(true)) {
  323.                 SkipToEol(lp);
  324.                 return;         // report it as error and exit early
  325.         }
  326.         if (comma(lp)) {
  327.                 if (!ParseExpressionNoSyntaxError(lp, valPageNum)) {
  328.                         Error("[DISP] Syntax error in <page number>", lp);
  329.                         return;
  330.                 }
  331.                 if (!DeviceID) {
  332.                         Error("[DISP] <page number> is accepted only in device mode", line);
  333.                         return;
  334.                 }
  335.                 if (valPageNum < 0 || Device->PagesCount <= valPageNum) {
  336.                         ErrorInt("[DISP] <page number> is out of range", valPageNum);
  337.                         return;
  338.                 }
  339.                 dispPageNum = valPageNum;
  340.         } else {
  341.                 dispPageNum = LABEL_PAGE_UNDEFINED;
  342.         }
  343.         // crop (with warning) address in device or non-longptr mode to 16bit address range
  344.         if ((DeviceID || !Options::IsLongPtr) && !check16u(valAdr)) valAdr &= 0xFFFF;
  345.         // everything is valid, switch to DISP mode (dispPageNum is already set above)
  346.         adrdisp = CurAddress;
  347.         CurAddress = valAdr;
  348.         PseudoORG = Relocation::type ? DISP_INSIDE_RELOCATE : DISP_ACTIVE;
  349. }
  350.  
  351. static void dirENT() {
  352.         if (DISP_NONE == PseudoORG) {
  353.                 Error("ENT should be after DISP");
  354.                 return;
  355.         }
  356.         // check if the DISP..ENT block is either fully inside relocation block, or engulfing it fully.
  357.         if (DISP_ACTIVE == PseudoORG && Relocation::type) {
  358.                 Error("The DISP block did start outside of relocation block, can't end inside it");
  359.                 return;
  360.         }
  361.         if (DISP_INSIDE_RELOCATE == PseudoORG && !Relocation::type) {
  362.                 Error("The DISP block did start inside of relocation block, can't end outside of it");
  363.                 return;
  364.         }
  365.         CurAddress = adrdisp;
  366.         PseudoORG = DISP_NONE;
  367.         dispPageNum = LABEL_PAGE_UNDEFINED;
  368. }
  369.  
  370. static void dirPAGE() {
  371.         if (!DeviceID) {
  372.                 Warning("PAGE only allowed in real device emulation mode (See DEVICE)");
  373.                 SkipParam(lp);
  374.         } else {
  375.                 dirPageImpl("PAGE");
  376.         }
  377. }
  378.  
  379. static void dirMMU() {
  380.         if (!DeviceID) {
  381.                 Warning("MMU is allowed only in real device emulation mode (See DEVICE)");
  382.                 SkipToEol(lp);
  383.                 return;
  384.         }
  385.         aint slot1, slot2, pageN = -1, address = -1;
  386.         CDeviceSlot::ESlotOptions slotOpt = CDeviceSlot::O_NONE;
  387.         if (!ParseExpression(lp, slot1)) {
  388.                 Error("[MMU] First slot number parsing failed", bp, SUPPRESS);
  389.                 return;
  390.         }
  391.         slot2 = slot1;
  392.         if (!comma(lp)) {       // second slot or slot-option should follow (if not comma)
  393.                 // see if there is slot1-only with option-char (e/w/n options)
  394.                 const char slotOptChar = (*lp)|0x20;    // primitive ASCII tolower
  395.                 if ('a' <= slotOptChar && slotOptChar <= 'z' && (',' == lp[1] || White(lp[1]))) {
  396.                         if ('e' == slotOptChar) slotOpt = CDeviceSlot::O_ERROR;
  397.                         else if ('w' == slotOptChar) slotOpt = CDeviceSlot::O_WARNING;
  398.                         else if ('n' == slotOptChar) slotOpt = CDeviceSlot::O_NEXT;
  399.                         else {
  400.                                 Warning("[MMU] Unknown slot option (legal: e, w, n)", lp);
  401.                         }
  402.                         ++lp;
  403.                 } else {        // there was no option char, check if there was slot2 number to define range
  404.                         if (!ParseExpression(lp, slot2)) {
  405.                                 Error("[MMU] Second slot number parsing failed", bp, SUPPRESS);
  406.                                 return;
  407.                         }
  408.                 }
  409.                 if (!comma(lp)) {
  410.                         Error("[MMU] Comma and page number expected after slot info", bp, SUPPRESS);
  411.                         return;
  412.                 }
  413.         }
  414.         if (!ParseExpression(lp, pageN)) {
  415.                 Error("[MMU] Page number parsing failed", bp, SUPPRESS);
  416.                 return;
  417.         }
  418.         if (comma(lp)) {
  419.                 if (!ParseExpressionNoSyntaxError(lp, address)) {
  420.                         Error("[MMU] address parsing failed", bp, SUPPRESS);
  421.                         return;
  422.                 }
  423.                 check16(address);
  424.                 address &= 0xFFFF;
  425.         }
  426.         // convert slot entered as addresses into slot numbering (must be precise start address of slot)
  427.         slot1 = Device->SlotNumberFromPreciseAddress(slot1);
  428.         slot2 = Device->SlotNumberFromPreciseAddress(slot2);
  429.         // validate argument values
  430.         if (slot1 < 0 || slot2 < slot1 || Device->SlotsCount <= slot2) {
  431.                 char buf[LINEMAX];
  432.                 SPRINTF1(buf, LINEMAX, "[MMU] Slot number(s) must be in range 0..%u (or exact starting address of slot) and form a range",
  433.                                  Device->SlotsCount - 1);
  434.                 Error(buf, NULL, SUPPRESS);
  435.                 return;
  436.         }
  437.         if (pageN < 0 || Device->PagesCount <= pageN + (slot2 - slot1)) {
  438.                 char buf[LINEMAX];
  439.                 SPRINTF1(buf, LINEMAX, "[MMU] Requested page(s) must be in range 0..%u", Device->PagesCount - 1);
  440.                 Error(buf, NULL, SUPPRESS);
  441.                 return;
  442.         }
  443.         // all valid, set it up
  444.         for (aint slotN = slot1; slotN <= slot2; ++slotN, ++pageN) {
  445.                 Device->GetSlot(slotN)->Page = Device->GetPage(pageN);
  446.                 // this ^ is also enough to keep global "Slot" up to date (it's a pointer)
  447.                 Device->GetSlot(slotN)->Option = slotOpt;       // resets whole range to NONE when range
  448.         }
  449.         // wrap output addresses back into 64ki address space, it's essential for MMU functionality
  450.         if (DISP_NONE != PseudoORG) adrdisp &= 0xFFFF; else CurAddress &= 0xFFFF;
  451.         // set explicit ORG address if the third argument was provided
  452.         if (0 <= address) {
  453.                 CurAddress = address;
  454.                 if (DISP_NONE != PseudoORG) {
  455.                         WarningById(W_DISPLACED_ORG);
  456.                 }
  457.         }
  458.         Device->CheckPage(CDevice::CHECK_RESET);
  459. }
  460.  
  461. static void dirSLOT() {
  462.         aint val;
  463.         if (!DeviceID) {
  464.                 Warning("SLOT only allowed in real device emulation mode (See DEVICE)");
  465.                 SkipParam(lp);
  466.                 return;
  467.         }
  468.         if (!ParseExpressionNoSyntaxError(lp, val)) {
  469.                 Error("[SLOT] Syntax error in <slot_number>", lp, SUPPRESS);
  470.                 return;
  471.         }
  472.         val = Device->SlotNumberFromPreciseAddress(val);
  473.         if (!Device->SetSlot(val)) {
  474.                 char buf[LINEMAX];
  475.                 SPRINTF1(buf, LINEMAX, "[SLOT] Slot number must be in range 0..%u, or exact starting address of slot", Device->SlotsCount - 1);
  476.                 Error(buf, NULL, IF_FIRST);
  477.         }
  478. }
  479.  
  480. static void dirALIGN() {
  481.         // default alignment is 4, default filler is "0/none" (if not specified in directive explicitly)
  482.         aint val, fill;
  483.         ParseAlignArguments(lp, val, fill);
  484.         if (-1 == val) val = 4;
  485.         // calculate how many bytes has to be filled to reach desired alignment
  486.         aint len = (~CurAddress + 1) & (val - 1);
  487.         if (len < 1) return;            // nothing to fill, already aligned
  488.         if (-1 == fill) EmitBlock(0, len, true);
  489.         else                    EmitBlock(fill, len, false);
  490. }
  491.  
  492. static void dirMODULE() {
  493.         char* n = GetID(lp);
  494.         if (n && (nullptr == STRCHR(n, '.'))) {
  495.                 if (*ModuleName) STRCAT(ModuleName, LINEMAX-1-strlen(ModuleName), ".");
  496.                 STRCAT(ModuleName, LINEMAX-1-strlen(ModuleName), n);
  497.                 // reset non-local label to default "_"
  498.                 if (vorlabp) free(vorlabp);
  499.                 vorlabp = STRDUP("_");
  500.                 if (IsSldExportActive()) {
  501.                         WriteToSldFile(-1, CurAddress, 'L', ExportModuleToSld());
  502.                 }
  503.         } else {
  504.                 if (n) {
  505.                         Error("[MODULE] Dots not allowed in <module_name>", n, SUPPRESS);
  506.                 } else {
  507.                         Error("[MODULE] Syntax error in <name>", bp, SUPPRESS);
  508.                 }
  509.         }
  510. }
  511.  
  512. static void dirENDMODULE() {
  513.         if (! *ModuleName) {
  514.                 Error("ENDMODULE without MODULE");
  515.                 return;
  516.         }
  517.         if (IsSldExportActive()) {
  518.                 WriteToSldFile(-1, CurAddress, 'L', ExportModuleToSld(true));
  519.         }
  520.         // remove last part of composite modules name
  521.         char* lastDot = strrchr(ModuleName, '.');
  522.         if (lastDot)    *lastDot = 0;
  523.         else                    *ModuleName = 0;
  524.         // reset non-local label to default "_"
  525.         if (vorlabp) free(vorlabp);
  526.         vorlabp = STRDUP("_");
  527. }
  528.  
  529. static void dirEND() {
  530.         char* p = lp;
  531.         aint val;
  532.         if (ParseExpression(lp, val)) {
  533.                 if (val > 65535 || val < 0) ErrorInt("[END] Invalid address", IF_FIRST);
  534.                 else                                            StartAddress = val;
  535.         } else {
  536.                 lp = p;
  537.         }
  538.  
  539.         IsRunning = 0;
  540. }
  541.  
  542. static void dirSIZE() {
  543.         aint val;
  544.         if (!ParseExpressionNoSyntaxError(lp, val)) {
  545.                 Error("[SIZE] Syntax error in <filesize>", bp, SUPPRESS);
  546.                 return;
  547.         }
  548.         if (LASTPASS != pass) return;   // only active during final pass
  549.         if (-1L == size) size = val;    // first time set
  550.         else if (size != val) ErrorInt("[SIZE] Different size than previous", size);    // just check it's same
  551. }
  552.  
  553. static void dirINCBIN() {
  554.         int offset = 0, length = INT_MAX;
  555.         std::unique_ptr<char[]> fnaam(GetFileName(lp));
  556.         if (anyComma(lp)) {
  557.                 aint val;
  558.                 if (!anyComma(lp)) {
  559.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  560.                                 Error("[INCBIN] Syntax error in <offset>", bp, SUPPRESS);
  561.                                 return;
  562.                         }
  563.                         offset = val;
  564.                 } else --lp;            // there was second comma right after, reread it
  565.                 if (anyComma(lp)) {
  566.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  567.                                 Error("[INCBIN] Syntax error in <length>", bp, SUPPRESS);
  568.                                 return;
  569.                         }
  570.                         length = val;
  571.                 }
  572.         }
  573.         BinIncFile(fnaam.get(), offset, length);
  574. }
  575.  
  576. static void dirINCHOB() {
  577.         aint val;
  578.         char* fnaamh;
  579.         unsigned char len[2];
  580.         int offset = 0,length = -1;
  581.         FILE* ff;
  582.  
  583.         std::unique_ptr<char[]> fnaam(GetFileName(lp));
  584.         if (anyComma(lp)) {
  585.                 if (!anyComma(lp)) {
  586.                         if (!ParseExpression(lp, val)) {
  587.                                 Error("[INCHOB] Syntax error", bp, IF_FIRST); return;
  588.                         }
  589.                         if (val < 0) {
  590.                                 Error("[INCHOB] Negative values are not allowed", bp); return;
  591.                         }
  592.                         offset += val;
  593.                 } else --lp;            // there was second comma right after, reread it
  594.                 if (anyComma(lp)) {
  595.                         if (!ParseExpression(lp, val)) {
  596.                                 Error("[INCHOB] Syntax error", bp, IF_FIRST); return;
  597.                         }
  598.                         if (val < 0) {
  599.                                 Error("[INCHOB] Negative values are not allowed", bp); return;
  600.                         }
  601.                         length = val;
  602.                 }
  603.         }
  604.  
  605.         fnaamh = GetPath(fnaam.get());
  606.         if (!FOPEN_ISOK(ff, fnaamh, "rb")) {
  607.                 Error("[INCHOB] Error opening file", fnaam.get(), FATAL);
  608.         }
  609.         if (fseek(ff, 0x0b, 0) || 2 != fread(len, 1, 2, ff)) {
  610.                 Error("[INCHOB] Hobeta file has wrong format", fnaam.get(), FATAL);
  611.         }
  612.         fclose(ff);
  613.         if (length == -1) {
  614.                 // calculate remaining length of the file (from the specified offset)
  615.                 length = len[0] + (len[1] << 8) - offset;
  616.         }
  617.         offset += 17;           // adjust offset (skip HOB header)
  618.         BinIncFile(fnaam.get(), offset, length);
  619.         free(fnaamh);
  620. }
  621.  
  622. static void dirINCTRD() {
  623.         aint val, offset = 0, length = INT_MAX;
  624.         std::unique_ptr<char[]> trdname(GetFileName(lp));
  625.         std::unique_ptr<char[]> filename;
  626.         if (anyComma(lp) && !anyComma(lp)) filename.reset(GetFileName(lp));
  627.         if ( !filename || !filename[0] ) {
  628.                 // file-in-disk syntax error
  629.                 Error("[INCTRD] Syntax error", bp, IF_FIRST);
  630.                 SkipToEol(lp);
  631.                 return;
  632.         }
  633.         if (anyComma(lp)) {
  634.                 if (!anyComma(lp)) {
  635.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  636.                                 Error("[INCTRD] Syntax error", bp, IF_FIRST);
  637.                                 SkipToEol(lp);
  638.                                 return;
  639.                         }
  640.                         if (val < 0) {
  641.                                 ErrorInt("[INCTRD] Negative offset value is not allowed", val);
  642.                                 SkipToEol(lp);
  643.                                 return;
  644.                         }
  645.                         offset = val;
  646.                 } else --lp;            // there was second comma right after, reread it
  647.                 if (anyComma(lp)) {
  648.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  649.                                 Error("[INCTRD] Syntax error", bp, IF_FIRST);
  650.                                 SkipToEol(lp);
  651.                                 return;
  652.                         }
  653.                         if (val < 0) {
  654.                                 ErrorInt("[INCTRD] Negative length value is not allowed", val);
  655.                                 SkipToEol(lp);
  656.                                 return;
  657.                         }
  658.                         length = val;
  659.                 }
  660.         }
  661.         if (TRD_PrepareIncFile(trdname.get(), filename.get(), offset, length)) {
  662.                 BinIncFile(trdname.get(), offset, length);
  663.         }
  664. }
  665.  
  666. static void dirSAVESNA() {
  667.         if (pass != LASTPASS) return;           // syntax error is not visible in early passes
  668.  
  669.         if (!DeviceID) {
  670.                 Error("SAVESNA only allowed in real device emulation mode (See DEVICE)", nullptr, SUPPRESS);
  671.                 return;
  672.         } else if (!IsZXSpectrumDevice(DeviceID)) {
  673.                 Error("[SAVESNA] Device must be ZXSPECTRUM48 or ZXSPECTRUM128.", nullptr, SUPPRESS);
  674.                 return;
  675.         }
  676.  
  677.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  678.         int start = StartAddress;
  679.         if (anyComma(lp)) {
  680.                 aint val;
  681.                 if (!ParseExpression(lp, val)) return;
  682.                 if (0 <= start) Warning("[SAVESNA] Start address was also defined by END, SAVESNA argument used instead");
  683.                 if (0 <= val) {
  684.                         start = val;
  685.                 } else {
  686.                         Error("[SAVESNA] Negative values are not allowed", bp, SUPPRESS);
  687.                         return;
  688.                 }
  689.         }
  690.         if (start < 0) {
  691.                 Error("[SAVESNA] No start address defined", bp, SUPPRESS);
  692.                 return;
  693.         }
  694.  
  695.         if (!SaveSNA_ZX(fnaam.get(), start)) Error("[SAVESNA] Error writing file (Disk full?)", bp, IF_FIRST);
  696. }
  697.  
  698. static void dirEMPTYTAP() {
  699.         if (pass != LASTPASS) {
  700.                 SkipParam(lp);
  701.                 return;
  702.         }
  703.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  704.         if (!fnaam[0]) {
  705.                 Error("[EMPTYTAP] Syntax error", bp, IF_FIRST); return;
  706.         }
  707.         TAP_SaveEmpty(fnaam.get());
  708. }
  709.  
  710. static void dirSAVETAP() {
  711.  
  712.         if (pass != LASTPASS) {
  713.                 SkipParam(lp);
  714.                 return;
  715.         }
  716.  
  717.         bool exec = true, realtapeMode = false;
  718.         int headerType = -1;
  719.         aint val;
  720.         int start = -1, length = -1, param2 = -1, param3 = -1;
  721.  
  722.         if (!DeviceID) {
  723.                 Error("SAVETAP only allowed in real device emulation mode (See DEVICE)");
  724.                 exec = false;
  725.         }
  726.  
  727.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  728.         std::unique_ptr<char[]> fnaamh;
  729.         if (anyComma(lp)) {
  730.                 if (!anyComma(lp)) {
  731.                         char *tlp = lp;
  732.                         char *id;
  733.  
  734.                         if ((id = GetID(lp)) && strlen(id) > 0) {
  735.                                 if (cmphstr(id, "basic")) {
  736.                                         headerType = BASIC;
  737.                                         realtapeMode = true;
  738.                                 } else if (cmphstr(id, "numbers")) {
  739.                                         headerType = NUMBERS;
  740.                                         realtapeMode = true;
  741.                                 } else if (cmphstr(id, "chars")) {
  742.                                         headerType = CHARS;
  743.                                         realtapeMode = true;
  744.                                 } else if (cmphstr(id, "code")) {
  745.                                         headerType = CODE;
  746.                                         realtapeMode = true;
  747.                                 } else if (cmphstr(id, "headless")) {
  748.                                         headerType = HEADLESS;
  749.                                         realtapeMode = true;
  750.                                 }
  751.                         }
  752.  
  753.                         if (realtapeMode) {
  754.                                 if (anyComma(lp)) {
  755.                                         if (headerType == HEADLESS) {
  756.                                                 if (!anyComma(lp)) {
  757.                                                         if (!ParseExpression(lp, val)) {
  758.                                                                 Error("[SAVETAP] Syntax error", bp, PASS3); return;
  759.                                                         }
  760.                                                         if (val < 0) {
  761.                                                                 Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  762.                                                         } else if (val > 0xFFFF) {
  763.                                                                 Error("[SAVETAP] Values higher than FFFFh are not allowed", bp, PASS3); return;
  764.                                                         }
  765.                                                         start = val;
  766.                                                 } else {
  767.                                                         Error("[SAVETAP] Syntax error. Missing start address", bp, PASS3); return;
  768.                                                 }
  769.                                                 if (anyComma(lp)) {
  770.                                                         if (!ParseExpression(lp, val)) {
  771.                                                                 Error("[SAVETAP] Syntax error", bp, PASS3); return;
  772.                                                         }
  773.                                                         if (val < 0) {
  774.                                                                 Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  775.                                                         } else if (val > 0xFFFF) {
  776.                                                                 Error("[SAVETAP] Values higher than FFFFh are not allowed", bp, PASS3); return;
  777.                                                         }
  778.                                                         length = val;
  779.                                                 }
  780.                                                 if (anyComma(lp)) {
  781.                                                         if (!ParseExpression(lp, val)) {
  782.                                                                 Error("[SAVETAP] Syntax error", bp, PASS3); return;
  783.                                                         }
  784.                                                         if (val < 0 || val > 255) {
  785.                                                                 Error("[SAVETAP] Invalid flag byte", bp, PASS3); return;
  786.                                                         }
  787.                                                         param3 = val;
  788.                                                 }
  789.                                         } else if (!anyComma(lp)) {
  790.                                                 fnaamh.reset(GetFileName(lp));
  791.                                                 if (!fnaamh[0]) {
  792.                                                         Error("[SAVETAP] Syntax error in tape file name", bp, PASS3);
  793.                                                         return;
  794.                                                 } else if (anyComma(lp) && !anyComma(lp) && ParseExpression(lp, val)) {
  795.                                                         if (val < 0) {
  796.                                                                 Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  797.                                                         } else if (val > 0xFFFF) {
  798.                                                                 Error("[SAVETAP] Values higher than FFFFh are not allowed", bp, PASS3); return;
  799.                                                         }
  800.                                                         start = val;
  801.  
  802.                                                         if (anyComma(lp) && !anyComma(lp) && ParseExpression(lp, val)) {
  803.                                                                 if (val < 0) {
  804.                                                                         Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  805.                                                                 } else if (val > 0xFFFF) {
  806.                                                                         Error("[SAVETAP] Values higher than FFFFh are not allowed", bp, PASS3); return;
  807.                                                                 }
  808.                                                                 length = val;
  809.  
  810.                                                                 if (anyComma(lp)) {
  811.                                                                         if (!ParseExpression(lp, val)) {
  812.                                                                                 Error("[SAVETAP] Syntax error", bp, IF_FIRST); return;
  813.                                                                         }
  814.                                                                         if (val < 0) {
  815.                                                                                 Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  816.                                                                         } else if (val > 0xFFFF) {
  817.                                                                                 Error("[SAVETAP] Values more than FFFFh are not allowed", bp, PASS3); return;
  818.                                                                         }
  819.                                                                         param2 = val;
  820.                                                                 }
  821.                                                                 if (anyComma(lp)) {
  822.                                                                         if (!ParseExpression(lp, val)) {
  823.                                                                                 Error("[SAVETAP] Syntax error", bp, IF_FIRST); return;
  824.                                                                         }
  825.                                                                         if (val < 0) {
  826.                                                                                 Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  827.                                                                         } else if (val > 0xFFFF) {
  828.                                                                                 Error("[SAVETAP] Values more than FFFFh are not allowed", bp, PASS3); return;
  829.                                                                         }
  830.                                                                         param3 = val;
  831.                                                                 }
  832.                                                         } else {
  833.                                                                 Error("[SAVETAP] Syntax error. Missing block length", bp, PASS3); return;
  834.                                                         }
  835.                                                 } else {
  836.                                                         Error("[SAVETAP] Syntax error. Missing start address", bp, PASS3); return;
  837.                                                 }
  838.                                         } else {
  839.                                                 Error("[SAVETAP] Syntax error. Missing tape block file name", bp, PASS3); return;
  840.                                         }
  841.                                 } else {
  842.                                         realtapeMode = false;
  843.                                 }
  844.                         }
  845.                         if (!realtapeMode) {
  846.                                 lp = tlp;
  847.                                 IsLabelNotFound = false;
  848.                                 if (!ParseExpression(lp, val) || IsLabelNotFound) {
  849.                                         Error("[SAVETAP] Syntax error", bp, PASS3); return;
  850.                                 }
  851.                                 if (val < 0) {
  852.                                         Error("[SAVETAP] Negative values are not allowed", bp, PASS3); return;
  853.                                 }
  854.                                 start = val;
  855.                         }
  856.                 } else {
  857.                         Error("[SAVETAP] Syntax error. No parameters", bp, PASS3); return;
  858.                 }
  859.         } else if (StartAddress < 0) {
  860.                 Error("[SAVETAP] Syntax error. No parameters", bp, PASS3); return;
  861.         } else {
  862.                 start = StartAddress;
  863.         }
  864.  
  865.         if (exec) {
  866.                 int done = 0;
  867.  
  868.                 if (realtapeMode) {
  869.                         done = TAP_SaveBlock(fnaam.get(), headerType, fnaamh.get(), start, length, param2, param3);
  870.                 } else {
  871.                         if (!IsZXSpectrumDevice(DeviceID)) {
  872.                                 Error("[SAVETAP snapshot] Device is not of ZX Spectrum type.", Device->ID, SUPPRESS);
  873.                         } else {
  874.                                 done = TAP_SaveSnapshot(fnaam.get(), start);
  875.                         }
  876.                 }
  877.  
  878.                 if (!done) {
  879.                         Error("[SAVETAP] Error writing file", bp, IF_FIRST);
  880.                 }
  881.         }
  882. }
  883.  
  884. static void dirSAVEBIN() {
  885.         if (!DeviceID) {
  886.                 Error("SAVEBIN only allowed in real device emulation mode (See DEVICE)");
  887.                 SkipToEol(lp);
  888.                 return;
  889.         }
  890.         bool exec = (LASTPASS == pass);
  891.         aint val;
  892.         int start = -1, length = -1;
  893.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  894.         if (anyComma(lp)) {
  895.                 if (!anyComma(lp)) {
  896.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  897.                                 Error("[SAVEBIN] Syntax error", bp, SUPPRESS); return;
  898.                         }
  899.                         if (val < 0) {
  900.                                 Error("[SAVEBIN] Values less than 0000h are not allowed", bp); return;
  901.                         } else if (val > 0xFFFF) {
  902.                                 Error("[SAVEBIN] Values more than FFFFh are not allowed", bp); return;
  903.                         }
  904.                         start = val;
  905.                 } else {
  906.                         Error("[SAVEBIN] Syntax error. No parameters", bp, PASS3); return;
  907.                 }
  908.                 if (anyComma(lp)) {
  909.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  910.                                 Error("[SAVEBIN] Syntax error", bp, SUPPRESS); return;
  911.                         }
  912.                         if (val < 0) {
  913.                                 Error("[SAVEBIN] Negative values are not allowed", bp); return;
  914.                         }
  915.                         length = val;
  916.                 }
  917.         } else {
  918.                 Error("[SAVEBIN] Syntax error. No parameters", bp); return;
  919.         }
  920.  
  921.         if (exec && !SaveBinary(fnaam.get(), start, length)) {
  922.                 Error("[SAVEBIN] Error writing file (Disk full?)", bp, IF_FIRST);
  923.         }
  924. }
  925.  
  926. static void dirSAVEDEV() {
  927.         bool exec = DeviceID && LASTPASS == pass;
  928.         if (!exec && LASTPASS == pass) Error("SAVEDEV only allowed in real device emulation mode (See DEVICE)");
  929.  
  930.         aint args[3]{-1, -1, -1};               // page, offset, length
  931.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  932.         for (auto & arg : args) {
  933.                 if (!comma(lp) || !ParseExpression(lp, arg)) {
  934.                         exec = false;
  935.                         Error("Expected syntax SAVEDEV <filename>,<startPage>,<startOffset>,<length>", bp, SUPPRESS);
  936.                 }
  937.         }
  938.         if (exec) {
  939.                 // validate arguments
  940.                 if (args[0] < 0 || Device->PagesCount <= args[0]) {
  941.                         exec = false; ErrorInt("[SAVEDEV] page number is out of range", args[0]);
  942.                 }
  943.                 const int32_t start = Device->GetMemoryOffset(args[0], args[1]);
  944.                 const int32_t totalRam = Device->GetMemoryOffset(Device->PagesCount, 0);
  945.                 if (exec && (start < 0 || totalRam <= start)) {
  946.                         exec = false; ErrorInt("[SAVEDEV] calculated start address is out of range", start);
  947.                 }
  948.                 if (exec && (args[2] <= 0 || totalRam < start + args[2])) {
  949.                         exec = false;
  950.                         if (args[2]) ErrorInt("[SAVEDEV] invalid end address (bad length?)", start + args[2]);
  951.                         else Warning("[SAVEDEV] zero length requested");
  952.                 }
  953.                 if (exec && !SaveDeviceMemory(fnaam.get(), (size_t)start, (size_t)args[2])) {
  954.                         Error("[SAVEDEV] Error writing file (Disk full?)", bp, IF_FIRST);
  955.                 }
  956.         }
  957. }
  958.  
  959. static void dirSAVE3DOS() {
  960.         if (!DeviceID) {
  961.                 Error("SAVE3DOS works in real device emulation mode (See DEVICE)");
  962.                 SkipToEol(lp);
  963.                 return;
  964.         }
  965.         bool exec = (LASTPASS == pass);
  966.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  967.         aint args[5] = { -1, -1, 3, -1, -1 };   // address, size, type, w2_line, w3
  968.         const bool optional[] = {false, false, true, true, true};
  969.         if (!anyComma(lp) || !getIntArguments<5>(lp, args, optional)) {
  970.                 Error("[SAVE3DOS] expected syntax is <filename>,<address>,<size>[,<type>[,<w2_line>[,<w3>]]]", bp, SUPPRESS);
  971.                 return;
  972.         }
  973.         aint &address = args[0], &size = args[1], &type = args[2], &w2_line = args[3], &w3 = args[4];
  974.         if (address < 0 || size < 1 || 0x10000 < address + size) {
  975.                 Error("[SAVE3DOS] [address, size] region outside of 64ki", bp);
  976.                 return;
  977.         }
  978.         if (-1 == w3) w3 = size;        // default for w3 is size for all types, unless overridden
  979.         switch (type) {
  980.         case 0:         // type Program: default w2 = 0x8000
  981.                 if (-1 == w2_line) w2_line = 0x8000;
  982.         case 1:         // type Numeric array: no idea what w2 actually should be for these
  983.         case 2:         // type Character array:
  984.                 break;
  985.         case 3:         // type Code: default w2 = load address
  986.                 if (-1 == w2_line) w2_line = address;
  987.                 break;
  988.         default:
  989.                 Error("[SAVE3DOS] expected type 0..3", bp);
  990.                 return;
  991.         }
  992.         if (exec && !SaveBinary3dos(fnaam.get(), address, size, type, w2_line, w3)) {
  993.                 Error("[SAVE3DOS] Error writing file (Disk full?)", bp, IF_FIRST);
  994.         }
  995. }
  996.  
  997. static void dirSAVEAMSDOS() {
  998.         if (!DeviceID) {
  999.                 Error("SAVEAMSDOS works in real device emulation mode (See DEVICE)");
  1000.                 SkipToEol(lp);
  1001.                 return;
  1002.         }
  1003.         bool exec = (LASTPASS == pass);
  1004.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  1005.         aint args[] = { -1, -1, 0, 2 }; // address, size, start, type
  1006.         const bool optional[] = {false, false, true, true};
  1007.         if (!anyComma(lp) || !getIntArguments<4>(lp, args, optional)) {
  1008.                 Error("[SAVEAMSDOS] expected syntax is <filename>,<address>,<size>[,<start = 0>[,<type = 2>]", bp, SUPPRESS);
  1009.                 return;
  1010.         }
  1011.         aint &address = args[0], &size = args[1], &start = args[2], &type = args[3];
  1012.         if (address < 0 || size < 1 || 0x10000 < address + size) {
  1013.                 Error("[SAVEAMSDOS] [address, size] region outside of 64ki", bp);
  1014.                 return;
  1015.         }
  1016.         check16u(start);
  1017.         if (type < 0) type = -0x1000;           // check8 works for -256..+255 values, in this case just 0..255 is valid
  1018.         check8(type);
  1019.         if (exec && !SaveBinaryAmsdos(fnaam.get(), address, size, start, type)) {
  1020.                 Error("[SAVEAMSDOS] Error writing file (Disk full?)", bp, IF_FIRST);
  1021.         }
  1022. }
  1023.  
  1024. static void dirSAVEHOB() {
  1025.  
  1026.         if (!DeviceID || pass != LASTPASS) {
  1027.                 if (!DeviceID) Error("SAVEHOB only allowed in real device emulation mode (See DEVICE)");
  1028.                 SkipToEol(lp);
  1029.                 return;
  1030.         }
  1031.         aint val;
  1032.         int start = -1,length = -1;
  1033.         bool exec = true;
  1034.  
  1035.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  1036.         std::unique_ptr<char[]> fnaamh;
  1037.         if (anyComma(lp)) {
  1038.                 if (!anyComma(lp)) {
  1039.                         fnaamh.reset(GetFileName(lp));
  1040.                         if (!fnaamh[0]) {
  1041.                                 Error("[SAVEHOB] Syntax error", bp, PASS3); return;
  1042.                         }
  1043.                 } else {
  1044.                         Error("[SAVEHOB] Syntax error. No parameters", bp, PASS3); return;
  1045.                 }
  1046.         } else {
  1047.                 Error("[SAVEHOB] Syntax error. No parameters", bp, PASS3); return; //is this ok?
  1048.         }
  1049.  
  1050.         if (anyComma(lp)) {
  1051.                 if (!anyComma(lp)) {
  1052.                         if (!ParseExpression(lp, val)) {
  1053.                                 Error("[SAVEHOB] Syntax error", bp, PASS3); return;
  1054.                         }
  1055.                         if (val < 0x4000) {
  1056.                                 Error("[SAVEHOB] Values less than 4000h are not allowed", bp, PASS3); return;
  1057.                         } else if (val > 0xFFFF) {
  1058.                                 Error("[SAVEHOB] Values more than FFFFh are not allowed", bp, PASS3); return;
  1059.                         }
  1060.                         start = val;
  1061.                 } else {
  1062.                         Error("[SAVEHOB] Syntax error. No parameters", bp, PASS3); return;
  1063.                 }
  1064.                 if (anyComma(lp)) {
  1065.                         if (!ParseExpression(lp, val)) {
  1066.                                 Error("[SAVEHOB] Syntax error", bp, PASS3); return;
  1067.                         }
  1068.                         if (val < 0) {
  1069.                                 Error("[SAVEHOB] Negative values are not allowed", bp, PASS3); return;
  1070.                         }
  1071.                         length = val;
  1072.                 }
  1073.         } else {
  1074.                 Error("[SAVEHOB] Syntax error. No parameters", bp, PASS3); return;
  1075.         }
  1076.         if (exec && !SaveHobeta(fnaam.get(), fnaamh.get(), start, length)) {
  1077.                 Error("[SAVEHOB] Error writing file (Disk full?)", bp, IF_FIRST); return;
  1078.         }
  1079. }
  1080.  
  1081. static void dirEMPTYTRD() {
  1082.         if (pass != LASTPASS) {
  1083.                 SkipToEol(lp);
  1084.                 return;
  1085.         }
  1086.         char diskLabel[9] = "        ";
  1087.  
  1088.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  1089.         if (!fnaam[0]) {
  1090.                 Error("[EMPTYTRD] Syntax error", bp, IF_FIRST);
  1091.                 return;
  1092.         }
  1093.         if (anyComma(lp)) {
  1094.                 std::unique_ptr<char[]> srcLabel(GetFileName(lp, false));
  1095.                 if (!srcLabel[0]) {
  1096.                         Error("[EMPTYTRD] Syntax error, empty label", bp, IF_FIRST);
  1097.                 } else {
  1098.                         for (int i = 0; i < 8; ++i) {
  1099.                                 if (!srcLabel[i]) break;
  1100.                                 diskLabel[i] = srcLabel[i];
  1101.                         }
  1102.                         if (8 < strlen(srcLabel.get())) {
  1103.                                 Warning("[EMPTYTRD] label will be truncated to 8 characters", diskLabel);
  1104.                         }
  1105.                 }
  1106.         }
  1107.         TRD_SaveEmpty(fnaam.get(), diskLabel);
  1108. }
  1109.  
  1110. static void dirSAVETRD() {
  1111.         if (!DeviceID || pass != LASTPASS) {
  1112.                 if (!DeviceID) Error("SAVETRD only allowed in real device emulation mode (See DEVICE)");
  1113.                 SkipToEol(lp);
  1114.                 return;
  1115.         }
  1116.  
  1117.         bool exec = true, replace = false, addplace = false;
  1118.         aint val;
  1119.         int start = -1, length = -1, autostart = -1, lengthMinusVars = -1;
  1120.  
  1121.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  1122.         std::unique_ptr<char[]> fnaamh;
  1123.         if (anyComma(lp)) {
  1124.                 if (!anyComma(lp)) {
  1125.                         if ((replace = ('|' == *lp))) SkipBlanks(++lp); // detect "|" for "replace" feature
  1126.                         else if ((addplace = ('&' == *lp))) SkipBlanks(++lp); // detect "&" for "addplace" feature
  1127.                         fnaamh.reset(GetFileName(lp));
  1128.                         if (!fnaamh[0]) {
  1129.                                 Error("[SAVETRD] Syntax error", bp, PASS3); return;
  1130.                         }
  1131.                 } else {
  1132.                         Error("[SAVETRD] Syntax error. No parameters", bp, PASS3); return;
  1133.                 }
  1134.         } else {
  1135.                 Error("[SAVETRD] Syntax error. No parameters", bp, PASS3); return; //is this ok?
  1136.         }
  1137.  
  1138.         if (anyComma(lp)) {
  1139.                 if (!anyComma(lp)) {
  1140.                         if (!ParseExpression(lp, val)) {
  1141.                                 Error("[SAVETRD] Syntax error", bp, PASS3); return;
  1142.                         }
  1143.                         if (val > 0xFFFF) {
  1144.                                 Error("[SAVETRD] Values more than 0FFFFh are not allowed", bp, PASS3); return;
  1145.                         }
  1146.                         start = val;
  1147.                 } else {
  1148.                         Error("[SAVETRD] Syntax error. No parameters", bp, PASS3); return;
  1149.                 }
  1150.                 if (anyComma(lp)) {
  1151.                         if (!anyComma(lp)) {
  1152.                                 if (!ParseExpression(lp, val)) {
  1153.                                         Error("[SAVETRD] Syntax error", bp, PASS3); return;
  1154.                                 }
  1155.                                 if (val < 0) {
  1156.                                         Error("[SAVETRD] Negative values are not allowed", bp, PASS3); return;
  1157.                                 }
  1158.                                 length = val;
  1159.                         } else {
  1160.                                 Error("[SAVETRD] Syntax error. No parameters", bp, PASS3); return;
  1161.                         }
  1162.                 }
  1163.                 if (anyComma(lp)) {
  1164.                         if (addplace) {
  1165.                                 Error("[SAVETRD] Autostart is not used here", bp, PASS3); return;
  1166.                         } else {
  1167.                                 if (!ParseExpression(lp, val)) {
  1168.                                         Error("[SAVETRD] Syntax error", bp, PASS3); return;
  1169.                                 }
  1170.                                 if (val < 0) {
  1171.                                         Error("[SAVETRD] Negative values are not allowed", bp, PASS3); return;
  1172.                                 }
  1173.                                 autostart = val;
  1174.                                 // optional length of BASIC without variables
  1175.                                 if (anyComma(lp)) {
  1176.                                         if (!ParseExpression(lp, val)) {
  1177.                                                 Error("[SAVETRD] Syntax error", bp, PASS3); return;
  1178.                                         }
  1179.                                         lengthMinusVars = val;
  1180.                                 }
  1181.                         }
  1182.                 }
  1183.         } else {
  1184.                 Error("[SAVETRD] Syntax error. No parameters", bp, PASS3); return;
  1185.         }
  1186.  
  1187.         if (exec) TRD_AddFile(fnaam.get(), fnaamh.get(), start, length, autostart, replace, addplace, lengthMinusVars);
  1188. }
  1189.  
  1190. static void dirENCODING() {
  1191.         std::unique_ptr<char[]> opt(GetFileName(lp, false));
  1192.         char* comparePtr = opt.get();
  1193.         if (cmphstr(comparePtr, "dos")) {
  1194.                 ConvertEncoding = ENCDOS;
  1195.         } else if (cmphstr(comparePtr, "win")) {
  1196.                 ConvertEncoding = ENCWIN;
  1197.         } else {
  1198.                 Error("[ENCODING] Invalid argument (valid values: \"dos\" and \"win\")", opt.get(), IF_FIRST);
  1199.         }
  1200. }
  1201.  
  1202. static void dirOPT() {
  1203.         // supported options: --zxnext[=cspect] --reversepop --dirbol --nofakes --syntax=<...> -W...
  1204.         // process OPT specific command keywords first: {push, pop, reset, listoff, liston, listall, listact, listmc}
  1205.         bool didReset = false, didList = Options::syx.IsListingSuspended;
  1206.         while (!SkipBlanks(lp) && '-' != *lp) {
  1207.                 if (cmphstr(lp, "pop")) {       // "pop" previous syntax state
  1208.                         if (!Options::SSyntax::popSyntax()) Warning("[OPT] no previous syntax found");
  1209.                         return;
  1210.                 } else if (cmphstr(lp, "push")) {       // "push" previous syntax state
  1211.                         if (didReset) Warning("[OPT] pushing syntax status after reset");
  1212.                         // preserve current syntax status, before using arguments of OPT
  1213.                         Options::SSyntax::pushCurrentSyntax();
  1214.                 } else if (cmphstr(lp, "reset")) {      // keep current syntax state
  1215.                         Options::SSyntax::resetCurrentSyntax();
  1216.                         didReset = true;
  1217.                 } else if (cmphstr(lp, "listoff")) {
  1218.                         if (!didList) {
  1219.                                 ListFile();             // *list* the OPT line suspending the listing
  1220.                                 // show in listing file that some part was suspended
  1221.                                 FILE* listFile = GetListingFile();
  1222.                                 if (LASTPASS == pass && listFile) fputs("# listing file suspended...\n", listFile);
  1223.                         }
  1224.                         donotlist = 1;
  1225.                         Options::syx.IsListingSuspended = didList = true;
  1226.                 } else if (cmphstr(lp, "liston")) {
  1227.                         Options::syx.IsListingSuspended = false;
  1228.                 } else if (cmphstr(lp, "listall")) {
  1229.                         if (!didList) ListFile();               // *list* the OPT line changing the filtering
  1230.                         didList = true;
  1231.                         donotlist = 1;
  1232.                         Options::syx.ListingType = Options::LST_T_ALL;
  1233.                 } else if (cmphstr(lp, "listact")) {
  1234.                         if (!didList) ListFile();               // *list* the OPT line changing the filtering
  1235.                         didList = true;
  1236.                         donotlist = 1;
  1237.                         Options::syx.ListingType = Options::LST_T_ACTIVE;
  1238.                 } else if (cmphstr(lp, "listmc")) {
  1239.                         if (!didList) ListFile();               // *list* the OPT line changing the filtering
  1240.                         didList = true;
  1241.                         donotlist = 1;
  1242.                         Options::syx.ListingType = Options::LST_T_MC_ONLY;
  1243.                 } else {
  1244.                         Error("[OPT] invalid command (valid commands: push, pop, reset, liston, listoff, listall, listact, listmc)", lp);
  1245.                         SkipToEol(lp);
  1246.                         return;
  1247.                 }
  1248.         }
  1249.         // split user arguments into "argc, argv" like variables (by white-space)
  1250.         char parsedOpts[LINEMAX];
  1251.         std::vector<char*> parsedOptsArray;
  1252.         int charI = 0, errI;
  1253.         while (!SkipBlanks(lp)) {
  1254.                 parsedOptsArray.push_back(parsedOpts + charI);
  1255.                 while (*lp && !White()) parsedOpts[charI++] = *lp++;
  1256.                 parsedOpts[charI++] = 0;
  1257.         }
  1258.         int optI = parsedOptsArray.size();
  1259.         parsedOptsArray.push_back(nullptr);
  1260.         // parse user arguments and adjust current syntax setup
  1261.         if (optI != (errI = Options::parseSyntaxOptions(optI, parsedOptsArray.data()))) {
  1262.                 Error("[OPT] invalid/failed option", parsedOptsArray[errI]);
  1263.         }
  1264.         // init Z80N extensions if requested (the Init is safe to be called multiple times)
  1265.         if (Options::syx.IsNextEnabled) Z80::InitNextExtensions();
  1266. }
  1267.  
  1268. static void dirLABELSLIST() {
  1269.         if (pass != 1 || !DeviceID) {
  1270.                 if (!DeviceID) Error("LABELSLIST only allowed in real device emulation mode (See DEVICE)");
  1271.                 SkipToEol(lp);
  1272.                 return;
  1273.         }
  1274.         std::unique_ptr<char[]> opt(GetOutputFileName(lp));
  1275.         if (opt[0]) {
  1276.                 STRCPY(Options::UnrealLabelListFName, LINEMAX, opt.get());
  1277.                 Options::EmitVirtualLabels = false;
  1278.                 if (comma(lp)) {
  1279.                         aint virtualLabelsArg;
  1280.                         if (!ParseExpressionNoSyntaxError(lp, virtualLabelsArg)) {
  1281.                                 Error("[LABELSLIST] Syntax error in <virtual labels>", bp, EARLY);
  1282.                                 return;
  1283.                         }
  1284.                         Options::EmitVirtualLabels = (virtualLabelsArg != 0);
  1285.                 }
  1286.         } else {
  1287.                 Error("[LABELSLIST] No filename", bp, EARLY);   // pass == 1 -> EARLY
  1288.         }
  1289. }
  1290.  
  1291. static void dirCSPECTMAP() {
  1292.         if (LASTPASS != pass || !DeviceID) {
  1293.                 if (!DeviceID) Error("CSPECTMAP only allowed in real device emulation mode (See DEVICE)");
  1294.                 SkipParam(lp);
  1295.                 return;
  1296.         }
  1297.         std::unique_ptr<char[]> fName(GetOutputFileName(lp));
  1298.         if (fName[0]) {
  1299.                 STRCPY(Options::CSpectMapFName, LINEMAX, fName.get());
  1300.         } else {                // create default map file name from current source file name (appends ".map")
  1301.                 assert(!sourcePosStack.empty());
  1302.                 STRCPY(Options::CSpectMapFName, LINEMAX-5, sourcePosStack.back().filename);
  1303.                 STRCAT(Options::CSpectMapFName, LINEMAX-1, ".map");
  1304.         }
  1305.         // remember page size of current device (in case the source is multi-device later)
  1306.         Options::CSpectMapPageSize = Device->GetPage(0)->Size;
  1307. }
  1308.  
  1309. static void dirBPLIST() {
  1310.         // breakpoint file is opened in second pass, and content is written through third pass
  1311.         // so position of `BPLIST` directive in source does not matter
  1312.         if (2 != pass || !DeviceID) {   // nothing to do in first or last pass, second will open the file
  1313.                 if (2 == pass) {        // !Device is true -> no device in second pass -> error
  1314.                         Error("BPLIST only allowed in real device emulation mode (See DEVICE)", nullptr, EARLY);
  1315.                 }
  1316.                 SkipToEol(lp);
  1317.                 return;
  1318.         }
  1319.         std::unique_ptr<char[]> fName(GetOutputFileName(lp));
  1320.         EBreakpointsFile type = BPSF_UNREAL;
  1321.         if (cmphstr(lp, "unreal")) {
  1322.                 type = BPSF_UNREAL;
  1323.         } else if (cmphstr(lp, "zesarux")) {
  1324.                 type = BPSF_ZESARUX;
  1325.         } else if (!SkipBlanks()) {
  1326.                 Warning("[BPLIST] invalid breakpoints file type (use \"unreal\" or \"zesarux\")", lp, W_EARLY);
  1327.         }
  1328.         OpenBreakpointsFile(fName.get(), type);
  1329. }
  1330.  
  1331. static void dirSETBREAKPOINT() {
  1332.         if (LASTPASS != pass) {
  1333.                 SkipToEol(lp);
  1334.                 return;
  1335.         }
  1336.         aint val = 0;
  1337.         if (SkipBlanks(lp)) {           // without any expression do the "$" breakpoint
  1338.                 WriteBreakpoint(CurAddress);
  1339.         } else if (ParseExpressionNoSyntaxError(lp, val)) {
  1340.                 WriteBreakpoint(val);
  1341.         } else {
  1342.                 Error("[SETBREAKPOINT] Syntax error", bp, SUPPRESS);
  1343.         }
  1344. }
  1345.  
  1346. /*void dirTEXTAREA() {
  1347.  
  1348. }*/
  1349.  
  1350. // error message templates for IF**some** directives
  1351. constexpr static size_t dirIfErrorsN = 2, dirIfErrorsSZ = 48;
  1352. const static char dirIfErrorsTxtSrc[dirIfErrorsN][dirIfErrorsSZ] = {
  1353.         { "[%s] No ENDIF" },
  1354.         { "[%s] one ELSE only expected" }
  1355. };
  1356.  
  1357. // IF and IFN internal helper, to evaluate expression
  1358. static bool dirIfIfn(aint & val) {
  1359.         IsLabelNotFound = false;
  1360.         if (!ParseExpression(lp, val)) {
  1361.                 Error("[IF/IFN] Syntax error", lp, IF_FIRST);
  1362.                 return false;
  1363.         }
  1364.         if (IsLabelNotFound) {
  1365.                 WarningById(W_FWD_REF, bp, W_EARLY);
  1366.         }
  1367.         return true;
  1368. }
  1369.  
  1370. // main IF implementation parsing/skipping part of source depending on "val", handling ELSE/ENDIF
  1371. static void dirIfInternal(const char* dirName, aint val) {
  1372.         // set up error messages for the particular pseudo-op
  1373.         char errorsTxt[dirIfErrorsN][dirIfErrorsSZ];
  1374.         for (size_t i = 0; i < dirIfErrorsN; ++i) {
  1375.                 SPRINTF1(errorsTxt[i], dirIfErrorsSZ, dirIfErrorsTxtSrc[i], dirName);
  1376.         }
  1377.         // do the IF**some** part
  1378.         ListFile();
  1379.         EReturn ret = END;
  1380.         aint elseCounter = 0;
  1381.         aint orVal = false;
  1382.         while (ENDIF != ret) {
  1383.                 orVal |= val;
  1384.                 switch (ret = val ? ReadFile() : SkipFile()) {
  1385.                         case ELSE:
  1386.                                 if (elseCounter++) Error(errorsTxt[1]);
  1387.                                 val = !val && !orVal;
  1388.                                 break;
  1389.                         case ELSEIF:
  1390.                                 val = !val && !orVal;
  1391.                                 if (val) {              // active ELSEIF, evaluate expression
  1392.                                         if (!dirIfIfn(val)) {
  1393.                                                 val = false;            // syntax error in expression
  1394.                                                 orVal = true;           // force remaining IF-blocks inactive
  1395.                                         }
  1396.                                 }
  1397.                                 break;
  1398.                         case ENDIF:
  1399.                                 break;
  1400.                         default:
  1401.                                 if (IsRunning) Error(errorsTxt[0]);
  1402.                                 donotlist=!IsRunning;           // do the listing only if still running
  1403.                                 return;
  1404.                 }
  1405.         }
  1406. }
  1407.  
  1408. static void dirIF() {
  1409.         aint val;
  1410.         if (dirIfIfn(val)) dirIfInternal("IF", val);
  1411. }
  1412.  
  1413. static void dirIFN() {
  1414.         aint val;
  1415.         if (dirIfIfn(val)) dirIfInternal("IFN", !val);
  1416. }
  1417.  
  1418. // IFUSED and IFNUSED internal helper, to parse label
  1419. static bool dirIfusedIfnused(char* & id) {
  1420.         id = NULL;
  1421.         if (SkipBlanks()) {                                             // no argument (use last parsed label)
  1422.                 if (LastParsedLabel) {
  1423.                         id = STRDUP(LastParsedLabel);
  1424.                 } else {
  1425.                         Error("[IFUSED/IFNUSED] no label defined ahead");
  1426.                         return false;
  1427.                 }
  1428.         } else {
  1429.                 std::unique_ptr<char[]> validLabel(ValidateLabel(lp, false, true));
  1430.                 if (validLabel) {
  1431.                         id = STRDUP(validLabel.get());
  1432.                         while (islabchar(*lp)) ++lp;    // advance lp beyond parsed label (valid chars only)
  1433.                 } else {
  1434.                         SkipToEol(lp);                                  // ValidateLabel aready reported some error, skip rest
  1435.                 }
  1436.         }
  1437.         return id && SkipBlanks();                              // valid "id" and no extra characters = OK
  1438. }
  1439.  
  1440. static void dirIFUSED() {
  1441.         char* id;
  1442.         if (dirIfusedIfnused(id)) dirIfInternal("IFUSED", LabelTable.IsUsed(id));
  1443.         if (id) free(id);
  1444. }
  1445.  
  1446. static void dirIFNUSED() {
  1447.         char* id;
  1448.         if (dirIfusedIfnused(id)) dirIfInternal("IFNUSED", !LabelTable.IsUsed(id));
  1449.         if (id) free(id);
  1450. }
  1451.  
  1452. static void dirIFDEF() {
  1453.         char* id;
  1454.         if ((id = GetID(lp)) && *id) {
  1455.                 dirIfInternal("IFDEF", DefineTable.FindDuplicate(id));
  1456.         } else {
  1457.                 Error("[IFDEF] Illegal identifier", bp);
  1458.         }
  1459. }
  1460.  
  1461. static void dirIFNDEF() {
  1462.         char* id;
  1463.         if ((id = GetID(lp)) && *id) {
  1464.                 dirIfInternal("IFNDEF", !DefineTable.FindDuplicate(id));
  1465.         } else {
  1466.                 Error("[IFNDEF] Illegal identifier", bp);
  1467.         }
  1468. }
  1469.  
  1470. static void dirElseCheckLiveDup() {
  1471.         if (RepeatStack.empty()) return;
  1472.         if (!RepeatStack.top().IsInWork) return;
  1473.  
  1474.         // Seems some ELSE/ELSEIF/ENDIF was encountered inside DUP->EDUP without starting IF
  1475.         // -> probably IF was outside of DUP->EDUP block, which is not legal in sjasmplus
  1476.         // terminate the current DUP->EDUP macro early and report the open ELSE/ELSEIF/ENDIF
  1477.         Error("Conditional block must start and finish inside the repeat block, nested completely");
  1478.         lijstp = nullptr;
  1479.         RepeatStack.top().RepeatCount = 0;
  1480. }
  1481.  
  1482. static void dirELSE() {
  1483.         dirElseCheckLiveDup();
  1484.         Error("ELSE without IF/IFN/IFUSED/IFNUSED/IFDEF/IFNDEF");
  1485. }
  1486.  
  1487. static void dirELSEIF() {
  1488.         dirElseCheckLiveDup();
  1489.         Error("ELSEIF without IF/IFN");
  1490. }
  1491.  
  1492. static void dirENDIF() {
  1493.         dirElseCheckLiveDup();
  1494.         Error("ENDIF without IF/IFN/IFUSED/IFNUSED/IFDEF/IFNDEF");
  1495. }
  1496.  
  1497. /*void dirENDTEXTAREA() {
  1498.   Error("ENDT without TEXTAREA",0);
  1499. }*/
  1500.  
  1501. static void dirINCLUDE() {
  1502.         std::unique_ptr<char[]> fnaam(GetFileName(lp));
  1503.         if (fnaam[0]) {
  1504.                 EDelimiterType dt = GetDelimiterOfLastFileName();
  1505.                 ListFile();
  1506.                 IncludeFile(fnaam.get(), DT_ANGLE == dt);
  1507.                 donotlist = 1;
  1508.         } else {
  1509.                 Error("[INCLUDE] empty filename", bp);
  1510.         }
  1511. }
  1512.  
  1513. static void dirOUTPUT() {
  1514.         if (LASTPASS != pass) {
  1515.                 SkipToEol(lp);
  1516.                 return;
  1517.         }
  1518.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  1519.         char modechar = 0;
  1520.         int mode = OUTPUT_TRUNCATE;
  1521.         if (comma(lp)) {
  1522.                 if (!SkipBlanks(lp)) modechar = (*lp++) | 0x20;
  1523.                 switch (modechar) {
  1524.                         case 't': mode = OUTPUT_TRUNCATE;       break;
  1525.                         case 'r': mode = OUTPUT_REWIND;         break;
  1526.                         case 'a': mode = OUTPUT_APPEND;         break;
  1527.                         default:
  1528.                                 Error("[OUTPUT] Invalid <mode> (valid modes: t, a, r)", bp);
  1529.                                 return;
  1530.                 }
  1531.         }
  1532.         //Options::NoDestinationFile = false;
  1533.         NewDest(fnaam.get(), mode);
  1534. }
  1535.  
  1536. static void dirOUTEND()
  1537. {
  1538.         if (pass == LASTPASS) CloseDest();
  1539. }
  1540.  
  1541. static void dirTAPOUT()
  1542. {
  1543.         aint val;
  1544.         std::unique_ptr<char[]> fnaam(GetOutputFileName(lp));
  1545.         int tape_flag = 255;
  1546.         if (comma(lp))
  1547.         {
  1548.                 if (!ParseExpression(lp, val))
  1549.                 {
  1550.                         Error("[TAPOUT] Missing flagbyte value", bp, PASS3); return;
  1551.                 }
  1552.                 tape_flag = val;
  1553.         }
  1554.         if (pass == LASTPASS) OpenTapFile(fnaam.get(), tape_flag);
  1555. }
  1556.  
  1557. static void dirTAPEND()
  1558. {
  1559.         // if (!FP_tapout) {Error("TAPEND without TAPOUT", bp, PASS3); return;}
  1560.         if (pass == LASTPASS) CloseTapFile();
  1561. }
  1562.  
  1563. static void dirDEFINE() {
  1564.         bool replaceEnabled = ('+' == *lp) ? ++lp, true : false;
  1565.         char* id = GetID(lp);
  1566.         if (nullptr == id) {
  1567.                 Error("[DEFINE] Illegal <id>", lp, SUPPRESS);
  1568.                 return;
  1569.         }
  1570.         if (White(*lp)) ++lp;           // skip one whitespace (not considered part of value) (others are)
  1571.         // but trim trailing spaces of value, if there's eol-comment
  1572.         if (eolComment) {
  1573.                 char *rtrim = lp + strlen(lp);
  1574.                 while (lp < rtrim && ' ' == rtrim[-1]) --rtrim;
  1575.                 *rtrim = 0;
  1576.         }
  1577.  
  1578.         if (replaceEnabled) {
  1579.                 DefineTable.Replace(id, lp);
  1580.         } else {
  1581.                 DefineTable.Add(id, lp, nullptr);
  1582.         }
  1583.         SkipToEol(lp);
  1584.         substitutedLine = line;         // override substituted listing for DEFINE
  1585. }
  1586.  
  1587. static void dirUNDEFINE() {
  1588.         char* id;
  1589.  
  1590.         if (!(id = GetID(lp)) && *lp != '*') {
  1591.                 Error("[UNDEFINE] Illegal <id>", lp, SUPPRESS);
  1592.                 return;
  1593.         }
  1594.  
  1595.         if (*lp == '*') {
  1596.                 ++lp;
  1597.                 DefineTable.RemoveAll();
  1598.         } else if (DefineTable.FindDuplicate(id)) {
  1599.                 DefineTable.Remove(id);
  1600.         } else {
  1601.                 Warning("[UNDEFINE] Identifier not found", id);
  1602.         }
  1603. }
  1604.  
  1605. static void dirEXPORT() {
  1606.         aint val;
  1607.         char* n, * p;
  1608.  
  1609.         if (!Options::ExportFName[0]) {
  1610.                 assert(!sourcePosStack.empty());
  1611.                 STRCPY(Options::ExportFName, LINEMAX, sourcePosStack.back().filename);
  1612.                 if (!(p = strchr(Options::ExportFName, '.'))) {
  1613.                         p = Options::ExportFName;
  1614.                 } else {
  1615.                         *p = 0;
  1616.                 }
  1617.                 STRCAT(p, LINEMAX, ".exp");
  1618.                 Warning("[EXPORT] Filename for exportfile was not indicated. Output will be in", Options::ExportFName, W_EARLY);
  1619.         }
  1620.         if (!(n = p = GetID(lp))) {
  1621.                 Error("[EXPORT] Syntax error", lp, SUPPRESS);
  1622.                 return;
  1623.         }
  1624.         if (pass != LASTPASS) return;
  1625.         IsLabelNotFound = false;
  1626.         GetLabelValue(n, val);
  1627.         if (!IsLabelNotFound) WriteExp(p, val);
  1628. }
  1629.  
  1630. static void dirDISPLAY() {
  1631.         char decprint = 'H';
  1632.         char e[LINEMAX + 32], optionChar;               // put extra buffer at end for particular H/A/D number printout
  1633.         char* ep = e, * const endOfE = e + LINEMAX;
  1634.         aint val;
  1635.         do {
  1636.                 if (SkipBlanks()) {
  1637.                         Error("[DISPLAY] Expression expected");
  1638.                         break;
  1639.                 }
  1640.                 if (*lp == '/') {
  1641.                         switch (optionChar = toupper((byte)lp[1])) {
  1642.                         case 'A': case 'D': case 'H': case 'B': case 'C':
  1643.                                 // known options, switching hex+dec / dec / hex / binary mode / char mode
  1644.                                 decprint = optionChar;
  1645.                                 break;
  1646.                         case 'L': case 'T':                             // silently ignored options (legacy compatibility)
  1647.                                 // in ALASM: 'L' is "concatenate to previous line" (as if there was no \r\n on it)
  1648.                                 // in ALASM: 'T' used ahead of expression will display first the expression itself, then value
  1649.                                 break ;
  1650.                         default:
  1651.                                 Error("[DISPLAY] Syntax error, unknown option", lp, SUPPRESS);
  1652.                                 return;
  1653.                         }
  1654.                         lp += 2;
  1655.                         continue;
  1656.                 }
  1657.                 // try to parse some string literal
  1658.                 const int remainingBufferSize = endOfE - ep;
  1659.                 if (remainingBufferSize <= 0) {
  1660.                         Error("[DISPLAY] internal buffer overflow, resulting text is too long", line);
  1661.                         return;
  1662.                 }
  1663.                 int ei = 0;
  1664.                 val = GetCharConstAsString(lp, ep, ei, remainingBufferSize);
  1665.                 if (-1 == val) {
  1666.                         Error("[DISPLAY] Syntax error", line);
  1667.                         return;
  1668.                 } else if (val) {
  1669.                         ep += ei;                               // string literal successfuly parsed
  1670.                 } else {
  1671.                         // string literal was not there, how about expression?
  1672.                         if (ParseExpressionNoSyntaxError(lp, val)) {
  1673.                                 if (decprint == 'B') {  // 8-bit binary (doesn't care about higher bits)
  1674.                                         *(ep++) = '%';
  1675.                                         aint bitMask = 0x80;
  1676.                                         while (bitMask) {
  1677.                                                 *(ep++) = (val & bitMask) ? '1' : '0';
  1678.                                                 if (0x10 == bitMask) *(ep++) = '\'';
  1679.                                                 bitMask >>= 1;
  1680.                                         }
  1681.                                 }
  1682.                                 if (decprint == 'C') {
  1683.                                         val &= 0xFF;    // truncate to 8bit value
  1684.                                         if (' ' <= val && val < 127) {  // printable ASCII
  1685.                                                 *ep++ = '\'';
  1686.                                                 *ep++ = val;
  1687.                                                 *ep++ = '\'';
  1688.                                         } else {                // non-printable char, do the \x?? form
  1689.                                                 *ep++ = '\'';
  1690.                                                 *ep++ = '\\';
  1691.                                                 *ep++ = 'x';
  1692.                                                 PrintHex(ep, val, 2);
  1693.                                                 *ep++ = '\'';
  1694.                                         }
  1695.                                 }
  1696.                                 if (decprint == 'H' || decprint == 'A') {
  1697.                                         *(ep++) = '0';
  1698.                                         *(ep++) = 'x';
  1699.                                         PrintHexAlt(ep, val);
  1700.                                 }
  1701.                                 if (decprint == 'D' || decprint == 'A') {
  1702.                                         if (decprint == 'A') {
  1703.                                                 *(ep++) = ','; *(ep++) = ' ';
  1704.                                         }
  1705.                                         int charsToPrint = SPRINTF1(ep, remainingBufferSize, "%u", val);
  1706.                                         if (remainingBufferSize <= charsToPrint) {
  1707.                                                 Error("[DISPLAY] internal buffer overflow, resulting text is too long", line);
  1708.                                                 return;
  1709.                                         }
  1710.                                         ep += charsToPrint;
  1711.                                 }
  1712.                                 decprint = 'H';
  1713.                         } else {
  1714.                                 Error("[DISPLAY] Syntax error", line, SUPPRESS);
  1715.                                 return;
  1716.                         }
  1717.                 }
  1718.         } while(comma(lp));
  1719.         *ep = 0; // end line
  1720.  
  1721.         if (LASTPASS == pass && *e) {
  1722.                 _CERR "> " _CMDL Options::tcols->display _CMDL e _CMDL Options::tcols->end _ENDL;
  1723.         }
  1724. }
  1725.  
  1726. static void dirMACRO() {
  1727.         if (lijst) {
  1728.                 Error("[MACRO] No macro definitions allowed here", NULL, SUPPRESS);
  1729.                 return;
  1730.         }
  1731.         // check if the name of macro is defined at beginning of the line ("label" name)
  1732.         const bool labelName = LastParsedLabelLine == CompiledCurrentLine;
  1733.         assert(!labelName || LastParsedLabel);
  1734.         char* lpLabel = labelName ? LastParsedLabel : lp;       // temporary pointer to advance by GetID
  1735.         char* n = GetID(lpLabel);                                                       // get+validate macro name
  1736.         if (*lpLabel && !White(*lpLabel)) n = nullptr;          // if there's unexpected trailing char, report illegal name
  1737.         if (n) {
  1738.                 if (!labelName) lp = lpLabel;                                   // name was after MACRO keyword, advance global `lp` (to parse arguments)
  1739.                 MacroTable.Add(n, lp);
  1740.         } else {
  1741.                 Error("[MACRO] Illegal macroname", labelName ? LastParsedLabel : lp);   // report what was fed into GetID
  1742.                 SkipToEol(lp);
  1743.         }
  1744. }
  1745.  
  1746. static void dirENDS() {
  1747.         Error("[ENDS] End structure without structure");
  1748. }
  1749.  
  1750. static void dirASSERT() {
  1751.         char* p = lp;
  1752.         aint val;
  1753.         if (!ParseExpressionNoSyntaxError(lp, val)) {
  1754.                 Error("[ASSERT] Syntax error", p, SUPPRESS);
  1755.                 return;
  1756.         }
  1757.         if (pass == LASTPASS && !val) {
  1758.                 Error("[ASSERT] Assertion failed", p);
  1759.         }
  1760.         if (comma(lp)) SkipToEol(lp);
  1761. }
  1762.  
  1763. static void dirSHELLEXEC() {
  1764.         //TODO for v2.x change the "SHELLEXEC <command>[, <params>]" syntax to "SHELLEXEC <whatever>"
  1765.         // (and add good examples how to deal with quotes/colons/long file names with spaces)
  1766.         std::unique_ptr<char[]> command(GetFileName(lp, false));
  1767.         std::unique_ptr<char[]> parameters;
  1768.         if (comma(lp)) {
  1769.                 parameters.reset(GetFileName(lp, false));
  1770.         }
  1771.         if (pass == LASTPASS) {
  1772.                 if (!system(nullptr)) {
  1773.                         Error("[SHELLEXEC] clib command processor is not available on this platform!");
  1774.                 } else {
  1775.                         temp[0] = 0;
  1776.                         STRNCPY(temp, LINEMAX, command.get(), LINEMAX-1);
  1777.                         if (parameters) {
  1778.                                 STRNCAT(temp, LINEMAX, " ", 2);
  1779.                                 STRNCAT(temp, LINEMAX, parameters.get(), LINEMAX-1);
  1780.                         }
  1781.                         if (Options::OutputVerbosity <= OV_ALL) {
  1782.                                 _CERR "Executing <" _CMDL temp _CMDL ">" _ENDL;
  1783.                         }
  1784.                         // flush both stdout and stderr before trying to execute anything externally
  1785.                         _COUT flush;
  1786.                         _CERR flush;
  1787.                         // execute the requested command
  1788.                         int exitCode = system(temp);
  1789.                         if (exitCode) {
  1790.                                 ErrorInt("[SHELLEXEC] non-zero exit code", WEXITSTATUS(exitCode));
  1791.                         }
  1792.                 }
  1793.         }
  1794. }
  1795.  
  1796. static void dirSTRUCT() {
  1797.         CStructure* st;
  1798.         int global = 0;
  1799.         aint offset = 0;
  1800.         char* naam;
  1801.         SkipBlanks();
  1802.         if (*lp == '@') {
  1803.                 ++lp; global = 1;
  1804.         }
  1805.  
  1806.         if (!(naam = GetID(lp)) || !strlen(naam)) {
  1807.                 Error("[STRUCT] Illegal structure name", lp, SUPPRESS);
  1808.                 return;
  1809.         }
  1810.         if (comma(lp)) {
  1811.                 IsLabelNotFound = false;
  1812.                 if (!ParseExpressionNoSyntaxError(lp, offset)) {
  1813.                         Error("[STRUCT] Offset syntax error", lp, SUPPRESS);
  1814.                         return;
  1815.                 }
  1816.                 if (IsLabelNotFound) {
  1817.                         Error("[STRUCT] Forward reference", NULL, EARLY);
  1818.                 }
  1819.         }
  1820.         if (!SkipBlanks()) {
  1821.                 Error("[STRUCT] syntax error, unexpected", lp);
  1822.         }
  1823.         st = StructureTable.Add(naam, offset, global);
  1824.         ListFile();
  1825.         while (ReadLine()) {
  1826.                 lp = line; /*if (White()) { SkipBlanks(lp); if (*lp=='.') ++lp; if (cmphstr(lp,"ends")) break; }*/
  1827.                 SkipBlanks(lp);
  1828.                 if (*lp == '.') {
  1829.                         ++lp;
  1830.                 }
  1831.                 if (cmphstr(lp, "ends")) {
  1832.                         ++CompiledCurrentLine;
  1833.                         if (st) st->deflab();
  1834.                         lp = ReplaceDefine(lp);         // skip any empty substitutions and comments
  1835.                         substitutedLine = line;         // override substituted listing for ENDS
  1836.                         return;
  1837.                 }
  1838.                 if (st) ParseStructLine(st);
  1839.                 ListFile(true);
  1840.         }
  1841.         Error("[STRUCT] Unexpected end of structure");
  1842.         st->deflab();
  1843. }
  1844.  
  1845. static void dirFPOS() {
  1846.         aint val;
  1847.         int method = SEEK_SET;
  1848.         SkipBlanks(lp);
  1849.         if ((*lp == '+') || (*lp == '-')) {
  1850.                 method = SEEK_CUR;
  1851.         }
  1852.         if (!ParseExpressionNoSyntaxError(lp, val)) {
  1853.                 Error("[FPOS] Syntax error", lp, SUPPRESS);
  1854.         } else if (pass == LASTPASS) {
  1855.                 SeekDest(val, method);
  1856.         }
  1857. }
  1858.  
  1859. // isWhile == false: DUP/REPT parsing
  1860. // isWhile == true: WHILE parsing
  1861. static void DupWhileImplementation(bool isWhile) {
  1862.         aint val = 0;
  1863.         CStringsList* condition = nullptr;
  1864.  
  1865.         if (!RepeatStack.empty()) {
  1866.                 SRepeatStack& dup = RepeatStack.top();
  1867.                 if (!dup.IsInWork) {
  1868.                         SkipToEol(lp);          // Just skip the expression to the end of line, don't evaluate yet
  1869.                         ++dup.Level;
  1870.                         return;
  1871.                 }
  1872.         }
  1873.  
  1874.         const char* indexVar = nullptr;
  1875.         if (isWhile) {
  1876.                 condition = new CStringsList(lp);
  1877.                 if (nullptr == condition) ErrorOOM();
  1878.                 lp += strlen(condition->string);
  1879.                 // scan condition string for extra guardian value, and split + parse it as needed
  1880.                 char* expressionSource = condition->string;
  1881.                 bool parseOk = ParseExpressionNoSyntaxError(expressionSource, val);
  1882.                 if (parseOk && *expressionSource && comma(expressionSource)) {
  1883.                         // comma found, try to parse explicit guardian value
  1884.                         char* guardianSource = expressionSource;
  1885.                         parseOk = parseOk && ParseExpressionNoSyntaxError(guardianSource, val);
  1886.                         // overwrite the comma to keep only condition string without guardian argument
  1887.                         if (parseOk) {
  1888.                                 assert(',' == expressionSource[-1]);
  1889.                                 expressionSource[-1] = 0;
  1890.                                 ++val;          // +1 to explicit value to report error when WHILE does *over* that
  1891.                         }
  1892.                 } else {
  1893.                         val = 100001;   // default guardian value is 100k
  1894.                 }
  1895.                 if (!parseOk) {
  1896.                         Error("[WHILE] Syntax error in <expression>", condition->string, SUPPRESS);
  1897.                         free(condition->string);                        // release original string
  1898.                         condition->string = STRDUP("0");        // force it to evaluate to zero
  1899.                         val = 1;
  1900.                 }
  1901.         } else {
  1902.                 IsLabelNotFound = false;
  1903.                 if (!ParseExpressionNoSyntaxError(lp, val)) {
  1904.                         Error("[DUP/REPT] Syntax error in <count>", lp, SUPPRESS);
  1905.                         return;
  1906.                 }
  1907.                 if (IsLabelNotFound) {
  1908.                         Error("[DUP/REPT] Forward reference", NULL, ALL);
  1909.                 }
  1910.                 if ((int) val < 0) {
  1911.                         ErrorInt("[DUP/REPT] Repeat value must be positive or zero", val, IF_FIRST); return;
  1912.                 }
  1913.                 if (comma(lp)) {
  1914.                         indexVar = GetID(lp);
  1915.                         if (nullptr == indexVar) {
  1916.                                 Error("[DUP/REPT] invalid index variable name", lp, IF_FIRST);
  1917.                                 SkipToEol(lp);
  1918.                         }
  1919.                 }
  1920.         }
  1921.  
  1922.         RepeatStack.emplace(val, condition, new CStringsList(indexVar ? indexVar : ""));
  1923.         if (!SkipBlanks()) Error("[DUP] unexpected chars", lp, SUPPRESS);
  1924. }
  1925.  
  1926. static void dirDUP() {
  1927.         DupWhileImplementation(false);
  1928. }
  1929.  
  1930. static void dirWHILE() {
  1931.         DupWhileImplementation(true);
  1932. }
  1933.  
  1934. static bool shouldRepeat(SRepeatStack& dup) {
  1935.         if (nullptr == dup.RepeatCondition) {
  1936.                 return 0 <= --dup.RepeatCount;
  1937.         } else {
  1938.                 if (!dup.RepeatCount--) {
  1939.                         sourcePosStack.push_back(dup.RepeatCondition->source);
  1940.                         Error("[WHILE] infinite loop? (reaching the guardian value, default 100k)");
  1941.                         sourcePosStack.pop_back();
  1942.                         return false;
  1943.                 }
  1944.                 aint val = 0;
  1945.                 IsLabelNotFound = false;
  1946.                 char* expressionSource = dup.RepeatCondition->string;
  1947.                 if (!ParseExpressionNoSyntaxError(expressionSource, val) || *expressionSource) {
  1948.                         const TextFilePos oSourcePos = sourcePosStack.back();
  1949.                         sourcePosStack.back() = dup.RepeatCondition->source;
  1950.                         Error("[WHILE] Syntax error in <expression>", dup.RepeatCondition->string, SUPPRESS);
  1951.                         sourcePosStack.back() = oSourcePos;
  1952.                         return false;
  1953.                 }
  1954.                 if (IsLabelNotFound) {
  1955.                         WarningById(W_FWD_REF, dup.RepeatCondition->string, W_EARLY);
  1956.                         return false;
  1957.                 }
  1958.                 return val;
  1959.         }
  1960. }
  1961.  
  1962. static void dirEDUP() {
  1963.         if (RepeatStack.empty() || RepeatStack.top().IsInWork) {
  1964.                 Error("[EDUP/ENDR/ENDW] End repeat without repeat");
  1965.                 return;
  1966.         }
  1967.  
  1968.         SRepeatStack& dup = RepeatStack.top();
  1969.         if (!dup.IsInWork && dup.Level) {
  1970.                 --dup.Level;
  1971.                 return;
  1972.         }
  1973.         dup.IsInWork = true;
  1974.         // kill the "EDUP" inside DUP-list (+ works as "while (IsRunning && lijstp && lijstp->string)" terminator)
  1975.         if (dup.Pointer->string) free(dup.Pointer->string);
  1976.         dup.Pointer->string = NULL;
  1977.         ++listmacro;
  1978.         char* ml = STRDUP(line);        // copy the EDUP line for List purposes (after the DUP block emit)
  1979.         if (ml == NULL) ErrorOOM();
  1980.  
  1981.         CStringsList* olijstp = lijstp;
  1982.         ++lijst;
  1983.         assert(!sourcePosStack.empty());
  1984.         const TextFilePos oSourcePos = sourcePosStack.back();
  1985.         aint currentRepeatIndex = 0;
  1986.         while (IsRunning && dup.Lines && shouldRepeat(dup)) {
  1987.                 sourcePosStack.back() = dup.sourcePos;
  1988.                 lijstp = dup.Lines;
  1989.                 assert(lijstp);
  1990.                 if (*lijstp->string) {
  1991.                         // if the DUP has index variable, the first "line" is the variable name, set it up to current index
  1992.                         std::unique_ptr<char[]> indexVar(ValidateLabel(lijstp->string,  false));
  1993.                         if (indexVar.get()) LabelTable.Insert(indexVar.get(), currentRepeatIndex++, LABEL_IS_DEFL);
  1994.                 }
  1995.                 lijstp = lijstp->next;  // skip first empty line / indexVar name
  1996.                 while (IsRunning && lijstp && lijstp->string) { // the EDUP/REPT/ENDM line has string=NULL => ends loop
  1997.                         if (lijstp->source.line) sourcePosStack.back() = lijstp->source;
  1998.                         STRCPY(line, LINEMAX, lijstp->string);
  1999.                         substitutedLine = line;         // reset substituted listing
  2000.                         eolComment = NULL;                      // reset end of line comment
  2001.                         lijstp = lijstp->next;
  2002.                         ParseLineSafe();
  2003.                         sourcePosStack.back().nextSegment();
  2004.                 }
  2005.         }
  2006.         sourcePosStack.back() = oSourcePos;
  2007.         RepeatStack.pop();
  2008.         lijstp = olijstp;
  2009.         --lijst;
  2010.         --listmacro;
  2011.         STRCPY(line, LINEMAX,  ml);             // show EDUP line itself
  2012.         free(ml);
  2013.         ++CompiledCurrentLine;
  2014.         substitutedLine = line;                 // override substituted list line for EDUP
  2015.         ListFile();
  2016. }
  2017.  
  2018. static void dirENDM() {
  2019.         if (!RepeatStack.empty()) {
  2020.                 Warning("ENDM used as DUP/REPT block terminator, this is deprecated (and bugged when used inside macro), change to EDUP or ENDR");
  2021.                 dirEDUP();
  2022.         } else {
  2023.                 Error("[ENDM] End macro without macro");
  2024.         }
  2025. }
  2026.  
  2027. static bool dirDEFARRAY_parseItems(CStringsList** nextPtr) {
  2028.         char ml[LINEMAX];
  2029.         do {
  2030.                 const char* const itemLp = lp;
  2031.                 char* n = ml;
  2032.                 if (!GetMacroArgumentValue(lp, n)) {
  2033.                         Error("[DEFARRAY] Syntax error", itemLp, SUPPRESS);
  2034.                         return false;
  2035.                 }
  2036.                 *nextPtr = new CStringsList(ml);
  2037.                 if ((*nextPtr)->string == NULL) ErrorOOM();
  2038.                 nextPtr = &((*nextPtr)->next);
  2039.         } while (anyComma(lp));
  2040.         return SkipBlanks();
  2041. }
  2042.  
  2043. static void dirDEFARRAY_add(const char* id) {
  2044.         DefineTable.Get(id);
  2045.         if (NULL == DefineTable.DefArrayList) {
  2046.                 Error("[DEFARRAY+] unknown array <id>", id);
  2047.                 SkipToEol(lp);
  2048.                 return;
  2049.         }
  2050.         // array was already defined, seek to the last item in the list
  2051.         while (DefineTable.DefArrayList->next) DefineTable.DefArrayList = DefineTable.DefArrayList->next;
  2052.         dirDEFARRAY_parseItems(&DefineTable.DefArrayList->next);
  2053.         return;
  2054. }
  2055.  
  2056. static void dirDEFARRAY() {
  2057.         bool plus = ('+' == *lp) ? ++lp, true : false;
  2058.         const char* id = White() ? GetID(lp) : nullptr;
  2059.         if (!id) {
  2060.                 Error("[DEFARRAY] Syntax error in <id>", lp);
  2061.                 SkipToEol(lp);
  2062.                 return;
  2063.         }
  2064.         if (!White() || SkipBlanks()) { // enforce whitespace between ID and first item and detect empty ones
  2065.                 if (SkipBlanks()) Error("[DEFARRAY] must have at least one entry");
  2066.                 else Error("[DEFARRAY] missing space between <id> and first <item>", lp);
  2067.                 SkipToEol(lp);
  2068.                 return;
  2069.         }
  2070.         if (plus) {
  2071.                 dirDEFARRAY_add(id);
  2072.         } else {
  2073.                 CStringsList* a = NULL;
  2074.                 if (!dirDEFARRAY_parseItems(&a) || NULL == a) {
  2075.                         if (a) delete a;        // release already parsed items, if there was syntax error
  2076.                         return;
  2077.                 }
  2078.                 DefineTable.Add(id, "", a);
  2079.         }
  2080. }
  2081.  
  2082. static const char* DEFDEVICE_SYNTAX_ERR = "[DEFDEVICE] expected syntax is <deviceid>, <slot_size>, <page_count>[, <slot_0_initial_page>[, ...]]";
  2083.  
  2084. static void dirDEFDEVICE() {
  2085.         //DEFDEVICE <deviceid>, <slot_size>, <page_count>[, <slot_0_initial_page>[, ...]]
  2086.         const char* id = GetID(lp);
  2087.         if (!id) {
  2088.                 Error(DEFDEVICE_SYNTAX_ERR, bp, SUPPRESS);
  2089.                 return;
  2090.         }
  2091.  
  2092.         const bool is_defined = std::any_of(
  2093.                 DefDevices.begin(), DefDevices.end(),
  2094.                 [&](const CDeviceDef* el) { return 0 == strcasecmp(id, el->getID()); }
  2095.         );
  2096.         if (is_defined || 1 < pass) {
  2097.                 // same id defined twice during first pass?
  2098.                 if (pass <= 1) Error("[DEFDEVICE] device with such ID is already defined", id, EARLY);
  2099.                 // in later passes ignore the line, DEFDEVICE works only in first pass
  2100.                 SkipToEol(lp);
  2101.                 return;
  2102.         }
  2103.  
  2104.         // add new definition if arguments are correct and this is first pass
  2105.         aint args[2 + CDeviceDef::MAX_SLOT_N] = {};     // slot_size, page_count, initial pages, ...
  2106.         bool optional[2 + CDeviceDef::MAX_SLOT_N] = {false, false};
  2107.         aint &slot_size = args[0], &page_count = args[1], *initial_pages = args + 2;
  2108.         for (size_t i = 2; i < CDeviceDef::MAX_SLOT_N; ++i) {
  2109.                 args[i] = -1;
  2110.                 optional[i] = true;
  2111.         }
  2112.         if (!anyComma(lp) || !getIntArguments<2 + CDeviceDef::MAX_SLOT_N>(lp, args, optional)) {
  2113.                 Error(DEFDEVICE_SYNTAX_ERR, bp, EARLY);
  2114.                 return;
  2115.         }
  2116.         if (slot_size < 256 || 0x10000 < slot_size || page_count <= 0) {
  2117.                 Error("[DEFDEVICE] valid slot_size: 256..64ki, page_count: 1 or more", bp, EARLY);
  2118.                 return;
  2119.         }
  2120.         DefDevices.push_back(new CDeviceDef(id, slot_size, page_count));
  2121.  
  2122.         // init "initialPages array by going 0, 1, 2, ..., page_count-1, page_count-1, ... or parsed explicit values
  2123.         CDeviceDef & dev = *DefDevices.back();
  2124.         int previous_page = -1;
  2125.         for (int32_t i = 0; i < dev.SlotsCount; ++i) {
  2126.                 if (0 <= initial_pages[i] && initial_pages[i] < dev.PagesCount) {
  2127.                         previous_page = initial_pages[i];
  2128.                 } else {
  2129.                         if (-1 != initial_pages[i]) ErrorInt("[DEFDEVICE] invalid initial page", initial_pages[i], EARLY);
  2130.                         if (previous_page < dev.PagesCount - 1) ++previous_page;
  2131.                 }
  2132.                 dev.initialPages[i] = previous_page;
  2133.         }
  2134. }
  2135.  
  2136. static void dirDEVICE() {
  2137.         // refresh source position of first DEVICE directive
  2138.         if (1 == ++deviceDirectivesCount) {
  2139.                 assert(!sourcePosStack.empty());
  2140.                 globalDeviceSourcePos = sourcePosStack.back();
  2141.         }
  2142.  
  2143.         char* id = GetID(lp);
  2144.         if (id) {
  2145.                 aint ramtop = 0;
  2146.                 if (anyComma(lp)) {
  2147.                         if (!ParseExpressionNoSyntaxError(lp, ramtop)) {
  2148.                                 Error("[DEVICE] Syntax error", bp); return;
  2149.                         }
  2150.                         if (ramtop < 0x5D00 || 0xFFFF < ramtop) {
  2151.                                 ErrorInt("[DEVICE] valid range for RAMTOP is $5D00..$FFFF", ramtop); return;
  2152.                         }
  2153.                 }
  2154.                 // if (1 == deviceDirectivesCount && Device) -> device was already set globally, skip SetDevice
  2155.                 if (1 < deviceDirectivesCount || !Devices) {
  2156.                         if (!SetDevice(id, ramtop)) {
  2157.                                 Error("[DEVICE] Invalid parameter", id, IF_FIRST);
  2158.                         }
  2159.                 }
  2160.         } else {
  2161.                 Error("[DEVICE] Syntax error in <deviceid>", lp, SUPPRESS);
  2162.         }
  2163. }
  2164.  
  2165. static void dirSLDOPT() {
  2166.         SkipBlanks(lp);
  2167.         if (cmphstr(lp, "COMMENT")) {
  2168.                 do {
  2169.                         SldAddCommentKeyword(GetID(lp));
  2170.                 } while (!SkipBlanks(lp) && anyComma(lp));
  2171.         } else {
  2172.                 Error("[SLDOPT] Syntax error in <type> (valid is only COMMENT)", lp, SUPPRESS);
  2173.         }
  2174. }
  2175.  
  2176. void InsertDirectives() {
  2177.         DirectivesTable.insertd(".assert", dirASSERT);
  2178.         DirectivesTable.insertd(".byte", dirBYTE);
  2179.         DirectivesTable.insertd(".abyte", dirABYTE);
  2180.         DirectivesTable.insertd(".abytec", dirABYTEC);
  2181.         DirectivesTable.insertd(".abytez", dirABYTEZ);
  2182.         DirectivesTable.insertd(".word", dirWORD);
  2183.         DirectivesTable.insertd(".block", dirBLOCK);
  2184.         DirectivesTable.insertd(".dword", dirDWORD);
  2185.         DirectivesTable.insertd(".d24", dirD24);
  2186.         DirectivesTable.insertd(".dg", dirDG);
  2187.         DirectivesTable.insertd(".defg", dirDG);
  2188.         DirectivesTable.insertd(".dh", dirDH);
  2189.         DirectivesTable.insertd(".defh", dirDH);
  2190.         DirectivesTable.insertd(".hex", dirDH);
  2191.         DirectivesTable.insertd(".org", dirORG);
  2192.         DirectivesTable.insertd(".fpos",dirFPOS);
  2193.         DirectivesTable.insertd(".align", dirALIGN);
  2194.         DirectivesTable.insertd(".module", dirMODULE);
  2195.         DirectivesTable.insertd(".size", dirSIZE);
  2196.         //DirectivesTable.insertd(".textarea",dirTEXTAREA);
  2197.         DirectivesTable.insertd(".textarea", dirDISP);
  2198.         DirectivesTable.insertd(".else", dirELSE);
  2199.         DirectivesTable.insertd(".elseif", dirELSEIF);
  2200.         DirectivesTable.insertd(".export", dirEXPORT);
  2201.         DirectivesTable.insertd(".display", dirDISPLAY);
  2202.         DirectivesTable.insertd(".end", dirEND);
  2203.         DirectivesTable.insertd(".include", dirINCLUDE);
  2204.         DirectivesTable.insertd(".incbin", dirINCBIN);
  2205.         DirectivesTable.insertd(".binary", dirINCBIN);
  2206.         DirectivesTable.insertd(".inchob", dirINCHOB);
  2207.         DirectivesTable.insertd(".inctrd", dirINCTRD);
  2208.         DirectivesTable.insertd(".insert", dirINCBIN);
  2209.         DirectivesTable.insertd(".savenex", dirSAVENEX);
  2210.         DirectivesTable.insertd(".savesna", dirSAVESNA);
  2211.         DirectivesTable.insertd(".savehob", dirSAVEHOB);
  2212.         DirectivesTable.insertd(".savebin", dirSAVEBIN);
  2213.         DirectivesTable.insertd(".savedev", dirSAVEDEV);
  2214.         DirectivesTable.insertd(".emptytap", dirEMPTYTAP);
  2215.         DirectivesTable.insertd(".savetap", dirSAVETAP);
  2216.         DirectivesTable.insertd(".emptytrd", dirEMPTYTRD);
  2217.         DirectivesTable.insertd(".savetrd", dirSAVETRD);
  2218.         DirectivesTable.insertd(".savecpcsna", dirSAVECPCSNA);
  2219.         DirectivesTable.insertd(".savecdt", dirSAVECDT);
  2220.         DirectivesTable.insertd(".save3dos", dirSAVE3DOS);
  2221.         DirectivesTable.insertd(".saveamsdos", dirSAVEAMSDOS);
  2222.         DirectivesTable.insertd(".shellexec", dirSHELLEXEC);
  2223. /*#ifdef WIN32
  2224.         DirectivesTable.insertd(".winexec", dirWINEXEC);
  2225. #endif*/
  2226.         DirectivesTable.insertd(".if", dirIF);
  2227.         DirectivesTable.insertd(".ifn", dirIFN);
  2228.         DirectivesTable.insertd(".ifused", dirIFUSED);
  2229.         DirectivesTable.insertd(".ifnused", dirIFNUSED);
  2230.         DirectivesTable.insertd(".ifdef", dirIFDEF);
  2231.         DirectivesTable.insertd(".ifndef", dirIFNDEF);
  2232.         DirectivesTable.insertd(".output", dirOUTPUT);
  2233.         DirectivesTable.insertd(".outend", dirOUTEND);
  2234.         DirectivesTable.insertd(".tapout", dirTAPOUT);
  2235.         DirectivesTable.insertd(".tapend", dirTAPEND);
  2236.         DirectivesTable.insertd(".define", dirDEFINE);
  2237.         DirectivesTable.insertd(".undefine", dirUNDEFINE);
  2238.         DirectivesTable.insertd(".defarray", dirDEFARRAY);
  2239.         DirectivesTable.insertd(".macro", dirMACRO);
  2240.         DirectivesTable.insertd(".struct", dirSTRUCT);
  2241.         DirectivesTable.insertd(".dc", dirDC);
  2242.         DirectivesTable.insertd(".dz", dirDZ);
  2243.         DirectivesTable.insertd(".db", dirBYTE);
  2244.         DirectivesTable.insertd(".dm", dirBYTE);
  2245.         DirectivesTable.insertd(".dw", dirWORD);
  2246.         DirectivesTable.insertd(".ds", dirBLOCK);
  2247.         DirectivesTable.insertd(".dd", dirDWORD);
  2248.         DirectivesTable.insertd(".defb", dirBYTE);
  2249.         DirectivesTable.insertd(".defw", dirWORD);
  2250.         DirectivesTable.insertd(".defs", dirBLOCK);
  2251.         DirectivesTable.insertd(".defd", dirDWORD);
  2252.         DirectivesTable.insertd(".defm", dirBYTE);
  2253.         DirectivesTable.insertd(".endmod", dirENDMODULE);
  2254.         DirectivesTable.insertd(".endmodule", dirENDMODULE);
  2255.         DirectivesTable.insertd(".rept", dirDUP);
  2256.         DirectivesTable.insertd(".dup", dirDUP);
  2257.         DirectivesTable.insertd(".while", dirWHILE);
  2258.         DirectivesTable.insertd(".disp", dirDISP);
  2259.         DirectivesTable.insertd(".phase", dirDISP);
  2260.         DirectivesTable.insertd(".ent", dirENT);
  2261.         DirectivesTable.insertd(".unphase", dirENT);
  2262.         DirectivesTable.insertd(".dephase", dirENT);
  2263.         DirectivesTable.insertd(".page", dirPAGE);
  2264.         DirectivesTable.insertd(".slot", dirSLOT);
  2265.         DirectivesTable.insertd(".mmu", dirMMU);
  2266.         DirectivesTable.insertd(".encoding", dirENCODING);
  2267.         DirectivesTable.insertd(".opt", dirOPT);
  2268.         DirectivesTable.insertd(".labelslist", dirLABELSLIST);
  2269.         DirectivesTable.insertd(".cspectmap", dirCSPECTMAP);
  2270.         DirectivesTable.insertd(".endif", dirENDIF);
  2271.         DirectivesTable.insertd(".endt", dirENT);
  2272.         DirectivesTable.insertd(".endm", dirENDM);
  2273.         DirectivesTable.insertd(".edup", dirEDUP);
  2274.         DirectivesTable.insertd(".endr", dirEDUP);
  2275.         DirectivesTable.insertd(".endw", dirEDUP);
  2276.         DirectivesTable.insertd(".ends", dirENDS);
  2277.  
  2278.         DirectivesTable.insertd(".device", dirDEVICE);
  2279.         DirectivesTable.insertd(".defdevice", dirDEFDEVICE);
  2280.  
  2281.         DirectivesTable.insertd(".bplist", dirBPLIST);
  2282.         DirectivesTable.insertd(".setbreakpoint", dirSETBREAKPOINT);
  2283.         DirectivesTable.insertd(".setbp", dirSETBREAKPOINT);
  2284.  
  2285.         DirectivesTable.insertd(".relocate_start", Relocation::dirRELOCATE_START);
  2286.         DirectivesTable.insertd(".relocate_end", Relocation::dirRELOCATE_END);
  2287.         DirectivesTable.insertd(".relocate_table", Relocation::dirRELOCATE_TABLE);
  2288.  
  2289.         DirectivesTable.insertd(".sldopt", dirSLDOPT);
  2290.  
  2291. #ifdef USE_LUA
  2292.         DirectivesTable.insertd(".lua", dirLUA);
  2293.         DirectivesTable.insertd(".endlua", dirENDLUA);
  2294.         DirectivesTable.insertd(".includelua", dirINCLUDELUA);
  2295. #endif //USE_LUA
  2296.  
  2297.         DirectivesTable_dup.insertd(".dup", dirDUP);
  2298.         DirectivesTable_dup.insertd(".edup", dirEDUP);
  2299.         DirectivesTable_dup.insertd(".endm", dirENDM);
  2300.         DirectivesTable_dup.insertd(".endr", dirEDUP);
  2301.         DirectivesTable_dup.insertd(".endw", dirEDUP);
  2302.         DirectivesTable_dup.insertd(".rept", dirDUP);
  2303.         DirectivesTable_dup.insertd(".while", dirWHILE);
  2304. }
  2305.  
  2306. //eof direct.cpp
  2307.