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

z00m128 / sjasmplus / 1549

08 Feb 2026 03:35PM UTC coverage: 96.402% (-0.2%) from 96.574%
1549

push

cirrus-ci

ped7g
CLI: added --help=syntax

Makes the offline-and-only-exe type of situation a bit more "complete",
although I guess you really need documentation.html to deal with
detailed syntax of directives, etc.

But the "check docs for details" output from --help always irked me a
bit as very lame, so here we go with full help sub-page.

9 of 27 new or added lines in 1 file covered. (33.33%)

1 existing line in 1 file now uncovered.

9673 of 10034 relevant lines covered (96.4%)

170550.98 hits per line

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

96.02
/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 "  --autoreloc              Switch to autorelocation mode. See more in docs." _ENDL;
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 " Note: use OUTPUT, LUA/ENDLUA and other pseudo-ops to control output" _ENDL;
3✔
60
        _COUT " Logging:" _ENDL;
3✔
61
        _COUT "  --nologo                 Do not show startup message" _ENDL;
3✔
62
        _COUT "  --version                Basic info (with --nologo only raw version string)" _ENDL;
3✔
63
        _COUT "  --msg=[all|war|err|none|lst|lstlab]" _ENDL;
3✔
64
        _COUT "                           Stderr messages verbosity (\"all\" is default)" _ENDL;
3✔
65
        _COUT "  --fullpath[=on|rel|off]  Input file paths: full | CWD relative | name only" _ENDL;
3✔
66
        _COUT "  --color=[on|off|auto]    Enable or disable ANSI coloring of warnings/errors" _ENDL;
3✔
67
        _COUT " Other:" _ENDL;
3✔
68
        _COUT "  -D<NAME>[=<value>] or --define <NAME>[=<value>]" _ENDL;
3✔
69
        _COUT "                           Define <NAME> as <value>" _ENDL;
3✔
70
        _COUT "  -W[no-][all|<id>]        Enable/disable warning(s), see \"--help=warnings\"" _ENDL;
3✔
71
        _COUT "  -                        Reads STDIN as source (even in between other files)" _ENDL;
3✔
72
        _COUT "  --longptr                No device: program counter $ can go beyond 0x10000" _ENDL;
3✔
73
        _COUT "  --reversepop             Enable reverse POP order (as in base SjASM version)" _ENDL;
3✔
74
        _COUT "  --dirbol                 Enable directives from the beginning of line" _ENDL;
3✔
75
        _COUT "  --dos866                 Encode from Windows codepage to DOS 866 (Cyrillic)" _ENDL;
3✔
76
        _COUT "  --syntax=<...>           Adjust parsing syntax, see \"--help=syntax\"" _ENDL;
3✔
77
}
3✔
78

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

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

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

119
        const STerminalColorSequences* tcols = &tcols_none;
120

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

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

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

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

161
        CDefineTable CmdDefineTable;                // is initialized by constructor
162

163
        static const char* fakes_disabled_txt_error = "Fake instructions are not enabled";
164
        static const char* fakes_in_i8080_txt_error = "Fake instructions are not implemented in i8080 mode";
165
        static const char* fakes_in_lr35902_txt_error = "Fake instructions are not implemented in Sharp LR35902 mode";
166

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

186
        std::stack<SSyntax> SSyntax::syxStack;
187

188
        void SSyntax::resetCurrentSyntax() {
115✔
189
                new (&syx) SSyntax();        // restore defaults in current syntax
115✔
190
        }
115✔
191

192
        void SSyntax::pushCurrentSyntax() {
78✔
193
                syxStack.push(syx);                // store current syntax options into stack
78✔
194
        }
78✔
195

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

203
        void SSyntax::restoreSystemSyntax() {
1,577✔
204
                while (!syxStack.empty()) syxStack.pop();        // empty the syntax stack first
1,579✔
205
                syx = systemSyntax;                // reset to original system syntax
1,577✔
206
        }
1,577✔
207

208
} // eof namespace Options
209

210
static void PrintHelp(bool forceMainHelp) {
4✔
211
        if (forceMainHelp || Options::ShowHelp) PrintHelpMain();
4✔
212
        if (Options::ShowHelpWarnings) PrintHelpWarnings();
4✔
213
        if (Options::ShowHelpSyntax) PrintHelpSyntax();
4✔
214
}
4✔
215

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

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

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

237
SSource::SSource(const char* newfname) : stdin_log(nullptr) {
800✔
238
        STRNCPY(fname, MAX_PATH, newfname, MAX_PATH-1);
800✔
239
        fname[MAX_PATH-1] = 0;
800✔
240
}
800✔
241

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

248
SSource::~SSource() {
1,836✔
249
        if (stdin_log) delete stdin_log;
1,836✔
250
}
1,836✔
251

252
std::vector<SSource> sourceFiles;
253

254
int ConvertEncoding = ENCWIN;
255

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

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

280
// reserve keywords in labels table, to detect when user is defining label colliding with keyword
281
static void ReserveLabelKeywords() {
531✔
282
        for (const char* keyword : {
6,903✔
283
                "abs", "and", "exist", "high", "low", "mod", "norel", "not", "or", "shl", "shr", "xor"
284
        }) {
7,434✔
285
                LabelTable.Insert(keyword, -65536, LABEL_IS_UNDEFINED|LABEL_IS_KEYWORD);
6,372✔
286
        }
287
}
531✔
288

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

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

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

362
        // open Export file (defined by --exp or by EXPORT directive), even when no EXPORT is in source
363
        if (LASTPASS == pass) OpenExpFile();                                        // will not do anything if filename is empty
1,577✔
364
}
1,577✔
365

366
static void FreeRAM() {
534✔
367
        if (Devices) {
534✔
368
                delete Devices;                Devices = nullptr;
167✔
369
        }
370
        if (globalDeviceID) {
534✔
371
                free(globalDeviceID);        globalDeviceID = nullptr;
113✔
372
        }
373
        for (CDeviceDef* deviceDef : DefDevices) delete deviceDef;
542✔
374
        DefDevices.clear();
534✔
375
        lijstp = NULL;                // do not delete this, should be released by owners of DUP/regular macros
534✔
376
        LabelTable.RemoveAll();
534✔
377
        DefineTable.RemoveAll();
534✔
378
        SetLastParsedLabel(nullptr);
534✔
379
        if (PreviousIsLabel) {
534✔
380
                free(PreviousIsLabel);
37✔
381
                PreviousIsLabel = nullptr;
37✔
382
        }
383
}
534✔
384

385

386
void ExitASM(int p) {
13✔
387
        FreeRAM();
13✔
388
        if (pass == LASTPASS) {
13✔
389
                Close();
2✔
390
        }
391
        exit(p);
13✔
392
}
393

394
namespace Options {
395

396
        class COptionsParser {
397
        private:
398
                const char* arg;
399
                char opt[LINEMAX];
400
                char val[LINEMAX];
401

402
                // returns 1 when argument was processed (keyword detected, value copied into path var)
403
                int CheckAssignmentOption(const char* keyword, std::filesystem::path & path) {
11,217✔
404
                        if (strcmp(keyword, opt)) return 0;                // detect "keyword" (return 0 if not)
11,217✔
405
                        if (*val) {
356✔
406
                                path = val;
355✔
407
                        } else {
408
                                Error("no parameters found in", arg, ALL);
1✔
409
                        }
410
                        return 1;        // keyword detected, option was processed
356✔
411
                }
412

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

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

469
        public:
470
                void GetOptions(const char* const * const argv, int& i, bool onlySyntaxOptions = false) {
1,080✔
471
                        while ((arg=argv[i]) && ('-' == arg[0])) {
5,083✔
472
                                bool doubleDash = false;
4,008✔
473
                                // copy "option" (up to '=' char) into `opt`, copy "value" (after '=') into `val`
474
                                if ('-' == arg[1]) {        // double-dash detected, value is expected after "="
4,008✔
475
                                        doubleDash = true;
3,325✔
476
                                        splitByChar(arg + 2, '=', opt, LINEMAX, val, LINEMAX);
3,325✔
477
                                } else {                                // single dash, parse value from second character onward
478
                                        opt[0] = arg[1];        // copy only single letter into `opt`
683✔
479
                                        opt[1] = 0;
683✔
480
                                        if (opt[0]) {                // if it was not empty, try to copy also `val`
683✔
481
                                                STRCPY(val, LINEMAX, arg + 2);
678✔
482
                                        }
483
                                }
484

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

623
                                ++i;                                        // next CLI argument
4,003✔
624
                        } // end of while ((arg=argv[i]) && ('-' == arg[0]))
625
                }
626

627
                void checkIncludePaths(const std::vector<std::filesystem::path> & includes) {
534✔
628
                        for (auto it = includes.crbegin(); it != includes.crend(); ++it) {
1,102✔
629
                                if (std::filesystem::is_directory(*it)) continue;
568✔
630
                                const char* errtxt = '~' == it->string()[0] ? "include path starts with ~ (check docs)" : "include path not found";
7✔
631
                                Error(errtxt, it->string().c_str(), ALL);
7✔
632
                        }
633
                        return;
534✔
634
                }
635
        };
636

637
        int parseSyntaxOptions(int n, char** options) {
372✔
638
                if (n <= 0) return 0;
372✔
639
                int i = 0;
268✔
640
                Options::COptionsParser optParser;
641
                optParser.GetOptions(options, i, true);
268✔
642
                return i;
266✔
643
        }
644
}
645

646
// ==============================================================================================
647
// == UnitTest++ part, checking if unit tests are requested and does launch test-runner then   ==
648
// ==============================================================================================
649
#ifdef ADD_UNIT_TESTS
650

651
# include "UnitTest++/UnitTest++.h"
652

653
# define STOP_MAKE_BY_NON_ZERO_EXIT_CODE 0
654

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

668
# define CHECK_UNIT_TESTS { /* no unit tests in this build */ }
669

670
#endif
671

672
// == end of UnitTest++ part ====================================================================
673

674
#ifdef WIN32
675
int main(int argc, char* argv[]) {
676
#else
677
int main(int argc, char **argv) {
542✔
678
#endif
679
        Options::SetTerminalColors(autoColorsDetection());
542✔
680

681
        const char* logo = "SjASMPlus Z80 Cross-Assembler v" VERSION " (https://github.com/z00m128/sjasmplus)";
542✔
682

683
        sourcePosStack.reserve(32);
542✔
684
        smartSmcLines.reserve(64);
542✔
685
        sourceFiles.reserve(32);
542✔
686
        Options::IncludeDirsList.reserve(32);
542✔
687

688
        CHECK_UNIT_TESTS                // UnitTest++ extra handling in specially built executable
542✔
689

690
        const word little_endian_test[] = { 0x1234 };
541✔
691
        const byte le_test_byte = *reinterpret_cast<const byte*>(little_endian_test);
541✔
692
        Options::IsBigEndian = (0x12 == le_test_byte);
541✔
693

694
        // start counter
695
        long dwStart = GetTickCount();
541✔
696

697
        LaunchDirectory = std::filesystem::current_path();        // get CWD as launch directory
541✔
698
        Options::COptionsParser optParser;
699
        char* envFlags = std::getenv("SJASMPLUSOPTS");
541✔
700
        if (nullptr != envFlags) {
541✔
701
                // split environment arguments into "argc, argv" like variables (by white-space)
702
                char* parsedOptsArray[33] {};        // there must be one more nullptr in the array (32+1)
4✔
703
                int optI = 0, charI = 0;
4✔
704
                while (optI < 32 && !SkipBlanks(envFlags)) {
47✔
705
                        parsedOptsArray[optI++] = temp + charI;
43✔
706
                        while (*envFlags && !White(*envFlags) && charI < LINEMAX-1) temp[charI++] = *envFlags++;
330✔
707
                        temp[charI++] = 0;
43✔
708
                }
709
                if (!SkipBlanks(envFlags)) {
4✔
710
                        Error("SJASMPLUSOPTS environment variable contains too many options (max is 32)", nullptr, ALL);
1✔
711
                }
712
                // process environment variable ahead of command line options (in the same way)
713
                int i = 0;
4✔
714
                while (parsedOptsArray[i]) {
6✔
715
                        optParser.GetOptions(parsedOptsArray, i);
5✔
716
                        if (!parsedOptsArray[i]) break;
5✔
717
                        sourceFiles.push_back(SSource(parsedOptsArray[i++]));
2✔
718
                }
719
        }
720

721
        // setup __DATE__ and __TIME__ macros (setup them just once, not every pass!)
722
        auto now = std::chrono::system_clock::now();
541✔
723
        std::time_t now_c = std::chrono::system_clock::to_time_t(now);
541✔
724
        std::tm now_tm = *std::localtime(&now_c);
541✔
725
        char dateBuffer[32] = {}, timeBuffer[32] = {};
541✔
726
        SPRINTF3(dateBuffer, 30, "\"%04d-%02d-%02d\"", now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
541✔
727
        SPRINTF3(timeBuffer, 30, "\"%02d:%02d:%02d\"", now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec);
541✔
728
        Options::CmdDefineTable.Add("__DATE__", dateBuffer, nullptr);
541✔
729
        Options::CmdDefineTable.Add("__TIME__", timeBuffer, nullptr);
541✔
730

731
        int i = 1;
541✔
732
        if (argc > 1) {
541✔
733
                while (argv[i]) {
1,336✔
734
                        optParser.GetOptions(argv, i);
807✔
735
                        if (!argv[i]) break;
807✔
736
                        sourceFiles.push_back(SSource(argv[i++]));
796✔
737
                }
738
        }
739
        // warn about BE-host only when there's any CLI argument && after CLI options were parsed
740
        if (2 <= argc && Options::IsBigEndian) WarningById(W_BE_HOST, nullptr, W_EARLY);
541✔
741
        if (Options::IsDefaultListingName && Options::ListingFName.has_filename()) {
541✔
742
                Error("Using both  --lst  and  --lst=<filename>  is not possible.", NULL, FATAL);
1✔
743
        }
744
        if (OV_LST == Options::OutputVerbosity && (Options::IsDefaultListingName || Options::ListingFName.has_filename())) {
540✔
745
                Error("Using  --msg=lst[lab]  and other list options is not possible.", NULL, FATAL);
1✔
746
        }
747
        if (Options::IsDefaultSldName && Options::SourceLevelDebugFName.has_filename()) {
539✔
748
                Error("Using both  --sld  and  --sld=<filename>  is not possible.", NULL, FATAL);
1✔
749
        }
750
        Options::systemSyntax = Options::syx;                // create copy of initial system settings of syntax
538✔
751

752
        if (argc == 1 || Options::ShowHelp || Options::ShowHelpWarnings || Options::ShowHelpSyntax) {
538✔
753
                _COUT logo _ENDL;
4✔
754
                PrintHelp(argc == 1);
4✔
755
                exit(argc == 1);
4✔
756
        }
757

758
        if (!Options::HideLogo) {
534✔
759
                _CERR logo _ENDL;
3✔
760
        }
761

762
        if ((Options::FNAME_BASE == Options::FileVerbosity) &&
540✔
763
                (Options::IsDefaultSldName || Options::SourceLevelDebugFName.has_filename())) {
6✔
764
                Warning("missing  --fullpath  with  --sld  may produce incomplete file paths.", NULL, W_EARLY);
1✔
765
        }
766

767
        optParser.checkIncludePaths(Options::IncludeDirsList);
534✔
768

769
        if (Options::ShowVersion) {
534✔
770
                if (Options::HideLogo) {        // if "sjasmplus --version --nologo", emit only the raw VERSION
2✔
771
                        _CERR VERSION _ENDL;
1✔
772
                }
773
                // otherwise the full logo was already printed
774
                // now check if there were some sources to assemble, if NOT, exit with "OK"!
775
                if (0 == sourceFiles.size()) exit(0);
2✔
776
        }
777

778
        // exit with error if no input file were specified
779
        if (0 == sourceFiles.size()) {
532✔
780
                Error("no inputfile(s)", nullptr, ALL);
1✔
781
                exit(1);
1✔
782
        }
783

784
        // create default output name, if not specified
785
        ConstructDefaultFilename(Options::DestinationFName, "out");
531✔
786
        int base_encoding = ConvertEncoding;
531✔
787

788
        // init some vars
789
        InitCPU();
531✔
790

791
        // open lists (if not set to "default" file name, then the OpenFile will handle it)
792
        OpenList();
531✔
793

794
        ReserveLabelKeywords();
531✔
795

796
        do {
797
                ++pass;
1,577✔
798
                if (pass == LASTPASS) OpenSld();        //open source level debugging file (BEFORE InitPass)
1,577✔
799
                InitPass();
1,577✔
800
                if (pass == LASTPASS) OpenDest();
1,577✔
801

802
                static bool warn_stdin_default_lst = true;
803
                for (SSource & src : sourceFiles) {
3,948✔
804
                        if (src.stdin_log && warn_stdin_default_lst && 1 == pass && Options::IsDefaultListingName) {
2,381✔
805
                                Warning("use explicit --lst=<filename> for <stdin> source", nullptr, W_EARLY);
1✔
806
                                warn_stdin_default_lst = false;
1✔
807
                        }
808
                        IsRunning = 1;
2,381✔
809
                        ConvertEncoding = base_encoding;
2,381✔
810
                        // don't search include paths, but use GetInputFile to get "archived" fullpath_ref_t for c_str() lifetime
811
                        fullpath_ref_t src_fname = GetInputFile(delim_string_t(src.fname, DT_COUNT));
2,381✔
812
                        OpenFile(src_fname, src.stdin_log);
2,381✔
813
                }
814

815
                while (!RepeatStack.empty()) {
1,570✔
816
                        sourcePosStack.push_back(RepeatStack.top().sourcePos);        // mark DUP line with error
3✔
817
                        Error("[DUP/REPT] missing EDUP/ENDR to end repeat-block");
3✔
818
                        sourcePosStack.pop_back();
3✔
819
                        RepeatStack.pop();
3✔
820
                }
821

822
                if (DISP_NONE != PseudoORG) {
1,567✔
823
                        CurAddress = adrdisp;
3✔
824
                        PseudoORG = DISP_NONE;
3✔
825
                }
826

827
                if (Options::OutputVerbosity <= OV_ALL) {
1,567✔
828
                        if (pass != LASTPASS) {
9✔
829
                                _CERR "Pass " _CMDL pass _CMDL " complete (" _CMDL ErrorCount _CMDL " errors)" _ENDL;
6✔
830
                        } else {
831
                                _CERR "Pass 3 complete" _ENDL;
3✔
832
                        }
833
                }
834
        } while (pass < LASTPASS);
1,567✔
835

836
        pass = 9999; /* added for detect end of compiling */
521✔
837

838
        // dump label table into listing file, the explicit one (Options::IsDefaultListingName == false)
839
        if (Options::AddLabelListing) LabelTable.Dump();
521✔
840

841
        Close();
521✔
842

843
        if (Options::UnrealLabelListFName.has_filename()) {
521✔
844
                LabelTable.DumpForUnreal();
10✔
845
        }
846

847
        if (Options::CSpectMapFName.has_filename()) {
521✔
848
                LabelTable.DumpForCSpect();
12✔
849
        }
850

851
        if (Options::SymbolListFName.has_filename()) {
521✔
852
                LabelTable.DumpSymbols();
6✔
853
        }
854

855
        // sync files, release everything
856
        cout << flush;
521✔
857
        FreeRAM();
521✔
858
        lua_impl_close();
521✔
859

860
        if (Options::OutputVerbosity <= OV_ALL) {
521✔
861
                double dwCount;
862
                dwCount = GetTickCount() - dwStart;
3✔
863
                if (dwCount < 0) dwCount = 0;
3✔
864
                fprintf(stderr, "Errors: %d, warnings: %d, compiled: %d lines, work time: %.3f seconds\n",
3✔
865
                                ErrorCount, WarningCount, CompiledCurrentLine, dwCount / 1000);
866
        }
867

868
        return (ErrorCount != 0);
521✔
869
}
870
//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