• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

z00m128 / sjasmplus / 26135148044

20 May 2026 01:12AM UTC coverage: 97.798% (+0.5%) from 97.312%
26135148044

push

github

ped7g
CI: details tuning, switching coveralls.io to lcov data

Changes:
- Makefile has new target `coverage-lcov` producing web page with lcov
- using official coveralls github action to upload coverage
- processing coverage data for coveralls.io with lcov
- more diverse linux build runners (x64 + arm64, gcc container + ubuntu
runner)
- both macos-15 + macos-26 (arm64 only)
- test runner script shows sjasmplus version just before summary
- sjasmplus --version shows when unit tests are included ("+ut" version
suffix)
- `make clean` now also removes HTML coverage pages and
build/{examples|tests} folders

1 of 1 new or added line in 1 file covered. (100.0%)

154 existing lines in 14 files now uncovered.

10128 of 10356 relevant lines covered (97.8%)

172537.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.8
/sjasm/sjasm.cpp
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
// sjasm.cpp
30

31
#include "sjdefs.h"
32
#include <cstdlib>
33
#include <chrono>
34
#include <ctime>
35

36
static void PrintHelpMain() {
3✔
37
        // Please keep help lines at most 79 characters long (cursor at column 88 after last char)
38
        //     |<-- ...8901234567890123456789012345678901234567890123456789012... 80 chars -->|
39
        _COUT "Based on code of SjASM by Sjoerd Mastijn (http://www.xl2s.tk)" _ENDL;
3✔
40
        _COUT "Copyright 2019-2026 by Peter Helcmanovsky and all other participants" _ENDL;
3✔
41
        //_COUT "Patches by Antipod / boo_boo / PulkoMandy and others" _ENDL;
42
        //_COUT "Tidy up by Tygrys / UB880D / Cizo / mborik / z00m" _ENDL;
43
        _COUT "\nUsage:\nsjasmplus [options] sourcefile(s)" _ENDL;
3✔
44
        _COUT "\nOption flags as follows:" _ENDL;
3✔
45
        _COUT "  -h or --help[=warnings|=syntax]" _ENDL;
3✔
46
        _COUT "  --zxnext[=cspect]        Enable ZX Spectrum Next Z80 extensions (Z80N)" _ENDL;
3✔
47
        _COUT "  --i8080                  Limit valid instructions to i8080 only (+ no fakes)" _ENDL;
3✔
48
        _COUT "  --lr35902                Sharp LR35902 CPU instructions mode (+ no fakes)" _ENDL;
3✔
49
        _COUT "  --outprefix=<path>       Prefix for save/output/.. filenames in directives" _ENDL;
3✔
50
        _COUT "  -i <path> or -I <path> or --inc= <path> (--inc only to empty the list)" _ENDL;
3✔
51
        _COUT "                           Include path (later defined have higher priority)" _ENDL;
3✔
52
        _COUT "  --lst[=<filename>]       Save listing to <filename> (<source>.lst is default)" _ENDL;
3✔
53
        _COUT "  --lstlab[=sort]          Append [sorted] symbol table to listing" _ENDL;
3✔
54
        _COUT "  --sym=<filename>         Save symbol table to <filename>" _ENDL;
3✔
55
        _COUT "  --exp=<filename>         Save exports to <filename> (see EXPORT pseudo-op)" _ENDL;
3✔
56
        _COUT "  --hex=<filename>         Intel HEX saved to <filename> (- is STDOUT)" _ENDL;
3✔
57
        _COUT "  --raw=<filename>         Machine code saved also to <filename> (- is STDOUT)" _ENDL;
3✔
58
        _COUT "  --sld[=<filename>]       Save Source Level Debugging data to <filename>" _ENDL;
3✔
59
        _COUT "  --cleanonerror           Remove newly created binary outputs upon error" _ENDL;
3✔
60
        _COUT " Note: use OUTPUT, LUA/ENDLUA and other pseudo-ops to control output" _ENDL;
3✔
61
        _COUT " Logging:" _ENDL;
3✔
62
        _COUT "  --nologo                 Do not show startup message" _ENDL;
3✔
63
        _COUT "  --version                Basic info (with --nologo only raw version string)" _ENDL;
3✔
64
        _COUT "  --msg=[all|war|err|none|lst|lstlab]" _ENDL;
3✔
65
        _COUT "                           Stderr messages verbosity (\"all\" is default)" _ENDL;
3✔
66
        _COUT "  --fullpath[=on|rel|off]  Input file paths: full | CWD relative | name only" _ENDL;
3✔
67
        _COUT "  --color=[on|off|auto]    Enable or disable ANSI coloring of warnings/errors" _ENDL;
3✔
68
        _COUT " Other:" _ENDL;
3✔
69
        _COUT "  -D<NAME>[=<value>] or --define <NAME>[=<value>]" _ENDL;
3✔
70
        _COUT "                           Define <NAME> as <value>" _ENDL;
3✔
71
        _COUT "  -W[no-][all|<id>]        Enable/disable warning(s), see \"--help=warnings\"" _ENDL;
3✔
72
        _COUT "  -                        Reads STDIN as source (even in between other files)" _ENDL;
3✔
73
        _COUT "  --longptr                No device: program counter $ can go beyond 0x10000" _ENDL;
3✔
74
        _COUT "  --reversepop             Enable reverse POP order (as in base SjASM version)" _ENDL;
3✔
75
        _COUT "  --dirbol                 Enable directives from the beginning of line" _ENDL;
3✔
76
        _COUT "  --dos866                 Encode from Windows codepage to DOS 866 (Cyrillic)" _ENDL;
3✔
77
        _COUT "  --syntax=<...>           Adjust parsing syntax, see \"--help=syntax\"" _ENDL;
3✔
78
}
3✔
79

80
static void PrintHelpSyntax() {
1✔
81
        // Please keep help lines at most 79 characters long (cursor at column 88 after last char)
82
        //     |<-- ...8901234567890123456789012345678901234567890123456789012... 80 chars -->|
83
        _COUT "\nThe following options can be used with --syntax option:" _ENDL;
1✔
84
        _COUT "  a     Multi-argument delimiter \",,\" (default is \",\")" _ENDL;
1✔
85
        _COUT "        Even when selected, dec|inc|pop|push instructions still accept also \",\"" _ENDL;
1✔
86
        _COUT "  b     Whole expression parentheses are legal for memory access only" _ENDL;
1✔
87
        _COUT "  B     Memory access brackets [] required (by default both () and [] works)" _ENDL;
1✔
88
        _COUT "  f F   Fake instructions: warning / disabled (default = enabled)" _ENDL;
1✔
89
        _COUT "  i     Case insensitive instructions/directives (default = same case required)" _ENDL;
1✔
90
        _COUT "  M     Alias \"m\" and \"M\" for \"(hl)\" to cover 8080-like syntax: ADD A,M" _ENDL;
1✔
91
        _COUT "  s     Use only \"simple\" whole-word substitutions, no sub-words" _ENDL;
1✔
92
        _COUT "  w     Report warnings as errors" _ENDL _ENDL;
1✔
93
        _COUT "The recommended setup for new projects is " _CMDL Options::tcols->bold _CMDL Options::tcols->warning;
1✔
94
        _COUT "--syntax=abfw" _CMDL Options::tcols->end _CMDL " which makes syntax less" _ENDL;
1✔
95
        _COUT "relaxed => easier to catch some typos and mistakes." _ENDL;
1✔
96
        _COUT "You can use directive OPT to inline syntax settings into source (recommended):" _ENDL;
1✔
97
        _COUT "```" _CMDL Options::tcols->bold _CMDL Options::tcols->display _ENDL;
1✔
98
        _COUT "        OPT reset --syntax=abfw" _ENDL;
1✔
99
        _COUT "        ld b,($c000) ; <- error: Illegal instruction (can't access memory)" _ENDL;
1✔
100
        _COUT Options::tcols->end _CMDL "```" _ENDL;
1✔
101
}
1✔
102

103
namespace Options {
104
        const STerminalColorSequences tcols_ansi = {
105
                /*end*/                "\033[m",
106
                /*display*/        "\033[36m",                // Cyan
107
                /*warning*/        "\033[33m",                // Yellow
108
                /*error*/        "\033[31m",                // Red
109
                /*bold*/        "\033[1m"                // bold
110
        };
111

112
        const STerminalColorSequences tcols_none = {
113
                /*end*/                "",
114
                /*display*/        "",
115
                /*warning*/        "",
116
                /*error*/        "",
117
                /*bold*/        ""
118
        };
119

120
        const STerminalColorSequences* tcols = &tcols_none;
121

122
        void SetTerminalColors(bool enabled) {
1,086✔
123
                tcols = enabled ? &tcols_ansi : &tcols_none;
1,086✔
124
        }
1,086✔
125

126
        std::filesystem::path OutPrefix {""};
127
        std::filesystem::path SymbolListFName {""};
128
        std::filesystem::path ListingFName {""};
129
        std::filesystem::path ExportFName {""};
130
        std::filesystem::path DestinationFName {""};
131
        std::filesystem::path HEXFName {""};
132
        std::filesystem::path RAWFName {""};
133
        std::filesystem::path UnrealLabelListFName {""};
134
        std::filesystem::path CSpectMapFName {""};
135
        int CSpectMapPageSize = 0x4000;
136
        std::filesystem::path SourceLevelDebugFName {""};
137
        bool IsDefaultSldName = false;
138

139
        EOutputVerbosity OutputVerbosity = OV_ALL;
140
        bool IsLabelTableInListing = false;
141
        bool IsDefaultListingName = false;
142
        EFileVerbosity FileVerbosity = FNAME_BASE;
143
        bool AddLabelListing = false;
144
        bool HideLogo = false;
145
        bool ShowHelp = false;
146
        bool ShowHelpWarnings = false;
147
        bool ShowHelpSyntax = false;
148
        bool ShowVersion = false;
149
        bool NoDestinationFile = true;                // no *.out file by default
150
        SSyntax syx, systemSyntax;
151
        bool IsI8080 = false;
152
        bool IsLR35902 = false;
153
        bool IsLongPtr = false;
154
        bool SortSymbols = false;
155
        bool IsBigEndian = false;
156
        bool DeleteOnError = false;
157
        bool EmitVirtualLabels = false;
158

159
        // Include directories list is initialized with "." (aka LaunchDirectory aka CWD) directory
160
        std::vector<std::filesystem::path> IncludeDirsList{"."};
161
                // ^ Not empty string, because that fails include path existence check
162
                // ^ Not empty list b/c legacy: launch dir is implicit include path (unless reset by `--inc`)
163

164
        CDefineTable CmdDefineTable;                // is initialized by constructor
165

166
        static const char* fakes_disabled_txt_error = "Fake instructions are not enabled";
167
        static const char* fakes_in_i8080_txt_error = "Fake instructions are not implemented in i8080 mode";
168
        static const char* fakes_in_lr35902_txt_error = "Fake instructions are not implemented in Sharp LR35902 mode";
169

170
        // returns true if fakes are completely disabled, false when they are enabled
171
        // showMessage=true: will also display error/warning (use when fake ins. is emitted)
172
        // showMessage=false: can be used to silently check if fake instructions are even possible
173
        bool noFakes(bool showMessage) {
4,005✔
174
                bool fakesDisabled = Options::IsI8080 || Options::IsLR35902 || (!syx.FakeEnabled);
4,005✔
175
                if (!showMessage) return fakesDisabled;
4,005✔
176
                if (fakesDisabled) {
2,322✔
177
                        const char* errorTxt = fakes_disabled_txt_error;
315✔
178
                        if (Options::IsI8080) errorTxt = fakes_in_i8080_txt_error;
315✔
179
                        if (Options::IsLR35902) errorTxt = fakes_in_lr35902_txt_error;
315✔
180
                        Error(errorTxt, bp, SUPPRESS);
315✔
181
                        return true;
315✔
182
                }
183
                if (syx.FakeWarning) {
2,007✔
184
                        WarningById(W_FAKE, bp);
687✔
185
                }
186
                return false;
2,007✔
187
        }
188

189
        std::stack<SSyntax> SSyntax::syxStack;
190

191
        void SSyntax::resetCurrentSyntax() {
115✔
192
                new (&syx) SSyntax();        // restore defaults in current syntax
115✔
193
        }
115✔
194

195
        void SSyntax::pushCurrentSyntax() {
78✔
196
                syxStack.push(syx);                // store current syntax options into stack
78✔
197
        }
78✔
198

199
        bool SSyntax::popSyntax() {
81✔
200
                if (syxStack.empty()) return false;        // no syntax stored in stack
81✔
201
                syx = syxStack.top();        // copy the syntax values from stack
75✔
202
                syxStack.pop();
75✔
203
                return true;
75✔
204
        }
205

206
        void SSyntax::restoreSystemSyntax() {
1,625✔
207
                while (!syxStack.empty()) syxStack.pop();        // empty the syntax stack first
1,627✔
208
                syx = systemSyntax;                // reset to original system syntax
1,625✔
209
        }
1,625✔
210

211
} // eof namespace Options
212

213
static void PrintHelp(bool forceMainHelp) {
5✔
214
        if (forceMainHelp || Options::ShowHelp) PrintHelpMain();
5✔
215
        if (Options::ShowHelpWarnings) PrintHelpWarnings();
5✔
216
        if (Options::ShowHelpSyntax) PrintHelpSyntax();
5✔
217
}
5✔
218

219
CDevice *Devices = nullptr;
220
CDevice *Device = nullptr;
221
CDevicePage *Page = nullptr;
222
char* DeviceID = nullptr;
223
TextFilePos globalDeviceSourcePos;
224
aint deviceDirectivesCount = 0;
225
static char* globalDeviceID = nullptr;
226
static aint globalDeviceZxRamTop = 0;
227

228
// extend
229
fullpath_p_t fileNameFull = nullptr;
230
char* lp, line[LINEMAX], temp[LINEMAX], * bp;
231
char sline[LINEMAX2], sline2[LINEMAX2], * substitutedLine, * eolComment, ModuleName[LINEMAX];
232

233
SSource::SSource(SSource && src) {        // move constructor, "pick" the stdin pointer
1,046✔
234
        memcpy(fname, src.fname, MAX_PATH);
1,046✔
235
        stdin_log = src.stdin_log;
1,046✔
236
        src.fname[0] = 0;
1,046✔
237
        src.stdin_log = nullptr;
1,046✔
238
}
1,046✔
239

240
SSource::SSource(const char* newfname) : stdin_log(nullptr) {
816✔
241
        STRNCPY(fname, MAX_PATH, newfname, MAX_PATH-1);
816✔
242
        fname[MAX_PATH-1] = 0;
816✔
243
}
816✔
244

245
SSource::SSource(int) {
6✔
246
        fname[0] = 0;
6✔
247
        stdin_log = new stdin_log_t();
6✔
248
        stdin_log->reserve(50*1024);
6✔
249
}
6✔
250

251
SSource::~SSource() {
1,868✔
252
        if (stdin_log) delete stdin_log;
1,868✔
253
}
1,868✔
254

255
std::vector<SSource> sourceFiles;
256

257
int ConvertEncoding = ENCWIN;
258

259
EDispMode PseudoORG = DISP_NONE;
260
bool IsLabelNotFound = false, IsSubstituting = false;
261
int pass = 0, ErrorCount = 0, WarningCount = 0, IncludeLevel = -1;
262
int IsRunning = 0, donotlist = 0, listmacro = 0;
263
int adrdisp = 0, dispPageNum = LABEL_PAGE_UNDEFINED, StartAddress = -1;
264
byte* MemoryPointer=NULL;
265
int macronummer = 0, lijst = 0, reglenwidth = 0;
266
source_positions_t sourcePosStack;
267
source_positions_t smartSmcLines;
268
source_positions_t::size_type smartSmcIndex;
269
uint32_t maxlin = 0;
270
aint CurAddress = 0, CompiledCurrentLine = 0, LastParsedLabelLine = 0, PredefinedCounter = 0;
271
aint destlen = 0, size = -1L, comlin = 0;
272
aint sourceBlockLevel = -2;
273

274
char* macrolabp=NULL, * LastParsedLabel=NULL;
275
std::stack<SRepeatStack> RepeatStack;
276
CStringsList* lijstp = NULL;
277
CLabelTable LabelTable;
278
CTemporaryLabelTable TemporaryLabelTable;
279
CDefineTable DefineTable;
280
CMacroDefineTable MacroDefineTable;
281
CMacroTable MacroTable;
282
CStructureTable StructureTable;
283

284
// reserve keywords in labels table, to detect when user is defining label colliding with keyword
285
static void ReserveLabelKeywords() {
547✔
286
        for (const char* keyword : {
14,769✔
287
                "abs", "and", "exist", "high", "low", "mod", "norel", "not", "or", "shl", "shr", "sizeof", "xor",
288
                "ABS", "AND", "EXIST", "HIGH", "LOW", "MOD", "NOREL", "NOT", "OR", "SHL", "SHR", "SIZEOF", "XOR"
289
        }) {
15,316✔
290
                LabelTable.Insert(keyword, -65536, LABEL_IS_UNDEFINED|LABEL_IS_KEYWORD);
14,222✔
291
        }
292
}
547✔
293

294
static void InitPass() {
1,625✔
295
        assert(sourcePosStack.empty());                                // there's no source position [left] in the stack
1,625✔
296
        Relocation::InitPass();
1,625✔
297
        Options::SSyntax::restoreSystemSyntax();        // release all stored syntax variants and reset to initial
1,625✔
298
        uint32_t maxpow10 = 1;
1,625✔
299
        reglenwidth = 0;
1,625✔
300
        do {
301
                ++reglenwidth;
2,645✔
302
                maxpow10 *= 10;
2,645✔
303
                if (maxpow10 < 10) ExitASM(1);        // 32b overflow
2,645✔
304
        } while (maxpow10 <= maxlin);
2,645✔
305
        *ModuleName = 0;
1,625✔
306
        SetLastParsedLabel(nullptr);
1,625✔
307
        InitVorlab();
1,625✔
308
        macrolabp = NULL;
1,625✔
309
        listmacro = 0;
1,625✔
310
        CurAddress = 0;
1,625✔
311
        CompiledCurrentLine = 0;
1,625✔
312
        smartSmcIndex = 0;
1,625✔
313
        PseudoORG = DISP_NONE; adrdisp = 0; dispPageNum = LABEL_PAGE_UNDEFINED;
1,625✔
314
        ListAddress = 0; macronummer = 0; lijst = 0; comlin = 0;
1,625✔
315
        sldSwapSrcPos = 0;
1,625✔
316
        lijstp = NULL;
1,625✔
317
        DidEmitByte();                                // reset the emitted flag
1,625✔
318
        StructureTable.ReInit();
1,625✔
319
        MacroTable.ReInit();
1,625✔
320
        MacroDefineTable.ReInit();
1,625✔
321
        DefineTable = Options::CmdDefineTable;
1,625✔
322
        TemporaryLabelTable.InitPass();
1,625✔
323

324
        // reset "device" stuff + detect "global device" directive
325
        if (globalDeviceID) {                // globalDeviceID detector has to trigger before every pass
1,625✔
326
                free(globalDeviceID);
115✔
327
                globalDeviceID = nullptr;
115✔
328
        }
329
        if (1 < pass && 1 == deviceDirectivesCount && Devices) {        // only single DEVICE used
1,625✔
330
                globalDeviceID = STRDUP(Devices->ID);                // make it global for next pass
231✔
331
                globalDeviceZxRamTop = Devices->ZxRamTop;
231✔
332
        }
333
        if (Devices) delete Devices;
1,625✔
334
        Devices = Device = nullptr;
1,625✔
335
        DeviceID = nullptr;
1,625✔
336
        Page = nullptr;
1,625✔
337
        deviceDirectivesCount = 0;
1,625✔
338
        // resurrect "global" device here
339
        if (globalDeviceID) {
1,625✔
340
                sourcePosStack.push_back(globalDeviceSourcePos);
231✔
341
                if (!SetDevice(globalDeviceID, globalDeviceZxRamTop)) {                // manually tested (remove "!")
231✔
UNCOV
342
                        Error("Failed to re-initialize global device", globalDeviceID, FATAL);
×
343
                }
344
                sourcePosStack.pop_back();
231✔
345
        }
346

347
        // predefined defines - (deprecated) classic sjasmplus v1.x (till v1.15.1)
348
        DefineTable.Replace("_SJASMPLUS", "1");
1,625✔
349
        DefineTable.Replace("_RELEASE", "0");
1,625✔
350
        DefineTable.Replace("_VERSION", "__VERSION__");
1,625✔
351
        DefineTable.Replace("_ERRORS", "__ERRORS__");
1,625✔
352
        DefineTable.Replace("_WARNINGS", "__WARNINGS__");
1,625✔
353
        // predefined defines - sjasmplus v2.x-like (since v1.16.0)
354
        // __DATE__ and __TIME__ are defined just once in main(...) (stored in Options::CmdDefineTable)
355
        DefineTable.Replace("__SJASMPLUS__", VERSION_NUM);                // modified from _SJASMPLUS
1,625✔
356
        DefineTable.Replace("__VERSION__", "\"" VERSION "\"");        // migrated from _VERSION
1,625✔
357
        DefineTable.Replace("__ERRORS__", ErrorCount);                        // migrated from _ERRORS (can be already > 0 from earlier pass)
1,625✔
358
        DefineTable.Replace("__WARNINGS__", WarningCount);                // migrated from _WARNINGS (can be already > 0 from earlier pass)
1,625✔
359
        DefineTable.Replace("__PASS__", pass);                                        // current pass of assembler
1,625✔
360
        DefineTable.Replace("__INCLUDE_LEVEL__", "-1");                        // include nesting
1,625✔
361
        DefineTable.Replace("__BASE_FILE__", "<none>");                        // the include-level 0 file
1,625✔
362
        DefineTable.Replace("__FILE__", "<none>");                                // current file
1,625✔
363
        DefineTable.Replace("__LINE__", "<dynamic value>");                // current line in current file
1,625✔
364
        DefineTable.Replace("__COUNTER__", "<dynamic value>");        // gcc-like, incremented upon every use
1,625✔
365
        PredefinedCounter = 0;
1,625✔
366

367
        // open Export file (defined by --exp or by EXPORT directive), even when no EXPORT is in source
368
        if (LASTPASS == pass) OpenExpFile();                                        // will not do anything if filename is empty
1,625✔
369
}
1,625✔
370

371
static void FreeRAM() {
550✔
372
        if (Devices) {
550✔
373
                delete Devices;                Devices = nullptr;
175✔
374
        }
375
        if (globalDeviceID) {
550✔
376
                free(globalDeviceID);        globalDeviceID = nullptr;
116✔
377
        }
378
        for (CDeviceDef* deviceDef : DefDevices) delete deviceDef;
558✔
379
        DefDevices.clear();
550✔
380
        lijstp = NULL;                // do not delete this, should be released by owners of DUP/regular macros
550✔
381
        LabelTable.RemoveAll();
550✔
382
        DefineTable.RemoveAll();
550✔
383
        SetLastParsedLabel(nullptr);
550✔
384
        if (PreviousIsLabel) {
550✔
385
                free(PreviousIsLabel);
40✔
386
                PreviousIsLabel = nullptr;
40✔
387
        }
388
}
550✔
389

390

391
void ExitASM(int p) {
13✔
392
        if (LASTPASS == pass) Close();
13✔
393
        cout << flush;
13✔
394
        FreeRAM();
13✔
395
        DeleteOnError(p);
13✔
396
        exit(p);
13✔
397
}
398

399
namespace Options {
400

401
        class COptionsParser {
402
        private:
403
                const char* arg;
404
                char opt[LINEMAX];
405
                char val[LINEMAX];
406

407
                // returns 1 when argument was processed (keyword detected, value copied into path var)
408
                int CheckAssignmentOption(const char* keyword, std::filesystem::path & path) {
13,315✔
409
                        if (strcmp(keyword, opt)) return 0;                // detect "keyword" (return 0 if not)
13,315✔
410
                        if (*val) {
372✔
411
                                path = val;
371✔
412
                        } else {
413
                                Error("no parameters found in", arg, ALL);
1✔
414
                        }
415
                        return 1;        // keyword detected, option was processed
372✔
416
                }
417

418
                static void splitByChar(const char* s, const int splitter,
3,469✔
419
                                                           char* v1, const size_t v1Size,
420
                                                           char* v2, const size_t v2Size) {
421
                        // only non-zero splitter character is supported
422
                        const char* spos = splitter ? STRCHR(s, splitter) : NULL;
3,469✔
423
                        if (NULL == spos) {
3,469✔
424
                                // splitter character not found, copy whole input string into v1, v2 = empty string
425
                                STRCPY(v1, v1Size, s);
1,278✔
426
                                v2[0] = 0;
1,278✔
427
                        } else {
428
                                // splitter found, copy string ahead splitter to v1, after it to v2
429
                                STRNCPY(v1, v1Size, s, spos - s);
2,191✔
430
                                v1[spos - s] = 0;
2,191✔
431
                                STRCPY(v2, v2Size, spos + 1);
2,191✔
432
                        }
433
                }
3,469✔
434

435
                void parseSyntaxValue() {
152✔
436
                        // Options::syx is expected to be already in default state before entering this
437
                        for (const auto & syntaxOption : val) {
467✔
438
                                switch (syntaxOption) {
467✔
439
                                case 0:   return;
152✔
440
                                // f F - instructions: fake warning, no fakes (default = fake enabled)
441
                                case 'f': syx.FakeEnabled = syx.FakeWarning = true; break;
315✔
442
                                case 'F': syx.FakeEnabled = false; break;
16✔
443
                                // a - multi-argument delimiter ",," (default is ",")
444
                                case 'a': syx.MultiArg = &doubleComma; break;
110✔
445
                                // b - single parentheses enforce mem access (default = relaxed syntax)
446
                                case 'b': syx.MemoryBrackets = 1; break;
79✔
447
                                // B - memory access brackets [] required (default = relaxed syntax)
448
                                case 'B': syx.MemoryBrackets = 2; break;
7✔
449
                                // l L - warn/error about labels using keywords (default = no message)
450
                                case 'l':
5✔
451
                                case 'L':
452
                                {
453
                                        const char this_option_is[2] = { syntaxOption, 0 };
5✔
454
                                        Error("Syntax option not implemented yet", this_option_is, PASS03);
5✔
455
                                        break;
5✔
456
                                }
457
                                // i - case insensitive instructions/directives (default = same case required)
458
                                case 'i': syx.CaseInsensitiveInstructions = true; break;
3✔
459
                                // w - warnings option: report warnings as errors
460
                                case 'w': syx.WarningsAsErrors = true; break;
38✔
461
                                // M - alias "m" and "M" for "(hl)" to cover 8080-like syntax: ADD A,M
462
                                case 'M': syx.Is_M_Memory = true; break;
3✔
463
                                // s - switch off sub-word substitution in DEFINEs (s like "Simple defines" or "Sub word")
464
                                case 's': syx.IsSubwordSubstitution = false; break;
3✔
465
                                // unrecognized option
466
                                default:
1✔
467
                                        const char this_option_is[2] = { syntaxOption, 0 };
1✔
468
                                        Error("Unrecognized syntax option", this_option_is, PASS03);
1✔
469
                                        break;
1✔
470
                                }
471
                        }
472
                }
473

474
        public:
475
                void GetOptions(const char* const * const argv, int& i, bool onlySyntaxOptions = false) {
1,101✔
476
                        while ((arg=argv[i]) && ('-' == arg[0])) {
5,223✔
477
                                bool doubleDash = false;
4,127✔
478
                                // copy "option" (up to '=' char) into `opt`, copy "value" (after '=') into `val`
479
                                if ('-' == arg[1]) {        // double-dash detected, value is expected after "="
4,127✔
480
                                        doubleDash = true;
3,427✔
481
                                        splitByChar(arg + 2, '=', opt, LINEMAX, val, LINEMAX);
3,427✔
482
                                } else {                                // single dash, parse value from second character onward
483
                                        opt[0] = arg[1];        // copy only single letter into `opt`
700✔
484
                                        opt[1] = 0;
700✔
485
                                        if (opt[0]) {                // if it was not empty, try to copy also `val`
700✔
486
                                                STRCPY(val, LINEMAX, arg + 2);
695✔
487
                                        }
488
                                }
489

490
                                // check for particular options and setup option value by it
491
                                // first check all syntax-only options which may be modified by OPT directive
492
                                if (!strcmp(opt, "zxnext")) {
4,127✔
493
                                        if (IsI8080) Error("Can't enable Next extensions while in i8080 mode", nullptr, FATAL);
50✔
494
                                        if (IsLR35902) Error("Can't enable Next extensions while in Sharp LR35902 mode", nullptr, FATAL);
49✔
495
                                        syx.IsNextEnabled = 1;
48✔
496
                                        if (!strcmp(val, "cspect")) syx.IsNextEnabled = 2;        // CSpect emulator extensions
48✔
497
                                } else if (!strcmp(opt, "reversepop")) {
4,077✔
498
                                        syx.IsReversePOP = true;
19✔
499
                                } else if (!strcmp(opt, "dirbol")) {
4,058✔
500
                                        syx.IsPseudoOpBOF = true;
24✔
501
                                } else if (!strcmp(opt, "nofakes")) {
4,034✔
502
                                        syx.FakeEnabled = false;
5✔
503
                                        // was deprecated, as it is provided by `--syntax=F` too, but now I decided to keep also this older option
504
                                        // (it's less cryptic than the --syntax letter soup, one duplicity of functionality will not end the world, right?)
505
                                } else if (!strcmp(opt, "syntax")) {
4,029✔
506
                                        parseSyntaxValue();
152✔
507
                                } else if (!doubleDash && 'W' == opt[0]) {
3,877✔
508
                                        CliWoption(val);
630✔
509
                                } else if (!strcmp(opt, "cleanonerror")) {
3,247✔
510
                                        // this one is not part of syx and can't be reset, but making it available with OPT as well (works even late-enabled)
511
                                        DeleteOnError = true;
4✔
512
                                } else if (onlySyntaxOptions) {
3,243✔
513
                                        // rest of the options is available only when launching the sjasmplus
514
                                        return;
3✔
515
                                } else if (!strcmp(opt, "lr35902")) {
3,240✔
516
                                        IsLR35902 = true;
16✔
517
                                        // force (silently) other CPU modes OFF
518
                                        IsI8080 = false;
16✔
519
                                        syx.IsNextEnabled = 0;
16✔
520
                                } else if (!strcmp(opt, "i8080")) {
3,224✔
521
                                        IsI8080 = true;
15✔
522
                                        // force (silently) other CPU modes OFF
523
                                        IsLR35902 = false;
15✔
524
                                        syx.IsNextEnabled = 0;
15✔
525
                                } else if ((!doubleDash && 'h' == opt[0] && !val[0]) || (doubleDash && !strcmp(opt, "help"))) {
3,209✔
526
                                        ShowHelp |= !!strcmp("warnings", val) && !!strcmp("syntax", val);
4✔
527
                                        ShowHelpWarnings |= !strcmp("warnings", val);
4✔
528
                                        ShowHelpSyntax |= !strcmp("syntax", val);
4✔
529
                                } else if (doubleDash && !strcmp(opt, "version")) {
3,205✔
530
                                        ShowVersion = true;
3✔
531
                                } else if (!strcmp(opt, "lstlab")) {
3,202✔
532
                                        AddLabelListing = true;
526✔
533
                                        if (val[0]) SortSymbols = !strcmp("sort", val);
526✔
534
                                } else if (!strcmp(opt, "longptr")) {
2,676✔
535
                                        IsLongPtr = true;
3✔
536
                                } else if (!strcmp(opt, "msg")) {
2,673✔
537
                                        if (!strcmp("none", val)) {
559✔
538
                                                OutputVerbosity = OV_NONE;
474✔
539
                                                HideLogo = true;
474✔
540
                                        } else if (!strcmp("err", val)) {
85✔
541
                                                OutputVerbosity = OV_ERROR;
1✔
542
                                        } else if (!strcmp("war", val)) {
84✔
543
                                                OutputVerbosity = OV_WARNING;
27✔
544
                                        } else if (!strcmp("all", val)) {
57✔
545
                                                OutputVerbosity = OV_ALL;
3✔
546
                                        } else if (!strcmp("lst", val)) {
54✔
547
                                                OutputVerbosity = OV_LST;
2✔
548
                                                AddLabelListing = false;
2✔
549
                                                HideLogo = true;
2✔
550
                                        } else if (!strcmp("lstlab", val)) {
52✔
551
                                                OutputVerbosity = OV_LST;
49✔
552
                                                AddLabelListing = true;
49✔
553
                                                SortSymbols = true;
49✔
554
                                                HideLogo = true;
49✔
555
                                        } else {
556
                                                Error("unexpected parameter in", arg, ALL);
3✔
557
                                        }
558
                                } else if (!strcmp(opt, "lst") && !val[0]) {
2,114✔
559
                                        IsDefaultListingName = true;
5✔
560
                                } else if (!strcmp(opt, "sld") && !val[0]) {
2,109✔
561
                                        IsDefaultSldName = true;
4✔
562
                                } else if (
2,105✔
563
                                        CheckAssignmentOption("outprefix", OutPrefix) ||
2,105✔
564
                                        CheckAssignmentOption("sym", SymbolListFName) ||
2,104✔
565
                                        CheckAssignmentOption("lst", ListingFName) ||
2,098✔
566
                                        CheckAssignmentOption("exp", ExportFName) ||
1,765✔
567
                                        CheckAssignmentOption("sld", SourceLevelDebugFName) ||
1,760✔
568
                                        CheckAssignmentOption("hex", HEXFName) ||
5,948✔
569
                                        CheckAssignmentOption("raw", RAWFName) ) {
1,739✔
570
                                        // was proccessed inside CheckAssignmentOption function
571
                                } else if (!strcmp(opt, "fullpath")) {
1,733✔
572
                                        if (!strcmp("on", val)) {
548✔
573
                                                FileVerbosity = FNAME_ABSOLUTE;
1✔
574
                                        } else if (!val[0] || !strcmp("rel", val)) {
547✔
575
                                                FileVerbosity = FNAME_LAUNCH_REL;
545✔
576
                                        } else if (!strcmp("off", val)) {
2✔
577
                                                FileVerbosity = FNAME_BASE;
1✔
578
                                        } else {
579
                                                Error("invalid --fullpath setting (use: on|rel|off)", val, ALL);
1✔
580
                                        }
581
                                } else if (!strcmp(opt, "color")) {
1,185✔
582
                                        if (!strcmp("on", val)) {
533✔
583
                                                SetTerminalColors(true);
3✔
584
                                        } else if (!strcmp("off", val)) {
530✔
585
                                                SetTerminalColors(false);
523✔
586
                                        } else if (!strcmp("auto", val)) {
7✔
587
                                                // already heuristically detected, nothing to do
588
                                        } else {
589
                                                Error("invalid --color setting (use: on|off|auto)", val, ALL);
1✔
590
                                        }
591
                                } else if (!strcmp(opt, "nologo")) {
652✔
592
                                        HideLogo = 1;
552✔
593
                                } else if (!strcmp(opt, "dos866")) {
100✔
594
                                        ConvertEncoding = ENCDOS;
1✔
595
                                } else if ((doubleDash && !strcmp(opt, "inc")) ||
99✔
596
                                                        (!doubleDash && 'i' == opt[0]) ||
74✔
597
                                                        (!doubleDash && 'I' == opt[0])) {
69✔
598
                                        if (*val) {
49✔
599
                                                IncludeDirsList.emplace_back(val);
35✔
600
                                        } else {
601
                                                if (!doubleDash || '=' == arg[5]) {
14✔
602
                                                        if (argv[i+1] && '-' != argv[i+1][0]) {                // include path provided as next argument (after space, like gcc)
8✔
603
                                                                IncludeDirsList.emplace_back(argv[++i]);
5✔
604
                                                        } else {
605
                                                                Error("no include path found for", arg, ALL);
3✔
606
                                                        }
607
                                                } else {        // individual `--inc` without "= path" will RESET include dirs
608
                                                        IncludeDirsList.clear();
6✔
609
                                                }
610
                                        }
611
                                } else if (!strcmp(opt, "define")) {        // for --define name=value the next argv is used (if available)
50✔
612
                                        const char* defarg = argv[i+1] ? argv[++i] : nullptr;
4✔
613
                                        char defN[LINEMAX] {}, defV[LINEMAX] {};
4✔
614
                                        if (defarg) splitByChar(defarg, '=', defN, LINEMAX, defV, LINEMAX);
4✔
615
                                        if (*defN) CmdDefineTable.Add(defN, defV, nullptr);
4✔
616
                                        else Error("missing define value", defarg, ALL);
1✔
617
                                } else if (!doubleDash && 'D' == opt[0]) {
46✔
618
                                        char defN[LINEMAX], defV[LINEMAX];
619
                                        if (*val) {                // for -Dname=value the `val` contains "name=value" string
40✔
620
                                                splitByChar(val, '=', defN, LINEMAX, defV, LINEMAX);
39✔
621
                                                CmdDefineTable.Add(defN, defV, NULL);
39✔
622
                                        } else {
623
                                                Error("no parameters found in", arg, ALL);
1✔
624
                                        }
625
                                } else if (!doubleDash && 0 == opt[0]) {
46✔
626
                                        // only single "-" was on command line = source STDIN
627
                                        sourceFiles.push_back(SSource(1));                // special constructor for stdin input
5✔
628
                                } else {
629
                                        Error("unrecognized option", arg, ALL);
1✔
630
                                }
631

632
                                ++i;                                        // next CLI argument
4,122✔
633
                        } // end of while ((arg=argv[i]) && ('-' == arg[0]))
634
                }
635

636
                void checkIncludePaths(const std::vector<std::filesystem::path> & includes) {
551✔
637
                        for (auto it = includes.crbegin(); it != includes.crend(); ++it) {
1,136✔
638
                                if (std::filesystem::is_directory(*it)) continue;
585✔
639
                                const char* errtxt = '~' == it->string()[0] ? "include path starts with ~ (check docs)" : "include path not found";
7✔
640
                                Error(errtxt, it->string().c_str(), ALL);
7✔
641
                        }
642
                        return;
551✔
643
                }
644
        };
645

646
        int parseSyntaxOptions(int n, char** options) {
375✔
647
                if (n <= 0) return 0;
375✔
648
                int i = 0;
271✔
649
                Options::COptionsParser optParser;
650
                optParser.GetOptions(options, i, true);
271✔
651
                return i;
269✔
652
        }
653
}
654

655
// ==============================================================================================
656
// == UnitTest++ part, checking if unit tests are requested and does launch test-runner then   ==
657
// ==============================================================================================
658
#ifdef ADD_UNIT_TESTS
659

660
# include "UnitTest++/UnitTest++.h"
661

662
# define STOP_MAKE_BY_NON_ZERO_EXIT_CODE 0
663

664
//detect "--unittest" switch, prepare UnitTest++, run the test runner, collect results, exit
665
# define CHECK_UNIT_TESTS \
666
        { \
667
                if (2 == argc && !strcmp("--unittest", argv[1])) { \
668
                        _COUT "SjASMPlus \033[96mv" VERSION "\033[0m | \033[95mrunning unit tests:\033[0m" _ENDL _END \
669
                        int exitCode = STOP_MAKE_BY_NON_ZERO_EXIT_CODE + UnitTest::RunAllTests(); \
670
                        if (exitCode) _COUT "\033[91mNon-zero result from test runner!\033[0m" _ENDL _END \
671
                        else _COUT "\033[92mOK: 0 UnitTest++ tests failed.\033[0m" _ENDL _END \
672
                        exit(exitCode); \
673
                } \
674
        }
675

676
# define VERSION_SUFFIX "+ut"
677

678
#else
679

680
# define CHECK_UNIT_TESTS { /* no unit tests in this build */ }
681

682
# define VERSION_SUFFIX
683

684
#endif
685

686
// == end of UnitTest++ part ====================================================================
687

688
#ifdef WIN32
689
int main(int argc, char* argv[]) {
690
#else
691
int main(int argc, char **argv) {
560✔
692
#endif
693
        Options::SetTerminalColors(autoColorsDetection());
560✔
694

695
        const char* logo = "SjASMPlus Z80 Cross-Assembler v" VERSION VERSION_SUFFIX " (https://github.com/z00m128/sjasmplus)";
560✔
696

697
        sourcePosStack.reserve(32);
560✔
698
        smartSmcLines.reserve(64);
560✔
699
        sourceFiles.reserve(32);
560✔
700
        Options::IncludeDirsList.reserve(32);
560✔
701

702
        CHECK_UNIT_TESTS                // UnitTest++ extra handling in specially built executable
560✔
703

704
        const word little_endian_test[] = { 0x1234 };
559✔
705
        const byte le_test_byte = *reinterpret_cast<const byte*>(little_endian_test);
559✔
706
        Options::IsBigEndian = (0x12 == le_test_byte);
559✔
707

708
        // start counter
709
        long dwStart = GetTickCount();
559✔
710

711
        LaunchDirectory = std::filesystem::current_path();        // get CWD as launch directory
559✔
712
        Options::COptionsParser optParser;
713
        char* envFlags = std::getenv("SJASMPLUSOPTS");
559✔
714
        if (nullptr != envFlags) {
559✔
715
                // split environment arguments into "argc, argv" like variables (by white-space)
716
                char* parsedOptsArray[33] {};        // there must be one more nullptr in the array (32+1)
4✔
717
                int optI = 0, charI = 0;
4✔
718
                while (optI < 32 && !SkipBlanks(envFlags)) {
47✔
719
                        parsedOptsArray[optI++] = temp + charI;
43✔
720
                        while (*envFlags && !White(*envFlags) && charI < LINEMAX-1) temp[charI++] = *envFlags++;
330✔
721
                        temp[charI++] = 0;
43✔
722
                }
723
                if (!SkipBlanks(envFlags)) {
4✔
724
                        Error("SJASMPLUSOPTS environment variable contains too many options (max is 32)", nullptr, ALL);
1✔
725
                }
726
                // process environment variable ahead of command line options (in the same way)
727
                int i = 0;
4✔
728
                while (parsedOptsArray[i]) {
6✔
729
                        optParser.GetOptions(parsedOptsArray, i);
5✔
730
                        if (!parsedOptsArray[i]) break;
5✔
731
                        sourceFiles.push_back(SSource(parsedOptsArray[i++]));
2✔
732
                }
733
        }
734

735
        // setup __DATE__ and __TIME__ macros (setup them just once, not every pass!)
736
        auto now = std::chrono::system_clock::now();
559✔
737
        std::time_t now_c = std::chrono::system_clock::to_time_t(now);
559✔
738
        std::tm now_tm = *std::localtime(&now_c);
559✔
739
        char dateBuffer[32] = {}, timeBuffer[32] = {};
559✔
740
        SPRINTF3(dateBuffer, 30, "\"%04d-%02d-%02d\"", now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
559✔
741
        SPRINTF3(timeBuffer, 30, "\"%02d:%02d:%02d\"", now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec);
559✔
742
        Options::CmdDefineTable.Add("__DATE__", dateBuffer, nullptr);
559✔
743
        Options::CmdDefineTable.Add("__TIME__", timeBuffer, nullptr);
559✔
744

745
        int i = 1;
559✔
746
        if (argc > 1) {
559✔
747
                while (argv[i]) {
1,370✔
748
                        optParser.GetOptions(argv, i);
825✔
749
                        if (!argv[i]) break;
825✔
750
                        sourceFiles.push_back(SSource(argv[i++]));
812✔
751
                }
752
        }
753
        // warn about BE-host only when there's any CLI argument && after CLI options were parsed
754
        if (2 <= argc && Options::IsBigEndian) WarningById(W_BE_HOST, nullptr, W_EARLY);
559✔
755
        if (Options::IsDefaultListingName && Options::ListingFName.has_filename()) {
559✔
756
                Error("Using both  --lst  and  --lst=<filename>  is not possible.", NULL, FATAL);
1✔
757
        }
758
        if (OV_LST == Options::OutputVerbosity && (Options::IsDefaultListingName || Options::ListingFName.has_filename())) {
558✔
759
                Error("Using  --msg=lst[lab]  and other list options is not possible.", NULL, FATAL);
1✔
760
        }
761
        if (Options::IsDefaultSldName && Options::SourceLevelDebugFName.has_filename()) {
557✔
762
                Error("Using both  --sld  and  --sld=<filename>  is not possible.", NULL, FATAL);
1✔
763
        }
764
        Options::systemSyntax = Options::syx;                // create copy of initial system settings of syntax
556✔
765

766
        if (argc == 1 || Options::ShowHelp || Options::ShowHelpWarnings || Options::ShowHelpSyntax) {
556✔
767
                _COUT logo _ENDL;
5✔
768
                PrintHelp(argc == 1);
5✔
769
                exit(argc == 1);
5✔
770
        }
771

772
        if (!Options::HideLogo) {
551✔
773
                _CERR logo _ENDL;
4✔
774
        }
775

776
        if ((Options::FNAME_BASE == Options::FileVerbosity) &&
558✔
777
                (Options::IsDefaultSldName || Options::SourceLevelDebugFName.has_filename())) {
7✔
778
                Warning("missing  --fullpath  with  --sld  may produce incomplete file paths.", NULL, W_EARLY);
1✔
779
        }
780

781
        optParser.checkIncludePaths(Options::IncludeDirsList);
551✔
782

783
        if (Options::ShowVersion) {
551✔
784
                if (Options::HideLogo) {        // if "sjasmplus --version --nologo", emit only the raw VERSION
3✔
785
                        _CERR VERSION _ENDL;
1✔
786
                }
787
                // otherwise the full logo was already printed
788
                // now check if there were some sources to assemble, if NOT, exit with "OK"!
789
                if (0 == sourceFiles.size()) exit(0);
3✔
790
        }
791

792
        // exit with error if no input file were specified
793
        if (0 == sourceFiles.size()) {
548✔
794
                Error("no inputfile(s)", nullptr, ALL);
1✔
795
                exit(1);
1✔
796
        }
797

798
        // create default output name, if not specified
799
        ConstructDefaultFilename(Options::DestinationFName, "out");
547✔
800
        int base_encoding = ConvertEncoding;
547✔
801

802
        // init some vars
803
        InitCPU();
547✔
804

805
        // open lists (if not set to "default" file name, then the OpenFile will handle it)
806
        OpenList();
547✔
807

808
        ReserveLabelKeywords();
547✔
809

810
        do {
811
                ++pass;
1,625✔
812
                if (pass == LASTPASS) OpenSld();        //open source level debugging file (BEFORE InitPass)
1,625✔
813
                InitPass();
1,625✔
814
                if (pass == LASTPASS) {
1,625✔
815
                        OpenDest();
539✔
816
                        OpenHex(Options::HEXFName);
539✔
817
                }
818

819
                static bool warn_stdin_default_lst = true;
820
                for (SSource & src : sourceFiles) {
4,044✔
821
                        // sanity reset of SIZEOF state (this should NEVER report anything with valid .asm code, unless internal bug)
822
                        sourceBlockLevel = -2;                        // open MODULE and similar errors may leave dangling nesting level
2,429✔
823
                        LabelTable.SizeBoundary(CLabelTable::BOUNDARY_FLOW);
2,429✔
824
                        if (src.stdin_log && warn_stdin_default_lst && 1 == pass && Options::IsDefaultListingName) {
2,429✔
825
                                Warning("use explicit --lst=<filename> for <stdin> source", nullptr, W_EARLY);
1✔
826
                                warn_stdin_default_lst = false;
1✔
827
                        }
828
                        IsRunning = 1;
2,429✔
829
                        ConvertEncoding = base_encoding;
2,429✔
830
                        // don't search include paths, but use GetInputFile to get "archived" fullpath_ref_t for c_str() lifetime
831
                        fullpath_ref_t src_fname = GetInputFile(delim_string_t(src.fname, DT_COUNT));
2,429✔
832
                        OpenFile(src_fname, src.stdin_log);
2,429✔
833
                }
834

835
                while (!RepeatStack.empty()) {
1,618✔
836
                        sourcePosStack.push_back(RepeatStack.top().sourcePos);        // mark DUP line with error
3✔
837
                        Error("[DUP/REPT] missing EDUP/ENDR to end repeat-block");
3✔
838
                        sourcePosStack.pop_back();
3✔
839
                        RepeatStack.pop();
3✔
840
                }
841

842
                if (DISP_NONE != PseudoORG) {
1,615✔
843
                        CurAddress = adrdisp;
3✔
844
                        PseudoORG = DISP_NONE;
3✔
845
                }
846

847
                if (Options::OutputVerbosity <= OV_ALL) {
1,615✔
848
                        if (pass != LASTPASS) {
12✔
849
                                _CERR "Pass " _CMDL pass _CMDL " complete (" _CMDL ErrorCount _CMDL " errors)" _ENDL;
8✔
850
                        } else {
851
                                _CERR "Pass 3 complete" _ENDL;
4✔
852
                        }
853
                }
854
        } while (pass < LASTPASS);
1,615✔
855

856
        pass = 9999; /* added for detect end of compiling */
537✔
857

858
        // dump label table into listing file, the explicit one (Options::IsDefaultListingName == false)
859
        if (Options::AddLabelListing) LabelTable.Dump();
537✔
860

861
        Close();
537✔
862

863
        if (Options::UnrealLabelListFName.has_filename()) {
537✔
864
                LabelTable.DumpForUnreal();
10✔
865
        }
866

867
        if (Options::CSpectMapFName.has_filename()) {
537✔
868
                LabelTable.DumpForCSpect();
12✔
869
        }
870

871
        if (Options::SymbolListFName.has_filename()) {
537✔
872
                LabelTable.DumpSymbols();
6✔
873
        }
874

875
        // sync files, release everything
876
        cout << flush;
537✔
877
        FreeRAM();
537✔
878
        lua_impl_close();
537✔
879
        DeleteOnError(ErrorCount != 0);
537✔
880

881
        if (Options::OutputVerbosity <= OV_ALL) {
537✔
882
                double dwCount;
883
                dwCount = GetTickCount() - dwStart;
4✔
884
                if (dwCount < 0) dwCount = 0;
4✔
885
                fprintf(stderr, "Errors: %d, warnings: %d, compiled: %d lines, work time: %.3f seconds\n",
4✔
886
                                ErrorCount, WarningCount, CompiledCurrentLine, dwCount / 1000);
887
        }
888

889
        return (ErrorCount != 0);
537✔
890
}
891
//eof sjasm.cpp
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc