added proper assertions; use hash tables for labels and module names
[urasm.git] / src / urasm.c
blob635e4cddf32ef24c608e518f2987d712f1af230c
1 // URASM Z80 assembler
2 // coded by Ketmar // Invisible Vector
3 // GPLv3 ONLY
4 //
5 #ifndef _GNU_SOURCE
6 # define _GNU_SOURCE
7 #endif
9 #include <getopt.h>
10 #include <setjmp.h>
11 #include <stdarg.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
23 #ifdef WIN32
24 # include <windows.h>
25 #endif
27 #include "liburasm/liburasm.h"
28 #include "libfdc/libfdc.h"
29 #include "urforth.h"
31 #include "ursna48.c"
34 #define UR_FORCE_INLINE static inline __attribute__((always_inline))
35 #define UR_INLINE static inline
38 #define VERSION_HI 0
39 #define VERSION_MID 2
40 #define VERSION_LO 2
43 #define MAYBE_UNUSED __attribute__((unused))
45 #define lambda(return_type, body_and_args) ({ \
46 return_type __fn__ body_and_args \
47 __fn__; \
51 // ////////////////////////////////////////////////////////////////////////// //
52 static const char *ur_assert_failure (const char *cond, const char *fname, int fline,
53 const char *func)
55 for (const char *t = fname; *t; ++t) {
56 #ifdef WIN32
57 if (*t == '/' || *t == '\\') fname = t+1;
58 #else
59 if (*t == '/') fname = t+1;
60 #endif
62 fflush(stdout);
63 fprintf(stderr, "\n%s:%d: Assertion in `%s` failed: %s\n", fname, fline, func, cond);
64 fflush(stderr);
65 abort();
68 #define ur_assert(cond_) do { if (__builtin_expect((!(cond_)), 0)) { ur_assert_failure(#cond_, __FILE__, __LINE__, __PRETTY_FUNCTION__); } } while (0)
71 //==========================================================================
73 // joaatHashStr
75 //==========================================================================
76 UR_FORCE_INLINE uint32_t joaatHashStr (const void *buf) {
77 uint32_t hash = 0x29a;
78 const uint8_t *s = (const uint8_t *)buf;
79 size_t len = (s != NULL ? strlen((const char *)s) : 0);
80 while (len--) {
81 //hash += (uint8_t)locase1251(*s++);
82 hash += (*s++);//|0x20; // this converts ASCII capitals to locase (and destroys other, but who cares)
83 hash += hash<<10;
84 hash ^= hash>>6;
86 // finalize
87 hash += hash<<3;
88 hash ^= hash>>11;
89 hash += hash<<15;
90 return hash;
94 ////////////////////////////////////////////////////////////////////////////////
95 UR_FORCE_INLINE int isSpace (char ch) { return (ch && ((unsigned)(ch&0xff) <= 32 || ch == 127)); }
96 UR_FORCE_INLINE int isAlpha (char ch) { return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')); }
97 UR_FORCE_INLINE int isDigit (char ch) { return (ch >= '0' && ch <= '9'); }
98 UR_FORCE_INLINE int isHexDigit (char ch) { return ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')); }
99 UR_FORCE_INLINE int isAlphaDigit (char ch) { return (isAlpha(ch) || isDigit(ch)); }
101 UR_FORCE_INLINE char toUpper (char ch) { return (ch >= 'a' && ch <= 'z' ? ch-'a'+'A' : ch); }
102 UR_FORCE_INLINE char toLower (char ch) { return (ch >= 'A' && ch <= 'Z' ? ch-'A'+'a' : ch); }
104 static int digitInBase (char ch, int base) {
105 if (ch < '0') return -1;
106 if (base <= 10) {
107 if (ch >= '0'+base) return -1;
108 return ch-'0';
110 ch = toUpper(ch);
111 if (ch <= '9') return ch-'0';
112 if (ch < 'A' || ch > 'A'+base-10) return -1;
113 ch -= 'A'-10;
114 return (ch < base ? ch : -1);
118 //==========================================================================
120 // strEquCI
122 //==========================================================================
123 static int strEquCI (const char *s0, const char *s1) {
124 int res = 1;
125 while (res && *s0 && *s1) {
126 char c0 = *s0++; if (c0 >= 'A' && c0 <= 'Z') c0 = c0 - 'A' + 'a';
127 char c1 = *s1++; if (c1 >= 'A' && c1 <= 'Z') c1 = c1 - 'A' + 'a';
128 res = (c0 == c1);
130 return (res && s0[0] == 0 && s1[0] == 0);
134 ////////////////////////////////////////////////////////////////////////////////
135 static char *strprintfVA (const char *fmt, va_list vaorig) {
136 char *buf = NULL;
137 int olen, len = 128;
139 buf = malloc(len);
140 if (buf == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
141 for (;;) {
142 char *nb;
143 va_list va;
145 va_copy(va, vaorig);
146 olen = vsnprintf(buf, len, fmt, va);
147 va_end(va);
148 if (olen >= 0 && olen < len) return buf;
149 if (olen < 0) olen = len*2-1;
150 nb = realloc(buf, olen+1);
151 if (nb == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
152 buf = nb;
153 len = olen+1;
158 static __attribute__((format(printf,1,2))) char *strprintf (const char *fmt, ...) {
159 char *buf = NULL;
160 va_list va;
162 va_start(va, fmt);
163 buf = strprintfVA(fmt, va);
164 va_end(va);
165 return buf;
169 ///////////////////////////////////////////////////////////////////////////////
170 // global variables
172 enum {
173 AMODE_NORMAL = 0,
174 AMODE_UFO = 1, // scanning UrForth code
177 static int asmMode = AMODE_NORMAL;
179 static char *ufoIncludeDir = NULL;
180 static char *sysIncludeDir = NULL;
181 static char *refFileName = NULL;
182 static char *lastIncludePath = NULL;
183 static char *lastSysIncludePath = NULL;
184 static int urasm_dump_all_labels = 1; // 0: do not write all-uppercase labels
186 #define MAX_LINE_SIZE (1024*1024)
187 static char currLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
189 // set by `processLabel()`
190 static char currSeenLabel[1024];
193 ///////////////////////////////////////////////////////////////////////////////
194 // init dirs
196 static void initInclideDir (void) {
197 const char *id = getenv("URASM_INCLUDE_DIR");
198 if (id && id[0]) {
199 sysIncludeDir = strdup(id);
200 } else {
201 char myDir[4096];
202 memset(myDir, 0, sizeof(myDir));
203 #ifndef WIN32
204 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
205 strcpy(myDir, ".");
206 } else {
207 char *p = (char *)strrchr(myDir, '/');
208 if (!p) strcpy(myDir, "."); else *p = '\0';
210 #else
211 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
212 char *p = strrchr(myDir, '\\');
213 if (!p) strcpy(myDir, "."); else *p = '\0';
214 #endif
215 strcat(myDir, "/libs");
216 sysIncludeDir = strdup(myDir);
218 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
219 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
223 static void initUFEInclideDir (void) {
224 const char *id = getenv("URASM_URFORTH_INCLUDE_DIR");
225 if (id && id[0]) {
226 ufoIncludeDir = strdup(id);
227 } else {
228 char myDir[4096];
229 memset(myDir, 0, sizeof(myDir));
230 #ifndef WIN32
231 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
232 strcpy(myDir, ".");
233 } else {
234 char *p = (char *)strrchr(myDir, '/');
235 if (!p) strcpy(myDir, "."); else *p = '\0';
237 #else
238 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
239 char *p = strrchr(myDir, '\\');
240 if (!p) strcpy(myDir, "."); else *p = '\0';
241 #endif
242 strcat(myDir, "/urflibs");
243 ufoIncludeDir = strdup(myDir);
245 while (ufoIncludeDir[0] && ufoIncludeDir[strlen(ufoIncludeDir)-1] == '/') ufoIncludeDir[strlen(ufoIncludeDir)-1] = '\0';
246 if (!ufoIncludeDir[0]) strcpy(ufoIncludeDir, ".");
250 ///////////////////////////////////////////////////////////////////////////////
251 // string utilities
253 /* trim trailing spaces and comments; normalize colons */
254 static void normalizeStr (char *s) {
255 if (asmMode == AMODE_UFO) return; // do nothing
256 char *p = s;
257 /* now skip all shit */
258 while (*p) {
259 const char ch = *p++;
260 /* check for "af'" */
261 if (ch == '\'' && p-s >= 2 && toLower(p[-2] == 'a') && toLower(p[-1] == 'f')) continue;
262 /* comment */
263 if (ch == ';') { p[-1] = 0; break; }
264 /* string */
265 if (ch == '"' || ch == '\'') {
266 const char qch = ch;
267 while (*p) {
268 const char c1 = *p++;
269 if (c1 == qch) break;
270 if (c1 == '\\' && *p) ++p;
272 continue;
274 /* reduce and normalise colons */
275 if (ch == ':') {
276 --p; /* back to colon */
277 /* remove spaces before colon */
278 char *t = p;
279 while (t != s && isSpace(t[-1])) --t;
280 if (t != p) memmove(t, p, strlen(p)+1);
281 p = t;
282 ur_assert(p[0] == ':');
283 ++p; /* skip colon */
284 /* remove following spaces and colons */
285 t = p;
286 while (*t == ':' || isSpace(*t)) ++t;
287 if (t != p) memmove(p, t, strlen(t)+1);
288 continue;
291 /* done; trim trailing spaces and colons */
292 size_t slen = strlen(s);
293 while (slen > 0 && (isSpace(s[slen-1]) || s[slen-1] == ':')) --slen;
294 s[slen] = 0;
298 /* check if string starts with the given command (case-insensitive) */
299 /* returns NULL or pointer to args */
300 /* skips spaces after command if any */
301 static char *strIsCommand (const char *command, char *str) {
302 for (int cnt = 1; cnt > 0; --cnt) {
303 while (*str && isSpace(*str)) ++str; // skip spaces
304 if (*str == '$') ++str;
305 for (; *command && *str; ++command, ++str) {
306 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
308 if (*command) return NULL; // alas
309 if (*str && isAlphaDigit(*str)) return NULL; // alas
310 while (*str && isSpace(*str)) ++str; // skip spaces
311 if (asmMode != AMODE_UFO) {
312 if (*str && *str == ':') break; // try again if we have a colon
314 return str; // found
316 return NULL;
320 /* parse string literal */
321 /* don't free() result */
322 /* skips trailing spaces */
323 static char *parseStr (char **str, char endQ, int *lenp) {
324 static char buf[MAX_LINE_SIZE];
325 int len = 0, n, f, base;
326 char *a = *str;
327 memset(buf, 0, sizeof(buf));
328 if (lenp) *lenp = 0;
329 for (; *a; ++a) {
330 if (*a == '\\') {
331 if (!a[1]) break;
332 switch (*(++a)) {
333 case 'a': buf[len++] = '\a'; break;
334 case 'b': buf[len++] = '\b'; break;
335 case 'e': buf[len++] = '\x1b'; break;
336 case 'f': buf[len++] = '\f'; break;
337 case 'n': buf[len++] = '\n'; break;
338 case 'r': buf[len++] = '\r'; break;
339 case 't': buf[len++] = '\t'; break;
340 case 'v': buf[len++] = '\v'; break;
341 case 'z': buf[len++] = '\0'; break;
342 case 'x': case 'X': // hex
343 ++a; // skip 'x'
344 base = 16; f = 2;
345 donum: for (n = 0; f > 0; --f) {
346 char ch = digitInBase(*a++, base);
348 if (ch < 0) { --a; break; }
349 n *= base;
350 n += ch;
352 buf[len++] = n;
353 --a; // return to the last digit, 'for' will skip it
354 break;
355 case '0': // octal
356 base = 8; f = 4;
357 goto donum;
358 case '1' ... '9': // decimal
359 base = 10; f = 3;
360 goto donum;
361 default: buf[len++] = a[0]; break; // others
363 } else {
364 if (*a == endQ) { ++a; break; }
365 buf[len++] = *a;
368 while (*a && isSpace(*a)) ++a; // skip trailing spaces
369 *str = a;
370 buf[len] = '\0';
371 if (lenp) *lenp = len;
372 return buf;
376 ///////////////////////////////////////////////////////////////////////////////
377 // source file stack, reader, etc
379 typedef struct SourceLine {
380 struct SourceLine *next;
381 char *line;
382 const char *fname;
383 int lineNo;
384 int system;
385 } SourceLine;
387 static SourceLine *asmText = NULL;
388 static SourceLine *currSrcLine = NULL;
390 typedef struct FileInfo_s {
391 struct FileInfo_s *next;
392 char *name;
393 } FileInfo;
395 static FileInfo *knownFiles = NULL;
398 static void clearKnownFiles (void) {
399 while (knownFiles) {
400 FileInfo *fi = knownFiles;
401 knownFiles = fi->next;
402 if (fi->name) free(fi->name);
403 free(fi);
408 static const char *appendKnownFileName (const char *fn) {
409 if (!fn || !fn[0]) return "<unknown>";
410 for (FileInfo *fi = knownFiles; fi; fi = fi->next) {
411 if (strcmp(fi->name, fn) == 0) return fi->name;
413 FileInfo *fi = calloc(1, sizeof(FileInfo));
414 ur_assert((fi->name = strdup(fn)) != NULL);
415 fi->next = knownFiles;
416 knownFiles = fi;
417 return fi->name;
421 // ////////////////////////////////////////////////////////////////////////// //
422 #define MAX_MACRO_ARGS (32)
424 typedef struct MacroDef {
425 struct MacroDef *next;
426 char *name;
427 SourceLine *lines;
428 int argc;
429 char *argdefaults[MAX_MACRO_ARGS]; // default values
430 char *argnames[MAX_MACRO_ARGS]; // argument names
431 } MacroDef;
433 typedef struct {
434 MacroDef *mac;
435 char *argvals[MAX_MACRO_ARGS]; // argument values
436 } CurMacroDef;
438 static MacroDef *maclist = NULL;
439 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
440 static int curmacronum = 0;
443 static void freeLineList (SourceLine *list) {
444 while (list) {
445 SourceLine *l = list;
446 list = l->next;
447 free(l->line);
448 //free(l->fname);
449 free(l);
454 static void clearMacros (void) {
455 while (maclist) {
456 MacroDef *md = maclist;
457 maclist = md->next;
458 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) {
459 if (md->argdefaults[f]) free(md->argdefaults[f]);
460 if (md->argnames[f]) free(md->argnames[f]);
462 free(md->name);
463 freeLineList(md->lines);
464 free(md);
469 static MacroDef *findMacro (const char *name) {
470 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strEquCI(name, mc->name)) return mc;
471 return NULL;
475 static void asmTextClear (void) {
476 freeLineList(asmText);
477 asmText = NULL;
478 currSrcLine = NULL;
482 #define currLineTrimBlanks() do { \
483 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
484 currLine[slen] = 0; \
485 } while (0)
487 #define currLineRemoveComment() do { \
488 size_t pos = 0; \
489 char instr = 0; \
490 while (pos < slen) { \
491 if (instr) { \
492 if (currLine[pos] == '\\' && pos+1 < slen) { \
493 ++pos; \
494 } else if (currLine[pos] == instr) { \
495 instr = 0; \
497 } else { \
498 if (currLine[pos] == ';') { slen = pos; break; } \
499 if (currLine[pos] == '"') { \
500 instr = '"'; \
501 } else if (currLine[pos] == '\'') { \
502 if (pos == 0 || \
503 (currLine[pos-1] != '_' && !isAlphaDigit(currLine[pos-1]))) \
505 instr = currLine[pos]; \
509 ++pos; \
511 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
512 currLine[slen] = 0; \
513 } while (0)
516 static SourceLine *loadTextFile (FILE *fl, int system, const char *fname, int *error) {
517 if (error) *error = 0;
519 fname = appendKnownFileName(fname);
520 int continuation = 0;
522 SourceLine *first = NULL;
523 SourceLine *last = NULL;
524 int lineNo = 0;
526 // read file
527 while (fgets(currLine, sizeof(currLine)-1, fl)) {
528 ++lineNo;
529 currLine[sizeof(currLine)-1] = '\0';
530 size_t slen = strlen(currLine);
532 if (slen == 0 || (currLine[slen-1] != '\n' && currLine[slen-1] != '\r')) {
533 if (!feof(fl)) {
534 if (error) *error = 1;
535 fprintf(stderr, "ERROR: file %s, line %d: line too long\n", fname, lineNo);
536 freeLineList(first);
537 return NULL;
541 currLineTrimBlanks();
543 int new_cont = 0;
544 if (slen && currLine[slen-1] == '\\') {
545 // this is a possible continuation line
546 if (slen > 1 && currLine[slen-2] == '\\') {
547 // nope, fix it
548 slen -= 1;
549 currLine[slen] = 0;
550 } else {
551 new_cont = 1;
552 --slen;
553 currLineTrimBlanks();
557 //!normalizeStr(currLine);
558 if (!currLine[0]) {
559 // don't store empty lines
560 continuation = new_cont;
561 continue;
564 // remove comments from continuations
565 if (continuation || new_cont) {
566 currLineRemoveComment();
569 // continuation?
570 if (continuation) {
571 // join continuation with the last line
572 ur_assert(last);
573 size_t newlen = slen+strlen(last->line)+4;
574 if (newlen > MAX_LINE_SIZE-8) {
575 if (error) *error = 1;
576 fprintf(stderr, "ERROR: too long continuation at line %d in file '%s'\n", lineNo, fname);
577 freeLineList(first);
578 return NULL;
580 char *newbuf = malloc(newlen);
581 ur_assert(newbuf); // who cares
582 snprintf(newbuf, newlen, "%s%s", last->line, currLine);
583 free(last->line);
584 last->line = newbuf;
585 } else {
586 // add current line
587 SourceLine *s = calloc(1, sizeof(SourceLine));
588 ur_assert(s);
589 s->system = system;
590 s->lineNo = lineNo;
591 ur_assert((s->line = strdup(currLine)) != NULL);
592 s->fname = fname;
593 if (last) last->next = s; else first = s;
594 last = s;
597 continuation = new_cont;
600 return first;
604 static int asmTextLoad (const char *fname, int system) {
605 FILE *fl;
606 int error;
608 if (!(fl = fopen(fname, "r"))) {
609 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
610 return -1;
612 printf("loading: %s\n", fname);
614 SourceLine *first = loadTextFile(fl, 0, fname, &error);
615 fclose(fl);
617 if (error) return -1;
619 SourceLine *last = asmText;
620 if (last) {
621 while (last->next) last = last->next;
622 last->next = first;
623 } else {
624 asmText = first;
626 return 0;
630 static char *extractFileDir (const char *s) {
631 if (!s || !s[0]) return strdup(".");
632 const char *slash;
633 #ifdef WIN32
634 slash = NULL;
635 for (const char *ts = s; *ts; ++ts) {
636 if (*ts == '/' || *ts == '\\') slash = ts;
638 if (slash == s && (s[0] == '/' || s[0] == '\\') && (s[1] == '/' || s[1] == '\\')) slash = NULL;
639 #else
640 slash = strrchr(s, '/');
641 #endif
642 if (!slash) return strdup(".");
643 ptrdiff_t len = (ptrdiff_t)(slash-s)+1;
644 char *res = malloc(len+1);
645 memcpy(res, s, len);
646 res[len] = 0;
647 #ifdef WIN32
648 while (len > 0 && (res[len-1] == '\\' || res[len-1] == '/')) --len;
649 #else
650 while (len > 0 && res[len-1] == '/') --len;
651 #endif
652 if (len == 0) { free(res); return strdup("."); }
653 res[len] = 0;
654 return res;
658 static void loadCurSrcLine (void) {
659 if (currSrcLine) {
660 strcpy(currLine, currSrcLine->line);
661 /* macros will not change include dirs */
662 if (!curmacro) {
663 char **incpp = (!currSrcLine->system ? &lastIncludePath : &lastSysIncludePath);
664 if (*incpp) free(*incpp);
665 *incpp = extractFileDir(currSrcLine->fname);
667 normalizeStr(currLine);
668 } else {
669 currLine[0] = 0;
674 UR_FORCE_INLINE SourceLine *setCurSrcLine (SourceLine *l) {
675 currSrcLine = l;
676 loadCurSrcLine();
677 return l;
681 UR_FORCE_INLINE SourceLine *setUFOCurSrcLine (SourceLine *l) {
682 currSrcLine = l;
683 if (currSrcLine) {
684 strcpy(currLine, currSrcLine->line);
685 } else {
686 currLine[0] = 0;
688 return l;
692 UR_FORCE_INLINE SourceLine *nextSrcLine (void) {
693 return (currSrcLine != NULL ? setCurSrcLine(currSrcLine->next) : NULL);
697 UR_FORCE_INLINE SourceLine *nextUFOSrcLine (void) {
698 return (currSrcLine != NULL ? setUFOCurSrcLine(currSrcLine->next) : NULL);
702 UR_FORCE_INLINE int strHasPathDelim (const char *s) {
703 if (!s || !s[0]) return 0;
704 #ifdef WIN32
705 return (strchr(s, '/') || strchr(s, '\\') ? 1 : 0);
706 #else
707 return (strchr(s, '/') ? 1 : 0);
708 #endif
712 /* returns malloced string */
713 static char *createIncludeName (const char *fname, int assystem, const char *defaultmain,
714 const char *lastIncPathUFO) {
715 if (!fname || !fname[0]) return NULL;
716 char *res;
717 if (fname[0] != '/') {
718 const char *incdir;
719 if (!assystem) {
720 incdir = lastIncludePath;
721 } else if (assystem == -669) {
722 incdir = lastIncPathUFO;
723 } else if (assystem == -666) {
724 incdir = lastIncPathUFO;
725 if (!incdir || !incdir[0]) incdir = ufoIncludeDir;
726 } else {
727 incdir = lastSysIncludePath;
728 if (!incdir || !incdir[0]) incdir = sysIncludeDir;
730 if (incdir == NULL || incdir[0] == 0) incdir = ".";
731 res = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
732 } else {
733 res = strprintf("%s", fname);
735 struct stat st;
736 if (defaultmain && defaultmain[0]) {
737 if (stat(res, &st) == 0) {
738 if (S_ISDIR(st.st_mode)) {
739 char *rs = strprintf("%s/%s", res, defaultmain);
740 free(res);
741 res = rs;
745 /* check if there is the disk file */
746 if (strHasPathDelim(fname) && stat(res, &st) != 0) {
747 /* no file, try "root include" */
748 const char *incdir = (!assystem ? NULL : assystem == -666 ? ufoIncludeDir : sysIncludeDir);
749 char *rs = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
750 free(res);
751 res = rs;
752 /* check for dir again */
753 if (defaultmain && defaultmain[0]) {
754 if (stat(res, &st) == 0) {
755 if (S_ISDIR(st.st_mode)) {
756 char *rs = strprintf("%s/%s", res, defaultmain);
757 free(res);
758 res = rs;
763 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
764 return res;
768 static int includeCount = 0;
770 // process 'INCLUDE'
771 // include file instead of the current line
772 // returns 0 on ok, <0 on error
773 static int asmTextInclude (const char *fname, int system, int softinclude) {
774 char *fn = createIncludeName(fname, system, "zzmain.zas", NULL);
776 FILE *fl = fopen(fn, "r");
777 if (!fl) {
778 if (softinclude) return 0;
779 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", currSrcLine->fname, currSrcLine->lineNo, currLine);
780 free(fn);
781 return -1;
784 if (includeCount > 256) {
785 fclose(fl);
786 free(fn);
787 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", currSrcLine->fname, currSrcLine->lineNo);
788 return -1;
791 ++includeCount;
792 printf("loading: %s\n", fn);
794 int error;
795 SourceLine *first = loadTextFile(fl, system, fn, &error);
796 fclose(fl);
797 free(fn);
799 if (error) return -1;
801 --includeCount;
802 currSrcLine->line[0] = 0; // remove `include` directive
804 SourceLine *last = first;
805 if (last) {
806 while (last->next) last = last->next;
807 last->next = currSrcLine->next;
808 currSrcLine->next = first;
810 return 0;
814 ///////////////////////////////////////////////////////////////////////////////
815 // prototypes
817 static void processCurrentLine (void); // only one, will skip to next one
820 ///////////////////////////////////////////////////////////////////////////////
821 // error raisers, etc
823 static jmp_buf errJP;
826 static void errorWriteFile (FILE *fo) {
827 if (currSrcLine) {
828 size_t slen = strlen(currSrcLine->line);
829 fprintf(fo, "at file %s, line %d\n%.70s%s\n*", currSrcLine->fname,
830 currSrcLine->lineNo, currSrcLine->line, (slen > 70 ? " ..." : ""));
831 } else {
832 fprintf(fo, "somewhere in time: ");
837 static void errorMsgV (const char *fmt, va_list ap) {
838 errorWriteFile(stderr);
839 vfprintf(stderr, fmt, ap);
840 va_end(ap);
841 fputc('\n', stderr);
842 fflush(stderr);
846 __attribute__((format(printf, 1, 2)))
847 static void warningMsg (const char *fmt, ...) {
848 va_list ap;
849 fprintf(stderr, "WARNING ");
850 va_start(ap, fmt);
851 errorMsgV(fmt, ap);
855 __attribute__((format(printf, 1, 2)))
856 static void errorMsg (const char *fmt, ...) {
857 va_list ap;
858 fprintf(stderr, "FATAL ");
859 va_start(ap, fmt);
860 errorMsgV(fmt, ap);
864 __attribute__((noreturn)) __attribute__((format(printf, 1, 2)))
865 static void fatal (const char *fmt, ...) {
866 va_list ap;
867 va_start(ap, fmt);
868 errorMsgV(fmt, ap);
869 longjmp(errJP, 666);
873 __attribute__((noreturn))
874 static void fatalUrLib (int errcode) {
875 errorMsg("%s", urasm_errormsg(errcode));
876 longjmp(errJP, 666);
880 ///////////////////////////////////////////////////////////////////////////////
881 // writer options
882 static int optWriteType = 't';
883 static int optWTChanged = 0;
884 static int optWriteFixups = 0;
885 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
886 static int optRunTape = 1;
887 static int optRunDMB = 1;
888 static int optRunSCL = -1; /* -1: save cargador, but no boot; 1: boot and cargador */
889 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
890 static int optSNA48 = 1;
891 static char *optOutputDir = NULL;
892 static char *optOutputFile = NULL;
893 static int optOutputFileFixed = 0;
896 static char *buildOutputFileName (const char *basename, const char *ext) {
897 ur_assert(ext);
898 ur_assert(basename);
899 if (ext[0] == '.') ++ext;
900 //fprintf(stderr, "********** <%s> <%s> <%s> %d <%s>\n", basename, ext, optOutputFile, optOutputFileFixed, optOutputDir);
901 if (optOutputFile) {
902 if (!optOutputFileFixed) {
903 optOutputFileFixed = 1;
904 char *spp = strrchr(optOutputFile, '/');
905 #ifdef WIN32
906 char *spp1 = strrchr(optOutputFile, '\\');
907 if (!spp || (spp < spp1)) spp = spp1;
908 #endif
909 if (!spp) spp = optOutputFile; else ++spp;
910 char *dotpos = strrchr(spp, '.');
911 if (dotpos) *dotpos = 0;
913 if (optOutputFile && optOutputFile[0]) return strprintf("%s/%s.%s", optOutputDir, optOutputFile, ext);
915 return strprintf("%s/%s.%s", optOutputDir, basename, ext);
919 typedef struct OutDataFile_t {
920 char *name;
921 char type; // 'C', 'B'
922 int hasstart;
923 uint16_t start; // start address, or basic start line
924 uint16_t size; // data size (never 0)
925 uint8_t *data;
926 struct OutDataFile_t *next;
927 } OutDataFile;
929 static OutDataFile *datafiles = NULL;
932 static OutDataFile *appendDataData (const void *bytes, uint16_t start, uint16_t count) {
933 if (!count) return NULL;
935 OutDataFile *off = malloc(sizeof(OutDataFile));
936 off->name = NULL;
937 off->type = 'C';
938 off->hasstart = 0;
939 off->start = start;
940 off->size = count;
941 off->data = malloc(count);
942 if (bytes) memcpy(off->data, bytes, count); else memset(off->data, 0, count);
943 off->next = NULL;
945 OutDataFile *last = datafiles;
946 if (!last) {
947 datafiles = off;
948 } else {
949 while (last->next) last = last->next;
950 last->next = off;
953 return off;
957 static OutDataFile *appendDataFile (const char *fname, long ofs, int len, int allowskip) {
958 if (!fname || !fname[0]) return NULL;
960 FILE *fl = fopen(fname, "rb");
961 if (!fl) {
962 if (allowskip) return NULL;
963 fatal("cannot open data file '%s'", fname);
965 if (fseek(fl, 0, SEEK_END) < 0) fatal("error reading data file '%s'", fname);
966 long fsize = ftell(fl);
967 if (fsize < 0) fatal("error reading data file '%s'", fname);
968 if (ofs < 0) {
969 // from file end
970 ofs = -ofs;
971 if (ofs < 0) fatal("invalid offset");
972 if (ofs >= fsize) ofs = 0; else ofs = fsize-ofs;
974 if (ofs >= fsize) { fclose(fl); return NULL; }
975 if (fseek(fl, ofs, SEEK_SET) < 0) fatal("error reading data file '%s'", fname);
976 fsize -= ofs;
977 if (len < 0) {
978 if (fsize > 65535) fatal("data file '%s' too big", fname);
979 len = (int)fsize;
981 if (len > 65535) fatal("data file '%s' too big", fname);
982 if (len == 0) { fclose(fl); return NULL; }
984 OutDataFile *off = malloc(sizeof(OutDataFile));
985 off->name = NULL;
986 off->type = 'C';
987 off->hasstart = 0;
988 off->start = 0;
989 off->size = (uint16_t)len;
990 off->data = malloc(len);
991 off->next = NULL;
992 if (fread(off->data, (unsigned)len, 1, fl) != 1) fatal("error reading data file '%s'", fname);
993 fclose(fl);
995 OutDataFile *last = datafiles;
996 if (!last) {
997 datafiles = off;
998 } else {
999 while (last->next) last = last->next;
1000 last->next = off;
1003 return off;
1007 //////////////////////////////////////////////////////////////////////////////
1008 // operator management
1010 // return !0 to skip current line
1011 typedef int (*UrAsmOpFn) (void);
1013 enum {
1014 PI_CONT_LINE = 0,
1015 PI_SKIP_LINE = 1
1018 typedef struct UrAsmOp {
1019 char *name;
1020 UrAsmOpFn fn;
1021 void *udata; // user data (struct info for structs, for example)
1022 struct UrAsmOp *next;
1023 } UrAsmOp;
1025 static UrAsmOp *oplist = NULL;
1026 static UrAsmOp *urCurrentOp = NULL;
1029 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
1030 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
1031 ur_assert(res);
1032 res->name = strdup(name);
1033 ur_assert(res->name);
1034 res->fn = fn;
1035 res->udata = NULL;
1036 res->next = oplist;
1037 oplist = res;
1038 return res;
1042 static UrAsmOp *urFindOp (const char *name) {
1043 if (!name) return NULL;
1044 UrAsmOp *res;
1045 if (name[0] == '$' && name[1]) {
1046 for (res = oplist; res; res = res->next) {
1047 if (res->name[0] == '$') {
1048 if (strEquCI(name, res->name)) break;
1049 } else {
1050 if (strEquCI(name+1, res->name)) break;
1053 } else {
1054 for (res = oplist; res; res = res->next) if (strEquCI(name, res->name)) break;
1056 return res;
1060 static void urClearOps (void) {
1061 while (oplist) {
1062 UrAsmOp *c = oplist;
1063 oplist = oplist->next;
1064 free(c->name);
1065 free(c);
1070 ///////////////////////////////////////////////////////////////////////////////
1071 // label management
1073 enum {
1074 LBL_TYPE_VERY_SPECIAL = -42,
1075 LBL_TYPE_UNKNOWN = -1,
1076 LBL_TYPE_ASS = 0, // `=`
1077 LBL_TYPE_EQU = 1, // equ
1078 LBL_TYPE_CODE = 2, // ':', or "lbl opcode"
1079 LBL_TYPE_STOFS = 3, // struct offset
1080 LBL_TYPE_DATA = 4,
1083 typedef struct UrLabelInfo_s {
1084 char *name;
1085 int32_t value;
1086 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
1087 int known; /* !0: label value already known */
1088 int refLine; /* first referenced line */
1089 int fixuptype; /* UR_FIXUP_XXX */
1090 int modHidden; /* hidden module label? hack for PLUS3BOOT */
1091 char savedFirstChar; /* for hidden labels */
1092 char *refFile;
1093 struct UrLabelInfo_s *next;
1094 uint32_t hash;
1095 struct UrLabelInfo_s *hlink;
1096 } UrLabelInfo;
1099 #define UR_LABEL_BUCKETS (1024)
1101 static UrLabelInfo *labels = NULL;
1102 static UrLabelInfo *labelsTail = NULL;
1103 static UrLabelInfo *labelBuckets[UR_LABEL_BUCKETS];
1106 //==========================================================================
1108 // initLabelBuckets
1110 //==========================================================================
1111 static void initLabelBuckets (void) {
1112 for (int f = 0; f < UR_LABEL_BUCKETS; f += 1) labelBuckets[f] = NULL;
1116 //==========================================================================
1118 // urClearLabels
1120 //==========================================================================
1121 static void urClearLabels (void) {
1122 UrLabelInfo *c;
1123 while ((c = labels) != NULL) {
1124 labels = c->next;
1125 if (c->name) free(c->name);
1126 if (c->refFile) free(c->refFile);
1127 free(c);
1129 labelsTail = NULL;
1130 initLabelBuckets();
1134 //==========================================================================
1136 // urFindLabel
1138 //==========================================================================
1139 static UrLabelInfo *urFindLabel (const char *name) {
1140 if (name != NULL && name[0] != 0) {
1141 #if 0
1142 for (UrLabelInfo *c = labels; c; c = c->next) {
1143 if (strcmp(name, c->name) == 0) return c;
1145 #else
1146 const uint32_t hash = joaatHashStr(name);
1147 UrLabelInfo *c = labelBuckets[hash%UR_LABEL_BUCKETS];
1148 while (c != NULL) {
1149 if (c->hash == hash && strcmp(c->name, name) == 0) return c;
1150 c = c->hlink;
1152 #endif
1154 return NULL;
1158 //==========================================================================
1160 // urAddLabel
1162 //==========================================================================
1163 static UrLabelInfo *urAddLabel (const char *name) {
1164 ur_assert(name != NULL && name[0] != 0);
1165 UrLabelInfo *c = urFindLabel(name);
1166 if (c == NULL) {
1167 c = calloc(1, sizeof(UrLabelInfo));
1168 ur_assert(c);
1169 c->name = strdup(name);
1170 ur_assert(c->name);
1171 c->type = LBL_TYPE_UNKNOWN;
1172 c->fixuptype = UR_FIXUP_NONE;
1173 c->modHidden = 0;
1174 c->savedFirstChar = 0;
1175 c->next = NULL;
1176 if (labelsTail) labelsTail->next = c; else labels = c;
1177 labelsTail = c;
1178 c->hash = joaatHashStr(c->name);
1179 const uint32_t bkt = c->hash%UR_LABEL_BUCKETS;
1180 c->hlink = labelBuckets[bkt];
1181 labelBuckets[bkt] = c;
1183 return c;
1187 //==========================================================================
1189 // urAddTempLocal
1191 // add local label; it may shadow others
1193 //==========================================================================
1194 static UrLabelInfo *urAddTempLocal (const char *name) {
1195 UrLabelInfo *c = calloc(1, sizeof(UrLabelInfo));
1196 ur_assert(c);
1197 c->name = strdup(name);
1198 ur_assert(c->name);
1199 c->type = LBL_TYPE_EQU;
1200 c->known = 1;
1201 c->fixuptype = UR_FIXUP_NONE;
1202 c->modHidden = 0;
1203 c->savedFirstChar = 0;
1204 c->next = labels;
1205 labels = c;
1206 if (!labelsTail) labelsTail = c;
1207 c->hash = joaatHashStr(c->name);
1208 const uint32_t bkt = c->hash%UR_LABEL_BUCKETS;
1209 c->hlink = labelBuckets[bkt];
1210 labelBuckets[bkt] = c;
1211 return c;
1215 static void urRemoveTempLocal (UrLabelInfo *tmplbl) {
1216 if (!tmplbl) return;
1217 ur_assert(tmplbl == labels);
1218 labels = tmplbl->next;
1219 if (!labels) labelsTail = NULL;
1220 const uint32_t bkt = tmplbl->hash%UR_LABEL_BUCKETS;
1221 ur_assert(labelBuckets[bkt] == tmplbl);
1222 labelBuckets[bkt] = tmplbl->hlink;
1223 free(tmplbl->name);
1224 free(tmplbl);
1228 ///////////////////////////////////////////////////////////////////////////////
1229 // structures
1230 typedef struct StructField_s {
1231 struct StructField_s *next; // next field
1232 char *name; // field name
1233 uint16_t ofs; // field offset in struct
1234 uint16_t size; // field size in bytes (can be 0 for aliases)
1235 int32_t initValue;
1236 int hasInit;
1237 } StructField;
1240 typedef struct StructInfo_s {
1241 struct StructInfo_s *next; // next structure
1242 char *name; // structure name
1243 StructField *fields;
1244 uint16_t size; // total structure size in bytes
1245 int zerofill;
1246 } StructInfo;
1249 // list of all structures
1250 static StructInfo *structList = NULL;
1253 static void freeStructs (void) {
1254 while (structList) {
1255 StructInfo *sth = structList;
1256 structList = sth->next;
1257 StructField *fld = sth->fields;
1258 while (fld) {
1259 StructField *fc = fld;
1260 fld = fld->next;
1261 if (fc->name) free(fc->name);
1262 free(fc);
1264 if (sth->name) free(sth->name);
1265 free(sth);
1270 ///////////////////////////////////////////////////////////////////////////////
1271 // fixup management
1272 typedef struct FixupItem_s {
1273 struct FixupItem_s *next;
1274 uint16_t opdestaddr;
1275 uint16_t opaddr;
1276 int fixuptype;
1277 int size;
1278 } FixupItem;
1279 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
1282 static void clearFixups (void) {
1283 FixupItem *c;
1284 while ((c = fixlisthead) != NULL) {
1285 fixlisthead = c->next;
1286 free(c);
1288 fixlisthead = fixlisttail = NULL;
1292 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
1293 FixupItem *fx = calloc(1, sizeof(FixupItem));
1295 fx->opdestaddr = opdestaddr;
1296 fx->opaddr = opaddr;
1297 fx->fixuptype = fixuptype;
1298 fx->size = size;
1300 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
1301 fx->next = NULL;
1302 fixlisttail = fx;
1306 ///////////////////////////////////////////////////////////////////////////////
1307 // destination memory management
1309 static uint8_t memory[65536];
1310 static uint8_t memused[65536];
1311 static uint8_t memresv[65536];
1312 static uint16_t start_pc = 0x100; // viva CP/M!
1313 static uint16_t start_disp = 0x100; // viva CP/M!
1314 static uint16_t start_ent = 0x100; // viva CP/M!
1315 static uint16_t pc = 0; /* current position to write */
1316 static uint16_t disp = 0; /* current 'virtual PC' */
1317 static uint16_t ent = 0; /* starting address */
1318 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
1319 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
1320 static int inTapeBlock = 0;
1321 static uint8_t tapeXorB = 0;
1323 static int findChunkFrom (int addr, int *start, int *len);
1326 // find next free area starting from `addr`
1327 // returns address or -1
1328 static int reserveFindFreeFrom (uint16_t addr) {
1329 for (uint32_t freeaddr = addr; freeaddr <= 0xffffU; ++freeaddr) {
1330 if (!memresv[freeaddr]) return freeaddr;
1332 return -1;
1336 // find next reserved area starting from `addr`
1337 // returns address or -1
1338 static int reserveFindReservedFrom (uint16_t addr) {
1339 for (uint32_t freeaddr = addr; freeaddr <= 0xffffU; ++freeaddr) {
1340 if (memresv[freeaddr]) return freeaddr;
1342 return -1;
1346 static void reserveReleaseUsed (uint16_t addr, int len) {
1347 while (len--) memused[addr++] = 0;
1351 //TODO: wrapping
1352 static int reserveHit (uint16_t addr, int len) {
1353 if (len <= 0) return 0;
1354 while (len--) {
1355 if (memresv[addr++]) return 1;
1357 return 0;
1361 // start from `addr`, look for free areas, and check
1362 // if we can insert jump from a pcaddr there
1363 // returns next free area or -1
1364 static int reservedFindNextJumpableFreeFrom (uint16_t pcaddr, uint16_t addr) {
1365 // check if there is enough room at least for a jr
1366 if (memresv[pcaddr] || memresv[pcaddr+1]) return -1;
1367 uint16_t caddr = addr;
1368 // skip reserved
1369 while (caddr <= 0xffffU && memresv[caddr]) ++caddr;
1370 if (caddr > 0xffffU) return -1; // alas
1371 // check distance
1372 uint32_t jrdist = caddr-(pcaddr+2U);
1373 if (jrdist < 128u) return (int)caddr; // yay, we can JR there, this free area is good
1374 // cannot JR, check if we have room for JP
1375 if (memresv[pcaddr+2]) return -1; // alas
1376 // ok, can JP
1377 return (int)caddr;
1381 // start from `addr`, look for free areas, and check
1382 // if we can insert jump from a pcaddr there, and if
1383 // that area is big enough to hold a next jump and at least one more byte
1384 // returns next free area or -1
1385 static int reservedFindNextSuitableFreeFrom (uint16_t pcaddr, uint16_t addr) {
1386 int caddr = addr;
1387 for (;;) {
1388 caddr = reservedFindNextJumpableFreeFrom(pcaddr, (uint16_t)caddr);
1389 if (caddr < 0) return -1; // alas
1390 // we can jump to naddr, now check if it is big enough
1391 // if it has less than 3 bytes, it is too small
1392 if (memresv[(uint16_t)(caddr+1)] || memresv[(uint16_t)(caddr+2)]) {
1393 // too small
1394 caddr += 3;
1395 continue;
1397 // if we have more than 3 bytes, it is definitely ok
1398 if (!memresv[(uint16_t)(caddr+3)]) return caddr; // ok
1399 // it has exactly 3 bytes, check if it is enough
1400 int xaddr = reservedFindNextJumpableFreeFrom(caddr+1, (uint16_t)caddr+3);
1401 if (xaddr >= 0) return caddr; // ok
1402 // alas
1403 caddr = reserveFindReservedFrom(caddr);
1404 if (caddr < 0) return -1;
1405 caddr = reserveFindFreeFrom(caddr);
1406 if (caddr < 0) return -1;
1410 UR_FORCE_INLINE uint8_t getByte (uint16_t addr) {
1411 return memory[addr];
1415 UR_FORCE_INLINE uint16_t getWord (uint16_t addr) {
1416 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
1420 UR_FORCE_INLINE void putByte (uint16_t addr, uint8_t b) {
1421 if (inTapeBlock) tapeXorB ^= b;
1422 memory[addr] = b;
1423 memused[addr] = 1;
1427 UR_FORCE_INLINE void putWord (uint16_t addr, uint16_t w) {
1428 putByte(addr, w&0xFFU);
1429 putByte(addr+1, (w>>8)&0xFFU);
1433 UR_FORCE_INLINE void emitByte (uint8_t b) {
1434 putByte(pc, b);
1435 ++pc;
1436 ++disp;
1440 UR_FORCE_INLINE void emitWord (uint16_t w) {
1441 emitByte(w&0xFFU);
1442 emitByte((w>>8)&0xFFU);
1446 UR_FORCE_INLINE void emitRWord (uint16_t w) {
1447 emitByte((w>>8)&0xFFU);
1448 emitByte(w&0xFFU);
1452 static void prepareMemory (void) {
1453 memset(memory, 0, sizeof(memory));
1454 memset(memused, 0, sizeof(memused));
1455 memset(memresv, 0, sizeof(memresv));
1459 ///////////////////////////////////////////////////////////////////////////////
1460 // module list management
1462 typedef struct ModuleInfo_t {
1463 char *name;
1464 char *fname; // opened in this file
1465 int seen; // !0: module already seen, skip other definitions from the same file
1466 struct ModuleInfo_t *next;
1467 uint32_t hash;
1468 struct ModuleInfo_t *hlink;
1469 } ModuleInfo;
1471 #define UR_MODULE_BUCKETS (1024)
1472 static ModuleInfo *modules = NULL;
1473 static ModuleInfo *currModule = NULL;
1474 static ModuleInfo *moduleBuckets[UR_MODULE_BUCKETS];
1476 static uint8_t mod_memory_saved[65536];
1477 static uint8_t mod_memused_saved[65536];
1478 static uint8_t mod_memresv_saved[65536];
1481 static void modulesClear (void) {
1482 currModule = NULL;
1483 while (modules) {
1484 ModuleInfo *c = modules;
1485 modules = modules->next;
1486 free(c->name);
1487 free(c->fname);
1488 free(c);
1490 for (int f = 0; f < UR_MODULE_BUCKETS; f +=1) moduleBuckets[f] = NULL;
1494 //==========================================================================
1496 // modulesResetSeen
1498 //==========================================================================
1499 static void modulesResetSeen (void) {
1500 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
1504 //==========================================================================
1506 // moduleFind
1508 //==========================================================================
1509 static ModuleInfo *moduleFind (const char *name) {
1510 if (!name || !name[0]) return NULL;
1511 #if 0
1512 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
1513 #else
1514 const uint32_t hash = joaatHashStr(name);
1515 ModuleInfo *c = moduleBuckets[hash%UR_MODULE_BUCKETS];
1516 while (c != NULL) {
1517 if (c->hash == hash && strcmp(c->name, name) == 0) return c;
1518 c = c->hlink;
1520 #endif
1521 return NULL;
1525 //==========================================================================
1527 // moduleAdd
1529 // special module name: PLUS3BOOT
1531 //==========================================================================
1532 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
1533 ModuleInfo *c;
1534 ur_assert(name && fname && name[0] && fname[0]);
1535 c = calloc(1, sizeof(ModuleInfo));
1536 ur_assert(c);
1537 c->name = strdup(name);
1538 ur_assert(c->name);
1539 c->fname = strdup(fname);
1540 ur_assert(c->fname);
1541 c->next = modules;
1542 modules = c;
1543 c->hash = joaatHashStr(name);
1544 const uint32_t bkt = c->hash%UR_MODULE_BUCKETS;
1545 c->hlink = moduleBuckets[bkt];
1546 moduleBuckets[bkt] = c;
1547 // +3DOS bootsector?
1548 if (strEquCI(c->name, "PLUS3BOOT")) {
1549 // save current memory state, because bootsector will be written as a separate independent file
1550 memcpy(mod_memory_saved, memory, sizeof(mod_memory_saved));
1551 memcpy(mod_memused_saved, memused, sizeof(mod_memused_saved));
1552 memcpy(mod_memresv_saved, memresv, sizeof(mod_memresv_saved));
1554 return c;
1558 //==========================================================================
1560 // moduleOpen
1562 //==========================================================================
1563 static void moduleOpen (void) {
1564 //fprintf(stderr, "::: OPENING MODULE <%s> :::\n", currModule->name);
1565 // unhide +3DOS boot labels
1566 if (strEquCI(currModule->name, "PLUS3BOOT")) {
1567 for (UrLabelInfo *lbl = labels; lbl; lbl = lbl->next) {
1568 if (lbl->modHidden == 1) {
1569 if (!lbl->savedFirstChar) fatal("internal module manager error");
1570 lbl->modHidden = 0;
1571 lbl->name[0] = lbl->savedFirstChar; // unhide it
1572 //fprintf(stderr, "***UN-HIDDEN: <%s>\n", lbl->name);
1579 //==========================================================================
1581 // isModuleLabel
1583 //==========================================================================
1584 static int isModuleLabel (const ModuleInfo *mi, const char *name) {
1585 if (!mi || !name || !name[0]) return 0;
1586 if (name[0] == '.') ++name;
1587 const size_t mnlen = strlen(mi->name);
1588 if (strlen(name) <= mnlen || name[mnlen] != '.') return 0;
1589 return (memcmp(name, mi->name, mnlen) == 0);
1593 //==========================================================================
1595 // moduleClose
1597 // close current module
1599 //==========================================================================
1600 static void moduleClose (const char *mname) {
1601 if (mname && !mname[0]) mname = NULL;
1602 if (!currModule) {
1603 if (!mname) fatal("trying to close unopened module");
1604 fatal("trying to close unopened module '%s'", mname);
1606 if (mname && strcmp(mname, currModule->name)) fatal("invalid module name in ENDMODULE; got '%s', expected '%s'", mname, currModule->name);
1608 // +3DOS bootsector?
1609 if (strEquCI(currModule->name, "PLUS3BOOT")) {
1610 // append bootsector chunk
1611 int start = 0, len;
1613 if (!findChunkFrom(start, &start, &len)) fatal("no +3DOS bootsector chunk");
1614 if (start != 0xFE10) fatal("+3DOS bootsector chunk must be 'org'ed and #FE10");
1615 if (len > 512-16) fatal("+3DOS bootsector chunk is too big");
1617 if (pass == 1) {
1618 OutDataFile *off = appendDataData(memory+(unsigned)start, (unsigned)start, (unsigned)len);
1619 if (!off) fatal("cannot append +3DOS bootsector");
1620 off->name = strdup("");
1621 off->type = 1; // special type
1623 start += len;
1624 if (findChunkFrom(start, &start, &len)) fatal("only +3DOS bootsector chunk is allowed (found #%04X, %d)", (unsigned)start, len);
1627 // restore memory
1628 memcpy(memory, mod_memory_saved, sizeof(memory));
1629 memcpy(memused, mod_memused_saved, sizeof(memused));
1630 memcpy(memresv, mod_memresv_saved, sizeof(memresv));
1632 // remove module labels
1633 for (UrLabelInfo *lbl = labels; lbl; lbl = lbl->next) {
1634 if (!isModuleLabel(currModule, lbl->name)) continue;
1635 //fprintf(stderr, "***HIDDEN: <%s>\n", lbl->name);
1636 lbl->modHidden = 1;
1637 lbl->savedFirstChar = lbl->name[0];
1638 lbl->name[0] = '{'; // hide it
1642 currModule = NULL;
1646 ///////////////////////////////////////////////////////////////////////////////
1647 // label getter and utilities
1649 static char *lastSeenGlobalLabel = NULL; /* global; malloced */
1652 static char *mangleLabelName (const char *name) {
1653 static char newname[MAX_LINE_SIZE*2+1024];
1654 if (!name || !name[0]) {
1655 newname[0] = '\0';
1656 return newname;
1658 // local label or macro label?
1659 if (name[0] == '.') {
1660 // two dots is a macro label
1661 if (name[1] == '.') {
1662 if (!name[2]) fatal("invalid label '%s'", name);
1663 // this is macro label
1664 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
1665 snprintf(newname, sizeof(newname), ".%s.MACLBL%d%s", curmacro->mac->name, curmacronum, name+1);
1666 return newname;
1668 if (!name[1]) fatal("invalid label '%s'", name);
1669 // simple local
1670 if (!lastSeenGlobalLabel) fatal("local label '%s' without a global", name);
1671 snprintf(newname, sizeof(newname), ".%s%s", lastSeenGlobalLabel, name);
1672 return newname;
1674 // global label
1675 // if global is prefixed with '@', it should not be mangled
1676 // this is to allow access to other labels from modules
1677 if (name[0] == '@') {
1678 if (name[1] == '@') fatal("double-prefixed label '%s'", name);
1679 if (!name[1]) fatal("invalid label '%s'", name);
1680 snprintf(newname, sizeof(newname), "%s", name+1);
1681 return newname;
1683 // if no module, nothing to do
1684 // also, do not mangle labels with dots inside:
1685 // those are either already mangled, or call to other modules
1686 if (!currModule || strchr(name, '.')) {
1687 snprintf(newname, sizeof(newname), "%s", name);
1688 return newname;
1690 // this is global unqualified label and we have a module; let's rename it
1691 snprintf(newname, sizeof(newname), "%s.%s", currModule->name, name);
1692 return newname;
1696 // WARNING! this will be invalidated
1697 static char lastFindLabelName[MAX_LINE_SIZE*2+1024];
1699 static UrLabelInfo *findLabel (const char *name) {
1700 if (!name || !name[0]) fatal("UrAsm internal error: empty name in `findLabel()`");
1701 char *nn = mangleLabelName(name);
1702 strcpy(lastFindLabelName, nn);
1703 UrLabelInfo *lbl = urFindLabel(nn);
1705 if (!lbl) {
1706 // try non-module label
1707 lbl = urFindLabel(ln);
1710 return lbl;
1714 static UrLabelInfo *findAddLabel (const char *name) {
1715 if (!name || !name[0]) fatal("UrAsm internal error: empty name in `findAddLabel()`");
1716 char *nn = mangleLabelName(name);
1717 strcpy(lastFindLabelName, nn);
1718 UrLabelInfo *lbl = urAddLabel(nn);
1719 if (!lbl->refFile) {
1720 lbl->refLine = currSrcLine->lineNo;
1721 lbl->refFile = strdup(currSrcLine->fname);
1723 return lbl;
1727 static int lblOptMakeU2 = 0;
1729 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
1730 // predefined
1731 if (strEquCI(name, "__URASM_DEFFMT_ANY_DISK")) {
1732 *found = 1;
1733 *defined = 1;
1734 *fixuptype = UR_FIXUP_NONE;
1735 return (optWriteType == 'S' || optWriteType == '3'); // SCL or +3DOS
1737 if (strEquCI(name, "__URASM_DEFFMT_SCL")) {
1738 *found = 1;
1739 *defined = 1;
1740 *fixuptype = UR_FIXUP_NONE;
1741 return (optWriteType == 'S');
1743 if (strEquCI(name, "__URASM_DEFFMT_SCL_MONO")) {
1744 *found = 1;
1745 *defined = 1;
1746 *fixuptype = UR_FIXUP_NONE;
1747 return (optWriteType == 'M');
1749 if (strEquCI(name, "__URASM_DEFFMT_P3DOS")) {
1750 *found = 1;
1751 *defined = 1;
1752 *fixuptype = UR_FIXUP_NONE;
1753 return (optWriteType == '3');
1755 if (strEquCI(name, "__URASM_DEFFMT_HOBETA")) {
1756 *found = 1;
1757 *defined = 1;
1758 *fixuptype = UR_FIXUP_NONE;
1759 return (optWriteType == 'H');
1761 if (strEquCI(name, "__URASM_DEFFMT_TAPE")) {
1762 *found = 1;
1763 *defined = 1;
1764 *fixuptype = UR_FIXUP_NONE;
1765 return (optWriteType == 't');
1767 if (strEquCI(name, "__URASM_DEFFMT_ANY_SNA")) {
1768 *found = 1;
1769 *defined = 1;
1770 *fixuptype = UR_FIXUP_NONE;
1771 return (optWriteType == 's');
1773 if (strEquCI(name, "__URASM_DEFFMT_SNA48")) {
1774 *found = 1;
1775 *defined = 1;
1776 *fixuptype = UR_FIXUP_NONE;
1777 return (optWriteType == 's' && optSNA48);
1779 if (strEquCI(name, "__URASM_DEFFMT_SNA128")) {
1780 *found = 1;
1781 *defined = 1;
1782 *fixuptype = UR_FIXUP_NONE;
1783 return (optWriteType == 's' && !optSNA48);
1785 if (strEquCI(name, "__URASM_DEFFMT_RAW")) {
1786 *found = 1;
1787 *defined = 1;
1788 *fixuptype = UR_FIXUP_NONE;
1789 return (optWriteType == 'r');
1791 if (strEquCI(name, "__URASM_DEFFMT_DMB")) {
1792 *found = 1;
1793 *defined = 1;
1794 *fixuptype = UR_FIXUP_NONE;
1795 return (optWriteType == 'd');
1798 UrLabelInfo *lbl = findLabel(name);
1799 if (!lbl) {
1800 if (pass != 0) {
1801 //fprintf(stderr, "====\n"); for (UrLabelInfo *l = labels; l; l = l->next) fprintf(stderr, " <%s>=%d (known=%d; type=%d)\n", l->name, l->value, l->known, l->type);
1802 errorMsg("using undefined label '%s'", name);
1803 *found = 0;
1804 *defined = 0;
1805 return 0;
1807 lbl = urAddLabel(lastFindLabelName);
1808 lbl->type = (lblOptMakeU2 ? LBL_TYPE_VERY_SPECIAL : LBL_TYPE_UNKNOWN);
1809 lbl->known = 0;
1810 lbl->refLine = currSrcLine->lineNo;
1811 lbl->refFile = strdup(currSrcLine->fname);
1812 //fprintf(stderr, "new label: [%s]\n", lbl->name);
1813 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
1814 } else {
1815 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
1817 if (lbl) {
1818 *found = 1;
1819 *defined = (lbl->known != 0);
1820 *fixuptype = lbl->fixuptype;
1821 return lbl->value;
1823 *found = 0;
1824 *defined = 0;
1825 return 0;
1829 // qtypes
1830 enum {
1831 UR_QTYPE_DEFINED,
1832 UR_QTYPE_KNOWN
1835 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
1836 UrLabelInfo *lbl = findLabel(name);
1837 switch (qtype) {
1838 case UR_QTYPE_DEFINED: return (lbl ? lbl->known != 0 : 0);
1839 case UR_QTYPE_KNOWN: return (lbl != NULL);
1840 default: ;
1842 return 0;
1846 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
1847 if (pass == 1) {
1848 //static const char *n[4] = {"none", "word", "low", "high"};
1849 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
1850 addFixup(opdestaddr, opaddr, fixuptype, size);
1855 static int checkLabels (void) {
1856 int wasError = 0;
1857 for (UrLabelInfo *c = labels; c; c = c->next) {
1858 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
1859 if (c->type == LBL_TYPE_UNKNOWN) {
1860 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
1861 wasError = 1;
1863 if (c->type == LBL_TYPE_ASS) c->known = -1;
1865 //if (wasError) longjmp(errJP, 667);
1866 return wasError;
1870 ///////////////////////////////////////////////////////////////////////////////
1871 // expression utils (aka string parsing)
1874 /* skip leading spaces */
1875 /* returns string with spaces skipped */
1876 UR_FORCE_INLINE char *strSkipSpaces (const char *s) {
1877 while (*s && isSpace(*s)) ++s;
1878 return (char *)s;
1882 /* skip leading spaces */
1883 /* returns string with spaces skipped */
1884 UR_FORCE_INLINE const char *strSkipSpacesConst (const char *s) {
1885 while (*s && isSpace(*s)) ++s;
1886 return s;
1890 /* remove trailing spaces from string */
1891 static void strTrimRight (char *s) {
1892 if (!s || !s[0]) return;
1893 size_t len = strlen(s);
1894 while (len > 0 && isSpace(s[len-1])) --len;
1895 s[len] = 0;
1899 /* skip leading spaces and colons */
1900 /* returns string with spaces skipped */
1901 UR_FORCE_INLINE char *strSkipSpacesColons (char *s) {
1902 while (*s && (isSpace(*s) || *s == ':')) ++s;
1903 return s;
1907 /* remove leading spaces from the current line */
1908 UR_FORCE_INLINE void removeSpaces (void) {
1909 char *e = strSkipSpaces(currLine);
1910 if (e != currLine) memmove(currLine, e, strlen(e)+1);
1914 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1915 /* correctly skip strings */
1916 /* returns string after skipped argument (with trailing spaces skipped) */
1917 static char *skipMacroArg (char *str) {
1918 int parens = 0;
1919 char *strstart = str;
1920 for (;;) {
1921 str = strSkipSpaces(str);
1922 if (!str[0]) return str;
1923 if (parens == 0 && (str[0] == ',' || str[0] == ':')) return str;
1924 /* check for "af'" */
1925 if (str[0] == '\'' && str-strstart >= 2 && toLower(str[-2] == 'a') && toLower(str[-1] == 'f')) {
1926 ++str;
1927 continue;
1929 if (str[0] == '(') { ++parens; continue; }
1930 if (str[0] == ')') { if (--parens < 0) parens = 0; continue; }
1931 /* check for string */
1932 if (str[0] == '"' || str[0] == '\'') {
1933 const char qch = *str++;
1934 while (*str) {
1935 const char ch = *str++;
1936 if (ch == qch) break;
1937 if (ch == '\\' && *str) ++str;
1939 continue;
1941 ++str;
1946 /* evaluate next numeric expression in input string */
1947 /* returns expression value */
1948 static int32_t getExprArg (int *defined, int *addr) {
1949 int error = 0;
1950 char *a = strSkipSpaces(currLine);
1951 int32_t res;
1952 if (!a[0]) fatal("expression expected");
1953 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
1954 if (error) fatalUrLib(error);
1955 if (*ee) {
1956 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
1957 memmove(currLine, ee, strlen(ee)+1);
1958 } else {
1959 currLine[0] = '\0';
1961 return res;
1965 /* evaluate next string expression in input string */
1966 /* returns expression value */
1967 static char *getStrExprArgFmt (void) {
1968 int error = 0;
1969 int donteval = 0, defined = 0;
1970 static char resbuf[256];
1971 char *a = strSkipSpaces(currLine);
1972 if (!a[0]) fatal("expression expected");
1973 urasm_exprval_t res;
1974 urasm_exprval_init(&res);
1975 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
1976 if (error) fatalUrLib(error);
1977 if (*ee) {
1978 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
1979 memmove(currLine, ee, strlen(ee)+1);
1980 } else {
1981 currLine[0] = '\0';
1983 if (res.str) {
1984 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
1985 } else {
1986 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
1988 urasm_exprval_clear(&res);
1989 return resbuf;
1993 /* evaluate next numeric expression in input string */
1994 /* there shoild be no other expressions in the string */
1995 /* returns expression value */
1996 static int32_t getOneExprArg (int *defined, int *addr) {
1997 int32_t res = getExprArg(defined, addr);
1998 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1999 return res;
2003 /* is next expression a string literal? */
2004 UR_FORCE_INLINE int isStrArg (void) {
2005 const char *s = strSkipSpaces(currLine);
2006 return (s[0] == '"' || s[0] == '\'');
2010 /* check of we reached end of operator */
2011 UR_FORCE_INLINE int isOperatorEnd (void) {
2012 const char *s = strSkipSpaces(currLine);
2013 return (s[0] == 0 || s[0] == ':');
2017 /* check of we reached end of operator */
2018 UR_FORCE_INLINE int isLineEnd (void) {
2019 const char *s = strSkipSpaces(currLine);
2020 return (s[0] == 0 || s[0] == ';');
2024 /* parse string argument from input string */
2025 /* returns parsed string */
2026 static char *getStrArg (int *lenp) {
2027 char *res, qCh;
2028 char *a = strSkipSpaces(currLine);
2029 qCh = *a++;
2030 if (qCh != '"' && qCh != '\'') fatal("string expected");
2031 res = parseStr(&a, qCh, lenp);
2032 if (*a) {
2033 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
2034 memmove(currLine, a, strlen(a)+1);
2035 } else {
2036 currLine[0] = '\0';
2038 return res;
2042 /* parse string argument from input string; allows math after the string */
2043 /* returns parsed string */
2044 /* returns NULL and math expr in `lenp` */
2045 static char *getStrExprArg (int *lenp, int isWord) {
2046 char *a = strSkipSpaces(currLine);
2047 const char qCh = *a++;
2048 if (qCh != '"' && qCh != '\'') fatal("string expected");
2049 char *res = parseStr(&a, qCh, lenp);
2050 if (!res[0]) fatal("invalid empty string");
2051 if (*a) {
2052 if (a[0] != ',' && a[0] != ':' && strlen(res) <= 2) {
2053 memmove(currLine, a, strlen(a)+1);
2054 // "ab" -- 'a' will be in low (first) byte
2055 // 'ab' -- 'a' will be in high (second) byte
2056 int32_t sval;
2057 if (res[1]) {
2058 // two-byte
2059 if (qCh == '"') {
2060 sval = (uint8_t)(res[0]&0xffU)|(((uint8_t)(res[1]&0xffU))<<8);
2061 } else {
2062 sval = (uint8_t)(res[1]&0xffU)|(((uint8_t)(res[0]&0xffU))<<8);
2064 } else {
2065 // one-byte
2066 sval = (uint8_t)(res[0]&0xffU);
2068 //fprintf(stderr, "SMATH:000: str=<%s> (sval=%d); <%s>\n", res, sval, currLine);
2069 // parse math
2070 int defined = 0, fixuptype = UR_FIXUP_NONE;
2071 int32_t xadd = getExprArg(&defined, &fixuptype);
2072 //fprintf(stderr, "SMATH:001: str=<%s> (sval=%d; xadd=%d); <%s>\n", res, sval, xadd, currLine);
2073 if (pass > 0 && !defined) fatal("undefined operand");
2074 sval += xadd;
2075 if (currLine[0] && currLine[0] != ',' && currLine[0] != ':') fatal("bad string expression");
2076 res = NULL;
2077 if (lenp) *lenp = sval;
2078 } else {
2079 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
2080 memmove(currLine, a, strlen(a)+1);
2082 } else {
2083 currLine[0] = '\0';
2085 return res;
2089 /* get identifier (and lowercase it) */
2090 static char *getOneIdArgLo (void) {
2091 static char res[MAX_LINE_SIZE+128];
2092 char *p;
2093 char *a = strSkipSpaces(currLine);
2094 memset(res, 0, sizeof(res));
2095 if (!a[0]) fatal("identifier expected");
2096 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
2097 for (; p > res && isSpace(p[-1]); --p) {}
2098 *p = '\0';
2099 if (p-res > 120) fatal("identifier too long: %s", res);
2100 while (*a && isSpace(*a)) ++a;
2101 if (*a) {
2102 memmove(currLine, a, strlen(a)+1);
2103 if (currLine[0] == ';') currLine[0] = 0;
2104 if (currLine[0]) fatal("extra arguments");
2105 } else {
2106 currLine[0] = '\0';
2108 if (!res[0]) fatal("identifier expected");
2109 for (char *t = res; *t; ++t) *t = toLower(*t);
2110 return res;
2114 /* get label argument */
2115 static char *getLabelArg (int checkdelim) {
2116 static char res[MAX_LINE_SIZE+128];
2117 char *p;
2118 char *a = strSkipSpaces(currLine);
2119 memset(res, 0, sizeof(res));
2120 if (!a[0]) fatal("label expected");
2121 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
2122 for (; p > res && isSpace(p[-1]); --p) {}
2123 *p = '\0';
2124 if (p-res > 120) fatal("label name too long: %s", res);
2125 while (*a && isSpace(*a)) ++a;
2126 if (*a) {
2127 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
2128 memmove(currLine, a, strlen(a)+1);
2129 } else {
2130 currLine[0] = '\0';
2132 if (!res[0]) fatal("label expected");
2133 return res;
2137 /* get label argument, and ensure that it is the last one */
2138 static char *getOneLabelArg (void) {
2139 char *res = getLabelArg(1);
2140 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
2141 return res;
2145 /* returns ',' or 0 */
2146 static char eatComma (void) {
2147 char *a = strSkipSpaces(currLine);
2148 if (!a[0]) { currLine[0] = '\0'; return 0; }
2149 if (a[0] == ':') return 0;
2150 if (a[0] != ',') fatal("invalid expression: ',' expected");
2151 for (++a; *a && isSpace(*a); ++a) {}
2152 if (!a[0]) { currLine[0] = '\0'; return 0; }
2153 memmove(currLine, a, strlen(a)+1);
2154 return ',';
2158 static int checkDelim (char dm) {
2159 char *a = strSkipSpaces(currLine);
2160 if (a[0] != dm) return 0;
2161 for (++a; *a && isSpace(*a); ++a) {}
2162 if (!a[0]) { currLine[0] = '\0'; return 1; }
2163 memmove(currLine, a, strlen(a)+1);
2164 return 1;
2168 static void matchDelim (char dm) {
2169 if (!checkDelim(dm)) fatal("invalid expression: '%c' expected", dm);
2173 static int checkIdentCI (const char *ident) {
2174 ur_assert(ident && ident[0]);
2175 char *a = strSkipSpaces(currLine);
2176 while (*a && *ident) {
2177 char c0 = *a++; if (c0 >= 'A' && c0 <= 'Z') c0 = c0 - 'A' + 'a';
2178 char c1 = *ident++; if (c1 >= 'A' && c1 <= 'Z') c1 = c1 - 'A' + 'a';
2179 if (c0 != c1) return 0;
2181 if (ident[0] != 0) return 0;
2182 if (*a == '_' || isAlphaDigit(*a)) return 0;
2183 while (*a && isSpace(*a)) a += 1;
2184 if (*a) {
2185 memmove(currLine, a, strlen(a)+1);
2186 } else {
2187 currLine[0] = '\0';
2189 return 1;
2193 ///////////////////////////////////////////////////////////////////////////////
2194 // label processor
2196 static MAYBE_UNUSED void removeSpacesAndColons (void) {
2197 char *ep = strSkipSpacesColons(currLine);
2198 memmove(currLine, ep, strlen(ep)+1);
2202 static void checkExprEnd (void) {
2203 char *ep = strSkipSpaces(currLine);
2204 memmove(currLine, ep, strlen(ep)+1);
2205 if (currLine[0] && currLine[0] != ':') fatal("end of expression expected");
2209 static void checkOperatorEnd (void) {
2210 char *ep = strSkipSpaces(currLine);
2211 memmove(currLine, ep, strlen(ep)+1);
2212 if (currLine[0]) fatal("end of operator expected");
2216 /* remove label from curLine */
2217 static void removeLabel (void) {
2218 char *ep = currLine;
2219 if (ep[0] && !isSpace(ep[0])) for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
2220 // skip spaces and colons
2221 ep = strSkipSpacesColons(ep);
2222 memmove(currLine, ep, strlen(ep)+1);
2226 static int labelDoEQU (const char *lblname, const char *value) {
2227 static char n2[256];
2228 UrLabelInfo *lbl;
2230 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
2231 memset(n2, 0, sizeof(n2));
2232 strcpy(n2, lblname);
2233 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
2234 // check if this can be an instruction
2235 lbl = urAddLabel(lblname);
2236 if (!lbl->refFile) {
2237 lbl->refLine = 0;
2238 lbl->refFile = strdup("artificially-defined-label");
2240 //fprintf(stderr, "labelDoEQU: <%s>=<%s>\n", lblname, value);
2241 strcpy(currLine, value);
2243 int defined = 1, addr = UR_FIXUP_NONE;
2244 int32_t res = getOneExprArg(&defined, &addr);
2245 lbl->type = LBL_TYPE_EQU;
2246 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2247 if (defined) {
2248 lbl->value = res;
2249 lbl->known = 1;
2250 } else {
2251 return -1; //fatal("can't calculate label %s", lbl->name);
2254 return 0;
2258 static void processLabel (void) {
2259 char *argstart;
2260 char *ep, *nn;
2261 static char n2[256];
2262 UrLabelInfo *lbl;
2263 int noLocAff = 0, doEQU = 0;
2264 //fprintf(stderr, "LINE %5d: <%s> from <%s>\n", (currSrcLine ? currSrcLine->lineNo : 0), currLine, (currSrcLine ? currSrcLine->fname : "<nowhere>"));
2265 memset(n2, 0, sizeof(n2));
2267 currSeenLabel[0] = 0;
2269 if (!currLine[0] || isSpace(currLine[0]) || currLine[0] == ':') {
2270 // this may be " id = smth" or " id equ smth"
2271 ep = currLine;
2272 // skip spaces
2273 while (isSpace(*ep)) ++ep;
2274 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
2275 removeLabel(); // removeLabel() removes any spaces, etc
2276 return;
2278 // this looks like a label; check for '=' or 'equ'
2279 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
2280 nn = ep;
2281 // skip trailing spaces
2282 while (isSpace(*ep)) ++ep;
2283 if (ep[0] == '=') {
2284 doEQU = 0;
2285 argstart = ++ep;
2286 } else if (isSpace(*nn)) {
2287 doEQU = 1;
2288 argstart = strIsCommand("EQU", ep);
2289 } else {
2290 argstart = NULL;
2292 if (!argstart) {
2293 removeLabel(); // removeLabel() removes any spaces, etc
2294 return;
2296 // remove leading spaces from name
2297 // copy label
2298 ep = currLine;
2299 // skip spaces
2300 while (isSpace(*ep)) ++ep;
2301 if (ep >= nn) fatal("internal compiler error");
2302 if (nn-ep > 120) fatal("label too long");
2303 memset(n2, 0, sizeof(n2));
2304 memmove(n2, ep, nn-ep);
2305 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
2306 //fatal("invalid label name");
2307 removeLabel(); // removeLabel() removes any spaces, etc
2308 return;
2310 // remove label name
2311 memmove(currLine, argstart, strlen(argstart)+1);
2312 // append label
2313 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
2314 lbl = findAddLabel(n2);
2315 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2316 if (doEQU && pass == 0 && lbl->type != LBL_TYPE_UNKNOWN) {
2317 fatal("duplicate label '%s'", lbl->name);
2319 int defined = 1, addr = UR_FIXUP_NONE;
2320 int32_t res = getOneExprArg(&defined, &addr);
2321 lbl->type = (doEQU ? LBL_TYPE_EQU : LBL_TYPE_ASS);
2322 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2323 if (defined) {
2324 lbl->value = res;
2325 lbl->known = 1;
2326 } else {
2327 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
2329 currLine[0] = '\0';
2330 return;
2333 // collect label
2334 for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
2335 if (ep-currLine > 120) fatal("label too long");
2337 // copy label
2338 memset(n2, 0, sizeof(n2));
2339 memmove(n2, currLine, ep-currLine);
2340 if (urFindOp(n2)) {
2341 ep = strSkipSpaces(ep);
2342 if (*ep != ':') return; // this must be an instruction, process it
2344 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
2346 // check for macro
2347 if (findMacro(n2)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
2349 // check if this can be instruction
2350 //ep = strSkipSpaces(ep);
2351 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
2352 // ok, we got a good label
2353 removeLabel();
2354 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
2355 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (currSrcLine ? currSrcLine->lineNo : 0));*/
2356 lbl = findAddLabel(n2);
2357 //printf("new: [%s]\n", lbl->name);
2359 // get command name
2360 if (currLine[0] == '=') {
2361 doEQU = 0;
2362 argstart = currLine+1;
2363 } else {
2364 doEQU = 1;
2365 argstart = strIsCommand("EQU", currLine);
2367 if (!argstart || doEQU) {
2368 if (pass == 0 && lbl->type != LBL_TYPE_UNKNOWN) fatal("duplicate label '%s'", lbl->name);
2371 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2372 if (argstart) {
2373 // do '=' or 'EQU'
2374 memmove(currLine, argstart, strlen(argstart)+1);
2375 if (!doEQU) {
2376 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != LBL_TYPE_ASS) {
2377 fatal("duplicate label '%s'", lbl->name);
2380 int defined = 1, addr = UR_FIXUP_NONE;
2381 int32_t res = getOneExprArg(&defined, &addr);
2382 lbl->type = (doEQU ? LBL_TYPE_EQU : LBL_TYPE_ASS);
2383 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2384 if (defined) {
2385 lbl->value = res;
2386 lbl->known = 1;
2387 } else {
2388 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
2390 currLine[0] = '\0';
2391 return;
2394 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
2395 // code label
2396 // update last seen global
2397 if (lbl->name[0] != '{' && lbl->name[0] != '.' && !noLocAff) {
2398 //fprintf(stderr, "**: LASTGLOB: <%s>\n", lbl->name);
2399 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2400 lastSeenGlobalLabel = strdup(lbl->name);
2402 lbl->type = LBL_TYPE_CODE;
2403 lbl->value = disp;
2404 lbl->known = 1;
2405 lbl->fixuptype = UR_FIXUP_WORD;
2407 snprintf(currSeenLabel, sizeof(currSeenLabel), "%s", n2);
2411 ///////////////////////////////////////////////////////////////////////////////
2412 // instruction finder (in source)
2414 /* array ends with NULL */
2415 /* returns line or NULL */
2416 /* iidx will be set to found instruction number */
2417 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
2418 if (iidx) *iidx = -1;
2419 for (SourceLine *cur = currSrcLine; cur; cur = cur->next) {
2420 va_list ap;
2421 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
2422 va_start(ap, iidx);
2423 for (int f = 0; ;++f) {
2424 const char *name = va_arg(ap, const char *);
2426 if (!name) break;
2427 if (strIsCommand(name, cur->line)) {
2428 va_end(ap);
2429 if (iidx) *iidx = f;
2430 return cur;
2433 va_end(ap);
2435 return NULL;
2439 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
2440 return findNextInstructionFromList(NULL, name, NULL);
2444 ///////////////////////////////////////////////////////////////////////////////
2445 // writers
2447 ///////////////////////////////////////////////////////////////////////////////
2448 static void writeFixups (void) {
2449 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
2450 if (cnt > 0) {
2451 int prevaddr = 0;
2452 fprintf(fo, "%s:\n", lbl);
2453 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
2454 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2455 if (chk(fx)) {
2456 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
2457 if (optFixupType == 2) {
2458 prevaddr = fx->opaddr;
2462 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
2466 if (optFixupType == 0) {
2467 /* simple text file */
2468 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
2469 printf("writing fixups to '%s'...\n", fname);
2470 FILE *fo = fopen(fname, "w");
2471 free(fname);
2472 if (fo == NULL) fatal("can't write fixup file");
2473 fprintf(fo, "; addr dadr sz\n");
2474 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2475 static const char type[4] = "NWLH";
2476 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
2478 fclose(fo);
2479 } else {
2480 /* various asm formats */
2481 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
2482 printf("writing fixups to '%s'...\n", fname);
2483 FILE *fo = fopen(fname, "w");
2484 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
2485 free(fname);
2486 if (fo == NULL) fatal("can't write fixup file");
2487 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2488 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
2489 switch (fx->fixuptype) {
2490 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
2491 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
2494 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
2495 return (fx->fixuptype == UR_FIXUP_WORD);
2496 }));
2497 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
2498 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
2499 }));
2500 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
2501 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
2502 }));
2503 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
2504 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
2505 }));
2506 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
2507 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
2508 }));
2509 fclose(fo);
2514 ///////////////////////////////////////////////////////////////////////////////
2515 /* return 'found' flag */
2516 static int findChunkFrom (int addr, int *start, int *len) {
2517 if (addr < 0) addr = 0;
2518 for (; addr <= 65535 && !memused[addr]; ++addr) {}
2519 if (addr > 65535) return 0;
2520 *start = addr;
2521 for (; addr <= 65535 && memused[addr]; ++addr) {}
2522 *len = addr-(*start);
2523 return 1;
2527 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
2528 for (; buflen >= 2; buflen -= 2) {
2529 int cnt = *buf++;
2530 uint8_t byte = *buf++;
2532 if (cnt == 0) {
2533 // copy
2534 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
2535 if (!memused[addr]) putByte(addr, *buf);
2536 if (addr == 0xffff) return;
2538 } else {
2539 // fill
2540 for (; cnt > 0; --cnt, ++addr) {
2541 if (!memused[addr]) putByte(addr, byte);
2542 if (addr == 0xffff) return;
2549 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
2550 if (fwrite(&b, 1, 1, fo) != 1) return -1;
2551 if (bxor) *bxor = (*bxor)^b;
2552 return 0;
2556 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
2557 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
2558 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
2559 return 0;
2563 ///////////////////////////////////////////////////////////////////////////////
2564 // .sna
2566 static int saveSna (const char *fname, int as48) {
2567 char *fn;// = malloc(strlen(fname)+16);
2568 uint8_t regs[27];
2569 FILE *fo;
2570 char abuf[32];
2572 //fn = strprintf("%s/%s.sna", optOutputDir, fname);
2573 fn = buildOutputFileName(fname, "sna");
2574 fo = fopen(fn, "wb");
2575 free(fn);
2576 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
2577 printf("out: %s.sna\n", fname);
2578 memmove(regs, ursna48, 27);
2579 #if 0
2580 if (optRunSNA) {
2581 /* push new address */
2582 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
2583 sp -= 2;
2584 putByte(sp, ent&0xFFU);
2585 putByte(sp+1, (ent>>8)&0xFFU);
2586 regs[23] = sp&0xFFU;
2587 regs[24] = (sp>>8)&0xFFU;
2589 fwrite(regs, 27, 1, fo);
2590 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
2591 fwrite(memory+16384, 49152, 1, fo);
2592 #endif
2594 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
2596 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
2597 //fprintf(stderr, "%d\n", memused[sp]);
2598 if (memused[sp] || memused[(sp+1)&0xffff]) {
2599 fprintf(stderr, "FATAL: can't save snapshot!\n");
2600 goto error;
2603 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
2604 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
2605 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
2607 sprintf(abuf, "%05d", clrAddr);
2608 memcpy(memory+23762, abuf, 5);
2609 sprintf(abuf, "%05d", ent);
2610 memcpy(memory+23773, abuf, 5);
2612 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
2614 if (!as48) {
2615 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
2617 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
2618 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
2619 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
2620 // write pages
2621 memset(memory, 0, 16384);
2622 for (int f = 1; f < 8; ++f) {
2623 if (f != 2 && f != 5) {
2624 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
2629 fclose(fo);
2630 return 0;
2631 error:
2632 fclose(fo);
2633 unlink(fname);
2634 return -1;
2638 ///////////////////////////////////////////////////////////////////////////////
2639 // bin chunks
2641 static void saveRaw (const char *basename) {
2642 char *fname = NULL;// = malloc(strlen(basename)+16);
2643 int start = 0, len;
2644 FILE *fo;
2646 while (findChunkFrom(start, &start, &len)) {
2647 if (fname != NULL) free(fname);
2648 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
2649 fo = fopen(fname, "wb");
2650 if (!fo) {
2651 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
2652 } else {
2653 printf("out: %s\n", fname);
2654 if (fwrite(memory+start, len, 1, fo) != 1) {
2655 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2656 fclose(fo);
2657 unlink(fname);
2658 } else {
2659 fclose(fo);
2662 start += len;
2664 if (fname != NULL) free(fname);
2668 ///////////////////////////////////////////////////////////////////////////////
2669 // HoBeta files
2671 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
2672 char name[8];
2673 char type;
2674 uint16_t start;
2675 uint16_t len;
2676 uint8_t zero; // always zero
2677 uint8_t secLen; // length in sectors
2678 uint16_t checksum;
2679 } HOBHeader;
2682 static uint16_t calcHobSum (const void *hdr) {
2683 const uint8_t *buf = (const uint8_t *)hdr;
2684 uint16_t res = 0;
2686 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
2687 return res;
2691 static void saveHob (const char *basename) {
2692 char *fname = NULL;//malloc(strlen(basename)+16);
2693 int start = 0, len;
2694 HOBHeader hdr;
2695 FILE *fo;
2697 while (findChunkFrom(start, &start, &len)) {
2698 if (fname != NULL) free(fname);
2699 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
2700 fo = fopen(fname, "wb");
2701 if (!fo) {
2702 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
2703 start += len;
2704 } else {
2705 char tmpbuf[sizeof(hdr.name)*2];
2706 printf("out: %s\n", fname);
2707 memset(&hdr, 0, sizeof(hdr));
2708 memset(&hdr.name, 32, 8);
2709 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
2710 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
2711 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
2712 hdr.type = 'C';
2713 hdr.start = start;
2714 hdr.len = len;
2715 hdr.secLen = (len+255)/256;
2716 hdr.checksum = calcHobSum(&hdr);
2717 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
2718 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2719 fclose(fo);
2720 unlink(fname);
2721 goto quit;
2723 if (fwrite(memory+start, len, 1, fo) != 1) {
2724 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2725 fclose(fo);
2726 unlink(fname);
2727 goto quit;
2729 start += len;
2730 while (len%256) {
2731 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
2732 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2733 fclose(fo);
2734 unlink(fname);
2735 goto quit;
2737 ++len;
2738 //fprintf(stderr, ":%d\n", len%256);
2740 fclose(fo);
2743 quit:
2744 if (fname != NULL) free(fname);
2748 // ////////////////////////////////////////////////////////////////////////// //
2749 typedef struct {
2750 uint8_t fcount;
2751 uint8_t dir[14*256];
2752 uint32_t dirpos;
2753 uint32_t currfstartpos;
2754 uint8_t *data;
2755 size_t datapos;
2756 size_t datasize;
2757 } SCLFile;
2760 static const uint8_t monoldr_code[169] = {
2761 0x00,0x00,0xff,0xf0,0xff,0xf0,0xff,0xf0,0xff,0xf3,0x01,0xfd,0x7f,0x3e,0x10,0xed,
2762 0x79,0xaf,0x21,0x00,0x58,0x11,0x01,0x58,0x01,0xff,0x02,0xfb,0x76,0xf3,0xd3,0xfe,
2763 0x36,0x00,0xed,0xb0,0x21,0x00,0x40,0x11,0x01,0x40,0x01,0x00,0x18,0x77,0xed,0xb0,
2764 0xfb,0x76,0xf3,0x3b,0x3b,0xe1,0xe5,0x11,0xce,0xff,0x19,0x7e,0x23,0x5e,0x23,0x56,
2765 0x23,0xc1,0xe5,0xd5,0x69,0x60,0x01,0x35,0x00,0x09,0x4f,0x87,0x81,0x01,0x43,0x00,
2766 0x81,0x4f,0x3e,0x00,0x88,0x47,0xed,0xb0,0xd1,0xe1,0xd5,0x01,0xff,0x03,0x13,0xed,
2767 0xa0,0xed,0xa0,0x13,0x10,0xf8,0xc9,0x31,0xa5,0xa5,0xfb,0x21,0x5a,0x5a,0xe5,0x21,
2768 0x9a,0x02,0xe5,0x76,0x3b,0x3b,0xe1,0x01,0x35,0x00,0x09,0x0e,0x00,0x7e,0xb7,0x28,
2769 0x23,0x23,0x5e,0x23,0x56,0x23,0xe5,0xeb,0x06,0x08,0xb8,0x30,0x01,0x47,0x90,0xf5,
2770 0xc5,0xe5,0xed,0x5b,0xf4,0x5c,0x0e,0x05,0xcd,0x13,0x3d,0xe1,0xc1,0x09,0xf1,0x20,
2771 0xe7,0xe1,0x18,0xd9,0xe1,0xd1,0xf9,0xeb,0xe9,
2775 static void sclInit (SCLFile *scl) {
2776 memset(scl, 0, sizeof(*scl));
2777 scl->datasize = 2*80*16*256; /* maximum disk size */
2778 scl->data = malloc(scl->datasize);
2779 memset(scl->data, 0, scl->datasize);
2780 scl->currfstartpos = 0xffffffffu;
2784 static void sclClear (SCLFile *scl) {
2785 if (!scl) return;
2786 if (scl->data) free(scl->data);
2787 memset(scl, 0, sizeof(*scl));
2788 scl->currfstartpos = 0xffffffffu;
2792 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
2793 if (scl->fcount == 255) {
2794 fprintf(stderr, "FATAL: too many files in SCL!\n");
2795 exit(1);
2797 if (scl->currfstartpos != 0xffffffffu) {
2798 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
2799 exit(1);
2801 int nlen = 0;
2802 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
2803 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
2804 scl->dir[scl->dirpos++] = type;
2805 scl->dir[scl->dirpos++] = v0&0xff;
2806 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
2807 scl->dir[scl->dirpos++] = v1&0xff;
2808 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
2809 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
2810 scl->currfstartpos = scl->datapos;
2814 static void sclEndFile (SCLFile *scl) {
2815 if (scl->currfstartpos == 0xffffffffu) {
2816 fprintf(stderr, "FATAL: last SCL file already finished!\n");
2817 exit(1);
2819 /* align to sector */
2820 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
2821 const uint32_t fsz = scl->datapos-scl->currfstartpos;
2822 if (fsz > 255*256) {
2823 fprintf(stderr, "FATAL: SCL file too big!\n");
2824 exit(1);
2826 ur_assert((fsz&255) == 0);
2827 scl->dir[scl->dirpos++] = fsz/256; /* size in sectors */
2828 ur_assert(scl->dirpos%14 == 0);
2829 ++scl->fcount;
2830 scl->currfstartpos = 0xffffffffu;
2834 static void sclWriteDataForce (SCLFile *scl, const void *buf, size_t len) {
2835 if (!len) return;
2836 if (len > 255*256) {
2837 fprintf(stderr, "FATAL: SCL file too big!\n");
2838 exit(1);
2840 memcpy(scl->data+scl->datapos, buf, len);
2841 scl->datapos += len;
2845 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
2846 if (scl->currfstartpos == 0xffffffffu) {
2847 fprintf(stderr, "FATAL: no open SCL file!\n");
2848 exit(1);
2850 sclWriteDataForce(scl, buf, len);
2854 static void sclWriteByte (SCLFile *scl, uint8_t b) {
2855 sclWriteData(scl, &b, 1);
2859 static void sclWriteWord (SCLFile *scl, uint16_t w) {
2860 uint8_t b = w&0xff;
2861 sclWriteData(scl, &b, 1);
2862 b = (w>>8)&0xff;
2863 sclWriteData(scl, &b, 1);
2867 static int sclFileWrite (FILE *fo, const void *buf, size_t count, uint32_t *checksum) {
2868 if (!count) return 0;
2869 const uint8_t *p = (const uint8_t *)buf;
2870 if (checksum) for (size_t n = count; n--; ++p) *checksum += (uint32_t)(*p);
2871 if (fwrite(buf, count, 1, fo) != 1) return -1;
2872 return 0;
2876 // <0: error; 0: ok
2877 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
2878 if (scl->currfstartpos != 0xffffffffu) {
2879 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
2880 exit(1);
2882 const char *sign = "SINCLAIR";
2883 uint32_t checksum = 0;
2884 // header
2885 if (sclFileWrite(fo, sign, 8, &checksum) != 0) return -1;
2886 if (sclFileWrite(fo, &scl->fcount, 1, &checksum) != 0) return -1;
2887 // directory
2888 if (sclFileWrite(fo, scl->dir, scl->dirpos, &checksum) != 0) return -1;
2889 // data
2890 if (sclFileWrite(fo, scl->data, scl->datapos, &checksum) != 0) return -1;
2891 // write checksum
2892 for (unsigned f = 0; f < 4; ++f) {
2893 const uint8_t b = (checksum>>(f*8))&0xff;
2894 if (fwrite(&b, 1, 1, fo) != 1) return -1;
2896 // done
2897 return 0;
2901 // ////////////////////////////////////////////////////////////////////////// //
2902 static void saveSCLCargador (SCLFile *scl) {
2903 static uint8_t cargador[16384]; // should be enough for everyone
2904 int linestart = -1;
2905 int linenum = 0;
2906 int start = 0, len, pos;
2908 void putStr (const char *s) {
2909 for (; *s; ++s) cargador[pos++] = *s;
2912 void putNum (int num) {
2913 char buf[64];
2914 sprintf(buf, "%d", num);
2915 putStr(buf);
2918 void startLine () {
2919 ++linenum;
2920 linestart = pos;
2921 // number
2922 cargador[pos++] = (linenum>>8)&0xff;
2923 cargador[pos++] = linenum&0xff;
2924 // size (will be fixed later)
2925 cargador[pos++] = 0;
2926 cargador[pos++] = 0;
2929 void flushLine () {
2930 if (linestart >= 0) {
2931 const int size = pos-linestart-4;
2932 cargador[linestart+2] = size&0xff;
2933 cargador[linestart+3] = (size>>8)&0xff;
2935 linestart = -1;
2938 char cname[16];
2940 pos = 0;
2941 startLine();
2942 // CLEAR VAL "xxx"
2943 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
2945 // load blocks
2946 int cont = 1;
2947 while (findChunkFrom(start, &start, &len)) {
2948 // :RANDOMIZE USR VAL "15619":REM:LOAD "
2949 if (cont) { putStr(":"); cont = 0; }
2950 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
2951 // generate chunk name
2952 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
2953 putStr(cname);
2954 //" CODE
2955 putStr("\"\xaf\r");
2956 flushLine();
2957 startLine();
2958 start += len;
2961 // load code files
2962 for (OutDataFile *off = datafiles; off; off = off->next) {
2963 if (off->type == 1) continue; // skip +3DOS bootsector
2964 if (off->type != 'C') continue;
2965 if (!off->hasstart) continue;
2966 // :RANDOMIZE USR VAL "15619":REM:LOAD "
2967 if (cont) { putStr(":"); cont = 0; }
2968 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
2969 // generate chunk name
2970 snprintf(cname, sizeof(cname), "%s", off->name);
2971 char *dotpos = strchr(cname, '.');
2972 char type = 'C';
2973 if (dotpos) { type = toUpper(dotpos[1]); *dotpos = 0; }
2974 if (type < 'A' || type > 'Z') type = 'C';
2975 for (char *s = cname; *s; ++s) *s = toUpper(*s);
2976 putStr(cname);
2977 //" CODE
2978 putStr("\"\xaf\r");
2979 flushLine();
2980 startLine();
2983 // and run
2984 if (cont) { putStr(":"); cont = 0; }
2985 // RANDOMIZE USR VAL "xxx"
2986 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
2987 putStr("\r");
2988 flushLine();
2990 //putWord(0xaa80);
2991 //putWord(1); // autostart line
2993 // write to SCL
2994 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
2995 sclWriteData(scl, cargador, (size_t)pos);
2996 sclWriteByte(scl, 0x80);
2997 sclWriteByte(scl, 0xaa);
2998 sclWriteWord(scl, 1);
2999 sclEndFile(scl);
3003 static void saveSCLCargadorMono (SCLFile *scl) {
3004 static uint8_t cargador[16384]; // should be enough for everyone
3005 int linestart = -1;
3006 int linenum = 0;
3007 int start = 0, len, pos;
3009 void putDB (uint8_t v) {
3010 cargador[pos++] = v;
3013 void putDW (uint16_t v) {
3014 cargador[pos++] = v&0xff;
3015 cargador[pos++] = (v>>8)&0xff;
3018 void putStr (const char *s) {
3019 for (; *s; ++s) cargador[pos++] = *s;
3022 void putNum (int num) {
3023 char buf[64];
3024 sprintf(buf, "%d", num);
3025 putStr(buf);
3028 void startLine () {
3029 linestart = pos;
3030 // number
3031 cargador[pos++] = (linenum>>8)&0xff;
3032 cargador[pos++] = linenum&0xff;
3033 // size (will be fixed later)
3034 cargador[pos++] = 0;
3035 cargador[pos++] = 0;
3036 linenum++;
3039 void flushLine () {
3040 if (linestart >= 0) {
3041 const int size = pos-linestart-4;
3042 cargador[linestart+2] = size&0xff;
3043 cargador[linestart+3] = (size>>8)&0xff;
3045 linestart = -1;
3048 pos = 0;
3049 startLine();
3051 // 0 REM code
3052 putStr("\xea");
3053 // monoloader parameters:
3054 // dw ldr_copy_addr
3055 // dw ldr_sp
3056 // dw prg_start_addr
3057 // dw prg_sp
3058 // block count
3059 const int bcountpos = pos;
3060 putDB(0);
3061 putDW(0x4000); // to screen$
3062 putDW(0x4200); // stack
3063 putDW(ent); // program start address
3064 putDW(0x0000); // stack
3065 // copy monoloader
3066 for (uint32_t f = 9; f < (uint32_t)sizeof(monoldr_code); ++f) {
3067 putDB(monoldr_code[f]);
3069 // put block data
3070 while (findChunkFrom(start, &start, &len)) {
3071 if (len > 255*256) fatal("code chunk too big");
3072 // sector count
3073 putDB(len/256+(len%256 ? 1 : 0));
3074 // address
3075 putDW(start);
3076 start += len;
3077 // one more block
3078 ++cargador[bcountpos];
3080 // load code files
3081 for (OutDataFile *off = datafiles; off; off = off->next) {
3082 if (off->type == 1) continue; // skip +3DOS bootsector
3083 if (off->type != 'C') continue;
3084 if (!off->hasstart) continue;
3085 if (off->size > 255*256) fatal("data chunk too big");
3086 // sector count
3087 putDB(off->size/256+(off->size%256 ? 1 : 0));
3088 // address
3089 putDW(off->start);
3090 // one more block
3091 ++cargador[bcountpos];
3093 // no more blocks
3094 putDB(0);
3096 flushLine();
3097 startLine();
3099 // CLEAR VAL "xxx":RANDOMIZE USR PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"+VAL "14"
3100 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\":");
3101 putStr("\xf9\xc0(\xbe\xb0\"23635\"+\xb0\"256\"*\xbe\xb0\"23636\"+\xb0\"14\")\r");
3102 flushLine();
3104 // write to SCL
3105 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
3106 sclWriteData(scl, cargador, (size_t)pos);
3107 sclWriteByte(scl, 0x80);
3108 sclWriteByte(scl, 0xaa);
3109 sclWriteWord(scl, 1);
3110 sclEndFile(scl);
3112 // put block data
3113 uint8_t ssz = 0;
3114 start = 0;
3115 while (findChunkFrom(start, &start, &len)) {
3116 ssz += len/256+(len%256 ? 1 : 0);
3117 sclWriteDataForce(scl, memory+start, len);
3118 /* align to sector */
3119 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3120 start += len;
3122 for (unsigned dphase = 0; dphase < 2; ++dphase) {
3123 // put code files
3124 for (OutDataFile *off = datafiles; off; off = off->next) {
3125 if (off->type == 1) continue; // skip +3DOS bootsector
3126 if (off->type != 'C') continue;
3127 if (dphase == 0) {
3128 if (!off->hasstart) continue;
3129 } else {
3130 if (off->hasstart) continue;
3132 if (off->size > 255*256) fatal("data chunk too big");
3133 ssz += off->size/256+(off->size%256 ? 1 : 0);
3134 sclWriteDataForce(scl, off->data, off->size);
3135 /* align to sector */
3136 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3140 // fix basic file sector size
3141 scl->dir[scl->dirpos-1] += ssz;
3145 static void saveSCL (const char *basename) {
3146 SCLFile scl;
3147 int fcount = 0;
3148 int start, len;
3149 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3150 char *fname = buildOutputFileName(basename, "scl");
3152 // count files
3153 start = 0;
3154 while (findChunkFrom(start, &start, &len)) {
3155 ++fcount;
3156 start += len;
3158 // count data files
3159 for (OutDataFile *off = datafiles; off; off = off->next) {
3160 if (off->type == 1) continue; // skip +3DOS bootsector
3161 if (off->type == 'C') ++fcount;
3164 if (fcount && optRunSCL) fcount += (optRunSCL > 0 ? 2 : 1); // +loader and boot
3165 if (fcount > 255) {
3166 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
3167 exit(1);
3170 // create output file
3171 FILE *fo = fopen(fname, "wb");
3172 if (!fo) {
3173 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
3174 exit(1);
3177 // initialise SCL writer
3178 sclInit(&scl);
3179 // write loader
3180 if (fcount && optRunSCL) {
3181 // create simple boot
3182 if (optRunSCL > 0) {
3183 const uint8_t dasboot[] = {
3184 0xf9,0xc0,0xb0,'"','1','5','6','1','9','"',':',0xea,':',0xef,'"','C','A','R','G','A','D','O','R','"','\r', //RANDOMIZE USR VAL "15619":REM:LOAD "CARGADOR"
3186 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
3187 sclWriteWord(&scl, 1); // line number
3188 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
3189 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
3190 // TR-DOS signature
3191 sclWriteByte(&scl, 0x80);
3192 sclWriteByte(&scl, 0xaa);
3193 // start line
3194 sclWriteWord(&scl, 0);
3195 sclEndFile(&scl);
3197 saveSCLCargador(&scl);
3200 // write chunks
3201 char cname[16];
3202 start = 0;
3203 while (findChunkFrom(start, &start, &len)) {
3204 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
3205 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
3206 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
3207 sclEndFile(&scl);
3208 start += len;
3211 // write data files
3212 for (OutDataFile *off = datafiles; off; off = off->next) {
3213 if (off->type == 1) continue; // skip +3DOS bootsector
3214 if (off->type != 'C') {
3215 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3216 continue;
3218 snprintf(cname, sizeof(cname), "%s", off->name);
3219 char *dotpos = strchr(cname, '.');
3220 char type = 'C';
3221 if (dotpos) { type = toUpper(dotpos[1]); *dotpos = 0; }
3222 if (type < 'A' || type > 'Z') type = 'C';
3223 for (char *s = cname; *s; ++s) *s = toUpper(*s);
3224 sclStartFile(&scl, cname, type, (unsigned)off->start, (unsigned)off->size);
3225 sclWriteData(&scl, off->data, (unsigned)off->size);
3226 sclEndFile(&scl);
3229 if (sclSaveToFile(fo, &scl) < 0) {
3230 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
3231 fclose(fo);
3232 unlink(fname);
3233 exit(1);
3235 sclClear(&scl);
3236 fclose(fo);
3237 printf("out: %s\n", fname);
3238 if (fname != NULL) free(fname);
3242 static void saveSCLMono (const char *basename) {
3243 SCLFile scl;
3244 int start, len;
3245 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3246 char *fname = buildOutputFileName(basename, "scl");
3248 // count total size in sectors
3249 int secsize = 1; // for loader
3250 start = 0;
3251 while (findChunkFrom(start, &start, &len)) {
3252 secsize += len/256+(len%256 ? 1 : 0);
3253 start += len;
3255 // count data files
3256 for (OutDataFile *off = datafiles; off; off = off->next) {
3257 if (off->type == 1) continue; // skip +3DOS bootsector
3258 if (off->type == 'C') {
3259 secsize += off->size/256+(off->size%256 ? 1 : 0);
3263 if (secsize > 254) {
3264 fprintf(stderr, "ERROR: can't write file '%s' (too many sectors: %d)!\n", fname, secsize);
3265 exit(1);
3268 // create output file
3269 FILE *fo = fopen(fname, "wb");
3270 if (!fo) {
3271 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
3272 exit(1);
3275 // initialise SCL writer
3276 sclInit(&scl);
3277 // write loader
3278 if (optRunSCL) {
3279 // create simple boot
3280 if (optRunSCL > 0) {
3281 const uint8_t dasboot[] = {
3282 0xf9,0xc0,0xb0,'"','1','5','6','1','9','"',':',0xea,':',0xef,'"','C','A','R','G','A','D','O','R','"','\r', //RANDOMIZE USR VAL "15619":REM:LOAD "CARGADOR"
3284 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
3285 sclWriteWord(&scl, 1); // line number
3286 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
3287 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
3288 // TR-DOS signature
3289 sclWriteByte(&scl, 0x80);
3290 sclWriteByte(&scl, 0xaa);
3291 // start line
3292 sclWriteWord(&scl, 0);
3293 sclEndFile(&scl);
3296 saveSCLCargadorMono(&scl);
3298 if (sclSaveToFile(fo, &scl) < 0) {
3299 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
3300 fclose(fo);
3301 unlink(fname);
3302 exit(1);
3304 sclClear(&scl);
3305 fclose(fo);
3306 printf("out: %s\n", fname);
3307 if (fname != NULL) free(fname);
3311 // ////////////////////////////////////////////////////////////////////////// //
3312 int optRunDSK = 0;
3313 int optDskType = P3DSK_PCW_SS;
3316 static int saveDSKCargador (P3DiskInfo *p3d, int autorun) {
3317 static uint8_t cargador[16384]; // should be enough for everyone
3318 int linestart = -1;
3319 int linenum = 0;
3320 int start = 0, len, pos;
3322 void putStr (const char *s) {
3323 for (; *s; ++s) cargador[pos++] = *s;
3326 void putNum (int num) {
3327 char buf[64];
3328 sprintf(buf, "%d", num);
3329 putStr(buf);
3332 void startLine () {
3333 ++linenum;
3334 linestart = pos;
3335 // number
3336 cargador[pos++] = (linenum>>8)&0xff;
3337 cargador[pos++] = linenum&0xff;
3338 // size (will be fixed later)
3339 cargador[pos++] = 0;
3340 cargador[pos++] = 0;
3343 void flushLine () {
3344 if (linestart >= 0) {
3345 const int size = pos-linestart-4;
3346 cargador[linestart+2] = size&0xff;
3347 cargador[linestart+3] = (size>>8)&0xff;
3349 linestart = -1;
3352 char cname[16];
3354 pos = 0;
3355 startLine();
3356 // CLEAR VAL "xxx"
3357 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
3358 // load blocks
3359 while (findChunkFrom(start, &start, &len)) {
3360 // :LOAD "
3361 putStr(":\xef\"");
3362 // generate chunk name
3363 snprintf(cname, sizeof(cname), "C%04X.BIN", (unsigned)start);
3364 putStr(cname);
3365 // " CODE
3366 putStr("\"\xaf");
3367 start += len;
3369 putStr("\r");
3370 flushLine();
3371 startLine();
3372 // and run
3373 // :RANDOMIZE USR VAL "xxx"
3374 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
3375 flushLine();
3377 // write to DSK
3378 // build cp/m 8.3 name
3379 char pdfname[16];
3380 if (autorun) strcpy(pdfname, "DISK"); else strcpy(pdfname, "CARGADOR.BAS");
3382 int crerr = p3dskCreateFile(p3d, pdfname);
3383 if (crerr < 0) {
3384 if (crerr == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3385 if (crerr != FLPERR_FILEEXIST) { fprintf(stderr, "+3DOS disk error (creating)\n"); return -1; }
3386 p3dskDeleteFiles(p3d, pdfname);
3387 crerr = p3dskCreateFile(p3d, pdfname);
3388 if (crerr < 0) { fprintf(stderr, "+3DOS disk error (creating1)\n"); return -1; }
3391 P3DskFileInfo nfo;
3392 if (p3dskOpenFile(p3d, &nfo, pdfname) != FLPERR_OK) { fprintf(stderr, "cannot open created +3DOS file '%s'\n", pdfname); return -1; }
3394 int wrres = p3dskWriteFile(&nfo, cargador, 128, (int)pos);
3395 if (wrres == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3396 if (wrres == FLPERR_NOSPACE) { fprintf(stderr, "NO FREE SPACE (writing)!\n"); return -1; }
3397 if (wrres < 0) { fprintf(stderr, "+3DOS disk writing error (writing)\n"); return -1; }
3399 // write +3DOS header
3400 P3DskFileHeader hdr;
3401 memset(&hdr, 0, sizeof(P3DskFileHeader));
3402 hdr.valid = 1;
3403 hdr.issue = 1;
3404 hdr.version = 0;
3405 hdr.filesize = (uint32_t)(pos+128);
3406 hdr.bastype = 0; // basic
3407 hdr.baslength = (uint16_t)pos;
3408 hdr.basaddr = 1;
3409 hdr.basvarsofs = (uint16_t)pos;
3410 if (p3dskWriteFileHeader(&nfo, &hdr) != FLPERR_OK) { fprintf(stderr, "error writing +3DOS file header\n"); return -1; }
3412 return 0;
3416 static int saveDSKChunk (P3DiskInfo *p3d, const char *name, const void *data, int start, int len, int p3header) {
3417 // write to DSK
3418 // build cp/m 8.3 name
3419 char pdfname[13];
3420 if (p3dskNormaliseFileName(pdfname, name) != FLPERR_OK) { fprintf(stderr, "bad +3DOS file name '%s'\n", name); return -1; }
3422 for (char *s = pdfname; *s; ++s) if (*s >= 'a' && *s <= 'z') s[0] -= 32;
3424 int crerr = p3dskCreateFile(p3d, pdfname);
3425 if (crerr < 0) {
3426 if (crerr == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3427 if (crerr != FLPERR_FILEEXIST) { fprintf(stderr, "+3DOS disk error (creating)\n"); return -1; }
3428 p3dskDeleteFiles(p3d, pdfname);
3429 crerr = p3dskCreateFile(p3d, pdfname);
3430 if (crerr < 0) { fprintf(stderr, "+3DOS disk error (creating1)\n"); return -1; }
3433 P3DskFileInfo nfo;
3434 if (p3dskOpenFile(p3d, &nfo, pdfname) != FLPERR_OK) { fprintf(stderr, "cannot open created +3DOS file '%s'\n", pdfname); return -1; }
3436 int wrres = p3dskWriteFile(&nfo, data, (p3header ? 128 : 0), (int)len);
3437 if (wrres == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3438 if (wrres == FLPERR_NOSPACE) { fprintf(stderr, "NO FREE SPACE (writing)!\n"); return -1; }
3439 if (wrres < 0) { fprintf(stderr, "+3DOS disk writing error (writing)\n"); return -1; }
3441 // write +3DOS header
3442 if (p3header) {
3443 P3DskFileHeader hdr;
3444 memset(&hdr, 0, sizeof(P3DskFileHeader));
3445 hdr.valid = 1;
3446 hdr.issue = 1;
3447 hdr.version = 0;
3448 hdr.filesize = (uint32_t)(len+128);
3449 hdr.bastype = 3; // code
3450 hdr.baslength = (uint16_t)len;
3451 hdr.basaddr = (uint16_t)start;
3452 hdr.basvarsofs = 0;
3453 if (p3dskWriteFileHeader(&nfo, &hdr) != FLPERR_OK) { fprintf(stderr, "error writing +3DOS file header\n"); return -1; }
3456 return 0;
3460 static void saveDSK (const char *basename) {
3461 // create and format the floppy
3462 Floppy *flp = flpCreate(0);
3463 if (p3dskFormatDisk(flp, optDskType) != FLPERR_OK) {
3464 flpDestroy(flp);
3465 fprintf(stderr, "ERROR: cannot format +3DOS disk\n");
3466 return;
3469 P3DiskInfo p3d;
3470 p3d.flp = flp;
3471 if (p3dskDetectGeom(p3d.flp, &p3d.geom) < 0) {
3472 flpDestroy(flp);
3473 fprintf(stderr, "ERROR: not a +3DOS disk!\n");
3474 return;
3477 int hasbootsector = 0;
3478 for (OutDataFile *off = datafiles; off; off = off->next) {
3479 if (off->type == 1) { hasbootsector = 1; break; }
3482 int start = 0, len;
3483 int haschunks = findChunkFrom(start, &start, &len);
3485 if (!hasbootsector && haschunks) {
3486 if (saveDSKCargador(&p3d, optRunDSK) != 0) {
3487 flpDestroy(flp);
3488 return;
3492 // write chunks
3493 start = 0;
3494 while (findChunkFrom(start, &start, &len)) {
3495 char cname[16];
3496 snprintf(cname, sizeof(cname), "C%04X.BIN", (unsigned)start);
3497 if (saveDSKChunk(&p3d, cname, memory+(unsigned)start, start, len, 1) != 0) {
3498 flpDestroy(flp);
3499 return;
3501 start += len;
3504 // write data files
3505 for (OutDataFile *off = datafiles; off; off = off->next) {
3506 if (off->type == 1) {
3507 // +3DOS bootsector
3508 if (!p3d.geom.restracks) {
3509 fprintf(stderr, "no room for +3DOS bootsector\n");
3510 continue;
3512 uint8_t *bootsec = malloc(512);
3513 if (flpGetSectorData(p3d.flp, 0, p3d.geom.firstsector, bootsec, 512) != FLPERR_OK) {
3514 fprintf(stderr, "cannot read +3DOS bootsector\n");
3515 continue;
3517 size_t clen = off->size;
3518 if (clen > 512-16) clen = 512-16;
3519 if (clen) memcpy(bootsec+16, off->data, clen);
3520 if (flpPutSectorData(p3d.flp, 0, p3d.geom.firstsector, bootsec, 512) != FLPERR_OK) fatal("cannot write +3DOS bootsector");
3521 if (p3dskWriteBootableChecksum(&p3d) != FLPERR_OK) fatal("cannot make +3DOS disk bootable");
3522 printf("written +3DOS bootsector (%u bytes of code)\n", off->size);
3523 continue;
3526 if (off->type != 'C') {
3527 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3528 continue;
3530 char pdfname[13];
3531 if (p3dskNormaliseFileName(pdfname, off->name) != FLPERR_OK) {
3532 fprintf(stderr, "skipping data file with invalid name '%s'\n", off->name);
3533 continue;
3535 saveDSKChunk(&p3d, pdfname, off->data, off->start, off->size, off->hasstart);
3538 // write disk
3539 //char *fname = strprintf("%s/%s.dsk", optOutputDir, basename);
3540 char *fname = buildOutputFileName(basename, "dsk");
3541 FILE *fo = fopen(fname, "wb");
3542 if (!fo) { fprintf(stderr, "cannot create disk file '%s'\n", fname); free(fname); flpDestroy(flp); return; }
3543 if (dskSaveDSK(flp, fo) != FLPERR_OK) {
3544 unlink(fname);
3545 fprintf(stderr, "error writing disk file '%s'\n", fname);
3546 free(fname);
3547 flpDestroy(flp);
3548 return;
3550 fclose(fo);
3551 printf("out: %s\n", fname);
3552 free(fname);
3553 flpDestroy(flp);
3557 ///////////////////////////////////////////////////////////////////////////////
3558 // .dmb
3560 static int saveDMB (const char *fname) {
3561 char *fn;// = malloc(strlen(fname)+16);
3562 int start = 0, len;
3563 uint16_t pcnt = 0;
3564 FILE *fo;
3566 //fn = strprintf("%s/%s.dmb", optOutputDir, fname);
3567 fn = buildOutputFileName(fname, "dmb");
3568 fo = fopen(fn, "wb");
3569 free(fn);
3570 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
3572 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
3574 printf("out: %s.dmb\n", fname);
3575 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
3576 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
3577 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
3578 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
3580 start = 0;
3581 while (findChunkFrom(start, &start, &len)) {
3582 if (fWriteWord(fo, len, NULL) != 0) goto error;
3583 if (fWriteWord(fo, start, NULL) != 0) goto error;
3584 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
3585 start += len;
3587 fclose(fo);
3588 return 0;
3589 error:
3590 fclose(fo);
3591 unlink(fname);
3592 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
3593 return -1;
3597 ///////////////////////////////////////////////////////////////////////////////
3598 // .tap
3600 static char tapeLoaderName[16];
3603 static void saveTapCargador (FILE *fo) {
3604 // count blocks
3605 static uint8_t cargador[16384]; // should be enough for everyone
3606 int start = 0, len, pos, f;
3607 uint8_t bxor;
3610 void putStr (const char *s) {
3611 for (; *s; ++s) cargador[pos++] = *s;
3614 void putNum (int num) {
3615 char buf[64];
3616 sprintf(buf, "%d", num);
3617 putStr(buf);
3621 pos = 4;
3622 // number
3623 cargador[0] = 0; cargador[1] = 10;
3624 // size (will be fixed later)
3625 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
3626 // load blocks
3627 while (findChunkFrom(start, &start, &len)) {
3628 // :LOAD "" CODE
3629 putStr(":\xef\"\"\xaf");
3630 start += len;
3632 // additional code files
3633 for (OutDataFile *off = datafiles; off; off = off->next) {
3634 if (off->type == 1) continue; // skip +3DOS bootsector
3635 if (off->type != 'C' || off->size > 65533 || !off->hasstart) {
3636 continue;
3638 // :LOAD "" CODE
3639 putStr(":\xef\"\"\xaf");
3640 start += len;
3642 // and run
3643 // :RANDOMIZE USR VAL "xxx"
3644 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
3645 // patch len
3646 cargador[2] = (pos-4)&0xff;
3647 cargador[3] = ((pos-4)>>8)&0xff;
3648 // write header
3649 fWriteWord(fo, 19, NULL); // length of header
3650 bxor = 0;
3651 fWriteByte(fo, 0, &bxor); // header block
3652 fWriteByte(fo, 0, &bxor); // 'basic' flag
3653 if (tapeLoaderName[0]) {
3654 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3655 } else {
3656 fWriteByte(fo, 'c', &bxor);
3657 fWriteByte(fo, 'a', &bxor);
3658 fWriteByte(fo, 'r', &bxor);
3659 fWriteByte(fo, 'g', &bxor);
3660 fWriteByte(fo, 'a', &bxor);
3661 fWriteByte(fo, 'd', &bxor);
3662 fWriteByte(fo, 'o', &bxor);
3663 fWriteByte(fo, 'r', &bxor);
3664 fWriteByte(fo, ' ', &bxor);
3665 fWriteByte(fo, ' ', &bxor);
3667 fWriteWord(fo, pos, &bxor); // length
3668 fWriteWord(fo, 10, &bxor); // start
3669 fWriteWord(fo, pos, &bxor); // length2
3670 fWriteByte(fo, bxor, NULL);
3671 // write data
3672 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
3673 bxor = 0;
3674 fWriteByte(fo, 0xFFU, &bxor); // data block
3675 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
3676 fWriteByte(fo, bxor, NULL);
3680 static void saveTap (const char *basename) {
3681 char *fname;// = malloc(strlen(basename)+16);
3682 char blkname[128];
3683 int start = 0, len, f;
3684 uint8_t bxor;
3685 FILE *fo;
3687 //fname = strprintf("%s/%s.tap", optOutputDir, basename);
3688 fname = buildOutputFileName(basename, "tap");
3689 fo = fopen(fname, "wb");
3690 free(fname);
3691 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
3692 printf("out: %s.tap\n", basename);
3693 if (optRunTape) saveTapCargador(fo);
3694 while (findChunkFrom(start, &start, &len)) {
3695 // write header
3696 if (tapeLoaderName[0]) {
3697 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3698 memcpy(blkname, tapeLoaderName, 10);
3699 } else {
3700 sprintf(blkname, "c%04X:%04X", start, len);
3702 //printf(" block: %s\n", blkname);
3703 fWriteWord(fo, 19, NULL); // length of header
3704 bxor = 0;
3705 fWriteByte(fo, 0, &bxor); // header block
3706 fWriteByte(fo, 3, &bxor); // 'code' flag
3707 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
3708 fWriteWord(fo, len, &bxor);
3709 fWriteWord(fo, start, &bxor);
3710 fWriteWord(fo, 32768, &bxor);
3711 fWriteByte(fo, bxor, NULL);
3712 // write data
3713 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
3714 bxor = 0;
3715 fWriteByte(fo, 0xFFU, &bxor); // data block
3716 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
3717 fWriteByte(fo, bxor, NULL);
3718 start += len;
3721 for (int dfpass = 0; dfpass < 2; ++dfpass) {
3722 // write data files
3723 for (OutDataFile *off = datafiles; off; off = off->next) {
3724 if (off->type == 1) continue; // skip +3DOS bootsector
3725 if (off->type != 'C' || off->size > 65533) {
3726 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3727 continue;
3729 if (dfpass == 0) {
3730 if (!off->hasstart) continue;
3731 } else {
3732 if (off->hasstart) continue;
3734 snprintf(blkname, 10, "%s", off->name);
3735 blkname[10] = 0; // just in case
3736 while (strlen(blkname) < 10) strcat(blkname, " ");
3737 // write data block
3738 fWriteWord(fo, 19, NULL); // length of header
3739 bxor = 0;
3740 fWriteByte(fo, 0, &bxor); // header block
3741 fWriteByte(fo, 3, &bxor); // 'code' flag
3742 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
3743 fWriteWord(fo, off->size, &bxor);
3744 fWriteWord(fo, off->start, &bxor);
3745 fWriteWord(fo, 32768, &bxor);
3746 fWriteByte(fo, bxor, NULL);
3747 // write data
3748 fWriteWord(fo, off->size+2, NULL); // plus type and checkbyte
3749 bxor = 0;
3750 fWriteByte(fo, 0xFFU, &bxor); // data block
3751 for (f = 0; f < off->size; ++f) fWriteByte(fo, off->data[f], &bxor);
3752 fWriteByte(fo, bxor, NULL);
3756 fclose(fo);
3760 ///////////////////////////////////////////////////////////////////////////////
3761 // pseudoinstructions
3763 // note that processCurrentLine() will NOT skip to the next line before
3764 // calling pseudoinstruction handler!
3765 // note that processCurrentLine() will skip current line after calling
3766 // pseudoinstruction handler!
3768 static int wasOrg = 0;
3769 static int wasClr = 0;
3772 // ////////////////////////////////////////////////////////////////////////// //
3773 // print message using printf-like syntax
3774 // doesn't print new line
3775 static void processPrintf (FILE *fo, const char *fmt) {
3776 if (!fmt || !fmt[0]) return;
3777 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
3778 char *tempstr = NULL;
3779 size_t tempsize = 0;
3780 char *fmtcopy = malloc(strlen(fmt)+1);
3781 strcpy(fmtcopy, fmt);
3782 char *currfmt = fmtcopy;
3783 char tempbuf[512];
3784 int stlen;
3785 char *strarg;
3786 int defined;
3787 int32_t exprval;
3788 while (*currfmt) {
3789 /* find '%' */
3790 char *prcs = strchr(currfmt, '%');
3791 if (!prcs || !prcs[1]) {
3792 /* no more formatting; print the tail and exit the loop */
3793 fprintf(fo, "%s", currfmt);
3794 break;
3796 /* is this "%%"? */
3797 int docheck = 1;
3798 if (prcs[1] == '%') {
3799 ++prcs;
3800 docheck = 0;
3802 /* print up to `prcs` */
3803 if (prcs > currfmt) {
3804 size_t partlen = (ptrdiff_t)(prcs-currfmt);
3805 if (partlen+1 > tempsize) {
3806 tempsize = ((partlen+8)|0xff)+1;
3807 tempstr = realloc(tempstr, tempsize);
3809 memcpy(tempstr, currfmt, partlen);
3810 tempstr[partlen] = 0;
3811 fprintf(fo, "%s", tempstr);
3813 currfmt = ++prcs; /* skip percent */
3814 if (!docheck) continue;
3815 /* parse format */
3816 char sign = ' ';
3817 int width = 0;
3818 int zerofill = 0;
3819 char ftype = ' ';
3820 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
3821 if (sign != '-' && *currfmt == '0') zerofill = 1;
3822 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
3823 if (width > 256) width = 256;
3824 ftype = *currfmt++;
3825 if (!ftype) break; /* oops */
3826 if (!eatComma()) fatal("out of arguments for string format");
3827 switch (ftype) {
3828 case 's': /* string */
3829 stlen = 0;
3830 switch (strSkipSpaces(currLine)[0]) {
3831 case '"': case '\'': /* string literal? */
3832 strarg = getStrArg(&stlen);
3833 break;
3834 default: /* expression */
3835 strarg = getStrExprArgFmt();
3836 stlen = (int)strlen(strarg);
3837 break;
3839 /* left pad */
3840 if (sign != '-' && stlen < width) {
3841 int padlen = width-stlen;
3842 memset(tempbuf, ' ', padlen);
3843 tempbuf[padlen+1] = 0;
3844 fprintf(fo, "%s", tempbuf);
3846 fprintf(fo, "%s", strarg);
3847 /* right pad */
3848 if (sign == '-' && stlen < width) {
3849 int padlen = width-stlen;
3850 memset(tempbuf, ' ', padlen);
3851 tempbuf[padlen+1] = 0;
3852 fprintf(fo, "%s", tempbuf);
3854 break;
3855 case 'd': /* decimal */
3856 defined = 1;
3857 exprval = getExprArg(&defined, NULL);
3858 if (width && zerofill) {
3859 fprintf(fo, "%0*d", width, exprval);
3860 } else if (width) {
3861 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
3862 } else {
3863 fprintf(fo, "%d", exprval);
3865 break;
3866 case 'c': /* char */
3867 defined = 1;
3868 exprval = getExprArg(&defined, NULL);
3869 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
3870 if (width) {
3871 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
3872 } else {
3873 fprintf(fo, "%c", exprval);
3875 break;
3876 case 'u': /* unsigned */
3877 defined = 1;
3878 exprval = getExprArg(&defined, NULL);
3879 if (width && zerofill) {
3880 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
3881 } else if (width) {
3882 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
3883 } else {
3884 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
3886 break;
3887 case 'x': case 'X': /* hex */
3888 defined = 1;
3889 exprval = getExprArg(&defined, NULL);
3890 if (width && zerofill) {
3891 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
3892 } else if (width) {
3893 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
3894 } else {
3895 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
3897 break;
3898 default:
3899 if (ftype <= 0 || ftype == 127) ftype = '?';
3900 fatal("invalid format specifier: '%c'", ftype);
3901 break;
3904 if (tempstr) free(tempstr);
3905 free(fmtcopy);
3909 ///////////////////////////////////////////////////////////////////////////////
3910 // user error
3912 static int piERROR (void) {
3913 int len = 0;
3914 char *res = getStrArg(&len);
3915 fprintf(stdout, "*** USER ERROR: ");
3916 processPrintf(stdout, res);
3917 fputc('\n', stdout);
3918 fatal("user error abort");
3919 return PI_SKIP_LINE;
3923 static int piWARNING (void) {
3924 int len = 0;
3925 char *res = getStrArg(&len);
3926 fprintf(stdout, "*** USER WARNING ");
3927 if (currSrcLine) {
3928 fprintf(stdout, "at file %s, line %d: ", currSrcLine->fname, currSrcLine->lineNo);
3929 } else {
3930 fprintf(stdout, "somewhere in time: ");
3932 processPrintf(stdout, res);
3933 fputc('\n', stdout);
3934 return PI_SKIP_LINE;
3938 ///////////////////////////////////////////////////////////////////////////////
3939 // user warnings
3941 static int piPrintfCommon (int passNo) {
3942 if (passNo < 0 || pass == passNo) {
3943 int len = 0;
3944 char *res = getStrArg(&len);
3945 processPrintf(stdout, res);
3946 fputc('\n', stdout);
3948 return PI_SKIP_LINE;
3952 static int piDISPLAYX (int passNo, int asHex) {
3953 for (;;) {
3954 if (isStrArg()) {
3955 int len = 0;
3956 char *res = getStrArg(&len);
3958 if (passNo < 0 || pass == passNo) printf("%s", res);
3959 } else {
3960 int defined = 1;
3961 int32_t v = getExprArg(&defined, NULL);
3963 if (passNo < 0 || pass == passNo) {
3964 if (asHex) printf("%04X", (unsigned int)v);
3965 else printf("%d", v);
3968 if (!eatComma()) break;
3970 return PI_SKIP_LINE;
3974 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
3975 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
3976 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
3977 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
3978 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
3979 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
3981 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
3982 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
3983 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
3986 ///////////////////////////////////////////////////////////////////////////////
3987 // ORG, DISP, etc.
3989 static int piORG (void) {
3990 int defined = 1;
3991 int32_t res = getOneExprArg(&defined, NULL);
3993 if (!defined) fatal("sorry, ORG operand value must be known here");
3994 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
3995 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
3996 pc = disp = res;
3997 if (!wasOrg) {
3998 wasOrg = 1;
3999 ent = res;
4000 if (!wasClr && res > 0) clrAddr = res-1;
4002 return PI_CONT_LINE;
4006 static int piDISP (void) {
4007 int defined = 1;
4008 int32_t res = getOneExprArg(&defined, NULL);
4010 if (!defined) fatal("sorry, DISP operand value must be known here");
4011 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
4012 //printf("DISP=%d\n", res);
4013 disp = res;
4014 return PI_CONT_LINE;
4018 static int piENDDISP (void) {
4019 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
4020 checkExprEnd();
4021 disp = pc;
4022 return PI_CONT_LINE;
4026 static int piENT (void) {
4027 int defined = 1;
4028 int32_t res = getOneExprArg(&defined, NULL);
4030 //if (!defined) fatal("sorry, ENT operand value must be known here");
4031 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
4032 ent = res;
4033 return PI_CONT_LINE;
4037 static int piCLR (void) {
4038 int defined = 1;
4039 int32_t res = getOneExprArg(&defined, NULL);
4041 //if (!defined) fatal("sorry, CLR operand value must be known here");
4042 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
4043 clrAddr = res;
4044 wasClr = 1;
4045 return PI_CONT_LINE;
4049 // RESERVE start, count
4050 static int piRESERVE (void) {
4051 int defined = 1, start;
4052 int32_t res = getExprArg(&defined, NULL);
4053 if (!defined) fatal("sorry, RESERVE operand values must be known here");
4054 if (res < 0 || res > 65535) fatal("invalid RESERVE address value: %d", res);
4055 start = res;
4056 if (!eatComma()) fatal("RESERVE needs 2 args");
4057 res = getOneExprArg(&defined, NULL);
4058 if (!defined) fatal("sorry, RESERVE operand values must be known here");
4059 if (res < 0 || res > 65535) fatal("invalid RESERVE length value: %d", res);
4060 while (res--) {
4061 if (memused[start]) fatal("trying to reserve already used memory at #%04X", start);
4062 memresv[start++] = 1;
4064 return PI_CONT_LINE;
4068 static int piALIGN (void) {
4069 int defined = 1;
4070 int32_t res;
4072 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
4073 res = getOneExprArg(&defined, NULL);
4074 if (!defined) fatal("sorry, ALIGN operand value must be known here");
4075 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
4076 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
4077 if (res > 0 && pc%res != 0) {
4078 pc /= res;
4079 pc *= res;
4080 pc += res;
4081 disp = pc;
4083 return PI_CONT_LINE;
4087 static int piDISPALIGN (void) {
4088 int defined = 1;
4089 int32_t res = getOneExprArg(&defined, NULL);
4091 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
4092 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
4093 if (res > 0 && disp%res != 0) {
4094 disp /= res;
4095 disp *= res;
4096 disp += res;
4098 return PI_CONT_LINE;
4102 ///////////////////////////////////////////////////////////////////////////////
4103 // DEFx
4105 // DEFINCR operations
4106 enum {
4107 DFI_SHR,
4108 DFI_SHL,
4109 DFI_SAR, // arith shift, extend bit 7
4110 DFI_SAL, // arith shift, extend bit 0
4111 DFI_ROR,
4112 DFI_ROL,
4113 DFI_AND,
4114 DFI_XOR,
4115 DFI_OR,
4116 DFI_ADD,
4117 DFI_SUB,
4118 DFI_MUL,
4119 DFI_DIV,
4120 DFI_MOD,
4123 typedef struct DefIncrOp_s DefIncrOp;
4124 struct DefIncrOp_s {
4125 DefIncrOp *next;
4126 uint16_t operator; // DFI_xxx
4127 uint16_t operand;
4130 static DefIncrOp *dfi_list = NULL;
4131 //static int defIncr = 0;
4134 static void dfi_free (void) {
4135 while (dfi_list) {
4136 DefIncrOp *c = dfi_list;
4137 dfi_list = c->next;
4138 free(c);
4143 //FIXME: make this faster
4144 #define DFI_XSHIFT(sop_) do { \
4145 for (uint16_t f = 0; f < opnd; ++f) { \
4146 val = ((val&xmask) sop_ 1)|(val&andmask ? ormask : 0); \
4148 } while (0)
4151 static int32_t dfi_apply (int32_t val, int isbyte) {
4152 const uint16_t xmask = (isbyte ? 0xff : 0xffff);
4153 val &= xmask;
4154 uint16_t andmask, ormask;
4155 for (DefIncrOp *dop = dfi_list; dop; dop = dop->next) {
4156 uint16_t opnd = dop->operand;
4157 switch (dop->operator) {
4158 case DFI_SHR:
4159 if (!opnd) break;
4160 if (opnd > (isbyte ? 7 : 15)) { val = 0; break; }
4161 val = (val&xmask)>>opnd;
4162 break;
4163 case DFI_SHL:
4164 if (!opnd) break;
4165 if (opnd > (isbyte ? 7 : 15)) { val = 0; break; }
4166 val = (val&xmask)<<opnd;
4167 break;
4168 case DFI_SAR:
4169 if (!opnd) break;
4170 if (isbyte) {
4171 if (opnd > 7) { val = (val&0x80 ? 0xff : 0x00); break; }
4172 andmask = ormask = 0x80;
4173 } else {
4174 if (opnd > 15) { val = (val&0x8000 ? 0xffff : 0x0000); break; }
4175 andmask = ormask = 0x8000;
4177 DFI_XSHIFT(>>);
4178 break;
4179 case DFI_SAL:
4180 if (!opnd) break;
4181 if (opnd > (isbyte ? 7 : 15)) { val = (val&0x01 ? 0xff : 0x00); break; }
4182 andmask = ormask = 0x01;
4183 DFI_XSHIFT(<<);
4184 break;
4185 case DFI_ROR:
4186 opnd &= (isbyte ? 7 : 15);
4187 if (!opnd) break;
4188 andmask = 0x01;
4189 ormask = (isbyte ? 0x80 : 0x8000);
4190 DFI_XSHIFT(>>);
4191 break;
4192 case DFI_ROL:
4193 opnd &= (isbyte ? 7 : 15);
4194 if (!opnd) break;
4195 andmask = (isbyte ? 0x80 : 0x8000);
4196 ormask = 0x01;
4197 DFI_XSHIFT(<<);
4198 break;
4199 case DFI_AND:
4200 val &= opnd;
4201 break;
4202 case DFI_XOR:
4203 val ^= opnd;
4204 break;
4205 case DFI_OR:
4206 val |= opnd;
4207 break;
4208 case DFI_ADD:
4209 val += opnd;
4210 break;
4211 case DFI_SUB:
4212 val -= opnd;
4213 break;
4214 case DFI_MUL:
4215 val *= opnd;
4216 break;
4217 case DFI_DIV:
4218 if (opnd) val /= opnd; else val = 0;
4219 break;
4220 case DFI_MOD:
4221 if (opnd) val %= opnd; else val = 0;
4222 break;
4224 val &= xmask;
4226 return val;
4230 static int piDEFINCR (void) {
4231 #if 0
4232 int defined = 1;
4233 int32_t cnt = getOneExprArg(&defined, NULL);
4234 if (!defined) fatal("DEFINCR: increment must be defined");
4235 defIncr = cnt;
4236 return PI_CONT_LINE;
4237 #else
4238 dfi_free();
4239 if (isStrArg()) {
4240 int len = 0;
4241 char *rstr = getStrArg(&len);
4242 if (!rstr || !strEquCI(rstr, "expr")) fatal("invalid DEFINCR command (\"expr\" expected)");
4243 DefIncrOp *last = NULL;
4244 while (eatComma()) {
4245 len = 0;
4246 rstr = getStrArg(&len);
4247 if (!rstr || len != 3) fatal("invalid DEFINCR operator");
4248 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4249 if (strEquCI(rstr, "SHR")) dop->operator = DFI_SHR;
4250 else if (strEquCI(rstr, "SHL")) dop->operator = DFI_SHL;
4251 else if (strEquCI(rstr, "SAR")) dop->operator = DFI_SAR;
4252 else if (strEquCI(rstr, "SAL")) dop->operator = DFI_SAL;
4253 else if (strEquCI(rstr, "ROR")) dop->operator = DFI_ROR;
4254 else if (strEquCI(rstr, "ROL")) dop->operator = DFI_ROL;
4255 else if (strEquCI(rstr, "AND")) dop->operator = DFI_AND;
4256 else if (strEquCI(rstr, "XOR")) dop->operator = DFI_XOR;
4257 else if (strEquCI(rstr, "OR")) dop->operator = DFI_OR;
4258 else if (strEquCI(rstr, "ADD")) dop->operator = DFI_ADD;
4259 else if (strEquCI(rstr, "SUB")) dop->operator = DFI_SUB;
4260 else if (strEquCI(rstr, "MUL")) dop->operator = DFI_MUL;
4261 else if (strEquCI(rstr, "DIV")) dop->operator = DFI_DIV;
4262 else if (strEquCI(rstr, "MOD")) dop->operator = DFI_MOD;
4263 else fatal("invalid DEFINCR operator '%s'", rstr);
4264 dop->next = NULL;
4265 if (!eatComma()) fatal("DEFINCR: operand expected");
4266 int defined = 1;
4267 int32_t res = getExprArg(&defined, NULL);
4268 if (!defined) fatal("DEFINCR: operand must be defined");
4269 if (res < 0) fatal("DEFINCR: operand must be positive");
4270 res &= 0xffff;
4271 dop->operand = (uint16_t)res;
4272 if (last) last->next = dop; else dfi_list = dop;
4273 last = dop;
4275 } else {
4276 int defined = 1;
4277 int32_t cnt = getOneExprArg(&defined, NULL);
4278 if (!defined) fatal("DEFINCR: increment must be defined");
4279 //defIncr = cnt;
4280 if (cnt > 0) {
4281 cnt &= 0xffff;
4282 if (cnt) {
4283 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4284 dop->next = NULL;
4285 dop->operator = DFI_ADD;
4286 dop->operand = cnt;
4287 dfi_list = dop;
4289 } else {
4290 cnt &= 0xffff; //UB, i don't care
4291 if (cnt) {
4292 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4293 dop->next = NULL;
4294 dop->operator = DFI_SUB;
4295 dop->operand = cnt;
4296 dfi_list = dop;
4300 #endif
4301 return PI_CONT_LINE;
4305 static int piDEFBW (int isWord) {
4306 int32_t res;
4307 for (;;) {
4308 int defined = 0, fixuptype = UR_FIXUP_NONE;
4310 if (isStrArg()) {
4311 int f, len = 0;
4312 char *rstr = getStrExprArg(&len, isWord);
4313 if (rstr) {
4314 for (f = 0; f < len; ++f) {
4315 int32_t b = (uint8_t)rstr[f];
4316 //b += defIncr;
4317 b = dfi_apply(b, 1/*isbyte*/);
4318 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
4319 if (b < 0) b += 256;
4320 emitByte(b);
4322 if (!eatComma()) break;
4323 continue;
4324 } else {
4325 defined = 1;
4326 res = len;
4328 } else {
4329 res = getExprArg(&defined, &fixuptype);
4333 //int32_t res = getExprArg(&defined, &fixuptype);
4335 if (pass > 0 && !defined) fatal("undefined operand");
4336 //res += defIncr;
4337 res = dfi_apply(res, !isWord/*isbyte*/);
4338 if (isWord) {
4339 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
4340 if (res < 0) res += 65536;
4341 if (isWord == 1) {
4342 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
4343 emitWord(res);
4344 } else {
4345 /* reversed word */
4346 switch (fixuptype) {
4347 case UR_FIXUP_WORD: /* swapped bytes */
4348 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
4349 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
4350 break;
4351 case UR_FIXUP_LOBYTE:
4352 case UR_FIXUP_HIBYTE:
4353 warningMsg("non-word fixup for reversed word; wtf?!");
4354 break;
4355 default: break;
4357 emitRWord(res);
4359 } else {
4360 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
4361 if (fixuptype != UR_FIXUP_NONE) {
4362 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
4363 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
4365 if (res < 0) res += 256;
4366 emitByte(res);
4369 if (!eatComma()) break;
4371 return PI_CONT_LINE;
4374 static int piDEFB (void) { return piDEFBW(0); }
4375 static int piDEFW (void) { return piDEFBW(1); }
4376 static int piDEFR (void) { return piDEFBW(2); }
4379 static int piDEFS (void) {
4380 for (;;) {
4381 int32_t bt, f;
4382 int defined = 0, fixuptype = UR_FIXUP_NONE;
4383 int32_t res = getExprArg(&defined, &fixuptype);
4385 if (pass > 0 && !defined) fatal("undefined operand");
4386 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
4387 if (*currLine && currLine[0] == ',') {
4388 (void)eatComma();
4389 bt = getExprArg(&defined, NULL);
4390 if (pass > 0 && !defined) fatal("undefined operand");
4391 //bt += defIncr;
4392 bt = dfi_apply(bt, 1/*isbyte*/);
4393 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
4394 if (bt < 0) bt += 256;
4395 if (fixuptype != UR_FIXUP_NONE) {
4396 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
4398 for (f = 0; f < res; ++f) {
4399 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
4400 emitByte(bt);
4402 } else {
4403 pc += res; disp += res;
4405 if (!eatComma()) break;
4407 return PI_CONT_LINE;
4411 /* bit 0: put '\0' */
4412 /* bit 1: set bit 7 of last byte */
4413 /* bit 2: put length */
4414 static int piDEFSTR (int type) {
4415 for (;;) {
4416 if (isStrArg()) {
4417 int f, len = 0;
4418 char *res = getStrArg(&len);
4420 if (type&0x04) {
4421 if (len > 255) fatal("string too long");
4422 emitByte(len);
4424 for (f = 0; f < len; ++f) {
4425 uint8_t b = (uint8_t)res[f];
4427 if ((type&0x02) && f == len-1) b |= 0x80;
4428 emitByte(b);
4430 if (type&0x01) emitByte(0);
4431 } else {
4432 int defined = 1;
4433 int32_t v = getExprArg(&defined, NULL);
4435 if (pass > 0 && !defined) fatal("undefined expression");
4436 if (!defined) v = 0; else v = dfi_apply(v, 1/*isbyte*/);
4437 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
4438 if (v < 0) v += 256;
4439 emitByte(v);
4441 if (!eatComma()) break;
4443 return PI_CONT_LINE;
4447 static int piDEFM (void) { return piDEFSTR(0x00); }
4448 static int piDEFZ (void) { return piDEFSTR(0x01); }
4449 static int piDEFX (void) { return piDEFSTR(0x02); }
4450 static int piDEFC (void) { return piDEFSTR(0x04); }
4453 ///////////////////////////////////////////////////////////////////////////////
4454 // INCBIN
4456 /* INCBIN "name"[,maxlen[,offset]] */
4457 static int piINCBIN (void) {
4458 int system = 0;
4459 char *fn, qCh;
4460 uint8_t bt;
4461 FILE *fl;
4462 int maxlen = 65536;
4463 int offset = 0;
4464 char *args = currLine;
4466 if (!currLine[0]) fatal("INCBIN without file name");
4468 if (isStrArg()) {
4469 qCh = *args++;
4470 system = 0;
4471 } else if (currLine[0] == '<') {
4472 qCh = '>'; ++args;
4473 system = 1;
4474 } else {
4475 qCh = 0;
4476 system = 0;
4479 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4480 int softinclude = 0;
4481 if (fn[0] == '?') { softinclude = 1; ++fn; while (isSpace(*fn)) ++fn; }
4482 if (!fn[0]) fatal("INCBIN: empty file name");
4483 // now fix the name
4484 char *fname = createIncludeName(fn, system, NULL, NULL);
4485 memmove(currLine, args, strlen(args)+1);
4487 // maxlen
4488 if (currLine[0] == ',') {
4489 int defined = 1;
4490 (void)eatComma();
4491 maxlen = getExprArg(&defined, NULL);
4492 if (!defined) fatal("INCBIN: undefined maxlen");
4493 if (maxlen < 1) { free(fname); return 1; } // nothing to do
4496 // offset (negative: from the end)
4497 if (currLine[0] == ',') {
4498 int defined = 1;
4499 (void)eatComma();
4500 offset = getOneExprArg(&defined, NULL);
4501 if (!defined) fatal("INCBIN: undefined offset");
4502 } else {
4503 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
4506 fl = fopen(fname, "rb");
4507 if (!fl) {
4508 if (!softinclude) fatal("INCBIN: file not found: '%s'", fname);
4509 } else {
4510 if (offset) {
4511 if (fseek(fl, offset, (offset < 0 ? SEEK_END : SEEK_SET)) < 0) {
4512 fclose(fl);
4513 fatal("INCBIN: error seeking to %d in file '%s'", offset, fname);
4516 while (maxlen-- > 0) {
4517 int res = fread(&bt, 1, 1, fl);
4518 if (!res) break;
4519 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: '%s'", fname); }
4520 emitByte(bt);
4522 fclose(fl);
4525 free(fname);
4526 return PI_SKIP_LINE;
4530 /* DATABIN "name"[,len[,offset[,codeaddr]]] */
4531 /* DATABIN "name|dfname"[,len[,offset[,codeaddr]]] */
4532 static int piDATABINcommon (int ascode) {
4533 if (pass != 1) return PI_SKIP_LINE;
4535 int system = 0;
4536 char *fn, qCh;
4537 int maxlen = -1;
4538 int offset = 0;
4539 int codeaddr = 32768;
4540 int hasstart = 0;
4541 char *args = currLine;
4543 if (!currLine[0]) fatal("DATABIN without file name");
4545 if (isStrArg()) {
4546 qCh = *args++;
4547 system = 0;
4548 } else if (currLine[0] == '<') {
4549 qCh = '>'; ++args;
4550 system = 1;
4551 } else {
4552 qCh = 0;
4553 system = 0;
4556 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4557 int allowskip = 0;
4558 if (fn[0] == '?') { allowskip = 1; ++fn; while (isSpace(*fn)) ++fn; }
4559 if (!fn[0]) fatal("DATABIN: empty file name");
4560 char *fnameup = strdup(fn);
4561 memmove(currLine, args, strlen(args)+1);
4563 // maxlen
4564 if (currLine[0] == ',') {
4565 int defined = 1;
4566 (void)eatComma();
4567 maxlen = getExprArg(&defined, NULL);
4568 if (!defined) fatal("DATABIN: undefined length");
4569 if (maxlen < 1) { free(fnameup); return 1; } // nothing to do
4572 // offset (negative: from the end)
4573 if (currLine[0] == ',') {
4574 int defined = 1;
4575 (void)eatComma();
4576 offset = getExprArg(&defined, NULL);
4577 if (!defined) fatal("DATABIN: undefined offset");
4580 // code address
4581 if (currLine[0] == ',') {
4582 int defined = 1;
4583 (void)eatComma();
4584 codeaddr = getOneExprArg(&defined, NULL);
4585 if (!defined) fatal("DATABIN: undefined codeaddr");
4586 hasstart = 1;
4587 } else {
4588 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
4591 // find and extract "disk name"
4592 char *diskname;
4594 char *pipeptr = strchr(fnameup, '|');
4595 if (pipeptr) {
4596 if (!pipeptr[1]) fatal("empty data file output name");
4597 diskname = strdup(pipeptr+1);
4598 *pipeptr = 0;
4599 } else {
4600 // build output name from disk name
4601 char *slp = strrchr(fnameup, '/');
4602 #ifdef WIN32
4603 char *slp1 = strrchr(fnameup, '\\');
4604 if (slp1 && (!slp || slp1 > slp)) slp = slp1;
4605 #endif
4606 if (slp) {
4607 ++slp;
4608 if (!slp[0]) fatal("empty data file output name");
4609 diskname = strdup(slp);
4610 } else {
4611 diskname = strdup(fnameup);
4615 // now fix the name
4616 char *fname = createIncludeName(fnameup, system, NULL, NULL);
4617 free(fnameup);
4619 OutDataFile *off = appendDataFile(fname, offset, maxlen, allowskip);
4621 if (off) {
4622 off->type = 'C';
4623 off->name = diskname;
4624 off->start = codeaddr&0xffffU;
4625 off->hasstart = (hasstart || ascode);
4626 //for (char *s = off->name; *s; ++s) *s = toUpper(*s);
4627 printf("data file: %s (start=%u; size=%u; std=%d)\n", off->name, off->start, off->size, off->hasstart);
4630 free(fname);
4631 return PI_SKIP_LINE;
4635 static int piDATABIN (void) { return piDATABINcommon(0); }
4636 static int piCODEBIN (void) { return piDATABINcommon(1); }
4639 ///////////////////////////////////////////////////////////////////////////////
4640 // INCLUDE
4642 /* INCLUDE "name" */
4643 static int piINCLUDE (void) {
4644 int system = 0;
4645 char *fn, qCh;
4646 char *args = currLine;
4647 if (!currLine[0]) fatal("INCLUDE without file name");
4648 if (isStrArg()) {
4649 qCh = *args++;
4650 system = 0;
4651 } else if (currLine[0] == '<') {
4652 qCh = '>'; ++args;
4653 system = 1;
4654 } else {
4655 qCh = 0;
4656 system = 0;
4658 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4659 // flags
4660 int softinclude = 0;
4661 if (fn[0] == '?') { softinclude = 1; ++fn; while (isSpace(*fn)) ++fn; }
4662 if (!fn[0]) fatal("INCLUDE: empty file name");
4663 // load
4664 if (asmTextInclude(fn, system, softinclude) != 0) {
4665 if (!softinclude) fatal("INCLUDE: some shit happens!");
4667 return PI_SKIP_LINE;
4671 ///////////////////////////////////////////////////////////////////////////////
4672 // MODULE, ENDMODULE
4674 static int piENDMODULE (void) {
4675 if (!currModule) fatal("ENDMODULE without MODULE");
4676 const char *mn = NULL;
4677 if (currLine[0]) mn = getOneLabelArg();
4678 moduleClose(mn);
4679 return PI_SKIP_LINE;
4683 static int piMODULE (void) {
4684 ModuleInfo *mi;
4685 char *mn;
4686 SourceLine *ol = currSrcLine;
4687 int inum;
4688 if (currModule) fatal("no nested modules allowed");
4689 mn = getOneLabelArg();
4690 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
4691 mi = moduleFind(mn);
4692 //fprintf(stderr, "+++MODULE: [%s] %p (seen=%d)\n", mn, mi, (mi ? mi->seen : -1));
4693 if (mi) {
4694 if (mi->seen) {
4695 if (strcmp(mi->fname, currSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
4696 /* skip module */
4697 nextSrcLine(); /* skip "MODULE" line */
4698 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
4699 setCurSrcLine(ol);
4700 fatal("no ENDMODULE");
4702 if (inum == 0) fatal("no nested modules allowed");
4703 currModule = mi;
4704 moduleOpen();
4705 //skipInstruction(); //k8:wtf?!
4706 return piENDMODULE();
4708 } else {
4709 mi = moduleAdd(mn, currSrcLine->fname);
4711 mi->seen = 1;
4712 currModule = mi;
4713 moduleOpen();
4714 return PI_SKIP_LINE;
4718 /* Z80, Z80N */
4719 static int piMODEL (void) {
4720 char *mn = getOneIdArgLo();
4721 if (strSkipSpaces(currLine)[0]) fatal("only one model name expected");
4722 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
4723 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
4724 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
4725 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
4726 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
4727 else fatal("invalid model name: %s", mn);
4728 return PI_SKIP_LINE;
4732 ////////////////////////////////////////////////////////////////////////////////
4733 // $SAVECODE "filename",addr,size [,ldaddr [,options...]]
4735 // options:
4736 // wipe -- wipe saved area (mark it as unused)
4738 static int piSAVECODE (void) {
4739 //if (pass != 1) return PI_SKIP_LINE;
4740 //char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4741 //matchDelim(',');
4743 int optWipe = 0, optData = 0;
4744 char *fn, qCh;
4745 char *args = currLine;
4747 if (!currLine[0]) fatal("SAVECODE without file name");
4749 if (isStrArg()) {
4750 qCh = *args++;
4751 } else if (currLine[0] == '<') {
4752 qCh = '>'; ++args;
4753 } else {
4754 qCh = 0;
4757 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4758 if (!fn[0]) fatal("DATABIN: empty file name");
4760 char *fname = strdup(fn);
4761 memmove(currLine, args, strlen(args)+1);
4763 // addr
4764 matchDelim(',');
4765 int defined = 1;
4766 int staddr = getExprArg(&defined, NULL);
4767 if (!defined) fatal("SAVECODE: undefined start address");
4768 if (staddr < 0 || staddr > 65535) fatal("SAVECODE: invalid start address (%d)", staddr);
4770 // size
4771 matchDelim(',');
4772 defined = 1;
4773 int size = getExprArg(&defined, NULL);
4774 if (!defined) fatal("SAVECODE: undefined size");
4775 if (size < 0 || size > 65535 || staddr+size > 65536) fatal("SAVECODE: invalid size");
4777 // load address
4778 int ldaddr = staddr;
4779 if (checkDelim(',')) {
4780 defined = 1;
4781 ldaddr = getExprArg(&defined, NULL);
4782 if (!defined) fatal("SAVECODE: undefined load address");
4783 if (ldaddr < 0 || ldaddr > 65535) fatal("SAVECODE: invalid load address");
4786 // parse options
4787 while (checkDelim(',')) {
4788 char *nstr = getLabelArg(0/*checkdelim*/);
4789 if (strEquCI(nstr, "wipe")) { optWipe = 1; continue; }
4790 if (strEquCI(nstr, "data")) { optData = 1; continue; }
4791 fatal("SAVECODE: unknown option '%s'", nstr);
4793 if (!isLineEnd()) fatal("SAVECODE: unknown extra args");
4795 // save, if we are on the second pass
4796 if (pass == 1 && size > 0) {
4797 OutDataFile *off = appendDataData(memory+(unsigned)staddr, (unsigned)staddr, (unsigned)size);
4798 if (!off) fatal("SAVECODE: out of memory");
4799 off->name = fname;
4800 off->hasstart = !optData;
4801 fname = NULL;
4804 if (optWipe) memset(memused+staddr, 0, size);
4806 if (fname) free(fname);
4807 return PI_SKIP_LINE;
4811 ////////////////////////////////////////////////////////////////////////////////
4812 // STRUCT
4814 static int piENDS (void) {
4815 fatal("ENDS without STRUCT");
4816 checkOperatorEnd();
4817 return PI_SKIP_LINE;
4821 // this will be registered for each new struct
4822 // use `urCurrentOp` to check what structure we are creating
4823 // `currSeenLabel` contains current seen label
4824 static int piSTRUCT_Internal (void) {
4825 StructInfo *sth = urCurrentOp->udata;
4827 // reserve room, set default values, add labels
4828 uint16_t origPC = pc;
4829 uint16_t origDisp = disp;
4831 pc += sth->size;
4832 disp += sth->size;
4834 if (sth->zerofill) {
4835 for (uint16_t f = 0; f < sth->size; ++f) {
4836 putByte(origPC+f, 0);
4840 for (StructField *fld = sth->fields; fld; fld = fld->next) {
4841 if (fld->hasInit) {
4842 if (fld->size == 1) {
4843 const uint8_t v = (fld->initValue < 0 ? 0x100+fld->initValue : fld->initValue)&0xff;
4844 putByte(origPC+fld->ofs, v);
4845 } else if (fld->size == 2) {
4846 const uint16_t v = (fld->initValue < 0 ? 0x10000+fld->initValue : fld->initValue)&0xffff;
4847 putWord(origPC+fld->ofs, v);
4848 } else if (fld->size == 4) {
4849 uint32_t v = 0;
4850 memcpy(&v, &fld->initValue, 4);
4851 putWord(origPC+fld->ofs, v&0xffff);
4852 putWord(origPC+fld->ofs+2, (v>>16)&0xffff);
4853 } else {
4854 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth->name);
4858 if (pass) continue;
4860 //fprintf(stderr, "LBL: <%s>\n", currSeenLabel);
4861 // need to create a new label?
4862 if (!currSeenLabel[0] || !fld->name || !fld->name[0]) continue;
4864 char *nn = mangleLabelName(currSeenLabel);
4865 char *lbn = malloc(strlen(nn)+strlen(fld->name)+64);
4866 sprintf(lbn, "%s.%s", nn, fld->name);
4868 UrLabelInfo *c = urFindLabel(lbn);
4869 if (!c) c = urAddLabel(lbn);
4870 free(lbn);
4872 c->type = LBL_TYPE_STOFS;
4873 c->value = origDisp+fld->ofs;
4874 c->known = 1;
4877 // create sizeof?
4878 if (currSeenLabel[0]) {
4879 char *nn = mangleLabelName(currSeenLabel);
4880 char *lbn = malloc(strlen(nn)+strlen("_sizeof")+64);
4881 sprintf(lbn, "%s.%s", nn, "_sizeof");
4883 UrLabelInfo *c = urFindLabel(lbn);
4884 if (!c) c = urAddLabel(lbn);
4885 free(lbn);
4887 c->type = LBL_TYPE_STOFS;
4888 c->value = sth->size;
4889 c->known = 1;
4892 // now parse initial values, if there are any
4893 if (!isLineEnd()) {
4894 const int isCurly = checkDelim('{');
4895 SourceLine *stline = currSrcLine;
4897 int wasNewLine = 1; // don't require a comma
4898 for (;;) {
4899 if (!currSrcLine) {
4900 if (isCurly) {
4901 setCurSrcLine(stline);
4902 fatal("STRUCT: no closing curly bracket");
4904 break;
4906 if (isLineEnd()) {
4907 if (!isCurly) break;
4908 // load next line
4909 nextSrcLine();
4910 wasNewLine = 1;
4911 continue;
4914 if (isCurly && checkDelim('}')) {
4915 // we're done
4916 if (!isLineEnd()) fatal("STRUCT: closing curly bracket must be alone on the line");
4917 break;
4920 if (wasNewLine) {
4921 wasNewLine = 0;
4922 } else {
4923 matchDelim(',');
4926 char *nstr = getLabelArg(0/*checkdelim*/);
4928 // find field
4929 StructField *fld = sth->fields;
4930 for (; fld; fld = fld->next) if (fld->name && strcmp(fld->name, nstr) == 0) break;
4931 if (!fld) fatal("unknown field `%s` in struct `%s`", nstr, sth->name);
4933 // only `=` is allowed
4934 matchDelim('=');
4936 int defined = 1;
4937 int32_t ival = getExprArg(&defined, NULL);
4939 if (fld->size == 1) {
4940 if (defined && (ival < -128 || ival > 255)) {
4941 fatal("STRUCT: field `%s` has inivalid initial value: %d",
4942 (fld->name ? fld->name : "<anonymous>"), ival);
4944 if (ival < -128) ival = -128;
4945 const uint8_t v = (ival < 0 ? 0x100+ival : ival)&0xff;
4946 putByte(origPC+fld->ofs, v);
4947 } else if (fld->size == 2) {
4948 if (defined && (ival < -32768 || ival > 65535)) {
4949 fatal("STRUCT: field `%s` has inivalid initial value: %d",
4950 (fld->name ? fld->name : "<anonymous>"), ival);
4952 if (ival < -32768) ival = -32768;
4953 const uint16_t v = (ival < 0 ? 0x10000+ival : ival)&0xffff;
4954 putWord(origPC+fld->ofs, v);
4955 } else if (fld->size == 4) {
4956 uint32_t v = 0;
4957 memcpy(&v, &ival, 4);
4958 putWord(origPC+fld->ofs, v&0xffff);
4959 putWord(origPC+fld->ofs+2, (v>>16)&0xffff);
4960 } else {
4961 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth->name);
4966 return PI_SKIP_LINE;
4970 static int structCheckSizeDecl (const char *s) {
4971 if (strEquCI(s, "byte")) return 1;
4972 if (strEquCI(s, "word")) return 2;
4973 if (strEquCI(s, "address")) return 2;
4974 if (strEquCI(s, "dword")) return 4;
4975 if (strEquCI(s, "label")) return 0;
4976 if (strEquCI(s, "defs")) return -666;
4977 if (strEquCI(s, "ds")) return -666;
4978 return -1;
4982 // STRUCT name [,init_offset]
4983 static int piSTRUCT (void) {
4984 int zerofill = 0, extend = 0;
4985 int defined = 1, inum;
4986 SourceLine *stline;
4988 //fprintf(stderr, "PASS=%d\n", pass);
4989 if (pass) {
4990 stline = currSrcLine;
4991 nextSrcLine(); // skip ourself
4992 if (!setCurSrcLine(findNextInstructionFromList(&inum, "ENDS", NULL))) {
4993 setCurSrcLine(stline);
4994 fatal("STRUCT: no ENDS");
4996 return PI_SKIP_LINE;
5000 if (checkDelim('*')) extend = 1;
5001 else if (checkDelim('!')) extend = 2; // compatibility with the old code
5002 else if (checkDelim('+')) extend = 2;
5005 // flags: `[flag, flag...]`
5006 // currently, only `zero_fill` flag is defined
5007 if (checkDelim('[')) {
5008 while (!checkDelim(']')) {
5009 if (zerofill == 0 && checkIdentCI("zero_fill")) zerofill = 1;
5010 else fatal("invalid structure flag");
5011 if (!checkDelim(',')) {
5012 if (!checkDelim(']')) fatal("invalid structure flag list");
5013 break;
5018 // if `extend` is before a struct name, we are extending it without inheriting
5019 if (checkIdentCI("extend") || checkIdentCI("extends")) extend = 1;
5021 // struct name
5022 char stname[128], estname[128];
5023 stname[0] = 0;
5024 char *nstr = getLabelArg(0/*checkdelim*/);
5025 if (!nstr || !nstr[0]) fatal("structure name expected");
5026 if (strlen(nstr) > 127) fatal("structure name too long");
5027 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr)) {
5028 fatal("structure name `%s` is invalid", nstr);
5030 strcpy(stname, nstr);
5032 StructInfo *sth = NULL;
5033 if (extend == 0) {
5034 for (sth = structList; sth; sth = sth->next) {
5035 if (strcmp(sth->name, stname) == 0) {
5036 fatal("duplicate struct name `%s`!", stname);
5039 if (urFindOp(stname)) fatal("invalid struct name `%s`!", stname);
5042 if (extend || checkIdentCI("extend") || checkIdentCI("extends")) {
5043 if (extend == 0) {
5044 // inherit
5045 extend = 2;
5046 nstr = getLabelArg(0/*checkdelim*/);
5047 if (!nstr || !nstr[0]) fatal("parent structure name expected");
5048 if (strlen(nstr) > 127) fatal("parent structure name too long");
5049 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr)) {
5050 fatal("structure name `%s` is invalid", nstr);
5052 strcpy(estname, nstr);
5053 for (sth = structList; sth; sth = sth->next) {
5054 if (strcmp(sth->name, estname) == 0) break;
5056 if (!sth) fatal("STRUCT: cannot extend unknown structure `%s`", estname);
5057 } else {
5058 // extend
5059 for (sth = structList; sth; sth = sth->next) {
5060 if (strcmp(sth->name, stname) == 0) break;
5062 if (!sth) fatal("STRUCT: cannot extend unknown structure `%s`", stname);
5064 // inherit?
5065 if (extend == 2) {
5066 StructInfo *stnew = calloc(1, sizeof(StructInfo));
5067 stnew->name = strdup(stname);
5068 stnew->size = sth->size;
5069 stnew->zerofill = (sth->zerofill || zerofill);
5071 // copy fields
5072 StructField *xlast = NULL;
5073 for (StructField *fld = sth->fields; fld; fld = fld->next) {
5074 StructField *nf = calloc(1, sizeof(StructField));
5075 nf->name = (fld->name ? strdup(fld->name) : NULL);
5076 nf->ofs = fld->ofs;
5077 nf->size = fld->size;
5078 nf->initValue = fld->initValue;
5079 nf->hasInit = fld->hasInit;
5080 if (xlast) xlast->next = nf; else stnew->fields = nf;
5081 xlast = nf;
5084 sth = stnew;
5086 } else {
5087 // allocate struct header
5088 sth = calloc(1, sizeof(StructInfo));
5089 sth->name = strdup(stname);
5090 sth->zerofill = zerofill;
5093 int32_t currofs = (sth ? sth->size : 0);
5094 // parse initial offset
5095 if (checkDelim(',')) {
5096 defined = 1;
5097 currofs = getExprArg(&defined, NULL);
5098 if (!defined) fatal("STRUCT: initial offset must be defined");
5099 if (currofs < 0 || currofs > 65535) fatal("STRUCT: invalid initial offset");
5102 // done with args
5103 if (!isLineEnd()) fatal("too many arguments for STRUCT");
5105 // for fast appending
5106 StructField *fldlast = (sth ? sth->fields : NULL);
5107 StructField *fldnew = NULL;
5109 if (fldlast) {
5110 while (fldlast->next) fldlast = fldlast->next;
5113 if (extend == 2) fldnew = sth->fields;
5115 // parse until ENDS
5116 stline = currSrcLine;
5117 nextSrcLine(); // skip ourself
5118 //fprintf(stderr, "001: <%s>\n", currLine);
5119 inum = -1;
5120 while (currSrcLine) {
5121 size_t slen = strlen(currLine);
5122 currLineRemoveComment();
5123 currLineTrimBlanks();
5124 if (!currLine[0]) { nextSrcLine(); continue; }
5126 if (strIsCommand("ENDS", currSrcLine->line)) break;
5127 //fprintf(stderr, "112: <%s> (%d)\n", currLine, inum);
5129 // parse new field
5130 int skipInit = 0;
5131 char *fldname = getLabelArg(0/*checkdelim*/);
5132 int fldsize = structCheckSizeDecl(fldname);
5133 if (fldsize == -666) {
5134 // defs/ds
5135 defined = 1;
5136 fldsize = getExprArg(&defined, NULL);
5137 if (!defined) fatal("STRUCT: DEFS size must be defined");
5138 if (fldsize < 0 || fldsize > 65535) fatal("STRUCT: DEFS size is invalid");
5139 skipInit = 1;
5140 fldname = NULL;
5141 } else if (fldsize < 0) {
5142 // this must be a label
5143 int found = 0;
5144 for (StructField *cf = sth->fields; cf; cf = cf->next) {
5145 if (cf->name && cf->name[0] && strcmp(cf->name, fldname) == 0) {
5146 found = 1;
5147 break;
5150 if (found) fatal("duplicate field name '%s'", fldname);
5151 fldname = strdup(fldname);
5152 char *szdecl = getLabelArg(0/*checkdelim*/);
5153 fldsize = structCheckSizeDecl(szdecl);
5154 if (fldsize == -666) {
5155 // defs/ds
5156 defined = 1;
5157 fldsize = getExprArg(&defined, NULL);
5158 if (!defined) fatal("STRUCT: DEFS size must be defined");
5159 if (fldsize < 0 || fldsize > 65535) fatal("STRUCT: DEFS size is invalid");
5160 skipInit = 1;
5161 } else {
5162 if (fldsize < 0) fatal("field size definition expected");
5164 } else {
5165 fldname = NULL; // no name
5168 // check for init value
5169 int hasInit = 0;
5170 int32_t initval = 0;
5172 if (!skipInit && checkDelim('=')) {
5173 defined = 1;
5174 initval = getExprArg(&defined, NULL);
5175 if (!defined) fatal("STRUCT: initial value must be defined");
5176 hasInit = 1;
5177 if (fldsize == 0 ||
5178 (fldsize == 1 && (initval < -128 || initval > 255)) ||
5179 (fldsize == 2 && (initval < -32768 || initval > 65535)))
5181 fatal("STRUCT: initial value is out of range");
5185 if (!isLineEnd()) fatal("STRUCT: extra field data");
5187 if (currofs+fldsize > 65535) fatal("STRUCT: too big");
5189 // create field
5190 StructField *fld = calloc(1, sizeof(StructField));
5191 fld->name = fldname;
5192 fld->ofs = currofs;
5193 fld->size = fldsize;
5194 fld->hasInit = hasInit;
5195 fld->initValue = initval;
5196 if (fldlast) fldlast->next = fld; else sth->fields = fld;
5197 fldlast = fld;
5198 currofs += fldsize;
5200 if (!fldnew) fldnew = fld;
5202 #if 0
5203 fprintf(stderr, "FLD <%s>: ofs=%u; size=%u; hasinit=%d; init=%d\n", fld->name, fld->ofs,
5204 fld->size, fld->hasInit, fld->initValue);
5205 #endif
5207 nextSrcLine();
5209 if (!currSrcLine) { setCurSrcLine(stline); fatal("no ENDS"); }
5210 if (currofs > 65535) fatal("STRUCT: too big");
5211 sth->size = currofs;
5213 //FIXME: structs must be local to module!
5215 // register new struct
5216 if (extend == 0 || extend == 2) {
5217 StructInfo *slast = structList;
5218 if (slast) {
5219 while (slast->next) slast = slast->next;
5220 slast->next = sth;
5221 } else {
5222 structList = sth;
5224 UrAsmOp *op = urAddOp(sth->name, &piSTRUCT_Internal);
5225 op->udata = sth;
5228 // add struct labels
5229 for (StructField *fld = fldnew; fld; fld = fld->next) {
5230 if (!fld->name || !fld->name[0]) continue;
5232 char *nn = mangleLabelName(sth->name);
5233 char *lbn = malloc(strlen(nn)+strlen(fld->name)+64);
5234 sprintf(lbn, "%s.%s", nn, fld->name);
5236 //fprintf(stderr, "%d: <%s>\n", extend, lbn);
5238 UrLabelInfo *c = urFindLabel(lbn);
5239 if (c) fatal("STRUCT: field name '%s' conflict!", fld->name);
5241 c = urAddLabel(lbn);
5242 free(lbn);
5243 c->type = LBL_TYPE_STOFS;
5244 c->value = fld->ofs;
5245 c->known = 1;
5248 // add `structname.sizeof`
5250 char *nn = mangleLabelName(sth->name);
5251 char *lbn = malloc(strlen(nn)+64);
5252 sprintf(lbn, "%s.%s", nn, "_sizeof");
5254 UrLabelInfo *c = urFindLabel(lbn);
5255 if (c) {
5256 if (!extend) fatal("STRUCT: field name '%s' conflict!", "_sizeof");
5257 } else {
5258 c = urAddLabel(lbn);
5260 free(lbn);
5262 c->type = LBL_TYPE_STOFS;
5263 c->value = sth->size;
5264 c->known = 1;
5267 return PI_SKIP_LINE;
5271 // ////////////////////////////////////////////////////////////////////////// //
5272 static int piEND_PASMO (void) {
5273 int defined = 1;
5274 int32_t res = getOneExprArg(&defined, NULL);
5275 //if (!defined) fatal("sorry, ENT operand value must be known here");
5276 if (res < 0 || res > 65535) fatal("invalid END operand value: %d", res);
5277 ent = res;
5278 return PI_CONT_LINE;
5282 ///////////////////////////////////////////////////////////////////////////////
5283 // DUP, EDUP
5285 static int piEDUP (void) {
5286 fatal("EDUP without DUP");
5287 checkOperatorEnd();
5288 return PI_SKIP_LINE;
5292 static int piENDR (void) {
5293 fatal("ENDR without REPT");
5294 checkOperatorEnd();
5295 return PI_SKIP_LINE;
5299 // DUP count [,var [,initval [,incr]]]
5300 static int piDUPEx (int isrept) {
5301 int defined = 1, inum;
5302 SourceLine *stline, *eline = NULL;
5304 //int32_t cnt = getOneExprArg(&defined, NULL);
5305 int32_t cnt = getExprArg(&defined, NULL);
5306 if (!defined) fatal("DUP: counter must be defined");
5307 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
5308 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
5310 int32_t initval = 0;
5311 int32_t incr = 1;
5313 // variable name
5314 char vname[128];
5315 vname[0] = 0;
5316 if (currLine[0] == ',') {
5317 (void)eatComma();
5318 char *lbl = getLabelArg(0/*checkdelim*/);
5319 if (lbl[0]) {
5320 if (strlen(lbl) > 127) fatal("DUP counter variable name too long");
5321 if (urFindOp(lbl) || !urasm_is_valid_name(lbl)) fatal("DUP counter variable name is invalid");
5322 strcpy(vname, lbl);
5326 // initial variable value
5327 if (currLine[0] == ',') {
5328 (void)eatComma();
5329 defined = 1;
5330 initval = getExprArg(&defined, NULL);
5331 if (!defined) fatal("DUP: initial value must be defined");
5334 // increment
5335 if (currLine[0] == ',') {
5336 (void)eatComma();
5337 defined = 1;
5338 incr = getExprArg(&defined, NULL);
5339 if (!defined) fatal("DUP: increment value must be defined");
5342 if (currLine[0]) fatal("too many arguments for DUP");
5344 int dupstack[128];
5345 int dupsp = 0;
5347 // now find corresponding EDUP
5348 // note that we should skip nested DUPs
5349 nextSrcLine(); // skip ourself
5350 stline = currSrcLine;
5351 while (currSrcLine) {
5352 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "REPT", "EDUP", "ENDR", "ENDM", NULL))) break;
5353 if (inum == 0 || inum == 1) {
5354 if (dupsp >= 128) fatal("too many nested DUPs");
5355 dupstack[dupsp++] = isrept;
5356 isrept = (inum == 1);
5357 nextSrcLine(); // skip DUP
5358 } else {
5359 // EDUP
5360 if (dupsp == 0) { eline = currSrcLine; break; }
5361 if (isrept) {
5362 if (inum < 3) fatal("invalid REPT end directive");
5363 } else {
5364 if (inum >= 3) fatal("invalid DUP end directive");
5366 isrept = dupstack[--dupsp];
5367 nextSrcLine(); // skip EDUP
5370 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
5372 // create counter local, if necessary
5373 UrLabelInfo *lcnt = (vname[0] ? urAddTempLocal(vname) : NULL);
5374 if (lcnt) lcnt->value = initval;
5376 // repeat lines
5377 while (cnt-- > 0) {
5378 setCurSrcLine(stline);
5379 while (currSrcLine != eline) processCurrentLine();
5380 // increment counter
5381 if (lcnt) lcnt->value += incr;
5384 // remove counter
5385 urRemoveTempLocal(lcnt);
5387 return PI_SKIP_LINE;
5391 static int piDUP (void) { return piDUPEx(0); }
5392 static int piREPT (void) { return piDUPEx(1); }
5395 ///////////////////////////////////////////////////////////////////////////////
5396 // IF, ENDIF
5398 static int ifCount = 0;
5401 // results:
5402 // -1: error (should not happen)
5403 // 0: successfully complete
5404 // 1: stopped *AT* "ELSE"
5405 // 2: stopped *AT* "ELSIF"
5406 static int ifSkipToEndIfOrElse (int wholeBody) {
5407 int inum, wasElse = 0;
5408 SourceLine *oline;
5410 while (currSrcLine) {
5411 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF",
5412 "ELSEIF", "MACRO", "ENDM", "IFX",
5413 "ELSEIFX",
5414 "ELSIF", "ELSIFX",
5415 NULL))) break;
5416 switch (inum) {
5417 case 0: /* if */
5418 case 6: /* ifx */
5419 nextSrcLine(); // skip IF
5420 ifSkipToEndIfOrElse(1); // and recurse
5421 nextSrcLine(); // skip ENDIF
5422 break;
5423 case 1: /* else */
5424 if (wasElse) fatal("duplicate ELSE");
5425 if (!wholeBody) return 1;
5426 wasElse = 1;
5427 nextSrcLine(); // skip ELSE
5428 break; // and continue
5429 case 2: /* endif */
5430 return 0; // and exit
5431 case 3: /* elseif */
5432 case 7: /* elseifx */
5433 case 8: /* elsif */
5434 case 9: /* elsifx */
5435 if (wasElse) fatal("ELSIF in ELSE");
5436 if (!wholeBody) return 2;
5437 nextSrcLine(); // skip ELSIF
5438 break; // and continue
5439 case 4: /* macro */
5440 // skip it as a whole
5441 nextSrcLine(); // skip MACRO
5442 for (;;) {
5443 oline = currSrcLine;
5444 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
5445 if (inum == 1) break;
5446 fatal("invalid nested MACRO");
5448 nextSrcLine(); // skip ENDM
5449 break;
5450 case 5: /* endm */
5451 fatal("unexpected ENDM");
5454 fatal("IF without ENDIF");
5455 return -1;
5459 static int piENDIF (void) {
5460 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5461 if (--ifCount < 0) fatal("ENDIF without IF");
5462 checkOperatorEnd();
5463 return PI_SKIP_LINE;
5467 static int piELSE (void) {
5468 if (--ifCount < 0) fatal("ELSE without IF");
5469 nextSrcLine(); // skip ELSE
5470 ifSkipToEndIfOrElse(1);
5471 return PI_SKIP_LINE;
5474 static int piELSIF (void) {
5475 if (--ifCount < 0) fatal("ELSIF without IF");
5476 nextSrcLine(); // skip ELSIF
5477 ifSkipToEndIfOrElse(1);
5478 return PI_SKIP_LINE;
5482 static int piELSIFX (void) {
5483 if (--ifCount < 0) fatal("ELSIFX without IF");
5484 nextSrcLine(); // skip ELSIFX
5485 ifSkipToEndIfOrElse(1);
5486 return PI_SKIP_LINE;
5490 static int piIFAll (int isIfX) {
5491 int defined = 1;
5492 int ooo = lblOptMakeU2;
5494 //fprintf(stderr, "piIFALL: <%s>\n", currLine);
5495 lblOptMakeU2 = (isIfX ? 1 : 0);
5496 int32_t cond = getOneExprArg(&defined, NULL);
5497 lblOptMakeU2 = ooo;
5498 //fprintf(stderr, "piIFALL COND: <%d>\n", cond);
5500 if (!defined) {
5501 if (!isIfX) fatal("IF: condition must be defined");
5502 cond = 0; // for IFX: 0 if there is any undefined label
5504 if (cond) {
5505 // ok, do it until ELSE/ELSIF/ENDIF
5506 ++ifCount;
5507 return PI_SKIP_LINE;
5509 for (;;) {
5510 int r;
5511 char *args;
5513 nextSrcLine(); // skip last instruction
5514 // skip until ELSE/ELSIF/ENDIF
5515 r = ifSkipToEndIfOrElse(0);
5516 /*!fprintf(stderr, "ELSIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5517 if (r == 0) break; // ENDIF
5518 if (r == 1) { ++ifCount; break; } // ELSE
5519 // ELSIF, do condition
5520 args = strIsCommand("ELSIF", currLine);
5521 if (args == NULL) args = strIsCommand("ELSEIF", currLine);
5522 if (args != NULL) {
5523 isIfX = 0;
5524 } else {
5525 args = strIsCommand("ELSIFX", currLine);
5526 if (args == NULL) args = strIsCommand("ELSEIFX", currLine);
5527 if (args == NULL) fatal("internal error in conditionals"); // the thing that should not be
5528 isIfX = 1;
5530 memmove(currLine, args, strlen(args)+1);
5531 /*!fprintf(stderr, "ELSIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5532 cond = getOneExprArg(&defined, NULL);
5533 /*!fprintf(stderr, "ELSIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0), cond);*/
5534 if (!defined) {
5535 if (!isIfX) fatal("ELSIF: condition must be defined");
5536 cond = 0; // for IFX: 0 if there is any undefined label
5538 if (cond) { ++ifCount; break; } // condition is true
5540 return PI_SKIP_LINE;
5544 static int piIF (void) { return piIFAll(0); }
5545 static int piIFX (void) { return piIFAll(1); }
5548 ///////////////////////////////////////////////////////////////////////////////
5549 // macro processor
5550 ///////////////////////////////////////////////////////////////////////////////
5552 what i did with MACRO is the brain-damaged cheating all the way.
5554 first, i will collect the MACRO body and remember it (removing it
5555 from the main source code, so second pass will not see it).
5557 second, when the macro is used, i will:
5558 * insert the whole macro body in place (label resolver will
5559 fix "..lbl" labels for us)
5560 * let the asm play with it
5562 this is not the best scheme, but it is fairly simple and it works.
5565 // will be called when parser encounters term starting with '=' or '*'
5566 // first term char will not be skipped
5567 // must return pointer to the first char after expression end
5568 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
5569 char name[257];
5570 char *p = name;
5572 if (curmacro == NULL) fatal("'=' outside of macro");
5573 if (*expr++ != '=') fatal("'=' expected!");
5575 expr = strSkipSpaces(expr);
5576 while (*expr) {
5577 if (isAlphaDigit(*expr) || *expr == '_') {
5578 if (p-name > 250) fatal("id too long");
5579 *p++ = *expr++;
5580 continue;
5582 break;
5584 *p = 0;
5586 expr = strSkipSpaces(expr);
5587 for (int f = 0; f < curmacro->mac->argc; ++f) {
5588 if (strEquCI(name, curmacro->mac->argnames[f])) {
5589 if (*expr == '[') {
5590 urasm_exprval_t v;
5591 int l = (int)strlen(curmacro->argvals[f]);
5592 ++expr; // skip "["
5593 urasm_exprval_init(&v);
5594 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
5595 if (*error) return expr;
5596 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
5597 ++expr;
5598 if (v.val < 0) v.val += l;
5599 if (v.val < 0 || v.val >= l) {
5600 res->val = '?';
5601 } else {
5602 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
5604 return expr;
5605 } else {
5606 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
5607 return expr;
5612 fatal("unknown macro variable: '%s'", name);
5616 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
5617 // opr starts with '=' (invariant)
5618 static int expandCB (char *opr, int oprlen) {
5619 char name[257], *p = name, *op = opr;
5621 if (curmacro == NULL) fatal("'=' outside of macro");
5622 if (*op++ != '=') fatal("'=' expected!"); // just in case
5623 //fprintf(stderr, "expand: [%s]\n", opr);
5625 if (!isAlpha(op[0])) return 0; // nothing to do
5627 // copy argument name
5628 while (*op) {
5629 if (isAlphaDigit(*op) || *op == '_') {
5630 if (p-name > 250) fatal("id too long");
5631 *p++ = *op++;
5632 continue;
5634 break;
5636 *p = 0;
5638 // expand argument? we only need to expand `=arg[n]`
5639 op = strSkipSpaces(op);
5640 if (op[0] != '[') return 0;
5642 for (int f = 0; f < curmacro->mac->argc; ++f) {
5643 if (strEquCI(name, curmacro->mac->argnames[f])) {
5644 ur_assert(*op == '[');
5645 // replace argument with character, or with literal value
5646 // used for `=regpair[0]` or `=regpair[]`
5647 if (op[1] == ']') {
5648 op += 2;
5649 const int l = (int)strlen(curmacro->argvals[f]);
5650 const int lleft = (int)strlen(op);
5651 char *tmp = malloc(l+lleft+8);
5652 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
5653 if ((int)strlen(tmp) > oprlen) {
5654 free(tmp);
5655 return -1;
5657 strcpy(opr, tmp);
5658 free(tmp);
5659 return 0;
5660 } else {
5661 urasm_exprval_t v;
5662 int error = 0, defined = 1, donteval = 0;
5663 ++op; // skip '['
5664 urasm_exprval_init(&v);
5665 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
5666 if (error) return -1;
5667 // result should be a number
5668 if (op == NULL || *op != ']' || v.str != NULL) return -1;
5669 ++op; // skip ']'
5670 // it is guaranteed to have more than one char in opr
5671 // so we can simply put char from argument value, and remove other expression chars
5672 const int l = (int)strlen(curmacro->argvals[f]);
5673 if (v.val < 0) v.val += l; // negative: indexing from the end
5674 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
5675 // copy char
5676 opr[0] = curmacro->argvals[f][v.val];
5677 // remove other chars
5678 memmove(opr+1, op, strlen(op)+1);
5680 return 0;
5684 fatal("unknown macro variable: '%s'", name);
5688 // main macro expander
5689 static void processMacro (MacroDef *mc) {
5690 SourceLine *oldcurline = currSrcLine;
5691 CurMacroDef cm;
5692 memset(&cm, 0, sizeof(cm));
5694 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
5695 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
5697 // parse macro arguments
5698 cm.mac = mc;
5699 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) cm.argvals[f] = NULL;
5700 int currArg = 0;
5701 for (;;) {
5702 removeSpaces();
5703 // do we have more arguments?
5704 if (!currLine[0]) break;
5705 // check for argument name (this is purely cosmetic thing)
5706 // use like this: "mcall :argname=[value]" (value may be omited for default)
5707 if (currLine[0] == ':' && isAlpha(currLine[1])) {
5708 int pos = 2;
5709 while (isAlphaDigit(currLine[pos]) || currLine[pos] == '_') ++pos;
5710 //hack!
5711 const char svch = currLine[pos];
5712 currLine[pos] = 0;
5713 if (!strEquCI(currLine+1, mc->argnames[currArg])) {
5714 // out-of-order, find proper argument and rewind to it
5715 currArg = -1;
5716 for (int c = 0; c < mc->argc; ++c) {
5717 if (strEquCI(currLine+1, mc->argnames[c])) {
5718 currArg = c;
5719 break;
5722 if (currArg < 0) fatal("macro '%s' has no argument named '%s'", mc->name, currLine+1);
5724 currLine[pos] = svch;
5725 // remove argument name
5726 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
5727 removeSpaces();
5728 // check for '='
5729 if (currLine[0] != '=') fatal("expected '=' after argument name");
5730 // remove '='
5731 memmove(currLine, currLine+1, strlen(currLine));
5732 removeSpaces();
5734 // too many args?
5735 if (currArg >= mc->argc) fatal("too many arguments to macro '%s'", mc->name);
5736 // check for default value
5737 if (currLine[0] == ',') {
5738 if (mc->argdefaults[currArg] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[currArg]);
5739 cm.argvals[currArg] = strdup(mc->argdefaults[currArg]);
5740 memmove(currLine, currLine+1, strlen(currLine));
5741 } else {
5742 // skip argument (so we will know its length, and will be able to copy it)
5743 char *e = skipMacroArg(currLine);
5744 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
5745 const char ech = *e;
5746 if (ech != 0) {
5747 if (ech == ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc->name);
5748 if (ech != ',') fatal("invalid invocation of macro '%s'", mc->name);
5750 *e = 0;
5751 cm.argvals[currArg] = strdup(currLine);
5752 // strip trailing spaces
5753 strTrimRight(cm.argvals[currArg]);
5754 if (ech) {
5755 *e = ech;
5756 memmove(currLine, e+1, strlen(e));
5757 } else {
5758 currLine[0] = 0;
5761 ++currArg;
5763 // check for line end
5764 removeSpaces();
5765 if (currLine[0]) fatal("invalid macro invocation");
5767 // setup default argument values
5768 for (int f = 0; f < mc->argc; ++f) {
5769 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
5770 if (cm.argvals[f]) continue;
5771 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
5772 cm.argvals[f] = strdup(mc->argdefaults[f]);
5773 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
5776 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
5778 // insert macro code into the source
5779 setCurSrcLine(mc->lines);
5780 curmacro = &cm;
5781 ++curmacronum;
5782 while (currSrcLine != NULL) {
5783 //fprintf(stderr, "*[%s] (%d)\n", currSrcLine->line, currSrcLine->lineNo);
5784 processCurrentLine();
5787 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) if (cm.argvals[f]) free(cm.argvals[f]);
5788 setCurSrcLine(oldcurline);
5789 curmacro = NULL;
5790 nextSrcLine();
5794 static int piMACRO (void) {
5795 char *name;
5796 int argc = 0;
5797 char *argdefaults[MAX_MACRO_ARGS];
5798 char *argnames[MAX_MACRO_ARGS];
5799 SourceLine *stline, *eline = NULL;
5800 MacroDef *mc;
5802 name = strdup(getLabelArg(0));
5803 //fprintf(stderr, "[%s]\n", name);
5804 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
5805 if (currLine[0] == ',') memmove(currLine, currLine+1, strlen(currLine));
5806 removeSpaces();
5808 while (currLine[0]) {
5809 if (argc >= MAX_MACRO_ARGS) fatal("too many arguments in MACRO");
5810 if (!isAlpha(currLine[0])) fatal("invalid MACRO definition");
5811 argnames[argc] = strdup(getLabelArg(0));
5812 if (currLine[0] == '=') {
5813 // default value
5814 char *e = strchr(currLine, ','), tch;
5816 if (e == NULL) e = currLine+strlen(currLine);
5817 tch = *e;
5818 *e = 0;
5819 argdefaults[argc] = strdup(currLine+1);
5820 *e = tch;
5821 memmove(currLine, e, strlen(e)+1);
5822 } else {
5823 argdefaults[argc] = NULL;
5825 removeSpaces();
5826 if (currLine[0] == ',') {
5827 memmove(currLine, currLine+1, strlen(currLine));
5828 removeSpaces();
5830 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
5831 ++argc;
5834 // now find corresponding ENDM
5835 // note that we should skip nested DUPs
5836 stline = currSrcLine;
5837 nextSrcLine(); // skip ourself
5838 while (currSrcLine) {
5839 int inum;
5841 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
5842 // ok, we found something; what is it?
5843 if (inum == 0) {
5844 // new MACRO
5845 fatal("no nested MACROs yet");
5846 } else {
5847 // ENDM, gotcha!
5848 eline = currSrcLine;
5849 // kill ENDM
5850 eline->line[0] = 0;
5851 nextSrcLine(); // skip ENDM
5852 break;
5855 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
5857 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
5858 mc->name = name;
5859 mc->argc = argc;
5860 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
5862 eline->next = NULL;
5863 mc->lines = stline->next;
5864 stline->next = currSrcLine;
5865 stline->line[0] = 0;
5867 mc->next = maclist;
5868 maclist = mc;
5869 setCurSrcLine(stline);
5870 return PI_SKIP_LINE;
5874 static int piENDM (void) {
5875 fatal("ENDM without MACRO");
5876 return PI_SKIP_LINE;
5880 ///////////////////////////////////////////////////////////////////////////////
5881 // line processor
5882 static void piTapParseLoaderName (void) {
5883 if (eatComma()) {
5884 int len;
5885 if (!isStrArg()) fatal("loader name expected");
5886 char *fn = getStrArg(&len);
5887 if (len > 10) fatal("loader name too long");
5888 memset(tapeLoaderName, ' ', 10);
5889 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
5894 static int piMATHMODE (void) {
5895 char *name = getOneLabelArg();
5896 if (strEquCI(name, "OLD")) urasm_use_old_priorities = 1;
5897 else if (strEquCI(name, "NEW")) urasm_use_old_priorities = 0;
5898 else fatal("invalid math mode; NEW or OLD expected");
5899 return PI_SKIP_LINE;
5903 static int piCASTRATES (void) {
5904 char *name = getOneLabelArg();
5905 if (strEquCI(name, "YES")) urasm_allow_hex_castrates = 1;
5906 else if (strEquCI(name, "TAN")) urasm_allow_hex_castrates = 1;
5907 else if (strEquCI(name, "NO")) urasm_allow_hex_castrates = 0;
5908 else if (strEquCI(name, "ONA")) urasm_allow_hex_castrates = 0;
5909 else fatal("yes/no value expected");
5910 return PI_SKIP_LINE;
5914 static int piREFOPT (void) {
5915 char *name = getOneLabelArg();
5916 if (strEquCI(name, "ALLLABELS")) urasm_dump_all_labels = 1;
5917 else if (strEquCI(name, "ALL_LABELS")) urasm_dump_all_labels = 1;
5918 else if (strEquCI(name, "NOUPCASE")) urasm_dump_all_labels = 0;
5919 else if (strEquCI(name, "NO_UPCASE")) urasm_dump_all_labels = 0;
5920 else if (strEquCI(name, "DEFAULT")) urasm_dump_all_labels = 1;
5921 else fatal("invalid refopt mode");
5922 return PI_SKIP_LINE;
5926 static int piDEFFMT (void) {
5927 char *name;
5929 if (isStrArg()) {
5930 int len = 0;
5931 name = getStrArg(&len);
5932 } else {
5933 name = getLabelArg(1);
5935 if (optWTChanged) return 1;
5937 optRunDMB = optRunTape = optRunSCL = optRunDSK = 0;
5938 //optRunSNA = 0;
5940 if (strEquCI(name, "SNA") || strEquCI(name, "RUNSNA") || strEquCI(name, "SNARUN")) {
5941 optWriteType = 's';
5942 if (currLine[0]) fatal("too many expressions");
5943 return PI_SKIP_LINE;
5945 if (strEquCI(name, "TAP") || strEquCI(name, "TAPE")) {
5946 optWriteType = 't';
5947 piTapParseLoaderName();
5948 return PI_SKIP_LINE;
5950 if (strEquCI(name, "RUNTAP") || strEquCI(name, "RUNTAPE") || strEquCI(name, "TAPERUN")) {
5951 optRunTape = 1;
5952 optWriteType = 't';
5953 piTapParseLoaderName();
5954 return PI_SKIP_LINE;
5956 if (strEquCI(name, "BIN") || strEquCI(name, "RAW")) {
5957 optWriteType = 'r';
5958 if (currLine[0]) fatal("too many expressions");
5959 return PI_SKIP_LINE;
5961 if (strEquCI(name, "DMB") || strEquCI(name, "RUNDMB") || strEquCI(name, "DMBRUN")) {
5962 optRunDMB = (name[3] != 0);
5963 optWriteType = 'd';
5964 if (currLine[0]) fatal("too many expressions");
5965 return PI_SKIP_LINE;
5967 if (strEquCI(name, "NONE") || strEquCI(name, "NOTHING")) {
5968 optWriteType = 'n';
5969 if (currLine[0]) fatal("too many expressions");
5970 return PI_SKIP_LINE;
5972 if (strEquCI(name, "SCL") || strEquCI(name, "RUNSCL") || strEquCI(name, "SCLRUN")) {
5973 optWriteType = 'S';
5974 optRunSCL = (name[3] != 0 ? -1 : 0); /* no boot */
5975 piTapParseLoaderName();
5976 return PI_SKIP_LINE;
5978 if (strEquCI(name, "SCLMONO") || strEquCI(name, "RUNSCLMONO") || strEquCI(name, "SCLMONORUN")) {
5979 optWriteType = 'M';
5980 optRunSCL = (name[7] != 0 ? -1 : 0); /* no boot */
5981 piTapParseLoaderName();
5982 return PI_SKIP_LINE;
5984 if (strEquCI(name, "SCLBOOT") || strEquCI(name, "BOOTSCL")) {
5985 optWriteType = 'S';
5986 optRunSCL = 1; /* with boot */
5987 piTapParseLoaderName();
5988 return PI_SKIP_LINE;
5990 if (strEquCI(name, "SCLMONOBOOT") || strEquCI(name, "BOOTSCLMONO")) {
5991 optWriteType = 'M';
5992 optRunSCL = 1; /* with boot */
5993 piTapParseLoaderName();
5994 return PI_SKIP_LINE;
5997 if (strEquCI(name, "DSKBOOT") || strEquCI(name, "BOOTDSK") ||
5998 strEquCI(name, "DSK180BOOT") || strEquCI(name, "BOOTDSK180"))
6000 optWriteType = '3';
6001 optRunDSK = 1; /* with boot */
6002 optDskType = P3DSK_PCW_SS; // +3DOS 180K disk
6003 piTapParseLoaderName();
6004 return PI_SKIP_LINE;
6007 if (strEquCI(name, "DSK720BOOT") || strEquCI(name, "BOOTDSK720")) {
6008 optWriteType = '3';
6009 optRunDSK = 1; /* with boot */
6010 optDskType = P3DSK_PCW_DS; // +3DOS 720K disk
6011 piTapParseLoaderName();
6012 return PI_SKIP_LINE;
6015 if (strEquCI(name, "DSK") || strEquCI(name, "DSK180")) {
6016 optWriteType = '3';
6017 optRunDSK = 0; /* without boot */
6018 optDskType = P3DSK_PCW_SS; // +3DOS 180K disk
6019 piTapParseLoaderName();
6020 return PI_SKIP_LINE;
6023 if (strEquCI(name, "DSK720")) {
6024 optWriteType = '3';
6025 optRunDSK = 0; /* without boot */
6026 optDskType = P3DSK_PCW_DS; // +3DOS 720K disk
6027 piTapParseLoaderName();
6028 return PI_SKIP_LINE;
6031 fatal("invalid default output type: %s", name);
6035 ///////////////////////////////////////////////////////////////////////////////
6036 // line processor
6038 static void processCurrentLine (void) {
6039 if (!currSrcLine) return; // do nothing
6040 loadCurSrcLine();
6041 ur_assert(asmMode != AMODE_UFO); // the thing that should not be
6042 processLabel();
6043 for (;;) {
6044 char *str, *ee, name[66];
6045 UrAsmOp *op;
6046 int len;
6047 const char *errpos;
6048 removeSpacesAndColons();
6049 // skip spaces and ':'
6050 if (!currLine[0]) { nextSrcLine(); break; }
6051 // try to find and process command
6052 str = currLine; //while (*str && isSpace(*str)) ++str; // skip spaces
6053 // find command end
6054 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ',' && *ee != ':'; ++ee) {}
6055 // get command, if any
6056 if (ee != str && ee-str <= 64) {
6057 MacroDef *mc;
6058 memset(name, 0, sizeof(name));
6059 memmove(name, str, ee-str);
6060 /* UrForth macro? */
6061 uint32_t fwmacro = ufoIsMacro(name);
6062 if (fwmacro != 0) {
6063 // ok, do it
6064 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6065 memmove(currLine, str, strlen(str)+1);
6066 ufoMacroRun(fwmacro, currLine, currSrcLine->fname, currSrcLine->lineNo);
6067 /* only one macro per line! */
6068 nextSrcLine(); // skip it
6069 break;
6071 /* known command? */
6072 op = urFindOp(name);
6073 if (op) {
6074 // ok, do it
6075 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6076 memmove(currLine, str, strlen(str)+1);
6077 urCurrentOp = op;
6078 if (op->fn()) {
6079 urCurrentOp = NULL;
6080 nextSrcLine(); // skip it
6081 break;
6083 urCurrentOp = NULL;
6084 continue;
6086 /* known macro? */
6087 if ((mc = findMacro(name)) != NULL) {
6088 // ok, do it
6089 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6090 memmove(currLine, str, strlen(str)+1);
6091 processMacro(mc);
6092 /* only one macro per line! */
6093 break;
6096 // with reservation management
6097 again_from_reserve:
6098 len = urasm_opasm(currLine, pc, disp, &errpos);
6099 if (len < 0) fatalUrLib(len);
6100 // check for reservation
6101 if (len > 0) {
6102 if (reserveHit(pc, len)) {
6103 insert_jump:
6104 if (pc != disp) fatal("trying to use reserved areas with phased code");
6105 reserveReleaseUsed(pc, len);
6106 int nextaddr = reservedFindNextSuitableFreeFrom(pc, pc+len);
6107 if (nextaddr < 0) fatal("codegen started in reserved area #%04X", pc);
6108 // insert jump to the new area, and retry
6109 fprintf(stderr, "...inserting jump from #%04X to #%04X\n", (unsigned)pc, (unsigned)nextaddr);
6110 // can we insert a JP? it is faster than JR
6111 if (memresv[pc] || memresv[pc+1]) fatal("internal reserve area manager error at #%04X", pc);
6112 if (!memresv[pc+2]) {
6113 // use JP
6114 emitByte(0xc3U); // JP
6115 emitWord((uint16_t)nextaddr);
6116 } else {
6117 // use JR
6118 int jrdest = nextaddr-(pc+2U);
6119 if (jrdest >= 128) fatal("internal reserve area manager error at #%04X (jr dist)", pc);
6120 emitByte(0x18U); // JR
6121 emitByte((uint8_t)jrdest);
6123 disp = pc = (uint16_t)nextaddr;
6124 goto again_from_reserve;
6125 } else {
6126 // did we compiled JP/JR?
6127 if (getByte(pc) == 0xc3U || getByte(pc) == 0x18U) {
6128 // no room for jump?
6129 if (memresv[(pc+len)&0xffffU] || memresv[(pc+len+1)&0xffffU] || memresv[(pc+len+2)&0xffffU]) {
6130 if (pc != disp) fatal("trying to use reserved areas with phased code");
6131 // find next free area
6132 int next = pc+len;
6133 while (next >= 0 && next < 65536) {
6134 next = reserveFindFreeFrom(next);
6135 if (next < 0) break;
6136 // has room for JP there?
6137 if (!memresv[next] && !memresv[(next+1)&0xffffU] && !memresv[(next+2)&0xffffU]) break;
6138 // no room for JP, has room for JP?
6139 if (!memresv[next] && !memresv[(next+1)&0xffffU]) {
6140 // check if jr can fit
6141 const int nj = reservedFindNextJumpableFreeFrom(next, next+2);
6142 if (nj >= 0) break;
6144 next = reserveFindReservedFrom(next);
6146 if (next >= 0) disp = pc = (next-len)&0xffffU; // so "+len" will make PC right
6148 } else {
6149 // check if we still have enough room for a possible jp/jr
6150 if (memresv[(pc+len)&0xffffU] || memresv[(pc+len+1)&0xffffU]) goto insert_jump;
6151 // we have at least a room for JR, check if we have a room for JP
6152 if (!memresv[(pc+len+2)&0xffffU]) {
6153 // no room for JP, check if jr will be enough
6154 int next = reservedFindNextSuitableFreeFrom(pc, pc+len+2);
6155 if (next >= 0 && next-(pc+len+2u) >= 128) goto insert_jump;
6160 pc += len;
6161 disp += len;
6162 if (len >= 0 && errpos) {
6163 memmove(currLine, errpos+1, strlen(errpos));
6164 } else {
6165 nextSrcLine(); // skip it
6166 break;
6172 ///////////////////////////////////////////////////////////////////////////////
6173 // start inline UrForth code
6175 static int piSTARTFORTH (void) {
6176 checkOperatorEnd();
6177 asmMode = AMODE_UFO;
6178 ufoInlineRun();
6179 asmMode = AMODE_NORMAL;
6180 return PI_SKIP_LINE;
6184 ///////////////////////////////////////////////////////////////////////////////
6185 // setup instructions
6187 static void registerInstructions (void) {
6188 urAddOp("DISPLAY", piDISPLAY);
6189 urAddOp("DISPLAY0", piDISPLAY0);
6190 urAddOp("DISPLAYA", piDISPLAYA);
6191 urAddOp("DISPHEX", piDISPHEX);
6192 urAddOp("DISPHEX0", piDISPHEX0);
6193 urAddOp("DISPHEXA", piDISPHEXA);
6195 urAddOp("DEFFMT", piDEFFMT);
6196 urAddOp("$MODEL", piMODEL);
6197 urAddOp("$SAVECODE", piSAVECODE);
6199 urAddOp("MACRO", piMACRO);
6200 urAddOp("ENDM", piENDM);
6202 urAddOp("ORG", piORG);
6203 urAddOp("DISP", piDISP);
6204 urAddOp("ENDDISP", piENDDISP);
6205 urAddOp("PHASE", piDISP);
6206 urAddOp("DEPHASE", piENDDISP);
6207 urAddOp("UNPHASE", piENDDISP);
6208 urAddOp("ALIGN", piALIGN);
6209 urAddOp("DISPALIGN", piDISPALIGN);
6210 urAddOp("PHASEALIGN", piDISPALIGN);
6211 urAddOp("ENT", piENT);
6212 urAddOp("CLR", piCLR);
6213 urAddOp("RESERVE", piRESERVE);
6215 urAddOp("INCLUDE", piINCLUDE);
6216 urAddOp("INCBIN", piINCBIN);
6217 urAddOp("DATABIN", piDATABIN);
6218 urAddOp("CODEBIN", piCODEBIN);
6220 urAddOp("MODULE", piMODULE);
6221 urAddOp("ENDMODULE", piENDMODULE);
6223 urAddOp("DUP", piDUP);
6224 urAddOp("EDUP", piEDUP);
6225 urAddOp("REPT", piREPT);
6226 urAddOp("ENDR", piENDR);
6227 // structures
6228 urAddOp("STRUCT", piSTRUCT);
6229 urAddOp("ENDS", piENDS);
6230 // pasmo support
6231 urAddOp("END", piEND_PASMO);
6233 urAddOp("IF", piIF);
6234 urAddOp("IFX", piIFX);
6235 urAddOp("ELSE", piELSE);
6236 urAddOp("ELSIF", piELSIF);
6237 urAddOp("ELSIFX", piELSIFX);
6238 urAddOp("ELSEIF", piELSIF);
6239 urAddOp("ELSEIFX", piELSIFX);
6240 urAddOp("ENDIF", piENDIF);
6242 urAddOp("DEFINCR", piDEFINCR);
6243 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
6244 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
6245 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
6246 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
6247 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
6248 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
6249 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
6250 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
6252 urAddOp("$ERROR", piERROR);
6253 urAddOp("$WARNING", piWARNING);
6255 urAddOp("$PRINTF", piPRINTF);
6256 urAddOp("$PRINTF0", piPRINTF0);
6257 urAddOp("$PRINTFA", piPRINTFA);
6259 urAddOp("$MATHMODE", piMATHMODE);
6260 urAddOp("$REFOPT", piREFOPT);
6261 urAddOp("$CASTRATES", piCASTRATES);
6263 urAddOp("$START_FORTH", piSTARTFORTH);
6267 ///////////////////////////////////////////////////////////////////////////////
6268 // !0: invalid label
6270 static inline void fnSkipSpaces (const char *expr) {
6271 while (*expr && isSpace(*expr)) ++expr;
6272 return expr;
6277 UR_FORCE_INLINE char fnNextChar (const char *expr) {
6278 while (*expr && isSpace(*expr)) ++expr;
6279 return *expr;
6283 #define FN_SKIP_BLANKS do { \
6284 while (*expr && isSpace(*expr)) ++expr; \
6285 } while (0)
6287 #define FN_CHECK_END do { \
6288 while (*expr && isSpace(*expr)) ++expr; \
6289 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
6290 ++expr; \
6291 } while (0)
6294 #define FN_CHECK_COMMA do { \
6295 while (*expr && isSpace(*expr)) ++expr; \
6296 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
6297 ++expr; \
6298 } while (0)
6301 static const char *readLabelName (char *buf, const char *expr) {
6302 int pos = 0;
6304 while (*expr && isSpace(*expr)) ++expr;
6305 for (;;) {
6306 char ch = *expr++;
6308 if (pos >= 128) return NULL;
6309 if (ch == ')') { --expr; break; }
6310 if (!ch) break;
6311 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
6312 buf[pos++] = ch;
6313 } else {
6314 break;
6317 if (pos < 1) return NULL;
6318 buf[pos] = '\0';
6319 if (!urasm_is_valid_name(buf)) return NULL;
6320 return expr;
6324 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
6325 char lbl[130];
6327 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
6328 FN_CHECK_END;
6329 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
6330 return expr;
6333 static const char *fnDefined (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) { return fnDefKn(res, expr, addr, donteval, defined, error, UR_QTYPE_DEFINED); }
6334 static const char *fnKnown (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) { return fnDefKn(res, expr, addr, donteval, defined, error, UR_QTYPE_KNOWN); }
6337 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6338 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6339 FN_CHECK_END;
6340 if (!donteval) res->val = (res->val%256 ? 0 : 1);
6341 return expr;
6345 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6346 urasm_exprval_t v0, v1;
6348 urasm_exprval_init(&v0);
6349 urasm_exprval_init(&v1);
6350 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
6351 if (*error) return expr;
6352 FN_CHECK_COMMA;
6353 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
6354 if (*error) return expr;
6355 FN_CHECK_END;
6356 if (!donteval) res->val = (v0.val/256 == v1.val/256);
6357 return expr;
6361 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6362 urasm_exprval_t v0, v1;
6363 urasm_exprval_init(&v0);
6364 urasm_exprval_init(&v1);
6365 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
6366 if (*error) return expr;
6367 FN_SKIP_BLANKS;
6368 if (fnNextChar(expr) == ',') {
6369 FN_CHECK_COMMA;
6370 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
6371 if (*error) return expr;
6372 } else {
6373 v1.val = 256;
6375 FN_CHECK_END;
6376 if (!donteval) {
6377 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
6378 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
6380 return expr;
6384 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6385 const char *ee = expr;
6386 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6387 FN_CHECK_END;
6388 if (!donteval) {
6389 if (res->fixuptype == UR_FIXUP_HIBYTE) {
6390 *error = UR_EXPRERR_FUNC; return ee;
6392 res->fixuptype = UR_FIXUP_LOBYTE;
6393 res->val &= 0xff;
6395 return expr;
6399 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6400 const char *ee = expr;
6401 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6402 FN_CHECK_END;
6403 if (!donteval) {
6404 if (res->fixuptype == UR_FIXUP_LOBYTE) {
6405 *error = UR_EXPRERR_FUNC; return ee;
6407 res->fixuptype = UR_FIXUP_HIBYTE;
6408 res->val = (res->val>>8)&0xff;
6410 return expr;
6414 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6415 const char *ee = expr;
6416 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6417 FN_CHECK_END;
6418 if (!donteval) {
6419 if (res->fixuptype != UR_FIXUP_NONE) {
6420 *error = UR_EXPRERR_FUNC; return ee;
6422 res->val = abs(res->val);
6424 return expr;
6428 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6429 const char *ee = expr;
6430 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6431 FN_CHECK_END;
6432 if (!donteval) {
6433 if (res->fixuptype != UR_FIXUP_NONE) {
6434 *error = UR_EXPRERR_FUNC; return ee;
6436 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
6438 return expr;
6442 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6443 int32_t scrbase = 0x4000;
6444 int32_t x, y;
6445 const char *ee = expr;
6446 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
6447 // first argument is `x`
6448 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6449 x = res->val;
6450 // second argument is `y`
6451 FN_CHECK_COMMA;
6452 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6453 y = res->val*nmul;
6454 // optional third arg is screen base
6455 FN_SKIP_BLANKS;
6456 if (expr[0] == ',') {
6457 FN_CHECK_COMMA;
6458 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6459 scrbase = res->val&0xffff;
6461 FN_CHECK_END;
6462 if (!donteval) {
6463 //urasm_exprval_clear(res);
6464 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
6465 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
6466 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
6467 res->val = scrbase+
6468 (y/64)*2048+
6469 (y%8)*256+
6470 ((y%64)/8)*32+
6472 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
6474 return expr;
6478 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6479 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
6482 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6483 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
6487 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6488 int32_t scrbase = 0x4000;
6489 int32_t x, y;
6490 const char *ee = expr;
6491 // first argument is `x`
6492 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6493 x = res->val;
6494 // second argument is `y`
6495 FN_CHECK_COMMA;
6496 urasm_exprval_clear(res);
6497 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6498 y = res->val;
6499 // optional third arg is screen base
6500 FN_SKIP_BLANKS;
6501 if (expr[0] == ',') {
6502 FN_CHECK_COMMA;
6503 urasm_exprval_clear(res);
6504 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6505 scrbase = res->val&0xffff;
6507 urasm_exprval_clear(res);
6508 FN_CHECK_END;
6509 if (!donteval) {
6510 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
6511 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
6512 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
6513 res->val = scrbase+6144+y*32+x;
6515 return expr;
6519 static const char *fnMArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6520 const char *ee = expr;
6521 // argument is macro argument name
6522 expr = strSkipSpacesConst(expr);
6523 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
6524 ++expr;
6525 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
6526 const char *nend = expr+1;
6527 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
6528 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
6529 char name[128];
6530 memset(name, 0, sizeof(name));
6531 memcpy(name, expr, nend-expr);
6532 expr = nend;
6533 // parse index
6534 int index = 0;
6535 int has_index = 0;
6536 expr = strSkipSpacesConst(expr);
6537 if (expr[0] == '[') {
6538 expr = strSkipSpacesConst(expr+1);
6539 if (expr[0] != ']') {
6540 has_index = 1;
6541 int doneg = 0;
6542 if (expr[0] == '+') ++expr;
6543 else if (expr[0] == '-') { doneg = 1; ++expr; }
6544 index = 0;
6545 while (isDigit(expr[0])) {
6546 index = index*10+(expr[0]-'0');
6547 if (index >= 0x1fffffff) fatal("`margtostr` index too high");
6548 ++expr;
6550 expr = strSkipSpacesConst(expr);
6551 if (doneg) index = -index;
6553 if (expr[0] != ']') fatal("`margtostr` invalid index syntax");
6554 ++expr;
6556 // done
6557 FN_CHECK_END;
6558 if (curmacro == NULL) fatal("`margtostr` outside of macro");
6559 if (!donteval) {
6560 for (int f = 0; f < curmacro->mac->argc; ++f) {
6561 if (strEquCI(name, curmacro->mac->argnames[f])) {
6562 // found argument, convert it to string
6563 if (!has_index) {
6564 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
6565 strcpy(res->str, curmacro->argvals[f]);
6566 } else {
6567 const size_t slen = strlen(curmacro->argvals[f]);
6568 if (index < 0) {
6569 index = -index;
6570 if (index > slen) fatal("index out of string");
6571 index = (int)slen-index;
6573 if (index >= slen) fatal("index out of string");
6574 res->str = realloc(res->str, 2);
6575 res->str[0] = curmacro->argvals[f][index];
6576 res->str[1] = 0;
6578 // lowercase it
6579 for (char *s = res->str; *s; ++s) *s = toLower(*s);
6580 //fprintf(stderr, "<%s>\n", res->str);
6581 if (res->str[0]) {
6582 if (res->str[1]) {
6583 res->val = ((unsigned char)res->str[0]);
6584 res->val |= ((unsigned char)res->str[1])<<8;
6585 } else {
6586 res->val = (unsigned char)res->str[0];
6588 } else {
6589 res->val = 0;
6591 return expr;
6594 fatal("unknown macro argument '%s'", name);
6596 return expr;
6600 static const char *fnStrLen (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6601 expr = strSkipSpacesConst(expr);
6602 int stlen = 0;
6603 switch (expr[0]) {
6604 case '"': case '\'': /* string literal? */
6606 char *a = (char *)expr+1;
6607 (void)parseStr(&a, expr[0], &stlen);
6608 expr = (const char *)a;
6610 break;
6611 default: /* expression */
6613 urasm_exprval_t r;
6614 urasm_exprval_init(&r);
6615 expr = urasm_expr_ex(&r, expr, disp, &donteval, defined, error);
6616 if (*error) fatalUrLib(*error);
6617 if (!r.str) fatal("string expected for `strlen()`");
6618 stlen = (int)strlen(r.str);
6619 urasm_exprval_clear(&r);
6621 break;
6623 FN_CHECK_END;
6624 if (!donteval) {
6625 urasm_exprval_setint(res, stlen);
6627 return expr;
6631 static void registerFunctions (void) {
6632 urasm_expr_register_func("defined", fnDefined);
6633 urasm_expr_register_func("known", fnKnown);
6634 urasm_expr_register_func("aligned256", fnAligned256);
6635 urasm_expr_register_func("align", fnAlign);
6636 urasm_expr_register_func("sameseg", fnSameSeg);
6637 urasm_expr_register_func("low", fnLow);
6638 urasm_expr_register_func("high", fnHigh);
6639 urasm_expr_register_func("abs", fnAbs);
6640 urasm_expr_register_func("bswap", fnBSwap);
6642 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
6643 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
6644 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
6646 urasm_expr_register_func("marg2str", fnMArgToStr);
6647 urasm_expr_register_func("strlen", fnStrLen);
6651 ///////////////////////////////////////////////////////////////////////////////
6652 // preparing another pass
6654 static void initPass (void) {
6655 asmMode = AMODE_NORMAL;
6656 currSrcLine = asmText;
6657 currModule = NULL;
6658 pc = start_pc;
6659 disp = start_disp;
6660 ent = start_ent;
6661 inTapeBlock = 0;
6662 tapeXorB = 0;
6663 wasOrg = 0;
6664 wasClr = 0;
6665 ifCount = 0;
6666 dfi_free();
6667 //defIncr = 0;
6668 lblOptMakeU2 = 0;
6669 curmacronum = 0;
6670 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
6671 lastSeenGlobalLabel = NULL;
6672 //lastSeenGlobalLabel = strdup(" [MAIN] ");
6673 modulesResetSeen();
6674 prepareMemory();
6675 urasm_use_old_priorities = 0;
6676 strcpy(lastFindLabelName, "");
6677 strcpy(currSeenLabel, "");
6681 static int postPass (void) {
6682 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
6683 lastSeenGlobalLabel = NULL;
6684 if (checkLabels()) return -1;
6685 if (ifCount != 0) fatal("unbalanced IFs");
6686 return 0;
6690 ///////////////////////////////////////////////////////////////////////////////
6691 static int labelCmp (const void *aa, const void *bb) {
6692 if (aa == bb) return 0;
6693 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
6694 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
6695 return
6696 a->value < b->value ? -1 :
6697 a->value > b->value ? 1 :
6702 static __attribute__((unused)) int strStartsWith (const char *s, const char *pat) {
6703 if (!pat || !pat[0]) return 0;
6704 if (!s || !s[0]) return 0;
6705 while (*s && *pat) {
6706 if (*s != *pat) return 0;
6707 ++s;
6708 ++pat;
6710 return (*pat == 0);
6714 static int isGoodLabelForRef (const UrLabelInfo *ll) {
6715 if (!ll) return 0;
6718 // do not output `=` labels
6719 if (ll->type == LBL_TYPE_ASS) return 0;
6720 // do not output struct labels
6721 if (ll->type == LBL_TYPE_STOFFS) return 0;
6724 if (urasm_dump_all_labels) {
6725 if (strStartsWith(ll->name, "UFO_") || strStartsWith(ll->name, "USE_")) return 0;
6726 return 1;
6729 for (const char *s = ll->name; *s; ++s) {
6730 const char ch = *s;
6731 if (ch >= 'A' && ch <= 'Z') continue;
6732 if (ch >= '0' && ch <= '9') continue;
6733 if (ch == '_') continue;
6734 return 1;
6737 return 0;
6741 static void writeLabelsFile (const char *fname) {
6742 if (!fname || !fname[0]) return;
6744 // count labels
6745 int lcount = 0;
6746 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) {
6747 if (isGoodLabelForRef(ll)) ++lcount;
6750 UrLabelInfo **larr = NULL;
6751 if (lcount > 0) {
6752 // create labels array
6753 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
6754 lcount = 0;
6755 for (UrLabelInfo *ll = labels; ll; ll = ll->next) {
6756 if (isGoodLabelForRef(ll)) larr[lcount++] = ll;
6758 // sort labels
6759 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
6762 // write labels
6763 FILE *fo = fopen(fname, "w");
6764 if (lcount > 0) {
6765 for (int f = 0; f < lcount; ++f) {
6766 UrLabelInfo *ll = larr[f];
6767 //if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
6768 if (!ll->name) continue; // just in case
6769 if (ll->name[0] == '{') continue; // we don't have these, but it is still reserved
6770 if (ll->value < 0) {
6771 fprintf(fo, "%d %s", ll->value, ll->name);
6772 } else {
6773 fprintf(fo, "#%04X %s", (unsigned)ll->value, ll->name);
6775 fprintf(fo, " ");
6776 switch (ll->type) {
6777 case LBL_TYPE_VERY_SPECIAL: fprintf(fo, "WTF"); break;
6778 case LBL_TYPE_UNKNOWN: fprintf(fo, "UNKNOWN"); break;
6779 case LBL_TYPE_ASS: fprintf(fo, "assign"); break;
6780 case LBL_TYPE_EQU: fprintf(fo, "equ"); break;
6781 case LBL_TYPE_CODE: fprintf(fo, "code"); break;
6782 case LBL_TYPE_STOFS: fprintf(fo, "stofs"); break;
6783 case LBL_TYPE_DATA: fprintf(fo, "data"); break;
6784 default: fprintf(fo, "WTF"); break;
6786 fputc('\n', fo);
6788 free(larr);
6791 fclose(fo);
6795 ///////////////////////////////////////////////////////////////////////////////
6796 // options
6798 static struct option longOpts[] = {
6799 {"org", required_argument, NULL, 600},
6800 {"define", required_argument, NULL, 601},
6801 {"defzero", required_argument, NULL, 602},
6802 {"outdir", required_argument, NULL, 660},
6803 {"reffile", optional_argument, NULL, 669},
6804 {"outfile", required_argument, NULL, 665},
6805 {"castrates", optional_argument, NULL, 656},
6807 {"sna", 0, NULL, 's'},
6808 {"sna128", 0, NULL, 'S'},
6809 {"tap", 0, NULL, 't'},
6810 {"autotap", 0, NULL, 'T'},
6811 {"raw", 0, NULL, 'r'},
6812 {"autodmb", 0, NULL, 'B'},
6813 {"dmb", 0, NULL, 'b'},
6814 {"none", 0, NULL, 'n'},
6815 {"help", 0, NULL, 'h'},
6816 {"hob", 0, NULL, 'H'},
6817 {"scl", 0, NULL, 'l'},
6818 {"autoscl", 0, NULL, 'L'},
6819 {"monoscl", 0, NULL, 'm'},
6820 {"autosclmono", 0, NULL, 'M'},
6821 {"dsk", 0, NULL, 690},
6822 {"dsk720", 0, NULL, 691},
6823 {"adsk", 0, NULL, 692},
6824 {"adsk720", 0, NULL, 693},
6825 {"fixups", optional_argument, NULL, 'F'},
6827 {NULL, 0, NULL, 0}
6831 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
6834 static void usage (const char *pname) {
6835 printf(
6836 "usage: %s [options] infile\n"
6837 "default infiles:", pname);
6838 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
6839 printf("\n"
6840 "options:\n"
6841 " -s --sna write 48K .SNA file with autostart\n"
6842 " -S --sna128 write 148K .SNA file with autostart\n"
6843 " -t --tap write .tap file\n"
6844 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
6845 " -r --raw write raw file(s)\n"
6846 " -b --dmb write DMB file\n"
6847 " -B --autodmb write DMB file with autostart\n"
6848 " -H --hob write HoBeta code file(s)\n"
6849 " -l --scl write SCL TR-DOS archive\n"
6850 " -L --autoscl write autostarting SCL TR-DOS archive\n"
6851 " -m --monoscl write SCL with monoloader\n"
6852 /*" -M --monosclauto write autorun SCL with monoloader\n"*/
6853 " --dsk write +3DOS 180KB disk\n"
6854 " --dsk720 write +3DOS 720KB disk\n"
6855 " --adsk write +3DOS 180KB disk with autostart\n"
6856 " --adsk720 write +3DOS 720KB disk with autostart\n"
6857 " -n --none write nothing\n"
6858 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
6859 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
6860 " text: .txt file\n"
6861 " asm: address lists with counters\n"
6862 " asmdiff: 2nd and other addresses are relative, with counters\n"
6863 " asmz: address lists, with 0 end markers\n"
6864 " -h --help this help\n"
6865 "specials:\n"
6866 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
6867 " --org xxx set ORG\n"
6868 " --define val perform 'val EQU 1'\n"
6869 " --defzero val perform 'val EQU 0'\n"
6870 " --outdir dir output dir for resulting files (default: current)\n"
6871 " --outfile fname output file name for image formats (default: main asm file name)\n"
6872 " --castrates allow '&abc' hex numbers (and disable '&b' prefix)\n"
6877 ///////////////////////////////////////////////////////////////////////////////
6878 //#include "urforth.c"
6880 __attribute__((noreturn))
6881 void ufoFatalError (void) {
6882 longjmp(errJP, 666);
6885 uint32_t ufoZXGetU8 (uint32_t addr) {
6886 addr &= 0xffffU;
6887 return getByte(addr);
6890 void ufoZXPutU8 (uint32_t addr, uint32_t v) {
6891 addr &= 0xffffU;
6892 v &= 0xffU;
6893 putByte(addr, v);
6896 uint32_t ufoZXGetU16 (uint32_t addr) {
6897 addr &= 0xffffU;
6898 return getWord(addr);
6901 void ufoZXPutU16 (uint32_t addr, uint32_t v) {
6902 addr &= 0xffffU;
6903 v &= 0xffffU;
6904 putWord(addr, v);
6907 void ufoZXEmitU8 (uint32_t v) {
6908 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
6909 emitByte(v&0xffU);
6912 void ufoZXEmitU16 (uint32_t v) {
6913 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
6914 emitWord(v&0xffffU);
6917 int ufoZXGetReserved (uint32_t addr) {
6918 return memresv[addr&0xffffU];
6921 void ufoZXSetReserved (uint32_t addr, int resvflag) {
6922 memresv[addr&0xffffU] = resvflag;
6925 int ufoZXGetPass (void) {
6926 return pass;
6929 uint32_t ufoZXGetOrg (void) {
6930 return pc;
6933 void ufoZXSetOrg (uint32_t addr) {
6934 if (addr > 0xffff) ufoFatal("invalid ORG address: %u", addr);
6935 pc = disp = (uint16_t)addr;
6936 if (!wasOrg) {
6937 wasOrg = 1; // so next `ORG` will not reset it
6938 ent = (uint16_t)addr;
6942 uint32_t ufoZXGetDisp (void) {
6943 return disp;
6946 void ufoZXSetDisp (uint32_t addr) {
6947 if (addr > 0xffff) ufoFatal("invalid DISP address: %u", addr);
6948 disp = (uint16_t)addr;
6951 uint32_t ufoZXGetEnt (void) {
6952 return (wasOrg ? (uint32_t)ent : ~0u);
6955 void ufoZXSetEnt (uint32_t addr) {
6956 if (addr > 0xffff) ufoFatal("invalid ENT address: %u", addr);
6957 wasOrg = 1; // so next `ORG` will not reset it
6958 ent = (uint16_t)addr;
6961 static int ufoZXLType2UFO (int type) {
6962 switch (type) {
6963 case LBL_TYPE_VERY_SPECIAL: return UFO_ZX_LABEL_UNDEFINED;
6964 case LBL_TYPE_UNKNOWN: return UFO_ZX_LABEL_UNKNOWN;
6965 case LBL_TYPE_ASS: return UFO_ZX_LABEL_VAR;
6966 case LBL_TYPE_EQU: return UFO_ZX_LABEL_EQU;
6967 case LBL_TYPE_CODE: return UFO_ZX_LABEL_CODE;
6968 case LBL_TYPE_DATA: return UFO_ZX_LABEL_DATA;
6969 case LBL_TYPE_STOFS: return UFO_ZX_LABEL_STOFS;
6971 ufoFatal("WTF?! unknown label type %d", type);
6974 int ufoZXGetLabelType (const char *name) {
6975 if (name == NULL || name[0] == 0) return UFO_ZX_LABEL_UNDEFINED;
6976 UrLabelInfo *lbl = findAddLabel(name);
6977 if (lbl == NULL) return UFO_ZX_LABEL_UNDEFINED;
6978 return ufoZXLType2UFO(lbl->type);
6981 // return !0 to stop; returns cb result
6982 uint32_t ufoZXForeachLabel (uint32_t (*cb) (const char *name, int type, int value, void *udata), void *udata) {
6983 uint32_t res = 0;
6984 UrLabelInfo *c = labels;
6985 while (res == 0 && c != NULL) {
6986 if (c->type != LBL_TYPE_VERY_SPECIAL && c->type != LBL_TYPE_UNKNOWN) {
6987 res = cb(c->name, c->type, c->value, udata);
6990 return res;
6993 // this also creates labels
6994 void ufoZXSetLabelValue (const char *name, int type, int value) {
6995 if (name == NULL || name[0] == 0) ufoFatal("empty label name");
6997 // convert label type
6998 switch (type) {
6999 case UFO_ZX_LABEL_UNDEFINED: ufoFatal("cannot set label with undefined type");
7000 case UFO_ZX_LABEL_UNKNOWN: ufoFatal("cannot set label with unknown type");
7001 case UFO_ZX_LABEL_VAR: type = LBL_TYPE_ASS; break;
7002 case UFO_ZX_LABEL_EQU: type = LBL_TYPE_EQU; break;
7003 case UFO_ZX_LABEL_CODE: type = LBL_TYPE_CODE; break;
7004 case UFO_ZX_LABEL_DATA: type = LBL_TYPE_DATA; break;
7005 case UFO_ZX_LABEL_STOFS: type = LBL_TYPE_STOFS; break;
7006 default: ufoFatal("invalid label type %d", type);
7009 UrLabelInfo *lbl = findAddLabel(name);
7011 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != type) {
7012 ufoFatal("invalid label '%s' type", name);
7014 if (type != LBL_TYPE_ASS) {
7015 if (lbl->type >= 0 && lbl->value != value) ufoFatal("invalid label '%s' value", name);
7018 lbl->value = value;
7019 if (lbl->type == LBL_TYPE_UNKNOWN) lbl->type = type;
7022 int ufoZXGetLabelValue (const char *name) {
7023 if (name == NULL || name[0] == 0) return 0;
7024 UrLabelInfo *lbl = findAddLabel(name);
7025 return (lbl ? lbl->value : 0);
7028 const char *ufoGetSrcLine (const char **fname, int *lnum) {
7029 SourceLine *sl = nextUFOSrcLine();
7030 if (sl == NULL) return NULL;
7031 *lnum = sl->lineNo;
7032 *fname = sl->fname;
7033 return (sl->line != NULL ? sl->line : "");
7036 char *ufoCreateIncludeName (const char *fname, int assystem, const char *lastIncPath) {
7037 assystem = (assystem ? -666 : -669);
7038 if (lastIncPath == NULL) lastIncPath = ufoIncludeDir;
7039 return createIncludeName(fname, assystem, NULL, lastIncPath);
7042 void ufoZXPostInit (void) {
7046 ///////////////////////////////////////////////////////////////////////////////
7047 // main
7049 int main (int argc, char *argv[]) {
7050 int res = 0, c;
7051 const char *pname = argv[0];
7052 char *inFile = NULL;
7053 char **defines = NULL, **values = NULL;
7054 int defcount = 0;
7055 int wantref = 0;
7057 initLabelBuckets();
7058 modulesClear();
7059 initInclideDir();
7060 initUFEInclideDir();
7062 urasm_getbyte = getByte;
7063 urasm_putbyte = putByte;
7064 urasm_label_by_name = findLabelCB;
7065 urasm_getval = getValueCB;
7066 urasm_expand = expandCB;
7067 urasm_fixup_operand = fixupOperandCB;
7069 //strcpy(tapeLoaderName, "cargador ");
7070 tapeLoaderName[0] = 0;
7072 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
7073 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLlMm", longOpts, NULL)) >= 0) {
7074 switch (c) {
7075 case '?': return 1;
7076 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
7077 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
7078 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
7079 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
7080 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break; /* with boot */
7081 case 'l': optRunSCL = -1; optWriteType = 'S'; optWTChanged = 1; break; /* no boot */
7082 case 'm': optRunSCL = -1; optWriteType = 'M'; optWTChanged = 1; break; /* no boot */
7083 case 'M': optRunSCL = 1; optWriteType = 'M'; optWTChanged = 1; break; /* with boot */
7084 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
7085 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
7086 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
7087 case 'h': usage(pname); res = 0; goto earlyerrquit;
7088 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
7089 case 'F':
7090 optWriteFixups = 1;
7091 if (optarg != NULL) {
7092 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
7093 optFixupType = 1;
7094 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
7095 optFixupType = 2;
7096 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
7097 optFixupType = 3;
7098 } else if (strcmp(optarg, "text") == 0) {
7099 optFixupType = 0;
7100 } else {
7101 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
7102 return 1;
7105 break;
7106 case 600: // org
7107 c = atoi(optarg);
7108 //fprintf(stderr, "ORG: %d\n", c);
7109 if (c < 0 || c > 65535) {
7110 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
7111 return 1;
7113 start_pc = start_disp = start_ent = c;
7114 break;
7115 case 601: // define
7116 //fprintf(stderr, "define: [%s]\n", optarg);
7117 defines = realloc(defines, sizeof(char *)*(defcount+1));
7118 values = realloc(values, sizeof(char *)*(defcount+1));
7119 defines[defcount] = strdup(optarg);
7120 values[defcount] = strdup("1");
7121 ++defcount;
7122 break;
7123 case 602: // defzero
7124 //fprintf(stderr, "defzero: [%s]\n", optarg);
7125 defines = realloc(defines, sizeof(char *)*(defcount+1));
7126 values = realloc(values, sizeof(char *)*(defcount+1));
7127 defines[defcount] = strdup(optarg);
7128 values[defcount] = strdup("0");
7129 ++defcount;
7130 break;
7131 case 660: // outdir
7132 if (optOutputDir != NULL) free(optOutputDir);
7133 optOutputDir = strdup(optarg);
7134 break;
7135 case 665: // outdir
7136 if (optOutputFile != NULL) free(optOutputFile);
7137 optOutputFile = strdup(optarg);
7138 break;
7139 case 669: // reffile
7140 if (refFileName) free(refFileName);
7141 refFileName = (optarg ? strdup(optarg) : NULL);
7142 wantref = 1;
7143 break;
7144 case 690: case 692: // +3DOS 180K disk
7145 optRunDSK = (c == 692);
7146 optDskType = P3DSK_PCW_SS;
7147 optWriteType = '3';
7148 optWTChanged = 1;
7149 break;
7150 case 691: case 693: // +3DOS 720K disk
7151 optRunDSK = (c == 693);
7152 optDskType = P3DSK_PCW_DS;
7153 optWriteType = '3';
7154 optWTChanged = 1;
7155 break;
7156 case 656: // hex castrates
7157 if (optarg != NULL) {
7158 if (strcmp(optarg, "tan") == 0 || strcmp(optarg, "yes") == 0) {
7159 urasm_allow_hex_castrates = 1;
7160 } else if (strcmp(optarg, "ona") == 0 || strcmp(optarg, "no") == 0) {
7161 urasm_allow_hex_castrates = 0;
7162 } else {
7163 fprintf(stderr, "FATAL: i don't know what '%s' means.\n", optarg);
7164 return 1;
7166 } else {
7167 urasm_allow_hex_castrates = 1;
7169 break;
7173 if (optind >= argc) {
7174 // try to find default input file
7175 for (int f = 0; defInFiles[f]; ++f) {
7176 if (!access(defInFiles[f], R_OK)) {
7177 inFile = strdup(defInFiles[f]);
7178 break;
7181 } else {
7182 inFile = strdup(argv[optind]);
7185 if (!inFile || !inFile[0]) {
7186 res = 1;
7187 fprintf(stderr, "ERROR: no input file!\n");
7188 goto earlyerrquit;
7191 if (optOutputDir == NULL) optOutputDir = strdup(".");
7192 #ifdef WIN32
7193 for (char *ss = optOutputDir; *ss; ++ss) if (*ss == '\\') *ss = '/';
7194 #endif
7196 char *ee = strrchr(optOutputDir, '/');
7197 if (ee && ee != optOutputDir) *ee = 0;
7199 if (!optOutputDir[0]) { free(optOutputDir); optOutputDir = strdup("."); }
7201 registerInstructions();
7202 registerFunctions();
7204 res = asmTextLoad(inFile, 0);
7205 if (!res) {
7206 for (int f = 0; f < defcount; ++f) {
7207 if (labelDoEQU(defines[f], values[f]) != 0) {
7208 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
7209 goto errquit;
7211 //fprintf(stderr, "****SET**** <%s> to <%s>\n", defines[f], values[f]);
7214 for (pass = 0; pass <= 1; ++pass) {
7215 initPass();
7216 printf("pass %d\n", pass);
7217 //fprintf(stderr, "===========pass %d===========\n", pass);
7218 setCurSrcLine(asmText);
7219 if (setjmp(errJP)) { res = 1; break; }
7220 while (currSrcLine) processCurrentLine();
7221 if (postPass()) { res = 1; break; }
7224 // write result
7225 if (res == 0) {
7226 char *oc = strdup(inFile);
7227 char *pd = strrchr(oc, '.');
7228 #ifdef WIN32
7229 if (pd && !strchr(oc, '/') && !strchr(oc, '\\')) *pd = '\0';
7230 #else
7231 if (pd && !strchr(oc, '/')) *pd = '\0';
7232 #endif
7233 switch (optWriteType) {
7234 case 's': saveSna(oc, optSNA48); break;
7235 case 't': saveTap(oc); break;
7236 case 'r': saveRaw(oc); break;
7237 case 'd': saveDMB(oc); break;
7238 case 'H': saveHob(oc); break;
7239 case 'S': saveSCL(oc); break;
7240 case 'M': saveSCLMono(oc); break;
7241 case '3': saveDSK(oc); break;
7243 free(oc);
7244 if (optWriteFixups) writeFixups();
7245 if (wantref) {
7246 /* build ref file name */
7247 if (!refFileName || !refFileName[0]) {
7248 if (refFileName) free(refFileName);
7249 refFileName = malloc(strlen(inFile)+128);
7250 strcpy(refFileName, inFile);
7251 char *ext = strrchr(refFileName, '.');
7252 if (!ext) {
7253 strcat(refFileName, ".ref");
7254 } else {
7255 #ifdef WIN32
7256 char *slash = NULL;
7257 for (char *ts = refFileName; *ts; ++ts) {
7258 if (*ts == '/' || *ts == '\\') slash = ts;
7260 #else
7261 char *slash = strrchr(refFileName, '/');
7262 #endif
7263 if (!slash || slash < ext) {
7264 strcpy(ext, ".ref");
7265 } else {
7266 strcat(refFileName, ".ref");
7270 writeLabelsFile(refFileName);
7271 printf("refs written to '%s'\n", refFileName);
7272 free(refFileName);
7275 } else {
7276 fprintf(stderr, "ERROR: loading error!\n");
7279 errquit:
7280 ufoDeinit();
7281 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
7282 clearFixups();
7283 urClearLabels();
7284 modulesClear();
7285 asmTextClear();
7286 urClearOps();
7287 clearMacros();
7288 freeStructs();
7289 clearKnownFiles();
7291 earlyerrquit:
7292 if (inFile) free(inFile);
7293 if (sysIncludeDir) free(sysIncludeDir);
7294 if (lastIncludePath) free(lastIncludePath);
7295 if (lastSysIncludePath) free(lastSysIncludePath);
7296 for (int f = defcount-1; f >= 0; --f) {
7297 free(values[f]);
7298 free(defines[f]);
7300 if (defines != NULL) { free(values); free(defines); }
7301 if (optOutputFile) free(optOutputFile);
7302 if (optOutputDir) free(optOutputDir);
7304 return (res ? 1 : 0);