?login_element?

Subversion Repositories NedoOS

Rev

Rev 539 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download

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